resource-watch/dataset

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

Summary

Maintainability
F
5 days
Test Coverage
const Router = require('koa-router');
const koaMulter = require('koa-multer');
const { default: logger } = require('logger');
const xor = require('lodash/xor');
const DatasetService = require('services/dataset.service');
const RelationshipsService = require('services/relationships.service');
const UserService = require('services/user.service');
const FileDataService = require('services/fileDataService.service');
const DatasetValidator = require('validators/dataset.validator');
const DatasetSerializer = require('serializers/dataset.serializer');
const DatasetDuplicated = require('errors/datasetDuplicated.error');
const DatasetNotFound = require('errors/datasetNotFound.error');
const DatasetProtected = require('errors/datasetProtected.error');
const MicroserviceConnection = require('errors/microserviceConnection.error');
const DatasetNotValid = require('errors/datasetNotValid.error');
const ConnectorUrlNotValid = require('errors/connectorUrlNotValid.error');
const { RWAPIMicroservice } = require('rw-api-microservice-node');
const { USER_ROLES } = require('app.constants');
const InvalidRequest = require('errors/invalidRequest.error');
const ForbiddenRequest = require('errors/forbiddenRequest.error');
const DatasetModel = require('models/dataset.model');

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

koaMulter({ dest: 'uploads/' });

const serializeObjToQuery = (obj) => Object.keys(obj).reduce((a, k) => {
    a.push(`${k}=${encodeURIComponent(obj[k])}`);
    return a;
}, []).join('&');

const getHostForPaginationLink = (ctx) => {
    if ('x-rw-domain' in ctx.request.header) {
        return ctx.request.header['x-rw-domain'];
    }
    if ('referer' in ctx.request.header) {
        const url = new URL(ctx.request.header.referer)
        return url.host;
    }
    return ctx.request.host
}

const arrayIntersection = (arr1, arr2) => arr1.filter((n) => arr2.indexOf(n) !== -1);

class DatasetRouter {

    /**
     * Fetch the applications being manipulated in the current request.
     * In the case of POST, PUT, GET or DELETE, the applications being manipulated are the ones provided in the request.
     * In the case of PATCH, the applications manipulated are the concat of the added and removed applications.
     *
     * Example: I am an admin who managers application 'A'. An existing dataset is assigned to applications ['A', 'B', 'C'].
     * I want to remove my application ('A') from array of applications of the dataset. So I provide ['B', 'C'] in the body
     * of the request. In this case, the return from this function is ['A'], the only application being manipulated.
     *
     * Returns an array with the applications that should be considered for permission management.
     */
    static async getDatasetApplications(ctx) {
        const requestApps = ctx.request.query.application ? ctx.request.query.application : ctx.request.body.application;
        if (requestApps && ctx.request.method === 'PATCH') {
            const dataset = await DatasetService.get(ctx.params.dataset, ctx.request.headers['x-api-key']);
            return xor(dataset.application || [], requestApps);
        }

        return requestApps;
    }

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

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

    static async notifyAdapter(ctx, dataset) {
        const { connectorType, provider } = dataset;
        const clonedDataset = { ...dataset.toObject() };
        clonedDataset.id = dataset._id;
        clonedDataset.connector_url = dataset.connectorUrl;
        clonedDataset.attributes_path = dataset.attributesPath;
        clonedDataset.data_columns = dataset.datasetAttributes;
        clonedDataset.data_path = dataset.dataPath;
        clonedDataset.table_name = dataset.tableName;
        clonedDataset.data = ctx.request.body.data;

        let uri = '/v1';
        if (connectorType === 'rest') {
            uri += `/rest-datasets/${provider}`;
        } else if (connectorType === 'document') {
            if (ctx.request.path.indexOf('clone') >= 0) {
                clonedDataset.connector_url = process.env.GATEWAY_URL + dataset.connector_url;
                clonedDataset.connectorUrl = process.env.GATEWAY_URL + dataset.connectorUrl;
            }
            uri += `/doc-datasets/${provider}`;
        } else {
            return null;
        }

        const request = {
            uri,
            method: 'POST',
            json: true,
            body: { connector: clonedDataset },
            headers: {
                'x-api-key': ctx.request.headers['x-api-key'],
            }
        };

        if (ctx.request.method === 'DELETE') {
            request.uri += `/${dataset.id}`;
            request.method = 'DELETE'
            delete request.body
        }

        try {
            return await RWAPIMicroservice.requestToMicroservice(request);
        } catch (err) {
            logger.error('Error connecting to dataset adapter');
            throw new MicroserviceConnection(`Error connecting to dataset adapter: ${err.message}`);
        }
    }

