2018-10-23 23:45:42 +08:00
|
|
|
'use strict';
|
|
|
|
|
2018-03-17 00:31:40 +08:00
|
|
|
const PSQL = require('cartodb-psql');
|
2018-04-09 22:18:30 +08:00
|
|
|
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
|
|
|
|
const credentials = require('../middlewares/credentials');
|
|
|
|
const authorize = require('../middlewares/authorize');
|
|
|
|
const dbConnSetup = require('../middlewares/db-conn-setup');
|
|
|
|
const rateLimit = require('../middlewares/rate-limit');
|
2018-03-15 00:46:19 +08:00
|
|
|
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit;
|
2018-04-09 22:18:30 +08:00
|
|
|
const cacheControlHeader = require('../middlewares/cache-control-header');
|
|
|
|
const dbParamsFromResLocals = require('../../utils/database-params');
|
2016-07-12 22:08:48 +08:00
|
|
|
|
2018-05-11 22:15:33 +08:00
|
|
|
module.exports = class AnalysesController {
|
|
|
|
constructor (pgConnection, authBackend, userLimitsBackend) {
|
|
|
|
this.pgConnection = pgConnection;
|
|
|
|
this.authBackend = authBackend;
|
|
|
|
this.userLimitsBackend = userLimitsBackend;
|
|
|
|
}
|
2016-07-12 22:08:48 +08:00
|
|
|
|
2019-10-04 18:22:23 +08:00
|
|
|
route (mapRouter) {
|
2018-05-11 22:15:33 +08:00
|
|
|
mapRouter.get('/analyses/catalog', this.middlewares());
|
|
|
|
}
|
2016-07-12 22:08:48 +08:00
|
|
|
|
2018-05-11 22:15:33 +08:00
|
|
|
middlewares () {
|
|
|
|
return [
|
|
|
|
credentials(),
|
|
|
|
authorize(this.authBackend),
|
|
|
|
dbConnSetup(this.pgConnection),
|
|
|
|
rateLimit(this.userLimitsBackend, RATE_LIMIT_ENDPOINTS_GROUPS.ANALYSIS_CATALOG),
|
|
|
|
cleanUpQueryParams(),
|
|
|
|
createPGClient(),
|
|
|
|
getDataFromQuery({ queryTemplate: catalogQueryTpl, key: 'catalog' }),
|
|
|
|
getDataFromQuery({ queryTemplate: tablesQueryTpl, key: 'tables' }),
|
|
|
|
prepareResponse(),
|
|
|
|
cacheControlHeader({ ttl: 10, revalidate: true }),
|
|
|
|
unauthorizedError()
|
|
|
|
];
|
|
|
|
}
|
2016-07-12 22:08:48 +08:00
|
|
|
};
|
|
|
|
|
2018-03-13 18:42:25 +08:00
|
|
|
function createPGClient () {
|
2017-11-18 02:20:42 +08:00
|
|
|
return function createPGClientMiddleware (req, res, next) {
|
2018-03-22 19:30:51 +08:00
|
|
|
const dbParams = dbParamsFromResLocals(res.locals);
|
|
|
|
|
|
|
|
res.locals.pg = new PSQL(dbParams);
|
|
|
|
|
2017-11-18 02:20:42 +08:00
|
|
|
next();
|
|
|
|
};
|
2018-03-13 18:42:25 +08:00
|
|
|
}
|
2017-11-18 02:20:42 +08:00
|
|
|
|
2019-10-22 01:07:24 +08:00
|
|
|
function getDataFromQuery ({ queryTemplate, key }) {
|
2017-11-19 19:37:09 +08:00
|
|
|
const readOnlyTransactionOn = true;
|
2017-11-18 02:14:31 +08:00
|
|
|
|
2019-10-22 01:07:24 +08:00
|
|
|
return function getCatalogMiddleware (req, res, next) {
|
2017-11-18 02:20:42 +08:00
|
|
|
const { pg, user } = res.locals;
|
2017-11-19 19:37:09 +08:00
|
|
|
const sql = queryTemplate({ _username: user });
|
2017-11-18 02:14:31 +08:00
|
|
|
|
2017-11-19 19:37:09 +08:00
|
|
|
pg.query(sql, (err, resultSet = {}) => {
|
2017-11-18 02:14:31 +08:00
|
|
|
if (err) {
|
2017-11-18 01:32:19 +08:00
|
|
|
return next(err);
|
2017-11-18 01:28:37 +08:00
|
|
|
}
|
2017-11-18 01:32:19 +08:00
|
|
|
|
2017-11-19 19:37:09 +08:00
|
|
|
res.locals[key] = resultSet.rows || [];
|
2017-11-18 02:14:31 +08:00
|
|
|
|
2017-11-18 01:32:19 +08:00
|
|
|
next();
|
2017-11-18 02:14:31 +08:00
|
|
|
}, readOnlyTransactionOn);
|
2017-11-18 01:28:37 +08:00
|
|
|
};
|
2018-03-13 18:42:25 +08:00
|
|
|
}
|
2017-11-18 01:28:37 +08:00
|
|
|
|
2018-03-13 18:42:25 +08:00
|
|
|
function prepareResponse () {
|
2017-11-18 01:25:13 +08:00
|
|
|
return function prepareResponseMiddleware (req, res, next) {
|
2017-11-18 02:14:31 +08:00
|
|
|
const { catalog, tables } = res.locals;
|
2017-11-18 01:25:13 +08:00
|
|
|
|
2017-11-18 02:14:31 +08:00
|
|
|
const analysisIdToTable = tables.reduce((analysisIdToTable, table) => {
|
2017-11-18 01:25:13 +08:00
|
|
|
const analysisId = table.relname.split('_')[2];
|
2017-11-19 21:05:20 +08:00
|
|
|
|
2017-11-18 01:25:13 +08:00
|
|
|
if (analysisId && analysisId.length === 40) {
|
|
|
|
analysisIdToTable[analysisId] = table;
|
|
|
|
}
|
2017-11-19 21:05:20 +08:00
|
|
|
|
2017-11-18 01:25:13 +08:00
|
|
|
return analysisIdToTable;
|
|
|
|
}, {});
|
|
|
|
|
2017-11-18 02:14:31 +08:00
|
|
|
const analysisCatalog = catalog.map(analysis => {
|
2019-10-22 05:33:27 +08:00
|
|
|
if (Object.prototype.hasOwnProperty.call(analysisIdToTable, analysis.node_id)) {
|
2017-11-18 01:25:13 +08:00
|
|
|
analysis.table = analysisIdToTable[analysis.node_id];
|
|
|
|
}
|
2017-11-19 21:05:20 +08:00
|
|
|
|
2017-11-18 01:25:13 +08:00
|
|
|
return analysis;
|
|
|
|
})
|
2019-10-22 01:07:24 +08:00
|
|
|
.sort((analysisA, analysisB) => {
|
|
|
|
if (!!analysisA.table && !!analysisB.table) {
|
|
|
|
return analysisB.table.size - analysisA.table.size;
|
|
|
|
}
|
2017-11-18 01:25:13 +08:00
|
|
|
|
2019-10-22 01:07:24 +08:00
|
|
|
if (analysisA.table) {
|
|
|
|
return -1;
|
|
|
|
}
|
2017-11-18 01:25:13 +08:00
|
|
|
|
2019-10-22 01:07:24 +08:00
|
|
|
if (analysisB.table) {
|
|
|
|
return 1;
|
|
|
|
}
|
2017-11-18 01:25:13 +08:00
|
|
|
|
2019-10-22 01:07:24 +08:00
|
|
|
return -1;
|
|
|
|
});
|
2017-11-18 01:25:13 +08:00
|
|
|
|
2018-05-09 21:00:18 +08:00
|
|
|
res.statusCode = 200;
|
2017-11-18 02:14:31 +08:00
|
|
|
res.body = { catalog: analysisCatalog };
|
|
|
|
|
2017-11-18 01:25:13 +08:00
|
|
|
next();
|
|
|
|
};
|
2018-03-13 18:42:25 +08:00
|
|
|
}
|
2017-11-18 01:25:13 +08:00
|
|
|
|
2018-03-13 18:43:08 +08:00
|
|
|
function unauthorizedError () {
|
2019-10-22 01:07:24 +08:00
|
|
|
return function unathorizedErrorMiddleware (err, req, res, next) {
|
2017-11-18 02:14:31 +08:00
|
|
|
if (err.message.match(/permission\sdenied/)) {
|
|
|
|
err = new Error('Unauthorized');
|
|
|
|
err.http_status = 401;
|
|
|
|
}
|
|
|
|
|
|
|
|
next(err);
|
|
|
|
};
|
2018-03-13 18:42:25 +08:00
|
|
|
}
|
2017-11-18 02:14:31 +08:00
|
|
|
|
2017-11-19 19:51:35 +08:00
|
|
|
const catalogQueryTpl = ctx => `
|
|
|
|
SELECT analysis_def->>'type' as type, * FROM cdb_analysis_catalog WHERE username = '${ctx._username}'
|
|
|
|
`;
|
|
|
|
|
|
|
|
var tablesQueryTpl = ctx => `
|
|
|
|
WITH analysis_tables AS (
|
|
|
|
SELECT
|
|
|
|
n.nspname AS nspname,
|
|
|
|
c.relname AS relname,
|
|
|
|
pg_total_relation_size(
|
|
|
|
format('%s.%s', pg_catalog.quote_ident(n.nspname), pg_catalog.quote_ident(c.relname))
|
|
|
|
) AS size,
|
|
|
|
format('%s.%s', pg_catalog.quote_ident(nspname), pg_catalog.quote_ident(relname)) AS fully_qualified_name
|
|
|
|
FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n
|
|
|
|
WHERE c.relnamespace = n.oid
|
|
|
|
AND pg_catalog.quote_ident(c.relname) ~ '^analysis_[a-z0-9]{10}_[a-z0-9]{40}$'
|
|
|
|
AND n.nspname IN ('${ctx._username}', 'public')
|
|
|
|
)
|
|
|
|
SELECT *, pg_size_pretty(size) as size_pretty
|
|
|
|
FROM analysis_tables
|
|
|
|
ORDER BY size DESC
|
|
|
|
`;
|