From a9c514aaf7d5d9a244988011d8fda05f30fbc064 Mon Sep 17 00:00:00 2001 From: Omer Lachish Date: Wed, 23 Jan 2019 11:10:04 +0200 Subject: [PATCH] Textless query result endpoint (#3311) * add an endpoint for running a query by its id and (optional) parameters without having to provide the query text * check for access to query before running it --- redash/handlers/api.py | 3 ++- redash/handlers/query_results.py | 23 +++++++++++++++++++++++ tests/handlers/test_query_results.py | 8 ++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/redash/handlers/api.py b/redash/handlers/api.py index 23225a7b5d..140c37ce83 100644 --- a/redash/handlers/api.py +++ b/redash/handlers/api.py @@ -6,7 +6,7 @@ from redash.handlers.base import org_scoped_rule from redash.handlers.permissions import ObjectPermissionsListResource, CheckPermissionResource from redash.handlers.alerts import AlertResource, AlertListResource, AlertSubscriptionListResource, AlertSubscriptionResource -from redash.handlers.dashboards import DashboardListResource, DashboardResource, DashboardShareResource, PublicDashboardResource +from redash.handlers.dashboards import DashboardListResource, DashboardResource, DashboardShareResource, PublicDashboardResource from redash.handlers.data_sources import DataSourceTypeListResource, DataSourceListResource, DataSourceSchemaResource, DataSourceResource, DataSourcePauseResource, DataSourceTestResource from redash.handlers.events import EventsResource from redash.handlers.queries import QueryForkResource, QueryRefreshResource, QueryListResource, QueryRecentResource, QuerySearchResource, QueryResource, MyQueriesResource @@ -92,6 +92,7 @@ def json_representation(data, code, headers=None): api.add_org_resource(QueryResultResource, '/api/query_results/.', '/api/query_results/', + '/api/queries//results', '/api/queries//results.', '/api/queries//results/.', endpoint='query_result') diff --git a/redash/handlers/query_results.py b/redash/handlers/query_results.py index d7cca6318e..575f576653 100644 --- a/redash/handlers/query_results.py +++ b/redash/handlers/query_results.py @@ -154,6 +154,29 @@ def options(self, query_id=None, query_result_id=None, filetype='json'): return make_response("", 200, headers) + @require_permission('execute_query') + def post(self, query_id): + """ + Execute a saved query. + + :param number query_id: The ID of the query whose results should be fetched. + :param object parameters: The parameter values to apply to the query. + :qparam number max_age: If query results less than `max_age` seconds old are available, + return them, otherwise execute the query; if omitted or -1, returns + any cached result, or executes if not available. Set to zero to + always execute. + """ + params = request.get_json(force=True) + parameters = params.get('parameters', {}) + max_age = int(params.get('max_age', 0)) + + query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org) + + if not has_access(query.data_source.groups, self.current_user, not_view_only): + return {'job': {'status': 4, 'error': 'You do not have permission to run queries with this data source.'}}, 403 + else: + return run_query(query.data_source, parameters, query.query_text, query_id, max_age) + @require_permission('view_query') def get(self, query_id=None, query_result_id=None, filetype='json'): """ diff --git a/tests/handlers/test_query_results.py b/tests/handlers/test_query_results.py index 4517cc6256..8a65233ef6 100644 --- a/tests/handlers/test_query_results.py +++ b/tests/handlers/test_query_results.py @@ -128,6 +128,14 @@ def test_has_full_access_to_data_source(self): rv = self.make_request('get', '/api/query_results/{}'.format(query_result.id)) self.assertEquals(rv.status_code, 200) + def test_execute_new_query(self): + query = self.factory.create_query() + + rv = self.make_request('post', '/api/queries/{}/results'.format(query.id), data={'parameters': {}}) + + self.assertEquals(rv.status_code, 200) + self.assertIn('job', rv.json) + def test_access_with_query_api_key(self): ds = self.factory.create_data_source(group=self.factory.org.default_group, view_only=False) query = self.factory.create_query()