    static async notifyAdapterCreate(ctx, dataset) {
        const { connectorType, provider } = dataset;
        const clonedDataset = { ...dataset.toObject() };

        clonedDataset.id = dataset._id;
        clonedDataset.connector_url = dataset.connectorUrl;
        clonedDataset.attributes_path = dataset.attributesPath;
        clonedDataset.data_columns = dataset.datasetAttributes;
        clonedDataset.data_path = dataset.dataPath;
        clonedDataset.table_name = dataset.tableName;
        clonedDataset.data = ctx.request.body.data;

        let uri = '/v1';
        if (connectorType === 'rest') {
            uri += `/rest-datasets/${provider}`;
        } else if (connectorType === 'document') {
            uri += `/doc-datasets/${provider}`;
            if (!clonedDataset.sources || !clonedDataset.sources.length) {
                if (clonedDataset.connectorUrl) {
                    clonedDataset.sources = [clonedDataset.connectorUrl];
                } else {
                    clonedDataset.sources = [];
                }
            }
            delete clonedDataset.connector_url;
            delete clonedDataset.connectorUrl;
        }

        return RWAPIMicroservice.requestToMicroservice({
            uri,
            method: 'POST',
            json: true,
            body: { connector: clonedDataset },
            headers: {
                'x-api-key': ctx.request.headers['x-api-key'],
            }
        });
    }

    static async get(ctx) {
        const id = ctx.params.dataset;
        logger.info(`[DatasetRouter] Getting dataset with id: ${id}`);
        const user = DatasetRouter.getUser(ctx);
        const { query } = ctx;
        delete query.loggedUser;
        try {
            const dataset = await DatasetService.get(id, ctx.request.headers['x-api-key'], query, user && user.role === 'ADMIN');
            const includes = ctx.query.includes ? ctx.query.includes.split(',').map((elem) => elem.trim()) : [];
            const datasetId = dataset.id || dataset[0].id;
            const datasetSlug = dataset.slug || dataset[0].slug;
            ctx.set('cache', `${datasetId} ${includes.map((el) => `${datasetId}-${el.trim()}`).join(' ')} ${datasetSlug} ${includes.map((el) => `${datasetSlug}-${el.trim()}`).join(' ')}`);
            ctx.body = DatasetSerializer.serialize(dataset);
        } catch (err) {
            if (err instanceof DatasetNotFound) {
                ctx.throw(404, err.message);
                return;
            }
            throw err;
        }
    }

    static async create(ctx) {
        logger.info(`[DatasetRouter] Creating dataset with name: ${ctx.request.body.name}`);
        try {
            const user = DatasetRouter.getUser(ctx);
            const dataset = await DatasetService.create(ctx.request.body, user, ctx.request.headers['x-api-key']);
            try {
                DatasetRouter.notifyAdapterCreate(ctx, dataset);
            } catch (error) {
                // do nothing
                logger.error(error);
            }
            ctx.set('uncache', 'dataset graph-dataset');
            ctx.body = DatasetSerializer.serialize(dataset);
        } catch (err) {
            if (err instanceof DatasetDuplicated) {
                ctx.throw(400, err.message);
                return;
            }
            if (err instanceof ConnectorUrlNotValid) {
                ctx.throw(400, err.message);
            }
            throw err;
        }
    }

