server/api/DatasetRoute.js
Rama master · réplica de lectura del repositorio original.
const { teamRoles } = require("../../shared/roles.json");
const { withSourceExecutionContext } = require("../modules/sourceExecution");
const homeSuggestions = require("../modules/ai/homeSuggestions/service");
const DatasetController = require("../controllers/DatasetController");
const DatasetIntelligenceController = require("../controllers/DatasetIntelligenceController");
const TeamController = require("../controllers/TeamController");
const verifyToken = require("../modules/verifyToken");
const accessControl = require("../modules/accessControl");
module.exports = (app) => {
const datasetController = new DatasetController();
const datasetIntelligenceController = new DatasetIntelligenceController();
const teamController = new TeamController();
const root = "/team/:team_id/datasets";
const hasProjectAccess = (projectIds = [], projects = []) => {
if (!Array.isArray(projectIds) || !Array.isArray(projects)) return false;
return projectIds.some((projectId) => projects.includes(projectId));
};
const checkPermissions = (actionType = "readOwn") => {
return async (req, res, next) => {
const { team_id } = req.params;
// Fetch the TeamRole for the user
const teamRole = await teamController.getTeamRole(team_id, req.user.id);
if (!teamRole) {
return res.status(403).json({ message: "Access denied" });
}
let permission;
if (["teamEditor", "projectAdmin", "projectEditor"].includes(teamRole.role) && actionType.indexOf("Any") > -1) {
permission = accessControl.can(teamRole.role)[actionType.replace("Any", "Own")]("dataset");
} else {
permission = accessControl.can(teamRole.role)[actionType]("dataset");
}
if (!permission.granted) {
return res.status(403).json({ message: "Access denied" });
}
const { role, projects } = teamRole;
// Handle permissions for teamOwner and teamAdmin
if (teamRoles.includes(role)) {
return next();
}
if (role === "projectAdmin" || role === "projectEditor" || role === "projectViewer") {
const datasets = await datasetController.findByProjects(team_id, projects);
if (!datasets || datasets.length === 0) {
return res.status(404).json({ message: "No datasets found" });
}
// save the projects in the user object
req.user.projects = projects;
return next();
}
return res.status(403).json({ message: "Access denied" });
};
};
const ensureDatasetBelongsToTeam = async (req, res, next) => {
const datasetId = req.params.dataset_id || req.params.id;
if (!datasetId) {
return res.status(400).send("Missing dataset id");
}
try {
const dataset = await datasetController.findByIdAndTeam(datasetId, req.params.team_id);
if (req.user.projects && !hasProjectAccess(dataset?.project_ids, req.user.projects)) {
return res.status(403).json({ message: "Access denied" });
}
req.dataset = dataset;
return next();
} catch (error) {
if (error?.message === "404") {
return res.status(404).send((error && error.message) || error);
}
return res.status(400).send((error && error.message) || error);
}
};
/*
** Route to search dataset intelligence
*/
app.get(`${root}/intelligence/search`, verifyToken, checkPermissions("readOwn"), async (req, res) => {
try {
const result = await datasetIntelligenceController.search({
teamId: req.params.team_id,
query: req.query.query,
projectId: req.query.project_id,
allowedProjectIds: req.user.projects,
limit: req.query.limit,
});
return res.status(200).send(result);
} catch (error) {
return res.status(400).send(error.message || error);
}
});
// ----------------------------------------------------
/*
** Route to get dataset intelligence
*/
app.get(`${root}/:dataset_id/intelligence`, verifyToken, checkPermissions("readOwn"), ensureDatasetBelongsToTeam, async (req, res) => {
try {
const result = await datasetIntelligenceController.get({
datasetId: req.params.dataset_id,
teamId: req.params.team_id,
});
return res.status(200).send(result);
} catch (error) {
return res.status(400).send(error.message || error);
}
});
app.post(`${root}/:dataset_id/intelligence/refresh`, verifyToken, checkPermissions("updateAny"), ensureDatasetBelongsToTeam, async (req, res) => {
try {
const result = await datasetIntelligenceController.refresh({
datasetId: req.params.dataset_id,
teamId: req.params.team_id,
});
return res.status(200).send(result);
} catch (error) {
return res.status(400).send(error.message || error);
}
});
app.put(`${root}/:dataset_id/intelligence/overrides`, verifyToken, checkPermissions("updateAny"), ensureDatasetBelongsToTeam, async (req, res) => {
try {
const result = await datasetIntelligenceController.updateOverrides({
datasetId: req.params.dataset_id,
teamId: req.params.team_id,
overrides: req.body,
});
return res.status(200).send(result);
} catch (error) {
return res.status(400).send(error.message || error);
}
});
// ----------------------------------------------------
/*
** Route to get all datasets
*/
app.get(root, verifyToken, checkPermissions("readOwn"), (req, res) => {
return datasetController.findByTeam(req.params.team_id)
.then((datasets) => {
if (req.user.projects) {
const filteredDatasets = datasets.filter((dataset) => {
if (!dataset.project_ids) return false;
return dataset.project_ids.some((project_id) => {
return req.user.projects.includes(project_id);
});
});
return res.status(200).send(filteredDatasets);
}
return res.status(200).send(datasets);
})
.catch((err) => {
return res.status(400).send(err);
});
});
/*
** Route to get a dataset by ID
*/
app.get(`${root}/:dataset_id`, verifyToken, checkPermissions("readOwn"), ensureDatasetBelongsToTeam, (req, res) => {
return datasetController.findByIdAndTeam(req.params.dataset_id, req.params.team_id)
.then((dataset) => {
return res.status(200).send(dataset);
})
.catch((err) => {
if (err && err.message && err.message === "401") {
return res.status(401).send(err);
}
return res.status(400).send(err);
});
});
// ----------------------------------------------------
/*
** Route to create a new dataset
*/
app.post(root, verifyToken, checkPermissions("createAny"), (req, res) => {
if (req.user.projects) {
req.body.project_ids = req.user.projects;
}
return datasetController.create(req.body)
.then((dataset) => {
homeSuggestions.recordUserActivity(Number(req.params.team_id), req.user.id, [{ entityType: "dataset", entityId: dataset.id }]);
return res.status(200).send(dataset);
})
.catch((err) => {
if (err && err.message && err.message === "401") {
return res.status(401).send(err);
}
return res.status(400).send(err);
});
});
// ----------------------------------------------------
/*
** Route to quickly create a dataset with all its data requests in one go
*/
app.post(`${root}/quick-create`, verifyToken, checkPermissions("createAny"), (req, res) => {
if (req.user.projects) {
req.body.project_ids = req.user.projects;
}
// Ensure team_id matches the route parameter
req.body.team_id = req.params.team_id;
req.body.xAxis = req.body.xAxis || req.body.dimension;
req.body.yAxis = req.body.yAxis || req.body.metric;
req.body.yAxisOperation = req.body.yAxisOperation || req.body.metricOperation;
return datasetController.createWithDataRequests(req.body)
.then((dataset) => {
homeSuggestions.recordUserActivity(Number(req.params.team_id), req.user.id, [{ entityType: "dataset", entityId: dataset.id }]);
return res.status(200).send(dataset);
})
.catch((err) => {
if (err && err.message && err.message === "401") {
return res.status(401).send(err);
}
return res.status(400).send(err);
});
});
// ----------------------------------------------------
/*
** Route to duplicate a dataset
*/
app.post(`${root}/:dataset_id/duplicate`, verifyToken, checkPermissions("createAny"), ensureDatasetBelongsToTeam, (req, res) => {
return datasetController.duplicateDataset(req.params.dataset_id, req.body.name)
.then((dataset) => {
return res.status(200).send(dataset);
})
.catch((err) => {
if (err && err.message && err.message === "401") {
return res.status(401).send(err);
}
return res.status(400).send(err);
});
});
// ----------------------------------------------------
/*
** Route to get the datasets by Chart ID
*/
app.get(`${root}`, verifyToken, checkPermissions("readyAny"), (req, res) => {
return datasetController.findByChart(req.params.chart_id)
.then((dataset) => {
return res.status(200).send(dataset);
})
.catch((err) => {
if (err && err.message && err.message === "401") {
return res.status(401).send(err);
}
return res.status(400).send(err);
});
});
// ----------------------------------------------------
/*
** Route to update a dataset
*/
app.put(`${root}/:dataset_id`, verifyToken, checkPermissions("updateAny"), ensureDatasetBelongsToTeam, (req, res) => {
req.body.team_id = req.params.team_id;
return datasetController.updateByTeam(req.params.dataset_id, req.params.team_id, req.body)
.then((dataset) => {
homeSuggestions.recordUserActivity(Number(req.params.team_id), req.user.id, [{ entityType: "dataset", entityId: req.params.dataset_id }]);
return res.status(200).send(dataset);
})
.catch((err) => {
if (err && err.message && err.message === "401") {
return res.status(401).send(err);
}
return res.status(400).send(err);
});
});
// ----------------------------------------------------
/*
** Route to delete all draft datasets
*/
app.delete(`${root}/drafts`, verifyToken, checkPermissions("deleteAny"), (req, res) => {
return datasetController.removeDrafts(req.params.team_id)
.then((result) => {
return res.status(200).send(result);
})
.catch((err) => {
return res.status(400).send(err);
});
});
// ----------------------------------------------------
/*
** Route to delete a dataset
*/
app.delete(`${root}/:dataset_id`, verifyToken, checkPermissions("deleteAny"), ensureDatasetBelongsToTeam, (req, res) => {
return datasetController.removeByTeam(req.params.dataset_id, req.params.team_id)
.then((result) => {
return res.status(200).send(result);
})
.catch((err) => {
return res.status(400).send(err);
});
});
// ----------------------------------------------------
/*
** [DEPRECATED] Route to run the request attached to the dataset
*/
app.get(`${root}/:dataset_id/request`, verifyToken, checkPermissions("readAny"), ensureDatasetBelongsToTeam, (req, res, next) => {
return withSourceExecutionContext({ activity: "preview", teamId: req.params.team_id }, next);
}, (req, res) => {
return datasetController.runRequest(
{
dataset_id: req.params.dataset_id,
chart_id: req.params.chart_id,
noSource: req.query.noSource,
getCache: req.query.getCache,
team_id: req.params.team_id,
}
)
.then((dataset) => {
const newDataset = dataset;
if (newDataset?.data) {
const { data } = newDataset;
if (typeof data === "object" && data instanceof Array) {
newDataset.data = data.slice(0, 20);
} else if (typeof data === "object") {
const resultsKey = [];
Object.keys(data).forEach((key) => {
if (data[key] instanceof Array) {
resultsKey.push(key);
}
});
if (resultsKey.length > 0) {
resultsKey.forEach((resultKey) => {
const slicedArray = data[resultKey].slice(0, 20);
newDataset.data[resultKey] = slicedArray;
});
}
}
}
return res.status(200).send(newDataset);
})
.catch((err) => {
if (err && err.message === "404") {
return res.status(404).send((err && err.message) || err);
}
return res.status(400).send((err && err.message) || err);
});
});
// ----------------------------------------------------
/*
** [NEW] Route to run the request attached to the dataset
*/
app.post(`${root}/:dataset_id/request`, verifyToken, checkPermissions("readAny"), ensureDatasetBelongsToTeam, (req, res, next) => {
return withSourceExecutionContext({ activity: "preview", teamId: req.params.team_id }, next);
}, (req, res) => {
return datasetController.runRequest({
dataset_id: req.params.dataset_id,
chart_id: req.body.chart_id,
noSource: req.body.noSource,
getCache: req.body.getCache,
variables: req.body.variables,
team_id: req.params.team_id,
})
.then((dataset) => {
const newDataset = dataset;
if (newDataset?.data) {
const { data } = newDataset;
if (typeof data === "object" && data instanceof Array) {
newDataset.data = data.slice(0, 20);
} else if (typeof data === "object") {
const resultsKey = [];
Object.keys(data).forEach((key) => {
if (data[key] instanceof Array) {
resultsKey.push(key);
}
});
if (resultsKey.length > 0) {
resultsKey.forEach((resultKey) => {
const slicedArray = data[resultKey].slice(0, 20);
newDataset.data[resultKey] = slicedArray;
});
}
}
}
return res.status(200).send(newDataset);
})
.catch((err) => {
if (err && err.message === "404") {
return res.status(404).send((err && err.message) || err);
}
return res.status(400).send((err && err.message) || err);
});
});
// ----------------------------------------------------
/*
** Route to get the charts associated with the dataset
*/
app.get(`${root}/:dataset_id/charts`, verifyToken, checkPermissions("readAny"), ensureDatasetBelongsToTeam, (req, res) => {
return datasetController.findRelatedCharts(req.params.dataset_id)
.then((charts) => {
return res.status(200).send(charts);
})
.catch((err) => {
return res.status(400).send(err);
});
});
// ----------------------------------------------------
/*
** Route to create a new variable binding
*/
app.post(`${root}/:id/variableBindings`, verifyToken, checkPermissions("updateAny"), ensureDatasetBelongsToTeam, (req, res) => {
return datasetController.createVariableBinding(
req.params.id,
req.body,
)
.then((variableBinding) => {
return res.status(200).send(variableBinding);
});
});
// -------------------------------------------------
/*
** Route to update a variable binding
*/
app.put(`${root}/:id/variableBindings/:variable_id`, verifyToken, checkPermissions("updateAny"), ensureDatasetBelongsToTeam, (req, res) => {
return datasetController.updateVariableBinding(
req.params.id,
req.params.variable_id,
req.body,
)
.then((variableBinding) => {
return res.status(200).send(variableBinding);
});
});
// -------------------------------------------------
/*
** Route to delete a variable binding
*/
app.delete(`${root}/:id/variableBindings/:variable_id`, verifyToken, checkPermissions("deleteAny"), ensureDatasetBelongsToTeam, (req, res) => {
return datasetController.deleteVariableBinding(
req.params.id,
req.params.variable_id,
)
.then((dataset) => {
return res.status(200).send(dataset);
});
});
// -------------------------------------------------
return (req, res, next) => {
next();
};
};