resource-watch/document-adapter

View on GitHub
app/src/routes/api/v1/dataset.router.js

Summary

Maintainability
F
5 days
Test Coverage
B
83%
const { RWAPIMicroservice } = require('rw-api-microservice-node');
const Router = require('koa-router');
const logger = require('logger');
const taskQueueService = require('services/taskQueueService');
const DatasetMiddleware = require('middleware/dataset.middleware');

const router = new Router({
    prefix: '/document'
});

class DatasetRouter {

    static async import(ctx) {
        logger.info('Adding dataset with dataset id: ', ctx.request.body);
        await taskQueueService.import({
            datasetId: ctx.request.body.connector.id,
            fileUrl: ctx.request.body.connector.sources,
            data: ctx.request.body.connector.data,
            dataPath: ctx.request.body.connector.dataPath,
            provider: ctx.params.provider || 'csv',
            legend: ctx.request.body.connector.legend,
            verified: ctx.request.body.connector.verified
        });
        ctx.body = '';
    }

    static async overwrite(ctx) {
        logger.info('Overwrite dataset with dataset id: ', ctx.params.dataset);
        ctx.assert(ctx.request.body.url || ctx.request.body.data || ctx.request.body.sources, 400, 'Url or data is required');
        ctx.assert(ctx.request.body.provider, 400, 'Provider required');
        if (ctx.request.body.dataset && (ctx.request.body.dataset.status !== 'saved' && ctx.request.body.dataset.status !== 'failed')) {
            ctx.throw(400, 'Dataset is not in saved status');
            return;
        }

        await taskQueueService.overwrite({
            datasetId: ctx.params.dataset,
            fileUrl: ctx.request.body.sources || [ctx.request.body.url],
            data: ctx.request.body.data,
            dataPath: ctx.request.body.dataPath,
            provider: ctx.request.body.provider || 'csv',
            legend: ctx.request.body.legend,
            index: ctx.request.body.dataset.tableName
        });
        ctx.set('cache-control', 'flush');
        ctx.body = '';
    }

    static async concat(ctx) {
        logger.info('Concat dataset with dataset id: ', ctx.params.dataset);
        ctx.assert(ctx.request.body.url || ctx.request.body.data || ctx.request.body.sources, 400, 'Url or data is required');
        ctx.assert(ctx.request.body.provider, 400, 'Provider required');
        if (ctx.request.body.dataset && (ctx.request.body.dataset.status !== 'saved' && ctx.request.body.dataset.status !== 'failed')) {
            ctx.throw(400, 'Dataset is not in saved status');
            return;
        }
        await taskQueueService.concat({
            datasetId: ctx.params.dataset,
            fileUrl: ctx.request.body.sources || [ctx.request.body.url],
            data: ctx.request.body.data,
            dataPath: ctx.request.body.dataPath,
            provider: ctx.request.body.provider || 'csv',
            legend: ctx.request.body.dataset.legend,
            index: ctx.request.body.dataset.tableName
        });
        ctx.set('cache-control', 'flush');
        ctx.body = '';
    }

    static async append(ctx) {
        logger.info('Concat dataset with dataset id: ', ctx.params.dataset);
        ctx.assert(ctx.request.body.url || ctx.request.body.data || ctx.request.body.sources, 400, 'Url or data is required');
        ctx.assert(ctx.request.body.provider, 400, 'Provider required');
        if (ctx.request.body.dataset && (ctx.request.body.dataset.status !== 'saved' && ctx.request.body.dataset.status !== 'failed')) {
            ctx.throw(400, 'Dataset is not in saved status');
            return;
        }
        await taskQueueService.append({
            datasetId: ctx.params.dataset,
            fileUrl: ctx.request.body.sources || [ctx.request.body.url],
            data: ctx.request.body.data,
            dataPath: ctx.request.body.dataPath,
            provider: ctx.request.body.provider || 'csv',
            legend: ctx.request.body.dataset.legend,
            index: ctx.request.body.dataset.tableName
        });
        ctx.set('cache-control', 'flush');
        ctx.body = '';
    }