    static async update(ctx) {
        const id = ctx.params.dataset;
        logger.info(`[DatasetRouter] Updating dataset with id: ${id}`);
        try {
            const user = DatasetRouter.getUser(ctx);
            const result = await DatasetService.update(id, ctx.request.body, user, ctx.request.headers['x-api-key']);
            const dataset = result[0];
            const uncache = [`dataset`, `${dataset.id} ${dataset.slug}`];
            if (result[1]) {
                uncache.push(`${dataset.id}-fields`);
                uncache.push(`${dataset.slug}-fields`);
                uncache.push(`${dataset.id}-query`);
                uncache.push(`${dataset.slug}-query`);
            }
            ctx.set('uncache', uncache.join(' '));
            ctx.body = DatasetSerializer.serialize(dataset);
        } catch (err) {
            if (err instanceof DatasetNotFound) {
                ctx.throw(404, err.message);
                return;
            }
            if (err instanceof ForbiddenRequest) {
                ctx.throw(403, err.message);
                return;
            }
            if (err instanceof InvalidRequest) {
                ctx.throw(400, err.message);
                return;
            }
            if (err instanceof DatasetDuplicated) {
                ctx.throw(400, err.message);
                return;
            }
            throw err;
        }
    }

    static async delete(ctx) {
        const id = ctx.params.dataset;
        logger.info(`[DatasetRouter] Deleting dataset with id: ${id}`);
        try {
            const dataset = await DatasetService.get(id, ctx.request.headers['x-api-key']);

            // Delete adapter-specific things before deleting the actual dataset. If adapter fails, bail
            await DatasetRouter.notifyAdapter(ctx, dataset);

            const user = DatasetRouter.getUser(ctx);
            await DatasetService.delete(dataset, user, ctx.request.headers['x-api-key']);

            ctx.set('uncache', `dataset ${dataset.id} ${dataset.slug}`);
            ctx.body = DatasetSerializer.serialize(dataset);
        } catch (err) {
            if (err instanceof DatasetNotFound) {
                ctx.throw(404, err.message);
                return;
            }
            if (err instanceof DatasetProtected) {
                ctx.throw(400, err.message);
                return;
            }
            throw err;
        }
    }

    static async deleteByUserId(ctx) {
        const userIdToDelete = ctx.params.userId;
        const loggedUser = DatasetRouter.getUser(ctx);

        let user;
        try {
            user = userIdToDelete === loggedUser.id ? loggedUser : await UserService.getUserById(userIdToDelete, ctx.request.headers['x-api-key']);
        } catch (error) {
            ctx.throw(404, `User ${userIdToDelete} does not exist`);
        }

        logger.info(`[DatasetRouter] Deleting all dataset for user with id: ${userIdToDelete}`);
        try {
            const datasets = await DatasetService.deleteByUserId(userIdToDelete, user, ctx.request.headers['x-api-key']);
            ctx.body = {
                deletedDatasets: DatasetSerializer.serialize(datasets.deletedDatasets).data
            }

            if (datasets.protectedDatasets) {
                ctx.body.protectedDatasets = DatasetSerializer.serialize(datasets.protectedDatasets).data;
            }
        } catch (err) {
            logger.error(`Error deleting datasets from user ${userIdToDelete}`, err);
            ctx.throw(500, `Error deleting datasets from user ${userIdToDelete}`);
        }
    }

    static async findByIds(ctx) {
        logger.info(`[DatasetRouter] Getting all datasets with ids`, ctx.request.body);
        if (ctx.request && ctx.request.body && ctx.request.body && ctx.request.body.ids.length > 0) {
            ctx.query.ids = ctx.request.body.ids.join(',');
            if (ctx.request.body.env) {
                ctx.request.query.env = ctx.request.body.env;
            }
            const datasets = await DatasetService.getAll(ctx.request.headers['x-api-key'], ctx.request.query);
            ctx.body = DatasetSerializer.serialize(datasets);
        } else {
            ctx.body = {
                data: []
            };
        }
    }

