From a30f347c03517cf9e086ddba46f1aaa6ac6dc2bb Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 1 Sep 2017 17:13:23 -0500 Subject: [PATCH] Move events server side (re #245) --- .../dashboards/edit-dashboard-dialog.js | 1 - client/app/components/dashboards/widget.js | 2 -- .../app/pages/admin/outdated-queries/index.js | 3 +- client/app/pages/admin/tasks/index.js | 3 +- client/app/pages/alert/index.js | 2 -- client/app/pages/alerts-list/index.js | 4 +-- client/app/pages/dashboards/dashboard.js | 2 -- client/app/pages/data-sources/list.js | 4 +-- client/app/pages/data-sources/show.js | 10 +------ client/app/pages/destinations/list.js | 4 +-- client/app/pages/destinations/show.js | 6 +--- client/app/pages/groups/data-sources.js | 3 +- client/app/pages/groups/list.js | 3 +- client/app/pages/groups/show.js | 4 +-- .../queries/queries-search-results-page.js | 2 -- client/app/pages/queries/source-view.js | 4 --- client/app/pages/queries/view.js | 1 - client/app/pages/query-snippets/edit.js | 1 - client/app/pages/query-snippets/list.js | 4 +-- client/app/pages/users/list.js | 4 +-- client/app/pages/users/show.js | 3 +- redash/handlers/admin.py | 19 ++++++++++-- redash/handlers/alerts.py | 12 ++++++++ redash/handlers/dashboards.py | 16 ++++++++++ redash/handlers/data_sources.py | 29 ++++++++++++++++++- redash/handlers/destinations.py | 20 ++++++++++++- redash/handlers/groups.py | 18 ++++++++++++ redash/handlers/queries.py | 17 ++++++++++- redash/handlers/query_snippets.py | 10 +++++++ redash/handlers/users.py | 11 ++++++- redash/handlers/visualizations.py | 5 ++++ redash/handlers/widgets.py | 5 ++++ 32 files changed, 169 insertions(+), 63 deletions(-) diff --git a/client/app/components/dashboards/edit-dashboard-dialog.js b/client/app/components/dashboards/edit-dashboard-dialog.js index ab83d7d800..9727353658 100644 --- a/client/app/components/dashboards/edit-dashboard-dialog.js +++ b/client/app/components/dashboards/edit-dashboard-dialog.js @@ -85,7 +85,6 @@ const EditDashboardDialog = { 'Please copy/backup your changes and reload this page.', { autoDismiss: false }); } }); - Events.record('edit', 'dashboard', this.dashboard.id); } else { $http.post('api/dashboards', { name: this.dashboard.name, diff --git a/client/app/components/dashboards/widget.js b/client/app/components/dashboards/widget.js index b44fd1184a..47c04dcdd5 100644 --- a/client/app/components/dashboards/widget.js +++ b/client/app/components/dashboards/widget.js @@ -59,8 +59,6 @@ function DashboardWidgetCtrl($location, $uibModal, $window, Events, currentUser) return; } - Events.record('delete', 'widget', this.widget.id); - this.widget.$delete((response) => { this.dashboard.widgets = this.dashboard.widgets.map(row => row.filter(widget => widget.id !== undefined)); diff --git a/client/app/pages/admin/outdated-queries/index.js b/client/app/pages/admin/outdated-queries/index.js index aa5a54ad5d..e1dfd1e280 100644 --- a/client/app/pages/admin/outdated-queries/index.js +++ b/client/app/pages/admin/outdated-queries/index.js @@ -3,8 +3,7 @@ import moment from 'moment'; import { Paginator } from '@/lib/pagination'; import template from './outdated-queries.html'; -function OutdatedQueriesCtrl($scope, Events, $http, $timeout) { - Events.record('view', 'page', 'admin/outdated_queries'); +function OutdatedQueriesCtrl($scope, $http, $timeout) { $scope.autoUpdate = true; this.queries = new Paginator([], { itemsPerPage: 50 }); diff --git a/client/app/pages/admin/tasks/index.js b/client/app/pages/admin/tasks/index.js index 53d9007ea9..bceb11e53c 100644 --- a/client/app/pages/admin/tasks/index.js +++ b/client/app/pages/admin/tasks/index.js @@ -3,8 +3,7 @@ import moment from 'moment'; import { Paginator } from '@/lib/pagination'; import template from './tasks.html'; -function TasksCtrl($scope, $location, $http, $timeout, Events) { - Events.record('view', 'page', 'admin/tasks'); +function TasksCtrl($scope, $location, $http, $timeout) { $scope.autoUpdate = true; $scope.selectedTab = 'in_progress'; diff --git a/client/app/pages/alert/index.js b/client/app/pages/alert/index.js index bb58280f9f..1b79cc93b1 100644 --- a/client/app/pages/alert/index.js +++ b/client/app/pages/alert/index.js @@ -6,8 +6,6 @@ function AlertCtrl($routeParams, $location, $sce, toastr, currentUser, Query, Ev if (this.alertId === 'new') { Events.record('view', 'page', 'alerts/new'); - } else { - Events.record('view', 'alert', this.alertId); } this.trustAsHtml = html => $sce.trustAsHtml(html); diff --git a/client/app/pages/alerts-list/index.js b/client/app/pages/alerts-list/index.js index 8aa44533c7..4301464cf3 100644 --- a/client/app/pages/alerts-list/index.js +++ b/client/app/pages/alerts-list/index.js @@ -8,9 +8,7 @@ const stateClass = { }; class AlertsListCtrl { - constructor(Events, Alert) { - Events.record('view', 'page', 'alerts'); - + constructor(Alert) { this.alerts = new Paginator([], { itemsPerPage: 20 }); Alert.query((alerts) => { this.alerts.updateRows(alerts.map(alert => ({ diff --git a/client/app/pages/dashboards/dashboard.js b/client/app/pages/dashboards/dashboard.js index 6e76fdbe22..3a79f80acb 100644 --- a/client/app/pages/dashboards/dashboard.js +++ b/client/app/pages/dashboards/dashboard.js @@ -112,7 +112,6 @@ function DashboardCtrl( this.loadDashboard = _.throttle((force) => { this.dashboard = Dashboard.get({ slug: $routeParams.dashboardSlug }, (dashboard) => { - Events.record('view', 'dashboard', dashboard.id); renderDashboard(dashboard, force); }, () => { // error... @@ -133,7 +132,6 @@ function DashboardCtrl( this.archiveDashboard = () => { const archive = () => { - Events.record('archive', 'dashboard', this.dashboard.id); this.dashboard.$delete(() => { $rootScope.$broadcast('reloadDashboards'); }); diff --git a/client/app/pages/data-sources/list.js b/client/app/pages/data-sources/list.js index 497da8a560..a09ce0eb59 100644 --- a/client/app/pages/data-sources/list.js +++ b/client/app/pages/data-sources/list.js @@ -1,8 +1,6 @@ import template from './list.html'; -function DataSourcesCtrl($scope, $location, currentUser, Events, DataSource) { - Events.record('view', 'page', 'admin/data_sources'); - +function DataSourcesCtrl($scope, $location, currentUser, DataSource) { $scope.dataSources = DataSource.query(); } diff --git a/client/app/pages/data-sources/show.js b/client/app/pages/data-sources/show.js index 62f75987ca..ed85aa4882 100644 --- a/client/app/pages/data-sources/show.js +++ b/client/app/pages/data-sources/show.js @@ -5,10 +5,8 @@ const logger = debug('redash:http'); function DataSourceCtrl( $scope, $routeParams, $http, $location, toastr, - currentUser, Events, DataSource, + currentUser, DataSource, ) { - Events.record('view', 'page', 'admin/data_source'); - $scope.dataSourceId = $routeParams.dataSourceId; if ($scope.dataSourceId === 'new') { @@ -24,8 +22,6 @@ function DataSourceCtrl( }); function deleteDataSource() { - Events.record('delete', 'datasource', $scope.dataSource.id); - $scope.dataSource.$delete(() => { toastr.success('Data source deleted successfully.'); $location.path('/data_sources/'); @@ -36,8 +32,6 @@ function DataSourceCtrl( } function testConnection(callback) { - Events.record('test', 'datasource', $scope.dataSource.id); - DataSource.test({ id: $scope.dataSource.id }, (httpResponse) => { if (httpResponse.ok) { toastr.success('Success'); @@ -53,8 +47,6 @@ function DataSourceCtrl( } function getDataSourceVersion(callback) { - Events.record('test', 'data_source_version', $scope.dataSource.id); - DataSource.version({ id: $scope.dataSource.id }, (httpResponse) => { if (httpResponse.ok) { const versionNumber = httpResponse.message; diff --git a/client/app/pages/destinations/list.js b/client/app/pages/destinations/list.js index 8bc73e085f..c6599c2791 100644 --- a/client/app/pages/destinations/list.js +++ b/client/app/pages/destinations/list.js @@ -1,8 +1,6 @@ import template from './list.html'; -function DestinationsCtrl($scope, $location, toastr, currentUser, Events, Destination) { - Events.record('view', 'page', 'admin/destinations'); - +function DestinationsCtrl($scope, $location, toastr, currentUser, Destination) { $scope.destinations = Destination.query(); } diff --git a/client/app/pages/destinations/show.js b/client/app/pages/destinations/show.js index 689240ba8c..277d1501c8 100644 --- a/client/app/pages/destinations/show.js +++ b/client/app/pages/destinations/show.js @@ -6,10 +6,8 @@ const logger = debug('redash:http'); function DestinationCtrl( $scope, $routeParams, $http, $location, toastr, - currentUser, Events, Destination, + currentUser, Destination, ) { - Events.record('view', 'page', 'admin/destination'); - $scope.destinationId = $routeParams.destinationId; if ($scope.destinationId === 'new') { @@ -25,8 +23,6 @@ function DestinationCtrl( }); $scope.delete = () => { - Events.record('delete', 'destination', $scope.destination.id); - $scope.destination.$delete(() => { toastr.success('Destination deleted successfully.'); $location.path('/destinations/'); diff --git a/client/app/pages/groups/data-sources.js b/client/app/pages/groups/data-sources.js index 4c4ccd4791..6cbfe9249f 100644 --- a/client/app/pages/groups/data-sources.js +++ b/client/app/pages/groups/data-sources.js @@ -1,8 +1,7 @@ import { contains } from 'underscore'; import template from './data-sources.html'; -function GroupDataSourcesCtrl($scope, $routeParams, $http, Events, Group, DataSource) { - Events.record('view', 'group_data_sources', $scope.groupId); +function GroupDataSourcesCtrl($scope, $routeParams, $http, Group, DataSource) { $scope.group = Group.get({ id: $routeParams.groupId }); $scope.dataSources = Group.dataSources({ id: $routeParams.groupId }); $scope.newDataSource = {}; diff --git a/client/app/pages/groups/list.js b/client/app/pages/groups/list.js index 94bc83aa0f..1ee987f251 100644 --- a/client/app/pages/groups/list.js +++ b/client/app/pages/groups/list.js @@ -1,8 +1,7 @@ import { Paginator } from '@/lib/pagination'; import template from './list.html'; -function GroupsCtrl($scope, $uibModal, currentUser, Events, Group) { - Events.record('view', 'page', 'groups'); +function GroupsCtrl($scope, $uibModal, currentUser, Group) { $scope.currentUser = currentUser; $scope.groups = new Paginator([], { itemsPerPage: 20 }); Group.query((groups) => { diff --git a/client/app/pages/groups/show.js b/client/app/pages/groups/show.js index 2bb532d4f6..7f8b3e331a 100644 --- a/client/app/pages/groups/show.js +++ b/client/app/pages/groups/show.js @@ -1,9 +1,7 @@ import { contains } from 'underscore'; import template from './show.html'; -function GroupCtrl($scope, $routeParams, $http, currentUser, Events, Group, User) { - Events.record('view', 'group', $scope.groupId); - +function GroupCtrl($scope, $routeParams, $http, currentUser, Group, User) { $scope.currentUser = currentUser; $scope.group = Group.get({ id: $routeParams.groupId }); $scope.members = Group.members({ id: $routeParams.groupId }); diff --git a/client/app/pages/queries/queries-search-results-page.js b/client/app/pages/queries/queries-search-results-page.js index 0518399f31..e45550d7de 100644 --- a/client/app/pages/queries/queries-search-results-page.js +++ b/client/app/pages/queries/queries-search-results-page.js @@ -24,8 +24,6 @@ function QuerySearchCtrl($location, $filter, currentUser, Events, Query) { $location.search({ q: this.term }); } }; - - Events.record('search', 'query', '', { term: this.term }); } export default function init(ngModule) { diff --git a/client/app/pages/queries/source-view.js b/client/app/pages/queries/source-view.js index b703fa358c..7572c73f26 100644 --- a/client/app/pages/queries/source-view.js +++ b/client/app/pages/queries/source-view.js @@ -72,8 +72,6 @@ function QuerySourceCtrl( }; $scope.duplicateQuery = () => { - Events.record('fork', 'query', $scope.query.id); - Query.fork({ id: $scope.query.id }, (newQuery) => { $location.url(newQuery.getSourceLink()).replace(); }); @@ -87,8 +85,6 @@ function QuerySourceCtrl( const confirm = { class: 'btn-danger', title: 'Delete' }; AlertDialog.open(title, message, confirm).then(() => { - Events.record('delete', 'visualization', vis.id); - Visualization.delete({ id: vis.id }, () => { if ($scope.selectedTab === String(vis.id)) { $scope.selectedTab = DEFAULT_TAB; diff --git a/client/app/pages/queries/view.js b/client/app/pages/queries/view.js index eafb826a62..40e45e193b 100644 --- a/client/app/pages/queries/view.js +++ b/client/app/pages/queries/view.js @@ -128,7 +128,6 @@ function QueryViewCtrl( KeyboardShortcuts.unbind(shortcuts); }); - Events.record('view', 'query', $scope.query.id); if ($scope.query.hasResult() || $scope.query.paramsRequired()) { getQueryResult(); } diff --git a/client/app/pages/query-snippets/edit.js b/client/app/pages/query-snippets/edit.js index 22bd85014e..8e870b5c67 100644 --- a/client/app/pages/query-snippets/edit.js +++ b/client/app/pages/query-snippets/edit.js @@ -3,7 +3,6 @@ import template from './edit.html'; function SnippetCtrl($routeParams, $http, $location, toastr, currentUser, Events, QuerySnippet) { this.snippetId = $routeParams.snippetId; - Events.record('view', 'query_snippet', this.snippetId); this.editorOptions = { mode: 'snippets', diff --git a/client/app/pages/query-snippets/list.js b/client/app/pages/query-snippets/list.js index df38ae2d5e..5097293625 100644 --- a/client/app/pages/query-snippets/list.js +++ b/client/app/pages/query-snippets/list.js @@ -1,9 +1,7 @@ import { Paginator } from '@/lib/pagination'; import template from './list.html'; -function SnippetsCtrl($location, currentUser, Events, QuerySnippet) { - Events.record('view', 'page', 'query_snippets'); - +function SnippetsCtrl($location, currentUser, QuerySnippet) { this.snippets = new Paginator([], { itemsPerPage: 20 }); QuerySnippet.query((snippets) => { this.snippets.updateRows(snippets); diff --git a/client/app/pages/users/list.js b/client/app/pages/users/list.js index 4e65311db3..2fdbde17ba 100644 --- a/client/app/pages/users/list.js +++ b/client/app/pages/users/list.js @@ -1,9 +1,7 @@ import { Paginator } from '@/lib/pagination'; import template from './list.html'; -function UsersCtrl(currentUser, Events, User) { - Events.record('view', 'page', 'users'); - +function UsersCtrl(currentUser, User) { this.currentUser = currentUser; this.users = new Paginator([], { itemsPerPage: 20 }); User.query((users) => { diff --git a/client/app/pages/users/show.js b/client/app/pages/users/show.js index e324f62a1a..eeea35b2fb 100644 --- a/client/app/pages/users/show.js +++ b/client/app/pages/users/show.js @@ -3,7 +3,7 @@ import template from './show.html'; function UserCtrl( $scope, $routeParams, $http, $location, toastr, - clientConfig, currentUser, Events, User, + clientConfig, currentUser, User, ) { $scope.userId = $routeParams.userId; $scope.currentUser = currentUser; @@ -13,7 +13,6 @@ function UserCtrl( $scope.userId = currentUser.id; } - Events.record('view', 'user', $scope.userId); $scope.canEdit = currentUser.hasPermission('admin') || currentUser.id === parseInt($scope.userId, 10); $scope.showSettings = false; $scope.showPasswordSettings = false; diff --git a/redash/handlers/admin.py b/redash/handlers/admin.py index 919dc91924..51b0c0ca05 100644 --- a/redash/handlers/admin.py +++ b/redash/handlers/admin.py @@ -1,10 +1,12 @@ import json +import time from flask import request -from flask_login import login_required +from flask_login import current_user, login_required from redash import models, redis_connection +from redash.authentication import current_org from redash.handlers import routes -from redash.handlers.base import json_response +from redash.handlers.base import json_response, record_event from redash.permissions import require_super_admin from redash.tasks.queries import QueryTaskTracker @@ -23,6 +25,13 @@ def outdated_queries(): else: outdated_queries = [] + record_event(current_org, current_user, { + 'action': 'view', + 'object_type': 'api_call', + 'object_id': 'admin/outdated_queries', + 'timestamp': int(time.time()), + }) + return json_response( dict(queries=[q.to_dict(with_stats=True, with_last_modified_by=False) for q in outdated_queries], @@ -41,6 +50,12 @@ def queries_tasks(): waiting = QueryTaskTracker.all(QueryTaskTracker.WAITING_LIST, limit=waiting_limit) in_progress = QueryTaskTracker.all(QueryTaskTracker.IN_PROGRESS_LIST, limit=progress_limit) done = QueryTaskTracker.all(QueryTaskTracker.DONE_LIST, limit=done_limit) + record_event(current_org, current_user, { + 'action': 'view', + 'object_type': 'api_call', + 'object_id': 'admin/tasks', + 'timestamp': int(time.time()), + }) response = { 'waiting': [t.data for t in waiting if t is not None], diff --git a/redash/handlers/alerts.py b/redash/handlers/alerts.py index fa9b0b15fe..43bf5bd12a 100644 --- a/redash/handlers/alerts.py +++ b/redash/handlers/alerts.py @@ -14,6 +14,12 @@ class AlertResource(BaseResource): def get(self, alert_id): alert = get_object_or_404(models.Alert.get_by_id_and_org, alert_id, self.current_org) require_access(alert.groups, self.current_user, view_only) + self.record_event({ + 'action': 'view', + 'timestamp': int(time.time()), + 'object_id': alert.id, + 'object_type': 'alert' + }) return alert.to_dict() def post(self, alert_id): @@ -73,6 +79,12 @@ def post(self): @require_permission('list_alerts') def get(self): + self.record_event({ + 'action': 'view', + 'timestamp': int(time.time()), + 'object_id': 'alerts', + 'object_type': 'api_call' + }) return [alert.to_dict() for alert in models.Alert.all(group_ids=self.current_user.group_ids)] diff --git a/redash/handlers/dashboards.py b/redash/handlers/dashboards.py index 4c5f64c090..30ab41f801 100644 --- a/redash/handlers/dashboards.py +++ b/redash/handlers/dashboards.py @@ -110,6 +110,12 @@ def get(self, dashboard_slug=None): response['can_edit'] = can_modify(dashboard, self.current_user) + self.record_event({ + 'action': 'view', + 'object_id': dashboard.id, + 'object_type': 'dashboard', + }) + return response @require_permission('edit_dashboard') @@ -158,6 +164,11 @@ def post(self, dashboard_slug): abort(400) result = dashboard.to_dict(with_widgets=True, user=self.current_user) + self.record_event({ + 'action': 'edit', + 'object_id': dashboard.id, + 'object_type': 'dashboard', + }) return result @require_permission('edit_dashboard') @@ -175,6 +186,11 @@ def delete(self, dashboard_slug): models.db.session.add(dashboard) d = dashboard.to_dict(with_widgets=True, user=self.current_user) models.db.session.commit() + self.record_event({ + 'action': 'archive', + 'object_id': dashboard.id, + 'object_type': 'dashboard', + }) return d diff --git a/redash/handlers/data_sources.py b/redash/handlers/data_sources.py index 14e53a0bb1..073c9cdccb 100644 --- a/redash/handlers/data_sources.py +++ b/redash/handlers/data_sources.py @@ -25,7 +25,13 @@ class DataSourceResource(BaseResource): @require_admin def get(self, data_source_id): data_source = models.DataSource.get_by_id_and_org(data_source_id, self.current_org) - return data_source.to_dict(all=True) + ds = data_source.to_dict(all=True) + self.record_event({ + 'action': 'view', + 'object_id': data_source.id, + 'object_type': 'data_source', + }) + return ds @require_admin def post(self, data_source_id): @@ -59,6 +65,11 @@ def post(self, data_source_id): def delete(self, data_source_id): data_source = models.DataSource.get_by_id_and_org(data_source_id, self.current_org) data_source.delete() + self.record_event({ + 'action': 'delete', + 'object_id': data_source_id, + 'object_type': 'datasource', + }) return make_response('', 204) @@ -83,6 +94,11 @@ def get(self): except AttributeError: logging.exception("Error with DataSource#to_dict (data source id: %d)", ds.id) + self.record_event({ + 'action': 'view', + 'object_id': 'admin/data_sources', + 'object_type': 'api_call', + }) return sorted(response.values(), key=lambda d: d['id']) @require_admin @@ -172,6 +188,12 @@ class DataSourceTestResource(BaseResource): def post(self, data_source_id): data_source = get_object_or_404(models.DataSource.get_by_id_and_org, data_source_id, self.current_org) + self.record_event({ + 'action': 'test', + 'object_id': data_source_id, + 'object_type': 'datasource', + }) + try: data_source.query_runner.test_connection() except Exception as e: @@ -183,6 +205,11 @@ class DataSourceVersionResource(BaseResource): def get(self, data_source_id): data_source = get_object_or_404(models.DataSource.get_by_id_and_org, data_source_id, self.current_org) require_access(data_source.groups, self.current_user, view_only) + self.record_event({ + 'action': 'test', + 'object_id': data_source_id, + 'object_type': 'data_source_version', + }) try: version_info = data_source.query_runner.get_data_source_version() except Exception as e: diff --git a/redash/handlers/destinations.py b/redash/handlers/destinations.py index c1895b7321..254e51f078 100644 --- a/redash/handlers/destinations.py +++ b/redash/handlers/destinations.py @@ -19,7 +19,13 @@ class DestinationResource(BaseResource): @require_admin def get(self, destination_id): destination = models.NotificationDestination.get_by_id_and_org(destination_id, self.current_org) - return destination.to_dict(all=True) + d = destination.to_dict(all=True) + self.record_event({ + 'action': 'view', + 'object_id': destination_id, + 'object_type': 'destination' + }) + return d @require_admin def post(self, destination_id): @@ -48,6 +54,12 @@ def delete(self, destination_id): models.db.session.delete(destination) models.db.session.commit() + self.record_event({ + 'action': 'delete', + 'object_id': destination_id, + 'object_type': 'destination', + }) + return make_response('', 204) @@ -63,6 +75,12 @@ def get(self): d = ds.to_dict() response[ds.id] = d + self.record_event({ + 'action': 'view', + 'object_id': 'admin/destinations', + 'object_type': 'api_call', + }) + return response.values() @require_admin diff --git a/redash/handlers/groups.py b/redash/handlers/groups.py index 7790044468..ba72346b10 100644 --- a/redash/handlers/groups.py +++ b/redash/handlers/groups.py @@ -30,6 +30,12 @@ def get(self): groups = models.Group.query.filter( models.Group.id.in_(self.current_user.group_ids)) + self.record_event({ + 'action': 'view', + 'object_id': 'groups', + 'object_type': 'api_call', + }) + return [g.to_dict() for g in groups] @@ -59,6 +65,12 @@ def get(self, group_id): group = models.Group.get_by_id_and_org(group_id, self.current_org) + self.record_event({ + 'action': 'view', + 'object_id': group_id, + 'object_type': 'group', + }) + return group.to_dict() @require_admin @@ -154,6 +166,12 @@ def get(self, group_id): data_sources = (models.DataSource.query .join(models.DataSourceGroup) .filter(models.DataSourceGroup.group == group)) + + self.record_event({ + 'action': 'view', + 'object_id': group_id, + 'object_type': 'group_data_sources', + }) return [ds.to_dict(with_permissions_for=group) for ds in data_sources] diff --git a/redash/handlers/queries.py b/redash/handlers/queries.py index 8215c6dea6..df5a56d8a5 100644 --- a/redash/handlers/queries.py +++ b/redash/handlers/queries.py @@ -45,7 +45,11 @@ def get(self): """ term = request.args.get('q', '') include_drafts = request.args.get('include_drafts') is not None - + self.record_event({ + 'action': 'search', + 'object_id': term, + 'object_type': 'query', + }) return [q.to_dict(with_last_modified_by=False) for q in models.Query.search(term, self.current_user.group_ids, include_drafts=include_drafts)] @@ -229,6 +233,12 @@ def get(self, query_id): result = q.to_dict(with_visualizations=True) result['can_edit'] = can_modify(q, self.current_user) + + self.record_event({ + 'action': 'view', + 'object_id': query_id, + 'object_type': 'query', + }) return result # TODO: move to resource of its own? (POST /queries/{id}/archive) @@ -258,6 +268,11 @@ def post(self, query_id): require_access(query.data_source.groups, self.current_user, not_view_only) forked_query = query.fork(self.current_user) models.db.session.commit() + self.record_event({ + 'action': 'fork', + 'object_id': query_id, + 'object_type': 'query', + }) return forked_query.to_dict(with_visualizations=True) diff --git a/redash/handlers/query_snippets.py b/redash/handlers/query_snippets.py index fc74865771..fbc6a2871c 100644 --- a/redash/handlers/query_snippets.py +++ b/redash/handlers/query_snippets.py @@ -11,6 +11,11 @@ class QuerySnippetResource(BaseResource): def get(self, snippet_id): snippet = get_object_or_404(models.QuerySnippet.get_by_id_and_org, snippet_id, self.current_org) + self.record_event({ + 'action': 'view', + 'object_id': snippet_id, + 'object_type': 'query_snippet', + }) return snippet.to_dict() def post(self, snippet_id): @@ -69,5 +74,10 @@ def post(self): return snippet.to_dict() def get(self): + self.record_event({ + 'action': 'view', + 'object_id': 'query_snippets', + 'object_type': 'api_call', + }) return [snippet.to_dict() for snippet in models.QuerySnippet.all(org=self.current_org)] diff --git a/redash/handlers/users.py b/redash/handlers/users.py index ea8ed73158..65ee9e8989 100644 --- a/redash/handlers/users.py +++ b/redash/handlers/users.py @@ -21,6 +21,11 @@ def invite_user(org, inviter, user): class UserListResource(BaseResource): @require_permission('list_users') def get(self): + self.record_event({ + 'action': 'view', + 'object_id': 'users', + 'object_type': 'api_call', + }) return [u.to_dict() for u in models.User.all(self.current_org)] @require_admin @@ -87,7 +92,11 @@ class UserResource(BaseResource): def get(self, user_id): require_permission_or_owner('list_users', user_id) user = get_object_or_404(models.User.get_by_id_and_org, user_id, self.current_org) - + self.record_event({ + 'action': 'view', + 'object_id': user_id, + 'object_type': 'user', + }) return user.to_dict(with_api_key=is_admin_or_owner(user_id)) def post(self, user_id): diff --git a/redash/handlers/visualizations.py b/redash/handlers/visualizations.py index 2af1e60852..52a958193c 100644 --- a/redash/handlers/visualizations.py +++ b/redash/handlers/visualizations.py @@ -49,5 +49,10 @@ def post(self, visualization_id): def delete(self, visualization_id): vis = get_object_or_404(models.Visualization.get_by_id_and_org, visualization_id, self.current_org) require_object_modify_permission(vis.query_rel, self.current_user) + self.record_event({ + 'action': 'delete', + 'object_id': visualization_id, + 'object_type': 'visualization', + }) models.db.session.delete(vis) models.db.session.commit() diff --git a/redash/handlers/widgets.py b/redash/handlers/widgets.py index 22acd9cfe0..31e1648998 100644 --- a/redash/handlers/widgets.py +++ b/redash/handlers/widgets.py @@ -99,4 +99,9 @@ def delete(self, widget_id): require_object_modify_permission(widget.dashboard, self.current_user) widget.delete() models.db.session.commit() + self.record_event({ + 'action': 'delete', + 'object_id': widget_id, + 'object_type': 'widget', + }) return {'layout': widget.dashboard.layout, 'version': widget.dashboard.version}