    static async reindex(ctx) {
        logger.info('Reindex dataset with dataset id: ', ctx.params.dataset);
        if (ctx.request.body.dataset && (ctx.request.body.dataset.status !== 'saved' && ctx.request.body.dataset.status !== 'failed')) {
            ctx.throw(400, 'Dataset is not in saved status');
            return;
        }
        await taskQueueService.reindex({
            datasetId: ctx.params.dataset,
            provider: ctx.request.body.provider || 'csv',
            legend: ctx.request.body.dataset.legend,
            index: ctx.request.body.dataset.tableName
        });
        ctx.set('cache-control', 'flush');
        ctx.body = '';
    }

    static async deleteIndex(ctx) {
        logger.info('Deleting index with dataset', ctx.request.body);
        const response = await RWAPIMicroservice.requestToMicroservice({
            method: 'GET',
            uri: `/v1/dataset/${ctx.params.dataset}`,
            headers: {
                'x-api-key': ctx.request.headers['x-api-key'],
            }
        });

        if (response.data.attributes.tableName) {
            await taskQueueService.deleteIndex({
                datasetId: ctx.params.dataset,
                index: response.data.attributes.tableName
            });
        }
        ctx.set('cache-control', 'flush');
        ctx.body = '';
    }

}

const containApps = (apps1, apps2) => {
    if (!apps1 || !apps2) {
        return false;
    }
    for (let i = 0, { length } = apps1; i < length; i += 1) {
        for (let j = 0, length2 = apps2.length; j < length2; j += 1) {
            if (apps1[i] === apps2[j]) {
                return true;
            }
        }
    }
    return false;
};

const checkUserHasPermission = (user, dataset) => {
    if (user && dataset) {
        if (user.id === 'microservice') {
            return true;
        }
        // check if user is admin of any application of the dataset or manager and owner of the dataset
        if (user.role === 'MANAGER' && user.id === dataset.userId) {
            return true;
        }
        if (user.role === 'ADMIN' && containApps(dataset.application, user.extraUserData ? user.extraUserData.apps : null)) {
            return true;
        }

    }
    return false;
};

const checkPermissionModify = async (ctx, next) => {
    logger.debug('Checking if the user has permissions');
    const user = ctx.request.body.loggedUser;
    const { dataset } = ctx.request.body;
    if (!user) {
        logger.debug(`User data missing`);
        ctx.throw(401, 'User credentials invalid or missing');
    }
    if (!dataset) {
        ctx.throw(400, 'Dataset not found');
    }
    if (checkUserHasPermission(user, dataset)) {
        // eslint-disable-next-line no-underscore-dangle
        if (dataset.overwrite || (ctx._matchedRoute === '/document/:dataset/reindex' && user.role === 'ADMIN')) {
            await next();
            return;
        }
        ctx.throw(409, 'Dataset locked. Overwrite false.');
    } else {
        logger.debug(`User ${user.id} with role ${user.role} and app(s) '${(user.extraUserData ? user.extraUserData.apps : []).join(', ')}' does not have permissions to modify dataset ${dataset.id}`);
        ctx.throw(403, 'Not authorized');
    }
};

const isAuthenticatedMiddleware = async (ctx, next) => {
    logger.info(`Verifying if user is authenticated`);
    const { query, body } = ctx.request;

    const user = { ...(query.loggedUser ? JSON.parse(query.loggedUser) : {}), ...body.loggedUser };

    if (!user || !user.id) {
        ctx.throw(401, 'Unauthorized');
        return;
    }
    await next();
};

router.post('/:provider', DatasetRouter.import);
router.post('/:dataset/data-overwrite', isAuthenticatedMiddleware, DatasetMiddleware.getDatasetById, checkPermissionModify, DatasetRouter.overwrite);
router.post('/:dataset/concat', isAuthenticatedMiddleware, DatasetMiddleware.getDatasetById, checkPermissionModify, DatasetRouter.concat);
router.post('/:dataset/append', isAuthenticatedMiddleware, DatasetMiddleware.getDatasetById, checkPermissionModify, DatasetRouter.append);
router.post('/:dataset/reindex', isAuthenticatedMiddleware, DatasetMiddleware.getDatasetById, checkPermissionModify, DatasetRouter.reindex);
router.delete('/:provider/:dataset', isAuthenticatedMiddleware, DatasetRouter.deleteIndex);

module.exports = router;