    static async getAll(ctx) {
        logger.info(`[DatasetRouter] Getting all datasets`);
        const user = DatasetRouter.getUser(ctx);
        const { query } = ctx;
        const { search } = query;
        const sort = ctx.query.sort || '';
        const userId = ctx.query.loggedUser && ctx.query.loggedUser !== 'null' ? JSON.parse(ctx.query.loggedUser).id : null;
        delete query.loggedUser;

        if (!search && sort.indexOf('relevance') >= 0) {
            ctx.throw(400, 'Cannot sort by relevance without search criteria');
            return;
        }

        try {
            if (query.sort && (query.sort.includes('user.role') || query.sort.includes('user.name'))) {
                logger.debug('Detected sorting by user role or name');
                const isAdmin = ['ADMIN', 'SUPERADMIN'].includes(user && user.role);
                if (!user || !isAdmin) {
                    ctx.throw(403, 'Sorting by user name or role not authorized.');
                    return;
                }

                // Reset all datasets' sorting columns
                await DatasetModel.updateMany({}, { userRole: '', userName: '' });

                // Fetch info to sort again
                const ids = await DatasetService.getAllDatasetUserIds();
                const users = await RelationshipsService.getUsersInfoByIds(ids, ctx.request.headers['x-api-key']);
                await Promise.all(users.map((u) => DatasetModel.updateMany(
                    { userId: u._id },
                    {
                        userRole: u.role ? u.role.toLowerCase() : '',
                        userName: u.name ? u.name.toLowerCase() : '',
                    },
                )));
            }

            /**
             * We'll want to limit the maximum page size in the future
             * However, as this will cause a production BC break, we can't enforce it just now
             */
            // if (query['page[size]'] && query['page[size]'] > 100) {
            //     ctx.throw(400, 'Invalid page size');
            // }

            if (Object.keys(query).find((el) => el.indexOf('vocabulary[') >= 0)) {
                ctx.query.ids = await RelationshipsService.filterByVocabularyTag(query, ctx.request.headers['x-api-key']);
                logger.debug('Ids from vocabulary-tag', ctx.query.ids);
            }
            if (Object.keys(query).find((el) => el.indexOf('user.role') >= 0) && user && user.role === 'ADMIN') {
                logger.debug('Obtaining users with role');
                ctx.query.usersRole = await UserService.getUsersWithRole(ctx.query['user.role'], ctx.request.headers['x-api-key']);
                logger.debug('Ids from users with role', ctx.query.usersRole);
            }
            if (Object.keys(query).find((el) => el.indexOf('collection') >= 0)) {
                if (!userId) {
                    ctx.throw(403, 'Collection filter not authorized');
                    return;
                }
                ctx.query.ids = await RelationshipsService.getCollections(ctx.query.collection, userId, ctx.request.headers['x-api-key']);
                ctx.query.ids = ctx.query.ids.length > 0 ? ctx.query.ids.join(',') : '';
                logger.debug('Ids from collections', ctx.query.ids);
            }
            if (Object.keys(query).find((el) => el.indexOf('favourite') >= 0)) {
                if (!userId) {
                    ctx.throw(403, 'Fav filter not authorized');
                    return;
                }
                const app = ctx.query.app || ctx.query.application || 'rw';
                ctx.query.ids = await RelationshipsService.getFavorites(app, userId, ctx.request.headers['x-api-key']);
                ctx.query.ids = ctx.query.ids.length > 0 ? ctx.query.ids.join(',') : '';
                logger.debug('Ids from collections', ctx.query.ids);
            }
            if (
                search
                || serializeObjToQuery(query).indexOf('concepts[0][0]') >= 0
                || sort.indexOf('most-favorited') >= 0
                || sort.indexOf('most-viewed') >= 0
                || sort.indexOf('relevance') >= 0
                || sort.indexOf('metadata') >= 0
            ) {
                let searchIds = null;
                let conceptIds = null;

                if (search) {
                    let metadataSort = null;
                    if (
                        sort.indexOf('metadata') >= 0
                        || sort.indexOf('relevance') >= 0
                    ) {
                        metadataSort = sort;
                    }

                    const metadataIds = await RelationshipsService.filterByMetadata(search, metadataSort, ctx.request.headers['x-api-key']);
                    const searchBySynonymsIds = await RelationshipsService.searchBySynonyms(query, ctx.request.headers['x-api-key']);
                    const datasetBySearchIds = await DatasetService.getDatasetIdsBySearch(search.split(' '));
                    searchIds = metadataIds.concat(searchBySynonymsIds).concat(datasetBySearchIds);
                }
                if (
                    serializeObjToQuery(query).indexOf('concepts[0][0]') >= 0
                    || sort.indexOf('most-favorited') >= 0
                    || sort.indexOf('most-viewed') >= 0
                ) {
                    conceptIds = await RelationshipsService.filterByConcepts(query, ctx.request.headers['x-api-key']);
                }
                if ((searchIds && searchIds.length === 0) || (conceptIds && conceptIds.length === 0)) {
                    ctx.body = DatasetSerializer.serialize([], null);
                    return;
                }
                const finalIds = searchIds && conceptIds ? arrayIntersection(conceptIds, searchIds) : searchIds || conceptIds;
                const uniqueIds = new Set([...finalIds]); // Intersect and unique
                ctx.query.ids = [...uniqueIds].join(); // it has to be string
            }

            const originalQuery = { ...query };
            delete originalQuery['page[size]'];
            delete originalQuery['page[number]'];
            delete originalQuery.ids;
            delete originalQuery.usersRole;
            delete originalQuery.requestApplication;
            const serializedQuery = serializeObjToQuery(originalQuery) ? `?${serializeObjToQuery(originalQuery)}&` : '?';
            const apiVersion = ctx.mountPath.split('/')[ctx.mountPath.split('/').length - 1];
            const link = `${ctx.request.protocol}://${getHostForPaginationLink(ctx)}/${apiVersion}${ctx.request.path}${serializedQuery}`;

            if (!query.env) {
                query.env = 'production';
            }

            const datasets = await DatasetService.getAll(ctx.request.headers['x-api-key'], query, user && user.role === 'ADMIN');
            ctx.set('cache', `dataset ${query.includes ? query.includes.split(',').map((elem) => elem.trim()).join(' ') : ''}`);
            ctx.body = DatasetSerializer.serialize(datasets, link);
        } catch (err) {
            if (err instanceof InvalidRequest) {
                ctx.throw(400, err);
            }
            ctx.throw(500, err);
        }
    }

    static async clone(ctx) {
        const id = ctx.params.dataset;
        logger.info(`[DatasetRouter] Cloning dataset with id: ${id}`);
        try {
            const user = DatasetRouter.getUser(ctx);
            const fullCloning = ctx.query.full === 'true';
            const dataset = await DatasetService.clone(id, ctx.request.body, user, ctx.request.headers['x-api-key'], fullCloning);
            try {
                DatasetRouter.notifyAdapter(ctx, dataset);
            } catch (error) {
                // do nothing
            }
            ctx.set('uncache', 'dataset graph-dataset');
            ctx.body = DatasetSerializer.serialize(dataset);
        } catch (err) {
            if (err instanceof DatasetDuplicated) {
                ctx.throw(400, err.message);
                return;
            }
            throw err;
        }
    }

    static async upload(ctx) {
        logger.info(`[DatasetRouter] Uploading new file`);
        try {
            const filename = `${Date.now()}_${ctx.request.body.files.dataset.name}`;
            await FileDataService.uploadFileToS3(ctx.request.body.files.dataset.path, filename, true);
            const fields = await FileDataService.getFields(ctx.request.body.files.dataset.path, ctx.request.body.fields.provider);
            ctx.body = {
                connectorUrl: `rw.dataset.raw/${filename}`,
                fields
            };
        } catch (err) {
            logger.error('Error uploading file', err);
            ctx.throw(500, 'Error uploading file');
        }
    }

    static async recover(ctx) {
        logger.info(`[DatasetRouter] Recovering dataset status`);
        try {
            await DatasetService.recover(ctx.params.dataset);
            ctx.body = 'OK';
        } catch (err) {
            if (err instanceof DatasetNotFound) {
                ctx.throw(404, err.message);
                return;
            }
            ctx.throw(500, 'Error recovering dataset status');
        }
    }

    static async flushDataset(ctx) {
        const datasetId = ctx.params.dataset;
        const dataset = await DatasetService.get(datasetId, ctx.request.headers['x-api-key']);
        ctx.set('uncache', `${dataset._id} ${dataset.slug} query-${dataset._id} query-${dataset.slug} fields-${dataset._id} fields-${dataset.slug}`);
        ctx.body = 'OK';
    }

}

const validationMiddleware = async (ctx, next) => {
    logger.info(`[DatasetRouter] Validating`);
    if (ctx.request.body.dataset) {
        ctx.request.body = Object.assign(ctx.request.body, ctx.request.body.dataset);
        delete ctx.request.body.dataset;
    }
    try {
        const newDatasetCreation = ctx.request.path === '/dataset' && ctx.request.method === 'POST';
        if (newDatasetCreation) {
            await DatasetValidator.validateCreation(ctx);
        } else if (ctx.request.path.indexOf('clone') >= 0) {
            await DatasetValidator.validateCloning(ctx);
        } else if (ctx.request.path.indexOf('upload') >= 0) {
            await DatasetValidator.validateUpload(ctx);
        } else if (ctx.request.path.indexOf('find-by-ids') >= 0) {
            await DatasetValidator.validateFindByIDS(ctx);
        } else {
            await DatasetValidator.validateUpdate(ctx);
        }
    } catch (err) {
        if (err instanceof DatasetNotValid) {
            ctx.throw(400, err.getMessages());
            return;
        }
        throw err;
    }
    await next();
};

const authorizationMiddleware = async (ctx, next) => {
    logger.info(`[DatasetRouter] Checking authorization`);
    // Get user from query (delete) or body (post-patch)
    const newDatasetCreation = ctx.request.path === '/dataset' && ctx.request.method === 'POST';
    const uploadDataset = ctx.request.path.indexOf('upload') >= 0 && ctx.request.method === 'POST';
    const user = DatasetRouter.getUser(ctx);
    if (ctx.request.path.endsWith('flush') && user.role === 'ADMIN') {
        await next();
        return;
    }
    if (user.id === 'microservice') {
        await next();
        return;
    }
    if (!user || USER_ROLES.indexOf(user.role) === -1) {
        ctx.throw(401, 'Unauthorized'); // if not logged or invalid ROLE -> out
        return;
    }
    if (user.role === 'USER') {
        if (!newDatasetCreation && !uploadDataset) {
            ctx.throw(403, 'Forbidden'); // if user is USER -> out
            return;
        }
    }

    const datasetApps = await DatasetRouter.getDatasetApplications(ctx);
    if (datasetApps && datasetApps.length > 0 && !DatasetService.validateAppPermission(user, datasetApps)) {
        ctx.throw(403, 'Forbidden - User does not have access to this dataset\'s application'); // if manager or admin but no application -> out
        return;
    }

    const allowedOperations = newDatasetCreation || uploadDataset;
    if ((user.role === 'MANAGER' || user.role === 'ADMIN') && !allowedOperations) {
        const permission = await DatasetService.hasPermission(ctx.params.dataset, user, datasetApps, ctx.request.headers['x-api-key']);
        if (!permission) {
            ctx.throw(403, 'Forbidden');
            return;
        }
    }
    await next(); // SUPERADMIN is included here
};

const deleteResourceAuthorizationMiddleware = async (ctx, next) => {
    logger.info(`[DatasetRouter] Checking authorization`);
    const user = DatasetRouter.getUser(ctx);
    const userFromParam = ctx.params.userId;

    if (user.id === 'microservice' || user.role === 'ADMIN') {
        await next();
        return;
    }

    if (userFromParam === user.id) {
        await next();
        return;
    }

    ctx.throw(403, 'Forbidden');
};

const authorizationBigQuery = async (ctx, next) => {
    logger.info(`[DatasetRouter] Checking if bigquery dataset`);
    // Get user from query (delete) or body (post-patch)
    const user = DatasetRouter.getUser(ctx);
    if (ctx.request.body.provider === 'bigquery'
        && (
            user.email !== 'enrique.cornejo@vizzuality.com'
            && user.email !== 'tiago.garcia@vizzuality.com'
            && user.email !== 'alicia.arenzana@vizzuality.com'
        )
    ) {
        ctx.throw(401, 'Unauthorized'); // if not logged or invalid ROLE -> out
        return;
    }
    await next();
};

// const authorizationSubscribable = async (ctx, next) => {
//     logger.info(`[DatasetRouter] Checking if it can update the subscribable prop`);
//     if (ctx.request.body.subscribable) {
//         const user = DatasetRouter.getUser(ctx);
//         if (user.email !== 'sergio.gordillo@vizzuality.com' && user.email !== 'raul.requero@vizzuality.com' && user.email !== 'alicia.arenzana@vizzuality.com') {
//             ctx.throw(401, 'Unauthorized'); // if not logged or invalid ROLE -> out
//             return;
//         }
//     }
//     await next();
// };

const authorizationRecover = async (ctx, next) => {
    logger.info(`[DatasetRouter] Authorization for recovering`);
    const user = DatasetRouter.getUser(ctx);
    if (user.role !== 'ADMIN') {
        ctx.throw(401, 'Unauthorized'); // if not logged or invalid ROLE -> out
        return;
    }
    await next();
};

const isAuthenticated = async (ctx, next) => {
    logger.info(`Verifying if user is authenticated`);
    const user = DatasetRouter.getUser(ctx);
    if (!user || !user.id) {
        ctx.throw(401, 'Unauthorized');
        return;
    }
    await next();
};

router.get('/', DatasetRouter.getAll);
router.post('/find-by-ids', validationMiddleware, DatasetRouter.findByIds);
router.post('/', isAuthenticated, validationMiddleware, authorizationMiddleware, authorizationBigQuery, DatasetRouter.create);
// router.post('/', validationMiddleware, authorizationMiddleware, authorizationBigQuery, authorizationSubscribable, DatasetRouter.create);
router.post('/upload', isAuthenticated, validationMiddleware, authorizationMiddleware, DatasetRouter.upload);
router.post('/:dataset/flush', isAuthenticated, authorizationMiddleware, DatasetRouter.flushDataset);
router.post('/:dataset/recover', isAuthenticated, authorizationRecover, DatasetRouter.recover);

router.get('/:dataset', DatasetRouter.get);
router.patch('/:dataset', isAuthenticated, validationMiddleware, authorizationMiddleware, DatasetRouter.update);
router.delete('/:dataset', isAuthenticated, authorizationMiddleware, DatasetRouter.delete);
router.post('/:dataset/clone', isAuthenticated, validationMiddleware, authorizationMiddleware, DatasetRouter.clone);

router.delete('/by-user/:userId', isAuthenticated, deleteResourceAuthorizationMiddleware, DatasetRouter.deleteByUserId)

module.exports = router;