From fefcb928da85873a240c206a7e201724b1652a84 Mon Sep 17 00:00:00 2001 From: Alexander Leibzon Date: Tue, 27 Sep 2016 19:13:44 +0300 Subject: [PATCH 001/243] add memsql as datasource --- Vagrantfile | 2 +- redash/models.py | 2 +- redash/query_runner/memsql_ds.py | 147 +++++++++++++++++++++ redash/settings.py | 1 + requirements.txt | 2 + requirements_all_ds.txt | 1 + setup/ubuntu/files/redash_supervisord_init | 0 7 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 redash/query_runner/memsql_ds.py mode change 100644 => 100755 setup/ubuntu/files/redash_supervisord_init diff --git a/Vagrantfile b/Vagrantfile index 875cca8fa6..1611006f3d 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -10,6 +10,6 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.network "forwarded_port", guest: 5000, host: 9001 config.vm.provision "shell" do |s| s.inline = "/opt/redash/current/setup/vagrant/provision.sh" - s.privileged = false + s.privileged = true end end diff --git a/redash/models.py b/redash/models.py index c9ca984b9a..5df37508a5 100644 --- a/redash/models.py +++ b/redash/models.py @@ -1225,7 +1225,7 @@ def to_dict(self): return d -all_models = (Organization, Group, DataSource, DataSourceGroup, User, QueryResult, Query, Alert, Dashboard, Visualization, Widget, Event, NotificationDestination, AlertSubscription, ApiKey) +all_models = (Organization, Group, DataSource, DataSourceGroup, User, QueryResult, Query, Alert, Dashboard, Visualization, Widget, Event, NotificationDestination, AlertSubscription, ApiKey, QuerySnippet) def init_db(): diff --git a/redash/query_runner/memsql_ds.py b/redash/query_runner/memsql_ds.py new file mode 100644 index 0000000000..14e7f4d119 --- /dev/null +++ b/redash/query_runner/memsql_ds.py @@ -0,0 +1,147 @@ +import json +import logging +import sys + +from redash.query_runner import * +from redash.utils import JSONEncoder + +logger = logging.getLogger(__name__) + +try: + from memsql.common import database + enabled = True +except ImportError, e: + logger.warning(e) + enabled = False + +COLUMN_NAME = 0 +COLUMN_TYPE = 1 + +types_map = { + 'BIGINT': TYPE_INTEGER, + 'TINYINT': TYPE_INTEGER, + 'SMALLINT': TYPE_INTEGER, + 'MEDIUMINT': TYPE_INTEGER, + 'INT': TYPE_INTEGER, + 'DOUBLE': TYPE_FLOAT, + 'DECIMAL': TYPE_FLOAT, + 'FLOAT': TYPE_FLOAT, + 'REAL': TYPE_FLOAT, + 'BOOL': TYPE_BOOLEAN, + 'BOOLEAN': TYPE_BOOLEAN, + 'TIMESTAMP': TYPE_DATETIME, + 'DATETIME': TYPE_DATETIME, + 'DATE': TYPE_DATETIME, + 'JSON': TYPE_STRING, + 'CHAR': TYPE_STRING, + 'VARCHAR': TYPE_STRING +} + + +class MemSQL(BaseSQLQueryRunner): + @classmethod + def configuration_schema(cls): + return { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "user": { + "type": "string" + }, + "password": { + "type": "string" + } + + }, + "required": ["host", "port"] + } + + @classmethod + def annotate_query(cls): + return False + + @classmethod + def type(cls): + return "memsql" + + @classmethod + def enabled(cls): + return enabled + + def __init__(self, configuration): + super(MemSQL, self).__init__(configuration) + + def _get_tables(self, schema): + try: + schemas_query = "show schemas" + + tables_query = "show tables in %s" + + columns_query = "show columns in %s" + + for schema_name in filter(lambda a: len(a) > 0, map(lambda a: str(a['Database']), self._run_query_internal(schemas_query))): + for table_name in filter(lambda a: len(a) > 0, map(lambda a: str(a['Tables_in_%s' % schema_name]), self._run_query_internal(tables_query % schema_name))): + columns = filter(lambda a: len(a) > 0, map(lambda a: str(a['Field']), self._run_query_internal(columns_query % table_name))) + + schema[table_name] = {'name': table_name, 'columns': columns} + except Exception, e: + raise sys.exc_info()[1], None, sys.exc_info()[2] + return schema.values() + + def run_query(self, query): + + cursor = None + try: + cursor = database.connect(**self.configuration.to_dict()) + + res = cursor.query(query) + # column_names = [] + # columns = [] + # + # for column in cursor.description: + # column_name = column[COLUMN_NAME] + # column_names.append(column_name) + # + # columns.append({ + # 'name': column_name, + # 'friendly_name': column_name, + # 'type': types_map.get(column[COLUMN_TYPE], None) + # }) + + rows = [dict(zip(list(row.keys()), list(row.values()))) for row in res] + + + #==================================================================================================== + #temporary - until https://github.com/memsql/memsql-python/pull/8 gets merged + #==================================================================================================== + columns = [] + column_names = rows[0].keys() if rows else None + for column in column_names: + columns.append({ + 'name': column, + 'friendly_name': column, + 'type': None + }) + + data = {'columns': columns, 'rows': rows} + json_data = json.dumps(data, cls=JSONEncoder) + error = None + except KeyboardInterrupt: + cursor.close() + error = "Query cancelled by user." + json_data = None + except Exception as e: + logging.exception(e) + raise sys.exc_info()[1], None, sys.exc_info()[2] + finally: + if cursor: + cursor.close() + + return json_data, error + +register(MemSQL) diff --git a/redash/settings.py b/redash/settings.py index 9725d9164f..5b1454df48 100644 --- a/redash/settings.py +++ b/redash/settings.py @@ -175,6 +175,7 @@ def all_settings(): 'redash.query_runner.sqlite', 'redash.query_runner.dynamodb_sql', 'redash.query_runner.mssql', + 'redash.query_runner.memsql_ds', ] enabled_query_runners = array_from_string(os.environ.get("REDASH_ENABLED_QUERY_RUNNERS", ",".join(default_query_runners))) diff --git a/requirements.txt b/requirements.txt index 0a348ea924..d46a454d3c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -40,3 +40,5 @@ xlsxwriter==0.8.4 pystache==0.5.4 parsedatetime==2.1 cryptography==1.4 +oauthlib==2.0.0 +WTForms==2.1 diff --git a/requirements_all_ds.txt b/requirements_all_ds.txt index 674567e3b0..5b16cbaca8 100644 --- a/requirements_all_ds.txt +++ b/requirements_all_ds.txt @@ -17,3 +17,4 @@ sasl>=0.1.3 thrift>=0.8.0 thrift_sasl>=0.1.0 cassandra-driver==3.1.1 +memsql==2.16.0 diff --git a/setup/ubuntu/files/redash_supervisord_init b/setup/ubuntu/files/redash_supervisord_init old mode 100644 new mode 100755 From ba0daa218e1c61af9632c4d18298367ae75b3fa1 Mon Sep 17 00:00:00 2001 From: hamza zia Date: Sun, 15 Jan 2017 20:36:25 +0500 Subject: [PATCH 002/243] JSON API that accepts params --- redash/handlers/embed.py | 45 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/redash/handlers/embed.py b/redash/handlers/embed.py index db87786845..04fa353326 100644 --- a/redash/handlers/embed.py +++ b/redash/handlers/embed.py @@ -53,7 +53,7 @@ def run_query_sync(data_source, parameter_values, query_text, max_age=0): # update cache if max_age > 0: run_time = time.time() - started_at - query_result, updated_query_ids = models.QueryResult.store_result(data_source.org_id, data_source.id, + query_result, updated_query_ids = models.QueryResult.store_result(data_source.org, data_source, query_hash, query_text, data, run_time, utils.utcnow()) @@ -84,6 +84,49 @@ def embed(query_id, visualization_id, org_slug=None): return send_file(full_path, **dict(cache_timeout=0, conditional=True)) +@routes.route(org_scoped_rule('/embed/query//json'), methods=['GET']) +@login_required +def embedjson(query_id, org_slug=None): + visualizations=1 + query = models.Query.get_by_id_and_org(query_id, current_org) + require_access(query.groups, current_user, view_only) + qr = {} + + parameter_values = collect_parameters_from_request(request.args) + + qr = query.latest_query_data + if len(parameter_values) > 0: + # run parameterized query + # + # WARNING: Note that the external query parameters + # are a potential risk of SQL injections. + # + max_age = int(request.args.get('maxAge', 0)) + results = run_query_sync(query.data_source, parameter_values, query.to_dict()['query'], max_age=max_age) + + if results is None: + abort(400, message="Unable to get results for this query") + else: + qr = {"data": json.loads(results)} + elif qr is None: + abort(400, message="No Results for this query") + else: + qr = qr.to_dict() + + record_event(current_org, current_user, { + 'action': 'view', + 'query_id': query_id, + 'embed': True, + 'referer': request.headers.get('Referer') + }) + + client_config = {} + client_config.update(settings.COMMON_CLIENT_CONFIG) + qr = project(qr, ('data', 'id', 'retrieved_at')) + return json_dumps(qr),200,{'Content-Type': 'application/json; charset=utf-8'} + + + @routes.route(org_scoped_rule('/public/dashboards/'), methods=['GET']) @login_required def public_dashboard(token, org_slug=None): From ce65578c72b3e4e56d837bb44f0d3747177f158b Mon Sep 17 00:00:00 2001 From: hamza zia Date: Sun, 15 Jan 2017 22:23:18 +0500 Subject: [PATCH 003/243] allowed embeds to recieve params --- .../app/pages/queries/visualization-embed.js | 3 +- redash/handlers/embed.py | 98 +------------------ redash/handlers/query_results.py | 75 +++++++++++++- 3 files changed, 77 insertions(+), 99 deletions(-) diff --git a/client/app/pages/queries/visualization-embed.js b/client/app/pages/queries/visualization-embed.js index 6aa5160e42..380e75ca35 100644 --- a/client/app/pages/queries/visualization-embed.js +++ b/client/app/pages/queries/visualization-embed.js @@ -33,7 +33,8 @@ export default function (ngModule) { return session($http, $route, Auth).then(() => { const queryId = $route.current.params.queryId; const query = $http.get(`/api/queries/${queryId}`).then(response => response.data); - const queryResult = $http.get(`/api/queries/${queryId}/results.json`).then(response => response.data); + // pass down any params from the top level url + const queryResult = $http.post(`/api/queries/${queryId}/results.json${location.search}`).then(response => response.data); return $q.all([query, queryResult]); }); } diff --git a/redash/handlers/embed.py b/redash/handlers/embed.py index 04fa353326..f241e0b789 100644 --- a/redash/handlers/embed.py +++ b/redash/handlers/embed.py @@ -1,5 +1,4 @@ import json -import logging import time import pystache @@ -12,60 +11,11 @@ from redash.handlers import routes from redash.handlers.base import (get_object_or_404, org_scoped_rule, record_event) -from redash.handlers.query_results import collect_query_parameters +from redash.handlers.query_results import collect_query_parameters, run_query_sync from redash.permissions import require_access, view_only -from redash.utils import (collect_parameters_from_request, gen_query_hash, - json_dumps) +from redash.utils import (collect_parameters_from_request, json_dumps) -# -# Run a parameterized query synchronously and return the result -# DISCLAIMER: Temporary solution to support parameters in queries. Should be -# removed once we refactor the query results API endpoints and handling -# on the client side. Please don't reuse in other API handlers. -# -def run_query_sync(data_source, parameter_values, query_text, max_age=0): - query_parameters = set(collect_query_parameters(query_text)) - missing_params = set(query_parameters) - set(parameter_values.keys()) - if missing_params: - raise Exception('Missing parameter value for: {}'.format(", ".join(missing_params))) - - if query_parameters: - query_text = pystache.render(query_text, parameter_values) - - if max_age <= 0: - query_result = None - else: - query_result = models.QueryResult.get_latest(data_source, query_text, max_age) - - query_hash = gen_query_hash(query_text) - - if query_result: - logging.info("Returning cached result for query %s" % query_hash) - return query_result.data - - try: - started_at = time.time() - data, error = data_source.query_runner.run_query(query_text, current_user) - - if error: - return None - # update cache - if max_age > 0: - run_time = time.time() - started_at - query_result, updated_query_ids = models.QueryResult.store_result(data_source.org, data_source, - query_hash, query_text, data, - run_time, utils.utcnow()) - - models.db.session.commit() - return data - except Exception, e: - if max_age > 0: - abort(404, message="Unable to get result from the database, and no cached query result found.") - else: - abort(503, message="Unable to get result from the database.") - return None - @routes.route(org_scoped_rule('/embed/query//visualization/'), methods=['GET']) @login_required @@ -83,50 +33,6 @@ def embed(query_id, visualization_id, org_slug=None): models.db.session.commit() return send_file(full_path, **dict(cache_timeout=0, conditional=True)) - -@routes.route(org_scoped_rule('/embed/query//json'), methods=['GET']) -@login_required -def embedjson(query_id, org_slug=None): - visualizations=1 - query = models.Query.get_by_id_and_org(query_id, current_org) - require_access(query.groups, current_user, view_only) - qr = {} - - parameter_values = collect_parameters_from_request(request.args) - - qr = query.latest_query_data - if len(parameter_values) > 0: - # run parameterized query - # - # WARNING: Note that the external query parameters - # are a potential risk of SQL injections. - # - max_age = int(request.args.get('maxAge', 0)) - results = run_query_sync(query.data_source, parameter_values, query.to_dict()['query'], max_age=max_age) - - if results is None: - abort(400, message="Unable to get results for this query") - else: - qr = {"data": json.loads(results)} - elif qr is None: - abort(400, message="No Results for this query") - else: - qr = qr.to_dict() - - record_event(current_org, current_user, { - 'action': 'view', - 'query_id': query_id, - 'embed': True, - 'referer': request.headers.get('Referer') - }) - - client_config = {} - client_config.update(settings.COMMON_CLIENT_CONFIG) - qr = project(qr, ('data', 'id', 'retrieved_at')) - return json_dumps(qr),200,{'Content-Type': 'application/json; charset=utf-8'} - - - @routes.route(org_scoped_rule('/public/dashboards/'), methods=['GET']) @login_required def public_dashboard(token, org_slug=None): diff --git a/redash/handlers/query_results.py b/redash/handlers/query_results.py index d7ca759df3..df9a2cd907 100644 --- a/redash/handlers/query_results.py +++ b/redash/handlers/query_results.py @@ -1,4 +1,5 @@ import csv +import logging import json import cStringIO import time @@ -12,7 +13,7 @@ from redash.tasks import QueryTask, record_event from redash.permissions import require_permission, not_view_only, has_access, require_access, view_only from redash.handlers.base import BaseResource, get_object_or_404 -from redash.utils import collect_query_parameters, collect_parameters_from_request +from redash.utils import collect_query_parameters, collect_parameters_from_request, gen_query_hash from redash.tasks.queries import enqueue_query @@ -20,6 +21,55 @@ def error_response(message): return {'job': {'status': 4, 'error': message}}, 400 +# +# Run a parameterized query synchronously and return the result +# DISCLAIMER: Temporary solution to support parameters in queries. Should be +# removed once we refactor the query results API endpoints and handling +# on the client side. Please don't reuse in other API handlers. +# +def run_query_sync(data_source, parameter_values, query_text, max_age=0): + query_parameters = set(collect_query_parameters(query_text)) + missing_params = set(query_parameters) - set(parameter_values.keys()) + if missing_params: + raise Exception('Missing parameter value for: {}'.format(", ".join(missing_params))) + + if query_parameters: + query_text = pystache.render(query_text, parameter_values) + + if max_age <= 0: + query_result = None + else: + query_result = models.QueryResult.get_latest(data_source, query_text, max_age) + + query_hash = gen_query_hash(query_text) + + if query_result: + logging.info("Returning cached result for query %s" % query_hash) + return query_result + + try: + started_at = time.time() + data, error = data_source.query_runner.run_query(query_text, current_user) + + if error: + logging.info('got bak error') + logging.info(error) + return None + + run_time = time.time() - started_at + query_result, updated_query_ids = models.QueryResult.store_result(data_source.org, data_source, + query_hash, query_text, data, + run_time, utils.utcnow()) + + models.db.session.commit() + return query_result + except Exception, e: + if max_age > 0: + abort(404, message="Unable to get result from the database, and no cached query result found.") + else: + abort(503, message="Unable to get result from the database.") + return None + def run_query(data_source, parameter_values, query_text, query_id, max_age=0): query_parameters = set(collect_query_parameters(query_text)) missing_params = set(query_parameters) - set(parameter_values.keys()) @@ -100,6 +150,28 @@ def options(self, query_id=None, query_result_id=None, filetype='json'): return make_response("", 200, headers) + @require_permission('view_query') + def post(self, query_id=None, filetype='json'): + # This method gets a cached version of query, or runs it in sync with params + if query_id is not None: + query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org) + require_access(query.groups, current_user, view_only) + + parameter_values = collect_parameters_from_request(request.args) + max_age = int(request.args.get('maxAge', 0)) + query_result = run_query_sync(query.data_source, parameter_values, query.to_dict()['query'], max_age=max_age) + + if filetype == 'json': + response = self.make_json_response(query_result) + elif filetype == 'xlsx': + response = self.make_excel_response(query_result) + else: + response = self.make_csv_response(query_result) + + return response + else: + abort(404, message='Query not given.') + @require_permission('view_query') def get(self, query_id=None, query_result_id=None, filetype='json'): # TODO: @@ -209,4 +281,3 @@ def get(self, job_id): def delete(self, job_id): job = QueryTask(job_id=job_id) job.cancel() - From eeee592abca61a987f11aaffd9577c254b6fd31b Mon Sep 17 00:00:00 2001 From: hamza zia Date: Fri, 3 Feb 2017 09:34:01 -0800 Subject: [PATCH 004/243] nuke separate endpiont for running queries in sync --- .../app/pages/queries/visualization-embed.js | 3 +- redash/handlers/query_results.py | 41 ++++++------------- 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/client/app/pages/queries/visualization-embed.js b/client/app/pages/queries/visualization-embed.js index 380e75ca35..81e3a21186 100644 --- a/client/app/pages/queries/visualization-embed.js +++ b/client/app/pages/queries/visualization-embed.js @@ -33,8 +33,7 @@ export default function (ngModule) { return session($http, $route, Auth).then(() => { const queryId = $route.current.params.queryId; const query = $http.get(`/api/queries/${queryId}`).then(response => response.data); - // pass down any params from the top level url - const queryResult = $http.post(`/api/queries/${queryId}/results.json${location.search}`).then(response => response.data); + const queryResult = $http.get(`/api/queries/${queryId}/results.json${location.search}`).then(response => response.data); return $q.all([query, queryResult]); }); } diff --git a/redash/handlers/query_results.py b/redash/handlers/query_results.py index df9a2cd907..97851220b1 100644 --- a/redash/handlers/query_results.py +++ b/redash/handlers/query_results.py @@ -150,28 +150,6 @@ def options(self, query_id=None, query_result_id=None, filetype='json'): return make_response("", 200, headers) - @require_permission('view_query') - def post(self, query_id=None, filetype='json'): - # This method gets a cached version of query, or runs it in sync with params - if query_id is not None: - query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org) - require_access(query.groups, current_user, view_only) - - parameter_values = collect_parameters_from_request(request.args) - max_age = int(request.args.get('maxAge', 0)) - query_result = run_query_sync(query.data_source, parameter_values, query.to_dict()['query'], max_age=max_age) - - if filetype == 'json': - response = self.make_json_response(query_result) - elif filetype == 'xlsx': - response = self.make_excel_response(query_result) - else: - response = self.make_csv_response(query_result) - - return response - else: - abort(404, message='Query not given.') - @require_permission('view_query') def get(self, query_id=None, query_result_id=None, filetype='json'): # TODO: @@ -179,15 +157,22 @@ def get(self, query_id=None, query_result_id=None, filetype='json'): # They need to be split, as they have different logic (for example, retrieving by query id # should check for query parameters and shouldn't cache the result). should_cache = query_result_id is not None - if query_result_id is None and query_id is not None: - query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org) - if query: - query_result_id = query.latest_query_data_id + + parameter_values = collect_parameters_from_request(request.args) + max_age = int(request.args.get('maxAge', 0)) + + query_result = None if query_result_id: query_result = get_object_or_404(models.QueryResult.get_by_id_and_org, query_result_id, self.current_org) - else: - query_result = None + elif query_id is not None: + query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org) + + if query is not None: + if settings.ALLOW_PARAMETERS_IN_EMBEDS: + query_result = run_query_sync(query.data_source, parameter_values, query.to_dict()['query'], max_age=max_age) + elif query.latest_query_data_id is not None: + query_result = get_object_or_404(models.QueryResult.get_by_id_and_org, query.latest_query_data_id, self.current_org) if query_result: require_access(query_result.data_source.groups, self.current_user, view_only) From ac538c35e90904375c16245a9a5c32d38ee137bc Mon Sep 17 00:00:00 2001 From: deecay Date: Mon, 20 Feb 2017 15:09:48 +0900 Subject: [PATCH 005/243] Add: option to hide pivot table controls --- client/app/visualizations/pivot/index.js | 80 ++++++++++++------- .../pivot/pivottable-editor.html | 9 +++ 2 files changed, 59 insertions(+), 30 deletions(-) create mode 100644 client/app/visualizations/pivot/pivottable-editor.html diff --git a/client/app/visualizations/pivot/index.js b/client/app/visualizations/pivot/index.js index e723e4d0bb..2fe72dc9d7 100644 --- a/client/app/visualizations/pivot/index.js +++ b/client/app/visualizations/pivot/index.js @@ -2,6 +2,9 @@ import $ from 'jquery'; import 'pivottable'; import 'pivottable/dist/pivot.css'; +import editorTemplate from './pivottable-editor.html'; + + function pivotTableRenderer() { return { restrict: 'E', @@ -12,48 +15,65 @@ function pivotTableRenderer() { template: '', replace: false, link($scope, element) { - $scope.$watch('queryResult && queryResult.getData()', (data) => { - if (!data) { - return; - } + function updatePivot() { + $scope.$watch('queryResult && queryResult.getData()', (data) => { + if (!data) { + return; + } - if ($scope.queryResult.getData() !== null) { - // We need to give the pivot table its own copy of the data, because it changes - // it which interferes with other visualizations. - data = $.extend(true, [], $scope.queryResult.getRawData()); - const options = { - renderers: $.pivotUtilities.renderers, - onRefresh(config) { - const configCopy = Object.assign({}, config); - // delete some values which are functions - delete configCopy.aggregators; - delete configCopy.renderers; - delete configCopy.onRefresh; - // delete some bulky default values - delete configCopy.rendererOptions; - delete configCopy.localeStrings; + if ($scope.queryResult.getData() !== null) { + // We need to give the pivot table its own copy of the data, because it changes + // it which interferes with other visualizations. + data = $.extend(true, [], $scope.queryResult.getRawData()); + const options = { + renderers: $.pivotUtilities.renderers, + onRefresh(config) { + const configCopy = Object.assign({}, config); + // delete some values which are functions + delete configCopy.aggregators; + delete configCopy.renderers; + delete configCopy.onRefresh; + // delete some bulky default values + delete configCopy.rendererOptions; + delete configCopy.localeStrings; - if ($scope.visualization) { - $scope.visualization.options = configCopy; - } - }, - }; + if ($scope.visualization) { + $scope.visualization.options = configCopy; + } + }, + }; - if ($scope.visualization) { - Object.assign(options, $scope.visualization.options); + if ($scope.visualization) { + Object.assign(options, $scope.visualization.options); + } + $(element).pivotUI(data, options, true); + if ($scope.visualization.options.controls.enabled) { + const controls = $('.pvtAxisContainer, .pvtRenderer, .pvtVals'); + for (let i = 0; i < controls.length; i += 1) { controls[i].style.display = 'none'; } + } } - $(element).pivotUI(data, options, true); - } - }); + }); + } + + $scope.$watch('queryResult && queryResult.getData()', updatePivot); + $scope.$watch('visualization.options.controls.enabled', updatePivot); }, }; } +function pivotTableEditor() { + return { + restrict: 'E', + template: editorTemplate, + }; +} + export default function (ngModule) { ngModule.directive('pivotTableRenderer', pivotTableRenderer); + ngModule.directive('pivotTableEditor', pivotTableEditor); ngModule.config((VisualizationProvider) => { - const editTemplate = '
'; + const editTemplate = ''; const defaultOptions = { }; diff --git a/client/app/visualizations/pivot/pivottable-editor.html b/client/app/visualizations/pivot/pivottable-editor.html new file mode 100644 index 0000000000..696054ac34 --- /dev/null +++ b/client/app/visualizations/pivot/pivottable-editor.html @@ -0,0 +1,9 @@ +
+
+
+ + Hide Controls + +
+
+
From 081ac5f651fc7869154d3e89cb6acd6e3667adff Mon Sep 17 00:00:00 2001 From: deecay Date: Tue, 21 Feb 2017 17:18:14 +0900 Subject: [PATCH 006/243] Remove unnecessary comment line. --- client/app/visualizations/pivot/pivottable-editor.html | 1 - 1 file changed, 1 deletion(-) diff --git a/client/app/visualizations/pivot/pivottable-editor.html b/client/app/visualizations/pivot/pivottable-editor.html index 696054ac34..c573a685c9 100644 --- a/client/app/visualizations/pivot/pivottable-editor.html +++ b/client/app/visualizations/pivot/pivottable-editor.html @@ -3,7 +3,6 @@
Hide Controls -
From 8c481cd7a76a0ae98c64e1c67bee86ac0e1cb907 Mon Sep 17 00:00:00 2001 From: rmakulov Date: Mon, 27 Feb 2017 15:50:29 +0300 Subject: [PATCH 007/243] added propertyOrder field --- redash/query_runner/__init__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/redash/query_runner/__init__.py b/redash/query_runner/__init__.py index b85b09e81a..2e7cb93255 100644 --- a/redash/query_runner/__init__.py +++ b/redash/query_runner/__init__.py @@ -1,6 +1,7 @@ import logging import json +from collections import OrderedDict from redash import settings logger = logging.getLogger(__name__) @@ -111,13 +112,16 @@ def _run_query_internal(self, query): @classmethod def to_dict(cls): + schema = cls.configuration_schema() + schema_properties = schema['properties'] + ordered_properties = sorted(schema_properties.items(), + key=lambda (k, v): ("propertyOrder" not in v, v.get("propertyOrder", None))) + schema['properties'] = OrderedDict(ordered_properties) return { 'name': cls.name(), 'type': cls.type(), - 'configuration_schema': cls.configuration_schema() + 'configuration_schema': schema } - - class BaseSQLQueryRunner(BaseQueryRunner): def __init__(self, configuration): super(BaseSQLQueryRunner, self).__init__(configuration) From ebaf0127018c5bf883eefa1c202f83a605677f12 Mon Sep 17 00:00:00 2001 From: rmakulov Date: Mon, 27 Feb 2017 19:05:43 +0300 Subject: [PATCH 008/243] propertyOrder replaced with the field that defines an order --- redash/query_runner/__init__.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/redash/query_runner/__init__.py b/redash/query_runner/__init__.py index 2e7cb93255..735b9887d8 100644 --- a/redash/query_runner/__init__.py +++ b/redash/query_runner/__init__.py @@ -1,3 +1,4 @@ +import sys import logging import json @@ -44,6 +45,13 @@ class InterruptException(Exception): pass +def index(l, x): # Allow non-full key list to be used in sorting + try: + return l.index(x) + except ValueError: + return sys.maxint, x + + class BaseQueryRunner(object): noop_query = None @@ -113,15 +121,17 @@ def _run_query_internal(self, query): @classmethod def to_dict(cls): schema = cls.configuration_schema() - schema_properties = schema['properties'] - ordered_properties = sorted(schema_properties.items(), - key=lambda (k, v): ("propertyOrder" not in v, v.get("propertyOrder", None))) - schema['properties'] = OrderedDict(ordered_properties) + if 'order' in schema.keys(): + order = schema['order'] + schema_properties = schema['properties'] + schema['properties'] = OrderedDict(sorted(schema_properties.items(), key=lambda t: index(order, t[0]))) return { 'name': cls.name(), 'type': cls.type(), 'configuration_schema': schema } + + class BaseSQLQueryRunner(BaseQueryRunner): def __init__(self, configuration): super(BaseSQLQueryRunner, self).__init__(configuration) @@ -142,6 +152,7 @@ def _get_tables_stats(self, tables_dict): res = self._run_query_internal('select count(*) as cnt from %s' % t) tables_dict[t]['size'] = res[0]['cnt'] + query_runners = {} @@ -151,7 +162,8 @@ def register(query_runner_class): logger.debug("Registering %s (%s) query runner.", query_runner_class.name(), query_runner_class.type()) query_runners[query_runner_class.type()] = query_runner_class else: - logger.debug("%s query runner enabled but not supported, not registering. Either disable or install missing dependencies.", query_runner_class.name()) + logger.debug("%s query runner enabled but not supported, not registering. Either disable or install missing " + "dependencies.", query_runner_class.name()) def get_query_runner(query_runner_type, configuration): From f77da51a7db96015ee9883d8f634820e90f09391 Mon Sep 17 00:00:00 2001 From: rmakulov Date: Thu, 2 Mar 2017 17:14:19 +0300 Subject: [PATCH 009/243] order on client side --- client/app/components/dynamic-form.html | 21 +++++++++++---------- client/app/components/dynamic-form.js | 21 ++++++++++++++++++++- redash/query_runner/__init__.py | 7 +------ redash/query_runner/pg.py | 1 + 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/client/app/components/dynamic-form.html b/client/app/components/dynamic-form.html index ce917aeca0..babaad47f7 100644 --- a/client/app/components/dynamic-form.html +++ b/client/app/components/dynamic-form.html @@ -7,21 +7,22 @@ -
- - +
+ + -
+
+
+ +
+
@@ -116,6 +123,13 @@
+ +
+ +
diff --git a/client/app/visualizations/chart/index.js b/client/app/visualizations/chart/index.js index 2d13133589..b9f93b9074 100644 --- a/client/app/visualizations/chart/index.js +++ b/client/app/visualizations/chart/index.js @@ -69,6 +69,7 @@ function ChartEditor(ColorPalette, clientConfig) { pie: { name: 'Pie', icon: 'pie-chart' }, scatter: { name: 'Scatter', icon: 'circle-o' }, bubble: { name: 'Bubble', icon: 'circle-o' }, + box: { name: 'Box', icon: 'square-o' }, }; if (clientConfig.allowCustomJSVisualizations) { diff --git a/client/app/visualizations/chart/plotly.js b/client/app/visualizations/chart/plotly.js index 5d1cdf119d..6349d8906a 100644 --- a/client/app/visualizations/chart/plotly.js +++ b/client/app/visualizations/chart/plotly.js @@ -4,10 +4,11 @@ import Plotly from 'plotly.js/lib/core'; import bar from 'plotly.js/lib/bar'; import pie from 'plotly.js/lib/pie'; import histogram from 'plotly.js/lib/histogram'; +import box from 'plotly.js/lib/box'; import moment from 'moment'; -Plotly.register([bar, pie, histogram]); +Plotly.register([bar, pie, histogram, box]); Plotly.setPlotConfig({ modeBarButtonsToRemove: ['sendDataToCloud'], }); @@ -197,6 +198,9 @@ const PlotlyChart = () => { link(scope, element) { function calculateHeight() { const height = Math.max(scope.height, (scope.height - 50) + bottomMargin); + if (scope.options.globalSeriesType === 'box') { + return scope.options.height || height; + } return height; } @@ -213,6 +217,9 @@ const PlotlyChart = () => { series.mode = 'markers'; } else if (type === 'bubble') { series.mode = 'markers'; + } else if (type === 'box') { + series.type = 'box'; + series.mode = 'markers'; } } @@ -273,6 +280,11 @@ const PlotlyChart = () => { return; } + if (scope.options.globalSeriesType === 'box') { + scope.layout.boxmode = 'group'; + scope.layout.boxgroupgap = 0.50; + } + let hasY2 = false; const sortX = scope.options.sortX === true || scope.options.sortX === undefined; const useUnifiedXaxis = sortX && scope.options.xAxis.type === 'category'; @@ -341,6 +353,22 @@ const PlotlyChart = () => { size: pluck(data, 'size'), }; } + + if (seriesOptions.type === 'box') { + plotlySeries.boxpoints = 'outliers'; + plotlySeries.marker = { + size: 3, + }; + if (scope.options.showpoints) { + plotlySeries.boxpoints = 'all'; + plotlySeries.jitter = 0.3; + plotlySeries.pointpos = -1.8; + plotlySeries.marker = { + size: 3, + }; + } + } + scope.data.push(plotlySeries); }); From a087fe4bcd15c9a938c13f64594d8a2312c948e4 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Tue, 11 Apr 2017 18:05:43 +0300 Subject: [PATCH 018/243] Fix: page freezes when rendering large result set. Closes #1711. --- client/app/components/dynamic-table/dynamic-table.html | 2 +- client/app/components/dynamic-table/index.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/app/components/dynamic-table/dynamic-table.html b/client/app/components/dynamic-table/dynamic-table.html index befe948d03..6be3aeb681 100644 --- a/client/app/components/dynamic-table/dynamic-table.html +++ b/client/app/components/dynamic-table/dynamic-table.html @@ -9,7 +9,7 @@ - + diff --git a/client/app/components/dynamic-table/index.js b/client/app/components/dynamic-table/index.js index 762c66cff6..c4c1342816 100644 --- a/client/app/components/dynamic-table/index.js +++ b/client/app/components/dynamic-table/index.js @@ -15,7 +15,7 @@ function DynamicTable($sanitize) { const first = this.count * (this.page - 1); const last = this.count * (this.page); - this.rows = this.allRows.slice(first, last); + this.rowsToDisplay = this.rows.slice(first, last); }; this.$onChanges = (changes) => { @@ -24,10 +24,10 @@ function DynamicTable($sanitize) { } if (changes.rows) { - this.allRows = changes.rows.currentValue; + this.rows = changes.rows.currentValue; } - this.rowsCount = this.allRows.length; + this.rowsCount = this.rows.length; this.pageChanged(); }; From ccf9cbd2c8f325acb219252542a70056374fa494 Mon Sep 17 00:00:00 2001 From: Jos van Egmond Date: Fri, 14 Apr 2017 16:06:38 +0200 Subject: [PATCH 019/243] Raise JQL limit from default 50 to 1000 --- redash/query_runner/jql.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/redash/query_runner/jql.py b/redash/query_runner/jql.py index 2b37339c48..599e6993c4 100644 --- a/redash/query_runner/jql.py +++ b/redash/query_runner/jql.py @@ -183,6 +183,8 @@ def run_query(self, query, user): if query_type == 'count': query['maxResults'] = 1 query['fields'] = '' + else: + query['maxResults'] = 1000 response = requests.get(jql_url, params=query, auth=(self.configuration.get('username'), self.configuration.get('password'))) From 2e7fafc4d8f1d59b76ceabb2f86c204fdb53a9a2 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Tue, 18 Apr 2017 14:59:44 +0300 Subject: [PATCH 020/243] CHANGELOG update. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index befb9592cf..633b0f0da4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixed +- Fix: page freezes when rendering large result set. - Fix: chart embeds were not rendering in PhantomJS. ## v1.0.1 - 2017-04-02 From 3c7c93fc9f09cfa17319aef14da3d23ec6e46641 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Tue, 18 Apr 2017 15:19:57 +0300 Subject: [PATCH 021/243] Fix: favicon wasn't showing up. Closes #1719. --- client/app/index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/app/index.html b/client/app/index.html index 36102dc8ed..424d101b0c 100644 --- a/client/app/index.html +++ b/client/app/index.html @@ -6,9 +6,9 @@ Redash - - - + + + From 742e38b08de0680fb8eae148be3fffcccdefc58f Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Tue, 18 Apr 2017 15:20:21 +0300 Subject: [PATCH 022/243] Update CHANGELOG and bump version --- CHANGELOG.md | 3 ++- package.json | 2 +- redash/__init__.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 633b0f0da4..ca5e4e8588 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # Change Log -## UNRELEASED +## v1.0.2 - 2017-04-18 ### Fixed +- Fix: support for unicode in dashboard tags. @deecay - Fix: page freezes when rendering large result set. - Fix: chart embeds were not rendering in PhantomJS. diff --git a/package.json b/package.json index 69cf7167e4..06d580eee6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redash-client", - "version": "1.0.1", + "version": "1.0.2", "description": "The frontend part of Redash.", "main": "index.js", "scripts": { diff --git a/redash/__init__.py b/redash/__init__.py index 6e2673d289..37ff067f3b 100644 --- a/redash/__init__.py +++ b/redash/__init__.py @@ -16,7 +16,7 @@ from redash.destinations import import_destinations -__version__ = '1.0.1' +__version__ = '1.0.2' def setup_logging(): From 939aae086f98f381b8cb32a91963418f7ee02f30 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Tue, 18 Apr 2017 15:22:05 +0300 Subject: [PATCH 023/243] ADd changelog entry for favicons fix --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca5e4e8588..1d0cae18ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixed +- Fix: favicon wasn't showing up. - Fix: support for unicode in dashboard tags. @deecay - Fix: page freezes when rendering large result set. - Fix: chart embeds were not rendering in PhantomJS. From f0719f5ea47c6ec054ba78fcc5a062abf6b66b4e Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Tue, 18 Apr 2017 22:50:33 +0300 Subject: [PATCH 024/243] Fix: sort by header no longer working. Closes #1726. --- client/app/components/dynamic-table/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/app/components/dynamic-table/index.js b/client/app/components/dynamic-table/index.js index c4c1342816..b5563f477c 100644 --- a/client/app/components/dynamic-table/index.js +++ b/client/app/components/dynamic-table/index.js @@ -41,9 +41,9 @@ function DynamicTable($sanitize) { } if (this.orderByField) { - this.allRows = sortBy(this.allRows, this.orderByField.name); + this.rows = sortBy(this.rows, this.orderByField.name); if (this.orderByReverse) { - this.allRows = this.allRows.reverse(); + this.rows = this.rows.reverse(); } this.pageChanged(); } From f504b682f3eae99154a5157f6e616c96b7b0434d Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Tue, 18 Apr 2017 22:50:57 +0300 Subject: [PATCH 025/243] Bump version. --- CHANGELOG.md | 6 ++++++ package.json | 2 +- redash/__init__.py | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d0cae18ff..c3437cf1b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## v1.0.3 - 2017-04-18 + +### Fixed + +- Fix: sort by column no longer working. + ## v1.0.2 - 2017-04-18 ### Fixed diff --git a/package.json b/package.json index 06d580eee6..c1982f8d23 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redash-client", - "version": "1.0.2", + "version": "1.0.3", "description": "The frontend part of Redash.", "main": "index.js", "scripts": { diff --git a/redash/__init__.py b/redash/__init__.py index 37ff067f3b..abaedcdd06 100644 --- a/redash/__init__.py +++ b/redash/__init__.py @@ -16,7 +16,7 @@ from redash.destinations import import_destinations -__version__ = '1.0.2' +__version__ = '1.0.3' def setup_logging(): From 519fb49f6ab6a8be07b7abd606d236a2e28f68d3 Mon Sep 17 00:00:00 2001 From: Maxime Fouilleul Date: Wed, 19 Apr 2017 16:39:43 +0200 Subject: [PATCH 026/243] Improve cassandra lib --- redash/query_runner/cass.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/redash/query_runner/cass.py b/redash/query_runner/cass.py index 7a577e6e30..d69dca9dc6 100644 --- a/redash/query_runner/cass.py +++ b/redash/query_runner/cass.py @@ -1,5 +1,6 @@ import json import logging +import uuid from redash.query_runner import BaseQueryRunner, register from redash.utils import JSONEncoder @@ -13,6 +14,11 @@ except ImportError: enabled = False +class CassandraJSONEncoder(JSONEncoder): + def default(self, o): + if isinstance(o, uuid.UUID): + return str(o) + return super(CassandraJSONEncoder, self).default(o) class Cassandra(BaseQueryRunner): noop_query = "SELECT dateof(now()) FROM system.local" @@ -44,6 +50,15 @@ def configuration_schema(cls): 'password': { 'type': 'string', 'title': 'Password' + }, + 'protocol': { + 'type': 'number', + 'title': 'Protocol Version', + 'default': 3 + }, + 'cqlversion': { + 'type': 'string', + 'title': 'CQL Version' } }, 'required': ['keyspace', 'host'] @@ -77,10 +92,9 @@ def run_query(self, query, user): if self.configuration.get('username', '') and self.configuration.get('password', ''): auth_provider = PlainTextAuthProvider(username='{}'.format(self.configuration.get('username', '')), password='{}'.format(self.configuration.get('password', ''))) - connection = Cluster([self.configuration.get('host', '')], auth_provider=auth_provider, protocol_version=3) + connection = Cluster([self.configuration.get('host', '')], auth_provider=auth_provider, protocol_version=self.configuration.get('protocol', ''), cql_version=self.configuration.get('cqlversion', '')) else: - connection = Cluster([self.configuration.get('host', '')], protocol_version=3) - + connection = Cluster([self.configuration.get('host', '')], protocol_version=self.configuration.get('protocol', ''), cql_version=self.configuration.get('cqlversion', '')) session = connection.connect() session.set_keyspace(self.configuration['keyspace']) logger.debug("Cassandra running query: %s", query) @@ -93,7 +107,7 @@ def run_query(self, query, user): rows = [dict(zip(column_names, row)) for row in result] data = {'columns': columns, 'rows': rows} - json_data = json.dumps(data, cls=JSONEncoder) + json_data = json.dumps(data, cls=CassandraJSONEncoder) error = None except KeyboardInterrupt: From 5d7795ca47d7b8fbc2bf74c8b75a177b2b07d324 Mon Sep 17 00:00:00 2001 From: Maxime Fouilleul Date: Wed, 19 Apr 2017 22:14:27 +0200 Subject: [PATCH 027/243] Fix code style --- redash/query_runner/cass.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/redash/query_runner/cass.py b/redash/query_runner/cass.py index d69dca9dc6..fd68648ba7 100644 --- a/redash/query_runner/cass.py +++ b/redash/query_runner/cass.py @@ -14,12 +14,14 @@ except ImportError: enabled = False + class CassandraJSONEncoder(JSONEncoder): def default(self, o): if isinstance(o, uuid.UUID): return str(o) return super(CassandraJSONEncoder, self).default(o) + class Cassandra(BaseQueryRunner): noop_query = "SELECT dateof(now()) FROM system.local" @@ -92,9 +94,14 @@ def run_query(self, query, user): if self.configuration.get('username', '') and self.configuration.get('password', ''): auth_provider = PlainTextAuthProvider(username='{}'.format(self.configuration.get('username', '')), password='{}'.format(self.configuration.get('password', ''))) - connection = Cluster([self.configuration.get('host', '')], auth_provider=auth_provider, protocol_version=self.configuration.get('protocol', ''), cql_version=self.configuration.get('cqlversion', '')) + connection = Cluster([self.configuration.get('host', '')], + auth_provider=auth_provider, + protocol_version=self.configuration.get('protocol', ''), + cql_version=self.configuration.get('cqlversion', '')) else: - connection = Cluster([self.configuration.get('host', '')], protocol_version=self.configuration.get('protocol', ''), cql_version=self.configuration.get('cqlversion', '')) + connection = Cluster([self.configuration.get('host', '')], + protocol_version=self.configuration.get('protocol', ''), + cql_version=self.configuration.get('cqlversion', '')) session = connection.connect() session.set_keyspace(self.configuration['keyspace']) logger.debug("Cassandra running query: %s", query) From ac1b0a46f991dda403ae79a0b3fa1609e69a2e28 Mon Sep 17 00:00:00 2001 From: Maxime Fouilleul Date: Wed, 19 Apr 2017 23:00:39 +0200 Subject: [PATCH 028/243] Fix trailing spaces (style) --- redash/query_runner/cass.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/redash/query_runner/cass.py b/redash/query_runner/cass.py index fd68648ba7..26e41cf9f8 100644 --- a/redash/query_runner/cass.py +++ b/redash/query_runner/cass.py @@ -94,13 +94,13 @@ def run_query(self, query, user): if self.configuration.get('username', '') and self.configuration.get('password', ''): auth_provider = PlainTextAuthProvider(username='{}'.format(self.configuration.get('username', '')), password='{}'.format(self.configuration.get('password', ''))) - connection = Cluster([self.configuration.get('host', '')], - auth_provider=auth_provider, - protocol_version=self.configuration.get('protocol', ''), + connection = Cluster([self.configuration.get('host', '')], + auth_provider=auth_provider, + protocol_version=self.configuration.get('protocol', ''), cql_version=self.configuration.get('cqlversion', '')) else: - connection = Cluster([self.configuration.get('host', '')], - protocol_version=self.configuration.get('protocol', ''), + connection = Cluster([self.configuration.get('host', '')], + protocol_version=self.configuration.get('protocol', ''), cql_version=self.configuration.get('cqlversion', '')) session = connection.connect() session.set_keyspace(self.configuration['keyspace']) From 9b5939476858094170f12bcdbc3c724ce6f521ec Mon Sep 17 00:00:00 2001 From: Maxime Fouilleul Date: Thu, 20 Apr 2017 11:16:37 +0200 Subject: [PATCH 029/243] Fix default values for proto/cqlversion --- redash/query_runner/cass.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/redash/query_runner/cass.py b/redash/query_runner/cass.py index 26e41cf9f8..16617a50a3 100644 --- a/redash/query_runner/cass.py +++ b/redash/query_runner/cass.py @@ -96,12 +96,12 @@ def run_query(self, query, user): password='{}'.format(self.configuration.get('password', ''))) connection = Cluster([self.configuration.get('host', '')], auth_provider=auth_provider, - protocol_version=self.configuration.get('protocol', ''), - cql_version=self.configuration.get('cqlversion', '')) + protocol_version=self.configuration.get('protocol', 3), + cql_version=self.configuration.get('cqlversion', 'None')) else: connection = Cluster([self.configuration.get('host', '')], - protocol_version=self.configuration.get('protocol', ''), - cql_version=self.configuration.get('cqlversion', '')) + protocol_version=self.configuration.get('protocol', 3), + cql_version=self.configuration.get('cqlversion', 'None')) session = connection.connect() session.set_keyspace(self.configuration['keyspace']) logger.debug("Cassandra running query: %s", query) From 9f2180764718370a6ac7c5da75279ee85c258e4c Mon Sep 17 00:00:00 2001 From: Maxime Fouilleul Date: Thu, 20 Apr 2017 11:33:12 +0200 Subject: [PATCH 030/243] remove useless param --- redash/query_runner/cass.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/redash/query_runner/cass.py b/redash/query_runner/cass.py index 16617a50a3..b211cbe275 100644 --- a/redash/query_runner/cass.py +++ b/redash/query_runner/cass.py @@ -57,10 +57,6 @@ def configuration_schema(cls): 'type': 'number', 'title': 'Protocol Version', 'default': 3 - }, - 'cqlversion': { - 'type': 'string', - 'title': 'CQL Version' } }, 'required': ['keyspace', 'host'] @@ -96,12 +92,10 @@ def run_query(self, query, user): password='{}'.format(self.configuration.get('password', ''))) connection = Cluster([self.configuration.get('host', '')], auth_provider=auth_provider, - protocol_version=self.configuration.get('protocol', 3), - cql_version=self.configuration.get('cqlversion', 'None')) + protocol_version=self.configuration.get('protocol', 3)) else: connection = Cluster([self.configuration.get('host', '')], - protocol_version=self.configuration.get('protocol', 3), - cql_version=self.configuration.get('cqlversion', 'None')) + protocol_version=self.configuration.get('protocol', 3)) session = connection.connect() session.set_keyspace(self.configuration['keyspace']) logger.debug("Cassandra running query: %s", query) From 93df24de39ed995bfe528d3b9b00ad009ec13c4f Mon Sep 17 00:00:00 2001 From: Abdelrahman Mahmoud Date: Wed, 15 Mar 2017 12:55:24 -0400 Subject: [PATCH 031/243] Fix Google analytics, Google Spreadsheet and Big Query integration .. upgrade outh2client and google-api-python-client --- redash/query_runner/big_query.py | 10 +++++----- redash/query_runner/google_analytics.py | 4 ++-- redash/query_runner/google_spreadsheets.py | 4 ++-- requirements_all_ds.txt | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/redash/query_runner/big_query.py b/redash/query_runner/big_query.py index 0be4597b7a..875e1604e3 100644 --- a/redash/query_runner/big_query.py +++ b/redash/query_runner/big_query.py @@ -18,7 +18,7 @@ import apiclient.errors from apiclient.discovery import build from apiclient.errors import HttpError - from oauth2client.client import SignedJwtAssertionCredentials + from oauth2client.service_account import ServiceAccountCredentials from oauth2client import gce enabled = True @@ -134,7 +134,7 @@ def _get_bigquery_service(self): key = json.loads(b64decode(self.configuration['jsonKeyFile'])) - credentials = SignedJwtAssertionCredentials(key['client_email'], key['private_key'], scope=scope) + credentials = ServiceAccountCredentials.from_json_keyfile_dict(key, scope) http = httplib2.Http(timeout=settings.BIGQUERY_HTTP_TIMEOUT) http = credentials.authorize(http) @@ -148,10 +148,10 @@ def _get_total_bytes_processed(self, jobs, query): "query": query, "dryRun": True, } - + if self.configuration.get('useStandardSql', False): job_data['useLegacySql'] = False - + response = jobs.query(projectId=self._get_project_id(), body=job_data).execute() return int(response["totalBytesProcessed"]) @@ -164,7 +164,7 @@ def _get_query_result(self, jobs, query): } } } - + if self.configuration.get('useStandardSql', False): job_data['configuration']['query']['useLegacySql'] = False diff --git a/redash/query_runner/google_analytics.py b/redash/query_runner/google_analytics.py index 26ecc86119..d479520edb 100644 --- a/redash/query_runner/google_analytics.py +++ b/redash/query_runner/google_analytics.py @@ -10,7 +10,7 @@ logger = logging.getLogger(__name__) try: - from oauth2client.client import SignedJwtAssertionCredentials + from oauth2client.service_account import ServiceAccountCredentials from apiclient.discovery import build import httplib2 enabled = True @@ -81,7 +81,7 @@ def _get_tables(self, schema): def _get_analytics_service(self): scope = ['https://www.googleapis.com/auth/analytics.readonly'] key = json.loads(b64decode(self.configuration['jsonKeyFile'])) - credentials = SignedJwtAssertionCredentials(key['client_email'], key["private_key"], scope=scope) + credentials = ServiceAccountCredentials.from_json_keyfile_dict(key, scope) return build('analytics', 'v3', http=credentials.authorize(httplib2.Http())) def run_query(self, query, user): diff --git a/redash/query_runner/google_spreadsheets.py b/redash/query_runner/google_spreadsheets.py index 2184da70ed..8d6126e1af 100644 --- a/redash/query_runner/google_spreadsheets.py +++ b/redash/query_runner/google_spreadsheets.py @@ -9,7 +9,7 @@ try: import gspread - from oauth2client.client import SignedJwtAssertionCredentials + from oauth2client.service_account import ServiceAccountCredentials enabled = True except ImportError: enabled = False @@ -164,7 +164,7 @@ def _get_spreadsheet_service(self): ] key = json.loads(b64decode(self.configuration['jsonKeyFile'])) - credentials = SignedJwtAssertionCredentials(key['client_email'], key["private_key"], scope=scope) + credentials = ServiceAccountCredentials.from_json_keyfile_dict(key, scope) spreadsheetservice = gspread.authorize(credentials) return spreadsheetservice diff --git a/requirements_all_ds.txt b/requirements_all_ds.txt index 5dad813f3f..ecc32512eb 100644 --- a/requirements_all_ds.txt +++ b/requirements_all_ds.txt @@ -1,9 +1,9 @@ -google-api-python-client==1.2 +google-api-python-client==1.5.1 gspread==0.2.5 impyla==0.10.0 influxdb==2.7.1 MySQL-python==1.2.5 -oauth2client==1.2 +oauth2client==3.0.0 pyhive==0.1.6 pymongo==3.2.1 pyOpenSSL==0.14 From 7324f1f4c7baf94708bf056b5115be255c8ed3b7 Mon Sep 17 00:00:00 2001 From: Abdelrahman Mahmoud Date: Wed, 15 Mar 2017 13:06:17 -0400 Subject: [PATCH 032/243] Fix code climate warnings --- redash/query_runner/big_query.py | 4 ++-- redash/query_runner/google_analytics.py | 4 ++-- redash/query_runner/google_spreadsheets.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/redash/query_runner/big_query.py b/redash/query_runner/big_query.py index 875e1604e3..936c993c7b 100644 --- a/redash/query_runner/big_query.py +++ b/redash/query_runner/big_query.py @@ -134,9 +134,9 @@ def _get_bigquery_service(self): key = json.loads(b64decode(self.configuration['jsonKeyFile'])) - credentials = ServiceAccountCredentials.from_json_keyfile_dict(key, scope) + creds = ServiceAccountCredentials.from_json_keyfile_dict(key, scope) http = httplib2.Http(timeout=settings.BIGQUERY_HTTP_TIMEOUT) - http = credentials.authorize(http) + http = creds.authorize(http) return build("bigquery", "v2", http=http) diff --git a/redash/query_runner/google_analytics.py b/redash/query_runner/google_analytics.py index d479520edb..0993a1eaca 100644 --- a/redash/query_runner/google_analytics.py +++ b/redash/query_runner/google_analytics.py @@ -81,8 +81,8 @@ def _get_tables(self, schema): def _get_analytics_service(self): scope = ['https://www.googleapis.com/auth/analytics.readonly'] key = json.loads(b64decode(self.configuration['jsonKeyFile'])) - credentials = ServiceAccountCredentials.from_json_keyfile_dict(key, scope) - return build('analytics', 'v3', http=credentials.authorize(httplib2.Http())) + creds = ServiceAccountCredentials.from_json_keyfile_dict(key, scope) + return build('analytics', 'v3', http=creds.authorize(httplib2.Http())) def run_query(self, query, user): logger.debug("Analytics is about to execute query: %s", query) diff --git a/redash/query_runner/google_spreadsheets.py b/redash/query_runner/google_spreadsheets.py index 8d6126e1af..1b0509ed9e 100644 --- a/redash/query_runner/google_spreadsheets.py +++ b/redash/query_runner/google_spreadsheets.py @@ -164,8 +164,8 @@ def _get_spreadsheet_service(self): ] key = json.loads(b64decode(self.configuration['jsonKeyFile'])) - credentials = ServiceAccountCredentials.from_json_keyfile_dict(key, scope) - spreadsheetservice = gspread.authorize(credentials) + creds = ServiceAccountCredentials.from_json_keyfile_dict(key, scope) + spreadsheetservice = gspread.authorize(creds) return spreadsheetservice def test_connection(self): From eb3e30f70f752d6334f601ebe1a248d3f488e861 Mon Sep 17 00:00:00 2001 From: Shimpei Kodama Date: Tue, 25 Apr 2017 11:00:52 +0900 Subject: [PATCH 033/243] Extend expiry time to prevent duplicate execution --- redash/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redash/settings.py b/redash/settings.py index 08eebbaa3e..e529bc881e 100644 --- a/redash/settings.py +++ b/redash/settings.py @@ -72,7 +72,7 @@ def all_settings(): # Celery related settings CELERY_BROKER = os.environ.get("REDASH_CELERY_BROKER", REDIS_URL) CELERY_BACKEND = os.environ.get("REDASH_CELERY_BACKEND", CELERY_BROKER) -CELERY_TASK_RESULT_EXPIRES = int(os.environ.get('REDASH_CELERY_TASK_RESULT_EXPIRES', 3600)) +CELERY_TASK_RESULT_EXPIRES = int(os.environ.get('REDASH_CELERY_TASK_RESULT_EXPIRES', 3600 * 12)) # The following enables periodic job (every 5 minutes) of removing unused query results. QUERY_RESULTS_CLEANUP_ENABLED = parse_boolean(os.environ.get("REDASH_QUERY_RESULTS_CLEANUP_ENABLED", "true")) @@ -129,7 +129,7 @@ def all_settings(): STATIC_ASSETS_PATHS = [fix_assets_path(path) for path in os.environ.get("REDASH_STATIC_ASSETS_PATH", "../client/dist/").split(',')] STATIC_ASSETS_PATHS.append(fix_assets_path('./static/')) -JOB_EXPIRY_TIME = int(os.environ.get("REDASH_JOB_EXPIRY_TIME", 3600 * 6)) +JOB_EXPIRY_TIME = int(os.environ.get("REDASH_JOB_EXPIRY_TIME", 3600 * 12)) COOKIE_SECRET = os.environ.get("REDASH_COOKIE_SECRET", "c292a0a3aa32397cdb050e233733900f") SESSION_COOKIE_SECURE = parse_boolean(os.environ.get("REDASH_SESSION_COOKIE_SECURE") or str(ENFORCE_HTTPS)) From 214a231371031051de54d9131096bdd672209553 Mon Sep 17 00:00:00 2001 From: Yohei Susa Date: Thu, 27 Apr 2017 18:45:49 +0900 Subject: [PATCH 034/243] Disable query annotations to Athena query runner --- redash/query_runner/athena.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index 2642377cb2..eda3c5ef98 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -40,6 +40,9 @@ def configuration_schema(cls): 'secret': ['aws_secret_key'] } + @classmethod + def annotate_query(cls): + return False def get_schema(self, get_stats=False): schema = {} From 7a47d6741dd9f711be41615f4bcaf1a6af91231a Mon Sep 17 00:00:00 2001 From: deecay Date: Sat, 29 Apr 2017 23:42:58 +0900 Subject: [PATCH 035/243] Change: Chosing box plot turns sortX off --- client/app/visualizations/box-plot/index.js | 2 +- client/app/visualizations/chart/plotly.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/app/visualizations/box-plot/index.js b/client/app/visualizations/box-plot/index.js index 7ce3973d80..b20cc258b0 100644 --- a/client/app/visualizations/box-plot/index.js +++ b/client/app/visualizations/box-plot/index.js @@ -176,7 +176,7 @@ export default function (ngModule) { VisualizationProvider.registerVisualization({ type: 'BOXPLOT', - name: 'Boxplot', + name: 'Boxplot (Deprecated)', renderTemplate, editorTemplate: editTemplate, }); diff --git a/client/app/visualizations/chart/plotly.js b/client/app/visualizations/chart/plotly.js index 6349d8906a..40b571776f 100644 --- a/client/app/visualizations/chart/plotly.js +++ b/client/app/visualizations/chart/plotly.js @@ -281,6 +281,7 @@ const PlotlyChart = () => { } if (scope.options.globalSeriesType === 'box') { + scope.options.sortX = false; scope.layout.boxmode = 'group'; scope.layout.boxgroupgap = 0.50; } From 1c955a570d8eed432aae6943350895ef88a3a5fa Mon Sep 17 00:00:00 2001 From: deecay Date: Sun, 30 Apr 2017 11:47:43 +0900 Subject: [PATCH 036/243] Bump Plotly version --- npm-shrinkwrap.json | 4099 ++++++++++++++++++++++++++++++++++++++++--- package.json | 2 +- 2 files changed, 3849 insertions(+), 252 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 8fbbadc5e2..e2c0c2d1d1 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -17,10 +17,36 @@ "from": "a-big-triangle@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/a-big-triangle/-/a-big-triangle-1.0.3.tgz" }, + "abbrev": { + "version": "1.1.0", + "from": "abbrev@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", + "dev": true + }, + "accepts": { + "version": "1.3.3", + "from": "accepts@>=1.3.3 <1.4.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", + "dev": true + }, "acorn": { - "version": "4.0.4", - "from": "acorn@4.0.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.4.tgz" + "version": "1.2.2", + "from": "acorn@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz" + }, + "acorn-jsx": { + "version": "3.0.1", + "from": "acorn-jsx@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "dev": true, + "dependencies": { + "acorn": { + "version": "3.3.0", + "from": "acorn@>=3.0.4 <4.0.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "dev": true + } + } }, "add-line-numbers": { "version": "1.0.1", @@ -32,6 +58,18 @@ "from": "affine-hull@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/affine-hull/-/affine-hull-1.0.0.tgz" }, + "ajv": { + "version": "4.11.8", + "from": "ajv@>=4.7.0 <5.0.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "dev": true + }, + "ajv-keywords": { + "version": "1.5.1", + "from": "ajv-keywords@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", + "dev": true + }, "align-text": { "version": "0.1.4", "from": "align-text@>=0.1.3 <0.2.0", @@ -52,15 +90,21 @@ "from": "alpha-shape@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/alpha-shape/-/alpha-shape-1.0.0.tgz" }, + "alphanum-sort": { + "version": "1.0.2", + "from": "alphanum-sort@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "dev": true + }, "alter": { "version": "0.2.0", "from": "alter@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz" }, "amdefine": { - "version": "1.0.0", + "version": "1.0.1", "from": "amdefine@>=0.0.4", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz" }, "angular": { "version": "1.5.8", @@ -127,6 +171,12 @@ "from": "angular-vs-repeat@latest", "resolved": "https://registry.npmjs.org/angular-vs-repeat/-/angular-vs-repeat-1.1.7.tgz" }, + "ansi-escapes": { + "version": "1.4.0", + "from": "ansi-escapes@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "dev": true + }, "ansi-regex": { "version": "2.0.0", "from": "ansi-regex@>=2.0.0 <3.0.0", @@ -137,21 +187,137 @@ "from": "ansi-styles@>=2.2.1 <3.0.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" }, + "ansicolors": { + "version": "0.2.1", + "from": "ansicolors@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.2.1.tgz", + "dev": true + }, + "anymatch": { + "version": "1.3.0", + "from": "anymatch@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz", + "dev": true + }, + "aproba": { + "version": "1.1.1", + "from": "aproba@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "from": "are-we-there-yet@>=1.1.2 <1.2.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "dev": true, + "dependencies": { + "readable-stream": { + "version": "2.2.9", + "from": "readable-stream@>=2.0.6 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", + "dev": true + }, + "string_decoder": { + "version": "1.0.0", + "from": "string_decoder@~1.0.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz", + "dev": true + } + } + }, + "argparse": { + "version": "1.0.9", + "from": "argparse@>=1.0.7 <2.0.0", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", + "dev": true + }, + "arr-diff": { + "version": "2.0.0", + "from": "arr-diff@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "dev": true + }, + "arr-flatten": { + "version": "1.0.3", + "from": "arr-flatten@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.3.tgz", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "from": "array-find-index@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "from": "array-flatten@1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "from": "array-union@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "dev": true + }, + "array-uniq": { + "version": "1.0.3", + "from": "array-uniq@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "dev": true + }, + "array-unique": { + "version": "0.2.1", + "from": "array-unique@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "dev": true + }, "arraytools": { "version": "1.1.2", "from": "arraytools@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/arraytools/-/arraytools-1.1.2.tgz" }, + "arrify": { + "version": "1.0.1", + "from": "arrify@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "dev": true + }, "asn1": { "version": "0.2.3", "from": "asn1@>=0.2.3 <0.3.0", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" }, + "assert": { + "version": "1.4.1", + "from": "assert@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "dev": true + }, "assert-plus": { "version": "0.2.0", "from": "assert-plus@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" }, + "async": { + "version": "1.5.2", + "from": "async@>=1.5.0 <2.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "dev": true + }, + "async-each": { + "version": "1.0.1", + "from": "async-each@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "dev": true + }, + "async-foreach": { + "version": "0.1.3", + "from": "async-foreach@>=0.1.3 <0.2.0", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "dev": true + }, "asynckit": { "version": "0.4.0", "from": "asynckit@>=0.4.0 <0.5.0", @@ -162,6 +328,12 @@ "from": "atob-lite@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-1.0.0.tgz" }, + "autoprefixer": { + "version": "6.7.7", + "from": "autoprefixer@>=6.3.1 <7.0.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "dev": true + }, "aws-sign2": { "version": "0.6.0", "from": "aws-sign2@>=0.6.0 <0.7.0", @@ -172,6 +344,416 @@ "from": "aws4@>=1.2.1 <2.0.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.5.0.tgz" }, + "babel-code-frame": { + "version": "6.22.0", + "from": "babel-code-frame@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "dev": true + }, + "babel-core": { + "version": "6.24.0", + "from": "babel-core@>=6.18.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.24.0.tgz", + "dev": true + }, + "babel-generator": { + "version": "6.24.1", + "from": "babel-generator@>=6.24.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.24.1.tgz", + "dev": true + }, + "babel-helper-bindify-decorators": { + "version": "6.24.1", + "from": "babel-helper-bindify-decorators@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz", + "dev": true + }, + "babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "from": "babel-helper-builder-binary-assignment-operator-visitor@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "dev": true + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "from": "babel-helper-call-delegate@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "dev": true + }, + "babel-helper-define-map": { + "version": "6.24.1", + "from": "babel-helper-define-map@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz", + "dev": true + }, + "babel-helper-explode-assignable-expression": { + "version": "6.24.1", + "from": "babel-helper-explode-assignable-expression@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "dev": true + }, + "babel-helper-explode-class": { + "version": "6.24.1", + "from": "babel-helper-explode-class@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz", + "dev": true + }, + "babel-helper-function-name": { + "version": "6.24.1", + "from": "babel-helper-function-name@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "dev": true + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "from": "babel-helper-get-function-arity@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "dev": true + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "from": "babel-helper-hoist-variables@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "dev": true + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "from": "babel-helper-optimise-call-expression@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "dev": true + }, + "babel-helper-regex": { + "version": "6.24.1", + "from": "babel-helper-regex@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.24.1.tgz", + "dev": true + }, + "babel-helper-remap-async-to-generator": { + "version": "6.24.1", + "from": "babel-helper-remap-async-to-generator@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", + "dev": true + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "from": "babel-helper-replace-supers@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "dev": true + }, + "babel-helpers": { + "version": "6.24.1", + "from": "babel-helpers@>=6.23.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "dev": true + }, + "babel-loader": { + "version": "6.4.1", + "from": "babel-loader@>=6.2.7 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-6.4.1.tgz", + "dev": true + }, + "babel-messages": { + "version": "6.23.0", + "from": "babel-messages@>=6.23.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "dev": true + }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "from": "babel-plugin-check-es2015-constants@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "dev": true + }, + "babel-plugin-syntax-async-functions": { + "version": "6.13.0", + "from": "babel-plugin-syntax-async-functions@>=6.8.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "dev": true + }, + "babel-plugin-syntax-async-generators": { + "version": "6.13.0", + "from": "babel-plugin-syntax-async-generators@>=6.5.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", + "dev": true + }, + "babel-plugin-syntax-class-properties": { + "version": "6.13.0", + "from": "babel-plugin-syntax-class-properties@>=6.8.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", + "dev": true + }, + "babel-plugin-syntax-decorators": { + "version": "6.13.0", + "from": "babel-plugin-syntax-decorators@>=6.13.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", + "dev": true + }, + "babel-plugin-syntax-dynamic-import": { + "version": "6.18.0", + "from": "babel-plugin-syntax-dynamic-import@>=6.18.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", + "dev": true + }, + "babel-plugin-syntax-exponentiation-operator": { + "version": "6.13.0", + "from": "babel-plugin-syntax-exponentiation-operator@>=6.8.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "dev": true + }, + "babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "from": "babel-plugin-syntax-object-rest-spread@>=6.8.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "dev": true + }, + "babel-plugin-syntax-trailing-function-commas": { + "version": "6.22.0", + "from": "babel-plugin-syntax-trailing-function-commas@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "dev": true + }, + "babel-plugin-transform-async-generator-functions": { + "version": "6.24.1", + "from": "babel-plugin-transform-async-generator-functions@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-async-to-generator": { + "version": "6.24.1", + "from": "babel-plugin-transform-async-to-generator@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-class-properties": { + "version": "6.24.1", + "from": "babel-plugin-transform-class-properties@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-decorators": { + "version": "6.24.1", + "from": "babel-plugin-transform-decorators@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "from": "babel-plugin-transform-es2015-arrow-functions@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "from": "babel-plugin-transform-es2015-block-scoped-functions@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.24.1", + "from": "babel-plugin-transform-es2015-block-scoping@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "from": "babel-plugin-transform-es2015-classes@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "from": "babel-plugin-transform-es2015-computed-properties@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "from": "babel-plugin-transform-es2015-destructuring@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "from": "babel-plugin-transform-es2015-duplicate-keys@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "from": "babel-plugin-transform-es2015-for-of@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "from": "babel-plugin-transform-es2015-function-name@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "from": "babel-plugin-transform-es2015-literals@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "from": "babel-plugin-transform-es2015-modules-amd@>=6.24.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.24.1", + "from": "babel-plugin-transform-es2015-modules-commonjs@>=6.24.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "from": "babel-plugin-transform-es2015-modules-systemjs@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "from": "babel-plugin-transform-es2015-modules-umd@>=6.24.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "from": "babel-plugin-transform-es2015-object-super@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "from": "babel-plugin-transform-es2015-parameters@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "from": "babel-plugin-transform-es2015-shorthand-properties@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "from": "babel-plugin-transform-es2015-spread@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "from": "babel-plugin-transform-es2015-sticky-regex@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "from": "babel-plugin-transform-es2015-template-literals@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "from": "babel-plugin-transform-es2015-typeof-symbol@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "dev": true + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "from": "babel-plugin-transform-es2015-unicode-regex@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-exponentiation-operator": { + "version": "6.24.1", + "from": "babel-plugin-transform-exponentiation-operator@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-object-rest-spread": { + "version": "6.23.0", + "from": "babel-plugin-transform-object-rest-spread@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz", + "dev": true + }, + "babel-plugin-transform-regenerator": { + "version": "6.24.1", + "from": "babel-plugin-transform-regenerator@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz", + "dev": true + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "from": "babel-plugin-transform-strict-mode@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "dev": true + }, + "babel-preset-es2015": { + "version": "6.24.0", + "from": "babel-preset-es2015@>=6.18.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.0.tgz", + "dev": true + }, + "babel-preset-stage-2": { + "version": "6.22.0", + "from": "babel-preset-stage-2@>=6.18.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.22.0.tgz", + "dev": true + }, + "babel-preset-stage-3": { + "version": "6.24.1", + "from": "babel-preset-stage-3@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", + "dev": true + }, + "babel-register": { + "version": "6.24.1", + "from": "babel-register@>=6.24.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.24.1.tgz", + "dev": true, + "dependencies": { + "babel-core": { + "version": "6.24.1", + "from": "babel-core@>=6.24.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.24.1.tgz", + "dev": true + } + } + }, + "babel-runtime": { + "version": "6.23.0", + "from": "babel-runtime@>=6.22.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "dev": true + }, + "babel-template": { + "version": "6.24.1", + "from": "babel-template@>=6.23.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.24.1.tgz", + "dev": true + }, + "babel-traverse": { + "version": "6.24.1", + "from": "babel-traverse@>=6.23.1 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.24.1.tgz", + "dev": true + }, + "babel-types": { + "version": "6.24.1", + "from": "babel-types@>=6.23.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.24.1.tgz", + "dev": true + }, + "babylon": { + "version": "6.17.0", + "from": "babylon@>=6.11.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.17.0.tgz", + "dev": true + }, "balanced-match": { "version": "0.4.2", "from": "balanced-match@>=0.4.1 <0.5.0", @@ -182,6 +764,17 @@ "from": "barycentric@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/barycentric/-/barycentric-1.0.1.tgz" }, + "base64-js": { + "version": "0.0.2", + "from": "base64-js@0.0.2", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.2.tgz" + }, + "batch": { + "version": "0.5.3", + "from": "batch@0.5.3", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.5.3.tgz", + "dev": true + }, "bcrypt-pbkdf": { "version": "1.0.0", "from": "bcrypt-pbkdf@>=1.0.0 <2.0.0", @@ -189,15 +782,21 @@ "optional": true }, "big-rat": { - "version": "1.0.3", + "version": "1.0.4", "from": "big-rat@>=1.0.3 <2.0.0", - "resolved": "https://registry.npmjs.org/big-rat/-/big-rat-1.0.3.tgz" + "resolved": "https://registry.npmjs.org/big-rat/-/big-rat-1.0.4.tgz" }, "big.js": { "version": "3.1.3", "from": "big.js@>=3.1.3 <4.0.0", "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz" }, + "binary-extensions": { + "version": "1.8.0", + "from": "binary-extensions@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.8.0.tgz", + "dev": true + }, "binary-search-bounds": { "version": "1.0.0", "from": "binary-search-bounds@>=1.0.0 <2.0.0", @@ -211,13 +810,43 @@ "bl": { "version": "1.2.0", "from": "bl@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.0.tgz" + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.0.tgz", + "dependencies": { + "readable-stream": { + "version": "2.2.9", + "from": "readable-stream@>=2.0.5 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz" + }, + "string_decoder": { + "version": "1.0.0", + "from": "string_decoder@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz" + } + } + }, + "block-stream": { + "version": "0.0.9", + "from": "block-stream@*", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "dev": true + }, + "bluebird": { + "version": "3.5.0", + "from": "bluebird@>=3.4.7 <4.0.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "dev": true }, "bn.js": { "version": "4.11.6", "from": "bn.js@>=4.11.6 <5.0.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz" }, + "boolbase": { + "version": "1.0.0", + "from": "boolbase@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "dev": true + }, "boom": { "version": "2.10.1", "from": "boom@>=2.0.0 <3.0.0", @@ -226,14 +855,7 @@ "bops": { "version": "0.0.6", "from": "bops@0.0.6", - "resolved": "https://registry.npmjs.org/bops/-/bops-0.0.6.tgz", - "dependencies": { - "base64-js": { - "version": "0.0.2", - "from": "base64-js@0.0.2", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.2.tgz" - } - } + "resolved": "https://registry.npmjs.org/bops/-/bops-0.0.6.tgz" }, "boundary-cells": { "version": "2.0.1", @@ -255,6 +877,12 @@ "from": "brace-expansion@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz" }, + "braces": { + "version": "1.8.5", + "from": "braces@>=1.8.2 <2.0.0", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "dev": true + }, "brfs": { "version": "1.4.3", "from": "brfs@>=1.4.0 <2.0.0", @@ -270,6 +898,16 @@ "from": "quote-stream@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz" }, + "readable-stream": { + "version": "2.2.9", + "from": "readable-stream@>=2.1.5 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz" + }, + "string_decoder": { + "version": "1.0.0", + "from": "string_decoder@~1.0.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz" + }, "through2": { "version": "2.0.3", "from": "through2@>=2.0.0 <3.0.0", @@ -277,6 +915,38 @@ } } }, + "browserify-aes": { + "version": "0.4.0", + "from": "browserify-aes@0.4.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-0.4.0.tgz", + "dev": true + }, + "browserify-zlib": { + "version": "0.1.4", + "from": "browserify-zlib@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "dev": true + }, + "browserslist": { + "version": "1.7.7", + "from": "browserslist@>=1.7.6 <2.0.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "dev": true + }, + "buffer": { + "version": "4.9.1", + "from": "buffer@>=4.9.0 <5.0.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "dev": true, + "dependencies": { + "base64-js": { + "version": "1.2.0", + "from": "base64-js@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz", + "dev": true + } + } + }, "buffer-equal": { "version": "0.0.1", "from": "buffer-equal@0.0.1", @@ -287,6 +957,24 @@ "from": "buffer-shims@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" }, + "builtin-modules": { + "version": "1.1.1", + "from": "builtin-modules@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "from": "builtin-status-codes@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "dev": true + }, + "bytes": { + "version": "2.3.0", + "from": "bytes@2.3.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.3.0.tgz", + "dev": true + }, "call-matcher": { "version": "1.0.1", "from": "call-matcher@>=1.0.1 <2.0.0", @@ -299,11 +987,61 @@ } } }, + "caller-path": { + "version": "0.1.0", + "from": "caller-path@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "dev": true + }, + "callsites": { + "version": "0.2.0", + "from": "callsites@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "dev": true + }, + "camel-case": { + "version": "3.0.0", + "from": "camel-case@>=3.0.0 <3.1.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "dev": true + }, "camelcase": { "version": "1.2.1", "from": "camelcase@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz" }, + "camelcase-keys": { + "version": "2.1.0", + "from": "camelcase-keys@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "dev": true, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "from": "camelcase@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "dev": true + } + } + }, + "caniuse-api": { + "version": "1.6.1", + "from": "caniuse-api@>=1.5.2 <2.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", + "dev": true + }, + "caniuse-db": { + "version": "1.0.30000664", + "from": "caniuse-db@>=1.0.30000634 <2.0.0", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000664.tgz", + "dev": true + }, + "cardinal": { + "version": "1.0.0", + "from": "cardinal@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-1.0.0.tgz", + "dev": true + }, "caseless": { "version": "0.11.0", "from": "caseless@>=0.11.0 <0.12.0", @@ -343,6 +1081,18 @@ } } }, + "chokidar": { + "version": "1.6.1", + "from": "chokidar@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.6.1.tgz", + "dev": true + }, + "circular-json": { + "version": "0.3.1", + "from": "circular-json@>=0.3.1 <0.4.0", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.1.tgz", + "dev": true + }, "circumcenter": { "version": "1.0.0", "from": "circumcenter@>=1.0.0 <2.0.0", @@ -358,11 +1108,55 @@ "from": "clamp@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz" }, + "clap": { + "version": "1.1.3", + "from": "clap@>=1.0.9 <2.0.0", + "resolved": "https://registry.npmjs.org/clap/-/clap-1.1.3.tgz", + "dev": true + }, + "clean-css": { + "version": "4.0.12", + "from": "clean-css@>=4.0.0 <4.1.0", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.0.12.tgz", + "dev": true + }, "clean-pslg": { "version": "1.1.2", "from": "clean-pslg@>=1.1.0 <2.0.0", "resolved": "https://registry.npmjs.org/clean-pslg/-/clean-pslg-1.1.2.tgz" }, + "cli-cursor": { + "version": "1.0.2", + "from": "cli-cursor@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "dev": true + }, + "cli-table": { + "version": "0.3.1", + "from": "cli-table@>=0.3.1 <0.4.0", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", + "dev": true, + "dependencies": { + "colors": { + "version": "1.0.3", + "from": "colors@1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "dev": true + } + } + }, + "cli-usage": { + "version": "0.1.4", + "from": "cli-usage@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/cli-usage/-/cli-usage-0.1.4.tgz", + "dev": true + }, + "cli-width": { + "version": "2.1.0", + "from": "cli-width@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", + "dev": true + }, "cliui": { "version": "2.1.0", "from": "cliui@>=2.1.0 <3.0.0", @@ -380,6 +1174,36 @@ "from": "clone@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz" }, + "co": { + "version": "4.6.0", + "from": "co@>=4.6.0 <5.0.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "dev": true + }, + "coa": { + "version": "1.0.1", + "from": "coa@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.1.tgz", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "from": "code-point-at@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "dev": true + }, + "color": { + "version": "0.11.4", + "from": "color@>=0.11.0 <0.12.0", + "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", + "dev": true + }, + "color-convert": { + "version": "1.9.0", + "from": "color-convert@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", + "dev": true + }, "color-id": { "version": "1.0.3", "from": "color-id@>=1.0.0 <2.0.0", @@ -405,11 +1229,28 @@ "from": "color-space@>=1.14.6 <2.0.0", "resolved": "https://registry.npmjs.org/color-space/-/color-space-1.14.7.tgz" }, + "color-string": { + "version": "0.3.0", + "from": "color-string@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", + "dev": true + }, "colormap": { "version": "2.2.0", "from": "colormap@>=2.1.0 <3.0.0", "resolved": "https://registry.npmjs.org/colormap/-/colormap-2.2.0.tgz" }, + "colormin": { + "version": "1.1.2", + "from": "colormin@>=1.0.5 <2.0.0", + "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", + "dev": true + }, + "colors": { + "version": "0.6.2", + "from": "colors@>=0.6.0-1 <0.7.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" + }, "combined-stream": { "version": "1.0.5", "from": "combined-stream@>=1.0.5 <1.1.0", @@ -420,6 +1261,12 @@ "from": "commander@>=2.9.0 <2.10.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz" }, + "commondir": { + "version": "1.0.1", + "from": "commondir@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "dev": true + }, "compare-angle": { "version": "1.0.1", "from": "compare-angle@>=1.0.0 <2.0.0", @@ -435,6 +1282,26 @@ "from": "compare-oriented-cell@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/compare-oriented-cell/-/compare-oriented-cell-1.0.1.tgz" }, + "compressible": { + "version": "2.0.10", + "from": "compressible@>=2.0.8 <2.1.0", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.10.tgz", + "dev": true, + "dependencies": { + "mime-db": { + "version": "1.27.0", + "from": "mime-db@>=1.27.0 <2.0.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", + "dev": true + } + } + }, + "compression": { + "version": "1.6.2", + "from": "compression@>=1.5.2 <2.0.0", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.6.2.tgz", + "dev": true + }, "concat-map": { "version": "0.0.1", "from": "concat-map@0.0.1", @@ -457,6 +1324,48 @@ } } }, + "connect-history-api-fallback": { + "version": "1.3.0", + "from": "connect-history-api-fallback@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz", + "dev": true + }, + "console-browserify": { + "version": "1.1.0", + "from": "console-browserify@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "from": "console-control-strings@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "from": "constants-browserify@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "dev": true + }, + "contains-path": { + "version": "0.1.0", + "from": "contains-path@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "dev": true + }, + "content-disposition": { + "version": "0.5.2", + "from": "content-disposition@0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "dev": true + }, + "content-type": { + "version": "1.0.2", + "from": "content-type@>=1.0.2 <1.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", + "dev": true + }, "convert-source-map": { "version": "1.3.0", "from": "convert-source-map@>=1.1.0 <2.0.0", @@ -467,6 +1376,18 @@ "from": "convex-hull@>=1.0.3 <2.0.0", "resolved": "https://registry.npmjs.org/convex-hull/-/convex-hull-1.0.3.tgz" }, + "cookie": { + "version": "0.3.1", + "from": "cookie@0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "from": "cookie-signature@1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "dev": true + }, "core-js": { "version": "2.4.1", "from": "core-js@>=2.4.0 <3.0.0", @@ -487,21 +1408,95 @@ "from": "country-regex@>=1.1.0 <2.0.0", "resolved": "https://registry.npmjs.org/country-regex/-/country-regex-1.1.0.tgz" }, + "cross-spawn": { + "version": "3.0.1", + "from": "cross-spawn@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "dev": true + }, "cryptiles": { "version": "2.0.5", "from": "cryptiles@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" }, + "crypto-browserify": { + "version": "3.3.0", + "from": "crypto-browserify@3.3.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.3.0.tgz", + "dev": true + }, + "css-color-names": { + "version": "0.0.4", + "from": "css-color-names@0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "dev": true + }, + "css-loader": { + "version": "0.25.0", + "from": "css-loader@>=0.25.0 <0.26.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.25.0.tgz", + "dev": true + }, + "css-select": { + "version": "1.2.0", + "from": "css-select@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "dev": true + }, + "css-selector-tokenizer": { + "version": "0.6.0", + "from": "css-selector-tokenizer@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.6.0.tgz", + "dev": true, + "dependencies": { + "regexpu-core": { + "version": "1.0.0", + "from": "regexpu-core@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "dev": true + } + } + }, + "css-what": { + "version": "2.1.0", + "from": "css-what@>=2.1.0 <2.2.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", + "dev": true + }, "csscolorparser": { "version": "1.0.3", "from": "csscolorparser@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz" }, + "cssesc": { + "version": "0.1.0", + "from": "cssesc@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "dev": true + }, + "cssnano": { + "version": "3.10.0", + "from": "cssnano@>=2.6.1 <4.0.0", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", + "dev": true + }, + "csso": { + "version": "2.3.2", + "from": "csso@>=2.3.1 <2.4.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", + "dev": true + }, "cubic-hermite": { "version": "1.0.0", "from": "cubic-hermite@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/cubic-hermite/-/cubic-hermite-1.0.0.tgz" }, + "currently-unhandled": { + "version": "0.4.1", + "from": "currently-unhandled@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "dev": true + }, "cwise": { "version": "1.0.10", "from": "cwise@>=1.0.3 <2.0.0", @@ -515,14 +1510,13 @@ "cwise-parser": { "version": "1.0.3", "from": "cwise-parser@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/cwise-parser/-/cwise-parser-1.0.3.tgz", - "dependencies": { - "esprima": { - "version": "1.2.5", - "from": "esprima@>=1.0.3 <2.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.5.tgz" - } - } + "resolved": "https://registry.npmjs.org/cwise-parser/-/cwise-parser-1.0.3.tgz" + }, + "d": { + "version": "1.0.0", + "from": "d@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "dev": true }, "d3": { "version": "3.5.17", @@ -551,6 +1545,12 @@ } } }, + "date-now": { + "version": "0.1.4", + "from": "date-now@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "dev": true + }, "debug": { "version": "2.2.0", "from": "debug@latest", @@ -581,6 +1581,12 @@ "from": "defined@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz" }, + "del": { + "version": "2.2.2", + "from": "del@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "dev": true + }, "delaunay-triangulate": { "version": "1.1.6", "from": "delaunay-triangulate@>=1.1.6 <2.0.0", @@ -591,6 +1597,88 @@ "from": "delayed-stream@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" }, + "delegates": { + "version": "1.0.0", + "from": "delegates@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "dev": true + }, + "depd": { + "version": "1.1.0", + "from": "depd@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz", + "dev": true + }, + "destroy": { + "version": "1.0.4", + "from": "destroy@>=1.0.4 <1.1.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "dev": true + }, + "detect-indent": { + "version": "4.0.0", + "from": "detect-indent@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "dev": true + }, + "doctrine": { + "version": "2.0.0", + "from": "doctrine@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", + "dev": true + }, + "dom-converter": { + "version": "0.1.4", + "from": "dom-converter@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz", + "dev": true, + "dependencies": { + "utila": { + "version": "0.3.3", + "from": "utila@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", + "dev": true + } + } + }, + "dom-serializer": { + "version": "0.1.0", + "from": "dom-serializer@>=0.0.0 <1.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "dev": true, + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "from": "domelementtype@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "dev": true + } + } + }, + "domain-browser": { + "version": "1.1.7", + "from": "domain-browser@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", + "dev": true + }, + "domelementtype": { + "version": "1.3.0", + "from": "domelementtype@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "dev": true + }, + "domhandler": { + "version": "2.1.0", + "from": "domhandler@>=2.1.0 <2.2.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", + "dev": true + }, + "domutils": { + "version": "1.5.1", + "from": "domutils@1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "dev": true + }, "double-bits": { "version": "1.1.1", "from": "double-bits@>=1.1.1 <2.0.0", @@ -634,11 +1722,61 @@ "from": "edges-to-adjacency-list@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/edges-to-adjacency-list/-/edges-to-adjacency-list-1.0.0.tgz" }, + "ee-first": { + "version": "1.1.1", + "from": "ee-first@1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.8", + "from": "electron-to-chromium@>=1.2.7 <2.0.0", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.8.tgz", + "dev": true + }, "emojis-list": { "version": "2.1.0", "from": "emojis-list@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz" }, + "encodeurl": { + "version": "1.0.1", + "from": "encodeurl@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "dev": true + }, + "enhanced-resolve": { + "version": "0.9.1", + "from": "enhanced-resolve@>=0.9.0 <0.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz", + "dev": true, + "dependencies": { + "memory-fs": { + "version": "0.2.0", + "from": "memory-fs@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", + "dev": true + } + } + }, + "entities": { + "version": "1.1.1", + "from": "entities@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "dev": true + }, + "errno": { + "version": "0.1.4", + "from": "errno@>=0.1.3 <0.2.0", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", + "dev": true + }, + "error-ex": { + "version": "1.3.1", + "from": "error-ex@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "dev": true + }, "es-abstract": { "version": "1.7.0", "from": "es-abstract@>=1.5.0 <2.0.0", @@ -649,26 +1787,63 @@ "from": "es-to-primitive@>=1.1.1 <2.0.0", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz" }, + "es5-ext": { + "version": "0.10.15", + "from": "es5-ext@>=0.10.14 <0.11.0", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.15.tgz", + "dev": true + }, + "es6-iterator": { + "version": "2.0.1", + "from": "es6-iterator@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", + "dev": true + }, + "es6-map": { + "version": "0.1.5", + "from": "es6-map@>=0.1.3 <0.2.0", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "dev": true + }, "es6-promise": { "version": "3.3.1", "from": "es6-promise@>=3.0.2 <4.0.0", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz" }, - "escape-string-regexp": { - "version": "1.0.5", - "from": "escape-string-regexp@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + "es6-set": { + "version": "0.1.5", + "from": "es6-set@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "dev": true }, - "escodegen": { - "version": "1.3.3", + "es6-symbol": { + "version": "3.1.1", + "from": "es6-symbol@>=3.1.1 <3.2.0", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "dev": true + }, + "es6-weak-map": { + "version": "2.0.2", + "from": "es6-weak-map@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "from": "escape-html@>=1.0.3 <1.1.0", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + }, + "escodegen": { + "version": "1.3.3", "from": "escodegen@>=1.3.2 <1.4.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.3.3.tgz", "dependencies": { - "esprima": { - "version": "1.1.1", - "from": "esprima@>=1.1.1 <1.2.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz" - }, "esutils": { "version": "1.0.0", "from": "esutils@>=1.0.0 <1.1.0", @@ -682,16 +1857,144 @@ } } }, + "escope": { + "version": "3.6.0", + "from": "escope@>=3.6.0 <4.0.0", + "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "dev": true, + "dependencies": { + "estraverse": { + "version": "4.2.0", + "from": "estraverse@>=4.1.1 <5.0.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "dev": true + } + } + }, + "eslint": { + "version": "3.19.0", + "from": "eslint@>=3.9.0 <4.0.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", + "dev": true, + "dependencies": { + "concat-stream": { + "version": "1.6.0", + "from": "concat-stream@>=1.5.2 <2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "dev": true + }, + "estraverse": { + "version": "4.2.0", + "from": "estraverse@>=4.2.0 <5.0.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "dev": true + }, + "readable-stream": { + "version": "2.2.6", + "from": "readable-stream@>=2.2.2 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", + "dev": true + } + } + }, + "eslint-config-airbnb-base": { + "version": "9.0.0", + "from": "eslint-config-airbnb-base@>=9.0.0 <10.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-9.0.0.tgz", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.2.3", + "from": "eslint-import-resolver-node@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz", + "dev": true + }, + "eslint-loader": { + "version": "1.7.1", + "from": "eslint-loader@>=1.6.0 <2.0.0", + "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-1.7.1.tgz", + "dev": true, + "dependencies": { + "loader-utils": { + "version": "1.1.0", + "from": "loader-utils@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "dev": true + } + } + }, + "eslint-module-utils": { + "version": "2.0.0", + "from": "eslint-module-utils@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.0.0.tgz", + "dev": true + }, + "eslint-plugin-import": { + "version": "2.2.0", + "from": "eslint-plugin-import@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.2.0.tgz", + "dev": true, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "from": "doctrine@1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "dev": true + } + } + }, + "espree": { + "version": "3.4.2", + "from": "espree@>=3.4.0 <4.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.4.2.tgz", + "dev": true, + "dependencies": { + "acorn": { + "version": "5.0.3", + "from": "acorn@>=5.0.1 <6.0.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.0.3.tgz", + "dev": true + } + } + }, "esprima": { - "version": "2.7.3", - "from": "esprima@>=2.6.0 <3.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz" + "version": "1.1.1", + "from": "esprima@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz" }, "espurify": { "version": "1.7.0", "from": "espurify@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/espurify/-/espurify-1.7.0.tgz" }, + "esquery": { + "version": "1.0.0", + "from": "esquery@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", + "dev": true, + "dependencies": { + "estraverse": { + "version": "4.2.0", + "from": "estraverse@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.1.0", + "from": "esrecurse@>=4.1.0 <5.0.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.1.0.tgz", + "dev": true, + "dependencies": { + "estraverse": { + "version": "4.1.1", + "from": "estraverse@>=4.1.0 <4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.1.1.tgz", + "dev": true + } + } + }, "estraverse": { "version": "1.5.1", "from": "estraverse@>=1.5.0 <1.6.0", @@ -702,21 +2005,95 @@ "from": "esutils@>=2.0.2 <3.0.0", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz" }, + "etag": { + "version": "1.8.0", + "from": "etag@>=1.8.0 <1.9.0", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz", + "dev": true + }, + "event-emitter": { + "version": "0.3.5", + "from": "event-emitter@>=0.3.5 <0.4.0", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "dev": true + }, + "eventemitter3": { + "version": "1.2.0", + "from": "eventemitter3@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", + "dev": true + }, "events": { "version": "1.1.1", "from": "events@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz" }, + "eventsource": { + "version": "0.1.6", + "from": "eventsource@0.1.6", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", + "dev": true + }, + "exit-hook": { + "version": "1.1.1", + "from": "exit-hook@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "dev": true + }, + "expand-brackets": { + "version": "0.1.5", + "from": "expand-brackets@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "dev": true + }, + "expand-range": { + "version": "1.8.2", + "from": "expand-range@>=1.8.1 <2.0.0", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "dev": true + }, + "express": { + "version": "4.15.2", + "from": "express@>=4.13.3 <5.0.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.15.2.tgz", + "dev": true, + "dependencies": { + "debug": { + "version": "2.6.1", + "from": "debug@2.6.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.1.tgz", + "dev": true + }, + "ms": { + "version": "0.7.2", + "from": "ms@0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "dev": true + } + } + }, "extend": { "version": "3.0.0", "from": "extend@>=3.0.0 <3.1.0", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" }, + "extglob": { + "version": "0.3.2", + "from": "extglob@>=0.3.1 <0.4.0", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "dev": true + }, "extract-frustum-planes": { "version": "1.0.0", "from": "extract-frustum-planes@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/extract-frustum-planes/-/extract-frustum-planes-1.0.0.tgz" }, + "extract-text-webpack-plugin": { + "version": "1.0.1", + "from": "extract-text-webpack-plugin@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-1.0.1.tgz", + "dev": true + }, "extsprintf": { "version": "1.0.2", "from": "extsprintf@1.0.2", @@ -727,11 +2104,6 @@ "from": "falafel@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/falafel/-/falafel-1.2.0.tgz", "dependencies": { - "acorn": { - "version": "1.2.2", - "from": "acorn@>=1.0.3 <2.0.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz" - }, "isarray": { "version": "0.0.1", "from": "isarray@0.0.1", @@ -749,26 +2121,95 @@ "from": "fast-levenshtein@>=2.0.4 <2.1.0", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" }, + "fastparse": { + "version": "1.1.1", + "from": "fastparse@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", + "dev": true + }, + "faye-websocket": { + "version": "0.10.0", + "from": "faye-websocket@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "dev": true + }, "feature-filter": { "version": "2.2.0", "from": "feature-filter@>=2.2.0 <3.0.0", "resolved": "https://registry.npmjs.org/feature-filter/-/feature-filter-2.2.0.tgz" }, + "figures": { + "version": "1.7.0", + "from": "figures@>=1.3.5 <2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "dev": true + }, + "file-entry-cache": { + "version": "2.0.0", + "from": "file-entry-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "dev": true + }, + "file-loader": { + "version": "0.9.0", + "from": "file-loader@>=0.9.0 <0.10.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-0.9.0.tgz", + "dev": true + }, + "filename-regex": { + "version": "2.0.1", + "from": "filename-regex@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "dev": true + }, + "fill-range": { + "version": "2.2.3", + "from": "fill-range@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "dev": true + }, "filtered-vector": { "version": "1.2.4", "from": "filtered-vector@>=1.2.1 <2.0.0", "resolved": "https://registry.npmjs.org/filtered-vector/-/filtered-vector-1.2.4.tgz" }, + "finalhandler": { + "version": "1.0.2", + "from": "finalhandler@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.2.tgz", + "dev": true, + "dependencies": { + "debug": { + "version": "2.6.4", + "from": "debug@2.6.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.4.tgz", + "dev": true + }, + "ms": { + "version": "0.7.3", + "from": "ms@0.7.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "0.1.1", + "from": "find-cache-dir@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", + "dev": true + }, + "find-up": { + "version": "1.1.2", + "from": "find-up@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "dev": true + }, "findup": { "version": "0.1.5", "from": "findup@>=0.1.5 <0.2.0", "resolved": "https://registry.npmjs.org/findup/-/findup-0.1.5.tgz", "dependencies": { - "colors": { - "version": "0.6.2", - "from": "colors@>=0.6.0-1 <0.7.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" - }, "commander": { "version": "2.1.0", "from": "commander@>=2.1.0 <2.2.0", @@ -776,10 +2217,22 @@ } } }, + "flat-cache": { + "version": "1.2.2", + "from": "flat-cache@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", + "dev": true + }, + "flatten": { + "version": "1.0.2", + "from": "flatten@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", + "dev": true + }, "font-atlas-sdf": { - "version": "1.1.3", + "version": "1.2.0", "from": "font-atlas-sdf@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/font-atlas-sdf/-/font-atlas-sdf-1.1.3.tgz" + "resolved": "https://registry.npmjs.org/font-atlas-sdf/-/font-atlas-sdf-1.2.0.tgz" }, "font-awesome": { "version": "4.7.0", @@ -791,6 +2244,18 @@ "from": "for-each@>=0.3.2 <0.4.0", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.2.tgz" }, + "for-in": { + "version": "1.0.2", + "from": "for-in@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "dev": true + }, + "for-own": { + "version": "0.1.5", + "from": "for-own@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "dev": true + }, "foreach": { "version": "2.0.5", "from": "foreach@>=2.0.5 <3.0.0", @@ -806,11 +2271,29 @@ "from": "form-data@>=2.1.1 <2.2.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz" }, + "forwarded": { + "version": "0.1.0", + "from": "forwarded@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz", + "dev": true + }, + "fresh": { + "version": "0.5.0", + "from": "fresh@0.5.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.0.tgz", + "dev": true + }, "fs.realpath": { "version": "1.0.0", "from": "fs.realpath@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" }, + "fstream": { + "version": "1.0.11", + "from": "fstream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "dev": true + }, "function-bind": { "version": "1.1.0", "from": "function-bind@>=1.0.2 <2.0.0", @@ -826,6 +2309,18 @@ "from": "gamma@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/gamma/-/gamma-0.1.0.tgz" }, + "gauge": { + "version": "2.7.4", + "from": "gauge@>=2.7.1 <2.8.0", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "dev": true + }, + "gaze": { + "version": "1.1.2", + "from": "gaze@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", + "dev": true + }, "generate-function": { "version": "2.0.0", "from": "generate-function@>=2.0.0 <3.0.0", @@ -863,11 +2358,23 @@ "from": "geojson-vt@>=2.4.0 <3.0.0", "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-2.4.0.tgz" }, + "get-caller-file": { + "version": "1.0.2", + "from": "get-caller-file@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "dev": true + }, "get-canvas-context": { "version": "1.0.2", "from": "get-canvas-context@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/get-canvas-context/-/get-canvas-context-1.0.2.tgz" }, + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "dev": true + }, "getpass": { "version": "0.1.6", "from": "getpass@>=0.1.1 <0.2.0", @@ -900,20 +2407,10 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@~1.0.26", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -959,20 +2456,10 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@~1.0.26", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -996,20 +2483,10 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@~1.0.26", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -1055,20 +2532,10 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@~1.0.26", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -1092,20 +2559,10 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@~1.0.26", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -1154,20 +2611,10 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@~1.0.26", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -1201,27 +2648,17 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@~1.0.26", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, "gl-plot3d": { - "version": "1.5.3", - "from": "gl-plot3d@>=1.5.2 <2.0.0", - "resolved": "https://registry.npmjs.org/gl-plot3d/-/gl-plot3d-1.5.3.tgz", + "version": "1.5.4", + "from": "gl-plot3d@>=1.5.4 <2.0.0", + "resolved": "https://registry.npmjs.org/gl-plot3d/-/gl-plot3d-1.5.4.tgz", "dependencies": { "bl": { "version": "0.9.5", @@ -1238,20 +2675,10 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@~1.0.26", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -1280,20 +2707,10 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@~1.0.26", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -1327,21 +2744,11 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@~1.0.26", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" - }, "snap-points-2d": { "version": "3.1.0", "from": "snap-points-2d@>=3.0.0 <4.0.0", @@ -1351,7 +2758,7 @@ }, "gl-scatter2d-sdf": { "version": "1.3.4", - "from": "gl-scatter2d-sdf@>=1.3.3 <2.0.0", + "from": "gl-scatter2d-sdf@1.3.4", "resolved": "https://registry.npmjs.org/gl-scatter2d-sdf/-/gl-scatter2d-sdf-1.3.4.tgz", "dependencies": { "binary-search-bounds": { @@ -1379,21 +2786,11 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@~1.0.26", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" - }, "snap-points-2d": { "version": "3.1.0", "from": "snap-points-2d@>=3.1.0 <4.0.0", @@ -1421,20 +2818,10 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@~1.0.26", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -1458,20 +2845,10 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@~1.0.26", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -1510,20 +2887,10 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@~1.0.26", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -1552,20 +2919,10 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@~1.0.26", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -1594,6 +2951,44 @@ "from": "glob@>=7.0.3 <8.0.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz" }, + "glob-base": { + "version": "0.3.0", + "from": "glob-base@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "dev": true + }, + "glob-parent": { + "version": "2.0.0", + "from": "glob-parent@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "dev": true + }, + "globals": { + "version": "9.17.0", + "from": "globals@>=9.0.0 <10.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.17.0.tgz", + "dev": true + }, + "globby": { + "version": "5.0.0", + "from": "globby@>=5.0.0 <6.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "dev": true + }, + "globule": { + "version": "1.1.0", + "from": "globule@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.1.0.tgz", + "dev": true, + "dependencies": { + "lodash": { + "version": "4.16.6", + "from": "lodash@>=4.16.4 <4.17.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.16.6.tgz", + "dev": true + } + } + }, "glsl-inject-defines": { "version": "1.0.3", "from": "glsl-inject-defines@>=1.0.1 <2.0.0", @@ -1714,9 +3109,9 @@ "resolved": "https://registry.npmjs.org/glslify-deps/-/glslify-deps-1.3.0.tgz" }, "graceful-fs": { - "version": "4.1.9", + "version": "4.1.11", "from": "graceful-fs@>=4.1.2 <5.0.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.9.tgz" + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" }, "graceful-readlink": { "version": "1.0.1", @@ -1728,6 +3123,12 @@ "from": "grid-index@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.0.0.tgz" }, + "growly": { + "version": "1.3.0", + "from": "growly@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "dev": true + }, "har-validator": { "version": "2.0.6", "from": "har-validator@>=2.0.6 <2.1.0", @@ -1748,36 +3149,186 @@ "from": "has-color@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz" }, + "has-flag": { + "version": "1.0.0", + "from": "has-flag@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "from": "has-unicode@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "dev": true + }, "hawk": { "version": "3.1.3", "from": "hawk@>=3.1.3 <3.2.0", "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz" }, + "he": { + "version": "1.1.1", + "from": "he@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "dev": true + }, "hoek": { "version": "2.16.3", "from": "hoek@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" }, - "http-signature": { + "home-or-tmp": { + "version": "2.0.0", + "from": "home-or-tmp@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "dev": true + }, + "hosted-git-info": { + "version": "2.4.2", + "from": "hosted-git-info@>=2.1.4 <3.0.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.4.2.tgz", + "dev": true + }, + "html-comment-regex": { + "version": "1.1.1", + "from": "html-comment-regex@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", + "dev": true + }, + "html-minifier": { + "version": "3.4.3", + "from": "html-minifier@>=3.2.3 <4.0.0", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.4.3.tgz", + "dev": true, + "dependencies": { + "uglify-js": { + "version": "2.8.22", + "from": "uglify-js@>=2.8.22 <2.9.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.22.tgz", + "dev": true + } + } + }, + "html-webpack-plugin": { + "version": "2.28.0", + "from": "html-webpack-plugin@>=2.24.0 <3.0.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-2.28.0.tgz", + "dev": true + }, + "htmlparser2": { + "version": "3.3.0", + "from": "htmlparser2@>=3.3.0 <3.4.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "dev": true, + "dependencies": { + "domutils": { + "version": "1.1.6", + "from": "domutils@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", + "dev": true + } + } + }, + "http-errors": { + "version": "1.6.1", + "from": "http-errors@>=1.6.1 <1.7.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.1.tgz", + "dev": true + }, + "http-proxy": { + "version": "1.16.2", + "from": "http-proxy@>=1.16.2 <2.0.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", + "dev": true + }, + "http-proxy-middleware": { + "version": "0.17.4", + "from": "http-proxy-middleware@>=0.17.1 <0.18.0", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz", + "dev": true, + "dependencies": { + "is-extglob": { + "version": "2.1.1", + "from": "is-extglob@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "dev": true + }, + "is-glob": { + "version": "3.1.0", + "from": "is-glob@>=3.1.0 <4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "dev": true + } + } + }, + "http-signature": { "version": "1.1.1", "from": "http-signature@>=1.1.0 <1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz" }, + "https-browserify": { + "version": "0.0.1", + "from": "https-browserify@0.0.1", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", + "dev": true + }, "husl": { "version": "5.0.3", "from": "husl@>=5.0.0 <6.0.0", "resolved": "https://registry.npmjs.org/husl/-/husl-5.0.3.tgz" }, + "icss-replace-symbols": { + "version": "1.0.2", + "from": "icss-replace-symbols@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.0.2.tgz", + "dev": true + }, "ieee754": { "version": "1.1.8", "from": "ieee754@>=1.1.4 <2.0.0", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz" }, + "ignore": { + "version": "3.2.7", + "from": "ignore@>=3.2.0 <4.0.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.2.7.tgz", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "from": "imurmurhash@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "dev": true + }, + "in-publish": { + "version": "2.0.0", + "from": "in-publish@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", + "dev": true + }, "incremental-convex-hull": { "version": "1.0.1", "from": "incremental-convex-hull@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/incremental-convex-hull/-/incremental-convex-hull-1.0.1.tgz" }, + "indent-string": { + "version": "2.1.0", + "from": "indent-string@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "dev": true + }, + "indexes-of": { + "version": "1.0.1", + "from": "indexes-of@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "from": "indexof@0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "dev": true + }, "inflight": { "version": "1.0.6", "from": "inflight@>=1.0.4 <2.0.0", @@ -1788,11 +3339,35 @@ "from": "inherits@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, + "inquirer": { + "version": "0.12.0", + "from": "inquirer@>=0.12.0 <0.13.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", + "dev": true + }, + "interpret": { + "version": "1.0.3", + "from": "interpret@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz", + "dev": true + }, "interval-tree-1d": { "version": "1.0.3", "from": "interval-tree-1d@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/interval-tree-1d/-/interval-tree-1d-1.0.3.tgz" }, + "invariant": { + "version": "2.2.2", + "from": "invariant@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "dev": true + }, + "invert-kv": { + "version": "1.0.0", + "from": "invert-kv@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "dev": true + }, "invert-permutation": { "version": "1.0.0", "from": "invert-permutation@>=1.0.0 <2.0.0", @@ -1803,11 +3378,41 @@ "from": "iota-array@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz" }, + "ipaddr.js": { + "version": "1.3.0", + "from": "ipaddr.js@1.3.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.3.0.tgz", + "dev": true + }, + "is-absolute-url": { + "version": "2.1.0", + "from": "is-absolute-url@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "from": "is-arrayish@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "from": "is-binary-path@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "dev": true + }, "is-buffer": { "version": "1.1.4", "from": "is-buffer@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.4.tgz" }, + "is-builtin-module": { + "version": "1.0.0", + "from": "is-builtin-module@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "dev": true + }, "is-callable": { "version": "1.1.3", "from": "is-callable@>=1.1.3 <2.0.0", @@ -1818,11 +3423,53 @@ "from": "is-date-object@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz" }, + "is-dotfile": { + "version": "1.0.2", + "from": "is-dotfile@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.2.tgz", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "from": "is-equal-shallow@>=0.1.3 <0.2.0", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "dev": true + }, + "is-extendable": { + "version": "0.1.1", + "from": "is-extendable@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "dev": true + }, + "is-extglob": { + "version": "1.0.0", + "from": "is-extglob@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "from": "is-finite@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "dev": true + }, "is-function": { "version": "1.0.1", "from": "is-function@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz" }, + "is-glob": { + "version": "2.0.1", + "from": "is-glob@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "dev": true + }, "is-mobile": { "version": "0.2.2", "from": "is-mobile@>=0.2.2 <0.3.0", @@ -1833,11 +3480,47 @@ "from": "is-my-json-valid@>=2.10.0 <3.0.0", "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz" }, + "is-number": { + "version": "2.1.0", + "from": "is-number@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "dev": true + }, + "is-path-cwd": { + "version": "1.0.0", + "from": "is-path-cwd@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.0", + "from": "is-path-in-cwd@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", + "dev": true + }, + "is-path-inside": { + "version": "1.0.0", + "from": "is-path-inside@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", + "dev": true + }, "is-plain-obj": { "version": "1.1.0", "from": "is-plain-obj@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz" }, + "is-posix-bracket": { + "version": "0.1.1", + "from": "is-posix-bracket@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "from": "is-primitive@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "dev": true + }, "is-property": { "version": "1.0.2", "from": "is-property@>=1.0.0 <2.0.0", @@ -1848,6 +3531,18 @@ "from": "is-regex@>=1.0.3 <2.0.0", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz" }, + "is-resolvable": { + "version": "1.0.0", + "from": "is-resolvable@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", + "dev": true + }, + "is-svg": { + "version": "2.1.0", + "from": "is-svg@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", + "dev": true + }, "is-symbol": { "version": "1.0.1", "from": "is-symbol@>=1.0.1 <2.0.0", @@ -1858,11 +3553,29 @@ "from": "is-typedarray@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" }, + "is-utf8": { + "version": "0.2.1", + "from": "is-utf8@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "dev": true + }, "isarray": { "version": "1.0.0", "from": "isarray@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" }, + "isexe": { + "version": "2.0.0", + "from": "isexe@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "dev": true + }, + "isobject": { + "version": "2.1.0", + "from": "isobject@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "dev": true + }, "isstream": { "version": "0.1.2", "from": "isstream@>=0.1.2 <0.2.0", @@ -1884,27 +3597,77 @@ "from": "jquery-ui@latest", "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.12.1.tgz" }, + "js-base64": { + "version": "2.1.9", + "from": "js-base64@>=2.1.9 <3.0.0", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "dev": true + }, + "js-tokens": { + "version": "3.0.1", + "from": "js-tokens@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz", + "dev": true + }, + "js-yaml": { + "version": "3.7.0", + "from": "js-yaml@>=3.7.0 <3.8.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", + "dev": true, + "dependencies": { + "esprima": { + "version": "2.7.3", + "from": "esprima@>=2.6.0 <3.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "dev": true + } + } + }, "jsbn": { "version": "0.1.0", "from": "jsbn@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.0.tgz", "optional": true }, + "jsesc": { + "version": "1.3.0", + "from": "jsesc@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "dev": true + }, "json-schema": { "version": "0.2.3", "from": "json-schema@0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" }, + "json-stable-stringify": { + "version": "1.0.1", + "from": "json-stable-stringify@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "dev": true + }, "json-stringify-safe": { "version": "5.0.1", "from": "json-stringify-safe@>=5.0.1 <5.1.0", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" }, + "json3": { + "version": "3.3.2", + "from": "json3@>=3.3.2 <4.0.0", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "dev": true + }, "json5": { "version": "0.5.0", "from": "json5@>=0.5.0 <0.6.0", "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.0.tgz" }, + "jsonify": { + "version": "0.0.0", + "from": "jsonify@>=0.0.0 <0.1.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "dev": true + }, "jsonlint-lines-primitives": { "version": "1.6.0", "from": "jsonlint-lines-primitives@>=1.6.0 <1.7.0", @@ -1940,6 +3703,12 @@ "from": "lazy-cache@>=1.0.3 <2.0.0", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz" }, + "lcid": { + "version": "1.0.0", + "from": "lcid@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "dev": true + }, "leaflet": { "version": "1.0.2", "from": "leaflet@latest", @@ -1955,11 +3724,73 @@ "from": "levn@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz" }, + "load-json-file": { + "version": "1.1.0", + "from": "load-json-file@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "dev": true, + "dependencies": { + "strip-bom": { + "version": "2.0.0", + "from": "strip-bom@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "dev": true + } + } + }, + "loader-fs-cache": { + "version": "1.0.1", + "from": "loader-fs-cache@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz", + "dev": true + }, "loader-utils": { "version": "0.2.16", "from": "loader-utils@>=0.2.11 <0.3.0", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.16.tgz" }, + "lodash": { + "version": "4.17.4", + "from": "lodash@>=4.2.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "dev": true + }, + "lodash._arraycopy": { + "version": "3.0.0", + "from": "lodash._arraycopy@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz", + "dev": true + }, + "lodash._arrayeach": { + "version": "3.0.0", + "from": "lodash._arrayeach@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz", + "dev": true + }, + "lodash._baseassign": { + "version": "3.2.0", + "from": "lodash._baseassign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "dev": true + }, + "lodash._baseclone": { + "version": "3.3.0", + "from": "lodash._baseclone@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._baseclone/-/lodash._baseclone-3.3.0.tgz", + "dev": true + }, + "lodash._basecopy": { + "version": "3.0.1", + "from": "lodash._basecopy@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "dev": true + }, + "lodash._basefor": { + "version": "3.0.3", + "from": "lodash._basefor@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._basefor/-/lodash._basefor-3.0.3.tgz", + "dev": true + }, "lodash._baseisequal": { "version": "3.0.7", "from": "lodash._baseisequal@>=3.0.0 <4.0.0", @@ -1970,11 +3801,53 @@ "from": "lodash._bindcallback@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz" }, + "lodash._createcompounder": { + "version": "3.0.0", + "from": "lodash._createcompounder@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._createcompounder/-/lodash._createcompounder-3.0.0.tgz", + "dev": true + }, "lodash._getnative": { "version": "3.9.1", "from": "lodash._getnative@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz" }, + "lodash._root": { + "version": "3.0.1", + "from": "lodash._root@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "dev": true + }, + "lodash.assign": { + "version": "4.2.0", + "from": "lodash.assign@>=4.2.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "dev": true + }, + "lodash.camelcase": { + "version": "3.0.1", + "from": "lodash.camelcase@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-3.0.1.tgz", + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "from": "lodash.clonedeep@>=4.3.2 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "dev": true + }, + "lodash.cond": { + "version": "4.5.2", + "from": "lodash.cond@>=4.3.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz", + "dev": true + }, + "lodash.deburr": { + "version": "3.2.0", + "from": "lodash.deburr@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.deburr/-/lodash.deburr-3.2.0.tgz", + "dev": true + }, "lodash.isarguments": { "version": "3.1.0", "from": "lodash.isarguments@>=3.0.0 <4.0.0", @@ -2000,11 +3873,65 @@ "from": "lodash.keys@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz" }, + "lodash.memoize": { + "version": "4.1.2", + "from": "lodash.memoize@>=4.1.2 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "dev": true + }, + "lodash.mergewith": { + "version": "4.6.0", + "from": "lodash.mergewith@>=4.6.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "from": "lodash.uniq@>=4.5.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "dev": true + }, + "lodash.words": { + "version": "3.2.0", + "from": "lodash.words@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.words/-/lodash.words-3.2.0.tgz", + "dev": true + }, "longest": { "version": "1.0.1", "from": "longest@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz" }, + "loose-envify": { + "version": "1.3.1", + "from": "loose-envify@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "dev": true + }, + "loud-rejection": { + "version": "1.6.0", + "from": "loud-rejection@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "dev": true + }, + "lower-case": { + "version": "1.1.4", + "from": "lower-case@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "dev": true + }, + "lru-cache": { + "version": "4.0.2", + "from": "lru-cache@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", + "dev": true + }, + "macaddress": { + "version": "0.2.8", + "from": "macaddress@>=0.2.8 <0.3.0", + "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", + "dev": true + }, "map-limit": { "version": "0.0.1", "from": "map-limit@0.0.1", @@ -2017,6 +3944,12 @@ } } }, + "map-obj": { + "version": "1.0.1", + "from": "map-obj@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "dev": true + }, "mapbox-gl": { "version": "0.22.1", "from": "mapbox-gl@>=0.22.0 <0.23.0", @@ -2030,12 +3963,12 @@ "mapbox-gl-shaders": { "version": "1.0.0", "from": "mapbox/mapbox-gl-shaders#de2ab007455aa2587c552694c68583f94c9f2747", - "resolved": "https://github.com/mapbox/mapbox-gl-shaders.git#de2ab007455aa2587c552694c68583f94c9f2747" + "resolved": "git+https://github.com/mapbox/mapbox-gl-shaders.git#de2ab007455aa2587c552694c68583f94c9f2747" }, "mapbox-gl-style-spec": { "version": "8.8.0", "from": "mapbox/mapbox-gl-style-spec#83b1a3e5837d785af582efd5ed1a212f2df6a4ae", - "resolved": "https://github.com/mapbox/mapbox-gl-style-spec.git#83b1a3e5837d785af582efd5ed1a212f2df6a4ae" + "resolved": "git+https://github.com/mapbox/mapbox-gl-style-spec.git#83b1a3e5837d785af582efd5ed1a212f2df6a4ae" }, "mapbox-gl-supported": { "version": "1.2.0", @@ -2052,6 +3985,12 @@ "from": "marked@latest", "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz" }, + "marked-terminal": { + "version": "1.7.0", + "from": "marked-terminal@>=1.6.2 <2.0.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-1.7.0.tgz", + "dev": true + }, "mat4-decompose": { "version": "1.0.4", "from": "mat4-decompose@>=1.0.3 <2.0.0", @@ -2072,10 +4011,80 @@ "from": "material-design-iconic-font@latest", "resolved": "https://registry.npmjs.org/material-design-iconic-font/-/material-design-iconic-font-2.2.0.tgz" }, + "math-expression-evaluator": { + "version": "1.2.17", + "from": "math-expression-evaluator@>=1.2.14 <2.0.0", + "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", + "dev": true + }, "matrix-camera-controller": { - "version": "2.1.1", - "from": "matrix-camera-controller@>=2.1.1 <3.0.0", - "resolved": "https://registry.npmjs.org/matrix-camera-controller/-/matrix-camera-controller-2.1.1.tgz" + "version": "2.1.3", + "from": "matrix-camera-controller@>=2.1.3 <3.0.0", + "resolved": "https://registry.npmjs.org/matrix-camera-controller/-/matrix-camera-controller-2.1.3.tgz" + }, + "media-typer": { + "version": "0.3.0", + "from": "media-typer@0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "dev": true + }, + "memory-fs": { + "version": "0.3.0", + "from": "memory-fs@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.3.0.tgz", + "dev": true, + "dependencies": { + "readable-stream": { + "version": "2.2.9", + "from": "readable-stream@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", + "dev": true + }, + "string_decoder": { + "version": "1.0.0", + "from": "string_decoder@~1.0.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz", + "dev": true + } + } + }, + "meow": { + "version": "3.7.0", + "from": "meow@>=3.7.0 <4.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "dev": true, + "dependencies": { + "minimist": { + "version": "1.2.0", + "from": "minimist@^1.1.3", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "dev": true + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "from": "merge-descriptors@1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "dev": true + }, + "methods": { + "version": "1.1.2", + "from": "methods@>=1.1.2 <1.2.0", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "dev": true + }, + "micromatch": { + "version": "2.3.11", + "from": "micromatch@>=2.1.5 <3.0.0", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "dev": true + }, + "mime": { + "version": "1.3.4", + "from": "mime@>=1.3.0 <1.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", + "dev": true }, "mime-db": { "version": "1.24.0", @@ -2097,6 +4106,12 @@ "from": "minimist@0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dev": true + }, "moment": { "version": "2.15.2", "from": "moment@latest", @@ -2171,6 +4186,30 @@ "from": "mustache@latest", "resolved": "https://registry.npmjs.org/mustache/-/mustache-2.2.1.tgz" }, + "mute-stream": { + "version": "0.0.5", + "from": "mute-stream@0.0.5", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", + "dev": true + }, + "nan": { + "version": "2.6.2", + "from": "nan@>=2.3.2 <3.0.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "from": "natural-compare@>=1.4.0 <2.0.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "dev": true + }, + "ncname": { + "version": "1.0.0", + "from": "ncname@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/ncname/-/ncname-1.0.0.tgz", + "dev": true + }, "ndarray": { "version": "1.0.18", "from": "ndarray@>=1.0.16 <2.0.0", @@ -2226,6 +4265,12 @@ "from": "ndarray-warp@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/ndarray-warp/-/ndarray-warp-1.0.1.tgz" }, + "negotiator": { + "version": "0.6.1", + "from": "negotiator@0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "dev": true + }, "nextafter": { "version": "1.0.0", "from": "nextafter@>=1.0.0 <2.0.0", @@ -2253,6 +4298,72 @@ "from": "ng-annotate-loader@latest", "resolved": "https://registry.npmjs.org/ng-annotate-loader/-/ng-annotate-loader-0.2.0.tgz" }, + "no-case": { + "version": "2.3.1", + "from": "no-case@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz", + "dev": true + }, + "node-emoji": { + "version": "1.5.1", + "from": "node-emoji@>=1.4.1 <2.0.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.5.1.tgz", + "dev": true + }, + "node-gyp": { + "version": "3.6.0", + "from": "node-gyp@>=3.3.1 <4.0.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.0.tgz", + "dev": true + }, + "node-libs-browser": { + "version": "0.7.0", + "from": "node-libs-browser@>=0.7.0 <0.8.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-0.7.0.tgz", + "dev": true, + "dependencies": { + "readable-stream": { + "version": "2.2.9", + "from": "readable-stream@>=2.0.5 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", + "dev": true, + "dependencies": { + "string_decoder": { + "version": "1.0.0", + "from": "string_decoder@~1.0.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz", + "dev": true + } + } + } + } + }, + "node-notifier": { + "version": "4.6.1", + "from": "node-notifier@>=4.1.0 <5.0.0", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-4.6.1.tgz", + "dev": true, + "dependencies": { + "lodash.clonedeep": { + "version": "3.0.2", + "from": "lodash.clonedeep@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-3.0.2.tgz", + "dev": true + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@^1.1.1", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "dev": true + } + } + }, + "node-sass": { + "version": "4.5.2", + "from": "node-sass@>=4.3.0 <5.0.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.5.2.tgz", + "dev": true + }, "nomnom": { "version": "1.8.1", "from": "nomnom@>=1.5.0", @@ -2280,16 +4391,64 @@ } } }, + "nopt": { + "version": "3.0.6", + "from": "nopt@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "dev": true + }, + "normalize-package-data": { + "version": "2.3.8", + "from": "normalize-package-data@>=2.3.4 <3.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.8.tgz", + "dev": true + }, "normalize-path": { "version": "2.0.1", "from": "normalize-path@>=2.0.1 <3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.0.1.tgz" }, + "normalize-range": { + "version": "0.1.2", + "from": "normalize-range@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "dev": true + }, + "normalize-url": { + "version": "1.9.1", + "from": "normalize-url@>=1.4.0 <2.0.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "dev": true + }, "normals": { "version": "1.1.0", "from": "normals@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/normals/-/normals-1.1.0.tgz" }, + "npmlog": { + "version": "4.0.2", + "from": "npmlog@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz", + "dev": true + }, + "nth-check": { + "version": "1.0.1", + "from": "nth-check@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", + "dev": true + }, + "num2fraction": { + "version": "1.2.2", + "from": "num2fraction@>=1.2.2 <2.0.0", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "dev": true + }, "numeric": { "version": "1.2.6", "from": "numeric@>=1.2.6 <2.0.0", @@ -2305,6 +4464,12 @@ "from": "object-assign@>=4.0.1 <5.0.0", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz" }, + "object-hash": { + "version": "1.1.8", + "from": "object-hash@>=1.1.4 <2.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.1.8.tgz", + "dev": true + }, "object-inspect": { "version": "1.2.2", "from": "object-inspect@>=1.2.1 <1.3.0", @@ -2315,11 +4480,41 @@ "from": "object-keys@>=1.0.8 <2.0.0", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz" }, + "object.omit": { + "version": "2.0.1", + "from": "object.omit@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "from": "on-finished@>=2.3.0 <2.4.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "dev": true + }, + "on-headers": { + "version": "1.0.1", + "from": "on-headers@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", + "dev": true + }, "once": { "version": "1.4.0", "from": "once@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" }, + "onetime": { + "version": "1.1.0", + "from": "onetime@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "dev": true + }, + "open": { + "version": "0.0.5", + "from": "open@0.0.5", + "resolved": "https://registry.npmjs.org/open/-/open-0.0.5.tgz", + "dev": true + }, "optimist": { "version": "0.6.1", "from": "optimist@>=0.6.0 <0.7.0", @@ -2352,6 +4547,50 @@ "from": "ordered-esprima-props@>=1.1.0 <1.2.0", "resolved": "https://registry.npmjs.org/ordered-esprima-props/-/ordered-esprima-props-1.1.0.tgz" }, + "original": { + "version": "1.0.0", + "from": "original@>=0.0.5", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.0.tgz", + "dev": true, + "dependencies": { + "url-parse": { + "version": "1.0.5", + "from": "url-parse@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.0.5.tgz", + "dev": true + } + } + }, + "os-browserify": { + "version": "0.2.1", + "from": "os-browserify@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "from": "os-homedir@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "dev": true + }, + "os-locale": { + "version": "1.4.0", + "from": "os-locale@>=1.4.0 <2.0.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "from": "os-tmpdir@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "dev": true + }, + "osenv": { + "version": "0.1.4", + "from": "osenv@>=0.0.0 <1.0.0", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "dev": true + }, "pace-progress": { "version": "1.0.2", "from": "git+https://github.com/getredash/pace.git", @@ -2362,21 +4601,87 @@ "from": "pad-left@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/pad-left/-/pad-left-1.0.2.tgz" }, + "pako": { + "version": "0.2.9", + "from": "pako@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "dev": true + }, + "param-case": { + "version": "2.1.1", + "from": "param-case@>=2.1.0 <2.2.0", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "dev": true + }, + "parse-glob": { + "version": "3.0.4", + "from": "parse-glob@>=3.0.4 <4.0.0", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "dev": true + }, + "parse-json": { + "version": "2.2.0", + "from": "parse-json@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "dev": true + }, "parse-unit": { "version": "1.0.1", "from": "parse-unit@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/parse-unit/-/parse-unit-1.0.1.tgz" }, + "parseurl": { + "version": "1.3.1", + "from": "parseurl@>=1.3.1 <1.4.0", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", + "dev": true + }, + "path-browserify": { + "version": "0.0.0", + "from": "path-browserify@0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "from": "path-exists@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "from": "path-is-absolute@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" }, + "path-is-inside": { + "version": "1.0.2", + "from": "path-is-inside@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "from": "path-to-regexp@0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "from": "path-type@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "dev": true + }, "pbf": { "version": "1.3.7", "from": "pbf@>=1.3.2 <2.0.0", "resolved": "https://registry.npmjs.org/pbf/-/pbf-1.3.7.tgz" }, + "pbkdf2-compat": { + "version": "2.0.1", + "from": "pbkdf2-compat@2.0.1", + "resolved": "https://registry.npmjs.org/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz", + "dev": true + }, "permutation-parity": { "version": "1.0.0", "from": "permutation-parity@>=1.0.0 <2.0.0", @@ -2387,6 +4692,12 @@ "from": "permutation-rank@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/permutation-rank/-/permutation-rank-1.0.0.tgz" }, + "pify": { + "version": "2.3.0", + "from": "pify@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "dev": true + }, "pinkie": { "version": "2.0.4", "from": "pinkie@>=2.0.0 <3.0.0", @@ -2402,6 +4713,18 @@ "from": "pivottable@latest", "resolved": "https://registry.npmjs.org/pivottable/-/pivottable-2.3.0.tgz" }, + "pkg-dir": { + "version": "1.0.0", + "from": "pkg-dir@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "dev": true + }, + "pkg-up": { + "version": "1.0.0", + "from": "pkg-up@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-1.0.0.tgz", + "dev": true + }, "planar-dual": { "version": "1.0.2", "from": "planar-dual@>=1.0.0 <2.0.0", @@ -2413,9 +4736,15 @@ "resolved": "https://registry.npmjs.org/planar-graph-to-polyline/-/planar-graph-to-polyline-1.0.5.tgz" }, "plotly.js": { - "version": "1.25.0", - "from": "plotly.js@1.25.0", - "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-1.25.0.tgz" + "version": "1.26.1", + "from": "plotly.js@1.26.1", + "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-1.26.1.tgz" + }, + "pluralize": { + "version": "1.2.1", + "from": "pluralize@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", + "dev": true }, "pngjs": { "version": "2.3.1", @@ -2437,31 +4766,319 @@ "from": "polytope-closest-point@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/polytope-closest-point/-/polytope-closest-point-1.0.0.tgz" }, + "postcss": { + "version": "5.2.17", + "from": "postcss@>=5.0.6 <6.0.0", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "dev": true + }, + "postcss-calc": { + "version": "5.3.1", + "from": "postcss-calc@>=5.2.0 <6.0.0", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", + "dev": true + }, + "postcss-colormin": { + "version": "2.2.2", + "from": "postcss-colormin@>=2.1.8 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", + "dev": true + }, + "postcss-convert-values": { + "version": "2.6.1", + "from": "postcss-convert-values@>=2.3.4 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", + "dev": true + }, + "postcss-discard-comments": { + "version": "2.0.4", + "from": "postcss-discard-comments@>=2.0.4 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "dev": true + }, + "postcss-discard-duplicates": { + "version": "2.1.0", + "from": "postcss-discard-duplicates@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", + "dev": true + }, + "postcss-discard-empty": { + "version": "2.1.0", + "from": "postcss-discard-empty@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "dev": true + }, + "postcss-discard-overridden": { + "version": "0.1.1", + "from": "postcss-discard-overridden@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "dev": true + }, + "postcss-discard-unused": { + "version": "2.2.3", + "from": "postcss-discard-unused@>=2.2.1 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", + "dev": true + }, + "postcss-filter-plugins": { + "version": "2.0.2", + "from": "postcss-filter-plugins@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", + "dev": true + }, + "postcss-merge-idents": { + "version": "2.1.7", + "from": "postcss-merge-idents@>=2.1.5 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "dev": true + }, + "postcss-merge-longhand": { + "version": "2.0.2", + "from": "postcss-merge-longhand@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", + "dev": true + }, + "postcss-merge-rules": { + "version": "2.1.2", + "from": "postcss-merge-rules@>=2.0.3 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", + "dev": true + }, + "postcss-message-helpers": { + "version": "2.0.0", + "from": "postcss-message-helpers@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", + "dev": true + }, + "postcss-minify-font-values": { + "version": "1.0.5", + "from": "postcss-minify-font-values@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "dev": true + }, + "postcss-minify-gradients": { + "version": "1.0.5", + "from": "postcss-minify-gradients@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "dev": true + }, + "postcss-minify-params": { + "version": "1.2.2", + "from": "postcss-minify-params@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "dev": true + }, + "postcss-minify-selectors": { + "version": "2.1.1", + "from": "postcss-minify-selectors@>=2.0.4 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "dev": true + }, + "postcss-modules-extract-imports": { + "version": "1.0.1", + "from": "postcss-modules-extract-imports@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.1.tgz", + "dev": true + }, + "postcss-modules-local-by-default": { + "version": "1.1.1", + "from": "postcss-modules-local-by-default@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.1.1.tgz", + "dev": true + }, + "postcss-modules-scope": { + "version": "1.0.2", + "from": "postcss-modules-scope@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.0.2.tgz", + "dev": true + }, + "postcss-modules-values": { + "version": "1.2.2", + "from": "postcss-modules-values@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.2.2.tgz", + "dev": true + }, + "postcss-normalize-charset": { + "version": "1.1.1", + "from": "postcss-normalize-charset@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "dev": true + }, + "postcss-normalize-url": { + "version": "3.0.8", + "from": "postcss-normalize-url@>=3.0.7 <4.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "dev": true + }, + "postcss-ordered-values": { + "version": "2.2.3", + "from": "postcss-ordered-values@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", + "dev": true + }, + "postcss-reduce-idents": { + "version": "2.4.0", + "from": "postcss-reduce-idents@>=2.2.2 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", + "dev": true + }, + "postcss-reduce-initial": { + "version": "1.0.1", + "from": "postcss-reduce-initial@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "dev": true + }, + "postcss-reduce-transforms": { + "version": "1.0.4", + "from": "postcss-reduce-transforms@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "dev": true + }, + "postcss-selector-parser": { + "version": "2.2.3", + "from": "postcss-selector-parser@>=2.2.2 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", + "dev": true + }, + "postcss-svgo": { + "version": "2.1.6", + "from": "postcss-svgo@>=2.1.1 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "dev": true + }, + "postcss-unique-selectors": { + "version": "2.0.2", + "from": "postcss-unique-selectors@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "dev": true + }, + "postcss-value-parser": { + "version": "3.3.0", + "from": "postcss-value-parser@>=3.2.3 <4.0.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", + "dev": true + }, + "postcss-zindex": { + "version": "2.2.0", + "from": "postcss-zindex@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", + "dev": true + }, "prelude-ls": { "version": "1.1.2", "from": "prelude-ls@>=1.1.2 <1.2.0", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" }, + "prepend-http": { + "version": "1.0.4", + "from": "prepend-http@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "dev": true + }, + "preserve": { + "version": "0.2.0", + "from": "preserve@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "dev": true + }, + "pretty-error": { + "version": "2.1.0", + "from": "pretty-error@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.0.tgz", + "dev": true + }, + "private": { + "version": "0.1.7", + "from": "private@>=0.1.6 <0.2.0", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", + "dev": true + }, + "process": { + "version": "0.11.10", + "from": "process@>=0.11.0 <0.12.0", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "dev": true + }, "process-nextick-args": { "version": "1.0.7", "from": "process-nextick-args@>=1.0.6 <1.1.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" }, + "progress": { + "version": "1.1.8", + "from": "progress@>=1.1.8 <2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "dev": true + }, "protocol-buffers-schema": { "version": "2.2.0", "from": "protocol-buffers-schema@>=2.0.2 <3.0.0", "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-2.2.0.tgz" }, + "proxy-addr": { + "version": "1.1.4", + "from": "proxy-addr@>=1.1.3 <1.2.0", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.4.tgz", + "dev": true + }, + "prr": { + "version": "0.0.0", + "from": "prr@>=0.0.0 <0.1.0", + "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "from": "pseudomap@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "dev": true + }, "punycode": { "version": "1.4.1", "from": "punycode@>=1.2.4 <2.0.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" }, + "q": { + "version": "1.5.0", + "from": "q@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.0.tgz", + "dev": true + }, + "qs": { + "version": "6.4.0", + "from": "qs@6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "dev": true + }, "quat-slerp": { "version": "1.0.1", "from": "quat-slerp@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/quat-slerp/-/quat-slerp-1.0.1.tgz" }, + "query-string": { + "version": "4.3.4", + "from": "query-string@>=4.1.0 <5.0.0", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "from": "querystring@0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "from": "querystring-es3@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "dev": true + }, + "querystringify": { + "version": "0.0.4", + "from": "querystringify@>=0.0.0 <0.1.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-0.0.4.tgz", + "dev": true + }, "quickselect": { "version": "1.0.0", "from": "quickselect@>=1.0.0 <2.0.0", @@ -2472,21 +5089,11 @@ "from": "quote-stream@>=0.0.0 <0.1.0", "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-0.0.0.tgz", "dependencies": { - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, "object-keys": { "version": "0.4.0", "from": "object-keys@>=0.4.0 <0.5.0", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz" }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@~1.0.17", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" - }, "through2": { "version": "0.4.2", "from": "through2@>=0.4.1 <0.5.0", @@ -2499,31 +5106,214 @@ } } }, + "randomatic": { + "version": "1.1.6", + "from": "randomatic@>=1.1.3 <2.0.0", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.6.tgz", + "dev": true + }, + "range-parser": { + "version": "1.2.0", + "from": "range-parser@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "dev": true + }, "rat-vec": { "version": "1.1.1", "from": "rat-vec@>=1.1.1 <2.0.0", "resolved": "https://registry.npmjs.org/rat-vec/-/rat-vec-1.1.1.tgz" }, + "raw-loader": { + "version": "0.5.1", + "from": "raw-loader@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "from": "read-pkg@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "dev": true + }, + "read-pkg-up": { + "version": "1.0.1", + "from": "read-pkg-up@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "dev": true + }, "readable-stream": { - "version": "2.1.5", - "from": "readable-stream@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz" + "version": "1.0.34", + "from": "readable-stream@>=1.0.33-1 <1.1.0-0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "dependencies": { + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + } + } + }, + "readdirp": { + "version": "2.1.0", + "from": "readdirp@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "dev": true, + "dependencies": { + "readable-stream": { + "version": "2.2.9", + "from": "readable-stream@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", + "dev": true + }, + "string_decoder": { + "version": "1.0.0", + "from": "string_decoder@~1.0.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz", + "dev": true + } + } + }, + "readline2": { + "version": "1.0.1", + "from": "readline2@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", + "dev": true + }, + "rechoir": { + "version": "0.6.2", + "from": "rechoir@>=0.6.2 <0.7.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "dev": true + }, + "redent": { + "version": "1.0.0", + "from": "redent@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "dev": true + }, + "redeyed": { + "version": "1.0.1", + "from": "redeyed@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-1.0.1.tgz", + "dev": true, + "dependencies": { + "esprima": { + "version": "3.0.0", + "from": "esprima@>=3.0.0 <3.1.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.0.0.tgz", + "dev": true + } + } + }, + "reduce-css-calc": { + "version": "1.3.0", + "from": "reduce-css-calc@>=1.2.6 <2.0.0", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "dev": true + }, + "reduce-function-call": { + "version": "1.0.2", + "from": "reduce-function-call@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", + "dev": true }, "reduce-simplicial-complex": { "version": "1.0.0", "from": "reduce-simplicial-complex@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/reduce-simplicial-complex/-/reduce-simplicial-complex-1.0.0.tgz" }, + "regenerate": { + "version": "1.3.2", + "from": "regenerate@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.2.tgz", + "dev": true + }, + "regenerator-runtime": { + "version": "0.10.5", + "from": "regenerator-runtime@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "dev": true + }, + "regenerator-transform": { + "version": "0.9.11", + "from": "regenerator-transform@0.9.11", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.9.11.tgz", + "dev": true + }, + "regex-cache": { + "version": "0.4.3", + "from": "regex-cache@>=0.4.2 <0.5.0", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz", + "dev": true + }, + "regexpu-core": { + "version": "2.0.0", + "from": "regexpu-core@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "dev": true + }, + "regjsgen": { + "version": "0.2.0", + "from": "regjsgen@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "from": "regjsparser@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "dev": true, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "from": "jsesc@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "dev": true + } + } + }, "regl": { "version": "1.3.0", "from": "regl@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/regl/-/regl-1.3.0.tgz" }, + "relateurl": { + "version": "0.2.7", + "from": "relateurl@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "dev": true + }, + "renderkid": { + "version": "2.0.1", + "from": "renderkid@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz", + "dev": true, + "dependencies": { + "utila": { + "version": "0.3.3", + "from": "utila@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", + "dev": true + } + } + }, + "repeat-element": { + "version": "1.1.2", + "from": "repeat-element@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "dev": true + }, "repeat-string": { "version": "1.6.1", "from": "repeat-string@>=1.5.2 <2.0.0", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" }, + "repeating": { + "version": "2.0.1", + "from": "repeating@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "dev": true + }, "request": { "version": "2.79.0", "from": "request@>=2.39.0 <3.0.0", @@ -2541,11 +5331,41 @@ } } }, + "require-directory": { + "version": "2.1.1", + "from": "require-directory@>=2.1.1 <3.0.0", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "from": "require-main-filename@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "dev": true + }, + "require-uncached": { + "version": "1.0.3", + "from": "require-uncached@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "from": "requires-port@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "dev": true + }, "resolve": { "version": "1.1.7", "from": "resolve@>=1.1.6 <2.0.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz" }, + "resolve-from": { + "version": "1.0.1", + "from": "resolve-from@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "dev": true + }, "resolve-protobuf-schema": { "version": "2.0.0", "from": "resolve-protobuf-schema@>=2.0.0 <3.0.0", @@ -2556,6 +5376,12 @@ "from": "resolve-url@>=0.2.1 <0.3.0", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz" }, + "restore-cursor": { + "version": "1.0.1", + "from": "restore-cursor@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "dev": true + }, "resumer": { "version": "0.0.0", "from": "resumer@>=0.0.0 <0.1.0", @@ -2571,6 +5397,18 @@ "from": "right-now@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/right-now/-/right-now-1.0.0.tgz" }, + "rimraf": { + "version": "2.6.1", + "from": "rimraf@>=2.2.8 <3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "dev": true + }, + "ripemd160": { + "version": "0.2.0", + "from": "ripemd160@0.2.0", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-0.2.0.tgz", + "dev": true + }, "robust-compress": { "version": "1.0.0", "from": "robust-compress@>=1.0.0 <2.0.0", @@ -2626,16 +5464,170 @@ "from": "robust-sum@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/robust-sum/-/robust-sum-1.0.0.tgz" }, + "run-async": { + "version": "0.1.0", + "from": "run-async@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", + "dev": true + }, "rw": { "version": "0.1.4", "from": "rw@>=0.1.4 <0.2.0", "resolved": "https://registry.npmjs.org/rw/-/rw-0.1.4.tgz" }, + "rx-lite": { + "version": "3.1.2", + "from": "rx-lite@>=3.1.2 <4.0.0", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", + "dev": true + }, "sane-topojson": { "version": "2.0.0", "from": "sane-topojson@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/sane-topojson/-/sane-topojson-2.0.0.tgz" }, + "sass-graph": { + "version": "2.2.2", + "from": "sass-graph@>=2.1.1 <3.0.0", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.2.tgz", + "dev": true, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "from": "camelcase@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "from": "cliui@>=3.2.0 <4.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "dev": true + }, + "yargs": { + "version": "6.6.0", + "from": "yargs@>=6.6.0 <7.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", + "dev": true + } + } + }, + "sass-loader": { + "version": "4.1.1", + "from": "sass-loader@>=4.1.1 <5.0.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-4.1.1.tgz", + "dev": true, + "dependencies": { + "async": { + "version": "2.2.0", + "from": "async@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.2.0.tgz", + "dev": true + } + } + }, + "sax": { + "version": "1.2.2", + "from": "sax@>=1.2.1 <1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.2.tgz", + "dev": true + }, + "scss-tokenizer": { + "version": "0.2.1", + "from": "scss-tokenizer@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.1.tgz", + "dev": true, + "dependencies": { + "source-map": { + "version": "0.4.4", + "from": "source-map@>=0.4.2 <0.5.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "dev": true + } + } + }, + "semver": { + "version": "5.3.0", + "from": "semver@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0||>=4.0.0 <5.0.0||>=5.0.0 <6.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "dev": true + }, + "send": { + "version": "0.15.1", + "from": "send@0.15.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.15.1.tgz", + "dev": true, + "dependencies": { + "debug": { + "version": "2.6.1", + "from": "debug@2.6.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.1.tgz", + "dev": true + }, + "ms": { + "version": "0.7.2", + "from": "ms@0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "dev": true + } + } + }, + "serve-index": { + "version": "1.8.0", + "from": "serve-index@>=1.7.2 <2.0.0", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.8.0.tgz", + "dev": true, + "dependencies": { + "http-errors": { + "version": "1.5.1", + "from": "http-errors@>=1.5.0 <1.6.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.5.1.tgz", + "dev": true + }, + "setprototypeof": { + "version": "1.0.2", + "from": "setprototypeof@1.0.2", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.2.tgz", + "dev": true + } + } + }, + "serve-static": { + "version": "1.12.1", + "from": "serve-static@1.12.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.1.tgz", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "from": "set-blocking@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "dev": true + }, + "set-immediate-shim": { + "version": "1.0.1", + "from": "set-immediate-shim@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "dev": true + }, + "setimmediate": { + "version": "1.0.5", + "from": "setimmediate@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "dev": true + }, + "setprototypeof": { + "version": "1.0.3", + "from": "setprototypeof@1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "dev": true + }, + "sha.js": { + "version": "2.2.6", + "from": "sha.js@2.2.6", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.2.6.tgz", + "dev": true + }, "shallow-copy": { "version": "0.0.1", "from": "shallow-copy@0.0.1", @@ -2646,6 +5638,24 @@ "from": "shelf-pack@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/shelf-pack/-/shelf-pack-1.1.0.tgz" }, + "shelljs": { + "version": "0.7.7", + "from": "shelljs@>=0.7.5 <0.8.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.7.tgz", + "dev": true + }, + "shellwords": { + "version": "0.1.0", + "from": "shellwords@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.0.tgz", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "from": "signal-exit@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "dev": true + }, "signum": { "version": "0.0.0", "from": "signum@>=0.0.0 <0.0.1", @@ -2703,6 +5713,18 @@ "from": "slab-decomposition@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/slab-decomposition/-/slab-decomposition-1.0.2.tgz" }, + "slash": { + "version": "1.0.0", + "from": "slash@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "dev": true + }, + "slice-ansi": { + "version": "0.0.4", + "from": "slice-ansi@0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "dev": true + }, "snap-points-2d": { "version": "1.0.1", "from": "snap-points-2d@>=1.0.1 <2.0.0", @@ -2713,6 +5735,26 @@ "from": "sntp@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" }, + "sockjs": { + "version": "0.3.18", + "from": "sockjs@>=0.3.15 <0.4.0", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.18.tgz", + "dev": true + }, + "sockjs-client": { + "version": "1.1.2", + "from": "sockjs-client@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.2.tgz", + "dev": true, + "dependencies": { + "faye-websocket": { + "version": "0.11.1", + "from": "faye-websocket@>=0.11.0 <0.12.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "dev": true + } + } + }, "sort-asc": { "version": "0.1.0", "from": "sort-asc@>=0.1.0 <0.2.0", @@ -2723,16 +5765,52 @@ "from": "sort-desc@>=0.1.1 <0.2.0", "resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.1.1.tgz" }, + "sort-keys": { + "version": "1.1.2", + "from": "sort-keys@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "dev": true + }, "sort-object": { "version": "0.3.2", "from": "sort-object@>=0.3.2 <0.4.0", "resolved": "https://registry.npmjs.org/sort-object/-/sort-object-0.3.2.tgz" }, + "source-list-map": { + "version": "0.1.8", + "from": "source-list-map@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", + "dev": true + }, "source-map": { "version": "0.5.6", "from": "source-map@>=0.5.1 <0.6.0", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" }, + "source-map-support": { + "version": "0.4.15", + "from": "source-map-support@>=0.4.2 <0.5.0", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz", + "dev": true + }, + "spdx-correct": { + "version": "1.0.2", + "from": "spdx-correct@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "dev": true + }, + "spdx-expression-parse": { + "version": "1.0.4", + "from": "spdx-expression-parse@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", + "dev": true + }, + "spdx-license-ids": { + "version": "1.2.2", + "from": "spdx-license-ids@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", + "dev": true + }, "split-polygon": { "version": "1.0.0", "from": "split-polygon@>=1.0.0 <2.0.0", @@ -2787,11 +5865,6 @@ "from": "static-module@>=1.1.2 <2.0.0", "resolved": "https://registry.npmjs.org/static-module/-/static-module-1.3.1.tgz", "dependencies": { - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, "object-inspect": { "version": "0.4.0", "from": "object-inspect@>=0.4.0 <0.5.0", @@ -2802,11 +5875,6 @@ "from": "object-keys@>=0.4.0 <0.5.0", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz" }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@~1.0.27-1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" - }, "through2": { "version": "0.4.2", "from": "through2@>=0.4.1 <0.5.0", @@ -2819,11 +5887,101 @@ } } }, + "statuses": { + "version": "1.3.1", + "from": "statuses@>=1.3.1 <1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "dev": true + }, + "stdout-stream": { + "version": "1.4.0", + "from": "stdout-stream@>=1.4.0 <2.0.0", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.0.tgz", + "dev": true, + "dependencies": { + "readable-stream": { + "version": "2.2.9", + "from": "readable-stream@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", + "dev": true + }, + "string_decoder": { + "version": "1.0.0", + "from": "string_decoder@~1.0.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz", + "dev": true + } + } + }, + "stream-browserify": { + "version": "2.0.1", + "from": "stream-browserify@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "dev": true, + "dependencies": { + "readable-stream": { + "version": "2.2.9", + "from": "readable-stream@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", + "dev": true + }, + "string_decoder": { + "version": "1.0.0", + "from": "string_decoder@~1.0.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz", + "dev": true + } + } + }, + "stream-cache": { + "version": "0.0.2", + "from": "stream-cache@>=0.0.1 <0.1.0", + "resolved": "https://registry.npmjs.org/stream-cache/-/stream-cache-0.0.2.tgz", + "dev": true + }, + "stream-http": { + "version": "2.7.0", + "from": "stream-http@>=2.3.1 <3.0.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.0.tgz", + "dev": true, + "dependencies": { + "readable-stream": { + "version": "2.2.9", + "from": "readable-stream@>=2.2.6 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", + "dev": true + }, + "string_decoder": { + "version": "1.0.0", + "from": "string_decoder@~1.0.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz", + "dev": true + } + } + }, + "strict-uri-encode": { + "version": "1.1.0", + "from": "strict-uri-encode@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "dev": true + }, "string_decoder": { "version": "0.10.31", "from": "string_decoder@>=0.10.0 <0.11.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, + "string-width": { + "version": "1.0.2", + "from": "string-width@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "dev": true + }, + "string.prototype.codepointat": { + "version": "0.2.0", + "from": "string.prototype.codepointat@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.0.tgz", + "dev": true + }, "string.prototype.trim": { "version": "1.1.2", "from": "string.prototype.trim@>=1.1.2 <1.2.0", @@ -2849,6 +6007,24 @@ "from": "strip-ansi@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" }, + "strip-bom": { + "version": "3.0.0", + "from": "strip-bom@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "dev": true + }, + "strip-indent": { + "version": "1.0.1", + "from": "strip-indent@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "from": "strip-json-comments@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "dev": true + }, "supercluster": { "version": "2.3.0", "from": "supercluster@>=2.0.1 <3.0.0", @@ -2859,11 +6035,57 @@ "from": "superscript-text@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/superscript-text/-/superscript-text-1.0.0.tgz" }, + "supports-color": { + "version": "3.2.3", + "from": "supports-color@>=3.2.3 <4.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "dev": true + }, "surface-nets": { "version": "1.0.2", "from": "surface-nets@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/surface-nets/-/surface-nets-1.0.2.tgz" }, + "svgo": { + "version": "0.7.2", + "from": "svgo@>=0.7.0 <0.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", + "dev": true, + "dependencies": { + "colors": { + "version": "1.1.2", + "from": "colors@>=1.1.2 <1.2.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "dev": true + } + } + }, + "table": { + "version": "3.8.3", + "from": "table@>=3.7.8 <4.0.0", + "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "from": "is-fullwidth-code-point@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "dev": true + }, + "string-width": { + "version": "2.0.0", + "from": "string-width@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.0.0.tgz", + "dev": true + } + } + }, + "tapable": { + "version": "0.1.10", + "from": "tapable@>=0.1.8 <0.2.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", + "dev": true + }, "tape": { "version": "4.6.3", "from": "tape@>=4.0.0 <5.0.0", @@ -2876,11 +6098,23 @@ } } }, + "tar": { + "version": "2.2.1", + "from": "tar@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "dev": true + }, "text-cache": { "version": "4.1.0", "from": "text-cache@>=4.1.0 <5.0.0", "resolved": "https://registry.npmjs.org/text-cache/-/text-cache-4.1.0.tgz" }, + "text-table": { + "version": "0.2.0", + "from": "text-table@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "dev": true + }, "through": { "version": "2.3.8", "from": "through@>=2.3.6 <3.0.0", @@ -2889,19 +6123,13 @@ "through2": { "version": "0.6.5", "from": "through2@>=0.6.3 <0.7.0", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "dependencies": { - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, - "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@>=1.0.33-1 <1.1.0-0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" - } - } + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz" + }, + "timers-browserify": { + "version": "2.0.2", + "from": "timers-browserify@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.2.tgz", + "dev": true }, "tiny-sdf": { "version": "1.0.2", @@ -2913,6 +6141,18 @@ "from": "tinycolor2@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz" }, + "to-arraybuffer": { + "version": "1.0.1", + "from": "to-arraybuffer@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "dev": true + }, + "to-fast-properties": { + "version": "1.0.3", + "from": "to-fast-properties@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "dev": true + }, "to-px": { "version": "1.0.1", "from": "to-px@>=1.0.1 <2.0.0", @@ -2928,6 +6168,12 @@ "from": "topojson-client@>=2.1.0 <3.0.0", "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-2.1.0.tgz" }, + "toposort": { + "version": "1.0.3", + "from": "toposort@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.3.tgz", + "dev": true + }, "tough-cookie": { "version": "2.3.2", "from": "tough-cookie@>=2.3.0 <2.4.0", @@ -2943,11 +6189,35 @@ "from": "triangulate-polyline@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/triangulate-polyline/-/triangulate-polyline-1.0.3.tgz" }, + "trim-newlines": { + "version": "1.0.0", + "from": "trim-newlines@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "dev": true + }, + "trim-right": { + "version": "1.0.1", + "from": "trim-right@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "dev": true + }, + "tryit": { + "version": "1.0.3", + "from": "tryit@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", + "dev": true + }, "tryor": { "version": "0.1.2", "from": "tryor@>=0.1.2 <0.2.0", "resolved": "https://registry.npmjs.org/tryor/-/tryor-0.1.2.tgz" }, + "tty-browserify": { + "version": "0.0.0", + "from": "tty-browserify@0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "dev": true + }, "tunnel-agent": { "version": "0.4.3", "from": "tunnel-agent@>=0.4.1 <0.5.0", @@ -2979,6 +6249,26 @@ "from": "type-check@>=0.3.2 <0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" }, + "type-is": { + "version": "1.6.15", + "from": "type-is@>=1.6.14 <1.7.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "dev": true, + "dependencies": { + "mime-db": { + "version": "1.27.0", + "from": "mime-db@~1.27.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", + "dev": true + }, + "mime-types": { + "version": "2.1.15", + "from": "mime-types@>=2.1.15 <2.2.0", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", + "dev": true + } + } + }, "typedarray": { "version": "0.0.6", "from": "typedarray@>=0.0.5 <0.1.0", @@ -3016,6 +6306,11 @@ "from": "unassert@>=1.3.1 <2.0.0", "resolved": "https://registry.npmjs.org/unassert/-/unassert-1.5.1.tgz", "dependencies": { + "acorn": { + "version": "4.0.11", + "from": "acorn@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.11.tgz" + }, "estraverse": { "version": "4.2.0", "from": "estraverse@>=4.1.0 <5.0.0", @@ -3028,11 +6323,21 @@ "from": "unassertify@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/unassertify/-/unassertify-2.0.4.tgz", "dependencies": { + "acorn": { + "version": "4.0.11", + "from": "acorn@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.11.tgz" + }, "escodegen": { "version": "1.8.1", "from": "escodegen@>=1.6.1 <2.0.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz" }, + "esprima": { + "version": "2.7.3", + "from": "esprima@>=2.7.1 <3.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz" + }, "estraverse": { "version": "1.9.3", "from": "estraverse@>=1.9.1 <2.0.0", @@ -3066,16 +6371,124 @@ "from": "uniq@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz" }, + "uniqid": { + "version": "4.1.1", + "from": "uniqid@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", + "dev": true + }, + "uniqs": { + "version": "2.0.0", + "from": "uniqs@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "dev": true + }, "unitbezier": { "version": "0.0.0", "from": "unitbezier@>=0.0.0 <0.0.1", "resolved": "https://registry.npmjs.org/unitbezier/-/unitbezier-0.0.0.tgz" }, + "unpipe": { + "version": "1.0.0", + "from": "unpipe@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "dev": true + }, + "upper-case": { + "version": "1.1.3", + "from": "upper-case@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "dev": true + }, + "url": { + "version": "0.11.0", + "from": "url@>=0.11.0 <0.12.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "dev": true, + "dependencies": { + "punycode": { + "version": "1.3.2", + "from": "punycode@1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "dev": true + } + } + }, + "url-loader": { + "version": "0.5.8", + "from": "url-loader@>=0.5.7 <0.6.0", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.5.8.tgz", + "dev": true, + "dependencies": { + "loader-utils": { + "version": "1.1.0", + "from": "loader-utils@^1.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "dev": true + } + } + }, + "url-parse": { + "version": "1.1.8", + "from": "url-parse@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.1.8.tgz", + "dev": true + }, + "user-home": { + "version": "2.0.0", + "from": "user-home@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", + "dev": true + }, + "util": { + "version": "0.10.3", + "from": "util@>=0.10.3 <0.11.0", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "dev": true, + "dependencies": { + "inherits": { + "version": "2.0.1", + "from": "inherits@2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "dev": true + } + } + }, "util-deprecate": { "version": "1.0.2", "from": "util-deprecate@>=1.0.1 <1.1.0", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" }, + "utila": { + "version": "0.4.0", + "from": "utila@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "dev": true + }, + "utils-merge": { + "version": "1.0.0", + "from": "utils-merge@1.0.0", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", + "dev": true + }, + "uuid": { + "version": "2.0.3", + "from": "uuid@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.1", + "from": "validate-npm-package-license@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", + "dev": true + }, + "vary": { + "version": "1.1.1", + "from": "vary@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz", + "dev": true + }, "vector-tile": { "version": "1.3.0", "from": "vector-tile@>=1.3.0 <2.0.0", @@ -3086,11 +6499,23 @@ "from": "vectorize-text@>=3.0.1 <4.0.0", "resolved": "https://registry.npmjs.org/vectorize-text/-/vectorize-text-3.0.2.tgz" }, + "vendors": { + "version": "1.0.1", + "from": "vendors@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.1.tgz", + "dev": true + }, "verror": { "version": "1.3.6", "from": "verror@1.3.6", "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" }, + "vm-browserify": { + "version": "0.0.4", + "from": "vm-browserify@0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "dev": true + }, "vt-pbf": { "version": "2.1.2", "from": "vt-pbf@>=2.0.2 <3.0.0", @@ -3101,6 +6526,20 @@ "from": "w3c-blob@0.0.1", "resolved": "https://registry.npmjs.org/w3c-blob/-/w3c-blob-0.0.1.tgz" }, + "watchpack": { + "version": "0.2.9", + "from": "watchpack@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-0.2.9.tgz", + "dev": true, + "dependencies": { + "async": { + "version": "0.9.2", + "from": "async@>=0.9.0 <0.10.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "dev": true + } + } + }, "weak-map": { "version": "1.0.5", "from": "weak-map@>=1.0.5 <2.0.0", @@ -3116,6 +6555,96 @@ "from": "webgl-context@>=2.2.0 <3.0.0", "resolved": "https://registry.npmjs.org/webgl-context/-/webgl-context-2.2.0.tgz" }, + "webpack": { + "version": "1.14.0", + "from": "webpack@>=1.13.3 <2.0.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-1.14.0.tgz", + "dev": true, + "dependencies": { + "acorn": { + "version": "3.3.0", + "from": "acorn@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "dev": true + }, + "interpret": { + "version": "0.6.6", + "from": "interpret@>=0.6.4 <0.7.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-0.6.6.tgz", + "dev": true + } + } + }, + "webpack-build-notifier": { + "version": "0.1.13", + "from": "webpack-build-notifier@>=0.1.13 <0.2.0", + "resolved": "https://registry.npmjs.org/webpack-build-notifier/-/webpack-build-notifier-0.1.13.tgz", + "dev": true + }, + "webpack-core": { + "version": "0.6.9", + "from": "webpack-core@>=0.6.9 <0.7.0", + "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", + "dev": true, + "dependencies": { + "source-map": { + "version": "0.4.4", + "from": "source-map@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "dev": true + } + } + }, + "webpack-dev-middleware": { + "version": "1.10.2", + "from": "webpack-dev-middleware@>=1.4.0 <2.0.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.10.2.tgz", + "dev": true, + "dependencies": { + "memory-fs": { + "version": "0.4.1", + "from": "memory-fs@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "dev": true + }, + "readable-stream": { + "version": "2.2.9", + "from": "readable-stream@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", + "dev": true + }, + "string_decoder": { + "version": "1.0.0", + "from": "string_decoder@~1.0.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz", + "dev": true + } + } + }, + "webpack-dev-server": { + "version": "1.16.3", + "from": "webpack-dev-server@>=1.16.2 <2.0.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-1.16.3.tgz", + "dev": true + }, + "webpack-sources": { + "version": "0.1.5", + "from": "webpack-sources@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.1.5.tgz", + "dev": true + }, + "websocket-driver": { + "version": "0.6.5", + "from": "websocket-driver@>=0.5.1", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", + "dev": true + }, + "websocket-extensions": { + "version": "0.1.1", + "from": "websocket-extensions@>=0.1.1", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.1.tgz", + "dev": true + }, "webworkify": { "version": "1.4.0", "from": "webworkify@>=1.3.0 <2.0.0", @@ -3126,11 +6655,35 @@ "from": "wgs84@0.0.0", "resolved": "https://registry.npmjs.org/wgs84/-/wgs84-0.0.0.tgz" }, + "whet.extend": { + "version": "0.9.9", + "from": "whet.extend@>=0.9.9 <0.10.0", + "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", + "dev": true + }, + "which": { + "version": "1.2.14", + "from": "which@>=1.2.9 <2.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", + "dev": true + }, + "which-module": { + "version": "1.0.0", + "from": "which-module@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "dev": true + }, "whoots-js": { "version": "2.1.0", "from": "whoots-js@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/whoots-js/-/whoots-js-2.1.0.tgz" }, + "wide-align": { + "version": "1.1.0", + "from": "wide-align@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz", + "dev": true + }, "window-size": { "version": "0.1.0", "from": "window-size@0.1.0", @@ -3146,21 +6699,65 @@ "from": "world-calendars@>=1.0.3 <2.0.0", "resolved": "https://registry.npmjs.org/world-calendars/-/world-calendars-1.0.3.tgz" }, + "wrap-ansi": { + "version": "2.1.0", + "from": "wrap-ansi@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "dev": true + }, "wrappy": { "version": "1.0.2", "from": "wrappy@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" }, + "write": { + "version": "0.2.1", + "from": "write@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "dev": true + }, + "xml-char-classes": { + "version": "1.0.0", + "from": "xml-char-classes@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/xml-char-classes/-/xml-char-classes-1.0.0.tgz", + "dev": true + }, "xtend": { "version": "4.0.1", "from": "xtend@>=4.0.0 <5.0.0", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" }, + "y18n": { + "version": "3.2.1", + "from": "y18n@>=3.2.1 <4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "from": "yallist@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "dev": true + }, "yargs": { "version": "3.10.0", "from": "yargs@>=3.10.0 <3.11.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz" }, + "yargs-parser": { + "version": "4.2.1", + "from": "yargs-parser@>=4.2.0 <5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "dev": true, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "from": "camelcase@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "dev": true + } + } + }, "zero-crossings": { "version": "1.0.1", "from": "zero-crossings@>=1.0.0 <2.0.0", diff --git a/package.json b/package.json index 8f5d5957b5..7754fc772c 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "ng-annotate-loader": "^0.2.0", "pace-progress": "git+https://github.com/getredash/pace.git", "pivottable": "^2.3.0", - "plotly.js": "1.25.0", + "plotly.js": "1.26.1", "ui-select": "^0.19.6", "underscore": "^1.8.3", "underscore.string": "^3.3.4" From 94a14f93a87dda028e25fd7a6c1cf3896f9bb710 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Tue, 2 May 2017 22:00:09 +0300 Subject: [PATCH 037/243] Remove dev dependencies from npm-shrinkwrap.json. --- npm-shrinkwrap.json | 4093 +++---------------------------------------- 1 file changed, 249 insertions(+), 3844 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index e2c0c2d1d1..298af0afa0 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "redash-client", - "version": "1.0.0", + "version": "1.0.3", "dependencies": { "3d-view": { "version": "2.0.0", @@ -17,36 +17,10 @@ "from": "a-big-triangle@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/a-big-triangle/-/a-big-triangle-1.0.3.tgz" }, - "abbrev": { - "version": "1.1.0", - "from": "abbrev@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", - "dev": true - }, - "accepts": { - "version": "1.3.3", - "from": "accepts@>=1.3.3 <1.4.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", - "dev": true - }, "acorn": { - "version": "1.2.2", - "from": "acorn@>=1.0.3 <2.0.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz" - }, - "acorn-jsx": { - "version": "3.0.1", - "from": "acorn-jsx@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "dev": true, - "dependencies": { - "acorn": { - "version": "3.3.0", - "from": "acorn@>=3.0.4 <4.0.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "dev": true - } - } + "version": "4.0.4", + "from": "acorn@4.0.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.4.tgz" }, "add-line-numbers": { "version": "1.0.1", @@ -58,18 +32,6 @@ "from": "affine-hull@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/affine-hull/-/affine-hull-1.0.0.tgz" }, - "ajv": { - "version": "4.11.8", - "from": "ajv@>=4.7.0 <5.0.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "dev": true - }, - "ajv-keywords": { - "version": "1.5.1", - "from": "ajv-keywords@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", - "dev": true - }, "align-text": { "version": "0.1.4", "from": "align-text@>=0.1.3 <0.2.0", @@ -90,21 +52,15 @@ "from": "alpha-shape@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/alpha-shape/-/alpha-shape-1.0.0.tgz" }, - "alphanum-sort": { - "version": "1.0.2", - "from": "alphanum-sort@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "dev": true - }, "alter": { "version": "0.2.0", "from": "alter@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz" }, "amdefine": { - "version": "1.0.1", + "version": "1.0.0", "from": "amdefine@>=0.0.4", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" }, "angular": { "version": "1.5.8", @@ -171,12 +127,6 @@ "from": "angular-vs-repeat@latest", "resolved": "https://registry.npmjs.org/angular-vs-repeat/-/angular-vs-repeat-1.1.7.tgz" }, - "ansi-escapes": { - "version": "1.4.0", - "from": "ansi-escapes@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "dev": true - }, "ansi-regex": { "version": "2.0.0", "from": "ansi-regex@>=2.0.0 <3.0.0", @@ -187,137 +137,21 @@ "from": "ansi-styles@>=2.2.1 <3.0.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" }, - "ansicolors": { - "version": "0.2.1", - "from": "ansicolors@>=0.2.1 <0.3.0", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.2.1.tgz", - "dev": true - }, - "anymatch": { - "version": "1.3.0", - "from": "anymatch@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz", - "dev": true - }, - "aproba": { - "version": "1.1.1", - "from": "aproba@>=1.0.3 <2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz", - "dev": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "from": "are-we-there-yet@>=1.1.2 <1.2.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", - "dev": true, - "dependencies": { - "readable-stream": { - "version": "2.2.9", - "from": "readable-stream@>=2.0.6 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", - "dev": true - }, - "string_decoder": { - "version": "1.0.0", - "from": "string_decoder@~1.0.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz", - "dev": true - } - } - }, - "argparse": { - "version": "1.0.9", - "from": "argparse@>=1.0.7 <2.0.0", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", - "dev": true - }, - "arr-diff": { - "version": "2.0.0", - "from": "arr-diff@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "dev": true - }, - "arr-flatten": { - "version": "1.0.3", - "from": "arr-flatten@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.3.tgz", - "dev": true - }, - "array-find-index": { - "version": "1.0.2", - "from": "array-find-index@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "dev": true - }, - "array-flatten": { - "version": "1.1.1", - "from": "array-flatten@1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "dev": true - }, - "array-union": { - "version": "1.0.2", - "from": "array-union@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "dev": true - }, - "array-uniq": { - "version": "1.0.3", - "from": "array-uniq@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "dev": true - }, - "array-unique": { - "version": "0.2.1", - "from": "array-unique@>=0.2.1 <0.3.0", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "dev": true - }, "arraytools": { "version": "1.1.2", "from": "arraytools@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/arraytools/-/arraytools-1.1.2.tgz" }, - "arrify": { - "version": "1.0.1", - "from": "arrify@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "dev": true - }, "asn1": { "version": "0.2.3", "from": "asn1@>=0.2.3 <0.3.0", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" }, - "assert": { - "version": "1.4.1", - "from": "assert@>=1.1.1 <2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "dev": true - }, "assert-plus": { "version": "0.2.0", "from": "assert-plus@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" }, - "async": { - "version": "1.5.2", - "from": "async@>=1.5.0 <2.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "dev": true - }, - "async-each": { - "version": "1.0.1", - "from": "async-each@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "dev": true - }, - "async-foreach": { - "version": "0.1.3", - "from": "async-foreach@>=0.1.3 <0.2.0", - "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", - "dev": true - }, "asynckit": { "version": "0.4.0", "from": "asynckit@>=0.4.0 <0.5.0", @@ -328,12 +162,6 @@ "from": "atob-lite@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-1.0.0.tgz" }, - "autoprefixer": { - "version": "6.7.7", - "from": "autoprefixer@>=6.3.1 <7.0.0", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", - "dev": true - }, "aws-sign2": { "version": "0.6.0", "from": "aws-sign2@>=0.6.0 <0.7.0", @@ -344,416 +172,6 @@ "from": "aws4@>=1.2.1 <2.0.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.5.0.tgz" }, - "babel-code-frame": { - "version": "6.22.0", - "from": "babel-code-frame@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", - "dev": true - }, - "babel-core": { - "version": "6.24.0", - "from": "babel-core@>=6.18.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.24.0.tgz", - "dev": true - }, - "babel-generator": { - "version": "6.24.1", - "from": "babel-generator@>=6.24.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.24.1.tgz", - "dev": true - }, - "babel-helper-bindify-decorators": { - "version": "6.24.1", - "from": "babel-helper-bindify-decorators@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz", - "dev": true - }, - "babel-helper-builder-binary-assignment-operator-visitor": { - "version": "6.24.1", - "from": "babel-helper-builder-binary-assignment-operator-visitor@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", - "dev": true - }, - "babel-helper-call-delegate": { - "version": "6.24.1", - "from": "babel-helper-call-delegate@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", - "dev": true - }, - "babel-helper-define-map": { - "version": "6.24.1", - "from": "babel-helper-define-map@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz", - "dev": true - }, - "babel-helper-explode-assignable-expression": { - "version": "6.24.1", - "from": "babel-helper-explode-assignable-expression@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", - "dev": true - }, - "babel-helper-explode-class": { - "version": "6.24.1", - "from": "babel-helper-explode-class@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz", - "dev": true - }, - "babel-helper-function-name": { - "version": "6.24.1", - "from": "babel-helper-function-name@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", - "dev": true - }, - "babel-helper-get-function-arity": { - "version": "6.24.1", - "from": "babel-helper-get-function-arity@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", - "dev": true - }, - "babel-helper-hoist-variables": { - "version": "6.24.1", - "from": "babel-helper-hoist-variables@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", - "dev": true - }, - "babel-helper-optimise-call-expression": { - "version": "6.24.1", - "from": "babel-helper-optimise-call-expression@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", - "dev": true - }, - "babel-helper-regex": { - "version": "6.24.1", - "from": "babel-helper-regex@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.24.1.tgz", - "dev": true - }, - "babel-helper-remap-async-to-generator": { - "version": "6.24.1", - "from": "babel-helper-remap-async-to-generator@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", - "dev": true - }, - "babel-helper-replace-supers": { - "version": "6.24.1", - "from": "babel-helper-replace-supers@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", - "dev": true - }, - "babel-helpers": { - "version": "6.24.1", - "from": "babel-helpers@>=6.23.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "dev": true - }, - "babel-loader": { - "version": "6.4.1", - "from": "babel-loader@>=6.2.7 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-6.4.1.tgz", - "dev": true - }, - "babel-messages": { - "version": "6.23.0", - "from": "babel-messages@>=6.23.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "dev": true - }, - "babel-plugin-check-es2015-constants": { - "version": "6.22.0", - "from": "babel-plugin-check-es2015-constants@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", - "dev": true - }, - "babel-plugin-syntax-async-functions": { - "version": "6.13.0", - "from": "babel-plugin-syntax-async-functions@>=6.8.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", - "dev": true - }, - "babel-plugin-syntax-async-generators": { - "version": "6.13.0", - "from": "babel-plugin-syntax-async-generators@>=6.5.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", - "dev": true - }, - "babel-plugin-syntax-class-properties": { - "version": "6.13.0", - "from": "babel-plugin-syntax-class-properties@>=6.8.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", - "dev": true - }, - "babel-plugin-syntax-decorators": { - "version": "6.13.0", - "from": "babel-plugin-syntax-decorators@>=6.13.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", - "dev": true - }, - "babel-plugin-syntax-dynamic-import": { - "version": "6.18.0", - "from": "babel-plugin-syntax-dynamic-import@>=6.18.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", - "dev": true - }, - "babel-plugin-syntax-exponentiation-operator": { - "version": "6.13.0", - "from": "babel-plugin-syntax-exponentiation-operator@>=6.8.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", - "dev": true - }, - "babel-plugin-syntax-object-rest-spread": { - "version": "6.13.0", - "from": "babel-plugin-syntax-object-rest-spread@>=6.8.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", - "dev": true - }, - "babel-plugin-syntax-trailing-function-commas": { - "version": "6.22.0", - "from": "babel-plugin-syntax-trailing-function-commas@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-async-generator-functions": { - "version": "6.24.1", - "from": "babel-plugin-transform-async-generator-functions@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-async-to-generator": { - "version": "6.24.1", - "from": "babel-plugin-transform-async-to-generator@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-class-properties": { - "version": "6.24.1", - "from": "babel-plugin-transform-class-properties@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-decorators": { - "version": "6.24.1", - "from": "babel-plugin-transform-decorators@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-arrow-functions": { - "version": "6.22.0", - "from": "babel-plugin-transform-es2015-arrow-functions@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-block-scoped-functions": { - "version": "6.22.0", - "from": "babel-plugin-transform-es2015-block-scoped-functions@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-block-scoping": { - "version": "6.24.1", - "from": "babel-plugin-transform-es2015-block-scoping@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-classes": { - "version": "6.24.1", - "from": "babel-plugin-transform-es2015-classes@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-computed-properties": { - "version": "6.24.1", - "from": "babel-plugin-transform-es2015-computed-properties@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-destructuring": { - "version": "6.23.0", - "from": "babel-plugin-transform-es2015-destructuring@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-duplicate-keys": { - "version": "6.24.1", - "from": "babel-plugin-transform-es2015-duplicate-keys@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-for-of": { - "version": "6.23.0", - "from": "babel-plugin-transform-es2015-for-of@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-function-name": { - "version": "6.24.1", - "from": "babel-plugin-transform-es2015-function-name@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-literals": { - "version": "6.22.0", - "from": "babel-plugin-transform-es2015-literals@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-modules-amd": { - "version": "6.24.1", - "from": "babel-plugin-transform-es2015-modules-amd@>=6.24.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.24.1", - "from": "babel-plugin-transform-es2015-modules-commonjs@>=6.24.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-modules-systemjs": { - "version": "6.24.1", - "from": "babel-plugin-transform-es2015-modules-systemjs@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-modules-umd": { - "version": "6.24.1", - "from": "babel-plugin-transform-es2015-modules-umd@>=6.24.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-object-super": { - "version": "6.24.1", - "from": "babel-plugin-transform-es2015-object-super@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-parameters": { - "version": "6.24.1", - "from": "babel-plugin-transform-es2015-parameters@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-shorthand-properties": { - "version": "6.24.1", - "from": "babel-plugin-transform-es2015-shorthand-properties@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-spread": { - "version": "6.22.0", - "from": "babel-plugin-transform-es2015-spread@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-sticky-regex": { - "version": "6.24.1", - "from": "babel-plugin-transform-es2015-sticky-regex@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-template-literals": { - "version": "6.22.0", - "from": "babel-plugin-transform-es2015-template-literals@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-typeof-symbol": { - "version": "6.23.0", - "from": "babel-plugin-transform-es2015-typeof-symbol@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-unicode-regex": { - "version": "6.24.1", - "from": "babel-plugin-transform-es2015-unicode-regex@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-exponentiation-operator": { - "version": "6.24.1", - "from": "babel-plugin-transform-exponentiation-operator@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-object-rest-spread": { - "version": "6.23.0", - "from": "babel-plugin-transform-object-rest-spread@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz", - "dev": true - }, - "babel-plugin-transform-regenerator": { - "version": "6.24.1", - "from": "babel-plugin-transform-regenerator@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-strict-mode": { - "version": "6.24.1", - "from": "babel-plugin-transform-strict-mode@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", - "dev": true - }, - "babel-preset-es2015": { - "version": "6.24.0", - "from": "babel-preset-es2015@>=6.18.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.0.tgz", - "dev": true - }, - "babel-preset-stage-2": { - "version": "6.22.0", - "from": "babel-preset-stage-2@>=6.18.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.22.0.tgz", - "dev": true - }, - "babel-preset-stage-3": { - "version": "6.24.1", - "from": "babel-preset-stage-3@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", - "dev": true - }, - "babel-register": { - "version": "6.24.1", - "from": "babel-register@>=6.24.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.24.1.tgz", - "dev": true, - "dependencies": { - "babel-core": { - "version": "6.24.1", - "from": "babel-core@>=6.24.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.24.1.tgz", - "dev": true - } - } - }, - "babel-runtime": { - "version": "6.23.0", - "from": "babel-runtime@>=6.22.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", - "dev": true - }, - "babel-template": { - "version": "6.24.1", - "from": "babel-template@>=6.23.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.24.1.tgz", - "dev": true - }, - "babel-traverse": { - "version": "6.24.1", - "from": "babel-traverse@>=6.23.1 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.24.1.tgz", - "dev": true - }, - "babel-types": { - "version": "6.24.1", - "from": "babel-types@>=6.23.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.24.1.tgz", - "dev": true - }, - "babylon": { - "version": "6.17.0", - "from": "babylon@>=6.11.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.17.0.tgz", - "dev": true - }, "balanced-match": { "version": "0.4.2", "from": "balanced-match@>=0.4.1 <0.5.0", @@ -764,17 +182,6 @@ "from": "barycentric@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/barycentric/-/barycentric-1.0.1.tgz" }, - "base64-js": { - "version": "0.0.2", - "from": "base64-js@0.0.2", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.2.tgz" - }, - "batch": { - "version": "0.5.3", - "from": "batch@0.5.3", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.5.3.tgz", - "dev": true - }, "bcrypt-pbkdf": { "version": "1.0.0", "from": "bcrypt-pbkdf@>=1.0.0 <2.0.0", @@ -791,12 +198,6 @@ "from": "big.js@>=3.1.3 <4.0.0", "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz" }, - "binary-extensions": { - "version": "1.8.0", - "from": "binary-extensions@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.8.0.tgz", - "dev": true - }, "binary-search-bounds": { "version": "1.0.0", "from": "binary-search-bounds@>=1.0.0 <2.0.0", @@ -808,45 +209,15 @@ "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-1.0.2.tgz" }, "bl": { - "version": "1.2.0", + "version": "1.2.1", "from": "bl@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.0.tgz", - "dependencies": { - "readable-stream": { - "version": "2.2.9", - "from": "readable-stream@>=2.0.5 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz" - }, - "string_decoder": { - "version": "1.0.0", - "from": "string_decoder@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz" - } - } - }, - "block-stream": { - "version": "0.0.9", - "from": "block-stream@*", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "dev": true - }, - "bluebird": { - "version": "3.5.0", - "from": "bluebird@>=3.4.7 <4.0.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz" }, "bn.js": { "version": "4.11.6", "from": "bn.js@>=4.11.6 <5.0.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz" }, - "boolbase": { - "version": "1.0.0", - "from": "boolbase@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "dev": true - }, "boom": { "version": "2.10.1", "from": "boom@>=2.0.0 <3.0.0", @@ -855,7 +226,14 @@ "bops": { "version": "0.0.6", "from": "bops@0.0.6", - "resolved": "https://registry.npmjs.org/bops/-/bops-0.0.6.tgz" + "resolved": "https://registry.npmjs.org/bops/-/bops-0.0.6.tgz", + "dependencies": { + "base64-js": { + "version": "0.0.2", + "from": "base64-js@0.0.2", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.2.tgz" + } + } }, "boundary-cells": { "version": "2.0.1", @@ -877,12 +255,6 @@ "from": "brace-expansion@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz" }, - "braces": { - "version": "1.8.5", - "from": "braces@>=1.8.2 <2.0.0", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "dev": true - }, "brfs": { "version": "1.4.3", "from": "brfs@>=1.4.0 <2.0.0", @@ -898,16 +270,6 @@ "from": "quote-stream@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz" }, - "readable-stream": { - "version": "2.2.9", - "from": "readable-stream@>=2.1.5 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz" - }, - "string_decoder": { - "version": "1.0.0", - "from": "string_decoder@~1.0.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz" - }, "through2": { "version": "2.0.3", "from": "through2@>=2.0.0 <3.0.0", @@ -915,38 +277,6 @@ } } }, - "browserify-aes": { - "version": "0.4.0", - "from": "browserify-aes@0.4.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-0.4.0.tgz", - "dev": true - }, - "browserify-zlib": { - "version": "0.1.4", - "from": "browserify-zlib@>=0.1.4 <0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", - "dev": true - }, - "browserslist": { - "version": "1.7.7", - "from": "browserslist@>=1.7.6 <2.0.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "dev": true - }, - "buffer": { - "version": "4.9.1", - "from": "buffer@>=4.9.0 <5.0.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "dev": true, - "dependencies": { - "base64-js": { - "version": "1.2.0", - "from": "base64-js@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz", - "dev": true - } - } - }, "buffer-equal": { "version": "0.0.1", "from": "buffer-equal@0.0.1", @@ -957,24 +287,6 @@ "from": "buffer-shims@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" }, - "builtin-modules": { - "version": "1.1.1", - "from": "builtin-modules@>=1.1.1 <2.0.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "from": "builtin-status-codes@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "dev": true - }, - "bytes": { - "version": "2.3.0", - "from": "bytes@2.3.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.3.0.tgz", - "dev": true - }, "call-matcher": { "version": "1.0.1", "from": "call-matcher@>=1.0.1 <2.0.0", @@ -987,61 +299,11 @@ } } }, - "caller-path": { - "version": "0.1.0", - "from": "caller-path@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "dev": true - }, - "callsites": { - "version": "0.2.0", - "from": "callsites@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "dev": true - }, - "camel-case": { - "version": "3.0.0", - "from": "camel-case@>=3.0.0 <3.1.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "dev": true - }, "camelcase": { "version": "1.2.1", "from": "camelcase@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz" }, - "camelcase-keys": { - "version": "2.1.0", - "from": "camelcase-keys@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "dev": true, - "dependencies": { - "camelcase": { - "version": "2.1.1", - "from": "camelcase@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "dev": true - } - } - }, - "caniuse-api": { - "version": "1.6.1", - "from": "caniuse-api@>=1.5.2 <2.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", - "dev": true - }, - "caniuse-db": { - "version": "1.0.30000664", - "from": "caniuse-db@>=1.0.30000634 <2.0.0", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000664.tgz", - "dev": true - }, - "cardinal": { - "version": "1.0.0", - "from": "cardinal@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-1.0.0.tgz", - "dev": true - }, "caseless": { "version": "0.11.0", "from": "caseless@>=0.11.0 <0.12.0", @@ -1081,18 +343,6 @@ } } }, - "chokidar": { - "version": "1.6.1", - "from": "chokidar@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.6.1.tgz", - "dev": true - }, - "circular-json": { - "version": "0.3.1", - "from": "circular-json@>=0.3.1 <0.4.0", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.1.tgz", - "dev": true - }, "circumcenter": { "version": "1.0.0", "from": "circumcenter@>=1.0.0 <2.0.0", @@ -1108,55 +358,11 @@ "from": "clamp@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz" }, - "clap": { - "version": "1.1.3", - "from": "clap@>=1.0.9 <2.0.0", - "resolved": "https://registry.npmjs.org/clap/-/clap-1.1.3.tgz", - "dev": true - }, - "clean-css": { - "version": "4.0.12", - "from": "clean-css@>=4.0.0 <4.1.0", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.0.12.tgz", - "dev": true - }, "clean-pslg": { "version": "1.1.2", "from": "clean-pslg@>=1.1.0 <2.0.0", "resolved": "https://registry.npmjs.org/clean-pslg/-/clean-pslg-1.1.2.tgz" }, - "cli-cursor": { - "version": "1.0.2", - "from": "cli-cursor@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "dev": true - }, - "cli-table": { - "version": "0.3.1", - "from": "cli-table@>=0.3.1 <0.4.0", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", - "dev": true, - "dependencies": { - "colors": { - "version": "1.0.3", - "from": "colors@1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "dev": true - } - } - }, - "cli-usage": { - "version": "0.1.4", - "from": "cli-usage@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/cli-usage/-/cli-usage-0.1.4.tgz", - "dev": true - }, - "cli-width": { - "version": "2.1.0", - "from": "cli-width@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", - "dev": true - }, "cliui": { "version": "2.1.0", "from": "cliui@>=2.1.0 <3.0.0", @@ -1174,36 +380,6 @@ "from": "clone@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz" }, - "co": { - "version": "4.6.0", - "from": "co@>=4.6.0 <5.0.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "dev": true - }, - "coa": { - "version": "1.0.1", - "from": "coa@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.1.tgz", - "dev": true - }, - "code-point-at": { - "version": "1.1.0", - "from": "code-point-at@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "dev": true - }, - "color": { - "version": "0.11.4", - "from": "color@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", - "dev": true - }, - "color-convert": { - "version": "1.9.0", - "from": "color-convert@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", - "dev": true - }, "color-id": { "version": "1.0.3", "from": "color-id@>=1.0.0 <2.0.0", @@ -1229,28 +405,11 @@ "from": "color-space@>=1.14.6 <2.0.0", "resolved": "https://registry.npmjs.org/color-space/-/color-space-1.14.7.tgz" }, - "color-string": { - "version": "0.3.0", - "from": "color-string@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", - "dev": true - }, "colormap": { "version": "2.2.0", "from": "colormap@>=2.1.0 <3.0.0", "resolved": "https://registry.npmjs.org/colormap/-/colormap-2.2.0.tgz" }, - "colormin": { - "version": "1.1.2", - "from": "colormin@>=1.0.5 <2.0.0", - "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", - "dev": true - }, - "colors": { - "version": "0.6.2", - "from": "colors@>=0.6.0-1 <0.7.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" - }, "combined-stream": { "version": "1.0.5", "from": "combined-stream@>=1.0.5 <1.1.0", @@ -1261,12 +420,6 @@ "from": "commander@>=2.9.0 <2.10.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz" }, - "commondir": { - "version": "1.0.1", - "from": "commondir@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "dev": true - }, "compare-angle": { "version": "1.0.1", "from": "compare-angle@>=1.0.0 <2.0.0", @@ -1282,26 +435,6 @@ "from": "compare-oriented-cell@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/compare-oriented-cell/-/compare-oriented-cell-1.0.1.tgz" }, - "compressible": { - "version": "2.0.10", - "from": "compressible@>=2.0.8 <2.1.0", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.10.tgz", - "dev": true, - "dependencies": { - "mime-db": { - "version": "1.27.0", - "from": "mime-db@>=1.27.0 <2.0.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", - "dev": true - } - } - }, - "compression": { - "version": "1.6.2", - "from": "compression@>=1.5.2 <2.0.0", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.6.2.tgz", - "dev": true - }, "concat-map": { "version": "0.0.1", "from": "concat-map@0.0.1", @@ -1324,48 +457,6 @@ } } }, - "connect-history-api-fallback": { - "version": "1.3.0", - "from": "connect-history-api-fallback@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz", - "dev": true - }, - "console-browserify": { - "version": "1.1.0", - "from": "console-browserify@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "from": "console-control-strings@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "dev": true - }, - "constants-browserify": { - "version": "1.0.0", - "from": "constants-browserify@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "dev": true - }, - "contains-path": { - "version": "0.1.0", - "from": "contains-path@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "dev": true - }, - "content-disposition": { - "version": "0.5.2", - "from": "content-disposition@0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "dev": true - }, - "content-type": { - "version": "1.0.2", - "from": "content-type@>=1.0.2 <1.1.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", - "dev": true - }, "convert-source-map": { "version": "1.3.0", "from": "convert-source-map@>=1.1.0 <2.0.0", @@ -1376,21 +467,9 @@ "from": "convex-hull@>=1.0.3 <2.0.0", "resolved": "https://registry.npmjs.org/convex-hull/-/convex-hull-1.0.3.tgz" }, - "cookie": { - "version": "0.3.1", - "from": "cookie@0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "from": "cookie-signature@1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "dev": true - }, "core-js": { "version": "2.4.1", - "from": "core-js@>=2.4.0 <3.0.0", + "from": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz" }, "core-util-is": { @@ -1408,95 +487,21 @@ "from": "country-regex@>=1.1.0 <2.0.0", "resolved": "https://registry.npmjs.org/country-regex/-/country-regex-1.1.0.tgz" }, - "cross-spawn": { - "version": "3.0.1", - "from": "cross-spawn@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", - "dev": true - }, "cryptiles": { "version": "2.0.5", "from": "cryptiles@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" }, - "crypto-browserify": { - "version": "3.3.0", - "from": "crypto-browserify@3.3.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.3.0.tgz", - "dev": true - }, - "css-color-names": { - "version": "0.0.4", - "from": "css-color-names@0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "dev": true - }, - "css-loader": { - "version": "0.25.0", - "from": "css-loader@>=0.25.0 <0.26.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.25.0.tgz", - "dev": true - }, - "css-select": { - "version": "1.2.0", - "from": "css-select@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "dev": true - }, - "css-selector-tokenizer": { - "version": "0.6.0", - "from": "css-selector-tokenizer@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.6.0.tgz", - "dev": true, - "dependencies": { - "regexpu-core": { - "version": "1.0.0", - "from": "regexpu-core@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "dev": true - } - } - }, - "css-what": { - "version": "2.1.0", - "from": "css-what@>=2.1.0 <2.2.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", - "dev": true - }, "csscolorparser": { "version": "1.0.3", "from": "csscolorparser@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz" }, - "cssesc": { - "version": "0.1.0", - "from": "cssesc@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "dev": true - }, - "cssnano": { - "version": "3.10.0", - "from": "cssnano@>=2.6.1 <4.0.0", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", - "dev": true - }, - "csso": { - "version": "2.3.2", - "from": "csso@>=2.3.1 <2.4.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", - "dev": true - }, "cubic-hermite": { "version": "1.0.0", "from": "cubic-hermite@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/cubic-hermite/-/cubic-hermite-1.0.0.tgz" }, - "currently-unhandled": { - "version": "0.4.1", - "from": "currently-unhandled@>=0.4.1 <0.5.0", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "dev": true - }, "cwise": { "version": "1.0.10", "from": "cwise@>=1.0.3 <2.0.0", @@ -1510,13 +515,14 @@ "cwise-parser": { "version": "1.0.3", "from": "cwise-parser@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/cwise-parser/-/cwise-parser-1.0.3.tgz" - }, - "d": { - "version": "1.0.0", - "from": "d@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/cwise-parser/-/cwise-parser-1.0.3.tgz", + "dependencies": { + "esprima": { + "version": "1.2.5", + "from": "esprima@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.5.tgz" + } + } }, "d3": { "version": "3.5.17", @@ -1545,12 +551,6 @@ } } }, - "date-now": { - "version": "0.1.4", - "from": "date-now@>=0.1.4 <0.2.0", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "dev": true - }, "debug": { "version": "2.2.0", "from": "debug@latest", @@ -1581,12 +581,6 @@ "from": "defined@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz" }, - "del": { - "version": "2.2.2", - "from": "del@>=2.0.2 <3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "dev": true - }, "delaunay-triangulate": { "version": "1.1.6", "from": "delaunay-triangulate@>=1.1.6 <2.0.0", @@ -1597,88 +591,6 @@ "from": "delayed-stream@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" }, - "delegates": { - "version": "1.0.0", - "from": "delegates@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "dev": true - }, - "depd": { - "version": "1.1.0", - "from": "depd@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz", - "dev": true - }, - "destroy": { - "version": "1.0.4", - "from": "destroy@>=1.0.4 <1.1.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "dev": true - }, - "detect-indent": { - "version": "4.0.0", - "from": "detect-indent@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "dev": true - }, - "doctrine": { - "version": "2.0.0", - "from": "doctrine@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", - "dev": true - }, - "dom-converter": { - "version": "0.1.4", - "from": "dom-converter@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz", - "dev": true, - "dependencies": { - "utila": { - "version": "0.3.3", - "from": "utila@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", - "dev": true - } - } - }, - "dom-serializer": { - "version": "0.1.0", - "from": "dom-serializer@>=0.0.0 <1.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", - "dev": true, - "dependencies": { - "domelementtype": { - "version": "1.1.3", - "from": "domelementtype@>=1.1.1 <1.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", - "dev": true - } - } - }, - "domain-browser": { - "version": "1.1.7", - "from": "domain-browser@>=1.1.1 <2.0.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", - "dev": true - }, - "domelementtype": { - "version": "1.3.0", - "from": "domelementtype@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", - "dev": true - }, - "domhandler": { - "version": "2.1.0", - "from": "domhandler@>=2.1.0 <2.2.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", - "dev": true - }, - "domutils": { - "version": "1.5.1", - "from": "domutils@1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "dev": true - }, "double-bits": { "version": "1.1.1", "from": "double-bits@>=1.1.1 <2.0.0", @@ -1722,61 +634,11 @@ "from": "edges-to-adjacency-list@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/edges-to-adjacency-list/-/edges-to-adjacency-list-1.0.0.tgz" }, - "ee-first": { - "version": "1.1.1", - "from": "ee-first@1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "dev": true - }, - "electron-to-chromium": { - "version": "1.3.8", - "from": "electron-to-chromium@>=1.2.7 <2.0.0", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.8.tgz", - "dev": true - }, "emojis-list": { "version": "2.1.0", "from": "emojis-list@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz" }, - "encodeurl": { - "version": "1.0.1", - "from": "encodeurl@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", - "dev": true - }, - "enhanced-resolve": { - "version": "0.9.1", - "from": "enhanced-resolve@>=0.9.0 <0.10.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz", - "dev": true, - "dependencies": { - "memory-fs": { - "version": "0.2.0", - "from": "memory-fs@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", - "dev": true - } - } - }, - "entities": { - "version": "1.1.1", - "from": "entities@>=1.1.1 <1.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", - "dev": true - }, - "errno": { - "version": "0.1.4", - "from": "errno@>=0.1.3 <0.2.0", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", - "dev": true - }, - "error-ex": { - "version": "1.3.1", - "from": "error-ex@>=1.2.0 <2.0.0", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "dev": true - }, "es-abstract": { "version": "1.7.0", "from": "es-abstract@>=1.5.0 <2.0.0", @@ -1787,63 +649,26 @@ "from": "es-to-primitive@>=1.1.1 <2.0.0", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz" }, - "es5-ext": { - "version": "0.10.15", - "from": "es5-ext@>=0.10.14 <0.11.0", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.15.tgz", - "dev": true - }, - "es6-iterator": { - "version": "2.0.1", - "from": "es6-iterator@>=2.0.1 <2.1.0", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", - "dev": true - }, - "es6-map": { - "version": "0.1.5", - "from": "es6-map@>=0.1.3 <0.2.0", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "dev": true - }, "es6-promise": { "version": "3.3.1", "from": "es6-promise@>=3.0.2 <4.0.0", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz" }, - "es6-set": { - "version": "0.1.5", - "from": "es6-set@>=0.1.5 <0.2.0", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "dev": true - }, - "es6-symbol": { - "version": "3.1.1", - "from": "es6-symbol@>=3.1.1 <3.2.0", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "dev": true - }, - "es6-weak-map": { - "version": "2.0.2", - "from": "es6-weak-map@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "from": "escape-html@>=1.0.3 <1.1.0", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "from": "escape-string-regexp@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + "escape-string-regexp": { + "version": "1.0.5", + "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" }, "escodegen": { "version": "1.3.3", "from": "escodegen@>=1.3.2 <1.4.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.3.3.tgz", "dependencies": { + "esprima": { + "version": "1.1.1", + "from": "esprima@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz" + }, "esutils": { "version": "1.0.0", "from": "esutils@>=1.0.0 <1.1.0", @@ -1857,144 +682,16 @@ } } }, - "escope": { - "version": "3.6.0", - "from": "escope@>=3.6.0 <4.0.0", - "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", - "dev": true, - "dependencies": { - "estraverse": { - "version": "4.2.0", - "from": "estraverse@>=4.1.1 <5.0.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "dev": true - } - } - }, - "eslint": { - "version": "3.19.0", - "from": "eslint@>=3.9.0 <4.0.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", - "dev": true, - "dependencies": { - "concat-stream": { - "version": "1.6.0", - "from": "concat-stream@>=1.5.2 <2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", - "dev": true - }, - "estraverse": { - "version": "4.2.0", - "from": "estraverse@>=4.2.0 <5.0.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "dev": true - }, - "readable-stream": { - "version": "2.2.6", - "from": "readable-stream@>=2.2.2 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", - "dev": true - } - } - }, - "eslint-config-airbnb-base": { - "version": "9.0.0", - "from": "eslint-config-airbnb-base@>=9.0.0 <10.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-9.0.0.tgz", - "dev": true - }, - "eslint-import-resolver-node": { - "version": "0.2.3", - "from": "eslint-import-resolver-node@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz", - "dev": true - }, - "eslint-loader": { - "version": "1.7.1", - "from": "eslint-loader@>=1.6.0 <2.0.0", - "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-1.7.1.tgz", - "dev": true, - "dependencies": { - "loader-utils": { - "version": "1.1.0", - "from": "loader-utils@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "dev": true - } - } - }, - "eslint-module-utils": { - "version": "2.0.0", - "from": "eslint-module-utils@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.0.0.tgz", - "dev": true - }, - "eslint-plugin-import": { - "version": "2.2.0", - "from": "eslint-plugin-import@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.2.0.tgz", - "dev": true, - "dependencies": { - "doctrine": { - "version": "1.5.0", - "from": "doctrine@1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "dev": true - } - } - }, - "espree": { - "version": "3.4.2", - "from": "espree@>=3.4.0 <4.0.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.4.2.tgz", - "dev": true, - "dependencies": { - "acorn": { - "version": "5.0.3", - "from": "acorn@>=5.0.1 <6.0.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.0.3.tgz", - "dev": true - } - } - }, "esprima": { - "version": "1.1.1", - "from": "esprima@>=1.1.1 <1.2.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz" + "version": "2.7.3", + "from": "esprima@>=2.6.0 <3.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz" }, "espurify": { "version": "1.7.0", "from": "espurify@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/espurify/-/espurify-1.7.0.tgz" }, - "esquery": { - "version": "1.0.0", - "from": "esquery@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", - "dev": true, - "dependencies": { - "estraverse": { - "version": "4.2.0", - "from": "estraverse@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.1.0", - "from": "esrecurse@>=4.1.0 <5.0.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.1.0.tgz", - "dev": true, - "dependencies": { - "estraverse": { - "version": "4.1.1", - "from": "estraverse@>=4.1.0 <4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.1.1.tgz", - "dev": true - } - } - }, "estraverse": { "version": "1.5.1", "from": "estraverse@>=1.5.0 <1.6.0", @@ -2005,95 +702,21 @@ "from": "esutils@>=2.0.2 <3.0.0", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz" }, - "etag": { - "version": "1.8.0", - "from": "etag@>=1.8.0 <1.9.0", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz", - "dev": true - }, - "event-emitter": { - "version": "0.3.5", - "from": "event-emitter@>=0.3.5 <0.4.0", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "dev": true - }, - "eventemitter3": { - "version": "1.2.0", - "from": "eventemitter3@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", - "dev": true - }, "events": { "version": "1.1.1", "from": "events@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz" }, - "eventsource": { - "version": "0.1.6", - "from": "eventsource@0.1.6", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", - "dev": true - }, - "exit-hook": { - "version": "1.1.1", - "from": "exit-hook@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", - "dev": true - }, - "expand-brackets": { - "version": "0.1.5", - "from": "expand-brackets@>=0.1.4 <0.2.0", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "dev": true - }, - "expand-range": { - "version": "1.8.2", - "from": "expand-range@>=1.8.1 <2.0.0", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "dev": true - }, - "express": { - "version": "4.15.2", - "from": "express@>=4.13.3 <5.0.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.15.2.tgz", - "dev": true, - "dependencies": { - "debug": { - "version": "2.6.1", - "from": "debug@2.6.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.1.tgz", - "dev": true - }, - "ms": { - "version": "0.7.2", - "from": "ms@0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "dev": true - } - } - }, "extend": { "version": "3.0.0", "from": "extend@>=3.0.0 <3.1.0", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" }, - "extglob": { - "version": "0.3.2", - "from": "extglob@>=0.3.1 <0.4.0", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "dev": true - }, "extract-frustum-planes": { "version": "1.0.0", "from": "extract-frustum-planes@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/extract-frustum-planes/-/extract-frustum-planes-1.0.0.tgz" }, - "extract-text-webpack-plugin": { - "version": "1.0.1", - "from": "extract-text-webpack-plugin@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-1.0.1.tgz", - "dev": true - }, "extsprintf": { "version": "1.0.2", "from": "extsprintf@1.0.2", @@ -2104,6 +727,11 @@ "from": "falafel@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/falafel/-/falafel-1.2.0.tgz", "dependencies": { + "acorn": { + "version": "1.2.2", + "from": "acorn@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz" + }, "isarray": { "version": "0.0.1", "from": "isarray@0.0.1", @@ -2121,95 +749,26 @@ "from": "fast-levenshtein@>=2.0.4 <2.1.0", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" }, - "fastparse": { - "version": "1.1.1", - "from": "fastparse@>=1.1.1 <2.0.0", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", - "dev": true - }, - "faye-websocket": { - "version": "0.10.0", - "from": "faye-websocket@>=0.10.0 <0.11.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "dev": true - }, "feature-filter": { "version": "2.2.0", "from": "feature-filter@>=2.2.0 <3.0.0", "resolved": "https://registry.npmjs.org/feature-filter/-/feature-filter-2.2.0.tgz" }, - "figures": { - "version": "1.7.0", - "from": "figures@>=1.3.5 <2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "dev": true - }, - "file-entry-cache": { - "version": "2.0.0", - "from": "file-entry-cache@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "dev": true - }, - "file-loader": { - "version": "0.9.0", - "from": "file-loader@>=0.9.0 <0.10.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-0.9.0.tgz", - "dev": true - }, - "filename-regex": { - "version": "2.0.1", - "from": "filename-regex@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "dev": true - }, - "fill-range": { - "version": "2.2.3", - "from": "fill-range@>=2.1.0 <3.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "dev": true - }, "filtered-vector": { "version": "1.2.4", "from": "filtered-vector@>=1.2.1 <2.0.0", "resolved": "https://registry.npmjs.org/filtered-vector/-/filtered-vector-1.2.4.tgz" }, - "finalhandler": { - "version": "1.0.2", - "from": "finalhandler@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.2.tgz", - "dev": true, - "dependencies": { - "debug": { - "version": "2.6.4", - "from": "debug@2.6.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.4.tgz", - "dev": true - }, - "ms": { - "version": "0.7.3", - "from": "ms@0.7.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz", - "dev": true - } - } - }, - "find-cache-dir": { - "version": "0.1.1", - "from": "find-cache-dir@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", - "dev": true - }, - "find-up": { - "version": "1.1.2", - "from": "find-up@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "dev": true - }, "findup": { "version": "0.1.5", "from": "findup@>=0.1.5 <0.2.0", "resolved": "https://registry.npmjs.org/findup/-/findup-0.1.5.tgz", "dependencies": { + "colors": { + "version": "0.6.2", + "from": "colors@>=0.6.0-1 <0.7.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" + }, "commander": { "version": "2.1.0", "from": "commander@>=2.1.0 <2.2.0", @@ -2217,18 +776,6 @@ } } }, - "flat-cache": { - "version": "1.2.2", - "from": "flat-cache@>=1.2.1 <2.0.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", - "dev": true - }, - "flatten": { - "version": "1.0.2", - "from": "flatten@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", - "dev": true - }, "font-atlas-sdf": { "version": "1.2.0", "from": "font-atlas-sdf@>=1.0.0 <2.0.0", @@ -2244,18 +791,6 @@ "from": "for-each@>=0.3.2 <0.4.0", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.2.tgz" }, - "for-in": { - "version": "1.0.2", - "from": "for-in@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "dev": true - }, - "for-own": { - "version": "0.1.5", - "from": "for-own@>=0.1.4 <0.2.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "dev": true - }, "foreach": { "version": "2.0.5", "from": "foreach@>=2.0.5 <3.0.0", @@ -2271,29 +806,11 @@ "from": "form-data@>=2.1.1 <2.2.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz" }, - "forwarded": { - "version": "0.1.0", - "from": "forwarded@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz", - "dev": true - }, - "fresh": { - "version": "0.5.0", - "from": "fresh@0.5.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.0.tgz", - "dev": true - }, "fs.realpath": { "version": "1.0.0", "from": "fs.realpath@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" }, - "fstream": { - "version": "1.0.11", - "from": "fstream@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "dev": true - }, "function-bind": { "version": "1.1.0", "from": "function-bind@>=1.0.2 <2.0.0", @@ -2309,18 +826,6 @@ "from": "gamma@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/gamma/-/gamma-0.1.0.tgz" }, - "gauge": { - "version": "2.7.4", - "from": "gauge@>=2.7.1 <2.8.0", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "dev": true - }, - "gaze": { - "version": "1.1.2", - "from": "gaze@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", - "dev": true - }, "generate-function": { "version": "2.0.0", "from": "generate-function@>=2.0.0 <3.0.0", @@ -2358,23 +863,11 @@ "from": "geojson-vt@>=2.4.0 <3.0.0", "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-2.4.0.tgz" }, - "get-caller-file": { - "version": "1.0.2", - "from": "get-caller-file@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "dev": true - }, "get-canvas-context": { "version": "1.0.2", "from": "get-canvas-context@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/get-canvas-context/-/get-canvas-context-1.0.2.tgz" }, - "get-stdin": { - "version": "4.0.1", - "from": "get-stdin@>=4.0.1 <5.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "dev": true - }, "getpass": { "version": "0.1.6", "from": "getpass@>=0.1.1 <0.2.0", @@ -2407,10 +900,20 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -2456,10 +959,20 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -2483,10 +996,20 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -2532,10 +1055,20 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -2559,10 +1092,20 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -2611,10 +1154,20 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -2648,10 +1201,20 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -2675,10 +1238,20 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -2707,10 +1280,20 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -2744,11 +1327,21 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" + }, "snap-points-2d": { "version": "3.1.0", "from": "snap-points-2d@>=3.0.0 <4.0.0", @@ -2786,11 +1379,21 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" + }, "snap-points-2d": { "version": "3.1.0", "from": "snap-points-2d@>=3.1.0 <4.0.0", @@ -2818,10 +1421,20 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -2845,10 +1458,20 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -2887,10 +1510,20 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -2919,10 +1552,20 @@ "from": "glslify-bundle@>=2.0.4 <3.0.0", "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-2.0.4.tgz" }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, "minimist": { "version": "1.2.0", "from": "minimist@^1.1.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" } } }, @@ -2951,44 +1594,6 @@ "from": "glob@>=7.0.3 <8.0.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz" }, - "glob-base": { - "version": "0.3.0", - "from": "glob-base@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "dev": true - }, - "glob-parent": { - "version": "2.0.0", - "from": "glob-parent@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "dev": true - }, - "globals": { - "version": "9.17.0", - "from": "globals@>=9.0.0 <10.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.17.0.tgz", - "dev": true - }, - "globby": { - "version": "5.0.0", - "from": "globby@>=5.0.0 <6.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "dev": true - }, - "globule": { - "version": "1.1.0", - "from": "globule@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.1.0.tgz", - "dev": true, - "dependencies": { - "lodash": { - "version": "4.16.6", - "from": "lodash@>=4.16.4 <4.17.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.16.6.tgz", - "dev": true - } - } - }, "glsl-inject-defines": { "version": "1.0.3", "from": "glsl-inject-defines@>=1.0.1 <2.0.0", @@ -3109,9 +1714,9 @@ "resolved": "https://registry.npmjs.org/glslify-deps/-/glslify-deps-1.3.0.tgz" }, "graceful-fs": { - "version": "4.1.11", + "version": "4.1.9", "from": "graceful-fs@>=4.1.2 <5.0.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.9.tgz" }, "graceful-readlink": { "version": "1.0.1", @@ -3123,12 +1728,6 @@ "from": "grid-index@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.0.0.tgz" }, - "growly": { - "version": "1.3.0", - "from": "growly@>=1.2.0 <2.0.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "dev": true - }, "har-validator": { "version": "2.0.6", "from": "har-validator@>=2.0.6 <2.1.0", @@ -3149,186 +1748,36 @@ "from": "has-color@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz" }, - "has-flag": { - "version": "1.0.0", - "from": "has-flag@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "dev": true - }, - "has-unicode": { - "version": "2.0.1", - "from": "has-unicode@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "dev": true - }, "hawk": { "version": "3.1.3", "from": "hawk@>=3.1.3 <3.2.0", "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz" }, - "he": { - "version": "1.1.1", - "from": "he@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "dev": true - }, "hoek": { "version": "2.16.3", "from": "hoek@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" }, - "home-or-tmp": { - "version": "2.0.0", - "from": "home-or-tmp@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "dev": true - }, - "hosted-git-info": { - "version": "2.4.2", - "from": "hosted-git-info@>=2.1.4 <3.0.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.4.2.tgz", - "dev": true - }, - "html-comment-regex": { - "version": "1.1.1", - "from": "html-comment-regex@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", - "dev": true - }, - "html-minifier": { - "version": "3.4.3", - "from": "html-minifier@>=3.2.3 <4.0.0", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.4.3.tgz", - "dev": true, - "dependencies": { - "uglify-js": { - "version": "2.8.22", - "from": "uglify-js@>=2.8.22 <2.9.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.22.tgz", - "dev": true - } - } - }, - "html-webpack-plugin": { - "version": "2.28.0", - "from": "html-webpack-plugin@>=2.24.0 <3.0.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-2.28.0.tgz", - "dev": true - }, - "htmlparser2": { - "version": "3.3.0", - "from": "htmlparser2@>=3.3.0 <3.4.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", - "dev": true, - "dependencies": { - "domutils": { - "version": "1.1.6", - "from": "domutils@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", - "dev": true - } - } - }, - "http-errors": { - "version": "1.6.1", - "from": "http-errors@>=1.6.1 <1.7.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.1.tgz", - "dev": true - }, - "http-proxy": { - "version": "1.16.2", - "from": "http-proxy@>=1.16.2 <2.0.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", - "dev": true - }, - "http-proxy-middleware": { - "version": "0.17.4", - "from": "http-proxy-middleware@>=0.17.1 <0.18.0", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz", - "dev": true, - "dependencies": { - "is-extglob": { - "version": "2.1.1", - "from": "is-extglob@>=2.1.0 <3.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "dev": true - }, - "is-glob": { - "version": "3.1.0", - "from": "is-glob@>=3.1.0 <4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "dev": true - } - } - }, - "http-signature": { + "http-signature": { "version": "1.1.1", "from": "http-signature@>=1.1.0 <1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz" }, - "https-browserify": { - "version": "0.0.1", - "from": "https-browserify@0.0.1", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", - "dev": true - }, "husl": { "version": "5.0.3", "from": "husl@>=5.0.0 <6.0.0", "resolved": "https://registry.npmjs.org/husl/-/husl-5.0.3.tgz" }, - "icss-replace-symbols": { - "version": "1.0.2", - "from": "icss-replace-symbols@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.0.2.tgz", - "dev": true - }, "ieee754": { "version": "1.1.8", "from": "ieee754@>=1.1.4 <2.0.0", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz" }, - "ignore": { - "version": "3.2.7", - "from": "ignore@>=3.2.0 <4.0.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.2.7.tgz", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "from": "imurmurhash@>=0.1.4 <0.2.0", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "dev": true - }, - "in-publish": { - "version": "2.0.0", - "from": "in-publish@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", - "dev": true - }, "incremental-convex-hull": { "version": "1.0.1", "from": "incremental-convex-hull@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/incremental-convex-hull/-/incremental-convex-hull-1.0.1.tgz" }, - "indent-string": { - "version": "2.1.0", - "from": "indent-string@>=2.1.0 <3.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "dev": true - }, - "indexes-of": { - "version": "1.0.1", - "from": "indexes-of@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "dev": true - }, - "indexof": { - "version": "0.0.1", - "from": "indexof@0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "dev": true - }, "inflight": { "version": "1.0.6", "from": "inflight@>=1.0.4 <2.0.0", @@ -3339,35 +1788,11 @@ "from": "inherits@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, - "inquirer": { - "version": "0.12.0", - "from": "inquirer@>=0.12.0 <0.13.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", - "dev": true - }, - "interpret": { - "version": "1.0.3", - "from": "interpret@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz", - "dev": true - }, "interval-tree-1d": { "version": "1.0.3", "from": "interval-tree-1d@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/interval-tree-1d/-/interval-tree-1d-1.0.3.tgz" }, - "invariant": { - "version": "2.2.2", - "from": "invariant@>=2.2.0 <3.0.0", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", - "dev": true - }, - "invert-kv": { - "version": "1.0.0", - "from": "invert-kv@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "dev": true - }, "invert-permutation": { "version": "1.0.0", "from": "invert-permutation@>=1.0.0 <2.0.0", @@ -3378,41 +1803,11 @@ "from": "iota-array@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz" }, - "ipaddr.js": { - "version": "1.3.0", - "from": "ipaddr.js@1.3.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.3.0.tgz", - "dev": true - }, - "is-absolute-url": { - "version": "2.1.0", - "from": "is-absolute-url@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "from": "is-arrayish@>=0.2.1 <0.3.0", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "from": "is-binary-path@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "dev": true - }, "is-buffer": { "version": "1.1.4", "from": "is-buffer@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.4.tgz" }, - "is-builtin-module": { - "version": "1.0.0", - "from": "is-builtin-module@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "dev": true - }, "is-callable": { "version": "1.1.3", "from": "is-callable@>=1.1.3 <2.0.0", @@ -3423,53 +1818,11 @@ "from": "is-date-object@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz" }, - "is-dotfile": { - "version": "1.0.2", - "from": "is-dotfile@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.2.tgz", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "from": "is-equal-shallow@>=0.1.3 <0.2.0", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "dev": true - }, - "is-extendable": { - "version": "0.1.1", - "from": "is-extendable@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "dev": true - }, - "is-extglob": { - "version": "1.0.0", - "from": "is-extglob@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "dev": true - }, - "is-finite": { - "version": "1.0.2", - "from": "is-finite@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "dev": true - }, "is-function": { "version": "1.0.1", "from": "is-function@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz" }, - "is-glob": { - "version": "2.0.1", - "from": "is-glob@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "dev": true - }, "is-mobile": { "version": "0.2.2", "from": "is-mobile@>=0.2.2 <0.3.0", @@ -3480,47 +1833,11 @@ "from": "is-my-json-valid@>=2.10.0 <3.0.0", "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz" }, - "is-number": { - "version": "2.1.0", - "from": "is-number@>=2.1.0 <3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "dev": true - }, - "is-path-cwd": { - "version": "1.0.0", - "from": "is-path-cwd@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.0", - "from": "is-path-in-cwd@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", - "dev": true - }, - "is-path-inside": { - "version": "1.0.0", - "from": "is-path-inside@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", - "dev": true - }, "is-plain-obj": { "version": "1.1.0", "from": "is-plain-obj@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz" }, - "is-posix-bracket": { - "version": "0.1.1", - "from": "is-posix-bracket@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "from": "is-primitive@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "dev": true - }, "is-property": { "version": "1.0.2", "from": "is-property@>=1.0.0 <2.0.0", @@ -3531,18 +1848,6 @@ "from": "is-regex@>=1.0.3 <2.0.0", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz" }, - "is-resolvable": { - "version": "1.0.0", - "from": "is-resolvable@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", - "dev": true - }, - "is-svg": { - "version": "2.1.0", - "from": "is-svg@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", - "dev": true - }, "is-symbol": { "version": "1.0.1", "from": "is-symbol@>=1.0.1 <2.0.0", @@ -3553,29 +1858,11 @@ "from": "is-typedarray@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" }, - "is-utf8": { - "version": "0.2.1", - "from": "is-utf8@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "dev": true - }, "isarray": { "version": "1.0.0", "from": "isarray@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" }, - "isexe": { - "version": "2.0.0", - "from": "isexe@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "dev": true - }, - "isobject": { - "version": "2.1.0", - "from": "isobject@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "dev": true - }, "isstream": { "version": "0.1.2", "from": "isstream@>=0.1.2 <0.2.0", @@ -3597,77 +1884,27 @@ "from": "jquery-ui@latest", "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.12.1.tgz" }, - "js-base64": { - "version": "2.1.9", - "from": "js-base64@>=2.1.9 <3.0.0", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", - "dev": true - }, - "js-tokens": { - "version": "3.0.1", - "from": "js-tokens@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz", - "dev": true - }, - "js-yaml": { - "version": "3.7.0", - "from": "js-yaml@>=3.7.0 <3.8.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", - "dev": true, - "dependencies": { - "esprima": { - "version": "2.7.3", - "from": "esprima@>=2.6.0 <3.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "dev": true - } - } - }, "jsbn": { "version": "0.1.0", "from": "jsbn@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.0.tgz", "optional": true }, - "jsesc": { - "version": "1.3.0", - "from": "jsesc@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "dev": true - }, "json-schema": { "version": "0.2.3", "from": "json-schema@0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" }, - "json-stable-stringify": { - "version": "1.0.1", - "from": "json-stable-stringify@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "dev": true - }, "json-stringify-safe": { "version": "5.0.1", "from": "json-stringify-safe@>=5.0.1 <5.1.0", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" }, - "json3": { - "version": "3.3.2", - "from": "json3@>=3.3.2 <4.0.0", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "dev": true - }, "json5": { "version": "0.5.0", "from": "json5@>=0.5.0 <0.6.0", "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.0.tgz" }, - "jsonify": { - "version": "0.0.0", - "from": "jsonify@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "dev": true - }, "jsonlint-lines-primitives": { "version": "1.6.0", "from": "jsonlint-lines-primitives@>=1.6.0 <1.7.0", @@ -3703,12 +1940,6 @@ "from": "lazy-cache@>=1.0.3 <2.0.0", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz" }, - "lcid": { - "version": "1.0.0", - "from": "lcid@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "dev": true - }, "leaflet": { "version": "1.0.2", "from": "leaflet@latest", @@ -3724,73 +1955,11 @@ "from": "levn@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz" }, - "load-json-file": { - "version": "1.1.0", - "from": "load-json-file@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "dev": true, - "dependencies": { - "strip-bom": { - "version": "2.0.0", - "from": "strip-bom@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "dev": true - } - } - }, - "loader-fs-cache": { - "version": "1.0.1", - "from": "loader-fs-cache@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz", - "dev": true - }, "loader-utils": { "version": "0.2.16", "from": "loader-utils@>=0.2.11 <0.3.0", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.16.tgz" }, - "lodash": { - "version": "4.17.4", - "from": "lodash@>=4.2.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "dev": true - }, - "lodash._arraycopy": { - "version": "3.0.0", - "from": "lodash._arraycopy@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz", - "dev": true - }, - "lodash._arrayeach": { - "version": "3.0.0", - "from": "lodash._arrayeach@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz", - "dev": true - }, - "lodash._baseassign": { - "version": "3.2.0", - "from": "lodash._baseassign@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", - "dev": true - }, - "lodash._baseclone": { - "version": "3.3.0", - "from": "lodash._baseclone@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/lodash._baseclone/-/lodash._baseclone-3.3.0.tgz", - "dev": true - }, - "lodash._basecopy": { - "version": "3.0.1", - "from": "lodash._basecopy@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "dev": true - }, - "lodash._basefor": { - "version": "3.0.3", - "from": "lodash._basefor@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/lodash._basefor/-/lodash._basefor-3.0.3.tgz", - "dev": true - }, "lodash._baseisequal": { "version": "3.0.7", "from": "lodash._baseisequal@>=3.0.0 <4.0.0", @@ -3801,53 +1970,11 @@ "from": "lodash._bindcallback@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz" }, - "lodash._createcompounder": { - "version": "3.0.0", - "from": "lodash._createcompounder@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/lodash._createcompounder/-/lodash._createcompounder-3.0.0.tgz", - "dev": true - }, "lodash._getnative": { "version": "3.9.1", "from": "lodash._getnative@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz" }, - "lodash._root": { - "version": "3.0.1", - "from": "lodash._root@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", - "dev": true - }, - "lodash.assign": { - "version": "4.2.0", - "from": "lodash.assign@>=4.2.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "dev": true - }, - "lodash.camelcase": { - "version": "3.0.1", - "from": "lodash.camelcase@>=3.0.1 <4.0.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-3.0.1.tgz", - "dev": true - }, - "lodash.clonedeep": { - "version": "4.5.0", - "from": "lodash.clonedeep@>=4.3.2 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "dev": true - }, - "lodash.cond": { - "version": "4.5.2", - "from": "lodash.cond@>=4.3.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz", - "dev": true - }, - "lodash.deburr": { - "version": "3.2.0", - "from": "lodash.deburr@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/lodash.deburr/-/lodash.deburr-3.2.0.tgz", - "dev": true - }, "lodash.isarguments": { "version": "3.1.0", "from": "lodash.isarguments@>=3.0.0 <4.0.0", @@ -3873,65 +2000,11 @@ "from": "lodash.keys@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz" }, - "lodash.memoize": { - "version": "4.1.2", - "from": "lodash.memoize@>=4.1.2 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "dev": true - }, - "lodash.mergewith": { - "version": "4.6.0", - "from": "lodash.mergewith@>=4.6.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz", - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "from": "lodash.uniq@>=4.5.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "dev": true - }, - "lodash.words": { - "version": "3.2.0", - "from": "lodash.words@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/lodash.words/-/lodash.words-3.2.0.tgz", - "dev": true - }, "longest": { "version": "1.0.1", "from": "longest@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz" }, - "loose-envify": { - "version": "1.3.1", - "from": "loose-envify@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", - "dev": true - }, - "loud-rejection": { - "version": "1.6.0", - "from": "loud-rejection@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "dev": true - }, - "lower-case": { - "version": "1.1.4", - "from": "lower-case@>=1.1.1 <2.0.0", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "dev": true - }, - "lru-cache": { - "version": "4.0.2", - "from": "lru-cache@>=4.0.1 <5.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", - "dev": true - }, - "macaddress": { - "version": "0.2.8", - "from": "macaddress@>=0.2.8 <0.3.0", - "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", - "dev": true - }, "map-limit": { "version": "0.0.1", "from": "map-limit@0.0.1", @@ -3944,32 +2017,28 @@ } } }, - "map-obj": { - "version": "1.0.1", - "from": "map-obj@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "dev": true - }, "mapbox-gl": { "version": "0.22.1", "from": "mapbox-gl@>=0.22.0 <0.23.0", - "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-0.22.1.tgz" + "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-0.22.1.tgz", + "dependencies": { + "mapbox-gl-shaders": { + "version": "1.0.0", + "from": "mapbox/mapbox-gl-shaders#de2ab007455aa2587c552694c68583f94c9f2747", + "resolved": "git://github.com/mapbox/mapbox-gl-shaders.git#de2ab007455aa2587c552694c68583f94c9f2747" + }, + "mapbox-gl-style-spec": { + "version": "8.8.0", + "from": "mapbox/mapbox-gl-style-spec#83b1a3e5837d785af582efd5ed1a212f2df6a4ae", + "resolved": "git://github.com/mapbox/mapbox-gl-style-spec.git#83b1a3e5837d785af582efd5ed1a212f2df6a4ae" + } + } }, "mapbox-gl-function": { "version": "1.3.0", "from": "mapbox-gl-function@>=1.2.1 <2.0.0", "resolved": "https://registry.npmjs.org/mapbox-gl-function/-/mapbox-gl-function-1.3.0.tgz" }, - "mapbox-gl-shaders": { - "version": "1.0.0", - "from": "mapbox/mapbox-gl-shaders#de2ab007455aa2587c552694c68583f94c9f2747", - "resolved": "git+https://github.com/mapbox/mapbox-gl-shaders.git#de2ab007455aa2587c552694c68583f94c9f2747" - }, - "mapbox-gl-style-spec": { - "version": "8.8.0", - "from": "mapbox/mapbox-gl-style-spec#83b1a3e5837d785af582efd5ed1a212f2df6a4ae", - "resolved": "git+https://github.com/mapbox/mapbox-gl-style-spec.git#83b1a3e5837d785af582efd5ed1a212f2df6a4ae" - }, "mapbox-gl-supported": { "version": "1.2.0", "from": "mapbox-gl-supported@>=1.2.0 <2.0.0", @@ -3985,12 +2054,6 @@ "from": "marked@latest", "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz" }, - "marked-terminal": { - "version": "1.7.0", - "from": "marked-terminal@>=1.6.2 <2.0.0", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-1.7.0.tgz", - "dev": true - }, "mat4-decompose": { "version": "1.0.4", "from": "mat4-decompose@>=1.0.3 <2.0.0", @@ -4011,81 +2074,11 @@ "from": "material-design-iconic-font@latest", "resolved": "https://registry.npmjs.org/material-design-iconic-font/-/material-design-iconic-font-2.2.0.tgz" }, - "math-expression-evaluator": { - "version": "1.2.17", - "from": "math-expression-evaluator@>=1.2.14 <2.0.0", - "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", - "dev": true - }, "matrix-camera-controller": { "version": "2.1.3", "from": "matrix-camera-controller@>=2.1.3 <3.0.0", "resolved": "https://registry.npmjs.org/matrix-camera-controller/-/matrix-camera-controller-2.1.3.tgz" }, - "media-typer": { - "version": "0.3.0", - "from": "media-typer@0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "dev": true - }, - "memory-fs": { - "version": "0.3.0", - "from": "memory-fs@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.3.0.tgz", - "dev": true, - "dependencies": { - "readable-stream": { - "version": "2.2.9", - "from": "readable-stream@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", - "dev": true - }, - "string_decoder": { - "version": "1.0.0", - "from": "string_decoder@~1.0.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz", - "dev": true - } - } - }, - "meow": { - "version": "3.7.0", - "from": "meow@>=3.7.0 <4.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "dev": true, - "dependencies": { - "minimist": { - "version": "1.2.0", - "from": "minimist@^1.1.3", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "dev": true - } - } - }, - "merge-descriptors": { - "version": "1.0.1", - "from": "merge-descriptors@1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "dev": true - }, - "methods": { - "version": "1.1.2", - "from": "methods@>=1.1.2 <1.2.0", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "dev": true - }, - "micromatch": { - "version": "2.3.11", - "from": "micromatch@>=2.1.5 <3.0.0", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "dev": true - }, - "mime": { - "version": "1.3.4", - "from": "mime@>=1.3.0 <1.4.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", - "dev": true - }, "mime-db": { "version": "1.24.0", "from": "mime-db@>=1.24.0 <1.25.0", @@ -4106,12 +2099,6 @@ "from": "minimist@0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" }, - "mkdirp": { - "version": "0.5.1", - "from": "mkdirp@>=0.5.1 <0.6.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "dev": true - }, "moment": { "version": "2.15.2", "from": "moment@latest", @@ -4186,30 +2173,6 @@ "from": "mustache@latest", "resolved": "https://registry.npmjs.org/mustache/-/mustache-2.2.1.tgz" }, - "mute-stream": { - "version": "0.0.5", - "from": "mute-stream@0.0.5", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", - "dev": true - }, - "nan": { - "version": "2.6.2", - "from": "nan@>=2.3.2 <3.0.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "from": "natural-compare@>=1.4.0 <2.0.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "dev": true - }, - "ncname": { - "version": "1.0.0", - "from": "ncname@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/ncname/-/ncname-1.0.0.tgz", - "dev": true - }, "ndarray": { "version": "1.0.18", "from": "ndarray@>=1.0.16 <2.0.0", @@ -4265,12 +2228,6 @@ "from": "ndarray-warp@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/ndarray-warp/-/ndarray-warp-1.0.1.tgz" }, - "negotiator": { - "version": "0.6.1", - "from": "negotiator@0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "dev": true - }, "nextafter": { "version": "1.0.0", "from": "nextafter@>=1.0.0 <2.0.0", @@ -4298,72 +2255,6 @@ "from": "ng-annotate-loader@latest", "resolved": "https://registry.npmjs.org/ng-annotate-loader/-/ng-annotate-loader-0.2.0.tgz" }, - "no-case": { - "version": "2.3.1", - "from": "no-case@>=2.2.0 <3.0.0", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz", - "dev": true - }, - "node-emoji": { - "version": "1.5.1", - "from": "node-emoji@>=1.4.1 <2.0.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.5.1.tgz", - "dev": true - }, - "node-gyp": { - "version": "3.6.0", - "from": "node-gyp@>=3.3.1 <4.0.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.0.tgz", - "dev": true - }, - "node-libs-browser": { - "version": "0.7.0", - "from": "node-libs-browser@>=0.7.0 <0.8.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-0.7.0.tgz", - "dev": true, - "dependencies": { - "readable-stream": { - "version": "2.2.9", - "from": "readable-stream@>=2.0.5 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", - "dev": true, - "dependencies": { - "string_decoder": { - "version": "1.0.0", - "from": "string_decoder@~1.0.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz", - "dev": true - } - } - } - } - }, - "node-notifier": { - "version": "4.6.1", - "from": "node-notifier@>=4.1.0 <5.0.0", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-4.6.1.tgz", - "dev": true, - "dependencies": { - "lodash.clonedeep": { - "version": "3.0.2", - "from": "lodash.clonedeep@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-3.0.2.tgz", - "dev": true - }, - "minimist": { - "version": "1.2.0", - "from": "minimist@^1.1.1", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "dev": true - } - } - }, - "node-sass": { - "version": "4.5.2", - "from": "node-sass@>=4.3.0 <5.0.0", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.5.2.tgz", - "dev": true - }, "nomnom": { "version": "1.8.1", "from": "nomnom@>=1.5.0", @@ -4391,64 +2282,16 @@ } } }, - "nopt": { - "version": "3.0.6", - "from": "nopt@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "dev": true - }, - "normalize-package-data": { - "version": "2.3.8", - "from": "normalize-package-data@>=2.3.4 <3.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.8.tgz", - "dev": true - }, "normalize-path": { "version": "2.0.1", "from": "normalize-path@>=2.0.1 <3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.0.1.tgz" }, - "normalize-range": { - "version": "0.1.2", - "from": "normalize-range@>=0.1.2 <0.2.0", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "dev": true - }, - "normalize-url": { - "version": "1.9.1", - "from": "normalize-url@>=1.4.0 <2.0.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "dev": true - }, "normals": { "version": "1.1.0", "from": "normals@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/normals/-/normals-1.1.0.tgz" }, - "npmlog": { - "version": "4.0.2", - "from": "npmlog@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz", - "dev": true - }, - "nth-check": { - "version": "1.0.1", - "from": "nth-check@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", - "dev": true - }, - "num2fraction": { - "version": "1.2.2", - "from": "num2fraction@>=1.2.2 <2.0.0", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "from": "number-is-nan@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "dev": true - }, "numeric": { "version": "1.2.6", "from": "numeric@>=1.2.6 <2.0.0", @@ -4464,12 +2307,6 @@ "from": "object-assign@>=4.0.1 <5.0.0", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz" }, - "object-hash": { - "version": "1.1.8", - "from": "object-hash@>=1.1.4 <2.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.1.8.tgz", - "dev": true - }, "object-inspect": { "version": "1.2.2", "from": "object-inspect@>=1.2.1 <1.3.0", @@ -4480,41 +2317,11 @@ "from": "object-keys@>=1.0.8 <2.0.0", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz" }, - "object.omit": { - "version": "2.0.1", - "from": "object.omit@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "dev": true - }, - "on-finished": { - "version": "2.3.0", - "from": "on-finished@>=2.3.0 <2.4.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "dev": true - }, - "on-headers": { - "version": "1.0.1", - "from": "on-headers@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", - "dev": true - }, "once": { "version": "1.4.0", "from": "once@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" }, - "onetime": { - "version": "1.1.0", - "from": "onetime@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "dev": true - }, - "open": { - "version": "0.0.5", - "from": "open@0.0.5", - "resolved": "https://registry.npmjs.org/open/-/open-0.0.5.tgz", - "dev": true - }, "optimist": { "version": "0.6.1", "from": "optimist@>=0.6.0 <0.7.0", @@ -4547,50 +2354,6 @@ "from": "ordered-esprima-props@>=1.1.0 <1.2.0", "resolved": "https://registry.npmjs.org/ordered-esprima-props/-/ordered-esprima-props-1.1.0.tgz" }, - "original": { - "version": "1.0.0", - "from": "original@>=0.0.5", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.0.tgz", - "dev": true, - "dependencies": { - "url-parse": { - "version": "1.0.5", - "from": "url-parse@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.0.5.tgz", - "dev": true - } - } - }, - "os-browserify": { - "version": "0.2.1", - "from": "os-browserify@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "from": "os-homedir@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "dev": true - }, - "os-locale": { - "version": "1.4.0", - "from": "os-locale@>=1.4.0 <2.0.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "dev": true - }, - "os-tmpdir": { - "version": "1.0.2", - "from": "os-tmpdir@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "dev": true - }, - "osenv": { - "version": "0.1.4", - "from": "osenv@>=0.0.0 <1.0.0", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", - "dev": true - }, "pace-progress": { "version": "1.0.2", "from": "git+https://github.com/getredash/pace.git", @@ -4601,87 +2364,21 @@ "from": "pad-left@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/pad-left/-/pad-left-1.0.2.tgz" }, - "pako": { - "version": "0.2.9", - "from": "pako@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "dev": true - }, - "param-case": { - "version": "2.1.1", - "from": "param-case@>=2.1.0 <2.2.0", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "dev": true - }, - "parse-glob": { - "version": "3.0.4", - "from": "parse-glob@>=3.0.4 <4.0.0", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "dev": true - }, - "parse-json": { - "version": "2.2.0", - "from": "parse-json@>=2.2.0 <3.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "dev": true - }, "parse-unit": { "version": "1.0.1", "from": "parse-unit@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/parse-unit/-/parse-unit-1.0.1.tgz" }, - "parseurl": { - "version": "1.3.1", - "from": "parseurl@>=1.3.1 <1.4.0", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", - "dev": true - }, - "path-browserify": { - "version": "0.0.0", - "from": "path-browserify@0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "dev": true - }, - "path-exists": { - "version": "2.1.0", - "from": "path-exists@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "dev": true - }, "path-is-absolute": { "version": "1.0.1", "from": "path-is-absolute@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" }, - "path-is-inside": { - "version": "1.0.2", - "from": "path-is-inside@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "from": "path-to-regexp@0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "dev": true - }, - "path-type": { - "version": "1.1.0", - "from": "path-type@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "dev": true - }, "pbf": { "version": "1.3.7", "from": "pbf@>=1.3.2 <2.0.0", "resolved": "https://registry.npmjs.org/pbf/-/pbf-1.3.7.tgz" }, - "pbkdf2-compat": { - "version": "2.0.1", - "from": "pbkdf2-compat@2.0.1", - "resolved": "https://registry.npmjs.org/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz", - "dev": true - }, "permutation-parity": { "version": "1.0.0", "from": "permutation-parity@>=1.0.0 <2.0.0", @@ -4692,12 +2389,6 @@ "from": "permutation-rank@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/permutation-rank/-/permutation-rank-1.0.0.tgz" }, - "pify": { - "version": "2.3.0", - "from": "pify@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "dev": true - }, "pinkie": { "version": "2.0.4", "from": "pinkie@>=2.0.0 <3.0.0", @@ -4713,18 +2404,6 @@ "from": "pivottable@latest", "resolved": "https://registry.npmjs.org/pivottable/-/pivottable-2.3.0.tgz" }, - "pkg-dir": { - "version": "1.0.0", - "from": "pkg-dir@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", - "dev": true - }, - "pkg-up": { - "version": "1.0.0", - "from": "pkg-up@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-1.0.0.tgz", - "dev": true - }, "planar-dual": { "version": "1.0.2", "from": "planar-dual@>=1.0.0 <2.0.0", @@ -4740,12 +2419,6 @@ "from": "plotly.js@1.26.1", "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-1.26.1.tgz" }, - "pluralize": { - "version": "1.2.1", - "from": "pluralize@>=1.2.1 <2.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", - "dev": true - }, "pngjs": { "version": "2.3.1", "from": "pngjs@>=2.2.0 <3.0.0", @@ -4766,319 +2439,31 @@ "from": "polytope-closest-point@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/polytope-closest-point/-/polytope-closest-point-1.0.0.tgz" }, - "postcss": { - "version": "5.2.17", - "from": "postcss@>=5.0.6 <6.0.0", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", - "dev": true - }, - "postcss-calc": { - "version": "5.3.1", - "from": "postcss-calc@>=5.2.0 <6.0.0", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", - "dev": true - }, - "postcss-colormin": { - "version": "2.2.2", - "from": "postcss-colormin@>=2.1.8 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", - "dev": true - }, - "postcss-convert-values": { - "version": "2.6.1", - "from": "postcss-convert-values@>=2.3.4 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", - "dev": true - }, - "postcss-discard-comments": { - "version": "2.0.4", - "from": "postcss-discard-comments@>=2.0.4 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", - "dev": true - }, - "postcss-discard-duplicates": { - "version": "2.1.0", - "from": "postcss-discard-duplicates@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", - "dev": true - }, - "postcss-discard-empty": { - "version": "2.1.0", - "from": "postcss-discard-empty@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", - "dev": true - }, - "postcss-discard-overridden": { - "version": "0.1.1", - "from": "postcss-discard-overridden@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", - "dev": true - }, - "postcss-discard-unused": { - "version": "2.2.3", - "from": "postcss-discard-unused@>=2.2.1 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", - "dev": true - }, - "postcss-filter-plugins": { - "version": "2.0.2", - "from": "postcss-filter-plugins@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", - "dev": true - }, - "postcss-merge-idents": { - "version": "2.1.7", - "from": "postcss-merge-idents@>=2.1.5 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", - "dev": true - }, - "postcss-merge-longhand": { - "version": "2.0.2", - "from": "postcss-merge-longhand@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", - "dev": true - }, - "postcss-merge-rules": { - "version": "2.1.2", - "from": "postcss-merge-rules@>=2.0.3 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", - "dev": true - }, - "postcss-message-helpers": { - "version": "2.0.0", - "from": "postcss-message-helpers@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", - "dev": true - }, - "postcss-minify-font-values": { - "version": "1.0.5", - "from": "postcss-minify-font-values@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", - "dev": true - }, - "postcss-minify-gradients": { - "version": "1.0.5", - "from": "postcss-minify-gradients@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", - "dev": true - }, - "postcss-minify-params": { - "version": "1.2.2", - "from": "postcss-minify-params@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", - "dev": true - }, - "postcss-minify-selectors": { - "version": "2.1.1", - "from": "postcss-minify-selectors@>=2.0.4 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", - "dev": true - }, - "postcss-modules-extract-imports": { - "version": "1.0.1", - "from": "postcss-modules-extract-imports@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.1.tgz", - "dev": true - }, - "postcss-modules-local-by-default": { - "version": "1.1.1", - "from": "postcss-modules-local-by-default@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.1.1.tgz", - "dev": true - }, - "postcss-modules-scope": { - "version": "1.0.2", - "from": "postcss-modules-scope@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.0.2.tgz", - "dev": true - }, - "postcss-modules-values": { - "version": "1.2.2", - "from": "postcss-modules-values@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.2.2.tgz", - "dev": true - }, - "postcss-normalize-charset": { - "version": "1.1.1", - "from": "postcss-normalize-charset@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", - "dev": true - }, - "postcss-normalize-url": { - "version": "3.0.8", - "from": "postcss-normalize-url@>=3.0.7 <4.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", - "dev": true - }, - "postcss-ordered-values": { - "version": "2.2.3", - "from": "postcss-ordered-values@>=2.1.0 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", - "dev": true - }, - "postcss-reduce-idents": { - "version": "2.4.0", - "from": "postcss-reduce-idents@>=2.2.2 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", - "dev": true - }, - "postcss-reduce-initial": { - "version": "1.0.1", - "from": "postcss-reduce-initial@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", - "dev": true - }, - "postcss-reduce-transforms": { - "version": "1.0.4", - "from": "postcss-reduce-transforms@>=1.0.3 <2.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", - "dev": true - }, - "postcss-selector-parser": { - "version": "2.2.3", - "from": "postcss-selector-parser@>=2.2.2 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", - "dev": true - }, - "postcss-svgo": { - "version": "2.1.6", - "from": "postcss-svgo@>=2.1.1 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", - "dev": true - }, - "postcss-unique-selectors": { - "version": "2.0.2", - "from": "postcss-unique-selectors@>=2.0.2 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", - "dev": true - }, - "postcss-value-parser": { - "version": "3.3.0", - "from": "postcss-value-parser@>=3.2.3 <4.0.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", - "dev": true - }, - "postcss-zindex": { - "version": "2.2.0", - "from": "postcss-zindex@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", - "dev": true - }, "prelude-ls": { "version": "1.1.2", "from": "prelude-ls@>=1.1.2 <1.2.0", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" }, - "prepend-http": { - "version": "1.0.4", - "from": "prepend-http@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "dev": true - }, - "preserve": { - "version": "0.2.0", - "from": "preserve@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "dev": true - }, - "pretty-error": { - "version": "2.1.0", - "from": "pretty-error@>=2.0.2 <3.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.0.tgz", - "dev": true - }, - "private": { - "version": "0.1.7", - "from": "private@>=0.1.6 <0.2.0", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", - "dev": true - }, - "process": { - "version": "0.11.10", - "from": "process@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "dev": true - }, "process-nextick-args": { "version": "1.0.7", "from": "process-nextick-args@>=1.0.6 <1.1.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" }, - "progress": { - "version": "1.1.8", - "from": "progress@>=1.1.8 <2.0.0", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", - "dev": true - }, "protocol-buffers-schema": { "version": "2.2.0", "from": "protocol-buffers-schema@>=2.0.2 <3.0.0", "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-2.2.0.tgz" }, - "proxy-addr": { - "version": "1.1.4", - "from": "proxy-addr@>=1.1.3 <1.2.0", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.4.tgz", - "dev": true - }, - "prr": { - "version": "0.0.0", - "from": "prr@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "from": "pseudomap@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "dev": true - }, "punycode": { "version": "1.4.1", "from": "punycode@>=1.2.4 <2.0.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" }, - "q": { - "version": "1.5.0", - "from": "q@>=1.1.2 <2.0.0", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.0.tgz", - "dev": true - }, - "qs": { - "version": "6.4.0", - "from": "qs@6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "dev": true - }, "quat-slerp": { "version": "1.0.1", "from": "quat-slerp@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/quat-slerp/-/quat-slerp-1.0.1.tgz" }, - "query-string": { - "version": "4.3.4", - "from": "query-string@>=4.1.0 <5.0.0", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "dev": true - }, - "querystring": { - "version": "0.2.0", - "from": "querystring@0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "from": "querystring-es3@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "dev": true - }, - "querystringify": { - "version": "0.0.4", - "from": "querystringify@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-0.0.4.tgz", - "dev": true - }, "quickselect": { "version": "1.0.0", "from": "quickselect@>=1.0.0 <2.0.0", @@ -5089,11 +2474,21 @@ "from": "quote-stream@>=0.0.0 <0.1.0", "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-0.0.0.tgz", "dependencies": { + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, "object-keys": { "version": "0.4.0", "from": "object-keys@>=0.4.0 <0.5.0", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz" }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@~1.0.17", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" + }, "through2": { "version": "0.4.2", "from": "through2@>=0.4.1 <0.5.0", @@ -5106,214 +2501,31 @@ } } }, - "randomatic": { - "version": "1.1.6", - "from": "randomatic@>=1.1.3 <2.0.0", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.6.tgz", - "dev": true - }, - "range-parser": { - "version": "1.2.0", - "from": "range-parser@>=1.2.0 <1.3.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "dev": true - }, "rat-vec": { "version": "1.1.1", "from": "rat-vec@>=1.1.1 <2.0.0", "resolved": "https://registry.npmjs.org/rat-vec/-/rat-vec-1.1.1.tgz" }, - "raw-loader": { - "version": "0.5.1", - "from": "raw-loader@>=0.5.1 <0.6.0", - "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz", - "dev": true - }, - "read-pkg": { - "version": "1.1.0", - "from": "read-pkg@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "dev": true - }, - "read-pkg-up": { - "version": "1.0.1", - "from": "read-pkg-up@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "dev": true - }, "readable-stream": { - "version": "1.0.34", - "from": "readable-stream@>=1.0.33-1 <1.1.0-0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "dependencies": { - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - } - } - }, - "readdirp": { - "version": "2.1.0", - "from": "readdirp@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "dev": true, - "dependencies": { - "readable-stream": { - "version": "2.2.9", - "from": "readable-stream@>=2.0.2 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", - "dev": true - }, - "string_decoder": { - "version": "1.0.0", - "from": "string_decoder@~1.0.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz", - "dev": true - } - } - }, - "readline2": { - "version": "1.0.1", - "from": "readline2@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", - "dev": true - }, - "rechoir": { - "version": "0.6.2", - "from": "rechoir@>=0.6.2 <0.7.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "dev": true - }, - "redent": { - "version": "1.0.0", - "from": "redent@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "dev": true - }, - "redeyed": { - "version": "1.0.1", - "from": "redeyed@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-1.0.1.tgz", - "dev": true, - "dependencies": { - "esprima": { - "version": "3.0.0", - "from": "esprima@>=3.0.0 <3.1.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.0.0.tgz", - "dev": true - } - } - }, - "reduce-css-calc": { - "version": "1.3.0", - "from": "reduce-css-calc@>=1.2.6 <2.0.0", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", - "dev": true - }, - "reduce-function-call": { - "version": "1.0.2", - "from": "reduce-function-call@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", - "dev": true + "version": "2.1.5", + "from": "readable-stream@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz" }, "reduce-simplicial-complex": { "version": "1.0.0", "from": "reduce-simplicial-complex@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/reduce-simplicial-complex/-/reduce-simplicial-complex-1.0.0.tgz" }, - "regenerate": { - "version": "1.3.2", - "from": "regenerate@>=1.2.1 <2.0.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.2.tgz", - "dev": true - }, - "regenerator-runtime": { - "version": "0.10.5", - "from": "regenerator-runtime@>=0.10.0 <0.11.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "dev": true - }, - "regenerator-transform": { - "version": "0.9.11", - "from": "regenerator-transform@0.9.11", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.9.11.tgz", - "dev": true - }, - "regex-cache": { - "version": "0.4.3", - "from": "regex-cache@>=0.4.2 <0.5.0", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz", - "dev": true - }, - "regexpu-core": { - "version": "2.0.0", - "from": "regexpu-core@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", - "dev": true - }, - "regjsgen": { - "version": "0.2.0", - "from": "regjsgen@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "from": "regjsparser@>=0.1.4 <0.2.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "dev": true, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "from": "jsesc@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "dev": true - } - } - }, "regl": { "version": "1.3.0", "from": "regl@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/regl/-/regl-1.3.0.tgz" }, - "relateurl": { - "version": "0.2.7", - "from": "relateurl@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "dev": true - }, - "renderkid": { - "version": "2.0.1", - "from": "renderkid@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz", - "dev": true, - "dependencies": { - "utila": { - "version": "0.3.3", - "from": "utila@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", - "dev": true - } - } - }, - "repeat-element": { - "version": "1.1.2", - "from": "repeat-element@>=1.1.2 <2.0.0", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "dev": true - }, "repeat-string": { "version": "1.6.1", "from": "repeat-string@>=1.5.2 <2.0.0", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" }, - "repeating": { - "version": "2.0.1", - "from": "repeating@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "dev": true - }, "request": { "version": "2.79.0", "from": "request@>=2.39.0 <3.0.0", @@ -5331,41 +2543,11 @@ } } }, - "require-directory": { - "version": "2.1.1", - "from": "require-directory@>=2.1.1 <3.0.0", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "from": "require-main-filename@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "dev": true - }, - "require-uncached": { - "version": "1.0.3", - "from": "require-uncached@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "from": "requires-port@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "dev": true - }, "resolve": { "version": "1.1.7", "from": "resolve@>=1.1.6 <2.0.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz" }, - "resolve-from": { - "version": "1.0.1", - "from": "resolve-from@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "dev": true - }, "resolve-protobuf-schema": { "version": "2.0.0", "from": "resolve-protobuf-schema@>=2.0.0 <3.0.0", @@ -5376,12 +2558,6 @@ "from": "resolve-url@>=0.2.1 <0.3.0", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz" }, - "restore-cursor": { - "version": "1.0.1", - "from": "restore-cursor@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "dev": true - }, "resumer": { "version": "0.0.0", "from": "resumer@>=0.0.0 <0.1.0", @@ -5397,18 +2573,6 @@ "from": "right-now@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/right-now/-/right-now-1.0.0.tgz" }, - "rimraf": { - "version": "2.6.1", - "from": "rimraf@>=2.2.8 <3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "dev": true - }, - "ripemd160": { - "version": "0.2.0", - "from": "ripemd160@0.2.0", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-0.2.0.tgz", - "dev": true - }, "robust-compress": { "version": "1.0.0", "from": "robust-compress@>=1.0.0 <2.0.0", @@ -5464,170 +2628,16 @@ "from": "robust-sum@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/robust-sum/-/robust-sum-1.0.0.tgz" }, - "run-async": { - "version": "0.1.0", - "from": "run-async@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", - "dev": true - }, "rw": { "version": "0.1.4", "from": "rw@>=0.1.4 <0.2.0", "resolved": "https://registry.npmjs.org/rw/-/rw-0.1.4.tgz" }, - "rx-lite": { - "version": "3.1.2", - "from": "rx-lite@>=3.1.2 <4.0.0", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", - "dev": true - }, "sane-topojson": { "version": "2.0.0", "from": "sane-topojson@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/sane-topojson/-/sane-topojson-2.0.0.tgz" }, - "sass-graph": { - "version": "2.2.2", - "from": "sass-graph@>=2.1.1 <3.0.0", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.2.tgz", - "dev": true, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "from": "camelcase@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "dev": true - }, - "cliui": { - "version": "3.2.0", - "from": "cliui@>=3.2.0 <4.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "dev": true - }, - "yargs": { - "version": "6.6.0", - "from": "yargs@>=6.6.0 <7.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", - "dev": true - } - } - }, - "sass-loader": { - "version": "4.1.1", - "from": "sass-loader@>=4.1.1 <5.0.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-4.1.1.tgz", - "dev": true, - "dependencies": { - "async": { - "version": "2.2.0", - "from": "async@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.2.0.tgz", - "dev": true - } - } - }, - "sax": { - "version": "1.2.2", - "from": "sax@>=1.2.1 <1.3.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.2.tgz", - "dev": true - }, - "scss-tokenizer": { - "version": "0.2.1", - "from": "scss-tokenizer@>=0.2.1 <0.3.0", - "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.1.tgz", - "dev": true, - "dependencies": { - "source-map": { - "version": "0.4.4", - "from": "source-map@>=0.4.2 <0.5.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "dev": true - } - } - }, - "semver": { - "version": "5.3.0", - "from": "semver@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0||>=4.0.0 <5.0.0||>=5.0.0 <6.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "dev": true - }, - "send": { - "version": "0.15.1", - "from": "send@0.15.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.15.1.tgz", - "dev": true, - "dependencies": { - "debug": { - "version": "2.6.1", - "from": "debug@2.6.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.1.tgz", - "dev": true - }, - "ms": { - "version": "0.7.2", - "from": "ms@0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "dev": true - } - } - }, - "serve-index": { - "version": "1.8.0", - "from": "serve-index@>=1.7.2 <2.0.0", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.8.0.tgz", - "dev": true, - "dependencies": { - "http-errors": { - "version": "1.5.1", - "from": "http-errors@>=1.5.0 <1.6.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.5.1.tgz", - "dev": true - }, - "setprototypeof": { - "version": "1.0.2", - "from": "setprototypeof@1.0.2", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.2.tgz", - "dev": true - } - } - }, - "serve-static": { - "version": "1.12.1", - "from": "serve-static@1.12.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.1.tgz", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "from": "set-blocking@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "dev": true - }, - "set-immediate-shim": { - "version": "1.0.1", - "from": "set-immediate-shim@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "dev": true - }, - "setimmediate": { - "version": "1.0.5", - "from": "setimmediate@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "dev": true - }, - "setprototypeof": { - "version": "1.0.3", - "from": "setprototypeof@1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "dev": true - }, - "sha.js": { - "version": "2.2.6", - "from": "sha.js@2.2.6", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.2.6.tgz", - "dev": true - }, "shallow-copy": { "version": "0.0.1", "from": "shallow-copy@0.0.1", @@ -5638,24 +2648,6 @@ "from": "shelf-pack@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/shelf-pack/-/shelf-pack-1.1.0.tgz" }, - "shelljs": { - "version": "0.7.7", - "from": "shelljs@>=0.7.5 <0.8.0", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.7.tgz", - "dev": true - }, - "shellwords": { - "version": "0.1.0", - "from": "shellwords@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.0.tgz", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "from": "signal-exit@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "dev": true - }, "signum": { "version": "0.0.0", "from": "signum@>=0.0.0 <0.0.1", @@ -5713,18 +2705,6 @@ "from": "slab-decomposition@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/slab-decomposition/-/slab-decomposition-1.0.2.tgz" }, - "slash": { - "version": "1.0.0", - "from": "slash@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "dev": true - }, - "slice-ansi": { - "version": "0.0.4", - "from": "slice-ansi@0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "dev": true - }, "snap-points-2d": { "version": "1.0.1", "from": "snap-points-2d@>=1.0.1 <2.0.0", @@ -5735,26 +2715,6 @@ "from": "sntp@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" }, - "sockjs": { - "version": "0.3.18", - "from": "sockjs@>=0.3.15 <0.4.0", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.18.tgz", - "dev": true - }, - "sockjs-client": { - "version": "1.1.2", - "from": "sockjs-client@>=1.0.3 <2.0.0", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.2.tgz", - "dev": true, - "dependencies": { - "faye-websocket": { - "version": "0.11.1", - "from": "faye-websocket@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", - "dev": true - } - } - }, "sort-asc": { "version": "0.1.0", "from": "sort-asc@>=0.1.0 <0.2.0", @@ -5765,52 +2725,16 @@ "from": "sort-desc@>=0.1.1 <0.2.0", "resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.1.1.tgz" }, - "sort-keys": { - "version": "1.1.2", - "from": "sort-keys@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "dev": true - }, "sort-object": { "version": "0.3.2", "from": "sort-object@>=0.3.2 <0.4.0", "resolved": "https://registry.npmjs.org/sort-object/-/sort-object-0.3.2.tgz" }, - "source-list-map": { - "version": "0.1.8", - "from": "source-list-map@>=0.1.4 <0.2.0", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", - "dev": true - }, "source-map": { "version": "0.5.6", "from": "source-map@>=0.5.1 <0.6.0", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" }, - "source-map-support": { - "version": "0.4.15", - "from": "source-map-support@>=0.4.2 <0.5.0", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz", - "dev": true - }, - "spdx-correct": { - "version": "1.0.2", - "from": "spdx-correct@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", - "dev": true - }, - "spdx-expression-parse": { - "version": "1.0.4", - "from": "spdx-expression-parse@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", - "dev": true - }, - "spdx-license-ids": { - "version": "1.2.2", - "from": "spdx-license-ids@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", - "dev": true - }, "split-polygon": { "version": "1.0.0", "from": "split-polygon@>=1.0.0 <2.0.0", @@ -5865,6 +2789,11 @@ "from": "static-module@>=1.1.2 <2.0.0", "resolved": "https://registry.npmjs.org/static-module/-/static-module-1.3.1.tgz", "dependencies": { + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, "object-inspect": { "version": "0.4.0", "from": "object-inspect@>=0.4.0 <0.5.0", @@ -5875,6 +2804,11 @@ "from": "object-keys@>=0.4.0 <0.5.0", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz" }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@~1.0.27-1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" + }, "through2": { "version": "0.4.2", "from": "through2@>=0.4.1 <0.5.0", @@ -5887,101 +2821,11 @@ } } }, - "statuses": { - "version": "1.3.1", - "from": "statuses@>=1.3.1 <1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "dev": true - }, - "stdout-stream": { - "version": "1.4.0", - "from": "stdout-stream@>=1.4.0 <2.0.0", - "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.0.tgz", - "dev": true, - "dependencies": { - "readable-stream": { - "version": "2.2.9", - "from": "readable-stream@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", - "dev": true - }, - "string_decoder": { - "version": "1.0.0", - "from": "string_decoder@~1.0.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz", - "dev": true - } - } - }, - "stream-browserify": { - "version": "2.0.1", - "from": "stream-browserify@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "dev": true, - "dependencies": { - "readable-stream": { - "version": "2.2.9", - "from": "readable-stream@>=2.0.2 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", - "dev": true - }, - "string_decoder": { - "version": "1.0.0", - "from": "string_decoder@~1.0.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz", - "dev": true - } - } - }, - "stream-cache": { - "version": "0.0.2", - "from": "stream-cache@>=0.0.1 <0.1.0", - "resolved": "https://registry.npmjs.org/stream-cache/-/stream-cache-0.0.2.tgz", - "dev": true - }, - "stream-http": { - "version": "2.7.0", - "from": "stream-http@>=2.3.1 <3.0.0", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.0.tgz", - "dev": true, - "dependencies": { - "readable-stream": { - "version": "2.2.9", - "from": "readable-stream@>=2.2.6 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", - "dev": true - }, - "string_decoder": { - "version": "1.0.0", - "from": "string_decoder@~1.0.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz", - "dev": true - } - } - }, - "strict-uri-encode": { - "version": "1.1.0", - "from": "strict-uri-encode@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "dev": true - }, "string_decoder": { "version": "0.10.31", "from": "string_decoder@>=0.10.0 <0.11.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, - "string-width": { - "version": "1.0.2", - "from": "string-width@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "dev": true - }, - "string.prototype.codepointat": { - "version": "0.2.0", - "from": "string.prototype.codepointat@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.0.tgz", - "dev": true - }, "string.prototype.trim": { "version": "1.1.2", "from": "string.prototype.trim@>=1.1.2 <1.2.0", @@ -6007,24 +2851,6 @@ "from": "strip-ansi@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" }, - "strip-bom": { - "version": "3.0.0", - "from": "strip-bom@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "dev": true - }, - "strip-indent": { - "version": "1.0.1", - "from": "strip-indent@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "from": "strip-json-comments@>=2.0.1 <2.1.0", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "dev": true - }, "supercluster": { "version": "2.3.0", "from": "supercluster@>=2.0.1 <3.0.0", @@ -6035,57 +2861,11 @@ "from": "superscript-text@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/superscript-text/-/superscript-text-1.0.0.tgz" }, - "supports-color": { - "version": "3.2.3", - "from": "supports-color@>=3.2.3 <4.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "dev": true - }, "surface-nets": { "version": "1.0.2", "from": "surface-nets@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/surface-nets/-/surface-nets-1.0.2.tgz" }, - "svgo": { - "version": "0.7.2", - "from": "svgo@>=0.7.0 <0.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", - "dev": true, - "dependencies": { - "colors": { - "version": "1.1.2", - "from": "colors@>=1.1.2 <1.2.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "dev": true - } - } - }, - "table": { - "version": "3.8.3", - "from": "table@>=3.7.8 <4.0.0", - "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": { - "version": "2.0.0", - "from": "is-fullwidth-code-point@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "dev": true - }, - "string-width": { - "version": "2.0.0", - "from": "string-width@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.0.0.tgz", - "dev": true - } - } - }, - "tapable": { - "version": "0.1.10", - "from": "tapable@>=0.1.8 <0.2.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", - "dev": true - }, "tape": { "version": "4.6.3", "from": "tape@>=4.0.0 <5.0.0", @@ -6098,23 +2878,11 @@ } } }, - "tar": { - "version": "2.2.1", - "from": "tar@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "dev": true - }, "text-cache": { "version": "4.1.0", "from": "text-cache@>=4.1.0 <5.0.0", "resolved": "https://registry.npmjs.org/text-cache/-/text-cache-4.1.0.tgz" }, - "text-table": { - "version": "0.2.0", - "from": "text-table@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "dev": true - }, "through": { "version": "2.3.8", "from": "through@>=2.3.6 <3.0.0", @@ -6123,13 +2891,19 @@ "through2": { "version": "0.6.5", "from": "through2@>=0.6.3 <0.7.0", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz" - }, - "timers-browserify": { - "version": "2.0.2", - "from": "timers-browserify@>=2.0.2 <3.0.0", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.2.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "dependencies": { + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@>=1.0.33-1 <1.1.0-0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" + } + } }, "tiny-sdf": { "version": "1.0.2", @@ -6141,18 +2915,6 @@ "from": "tinycolor2@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz" }, - "to-arraybuffer": { - "version": "1.0.1", - "from": "to-arraybuffer@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "dev": true - }, - "to-fast-properties": { - "version": "1.0.3", - "from": "to-fast-properties@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "dev": true - }, "to-px": { "version": "1.0.1", "from": "to-px@>=1.0.1 <2.0.0", @@ -6168,12 +2930,6 @@ "from": "topojson-client@>=2.1.0 <3.0.0", "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-2.1.0.tgz" }, - "toposort": { - "version": "1.0.3", - "from": "toposort@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.3.tgz", - "dev": true - }, "tough-cookie": { "version": "2.3.2", "from": "tough-cookie@>=2.3.0 <2.4.0", @@ -6189,35 +2945,11 @@ "from": "triangulate-polyline@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/triangulate-polyline/-/triangulate-polyline-1.0.3.tgz" }, - "trim-newlines": { - "version": "1.0.0", - "from": "trim-newlines@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "dev": true - }, - "trim-right": { - "version": "1.0.1", - "from": "trim-right@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "dev": true - }, - "tryit": { - "version": "1.0.3", - "from": "tryit@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", - "dev": true - }, "tryor": { "version": "0.1.2", "from": "tryor@>=0.1.2 <0.2.0", "resolved": "https://registry.npmjs.org/tryor/-/tryor-0.1.2.tgz" }, - "tty-browserify": { - "version": "0.0.0", - "from": "tty-browserify@0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "dev": true - }, "tunnel-agent": { "version": "0.4.3", "from": "tunnel-agent@>=0.4.1 <0.5.0", @@ -6249,26 +2981,6 @@ "from": "type-check@>=0.3.2 <0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" }, - "type-is": { - "version": "1.6.15", - "from": "type-is@>=1.6.14 <1.7.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", - "dev": true, - "dependencies": { - "mime-db": { - "version": "1.27.0", - "from": "mime-db@~1.27.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", - "dev": true - }, - "mime-types": { - "version": "2.1.15", - "from": "mime-types@>=2.1.15 <2.2.0", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", - "dev": true - } - } - }, "typedarray": { "version": "0.0.6", "from": "typedarray@>=0.0.5 <0.1.0", @@ -6306,11 +3018,6 @@ "from": "unassert@>=1.3.1 <2.0.0", "resolved": "https://registry.npmjs.org/unassert/-/unassert-1.5.1.tgz", "dependencies": { - "acorn": { - "version": "4.0.11", - "from": "acorn@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.11.tgz" - }, "estraverse": { "version": "4.2.0", "from": "estraverse@>=4.1.0 <5.0.0", @@ -6323,21 +3030,11 @@ "from": "unassertify@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/unassertify/-/unassertify-2.0.4.tgz", "dependencies": { - "acorn": { - "version": "4.0.11", - "from": "acorn@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.11.tgz" - }, "escodegen": { "version": "1.8.1", "from": "escodegen@>=1.6.1 <2.0.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz" }, - "esprima": { - "version": "2.7.3", - "from": "esprima@>=2.7.1 <3.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz" - }, "estraverse": { "version": "1.9.3", "from": "estraverse@>=1.9.1 <2.0.0", @@ -6371,124 +3068,16 @@ "from": "uniq@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz" }, - "uniqid": { - "version": "4.1.1", - "from": "uniqid@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", - "dev": true - }, - "uniqs": { - "version": "2.0.0", - "from": "uniqs@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "dev": true - }, "unitbezier": { "version": "0.0.0", "from": "unitbezier@>=0.0.0 <0.0.1", "resolved": "https://registry.npmjs.org/unitbezier/-/unitbezier-0.0.0.tgz" }, - "unpipe": { - "version": "1.0.0", - "from": "unpipe@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "dev": true - }, - "upper-case": { - "version": "1.1.3", - "from": "upper-case@>=1.1.1 <2.0.0", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "dev": true - }, - "url": { - "version": "0.11.0", - "from": "url@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "dev": true, - "dependencies": { - "punycode": { - "version": "1.3.2", - "from": "punycode@1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "dev": true - } - } - }, - "url-loader": { - "version": "0.5.8", - "from": "url-loader@>=0.5.7 <0.6.0", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.5.8.tgz", - "dev": true, - "dependencies": { - "loader-utils": { - "version": "1.1.0", - "from": "loader-utils@^1.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "dev": true - } - } - }, - "url-parse": { - "version": "1.1.8", - "from": "url-parse@>=1.1.1 <2.0.0", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.1.8.tgz", - "dev": true - }, - "user-home": { - "version": "2.0.0", - "from": "user-home@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", - "dev": true - }, - "util": { - "version": "0.10.3", - "from": "util@>=0.10.3 <0.11.0", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "dev": true, - "dependencies": { - "inherits": { - "version": "2.0.1", - "from": "inherits@2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "dev": true - } - } - }, "util-deprecate": { "version": "1.0.2", "from": "util-deprecate@>=1.0.1 <1.1.0", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" }, - "utila": { - "version": "0.4.0", - "from": "utila@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "dev": true - }, - "utils-merge": { - "version": "1.0.0", - "from": "utils-merge@1.0.0", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", - "dev": true - }, - "uuid": { - "version": "2.0.3", - "from": "uuid@>=2.0.2 <3.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.1", - "from": "validate-npm-package-license@>=3.0.1 <4.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", - "dev": true - }, - "vary": { - "version": "1.1.1", - "from": "vary@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz", - "dev": true - }, "vector-tile": { "version": "1.3.0", "from": "vector-tile@>=1.3.0 <2.0.0", @@ -6499,23 +3088,11 @@ "from": "vectorize-text@>=3.0.1 <4.0.0", "resolved": "https://registry.npmjs.org/vectorize-text/-/vectorize-text-3.0.2.tgz" }, - "vendors": { - "version": "1.0.1", - "from": "vendors@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.1.tgz", - "dev": true - }, "verror": { "version": "1.3.6", "from": "verror@1.3.6", "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" }, - "vm-browserify": { - "version": "0.0.4", - "from": "vm-browserify@0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "dev": true - }, "vt-pbf": { "version": "2.1.2", "from": "vt-pbf@>=2.0.2 <3.0.0", @@ -6526,20 +3103,6 @@ "from": "w3c-blob@0.0.1", "resolved": "https://registry.npmjs.org/w3c-blob/-/w3c-blob-0.0.1.tgz" }, - "watchpack": { - "version": "0.2.9", - "from": "watchpack@>=0.2.1 <0.3.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-0.2.9.tgz", - "dev": true, - "dependencies": { - "async": { - "version": "0.9.2", - "from": "async@>=0.9.0 <0.10.0", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "dev": true - } - } - }, "weak-map": { "version": "1.0.5", "from": "weak-map@>=1.0.5 <2.0.0", @@ -6555,96 +3118,6 @@ "from": "webgl-context@>=2.2.0 <3.0.0", "resolved": "https://registry.npmjs.org/webgl-context/-/webgl-context-2.2.0.tgz" }, - "webpack": { - "version": "1.14.0", - "from": "webpack@>=1.13.3 <2.0.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-1.14.0.tgz", - "dev": true, - "dependencies": { - "acorn": { - "version": "3.3.0", - "from": "acorn@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "dev": true - }, - "interpret": { - "version": "0.6.6", - "from": "interpret@>=0.6.4 <0.7.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-0.6.6.tgz", - "dev": true - } - } - }, - "webpack-build-notifier": { - "version": "0.1.13", - "from": "webpack-build-notifier@>=0.1.13 <0.2.0", - "resolved": "https://registry.npmjs.org/webpack-build-notifier/-/webpack-build-notifier-0.1.13.tgz", - "dev": true - }, - "webpack-core": { - "version": "0.6.9", - "from": "webpack-core@>=0.6.9 <0.7.0", - "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", - "dev": true, - "dependencies": { - "source-map": { - "version": "0.4.4", - "from": "source-map@>=0.4.1 <0.5.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "dev": true - } - } - }, - "webpack-dev-middleware": { - "version": "1.10.2", - "from": "webpack-dev-middleware@>=1.4.0 <2.0.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.10.2.tgz", - "dev": true, - "dependencies": { - "memory-fs": { - "version": "0.4.1", - "from": "memory-fs@>=0.4.1 <0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "dev": true - }, - "readable-stream": { - "version": "2.2.9", - "from": "readable-stream@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", - "dev": true - }, - "string_decoder": { - "version": "1.0.0", - "from": "string_decoder@~1.0.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz", - "dev": true - } - } - }, - "webpack-dev-server": { - "version": "1.16.3", - "from": "webpack-dev-server@>=1.16.2 <2.0.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-1.16.3.tgz", - "dev": true - }, - "webpack-sources": { - "version": "0.1.5", - "from": "webpack-sources@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.1.5.tgz", - "dev": true - }, - "websocket-driver": { - "version": "0.6.5", - "from": "websocket-driver@>=0.5.1", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", - "dev": true - }, - "websocket-extensions": { - "version": "0.1.1", - "from": "websocket-extensions@>=0.1.1", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.1.tgz", - "dev": true - }, "webworkify": { "version": "1.4.0", "from": "webworkify@>=1.3.0 <2.0.0", @@ -6655,35 +3128,11 @@ "from": "wgs84@0.0.0", "resolved": "https://registry.npmjs.org/wgs84/-/wgs84-0.0.0.tgz" }, - "whet.extend": { - "version": "0.9.9", - "from": "whet.extend@>=0.9.9 <0.10.0", - "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", - "dev": true - }, - "which": { - "version": "1.2.14", - "from": "which@>=1.2.9 <2.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", - "dev": true - }, - "which-module": { - "version": "1.0.0", - "from": "which-module@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "dev": true - }, "whoots-js": { "version": "2.1.0", "from": "whoots-js@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/whoots-js/-/whoots-js-2.1.0.tgz" }, - "wide-align": { - "version": "1.1.0", - "from": "wide-align@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz", - "dev": true - }, "window-size": { "version": "0.1.0", "from": "window-size@0.1.0", @@ -6699,65 +3148,21 @@ "from": "world-calendars@>=1.0.3 <2.0.0", "resolved": "https://registry.npmjs.org/world-calendars/-/world-calendars-1.0.3.tgz" }, - "wrap-ansi": { - "version": "2.1.0", - "from": "wrap-ansi@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "dev": true - }, "wrappy": { "version": "1.0.2", "from": "wrappy@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" }, - "write": { - "version": "0.2.1", - "from": "write@>=0.2.1 <0.3.0", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "dev": true - }, - "xml-char-classes": { - "version": "1.0.0", - "from": "xml-char-classes@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/xml-char-classes/-/xml-char-classes-1.0.0.tgz", - "dev": true - }, "xtend": { "version": "4.0.1", "from": "xtend@>=4.0.0 <5.0.0", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" }, - "y18n": { - "version": "3.2.1", - "from": "y18n@>=3.2.1 <4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "from": "yallist@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "dev": true - }, "yargs": { "version": "3.10.0", "from": "yargs@>=3.10.0 <3.11.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz" }, - "yargs-parser": { - "version": "4.2.1", - "from": "yargs-parser@>=4.2.0 <5.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", - "dev": true, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "from": "camelcase@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "dev": true - } - } - }, "zero-crossings": { "version": "1.0.1", "from": "zero-crossings@>=1.0.0 <2.0.0", From 79187cd29a57ba4c5c90e9e0c67aa61b966540f8 Mon Sep 17 00:00:00 2001 From: Alexander Leibzon Date: Tue, 2 May 2017 23:36:11 +0300 Subject: [PATCH 038/243] get_schema fix --- redash/query_runner/memsql_ds.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/redash/query_runner/memsql_ds.py b/redash/query_runner/memsql_ds.py index 14e7f4d119..ed2dfdad3f 100644 --- a/redash/query_runner/memsql_ds.py +++ b/redash/query_runner/memsql_ds.py @@ -39,6 +39,8 @@ class MemSQL(BaseSQLQueryRunner): + noop_query = 'SELECT 1' + @classmethod def configuration_schema(cls): return { @@ -86,6 +88,7 @@ def _get_tables(self, schema): for schema_name in filter(lambda a: len(a) > 0, map(lambda a: str(a['Database']), self._run_query_internal(schemas_query))): for table_name in filter(lambda a: len(a) > 0, map(lambda a: str(a['Tables_in_%s' % schema_name]), self._run_query_internal(tables_query % schema_name))): + table_name = '.'.join((schema_name, table_name)) columns = filter(lambda a: len(a) > 0, map(lambda a: str(a['Field']), self._run_query_internal(columns_query % table_name))) schema[table_name] = {'name': table_name, 'columns': columns} @@ -93,7 +96,7 @@ def _get_tables(self, schema): raise sys.exc_info()[1], None, sys.exc_info()[2] return schema.values() - def run_query(self, query): + def run_query(self, query, user): cursor = None try: @@ -121,12 +124,14 @@ def run_query(self, query): #==================================================================================================== columns = [] column_names = rows[0].keys() if rows else None - for column in column_names: - columns.append({ - 'name': column, - 'friendly_name': column, - 'type': None - }) + + if column_names: + for column in column_names: + columns.append({ + 'name': column, + 'friendly_name': column, + 'type': None + }) data = {'columns': columns, 'rows': rows} json_data = json.dumps(data, cls=JSONEncoder) From b9f8b6cdbf4fa1cc49865764d2e5b9007e64f885 Mon Sep 17 00:00:00 2001 From: Alexander Leibzon Date: Tue, 2 May 2017 23:49:09 +0300 Subject: [PATCH 039/243] reformat, as for pep-8 --- redash/query_runner/memsql_ds.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/redash/query_runner/memsql_ds.py b/redash/query_runner/memsql_ds.py index ed2dfdad3f..ddba85a320 100644 --- a/redash/query_runner/memsql_ds.py +++ b/redash/query_runner/memsql_ds.py @@ -9,6 +9,7 @@ try: from memsql.common import database + enabled = True except ImportError, e: logger.warning(e) @@ -86,10 +87,14 @@ def _get_tables(self, schema): columns_query = "show columns in %s" - for schema_name in filter(lambda a: len(a) > 0, map(lambda a: str(a['Database']), self._run_query_internal(schemas_query))): - for table_name in filter(lambda a: len(a) > 0, map(lambda a: str(a['Tables_in_%s' % schema_name]), self._run_query_internal(tables_query % schema_name))): + for schema_name in filter(lambda a: len(a) > 0, + map(lambda a: str(a['Database']), self._run_query_internal(schemas_query))): + for table_name in filter(lambda a: len(a) > 0, map(lambda a: str(a['Tables_in_%s' % schema_name]), + self._run_query_internal( + tables_query % schema_name))): table_name = '.'.join((schema_name, table_name)) - columns = filter(lambda a: len(a) > 0, map(lambda a: str(a['Field']), self._run_query_internal(columns_query % table_name))) + columns = filter(lambda a: len(a) > 0, map(lambda a: str(a['Field']), + self._run_query_internal(columns_query % table_name))) schema[table_name] = {'name': table_name, 'columns': columns} except Exception, e: @@ -118,10 +123,9 @@ def run_query(self, query, user): rows = [dict(zip(list(row.keys()), list(row.values()))) for row in res] - - #==================================================================================================== - #temporary - until https://github.com/memsql/memsql-python/pull/8 gets merged - #==================================================================================================== + # ==================================================================================================== + # temporary - until https://github.com/memsql/memsql-python/pull/8 gets merged + # ==================================================================================================== columns = [] column_names = rows[0].keys() if rows else None @@ -149,4 +153,5 @@ def run_query(self, query, user): return json_data, error + register(MemSQL) From 48322856d9fa1982429744be8c3e3b658f3e2ce8 Mon Sep 17 00:00:00 2001 From: Vladislav Denisov Date: Wed, 3 May 2017 08:52:51 +0300 Subject: [PATCH 040/243] jql: maxResults fix --- redash/query_runner/jql.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/redash/query_runner/jql.py b/redash/query_runner/jql.py index 599e6993c4..c20710e512 100644 --- a/redash/query_runner/jql.py +++ b/redash/query_runner/jql.py @@ -54,7 +54,7 @@ def parse_issue(issue, field_mapping): if 'watchCount' in v: result[output_name] = v['watchCount'] - + elif isinstance(v, list): if len(member_names) > 0: # if field mapping with dict member mappings defined get value of each member @@ -104,7 +104,7 @@ def __init__(cls, query_field_mapping): for k, v in query_field_mapping.iteritems(): field_name = k member_name = None - + # check for member name contained in field name member_parser = re.search('(\w+)\.(\w+)', k) if (member_parser): @@ -181,10 +181,10 @@ def run_query(self, query, user): field_mapping = FieldMapping(query.pop('fieldMapping', {})) if query_type == 'count': - query['maxResults'] = 1 + query['maxResults'] = query.get('maxResults', 1) query['fields'] = '' else: - query['maxResults'] = 1000 + query['maxResults'] = query.get('maxResults', 1000) response = requests.get(jql_url, params=query, auth=(self.configuration.get('username'), self.configuration.get('password'))) From a1a0d766fe1d56d2388f483cf79a9c970782793f Mon Sep 17 00:00:00 2001 From: Yohei Susa Date: Wed, 3 May 2017 22:17:59 +0900 Subject: [PATCH 041/243] Add environment variable for switching query annotations of Athena to disable --- redash/query_runner/athena.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index eda3c5ef98..23339446a1 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -4,8 +4,10 @@ import requests from redash.query_runner import BaseQueryRunner, register +from redash.settings import parse_boolean PROXY_URL = os.environ.get('ATHENA_PROXY_URL') +ANNOTATE_QUERY = parse_boolean(os.environ.get('ATHENA_ANNOTATE_QUERY', 'true')) class Athena(BaseQueryRunner): noop_query = 'SELECT 1' @@ -42,7 +44,7 @@ def configuration_schema(cls): @classmethod def annotate_query(cls): - return False + return ANNOTATE_QUERY def get_schema(self, get_stats=False): schema = {} From b9144a9d7a33b199498d96e3a7f1f489b79a4984 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Wed, 3 May 2017 23:53:23 +0300 Subject: [PATCH 042/243] WIP: support for MULTI_ORG mode (#1447) --- client/app/multi_org.html | 19 ++++++++++++ client/app/services/auth.js | 4 +-- redash/__init__.py | 13 +++++---- redash/handlers/authentication.py | 12 ++++---- redash/handlers/setup.py | 48 +++++++++++++++++-------------- redash/handlers/static.py | 15 +++++++--- webpack.config.js | 6 +++- 7 files changed, 77 insertions(+), 40 deletions(-) create mode 100644 client/app/multi_org.html diff --git a/client/app/multi_org.html b/client/app/multi_org.html new file mode 100644 index 0000000000..44673f7a45 --- /dev/null +++ b/client/app/multi_org.html @@ -0,0 +1,19 @@ + + + + + + + Redash + + + + + + + +
+
+
+ + diff --git a/client/app/services/auth.js b/client/app/services/auth.js index 0008f34787..074a7419aa 100644 --- a/client/app/services/auth.js +++ b/client/app/services/auth.js @@ -31,12 +31,12 @@ function AuthService($window, $location, $q, $http) { login() { const next = encodeURI($location.url()); logger('Calling login with next = %s', next); - window.location.href = `/login?next=${next}`; + window.location.href = `login?next=${next}`; }, logout() { logger('Logout.'); window.sessionStorage.removeItem(SESSION_ITEM); - $window.location.href = '/logout'; + $window.location.href = 'logout'; }, loadSession() { logger('Loading session'); diff --git a/redash/__init__.py b/redash/__init__.py index abaedcdd06..c2ed2e7b7c 100644 --- a/redash/__init__.py +++ b/redash/__init__.py @@ -1,7 +1,8 @@ +import os import logging import urlparse import redis -from flask import Flask +from flask import Flask, safe_join from flask_sslify import SSLify from werkzeug.contrib.fixers import ProxyFix from werkzeug.routing import BaseConverter, ValidationError @@ -72,9 +73,11 @@ def create_redis_connection(): class SlugConverter(BaseConverter): def to_python(self, value): - # This is an ugly workaround for when we enable multi-org and some files are being called by the index rule: - if value in ('google_login.png', 'favicon.ico', 'robots.txt', 'views'): - raise ValidationError() + # This is ay workaround for when we enable multi-org and some files are being called by the index rule: + for path in settings.STATIC_ASSETS_PATHS: + full_path = safe_join(path, value) + if os.path.isfile(full_path): + raise ValidationError() return value @@ -90,7 +93,7 @@ def create_app(load_admin=True): from redash.metrics.request import provision_app app = Flask(__name__, - template_folder=settings.STATIC_ASSETS_PATHS[-1], + template_folder=settings.STATIC_ASSETS_PATHS[0], static_folder=settings.STATIC_ASSETS_PATHS[-1], static_path='/static') diff --git a/redash/handlers/authentication.py b/redash/handlers/authentication.py index 032d582d20..d95d268bda 100644 --- a/redash/handlers/authentication.py +++ b/redash/handlers/authentication.py @@ -2,10 +2,8 @@ import logging from flask import flash, redirect, render_template, request, url_for -from flask_login import current_user, login_required, login_user, logout_user - -from sqlalchemy.orm.exc import NoResultFound +from flask_login import current_user, login_required, login_user, logout_user from redash import __version__, limiter, models, settings from redash.authentication import current_org, get_login_url from redash.authentication.account import (BadSignature, SignatureExpired, @@ -14,6 +12,7 @@ from redash.handlers import routes from redash.handlers.base import json_response, org_scoped_rule from redash.version_check import get_latest_version +from sqlalchemy.orm.exc import NoResultFound logger = logging.getLogger(__name__) @@ -94,9 +93,9 @@ def forgot_password(org_slug=None): def login(org_slug=None): # We intentionally use == as otherwise it won't actually use the proxy. So weird :O # noinspection PyComparisonWithNone - if current_org == None and not settings.MULTI_ORG: + if current_org is None and not settings.MULTI_ORG: return redirect('/setup') - elif current_org == None: + elif current_org is None: return redirect('/') index_url = url_for("redash.index", org_slug=org_slug) @@ -177,7 +176,8 @@ def config(org_slug=None): }) -@routes.route(org_scoped_rule('/api/session'), methods=['GET']) +# @routes.route(org_scoped_rule('/api/session'), methods=['GET']) +@routes.route('/api/session', methods=['GET']) @login_required def session(org_slug=None): if not isinstance(current_user._get_current_object(), models.ApiUser): diff --git a/redash/handlers/setup.py b/redash/handlers/setup.py index 65018e9e8f..086541acb2 100644 --- a/redash/handlers/setup.py +++ b/redash/handlers/setup.py @@ -1,13 +1,13 @@ -from flask import redirect, request, render_template, url_for, g -from flask_login import login_user -from wtforms import Form, PasswordField, StringField, BooleanField, validators -from wtforms.fields.html5 import EmailField +from flask import g, redirect, render_template, request, url_for +from flask_login import login_user from redash import settings -from redash.tasks.general import subscribe -from redash.handlers.base import routes -from redash.models import Organization, Group, User, db from redash.authentication.org_resolving import current_org +from redash.handlers.base import routes +from redash.models import Group, Organization, User, db +from redash.tasks.general import subscribe +from wtforms import BooleanField, Form, PasswordField, StringField, validators +from wtforms.fields.html5 import EmailField class SetupForm(Form): @@ -19,9 +19,26 @@ class SetupForm(Form): newsletter = BooleanField() +def create_org(org_name, user_name, email, password): + default_org = Organization(name=org_name, slug='default', settings={}) + admin_group = Group(name='admin', permissions=['admin', 'super_admin'], org=default_org, type=Group.BUILTIN_GROUP) + default_group = Group(name='default', permissions=Group.DEFAULT_PERMISSIONS, org=default_org, type=Group.BUILTIN_GROUP) + + db.session.add_all([default_org, admin_group, default_group]) + db.session.commit() + + user = User(org=default_org, name=user_name, email=email, group_ids=[admin_group.id, default_group.id]) + user.hash_password(password) + + db.session.add(user) + db.session.commit() + + return default_org, user + + @routes.route('/setup', methods=['GET', 'POST']) def setup(): - if current_org != None or settings.MULTI_ORG: + if current_org is not None or settings.MULTI_ORG: return redirect('/') form = SetupForm(request.form) @@ -29,18 +46,7 @@ def setup(): form.security_notifications.data = True if request.method == 'POST' and form.validate(): - default_org = Organization(name=form.org_name.data, slug='default', settings={}) - admin_group = Group(name='admin', permissions=['admin', 'super_admin'], org=default_org, type=Group.BUILTIN_GROUP) - default_group = Group(name='default', permissions=Group.DEFAULT_PERMISSIONS, org=default_org, type=Group.BUILTIN_GROUP) - - db.session.add_all([default_org, admin_group, default_group]) - db.session.commit() - - user = User(org=default_org, name=form.name.data, email=form.email.data, group_ids=[admin_group.id, default_group.id]) - user.hash_password(form.password.data) - - db.session.add(user) - db.session.commit() + default_org, user = create_org(form.org_name.data, form.name.data, form.email.data, form.password.data) g.org = default_org login_user(user) @@ -52,5 +58,3 @@ def setup(): return redirect(url_for('redash.index', org_slug=None)) return render_template('setup.html', form=form) - - diff --git a/redash/handlers/static.py b/redash/handlers/static.py index a2997c1c53..c2fba907fb 100644 --- a/redash/handlers/static.py +++ b/redash/handlers/static.py @@ -1,11 +1,13 @@ import os -from flask import current_app, safe_join, send_file +from flask import current_app, render_template, safe_join, send_file +from werkzeug.exceptions import NotFound + from flask_login import login_required from redash import settings from redash.handlers import routes +from redash.handlers.authentication import base_href from redash.handlers.base import org_scoped_rule -from werkzeug.exceptions import NotFound @routes.route('/') @@ -26,8 +28,13 @@ def send_static(filename): @login_required def index(**kwargs): - full_path = safe_join(settings.STATIC_ASSETS_PATHS[-2], 'index.html') - return send_file(full_path, **dict(cache_timeout=0, conditional=True)) + if settings.MULTI_ORG: + response = render_template("multi_org.html", base_href=base_href()) + else: + full_path = safe_join(settings.STATIC_ASSETS_PATHS[-2], 'index.html') + response = send_file(full_path, **dict(cache_timeout=0, conditional=True)) + + return response def register_static_routes(rules): diff --git a/webpack.config.js b/webpack.config.js index d82051c065..085f66ad5f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -16,6 +16,7 @@ var config = { // path: process.env.NODE_ENV === 'production' ? './dist' : './dev', path: './client/dist', filename: '[name].[chunkhash].js', + publicPath: '/' }, plugins: [ @@ -44,9 +45,12 @@ var config = { chunks: ['vendor'] }), new HtmlWebpackPlugin({ - // template: __dirname + '/app/' + 'index.html' template: './client/app/index.html' }), + new HtmlWebpackPlugin({ + template: './client/app/multi_org.html', + filename: 'multi_org.html' + }), new ExtractTextPlugin('styles.[chunkhash].css') ], From 6dde3170ab0027435da90dec01214b6628e80e8c Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 4 May 2017 10:39:32 +0300 Subject: [PATCH 043/243] Make embed & shared dashboard routes use mutli_org template --- redash/handlers/authentication.py | 3 ++- redash/handlers/embed.py | 29 +++++++++++------------------ redash/handlers/static.py | 8 ++++++-- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/redash/handlers/authentication.py b/redash/handlers/authentication.py index d95d268bda..9ff648b070 100644 --- a/redash/handlers/authentication.py +++ b/redash/handlers/authentication.py @@ -168,7 +168,8 @@ def client_config(): return client_config -@routes.route(org_scoped_rule('/api/config'), methods=['GET']) +# @routes.route(org_scoped_rule('/api/config'), methods=['GET']) +@routes.route('/api/config', methods=['GET']) def config(org_slug=None): return json_response({ 'org_slug': current_org.slug, diff --git a/redash/handlers/embed.py b/redash/handlers/embed.py index 1305a80918..78cb849618 100644 --- a/redash/handlers/embed.py +++ b/redash/handlers/embed.py @@ -1,21 +1,18 @@ -import json import logging import time import pystache +from flask import request + from authentication import current_org -from flask import current_app, render_template, request, safe_join, send_file from flask_login import current_user, login_required from flask_restful import abort -from funcy import project -from redash import models, serializers, settings, utils +from redash import models, utils from redash.handlers import routes -from redash.handlers.base import (get_object_or_404, org_scoped_rule, - record_event) +from redash.handlers.base import org_scoped_rule, record_event from redash.handlers.query_results import collect_query_parameters -from redash.permissions import require_access, view_only -from redash.utils import (collect_parameters_from_request, gen_query_hash, - json_dumps) +from redash.handlers.static import render_index +from redash.utils import gen_query_hash # @@ -54,12 +51,12 @@ def run_query_sync(data_source, parameter_values, query_text, max_age=0): if max_age > 0: run_time = time.time() - started_at query_result, updated_query_ids = models.QueryResult.store_result(data_source.org_id, data_source.id, - query_hash, query_text, data, - run_time, utils.utcnow()) + query_hash, query_text, data, + run_time, utils.utcnow()) models.db.session.commit() return data - except Exception, e: + except Exception: if max_age > 0: abort(404, message="Unable to get result from the database, and no cached query result found.") else: @@ -79,9 +76,7 @@ def embed(query_id, visualization_id, org_slug=None): 'referer': request.headers.get('Referer') }) - full_path = safe_join(settings.STATIC_ASSETS_PATHS[-2], 'index.html') - models.db.session.commit() - return send_file(full_path, **dict(cache_timeout=0, conditional=True)) + return render_index() @routes.route(org_scoped_rule('/public/dashboards/'), methods=['GET']) @@ -96,6 +91,4 @@ def public_dashboard(token, org_slug=None): # 'headless': 'embed' in request.args, # 'referer': request.headers.get('Referer') # }) - # models.db.session.commit() - full_path = safe_join(settings.STATIC_ASSETS_PATHS[-2], 'index.html') - return send_file(full_path, **dict(cache_timeout=0, conditional=True)) + return render_index() diff --git a/redash/handlers/static.py b/redash/handlers/static.py index c2fba907fb..98dcb5e6b2 100644 --- a/redash/handlers/static.py +++ b/redash/handlers/static.py @@ -26,8 +26,7 @@ def send_static(filename): raise NotFound() -@login_required -def index(**kwargs): +def render_index(): if settings.MULTI_ORG: response = render_template("multi_org.html", base_href=base_href()) else: @@ -37,6 +36,11 @@ def index(**kwargs): return response +@login_required +def index(**kwargs): + return render_index() + + def register_static_routes(rules): # Make sure that / is the first route considered as index. routes.add_url_rule(org_scoped_rule("/"), "index", index) From 23ba8b4aa126e3147f3dd3f09be33f668e382d23 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 4 May 2017 10:39:39 +0300 Subject: [PATCH 044/243] use relative links --- client/app/pages/dashboards/public-dashboard-page.js | 2 +- client/app/pages/queries/visualization-embed.js | 4 ++-- client/app/services/query-result.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/app/pages/dashboards/public-dashboard-page.js b/client/app/pages/dashboards/public-dashboard-page.js index 159e3d0746..cffa2c1034 100644 --- a/client/app/pages/dashboards/public-dashboard-page.js +++ b/client/app/pages/dashboards/public-dashboard-page.js @@ -31,7 +31,7 @@ export default function (ngModule) { 'ngInject'; const token = $route.current.params.token; - return $http.get(`/api/dashboards/public/${token}`).then(response => + return $http.get(`api/dashboards/public/${token}`).then(response => response.data ); } diff --git a/client/app/pages/queries/visualization-embed.js b/client/app/pages/queries/visualization-embed.js index 079fecb4c0..65710cc74e 100644 --- a/client/app/pages/queries/visualization-embed.js +++ b/client/app/pages/queries/visualization-embed.js @@ -36,8 +36,8 @@ export default function (ngModule) { function loadData($http, $route, $q, Auth) { return session($http, $route, Auth).then(() => { const queryId = $route.current.params.queryId; - const query = $http.get(`/api/queries/${queryId}`).then(response => response.data); - const queryResult = $http.get(`/api/queries/${queryId}/results.json`).then(response => response.data); + const query = $http.get(`api/queries/${queryId}`).then(response => response.data); + const queryResult = $http.get(`api/queries/${queryId}/results.json`).then(response => response.data); return $q.all([query, queryResult]); }); } diff --git a/client/app/services/query-result.js b/client/app/services/query-result.js index 4680f03b6a..44fe52df4d 100644 --- a/client/app/services/query-result.js +++ b/client/app/services/query-result.js @@ -409,7 +409,7 @@ function QueryResultService($resource, $timeout, $q) { } getLink(queryId, fileType, apiKey) { - let link = `/api/queries/${queryId}/results/${this.getId()}.${fileType}`; + let link = `api/queries/${queryId}/results/${this.getId()}.${fileType}`; if (apiKey) { link = `${link}?api_key=${apiKey}`; } From 8aa053ce21a7c9658cd9932a59850ca3a82a7d4d Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 4 May 2017 11:27:27 +0300 Subject: [PATCH 045/243] Log public dashboard view event --- redash/handlers/authentication.py | 14 +++++----- redash/handlers/embed.py | 26 ++++++++++------- redash/models.py | 46 +++++++++++++++++++------------ 3 files changed, 51 insertions(+), 35 deletions(-) diff --git a/redash/handlers/authentication.py b/redash/handlers/authentication.py index 9ff648b070..3e1c695567 100644 --- a/redash/handlers/authentication.py +++ b/redash/handlers/authentication.py @@ -152,7 +152,7 @@ def base_href(): def client_config(): - if not isinstance(current_user._get_current_object(), models.ApiUser) and current_user.is_authenticated: + if not current_user.is_api_user() and current_user.is_authenticated: client_config = { 'newVersionAvailable': get_latest_version(), 'version': __version__ @@ -181,7 +181,12 @@ def config(org_slug=None): @routes.route('/api/session', methods=['GET']) @login_required def session(org_slug=None): - if not isinstance(current_user._get_current_object(), models.ApiUser): + if current_user.is_api_user(): + user = { + 'permissions': [], + 'apiKey': current_user.id + } + else: email_md5 = hashlib.md5(current_user.email.lower()).hexdigest() gravatar_url = "https://www.gravatar.com/avatar/%s?s=40" % email_md5 @@ -193,11 +198,6 @@ def session(org_slug=None): 'groups': current_user.group_ids, 'permissions': current_user.permissions } - else: - user = { - 'permissions': [], - 'apiKey': current_user.id - } return json_response({ 'user': user, diff --git a/redash/handlers/embed.py b/redash/handlers/embed.py index 78cb849618..edd614560e 100644 --- a/redash/handlers/embed.py +++ b/redash/handlers/embed.py @@ -9,7 +9,8 @@ from flask_restful import abort from redash import models, utils from redash.handlers import routes -from redash.handlers.base import org_scoped_rule, record_event +from redash.handlers.base import (get_object_or_404, org_scoped_rule, + record_event) from redash.handlers.query_results import collect_query_parameters from redash.handlers.static import render_index from redash.utils import gen_query_hash @@ -82,13 +83,18 @@ def embed(query_id, visualization_id, org_slug=None): @routes.route(org_scoped_rule('/public/dashboards/'), methods=['GET']) @login_required def public_dashboard(token, org_slug=None): - # TODO: bring this back. - # record_event(current_org, current_user, { - # 'action': 'view', - # 'object_id': dashboard.id, - # 'object_type': 'dashboard', - # 'public': True, - # 'headless': 'embed' in request.args, - # 'referer': request.headers.get('Referer') - # }) + if current_user.is_api_user(): + dashboard = current_user.object + else: + api_key = get_object_or_404(models.ApiKey.get_by_api_key, token) + dashboard = api_key.object + + record_event(current_org, current_user, { + 'action': 'view', + 'object_id': dashboard.id, + 'object_type': 'dashboard', + 'public': True, + 'headless': 'embed' in request.args, + 'referer': request.headers.get('Referer') + }) return render_index() diff --git a/redash/models.py b/redash/models.py index e8035061d5..e1a263070c 100644 --- a/redash/models.py +++ b/redash/models.py @@ -1,35 +1,36 @@ +import cStringIO +import csv import datetime import functools import hashlib import itertools import json import logging -import cStringIO -import csv -import xlsxwriter from funcy import project -from flask_sqlalchemy import SQLAlchemy -from flask_login import UserMixin, AnonymousUserMixin -from sqlalchemy.dialects import postgresql -from sqlalchemy.event import listens_for -from sqlalchemy.inspection import inspect -from sqlalchemy.types import TypeDecorator -from sqlalchemy.ext.mutable import Mutable -from sqlalchemy.orm import object_session, backref, joinedload, subqueryload -# noinspection PyUnresolvedReferences -from sqlalchemy.orm.exc import NoResultFound -from sqlalchemy import or_ +import xlsxwriter +from flask_login import AnonymousUserMixin, UserMixin +from flask_sqlalchemy import SQLAlchemy from passlib.apps import custom_app_context as pwd_context - from redash import redis_connection, utils -from redash.destinations import get_destination, get_configuration_schema_for_destination_type +from redash.destinations import (get_configuration_schema_for_destination_type, + get_destination) +from redash.metrics import database from redash.permissions import has_access, view_only -from redash.query_runner import get_query_runner, get_configuration_schema_for_query_runner_type +from redash.query_runner import (get_configuration_schema_for_query_runner_type, + get_query_runner) from redash.utils import generate_token, json_dumps from redash.utils.configuration import ConfigurationContainer -from redash.metrics import database +from sqlalchemy import or_ +from sqlalchemy.dialects import postgresql +from sqlalchemy.event import listens_for +from sqlalchemy.ext.mutable import Mutable +from sqlalchemy.inspection import inspect +from sqlalchemy.orm import backref, joinedload, object_session, subqueryload +# noinspection PyUnresolvedReferences +from sqlalchemy.orm.exc import NoResultFound +from sqlalchemy.types import TypeDecorator db = SQLAlchemy(session_options={ 'expire_on_commit': False @@ -201,6 +202,9 @@ class AnonymousUser(AnonymousUserMixin, PermissionsCheckMixin): def permissions(self): return [] + def is_api_user(): + return False + class ApiUser(UserMixin, PermissionsCheckMixin): def __init__(self, api_key, org, groups, name=None): @@ -218,6 +222,9 @@ def __init__(self, api_key, org, groups, name=None): def __repr__(self): return u"<{}>".format(self.name) + def is_api_user(): + return True + @property def permissions(self): return ['view_query'] @@ -355,6 +362,9 @@ def to_dict(self, with_api_key=False): return d + def is_api_user(): + return False + @property def gravatar_url(self): email_md5 = hashlib.md5(self.email.lower()).hexdigest() From 75f90c190bb8ccbc48f38c770b9f02f2460592be Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 4 May 2017 11:49:10 +0300 Subject: [PATCH 046/243] Update test for new path --- redash/models.py | 6 +++--- tests/handlers/test_authentication.py | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/redash/models.py b/redash/models.py index e1a263070c..0925b82a80 100644 --- a/redash/models.py +++ b/redash/models.py @@ -202,7 +202,7 @@ class AnonymousUser(AnonymousUserMixin, PermissionsCheckMixin): def permissions(self): return [] - def is_api_user(): + def is_api_user(self): return False @@ -222,7 +222,7 @@ def __init__(self, api_key, org, groups, name=None): def __repr__(self): return u"<{}>".format(self.name) - def is_api_user(): + def is_api_user(self): return True @property @@ -362,7 +362,7 @@ def to_dict(self, with_api_key=False): return d - def is_api_user(): + def is_api_user(self): return False @property diff --git a/tests/handlers/test_authentication.py b/tests/handlers/test_authentication.py index b0477c92aa..07e3e0e52c 100644 --- a/tests/handlers/test_authentication.py +++ b/tests/handlers/test_authentication.py @@ -1,9 +1,11 @@ -from tests import BaseTestCase -import mock import time + +import mock +from tests import BaseTestCase + from redash import settings -from redash.models import User from redash.authentication.account import invite_token +from redash.models import User class TestInvite(BaseTestCase): @@ -70,4 +72,4 @@ def test_throttle_login(self): class TestSession(BaseTestCase): # really simple test just to trigger this route def test_get(self): - self.make_request('get', '/api/session', user=self.factory.user) + self.make_request('get', '/api/session', user=self.factory.user, org=False) From 75ebbe148b66e94d7893efa4515d488946822f71 Mon Sep 17 00:00:00 2001 From: Frank Bertsch Date: Wed, 2 Nov 2016 14:42:28 -0500 Subject: [PATCH 047/243] Add presto query cancellation --- redash/query_runner/presto.py | 6 +++++- requirements_all_ds.txt | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/redash/query_runner/presto.py b/redash/query_runner/presto.py index 715fe6e43b..7e68ed1255 100644 --- a/redash/query_runner/presto.py +++ b/redash/query_runner/presto.py @@ -118,7 +118,11 @@ def run_query(self, query, user): default_message = 'Unspecified DatabaseError: {0}'.format(db.message) message = db.message.get('failureInfo', {'message', None}).get('message') error = default_message if message is None else message - except Exception, ex: + except (KeyboardInterrupt, InterruptException) as e: + cursor.cancel() + error = "Query cancelled by user." + json_data = None + except Exception as ex: json_data = None error = ex.message if not isinstance(error, basestring): diff --git a/requirements_all_ds.txt b/requirements_all_ds.txt index f572273b13..03f2206866 100644 --- a/requirements_all_ds.txt +++ b/requirements_all_ds.txt @@ -4,7 +4,7 @@ impyla==0.10.0 influxdb==2.7.1 MySQL-python==1.2.5 oauth2client==3.0.0 -pyhive==0.1.6 +pyhive==0.3.0 pymongo==3.2.1 pyOpenSSL==0.14 vertica-python==0.5.1 From ea7c6c2be3aaa4ba02fa6e11496ed56e1eb2bdb5 Mon Sep 17 00:00:00 2001 From: Vladislav Denisov Date: Fri, 5 May 2017 09:26:09 +0300 Subject: [PATCH 048/243] jql: fixed maxResults in count query --- redash/query_runner/jql.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redash/query_runner/jql.py b/redash/query_runner/jql.py index c20710e512..37b1f345c6 100644 --- a/redash/query_runner/jql.py +++ b/redash/query_runner/jql.py @@ -181,7 +181,7 @@ def run_query(self, query, user): field_mapping = FieldMapping(query.pop('fieldMapping', {})) if query_type == 'count': - query['maxResults'] = query.get('maxResults', 1) + query['maxResults'] = 1 query['fields'] = '' else: query['maxResults'] = query.get('maxResults', 1000) From 914977f279dec85fb90cc32b9cad4bc7b9a3fee9 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Sun, 7 May 2017 12:35:23 +0300 Subject: [PATCH 049/243] Fix: properties of data source were not reset when changing type. Fixes #1748. --- client/app/components/dynamic-form.js | 55 ++++++++++++++++----------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/client/app/components/dynamic-form.js b/client/app/components/dynamic-form.js index dc0122d574..3d87f630ac 100644 --- a/client/app/components/dynamic-form.js +++ b/client/app/components/dynamic-form.js @@ -35,9 +35,13 @@ function DynamicForm($http, toastr, $q) { const type = find(types, t => t.type === $scope.target.type); const configurationSchema = type.configuration_schema; - $scope.type = type; - $scope.fields = orderedInputs(configurationSchema.properties, - configurationSchema.order || []); + + $scope.fields = orderedInputs( + configurationSchema.properties, + configurationSchema.order || [] + ); + + return type; } $scope.inProgressActions = {}; @@ -99,30 +103,37 @@ function DynamicForm($http, toastr, $q) { prop.required = contains(type.configuration_schema.required, name); }); }); - }); - $scope.$watch('target.type', (current, prev) => { - if (prev !== current) { - if (prev !== undefined) { - $scope.target.options = {}; - } - if (Object.keys($scope.target.options).length === 0) { - const properties = $scope.type.configuration_schema.properties; - Object.keys(properties).forEach((property) => { - $scope.target.options[property] = properties[property].default || ''; - }); + $scope.$watch('target.type', (current, prev) => { + if (prev !== current) { + if (prev !== undefined) { + $scope.target.options = {}; + } + + const type = setType($scope.types); + + if (Object.keys($scope.target.options).length === 0) { + const properties = type.configuration_schema.properties; + Object.keys(properties).forEach((property) => { + $scope.target.options[property] = + properties[property].default || ''; + }); + } } - setType($scope.types); - } + }); }); + $scope.saveChanges = () => { - $scope.target.$save(() => { - toastr.success('Saved.'); - $scope.dataSourceForm.$setPristine(); - }, () => { - toastr.error('Failed saving.'); - }); + $scope.target.$save( + () => { + toastr.success('Saved.'); + $scope.dataSourceForm.$setPristine(); + }, + () => { + toastr.error('Failed saving.'); + } + ); }; }, }; From f121c609adf17ed38e31176399af1a5020af0d63 Mon Sep 17 00:00:00 2001 From: deecay Date: Mon, 10 Apr 2017 15:36:15 +0900 Subject: [PATCH 050/243] Change: Sort dashboard-list in /dashboards --- client/app/pages/dashboards/dashboard-list.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/app/pages/dashboards/dashboard-list.js b/client/app/pages/dashboards/dashboard-list.js index d5fb4ac5a6..3421dc7fcb 100644 --- a/client/app/pages/dashboards/dashboard-list.js +++ b/client/app/pages/dashboards/dashboard-list.js @@ -46,6 +46,7 @@ function DashboardListCtrl(Dashboard, $location, clientConfig) { this.update = () => { this.dashboards.$promise.then((data) => { + data = _.sortBy(data, 'name'); const filteredDashboards = data.map((dashboard) => { dashboard.tags = (dashboard.name.match(TAGS_REGEX) || []).map(tag => tag.replace(/:$/, '')); dashboard.untagged_name = dashboard.name.replace(TAGS_REGEX, '').trim(); From a86ece66b540c19965bd87b0b43ea1ca368bb119 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Tue, 9 May 2017 10:41:26 +0300 Subject: [PATCH 051/243] Add chunkhash to filename only when running production build. --- webpack.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index 085f66ad5f..0760ad1132 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -15,7 +15,7 @@ var config = { output: { // path: process.env.NODE_ENV === 'production' ? './dist' : './dev', path: './client/dist', - filename: '[name].[chunkhash].js', + filename: '[name].js', publicPath: '/' }, @@ -134,6 +134,7 @@ if (process.env.DEV_SERVER_HOST) { if (process.env.NODE_ENV === 'production') { config.output.path = __dirname + '/client/dist'; + config.output.filename = '[name].[chunkhash].js'; config.plugins.push(new webpack.optimize.UglifyJsPlugin()); config.devtool = 'source-map'; } From 7c6327be5736199947155faa3185bf3e6c3616fa Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Tue, 9 May 2017 10:42:30 +0300 Subject: [PATCH 052/243] Clicking logo should take to account homepage --- client/app/components/app-header/app-header.html | 2 +- client/app/components/app-header/index.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/app/components/app-header/app-header.html b/client/app/components/app-header/app-header.html index 7426d02ad0..ee7b2e7596 100644 --- a/client/app/components/app-header/app-header.html +++ b/client/app/components/app-header/app-header.html @@ -7,7 +7,7 @@ - +
+
+ +
From 76470b9f094dd93ad549629eb9eae7ac65f7ec52 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 18 May 2017 09:25:44 +0300 Subject: [PATCH 072/243] Fix: don't remove locks for queries with task status of PENDING. It's possible the Celery metadata object was expired, but the task is still running (which will result in PENDING status when querying the AsyncResult object). --- redash/tasks/queries.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/redash/tasks/queries.py b/redash/tasks/queries.py index 788bf94f3f..0034aec0ec 100644 --- a/redash/tasks/queries.py +++ b/redash/tasks/queries.py @@ -1,14 +1,17 @@ import json -import time import logging import signal +import time + import redis + from celery.result import AsyncResult from celery.utils.log import get_task_logger -from redash import redis_connection, models, statsd_client, settings, utils +from redash import models, redis_connection, settings, statsd_client, utils +from redash.query_runner import InterruptException from redash.utils import gen_query_hash from redash.worker import celery -from redash.query_runner import InterruptException + from .alerts import check_alerts_for_query logger = get_task_logger(__name__) @@ -265,7 +268,7 @@ def refresh_queries(): with statsd_client.timer('manager.outdated_queries_lookup'): for query in models.Query.outdated_queries(): - if settings.FEATURE_DISABLE_REFRESH_QUERIES: + if settings.FEATURE_DISABLE_REFRESH_QUERIES: logging.info("Disabled refresh queries.") elif query.data_source.paused: logging.info("Skipping refresh of %s because datasource - %s is paused (%s).", query.id, query.data_source.name, query.data_source.pause_reason) @@ -299,14 +302,6 @@ def cleanup_tasks(): for tracker in in_progress: result = AsyncResult(tracker.task_id) - # If the AsyncResult status is PENDING it means there is no celery task object for this tracker, and we can - # mark it as "dead": - if result.status == 'PENDING': - logging.info("In progress tracker for %s is no longer enqueued, cancelling (task: %s).", - tracker.query_hash, tracker.task_id) - _unlock(tracker.query_hash, tracker.data_source_id) - tracker.update(state='cancelled') - if result.ready(): logging.info("in progress tracker %s finished", tracker.query_hash) _unlock(tracker.query_hash, tracker.data_source_id) From f679dc756252134e4afd765010e36ca29c86dc03 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 18 May 2017 09:31:04 +0300 Subject: [PATCH 073/243] Put a limit on how many keys we remove at a time to make sure it can handle large lists. --- redash/tasks/queries.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/redash/tasks/queries.py b/redash/tasks/queries.py index 0034aec0ec..56cfff9fde 100644 --- a/redash/tasks/queries.py +++ b/redash/tasks/queries.py @@ -117,16 +117,15 @@ def all(cls, list_name, offset=0, limit=-1): return tasks @classmethod - def prune(cls, list_name, keep_count): + def prune(cls, list_name, keep_count, max_keys=100): count = redis_connection.zcard(list_name) if count <= keep_count: return 0 - remove_count = count - keep_count + remove_count = min(max_keys, count - keep_count) keys = redis_connection.zrange(list_name, 0, remove_count - 1) redis_connection.delete(*keys) redis_connection.zremrangebyrank(list_name, 0, remove_count - 1) - return remove_count def __getattr__(self, item): @@ -317,7 +316,9 @@ def cleanup_tasks(): tracker.update(state='finished') # Maintain constant size of the finished tasks list: - QueryTaskTracker.prune(QueryTaskTracker.DONE_LIST, 1000) + removed = 1000 + while removed > 0: + removed = QueryTaskTracker.prune(QueryTaskTracker.DONE_LIST, 1000) @celery.task(name="redash.tasks.cleanup_query_results") From 4c1cb037a0d76dfd7283b175725efc45fec18da4 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 18 May 2017 09:33:28 +0300 Subject: [PATCH 074/243] Include Celery task name in statsd metrics. --- redash/metrics/celery.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/redash/metrics/celery.py b/redash/metrics/celery.py index 5c76be7bfd..25b63af136 100644 --- a/redash/metrics/celery.py +++ b/redash/metrics/celery.py @@ -1,11 +1,12 @@ from __future__ import absolute_import -import logging -import time import json +import logging import socket -from celery.signals import task_prerun, task_postrun -from redash import statsd_client, settings +import time + +from celery.signals import task_postrun, task_prerun +from redash import settings, statsd_client tasks_start_time = {} @@ -33,16 +34,19 @@ def task_postrun_handler(signal, sender, task_id, task, args, kwargs, retval, st try: run_time = 1000 * (time.time() - tasks_start_time.pop(task_id)) - tags = {'name': task.name, 'state': (state or 'unknown').lower(), 'hostname': socket.gethostname()} + state = (state or 'unknown').lower() + tags = {'state': state, 'hostname': socket.gethostname()} if task.name == 'redash.tasks.execute_query': if isinstance(retval, Exception): tags['state'] = 'exception' + state = 'exception' tags['data_source_id'] = args[1] - metric = "celery.task.runtime" + normalized_task_name = task.name.replace('redash.tasks.', '').replace('.', '_') + metric = "celery.task_runtime.{}".format(normalized_task_name) logging.debug("metric=%s", json.dumps({'metric': metric, 'tags': tags, 'value': run_time})) statsd_client.timing(metric_name(metric, tags), run_time) - statsd_client.incr(metric_name('celery.task.count', tags)) + statsd_client.incr(metric_name('celery.task.{}.{}'.format(normalized_task_name, state), tags)) except Exception: logging.exception("Exception during task_postrun handler.") From 79ffbbbe4b1f87797af58343eaf278a7ba939ed1 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 18 May 2017 09:34:32 +0300 Subject: [PATCH 075/243] Don't include paused datasource's queries in outdated queries count. --- redash/tasks/queries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redash/tasks/queries.py b/redash/tasks/queries.py index 56cfff9fde..c3dc28e3aa 100644 --- a/redash/tasks/queries.py +++ b/redash/tasks/queries.py @@ -276,8 +276,8 @@ def refresh_queries(): scheduled_query=query, metadata={'Query ID': query.id, 'Username': 'Scheduled'}) - query_ids.append(query.id) - outdated_queries_count += 1 + query_ids.append(query.id) + outdated_queries_count += 1 statsd_client.gauge('manager.outdated_queries', outdated_queries_count) From f4297ff3b0d7e88402e9675728b58d9f1c89f571 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 18 May 2017 09:36:21 +0300 Subject: [PATCH 076/243] Handle the case when the task object might not exist. --- redash/handlers/admin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/redash/handlers/admin.py b/redash/handlers/admin.py index 325652b885..5f5860bf2e 100644 --- a/redash/handlers/admin.py +++ b/redash/handlers/admin.py @@ -37,9 +37,9 @@ def queries_tasks(): done = QueryTaskTracker.all(QueryTaskTracker.DONE_LIST, limit=50) response = { - 'waiting': [t.data for t in waiting], - 'in_progress': [t.data for t in in_progress], - 'done': [t.data for t in done] + 'waiting': [t.data for t in waiting if t is not None], + 'in_progress': [t.data for t in in_progress if t is not Non], + 'done': [t.data for t in done if t is not Non] } return json_response(response) From 14c751b39eaa224d9a3fc365f13d3179b60e72bc Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 18 May 2017 11:07:39 +0300 Subject: [PATCH 077/243] Cohort: handle the case where the value/total might be strings. --- client/app/visualizations/cohort/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/app/visualizations/cohort/index.js b/client/app/visualizations/cohort/index.js index e2711114d1..56a974e90f 100644 --- a/client/app/visualizations/cohort/index.js +++ b/client/app/visualizations/cohort/index.js @@ -25,7 +25,7 @@ function cohortRenderer() { } const sortedData = _.sortBy($scope.queryResult.getData(), r => - r.date + r.day_number + r.date + parseInt(r.day_number, 10) ); const grouped = _.groupBy(sortedData, 'date'); @@ -35,9 +35,9 @@ function cohortRenderer() { , 0); const data = _.map(grouped, (values) => { - const row = [values[0].total]; + const row = [parseInt(values[0].total, 10)]; _.each(values, (value) => { - row.push(value.value); + row.push(parseInt(value.value, 10)); }); _.each(_.range(values.length, maxColumns), () => { row.push(null); From d7c502eb507e682541b99cd071774f7d3614bcb0 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 18 May 2017 11:13:11 +0300 Subject: [PATCH 078/243] Query results: better type guessing on the client side. --- client/app/services/query-result.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/client/app/services/query-result.js b/client/app/services/query-result.js index 44fe52df4d..d7b2ebefb6 100644 --- a/client/app/services/query-result.js +++ b/client/app/services/query-result.js @@ -85,17 +85,29 @@ function QueryResultService($resource, $timeout, $q) { // on the column type set by the backend. This logic is prone to errors, // and better be removed. Kept for now, for backward compatability. each(this.query_result.data.rows, (row) => { + let newType = null; + each(row, (v, k) => { if (isNumber(v)) { - columnTypes[k] = 'float'; + newType = 'float'; } else if (isString(v) && v.match(/^\d{4}-\d{2}-\d{2}T/)) { row[k] = moment.utc(v); - columnTypes[k] = 'datetime'; + newType = 'datetime'; } else if (isString(v) && v.match(/^\d{4}-\d{2}-\d{2}$/)) { row[k] = moment.utc(v); - columnTypes[k] = 'date'; + newType = 'date'; } else if (typeof (v) === 'object' && v !== null) { row[k] = JSON.stringify(v); + } else { + newType = 'string'; + } + + if (newType !== null) { + if (columnTypes[k] !== undefined && columnTypes[k] !== newType) { + columnTypes[k] = 'string'; + } else { + columnTypes[k] = newType; + } } }); }); From 62962d28caf09ac8a7121a01992d7fe063f2067f Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 18 May 2017 11:15:21 +0300 Subject: [PATCH 079/243] Only split columns with __/:: that end with filter/MultiFilter. --- client/app/services/query-result.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/app/services/query-result.js b/client/app/services/query-result.js index d7b2ebefb6..62c4d732ce 100644 --- a/client/app/services/query-result.js +++ b/client/app/services/query-result.js @@ -3,6 +3,7 @@ import moment from 'moment'; import { uniq, contains, values, some, each, isArray, isNumber, isString } from 'underscore'; const logger = debug('redash:services:QueryResult'); +const filterTypes = ['filter', 'multi-filter', 'multiFilter']; function getColumnNameWithoutType(column) { let typeSplit; @@ -18,6 +19,11 @@ function getColumnNameWithoutType(column) { if (parts[0] === '' && parts.length === 2) { return parts[1]; } + + if (!contains(filterTypes, parts[1])) { + return column; + } + return parts[0]; } @@ -343,7 +349,6 @@ function QueryResultService($resource, $timeout, $q) { } const filters = []; - const filterTypes = ['filter', 'multi-filter', 'multiFilter']; this.getColumns().forEach((col) => { const name = col.name; From e050c085dfcac6d94cc6ff9f342502a435d830cd Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 18 May 2017 11:23:38 +0300 Subject: [PATCH 080/243] Counter: support negative indexes to iterate from the end of the results. --- .../visualizations/counter/counter-editor.html | 4 ++-- client/app/visualizations/counter/index.js | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/client/app/visualizations/counter/counter-editor.html b/client/app/visualizations/counter/counter-editor.html index ab760ef9e1..6e19161b36 100644 --- a/client/app/visualizations/counter/counter-editor.html +++ b/client/app/visualizations/counter/counter-editor.html @@ -8,7 +8,7 @@
- +
@@ -22,7 +22,7 @@
- +
diff --git a/client/app/visualizations/counter/index.js b/client/app/visualizations/counter/index.js index 11b24a8e48..6bb5aadf0b 100644 --- a/client/app/visualizations/counter/index.js +++ b/client/app/visualizations/counter/index.js @@ -1,6 +1,18 @@ import counterTemplate from './counter.html'; import counterEditorTemplate from './counter-editor.html'; +function getRowNumber(index, size) { + if (index >= 0) { + return index - 1; + } + + if (Math.abs(index) > size) { + index %= size; + } + + return size + index; +} + function CounterRenderer() { return { restrict: 'E', @@ -9,8 +21,9 @@ function CounterRenderer() { const refreshData = () => { const queryData = $scope.queryResult.getData(); if (queryData) { - const rowNumber = $scope.visualization.options.rowNumber - 1; - const targetRowNumber = $scope.visualization.options.targetRowNumber - 1; + const rowNumber = getRowNumber($scope.visualization.options.rowNumber, queryData.length); + const targetRowNumber = + getRowNumber($scope.visualization.options.targetRowNumber, queryData.length); const counterColName = $scope.visualization.options.counterColName; const targetColName = $scope.visualization.options.targetColName; From 1aa54543edbf9962809ad912cddb2d886e2c9522 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 18 May 2017 11:28:40 +0300 Subject: [PATCH 081/243] Retry reload of query results if it had an error --- client/app/services/query-result.js | 31 ++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/client/app/services/query-result.js b/client/app/services/query-result.js index 62c4d732ce..9a2dc6de88 100644 --- a/client/app/services/query-result.js +++ b/client/app/services/query-result.js @@ -405,14 +405,39 @@ function QueryResultService($resource, $timeout, $q) { return queryResult; } + loadResult(tryCount) { + QueryResultResource.get({ id: this.job.query_result_id }, + (response) => { + this.update(response); + }, + (error) => { + if (tryCount === undefined) { + tryCount = 0; + } + + if (tryCount > 3) { + logger('Connection error while trying to load result', error); + this.update({ + job: { + error: 'failed communicating with server. Please check your Internet connection and try again.', + status: 4, + }, + }); + } else { + $timeout(() => { + this.loadResult(tryCount + 1); + }, 1000 * Math.pow(2, tryCount)); + } + } + ); + } + refreshStatus(query) { Job.get({ id: this.job.id }, (jobResponse) => { this.update(jobResponse); if (this.getStatus() === 'processing' && this.job.query_result_id && this.job.query_result_id !== 'None') { - QueryResultResource.get({ id: this.job.query_result_id }, (response) => { - this.update(response); - }); + this.loadResult(); } else if (this.getStatus() !== 'failed') { $timeout(() => { this.refreshStatus(query); From b70c329307318f10b2a6c0b58769ed3e5ce96943 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 18 May 2017 11:37:54 +0300 Subject: [PATCH 082/243] Dynamic form: change order of name and type (type first now). --- client/app/components/dynamic-form.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/app/components/dynamic-form.html b/client/app/components/dynamic-form.html index babaad47f7..4e333075c1 100644 --- a/client/app/components/dynamic-form.html +++ b/client/app/components/dynamic-form.html @@ -1,12 +1,12 @@
-
- - -
+
+ + +
Date: Thu, 18 May 2017 11:53:01 +0300 Subject: [PATCH 083/243] Show API Key in a modal dialog instead of alert. --- client/app/pages/queries/api-key-dialog.js | 37 ++++++++++++++++++++++ client/app/pages/queries/index.js | 2 ++ client/app/pages/queries/view.js | 7 +++- 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 client/app/pages/queries/api-key-dialog.js diff --git a/client/app/pages/queries/api-key-dialog.js b/client/app/pages/queries/api-key-dialog.js new file mode 100644 index 0000000000..0d9115769e --- /dev/null +++ b/client/app/pages/queries/api-key-dialog.js @@ -0,0 +1,37 @@ +const ApiKeyDialog = { + template: ` +`, + controller(clientConfig) { + 'ngInject'; + + this.apiKey = this.resolve.query.api_key; + this.csvUrl = `${clientConfig.basePath}api/queries/${this.resolve.query.id}/results.csv?api_key=${this.apiKey}`; + this.jsonUrl = `${clientConfig.basePath}api/queries/${this.resolve.query.id}/results.json?api_key=${this.apiKey}`; + }, + bindings: { + resolve: '<', + close: '&', + dismiss: '&', + }, +}; + +export default function (ngModule) { + ngModule.component('apiKeyDialog', ApiKeyDialog); +} diff --git a/client/app/pages/queries/index.js b/client/app/pages/queries/index.js index 1ccc4d02b3..44c760a462 100644 --- a/client/app/pages/queries/index.js +++ b/client/app/pages/queries/index.js @@ -4,6 +4,7 @@ import registerQueryResultsLink from './query-results-link'; import registerQueryEditor from './query-editor'; import registerSchemaBrowser from './schema-browser'; import registerEmbedCodeDialog from './embed-code-dialog'; +import registerApiKeyDialog from './api-key-dialog'; import registerScheduleDialog from './schedule-dialog'; import registerAlertUnsavedChanges from './alert-unsaved-changes'; import registerQuerySearchResultsPage from './queries-search-results-page'; @@ -17,6 +18,7 @@ export default function (ngModule) { registerScheduleDialog(ngModule); registerAlertUnsavedChanges(ngModule); registerVisualizationEmbed(ngModule); + registerApiKeyDialog(ngModule); return Object.assign({}, registerQuerySearchResultsPage(ngModule), registerSourceView(ngModule), diff --git a/client/app/pages/queries/view.js b/client/app/pages/queries/view.js index da5de16a99..e95070fe18 100644 --- a/client/app/pages/queries/view.js +++ b/client/app/pages/queries/view.js @@ -158,7 +158,12 @@ function QueryViewCtrl($scope, Events, $route, $routeParams, $location, $window, }; $scope.showApiKey = () => { - $window.alert(`API Key for this query:\n${$scope.query.api_key}`); + $uibModal.open({ + component: 'apiKeyDialog', + resolve: { + query: $scope.query, + }, + }); }; $scope.saveQuery = (customOptions, data) => { From 5a5917a04a68fb30916c4d30533ed265a5151836 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 18 May 2017 13:24:53 +0300 Subject: [PATCH 084/243] Sentry: upgrade client version. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b703b559c4..0103c6ffd7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -36,7 +36,7 @@ RestrictedPython==3.6.0 pysaml2==2.4.0 pycrypto==2.6.1 funcy==1.7.1 -raven==5.27.1 +raven==6.0.0 semver==2.2.1 xlsxwriter==0.9.3 pystache==0.5.4 From d60843fa5be236287ed916b9538a615932fb5ed7 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 18 May 2017 13:26:20 +0300 Subject: [PATCH 085/243] Sentry: don't install logging hook. --- redash/__init__.py | 7 +++++-- redash/worker.py | 12 ++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/redash/__init__.py b/redash/__init__.py index 8989e998a9..9609e4b32f 100644 --- a/redash/__init__.py +++ b/redash/__init__.py @@ -106,12 +106,15 @@ def create_app(load_admin=True): SSLify(app, skips=['ping']) if settings.SENTRY_DSN: + from raven import Client from raven.contrib.flask import Sentry from raven.handlers.logging import SentryHandler - sentry = Sentry(app, dsn=settings.SENTRY_DSN) + + client = Client(settings.SENTRY_DSN, release=__version__, install_logging_hook=False) + sentry = Sentry(app, client=client) sentry.client.release = __version__ - sentry_handler = SentryHandler(settings.SENTRY_DSN) + sentry_handler = SentryHandler(client=client) sentry_handler.setLevel(logging.ERROR) logging.getLogger().addHandler(sentry_handler) diff --git a/redash/worker.py b/redash/worker.py index 19fabad9c8..5e003c61f3 100644 --- a/redash/worker.py +++ b/redash/worker.py @@ -1,15 +1,16 @@ from __future__ import absolute_import +from datetime import timedelta from random import randint -from celery import Celery + from flask import current_app -from datetime import timedelta + +from celery import Celery from celery.schedules import crontab from celery.signals import worker_process_init -from redash import settings, __version__, create_app +from redash import __version__, create_app, settings from redash.metrics import celery as celery_metrics - celery = Celery('redash', broker=settings.CELERY_BROKER, include='redash.tasks') @@ -52,7 +53,7 @@ from raven import Client from raven.contrib.celery import register_signal - client = Client(settings.SENTRY_DSN, release=__version__) + client = Client(settings.SENTRY_DSN, release=__version__, install_logging_hook=False) register_signal(client) @@ -75,4 +76,3 @@ def __call__(self, *args, **kwargs): def init_celery_flask_app(**kwargs): app = create_app() app.app_context().push() - From 3807510bfeb40952339ac69e2449f97ff18c7f68 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 18 May 2017 13:39:34 +0300 Subject: [PATCH 086/243] Split refresh schemas into separate tasks and add a timeout. --- redash/models.py | 28 ++++++++++++------------ redash/tasks/queries.py | 34 ++++++++++++++++++----------- tests/tasks/test_refresh_schemas.py | 16 ++++++++------ 3 files changed, 44 insertions(+), 34 deletions(-) diff --git a/redash/models.py b/redash/models.py index 961a339dff..2473542c5c 100644 --- a/redash/models.py +++ b/redash/models.py @@ -479,6 +479,20 @@ def create_with_group(cls, *args, **kwargs): db.session.add_all([data_source, data_source_group]) return data_source + @classmethod + def all(cls, org, group_ids=None): + data_sources = cls.query.filter(cls.org == org).order_by(cls.id.asc()) + + if group_ids: + data_sources = data_sources.join(DataSourceGroup).filter( + DataSourceGroup.group_id.in_(group_ids)) + + return data_sources + + @classmethod + def get_by_id(cls, _id): + return cls.query.filter(cls.id == _id).one() + def get_schema(self, refresh=False): key = "data_source:schema:{}".format(self.id) @@ -536,24 +550,10 @@ def update_group_permission(self, group, view_only): def query_runner(self): return get_query_runner(self.type, self.options) - @classmethod - def get_by_id(cls, _id): - return cls.query.filter(cls.id == _id).one() - @classmethod def get_by_name(cls, name): return cls.query.filter(cls.name == name).one() - @classmethod - def all(cls, org, group_ids=None): - data_sources = cls.query.filter(cls.org == org).order_by(cls.id.asc()) - - if group_ids: - data_sources = data_sources.join(DataSourceGroup).filter( - DataSourceGroup.group_id.in_(group_ids)) - - return data_sources - #XXX examine call sites to see if a regular SQLA collection would work better @property def groups(self): diff --git a/redash/tasks/queries.py b/redash/tasks/queries.py index c3dc28e3aa..9dea6b1d88 100644 --- a/redash/tasks/queries.py +++ b/redash/tasks/queries.py @@ -5,6 +5,7 @@ import redis +from celery.exceptions import SoftTimeLimitExceeded from celery.result import AsyncResult from celery.utils.log import get_task_logger from redash import models, redis_connection, settings, statsd_client, utils @@ -234,9 +235,7 @@ def enqueue_query(query, data_source, user_id, scheduled_query=None, metadata={} queue_name = data_source.queue_name scheduled_query_id = None - result = execute_query.apply_async(args=( - query, data_source.id, metadata, user_id, - scheduled_query_id), + result = execute_query.apply_async(args=(query, data_source.id, metadata, user_id, scheduled_query_id), queue=queue_name) job = QueryTask(async_result=result) tracker = QueryTaskTracker.create( @@ -342,14 +341,30 @@ def cleanup_query_results(): logger.info("Deleted %d unused query results.", deleted_count) +@celery.task(name="redash.tasks.refresh_schema", soft_time_limit=60) +def refresh_schema(data_source_id): + ds = models.DataSource.get_by_id(data_source_id) + logger.info(u"task=refresh_schema state=start ds_id=%s", ds.id) + start_time = time.time() + try: + ds.get_schema(refresh=True) + logger.info(u"task=refresh_schema state=finished ds_id=%s runtime=%.2f", ds.id, time.time() - start_time) + statsd_client.incr('refresh_schema.success') + except SoftTimeLimitExceeded: + logger.info(u"task=refresh_schema state=timeout ds_id=%s runtime=%.2f", ds.id, time.time() - start_time) + statsd_client.incr('refresh_schema.timeout') + except Exception: + logger.warning(u"Failed refreshing schema for the data source: %s", ds.name, exc_info=1) + statsd_client.incr('refresh_schema.error') + logger.info(u"task=refresh_schema state=failed ds_id=%s runtime=%.2f", ds.id, time.time() - start_time) + + @celery.task(name="redash.tasks.refresh_schemas") def refresh_schemas(): """ Refreshes the data sources schemas. """ - blacklist = [int(ds_id) for ds_id in redis_connection.smembers('data_sources:schema:blacklist') if ds_id] - global_start_time = time.time() logger.info(u"task=refresh_schemas state=start") @@ -360,14 +375,7 @@ def refresh_schemas(): elif ds.id in blacklist: logger.info(u"task=refresh_schema state=skip ds_id=%s reason=blacklist", ds.id) else: - logger.info(u"task=refresh_schema state=start ds_id=%s", ds.id) - start_time = time.time() - try: - ds.get_schema(refresh=True) - logger.info(u"task=refresh_schema state=finished ds_id=%s runtime=%.2f", ds.id, time.time() - start_time) - except Exception: - logger.exception(u"Failed refreshing schema for the data source: %s", ds.name) - logger.info(u"task=refresh_schema state=failed ds_id=%s runtime=%.2f", ds.id, time.time() - start_time) + refresh_schema.apply_async(args=(ds.id,), queue="schemas") logger.info(u"task=refresh_schemas state=finish total_runtime=%.2f", time.time() - global_start_time) diff --git a/tests/tasks/test_refresh_schemas.py b/tests/tasks/test_refresh_schemas.py index 925176136b..809ecd5e5f 100644 --- a/tests/tasks/test_refresh_schemas.py +++ b/tests/tasks/test_refresh_schemas.py @@ -1,25 +1,27 @@ import datetime -from mock import patch, call, ANY + +from mock import ANY, call, patch from tests import BaseTestCase + from redash.tasks import refresh_schemas class TestRefreshSchemas(BaseTestCase): def test_calls_refresh_of_all_data_sources(self): self.factory.data_source # trigger creation - with patch('redash.models.DataSource.get_schema') as get_schema: + with patch('redash.tasks.queries.refresh_schema.apply_async') as refresh_job: refresh_schemas() - get_schema.assert_called_with(refresh=True) + refresh_job.assert_called() def test_skips_paused_data_sources(self): self.factory.data_source.pause() - with patch('redash.models.DataSource.get_schema') as get_schema: + with patch('redash.tasks.queries.refresh_schema.apply_async') as refresh_job: refresh_schemas() - get_schema.assert_not_called() + refresh_job.assert_not_called() self.factory.data_source.resume() - with patch('redash.models.DataSource.get_schema') as get_schema: + with patch('redash.tasks.queries.refresh_schema.apply_async') as refresh_job: refresh_schemas() - get_schema.assert_called_with(refresh=True) + refresh_job.assert_called() From 6b7234c910410d8bb90b8c1b9f95f298b928d775 Mon Sep 17 00:00:00 2001 From: Alexander Leibzon Date: Thu, 18 May 2017 14:00:13 +0300 Subject: [PATCH 087/243] fixes --- redash/query_runner/memsql_ds.py | 32 +++++++++++++++----------------- requirements.txt | 2 -- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/redash/query_runner/memsql_ds.py b/redash/query_runner/memsql_ds.py index ddba85a320..d0dd3735a4 100644 --- a/redash/query_runner/memsql_ds.py +++ b/redash/query_runner/memsql_ds.py @@ -61,7 +61,8 @@ def configuration_schema(cls): } }, - "required": ["host", "port"] + "required": ["host", "port"], + "secret": ["password"] } @classmethod @@ -80,25 +81,22 @@ def __init__(self, configuration): super(MemSQL, self).__init__(configuration) def _get_tables(self, schema): - try: - schemas_query = "show schemas" + schemas_query = "show schemas" - tables_query = "show tables in %s" + tables_query = "show tables in %s" - columns_query = "show columns in %s" + columns_query = "show columns in %s" - for schema_name in filter(lambda a: len(a) > 0, - map(lambda a: str(a['Database']), self._run_query_internal(schemas_query))): - for table_name in filter(lambda a: len(a) > 0, map(lambda a: str(a['Tables_in_%s' % schema_name]), - self._run_query_internal( - tables_query % schema_name))): - table_name = '.'.join((schema_name, table_name)) - columns = filter(lambda a: len(a) > 0, map(lambda a: str(a['Field']), - self._run_query_internal(columns_query % table_name))) + for schema_name in filter(lambda a: len(a) > 0, + map(lambda a: str(a['Database']), self._run_query_internal(schemas_query))): + for table_name in filter(lambda a: len(a) > 0, map(lambda a: str(a['Tables_in_%s' % schema_name]), + self._run_query_internal( + tables_query % schema_name))): + table_name = '.'.join((schema_name, table_name)) + columns = filter(lambda a: len(a) > 0, map(lambda a: str(a['Field']), + self._run_query_internal(columns_query % table_name))) - schema[table_name] = {'name': table_name, 'columns': columns} - except Exception, e: - raise sys.exc_info()[1], None, sys.exc_info()[2] + schema[table_name] = {'name': table_name, 'columns': columns} return schema.values() def run_query(self, query, user): @@ -134,7 +132,7 @@ def run_query(self, query, user): columns.append({ 'name': column, 'friendly_name': column, - 'type': None + 'type': TYPE_STRING }) data = {'columns': columns, 'rows': rows} diff --git a/requirements.txt b/requirements.txt index 8fac8920f6..b703b559c4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -42,6 +42,4 @@ xlsxwriter==0.9.3 pystache==0.5.4 parsedatetime==2.1 cryptography==1.4 -oauthlib==2.0.0 -WTForms==2.1 simplejson==3.10.0 From beb29c66c257961db7b2297bf2943092b15b756c Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 18 May 2017 14:47:17 +0300 Subject: [PATCH 088/243] Scheduled queries improvements: * Schedule queries with parameters using the default value. * Keep track of last execution (including failed ones) for scheduling purposes. --- redash/models.py | 71 ++++++++++++++++++++++++++++------------ redash/tasks/queries.py | 13 +++++++- redash/utils/__init__.py | 11 +++++-- tests/test_models.py | 21 ++++++++---- 4 files changed, 85 insertions(+), 31 deletions(-) diff --git a/redash/models.py b/redash/models.py index 2473542c5c..471f993473 100644 --- a/redash/models.py +++ b/redash/models.py @@ -6,6 +6,7 @@ import itertools import json import logging +import time from funcy import project @@ -16,7 +17,7 @@ from redash import redis_connection, utils from redash.destinations import (get_configuration_schema_for_destination_type, get_destination) -from redash.metrics import database +from redash.metrics import database # noqa: F401 from redash.permissions import has_access, view_only from redash.query_runner import (get_configuration_schema_for_query_runner_type, get_query_runner) @@ -28,15 +29,39 @@ from sqlalchemy.ext.mutable import Mutable from sqlalchemy.inspection import inspect from sqlalchemy.orm import backref, joinedload, object_session, subqueryload -# noinspection PyUnresolvedReferences -from sqlalchemy.orm.exc import NoResultFound +from sqlalchemy.orm.exc import NoResultFound # noqa: F401 from sqlalchemy.types import TypeDecorator db = SQLAlchemy(session_options={ 'expire_on_commit': False }) + Column = functools.partial(db.Column, nullable=False) + +class ScheduledQueriesExecutions(object): + KEY_NAME = 'sq:executed_at' + + def __init__(self): + self.executions = {} + + def refresh(self): + self.executions = redis_connection.hgetall(self.KEY_NAME) + + def update(self, query_id): + redis_connection.hmset(self.KEY_NAME, { + query_id: time.time() + }) + + def get(self, query_id): + timestamp = self.executions.get(str(query_id)) + if timestamp: + timestamp = utils.dt_from_timestamp(timestamp) + + return timestamp + +scheduled_queries_executions = ScheduledQueriesExecutions() + # AccessPermission and Change use a 'generic foreign key' approach to refer to # either queries or dashboards. # TODO replace this with association tables. @@ -329,7 +354,7 @@ class User(TimestampMixin, db.Model, BelongsToOrgMixin, UserMixin, PermissionsCh name = Column(db.String(320)) email = Column(db.String(320)) password_hash = Column(db.String(128), nullable=True) - #XXX replace with association table + # XXX replace with association table group_ids = Column('groups', MutableList.as_mutable(postgresql.ARRAY(db.Integer)), nullable=True) api_key = Column(db.String(40), default=lambda: generate_token(40), @@ -554,7 +579,7 @@ def query_runner(self): def get_by_name(cls, name): return cls.query.filter(cls.name == name).one() - #XXX examine call sites to see if a regular SQLA collection would work better + # XXX examine call sites to see if a regular SQLA collection would work better @property def groups(self): groups = db.session.query(DataSourceGroup).filter( @@ -563,7 +588,7 @@ def groups(self): class DataSourceGroup(db.Model): - #XXX drop id, use datasource/group as PK + # XXX drop id, use datasource/group as PK id = Column(db.Integer, primary_key=True) data_source_id = Column(db.Integer, db.ForeignKey("data_sources.id")) data_source = db.relationship(DataSource, back_populates="data_source_groups") @@ -821,7 +846,7 @@ def all_queries(cls, group_ids, user_id=None, drafts=False): joinedload(Query.latest_query_data).load_only('runtime', 'retrieved_at')) .join(DataSourceGroup, Query.data_source_id == DataSourceGroup.data_source_id) .filter(Query.is_archived == False) - .filter(DataSourceGroup.group_id.in_(group_ids))\ + .filter(DataSourceGroup.group_id.in_(group_ids)) .order_by(Query.created_at.desc())) if not drafts: @@ -842,12 +867,16 @@ def outdated_queries(cls): now = utils.utcnow() outdated_queries = {} + scheduled_queries_executions.refresh() + for query in queries: if query.latest_query_data: retrieved_at = query.latest_query_data.retrieved_at else: retrieved_at = now + retrieved_at = scheduled_queries_executions.get(query.id) or retrieved_at + if should_schedule_next(retrieved_at, now, query.schedule, query.schedule_failures): key = "{}:{}".format(query.query_hash, query.data_source_id) outdated_queries[key] = query @@ -966,11 +995,11 @@ class AccessPermission(GFKBase, db.Model): @classmethod def grant(cls, obj, access_type, grantee, grantor): - grant = cls.query.filter(cls.object_type==obj.__tablename__, - cls.object_id==obj.id, - cls.access_type==access_type, - cls.grantee==grantee, - cls.grantor==grantor).one_or_none() + grant = cls.query.filter(cls.object_type == obj.__tablename__, + cls.object_id == obj.id, + cls.access_type == access_type, + cls.grantee == grantee, + cls.grantor == grantor).one_or_none() if not grant: grant = cls(object_type=obj.__tablename__, @@ -1084,12 +1113,12 @@ def all(cls, group_ids): return db.session.query(Alert)\ .options(joinedload(Alert.user), joinedload(Alert.query_rel))\ .join(Query)\ - .join(DataSourceGroup, DataSourceGroup.data_source_id==Query.data_source_id)\ + .join(DataSourceGroup, DataSourceGroup.data_source_id == Query.data_source_id)\ .filter(DataSourceGroup.group_id.in_(group_ids)) @classmethod def get_by_id_and_org(cls, id, org): - return db.session.query(Alert).join(Query).filter(Alert.id==id, Query.org==org).one() + return db.session.query(Alert).join(Query).filter(Alert.id == id, Query.org == org).one() def to_dict(self, full=True): d = { @@ -1271,7 +1300,7 @@ def recent(cls, org, group_ids, user_id, for_user=False, limit=20): @classmethod def get_by_slug_and_org(cls, slug, org): - return cls.query.filter(cls.slug == slug, cls.org==org).one() + return cls.query.filter(cls.slug == slug, cls.org == org).one() def __unicode__(self): return u"%s=%s" % (self.id, self.name) @@ -1360,7 +1389,7 @@ def __unicode__(self): @classmethod def get_by_id_and_org(cls, widget_id, org): - return db.session.query(cls).join(Dashboard).filter(cls.id == widget_id, Dashboard.org== org).one() + return db.session.query(cls).join(Dashboard).filter(cls.id == widget_id, Dashboard.org == org).one() class Event(db.Model): @@ -1415,7 +1444,7 @@ class ApiKey(TimestampMixin, GFKBase, db.Model): org = db.relationship(Organization) api_key = Column(db.String(255), index=True, default=lambda: generate_token(40)) active = Column(db.Boolean, default=True) - #'object' provided by GFKBase + # 'object' provided by GFKBase created_by_id = Column(db.Integer, db.ForeignKey("users.id"), nullable=True) created_by = db.relationship(User) @@ -1424,11 +1453,11 @@ class ApiKey(TimestampMixin, GFKBase, db.Model): @classmethod def get_by_api_key(cls, api_key): - return cls.query.filter(cls.api_key==api_key, cls.active==True).one() + return cls.query.filter(cls.api_key == api_key, cls.active == True).one() @classmethod def get_by_object(cls, object): - return cls.query.filter(cls.object_type==object.__class__.__tablename__, cls.object_id==object.id, cls.active==True).first() + return cls.query.filter(cls.object_type == object.__class__.__tablename__, cls.object_id == object.id, cls.active == True).first() @classmethod def create_for_object(cls, object, user): @@ -1475,7 +1504,7 @@ def destination(self): @classmethod def all(cls, org): - notification_destinations = cls.query.filter(cls.org==org).order_by(cls.id.asc()) + notification_destinations = cls.query.filter(cls.org == org).order_by(cls.id.asc()) return notification_destinations @@ -1567,6 +1596,6 @@ def init_db(): default_group = Group(name='default', permissions=Group.DEFAULT_PERMISSIONS, org=default_org, type=Group.BUILTIN_GROUP) db.session.add_all([default_org, admin_group, default_group]) - #XXX remove after fixing User.group_ids + # XXX remove after fixing User.group_ids db.session.commit() return default_org, admin_group, default_group diff --git a/redash/tasks/queries.py b/redash/tasks/queries.py index 9dea6b1d88..6c90c3a12b 100644 --- a/redash/tasks/queries.py +++ b/redash/tasks/queries.py @@ -3,6 +3,7 @@ import signal import time +import pystache import redis from celery.exceptions import SoftTimeLimitExceeded @@ -271,7 +272,15 @@ def refresh_queries(): elif query.data_source.paused: logging.info("Skipping refresh of %s because datasource - %s is paused (%s).", query.id, query.data_source.name, query.data_source.pause_reason) else: - enqueue_query(query.query_text, query.data_source, query.user_id, + # if query.options and 'parameters' in query.options and len(query.options['parameters']) > 0: + if query.options and len(query.options.get('parameters', [])) > 0: + query_params = {p['name']: p['value'] + for p in query.options['parameters']} + query_text = pystache.render(query.query_text, query_params) + else: + query_text = query.query_text + + enqueue_query(query_text, query.data_source, query.user_id, scheduled_query=query, metadata={'Query ID': query.id, 'Username': 'Scheduled'}) @@ -410,6 +419,8 @@ def __init__(self, task, query, data_source_id, user_id, metadata, self.query_hash, self.data_source_id, False, metadata) + if self.tracker.scheduled: + models.scheduled_queries_executions.update(self.tracker.query_id) def run(self): signal.signal(signal.SIGINT, signal_handler) diff --git a/redash/utils/__init__.py b/redash/utils/__init__.py index cd16f470ac..e9c19d68ad 100644 --- a/redash/utils/__init__.py +++ b/redash/utils/__init__.py @@ -28,6 +28,15 @@ def utcnow(): return datetime.datetime.now(pytz.utc) +def dt_from_timestamp(timestamp, tz_aware=True): + timestamp = datetime.datetime.utcfromtimestamp(float(timestamp)) + + if tz_aware: + timestamp = timestamp.replace(tzinfo=pytz.utc) + + return timestamp + + def slugify(s): return re.sub('[^a-z0-9_\-]+', '-', s.lower()) @@ -156,5 +165,3 @@ def base_url(org): return "https://{}/{}".format(settings.HOST, org.slug) return settings.HOST - - diff --git a/tests/test_models.py b/tests/test_models.py index 81597def24..f2c8770208 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -2,9 +2,11 @@ import datetime import json from unittest import TestCase + import mock from dateutil.parser import parse as date_parse from tests import BaseTestCase + from redash import models from redash.models import db from redash.utils import gen_query_hash, utcnow @@ -79,12 +81,6 @@ def test_outdated_queries_skips_unscheduled_queries(self): self.assertNotIn(query, queries) - def test_outdated_queries_skips_unscheduled_queries(self): - query = self.factory.create_query(schedule='60') - queries = models.Query.outdated_queries() - - self.assertNotIn(query, queries) - def test_outdated_queries_works_with_ttl_based_schedule(self): two_hours_ago = utcnow() - datetime.timedelta(hours=2) query = self.factory.create_query(schedule="3600") @@ -94,6 +90,17 @@ def test_outdated_queries_works_with_ttl_based_schedule(self): queries = models.Query.outdated_queries() self.assertIn(query, queries) + def test_outdated_queries_works_scheduled_queries_tracker(self): + two_hours_ago = datetime.datetime.now() - datetime.timedelta(hours=2) + query = self.factory.create_query(schedule="3600") + query_result = self.factory.create_query_result(query=query, retrieved_at=two_hours_ago) + query.latest_query_data = query_result + + models.scheduled_queries_executions.update(query.id) + + queries = models.Query.outdated_queries() + self.assertNotIn(query, queries) + def test_skips_fresh_queries(self): half_an_hour_ago = utcnow() - datetime.timedelta(minutes=30) query = self.factory.create_query(schedule="3600") @@ -450,7 +457,7 @@ def test_doesnt_update_queries_with_different_hash(self): query_result, _ = models.QueryResult.store_result( self.data_source.org, self.data_source, self.query_hash, self.query, self.data, self.runtime, self.utcnow) - + self.assertEqual(query1.latest_query_data, query_result) self.assertEqual(query2.latest_query_data, query_result) self.assertNotEqual(query3.latest_query_data, query_result) From e85e962466d2eb0af4fb11c541362aa4538f490b Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 18 May 2017 15:37:14 +0300 Subject: [PATCH 089/243] Update CHANGELOG and bump version --- CHANGELOG.md | 362 ++++++++++++++++++++++++++------------------- package.json | 2 +- redash/__init__.py | 2 +- 3 files changed, 211 insertions(+), 155 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3437cf1b0..8a67b8a149 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,61 @@ # Change Log +## v2.0.0 - UNRELEASED + +### Added + +- [Cassandra] Support for UUID serializing and setting protocol version. @mfouilleul +- [BigQuery] Add maximumBillingTier to BigQuery configuration. @dotneet +- Add the propertyOrder field to specify order of data source settings. @rmakulov +- Add Plotly based Boxplot visualization. @deecay +- [Presto] Add: query cancellation support. @fbertsch +- [MongoDB] add $oids JSON extension. +- [PostgreSQL] support for loading materialized views in schema. +- [MySQL] Add option to hide SSL settings. +- [MySQL] support for RDS MySQL and SSL. +- [Google Analytics] support for mcf queries & better errors. +- Add: static enum parameter type. @rockwotj +- Add: option to hide pivot table controls. @deecay +- Retry reload of query results if it had an error. +- [Data Sources] Add: MemSQL query runner. @alexanderlz + +### Changed + +- Upgrade Google API client library for all Google data sources. @ahamino +- [JIRA JQL] change default max results limit from 50 to 1000. @jvanegmond +- Upgrade to newer Plotly version. @deecay +- [Athena] Configuration flag to disable query annotations for Athena. @suemoc +- Ignore extra columns in CSV output. @alexanderlz +- [TreasureData] improve error handling and upgrade client. +- [InfluxDB] simpler test connection query (show databases requires admin). +- [MSSQL] Mark integers as decimals as well, as sometimes decimal columns being returned + with integer column type. +- [Google Spreadsheets] add timeout to requests. +- Sort dashboards list by name. @deecay +- Include Celery task name in statsd metrics. +- Don't include paused datasource's queries in outdated queries count. +- Cohort: handle the case where the value/total might be strings. +- Query results: better type guessing on the client side. +- Counter: support negative indexes to iterate from the end of the results. +- Data sources and destinations configuration: change order of name and type (type first now). +- Show API Key in a modal dialog instead of alert. +- Sentry: upgrade client version. +- Sentry: don't install logging hook. +- Split refresh schemas into separate tasks and add a timeout. +- Execute scheduled queries with parameters using their default value. +- Keep track of last query execution (including failed ones) for scheduling purposes. + +### Fixed + +- Fix: set default values in options to enable 'default: True' for checkbox. @rmakulov +- Support MULTI_ORG again. +- [Google Spreadsheets] handle distant future dates. +- [SQLite] better handle utf-8 error messages. +- Fix: don't remove locks for queries with task status of PENDING. +- Only split columns with __/:: that end with filter/MultiFilter. + +### Other + ## v1.0.3 - 2017-04-18 ### Fixed @@ -100,7 +156,7 @@ ## v1.0.0-rc.1 - 2017-01-31 This version has two big changes behind the scenes: - + * Refactor the frontend to use latest (at the time) Angular version (1.5) along with better frontend pipeline based on WebPack. * Refactor the backend code to use SQLAlchemy and Alembic, for easier migrations/upgrades. @@ -186,109 +242,109 @@ We're releasing a new upgrade script -- see [here](https://redash.io/help-onprem ### Added -61fe16e #1374: Add: allow '*' in REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN (Allen Short) -2f09043 #1113: Add: share modify/access permissions for queries and dashboard (whummer) -3db0eea #1341: Add: support for specifying SAML nameid-format (zoetrope) -b0ecd0e #1343: Add: support for local SAML metadata file (zoetrope) -0235d37 #1335: Add: allow changing alert email subject. (Arik Fraimovich) -2135dfd #1333: Add: control over y axis min/max values (Arik Fraimovich) -49e788a #1328: Add: support for snapshot generation service (Arik Fraimovich) -229ca6c #1323: Add: collect runtime metrics for Celery tasks (Arik Fraimovich) -931a1f3 #1315: Add: support for loading BigQuery schema (Arik Fraimovich) -39b4f9a #1314: Add: support MongoDB SSL connections (Arik Fraimovich) -ca1ca9b #1312: Add: additional configuration for Celery jobs (Arik Fraimovich) -fc00e61 #1310: Add: support for date/time with seconds parameters (Arik Fraimovich) -d72a198 #1307: Add: API to force refresh data source schema (Arik Fraimovich) -beb89ec #1305: Add: UI to edit dashboard text box widget (Kazuhito Hokamura) -808fdd4 #1298: Add: JIRA (JQL) query runner (Arik Fraimovich) -ff9e844 #1280: Add: configuration flag to disable scheduled queries (Hirotaka Suzuki) -ef4699a #1269: Add: Google Drive federated tables support in BigQuery query runner (Kurt Gooden) -2eeb947 #1236: Add: query runner for Cassandra and ScyllaDB (syerushalmy) -10b398e #1249: Add: override slack webhook parameters (mystelynx) -2b5e340 #1252: Add: Schema loading support for Presto query runner (using information_schema) (Rohan Dhupelia) -2aaf5dd #1250: Add: query snippets feature (Arik Fraimovich) -8d8af73 #1226: Add: Sankey visualization (Arik Fraimovich) -a02edda #1222: Add: additional results format for sunburst visualization (Arik Fraimovich) -0e70188 #1213: Add: new sunburst sequence visualization (Arik Fraimovich) -9a6d2d7 #1204: Add: show views in schema browser for Vertica data sources (Matthew Carter) -600afa5 #1138: Add: ability to register user defined function (UDF) resources for BigQuery DataSource/Query (fabito) -b410410 #1166: Add: "every 14 days" refresh option (Arik Fraimovich) +61fe16e #1374: Add: allow '*' in REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN (Allen Short) +2f09043 #1113: Add: share modify/access permissions for queries and dashboard (whummer) +3db0eea #1341: Add: support for specifying SAML nameid-format (zoetrope) +b0ecd0e #1343: Add: support for local SAML metadata file (zoetrope) +0235d37 #1335: Add: allow changing alert email subject. (Arik Fraimovich) +2135dfd #1333: Add: control over y axis min/max values (Arik Fraimovich) +49e788a #1328: Add: support for snapshot generation service (Arik Fraimovich) +229ca6c #1323: Add: collect runtime metrics for Celery tasks (Arik Fraimovich) +931a1f3 #1315: Add: support for loading BigQuery schema (Arik Fraimovich) +39b4f9a #1314: Add: support MongoDB SSL connections (Arik Fraimovich) +ca1ca9b #1312: Add: additional configuration for Celery jobs (Arik Fraimovich) +fc00e61 #1310: Add: support for date/time with seconds parameters (Arik Fraimovich) +d72a198 #1307: Add: API to force refresh data source schema (Arik Fraimovich) +beb89ec #1305: Add: UI to edit dashboard text box widget (Kazuhito Hokamura) +808fdd4 #1298: Add: JIRA (JQL) query runner (Arik Fraimovich) +ff9e844 #1280: Add: configuration flag to disable scheduled queries (Hirotaka Suzuki) +ef4699a #1269: Add: Google Drive federated tables support in BigQuery query runner (Kurt Gooden) +2eeb947 #1236: Add: query runner for Cassandra and ScyllaDB (syerushalmy) +10b398e #1249: Add: override slack webhook parameters (mystelynx) +2b5e340 #1252: Add: Schema loading support for Presto query runner (using information_schema) (Rohan Dhupelia) +2aaf5dd #1250: Add: query snippets feature (Arik Fraimovich) +8d8af73 #1226: Add: Sankey visualization (Arik Fraimovich) +a02edda #1222: Add: additional results format for sunburst visualization (Arik Fraimovich) +0e70188 #1213: Add: new sunburst sequence visualization (Arik Fraimovich) +9a6d2d7 #1204: Add: show views in schema browser for Vertica data sources (Matthew Carter) +600afa5 #1138: Add: ability to register user defined function (UDF) resources for BigQuery DataSource/Query (fabito) +b410410 #1166: Add: "every 14 days" refresh option (Arik Fraimovich) 906365f #967: Add: extend ElasticSearch query_runner to support aggregations (lloydw) ### Changed 2de4aa2 #1395: Change: switch to requests in URL query runner (Arik Fraimovich) db1a941 #1392: Change: Update documentation links to point at the new location. (Arik Fraimovich) -002f794 #1368: Change: added ability to disable auto update in admin views (Arik Fraimovich) -aa5d14e #1366: Change: improve error message for exception in the Python query runner (deecay) -880627c #1355: Change: pass the user object to the run_query method (Arik Fraimovich) -23c605b #1342: SAML: specify entity id (zoetrope) -015b1dc #1334: Change: allow specifying recipient address when sending email test message (Arik Fraimovich) -39aaa2f #1292: Change: improvements to map visualization (Arik Fraimovich) -b22191b #1332: Change: upgrade Python packages (Arik Fraimovich) -23ba98b #1331: Celery: Upgrade Celery to more recent version. (Arik Fraimovich) -3283116 #1330: Change: upgrade Requests to latest version. (Arik Fraimovich) -39091e0 #1324: Change: add more logging and information for refresh schemas task (Arik Fraimovich) -462faea #1316: Change: remove deprecated settings (Arik Fraimovich) -73e1837 #1313: Change: more flexible column width calculation (Arik Fraimovich) -e8eb840 #1279: Change: update bootstrap.sh to support Ubuntu 16.04 (IllusiveMilkman) -8cf0252 #1262: Change: upgrade Plot.ly version and switch to smaller build (Arik Fraimovich) -0b79fb8 #1306: Change: paginate queries page & add explicit urls. (Arik Fraimovich) -41f99f5 #1299: Change: send Content-Type header (application/json) in query results responses (Tsuyoshi Tatsukawa) -dfb1a20 #1297: Change: update Slack configuration titles. (Arik Fraimovich) -8c1056c #1294: Change: don't annotate BigQuery queries (Arik Fraimovich) -a3cf92e #1289: Change: use key_as_string when available (ElasticSearch query runner) (Arik Fraimovich) -e155191 #1285: Change: do not display Oracle tablespace name in schema browser (Matthew Carter) -6cbc39c #1282: Change: deduplicate Google Spreadsheet columns (Arik Fraimovich) -4caf2e3 #1277: Set specific version of cryptography lib (Arik Fraimovich) -d22f0d4 #1216: Change: bootstrap.sh - use non interactive dist-upgrade (Atsushi Sasaki) -19530f4 #1245: Change: switch from CodeMirror to Ace editor (Arik Fraimovich) -dfb92db #1234: Change: MongoDB query runner set DB name as mandatory (Arik Fraimovich) -b750843 #1230: Change: annotate Presto queries with metadata (Noriaki Katayama) -5b20fe2 #1217: Change: install libffi-dev for Cryptography (Ubuntu setup script) (Atsushi Sasaki) -a9fac34 #1206: Change: update pymssql version to 2.1.3 (kitsuyui) -5d43cbe #1198: Change: add support for Standard SQL in BigQuery query runner (mystelynx) -84d0c22 #1193: Change: modify the argument order of moment.add function call (Kenya Yamaguchi) +002f794 #1368: Change: added ability to disable auto update in admin views (Arik Fraimovich) +aa5d14e #1366: Change: improve error message for exception in the Python query runner (deecay) +880627c #1355: Change: pass the user object to the run_query method (Arik Fraimovich) +23c605b #1342: SAML: specify entity id (zoetrope) +015b1dc #1334: Change: allow specifying recipient address when sending email test message (Arik Fraimovich) +39aaa2f #1292: Change: improvements to map visualization (Arik Fraimovich) +b22191b #1332: Change: upgrade Python packages (Arik Fraimovich) +23ba98b #1331: Celery: Upgrade Celery to more recent version. (Arik Fraimovich) +3283116 #1330: Change: upgrade Requests to latest version. (Arik Fraimovich) +39091e0 #1324: Change: add more logging and information for refresh schemas task (Arik Fraimovich) +462faea #1316: Change: remove deprecated settings (Arik Fraimovich) +73e1837 #1313: Change: more flexible column width calculation (Arik Fraimovich) +e8eb840 #1279: Change: update bootstrap.sh to support Ubuntu 16.04 (IllusiveMilkman) +8cf0252 #1262: Change: upgrade Plot.ly version and switch to smaller build (Arik Fraimovich) +0b79fb8 #1306: Change: paginate queries page & add explicit urls. (Arik Fraimovich) +41f99f5 #1299: Change: send Content-Type header (application/json) in query results responses (Tsuyoshi Tatsukawa) +dfb1a20 #1297: Change: update Slack configuration titles. (Arik Fraimovich) +8c1056c #1294: Change: don't annotate BigQuery queries (Arik Fraimovich) +a3cf92e #1289: Change: use key_as_string when available (ElasticSearch query runner) (Arik Fraimovich) +e155191 #1285: Change: do not display Oracle tablespace name in schema browser (Matthew Carter) +6cbc39c #1282: Change: deduplicate Google Spreadsheet columns (Arik Fraimovich) +4caf2e3 #1277: Set specific version of cryptography lib (Arik Fraimovich) +d22f0d4 #1216: Change: bootstrap.sh - use non interactive dist-upgrade (Atsushi Sasaki) +19530f4 #1245: Change: switch from CodeMirror to Ace editor (Arik Fraimovich) +dfb92db #1234: Change: MongoDB query runner set DB name as mandatory (Arik Fraimovich) +b750843 #1230: Change: annotate Presto queries with metadata (Noriaki Katayama) +5b20fe2 #1217: Change: install libffi-dev for Cryptography (Ubuntu setup script) (Atsushi Sasaki) +a9fac34 #1206: Change: update pymssql version to 2.1.3 (kitsuyui) +5d43cbe #1198: Change: add support for Standard SQL in BigQuery query runner (mystelynx) +84d0c22 #1193: Change: modify the argument order of moment.add function call (Kenya Yamaguchi) ### Fixed -d6febb0 #1375: Fix: Download Dataset does not work when not logged in (Joshua Dechant) -96553ad #1369: Fix: missing format call in Elasticsearch test method (Adam Griffiths) -c57c765 #1365: Fix: compare retrieval times in UTC timezone (Allen Short) -37dff5f #1360: Fix: connection test was broken for MySQL (ichihara) -360028c #1359: Fix: schema loading query for Hive was wrong for non default schema (laughingman7743) -7ee41d4 #1358: Fix: make sure all calls to run_query updated with new parameter (Arik Fraimovich) -0d94479 #1329: Fix: Redis memory leak. (Arik Fraimovich) -7145aa2 #1325: Fix: queries API was doing N+1 queries in most cases (Arik Fraimovich) -cd2e927 #1311: Fix: BoxPlot visualization wasn't rendering on a dashboard (Arik Fraimovich) -a562ce7 #1309: Fix: properly render checkboxes in dynamic forms (Arik Fraimovich) -d48192c #1308: Fix: support for Unicode columns name in Google Spreadsheets (Arik Fraimovich) -e42f93f #1283: Fix: schema browser was unstable after opening a table (Arik Fraimovich) -170bd65 #1272: Fix: TreasureData get_schema method was returning array instead of string as column name (ariarijp) -4710c41 #1265: Fix: refresh modal not working for unsaved query (Arik Fraimovich) -bc3a5ab #1264: Fix: dashboard refresh not working (Arik Fraimovich) -6202d09 #1240: Fix: when shared dashboard token not found, return 404 (Wesley Batista) -93aac14 #1251: Fix: autocomplete went crazy when database has no autocomplete. (Arik Fraimovich) -b8eca28 #1246: Fix: support large schemas in schema browser (Arik Fraimovich) -b781003 #1223: Fix: Alert: when hipchat Alert.name is multibyte character, occur error. (toyama0919) -0b928e6 #1227: Fix: Bower install fails in vagrant (Kazuhito Hokamura) -a411af2 #1232: Fix: don't show warning when query string (parameters value) changes (Kazuhito Hokamura) -3dbb5a6 #1221: Fix: sunburst didn't handle all cases of path lengths (Arik Fraimovich) -a7cc1ee #1218: Fix: updated result not being saved when changing query text. (Arik Fraimovich) -0617833 #1215: Fix: email alerts not working (Arik Fraimovich) -78f65b1 #1187: Fix: read only users receive the permission error modal in query view (Arik Fraimovich) -bba801f #1167: Fix the version of setuptools on bootstrap script for Ubuntu (Takuya Arita) -ce81d69 #1160: Fix indentation in docker-compose-example.yml (Hirofumi Wakasugi) -dd759fe #1155: Fix: make all configuration values of Oracle required (Arik Fraimovich) +d6febb0 #1375: Fix: Download Dataset does not work when not logged in (Joshua Dechant) +96553ad #1369: Fix: missing format call in Elasticsearch test method (Adam Griffiths) +c57c765 #1365: Fix: compare retrieval times in UTC timezone (Allen Short) +37dff5f #1360: Fix: connection test was broken for MySQL (ichihara) +360028c #1359: Fix: schema loading query for Hive was wrong for non default schema (laughingman7743) +7ee41d4 #1358: Fix: make sure all calls to run_query updated with new parameter (Arik Fraimovich) +0d94479 #1329: Fix: Redis memory leak. (Arik Fraimovich) +7145aa2 #1325: Fix: queries API was doing N+1 queries in most cases (Arik Fraimovich) +cd2e927 #1311: Fix: BoxPlot visualization wasn't rendering on a dashboard (Arik Fraimovich) +a562ce7 #1309: Fix: properly render checkboxes in dynamic forms (Arik Fraimovich) +d48192c #1308: Fix: support for Unicode columns name in Google Spreadsheets (Arik Fraimovich) +e42f93f #1283: Fix: schema browser was unstable after opening a table (Arik Fraimovich) +170bd65 #1272: Fix: TreasureData get_schema method was returning array instead of string as column name (ariarijp) +4710c41 #1265: Fix: refresh modal not working for unsaved query (Arik Fraimovich) +bc3a5ab #1264: Fix: dashboard refresh not working (Arik Fraimovich) +6202d09 #1240: Fix: when shared dashboard token not found, return 404 (Wesley Batista) +93aac14 #1251: Fix: autocomplete went crazy when database has no autocomplete. (Arik Fraimovich) +b8eca28 #1246: Fix: support large schemas in schema browser (Arik Fraimovich) +b781003 #1223: Fix: Alert: when hipchat Alert.name is multibyte character, occur error. (toyama0919) +0b928e6 #1227: Fix: Bower install fails in vagrant (Kazuhito Hokamura) +a411af2 #1232: Fix: don't show warning when query string (parameters value) changes (Kazuhito Hokamura) +3dbb5a6 #1221: Fix: sunburst didn't handle all cases of path lengths (Arik Fraimovich) +a7cc1ee #1218: Fix: updated result not being saved when changing query text. (Arik Fraimovich) +0617833 #1215: Fix: email alerts not working (Arik Fraimovich) +78f65b1 #1187: Fix: read only users receive the permission error modal in query view (Arik Fraimovich) +bba801f #1167: Fix the version of setuptools on bootstrap script for Ubuntu (Takuya Arita) +ce81d69 #1160: Fix indentation in docker-compose-example.yml (Hirofumi Wakasugi) +dd759fe #1155: Fix: make all configuration values of Oracle required (Arik Fraimovich) ### Docs -a69ee0c #1225: Fix: RST formatting of the Vagrant documentation (Kazuhito Hokamura) -03837c0 #1242: Docs: add warning re. quotes on column names and BigQuery (Ereli) -9a98075 #1255: Docs: add documentation for InfluxDB (vishesh92) -e0485de #1195: Docs: fix typo in maintenance page title (Antoine Augusti) -7681d3e #1164: Docs: update permission documentation (Daniel Darabos) -bcd3670 #1156: Docs: add SSL parameters to nginx configuration (Josh Cox) +a69ee0c #1225: Fix: RST formatting of the Vagrant documentation (Kazuhito Hokamura) +03837c0 #1242: Docs: add warning re. quotes on column names and BigQuery (Ereli) +9a98075 #1255: Docs: add documentation for InfluxDB (vishesh92) +e0485de #1195: Docs: fix typo in maintenance page title (Antoine Augusti) +7681d3e #1164: Docs: update permission documentation (Daniel Darabos) +bcd3670 #1156: Docs: add SSL parameters to nginx configuration (Josh Cox) ## v0.11.1.b2095 - 2016-08-02 @@ -306,73 +362,73 @@ Also, this release includes numerous smaller features, improvements, and bug fix A big thank you goes to all who contributed code and documentation in this release: @AntoineAugusti, @James226, @adamlwgriffiths, @alexdebrie, @anthony-coble, @ariarijp, @dheerajrav, @edwardsharp, @machira, @nabilblk, @ninneko, @ordd, @tomerben, @toru-takahashi, @vishesh92, @vorakumar and @whummer. ### Added -d5e5b24 #1136: Feature: add --org option to all relevant CLI commands. (@adamlwgriffiths) -87e25f2 #1129: Feature: support for JSON query formatting (Mongo, ElasticSearch) (@arikfr) -6bb2716 #1121: Show error when failing to communicate with server (@arikfr) -f21276e #1119: Feature: add UI to delete alerts (@arikfr) -8656540 #1069: Feature: UI for query parameters (@arikfr) -790128c #1067: Feature: word cloud visualization (@anthony-coble) -8b73a2b #1098: Feature: UI for alert destinations & new destination types (@alexdebrie) -1fbeb5d #1092: Add Heroku support (@adamlwgriffiths) -f64622d #1089: Add support for serialising UUID type within MSSQL #961 (@James226) -857caab #1085: Feature: API to pause a data source (@arikfr) -214aa3b #1060: Feature: support configuring user's groups with SAML (@vorakumar) -e20a005 #1007: Issue#1006: Make bottom margin editable for Chart visualization (@vorakumar) -6e0dd2b #1063: Add support for date/time Y axis (@tomerben) -b5a4a6b #979: Feature: Add CLI to edit group permissions (@ninneko) -6d495d2 #1014: Add server-side parameter handling for embeds (@whummer) -5255804 #1091: Add caching for queries used in embeds (@whummer) +d5e5b24 #1136: Feature: add --org option to all relevant CLI commands. (@adamlwgriffiths) +87e25f2 #1129: Feature: support for JSON query formatting (Mongo, ElasticSearch) (@arikfr) +6bb2716 #1121: Show error when failing to communicate with server (@arikfr) +f21276e #1119: Feature: add UI to delete alerts (@arikfr) +8656540 #1069: Feature: UI for query parameters (@arikfr) +790128c #1067: Feature: word cloud visualization (@anthony-coble) +8b73a2b #1098: Feature: UI for alert destinations & new destination types (@alexdebrie) +1fbeb5d #1092: Add Heroku support (@adamlwgriffiths) +f64622d #1089: Add support for serialising UUID type within MSSQL #961 (@James226) +857caab #1085: Feature: API to pause a data source (@arikfr) +214aa3b #1060: Feature: support configuring user's groups with SAML (@vorakumar) +e20a005 #1007: Issue#1006: Make bottom margin editable for Chart visualization (@vorakumar) +6e0dd2b #1063: Add support for date/time Y axis (@tomerben) +b5a4a6b #979: Feature: Add CLI to edit group permissions (@ninneko) +6d495d2 #1014: Add server-side parameter handling for embeds (@whummer) +5255804 #1091: Add caching for queries used in embeds (@whummer) ### Changed -0314313 #1149: Presto QueryRunner supports tinyint and smallint (@toru-takahashi) -8fa6fdb #1030: Make sure data sources list ordered by id (@arikfr) -8df822e #1141: Make create data source button more prominent (@arikfr) -96dd811 #1127: Mark basic_auth_password as secret (@adamlwgriffiths) -ad65391 #1130: Improve Slack notification style (@AntoineAugusti) -df637e3 #1116: Return meaningful error when there is no cached result. (@arikfr) -65635ec #1102: Switch to HipChat V2 API (@arikfr) -14fcf01 #1072: Remove counter from the tasks Done tab (as it always shows 50). #1047 (@arikfr) -1a1160e #1062: DynamoDB: Better exception handling (@arikfr) -ed45dcb #1044: Improve vagrant flow (@staritza) -8b5dc8e #1036: Add optional block for more scripts in template (@arikfr) +0314313 #1149: Presto QueryRunner supports tinyint and smallint (@toru-takahashi) +8fa6fdb #1030: Make sure data sources list ordered by id (@arikfr) +8df822e #1141: Make create data source button more prominent (@arikfr) +96dd811 #1127: Mark basic_auth_password as secret (@adamlwgriffiths) +ad65391 #1130: Improve Slack notification style (@AntoineAugusti) +df637e3 #1116: Return meaningful error when there is no cached result. (@arikfr) +65635ec #1102: Switch to HipChat V2 API (@arikfr) +14fcf01 #1072: Remove counter from the tasks Done tab (as it always shows 50). #1047 (@arikfr) +1a1160e #1062: DynamoDB: Better exception handling (@arikfr) +ed45dcb #1044: Improve vagrant flow (@staritza) +8b5dc8e #1036: Add optional block for more scripts in template (@arikfr) ### Fixed -dbd48e1 #1143: Fix: use the email input type where needed (@ariarijp) -7445972 #1142: Fix: dates in filters might be duplicated (@arikfr) -5d0ed02 #1140: Fix: Hive should use the enabled variable (@arikfr) -392627d #1139: Fix: Impala data source referencing wrong variable (@arikfr) -c5bfbba #1133: Fix: query scrolling issues (@vishesh92) -c01d266 #1128: Fix: visualization options not updating after changing type (@arikfr) -6bc0e7a #1126: Fix #669: save fails when doing partial save of new query (@arikfr) -3ce27b9 #1118: Fix: remove alerts for archived queries (@arikfr) -4fabaae #1117: Fix #1052: filter not working for date/time values (@arikfr) -c107c94 #1077: Fix: install needed dependencies to use Hive in Docker image (@nabilblk) -abc790c #1115: Fix: allow non integers in alert reference value (@arikfr) -4ec473c #1110: Fix #1109: mixed group permissions resulting in wrong permission (@arikfr) -1ca5262 #1099: Fix RST syntax for links (@adamlwgriffiths) -daa6c1c #1096: Fix typo in env variable VERSION_CHECK (@AntoineAugusti) -cd06d27 #1095: Fix: use create_query permission for new query button. (@ordd) -2bc0b27 #1061: Fix: area chart stacking doesn't work (@machira) -8c21e91 #1108: Remove potnetially concurrency not safe code form enqueue_query (@arikfr) -e831218 #1084: Fix #1049: duplicate alerts when data source belongs to multiple groups (@arikfr) -6edb0ca #1080: Fix typo (@jeffwidman) -64d7538 #1074: Fix: ElasticSearch wasn't using correct type names (@toyama0919) -3f90dd9 #1064: Fix: old task trackers were not really removed (@arikfr) -e10ecd2 #1058: Bring back filters if dashboard filters are enabled (@AntoineAugusti) -701035f #1059: Fix: DynamoDB having issues when setting host (@arikfr) -2924d4f #1040: Small fixes to visualizations view (@arikfr) -fec0d5f #1037: Fix: multi filter wasn't working with __ syntax (@dheerajrav) -b066ce4 #1033: Fix: only ask for notification permissions if wasn't denied (@arikfr) -960c416 #1032: Fix: make sure we return dashboards only for current org only (@arikfr) -b3844d3 #1029: Hive: close connection only if it exists (@arikfr) +dbd48e1 #1143: Fix: use the email input type where needed (@ariarijp) +7445972 #1142: Fix: dates in filters might be duplicated (@arikfr) +5d0ed02 #1140: Fix: Hive should use the enabled variable (@arikfr) +392627d #1139: Fix: Impala data source referencing wrong variable (@arikfr) +c5bfbba #1133: Fix: query scrolling issues (@vishesh92) +c01d266 #1128: Fix: visualization options not updating after changing type (@arikfr) +6bc0e7a #1126: Fix #669: save fails when doing partial save of new query (@arikfr) +3ce27b9 #1118: Fix: remove alerts for archived queries (@arikfr) +4fabaae #1117: Fix #1052: filter not working for date/time values (@arikfr) +c107c94 #1077: Fix: install needed dependencies to use Hive in Docker image (@nabilblk) +abc790c #1115: Fix: allow non integers in alert reference value (@arikfr) +4ec473c #1110: Fix #1109: mixed group permissions resulting in wrong permission (@arikfr) +1ca5262 #1099: Fix RST syntax for links (@adamlwgriffiths) +daa6c1c #1096: Fix typo in env variable VERSION_CHECK (@AntoineAugusti) +cd06d27 #1095: Fix: use create_query permission for new query button. (@ordd) +2bc0b27 #1061: Fix: area chart stacking doesn't work (@machira) +8c21e91 #1108: Remove potnetially concurrency not safe code form enqueue_query (@arikfr) +e831218 #1084: Fix #1049: duplicate alerts when data source belongs to multiple groups (@arikfr) +6edb0ca #1080: Fix typo (@jeffwidman) +64d7538 #1074: Fix: ElasticSearch wasn't using correct type names (@toyama0919) +3f90dd9 #1064: Fix: old task trackers were not really removed (@arikfr) +e10ecd2 #1058: Bring back filters if dashboard filters are enabled (@AntoineAugusti) +701035f #1059: Fix: DynamoDB having issues when setting host (@arikfr) +2924d4f #1040: Small fixes to visualizations view (@arikfr) +fec0d5f #1037: Fix: multi filter wasn't working with __ syntax (@dheerajrav) +b066ce4 #1033: Fix: only ask for notification permissions if wasn't denied (@arikfr) +960c416 #1032: Fix: make sure we return dashboards only for current org only (@arikfr) +b3844d3 #1029: Hive: close connection only if it exists (@arikfr) ### Docs -6bb09d8 #1146: Docs: add a link to settings documentation. (@adamlwgriffiths) -095e759 #1103: Docs: add section about monitoring (@AntoineAugusti) -e942486 #1090: Contributing Guide (@arikfr) -3037c4f #1066: Docs: command type-o fix. (@edwardsharp) -2ee0065 #1038: Add an ISSUE_TEMPLATE.md to direct people at the forum (@arikfr) -f7322a4 #1021: Vagrant docs: add purging the cache step (@ariarijp) +6bb09d8 #1146: Docs: add a link to settings documentation. (@adamlwgriffiths) +095e759 #1103: Docs: add section about monitoring (@AntoineAugusti) +e942486 #1090: Contributing Guide (@arikfr) +3037c4f #1066: Docs: command type-o fix. (@edwardsharp) +2ee0065 #1038: Add an ISSUE_TEMPLATE.md to direct people at the forum (@arikfr) +f7322a4 #1021: Vagrant docs: add purging the cache step (@ariarijp) --- diff --git a/package.json b/package.json index f3a53df3bb..00fc14cf6c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redash-client", - "version": "1.0.3", + "version": "2.0.0", "description": "The frontend part of Redash.", "main": "index.js", "scripts": { diff --git a/redash/__init__.py b/redash/__init__.py index 9609e4b32f..63ab2b3cdd 100644 --- a/redash/__init__.py +++ b/redash/__init__.py @@ -17,7 +17,7 @@ from redash.destinations import import_destinations -__version__ = '1.0.3' +__version__ = '2.0.0' def setup_logging(): From f65b3223f404628dbd8f89440477c91662605204 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Mon, 22 May 2017 14:17:27 +0300 Subject: [PATCH 090/243] Remove chatty log lines --- CHANGELOG.md | 2 -- redash/query_runner/google_analytics.py | 1 - redash/query_runner/memsql_ds.py | 4 +--- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a67b8a149..9bbc2a7192 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,8 +54,6 @@ - Fix: don't remove locks for queries with task status of PENDING. - Only split columns with __/:: that end with filter/MultiFilter. -### Other - ## v1.0.3 - 2017-04-18 ### Fixed diff --git a/redash/query_runner/google_analytics.py b/redash/query_runner/google_analytics.py index 0d7d155ad5..225903c427 100644 --- a/redash/query_runner/google_analytics.py +++ b/redash/query_runner/google_analytics.py @@ -18,7 +18,6 @@ import httplib2 enabled = True except ImportError as e: - logger.info(str(e)) enabled = False diff --git a/redash/query_runner/memsql_ds.py b/redash/query_runner/memsql_ds.py index d0dd3735a4..bd3e106c2f 100644 --- a/redash/query_runner/memsql_ds.py +++ b/redash/query_runner/memsql_ds.py @@ -9,10 +9,8 @@ try: from memsql.common import database - enabled = True -except ImportError, e: - logger.warning(e) +except ImportError: enabled = False COLUMN_NAME = 0 From 9c606b9660f3436d26e7723c596286378063ac36 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Mon, 22 May 2017 14:18:34 +0300 Subject: [PATCH 091/243] Update Snowflake connector --- requirements_all_ds.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_all_ds.txt b/requirements_all_ds.txt index 06a4a3f5e4..fc1ea7b521 100644 --- a/requirements_all_ds.txt +++ b/requirements_all_ds.txt @@ -18,7 +18,7 @@ thrift>=0.8.0 thrift_sasl>=0.1.0 cassandra-driver==3.1.1 memsql==2.16.0 -snowflake_connector_python==1.3.7 +snowflake_connector_python==1.3.16 atsd_client==2.0.12 simple_salesforce==0.72.2 # certifi is needed to support MongoDB and SSL: From 8e760705a6acadd103d6dc43947bcb687631355f Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Mon, 22 May 2017 14:26:26 +0300 Subject: [PATCH 092/243] Add: "dumb" recents option In some cases showing recent queries/dashboards based on events becomes too "expensive" in terms of database resources. This is a fallback option to show recently updated queries/dashboards instead. --- redash/handlers/dashboards.py | 22 ++++++++++++++-------- redash/handlers/queries.py | 29 ++++++++++++++++++----------- redash/settings.py | 4 ++-- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/redash/handlers/dashboards.py b/redash/handlers/dashboards.py index ce074b7056..52b75f2a98 100644 --- a/redash/handlers/dashboards.py +++ b/redash/handlers/dashboards.py @@ -1,15 +1,15 @@ from itertools import chain from flask import request, url_for -from flask_restful import abort from funcy import distinct, project, take -from sqlalchemy.orm.exc import StaleDataError -from redash import models, serializers +from flask_restful import abort +from redash import models, serializers, settings from redash.handlers.base import BaseResource, get_object_or_404 from redash.permissions import (can_modify, require_admin_or_owner, require_object_modify_permission, require_permission) +from sqlalchemy.orm.exc import StaleDataError class RecentDashboardsResource(BaseResource): @@ -18,13 +18,19 @@ def get(self): """ Lists dashboards modified in the last 7 days. """ - recent = [d.to_dict() for d in models.Dashboard.recent(self.current_org, self.current_user.group_ids, self.current_user.id, for_user=True)] + if settings.FEATURE_DUMB_RECENTS: + dashboards = models.Dashboard.all(self.current_org, self.current_user.groups, self.current_user).order_by(models.Dashboard.updated_at.desc()).limit(10) + dashboards = [d.to_dict() for d in dashboards] + else: + recent = [d.to_dict() for d in models.Dashboard.recent(self.current_org, self.current_user.group_ids, self.current_user.id, for_user=True)] + + global_recent = [] + if len(recent) < 10: + global_recent = [d.to_dict() for d in models.Dashboard.recent(self.current_org, self.current_user.group_ids, self.current_user.id)] - global_recent = [] - if len(recent) < 10: - global_recent = [d.to_dict() for d in models.Dashboard.recent(self.current_org, self.current_user.group_ids, self.current_user.id)] + dashboards = take(20, distinct(chain(recent, global_recent), key=lambda d: d['id'])) - return take(20, distinct(chain(recent, global_recent), key=lambda d: d['id'])) + return dashboards class DashboardListResource(BaseResource): diff --git a/redash/handlers/queries.py b/redash/handlers/queries.py index 1d4afec0c0..cd7103f826 100644 --- a/redash/handlers/queries.py +++ b/redash/handlers/queries.py @@ -2,12 +2,11 @@ import sqlparse from flask import jsonify, request -from flask_login import login_required -from flask_restful import abort from funcy import distinct, take -from sqlalchemy.orm.exc import StaleDataError -from redash import models +from flask_login import login_required +from flask_restful import abort +from redash import models, settings from redash.handlers.base import (BaseResource, get_object_or_404, org_scoped_rule, paginate, routes) from redash.handlers.query_results import run_query @@ -16,6 +15,7 @@ require_object_modify_permission, require_permission, view_only) from redash.utils import collect_parameters_from_request +from sqlalchemy.orm.exc import StaleDataError @routes.route(org_scoped_rule('/api/queries/format'), methods=['POST']) @@ -57,14 +57,21 @@ def get(self): Responds with a list of :ref:`query ` objects. """ - queries = models.Query.recent(self.current_user.group_ids, self.current_user.id) - recent = [d.to_dict(with_last_modified_by=False) for d in queries] - global_recent = [] - if len(recent) < 10: - global_recent = [d.to_dict(with_last_modified_by=False) for d in models.Query.recent(self.current_user.group_ids)] + if settings.FEATURE_DUMB_RECENTS: + results = models.Query.by_user(self.current_user, False).order_by(models.Query.updated_at.desc()).limit(10) + queries = [q.to_dict(with_last_modified_by=False) for q in results] + else: + queries = models.Query.recent(self.current_user.group_ids, self.current_user.id) + recent = [d.to_dict(with_last_modified_by=False) for d in queries] + + global_recent = [] + if len(recent) < 10: + global_recent = [d.to_dict(with_last_modified_by=False) for d in models.Query.recent(self.current_user.group_ids)] - return take(20, distinct(chain(recent, global_recent), key=lambda d: d['id'])) + queries = take(20, distinct(chain(recent, global_recent), key=lambda d: d['id'])) + + return queries class QueryListResource(BaseResource): @@ -136,7 +143,7 @@ def get(self): Responds with an array of :ref:`query ` objects. """ - + results = models.Query.all_queries(self.current_user.group_ids, self.current_user.id) page = request.args.get('page', 1, type=int) page_size = request.args.get('page_size', 25, type=int) diff --git a/redash/settings.py b/redash/settings.py index 3a4679209f..e0a57efd01 100644 --- a/redash/settings.py +++ b/redash/settings.py @@ -225,8 +225,8 @@ def all_settings(): FEATURE_DISABLE_REFRESH_QUERIES = parse_boolean(os.environ.get("REDASH_FEATURE_DISABLE_REFRESH_QUERIES", "false")) FEATURE_SHOW_QUERY_RESULTS_COUNT = parse_boolean(os.environ.get("REDASH_FEATURE_SHOW_QUERY_RESULTS_COUNT", "true")) FEATURE_SHOW_PERMISSIONS_CONTROL = parse_boolean(os.environ.get("REDASH_FEATURE_SHOW_PERMISSIONS_CONTROL", "false")) -FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS = parse_boolean(os.environ.get("REDASH_FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS", - "false")) +FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS = parse_boolean(os.environ.get("REDASH_FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS", "false")) +FEATURE_DUMB_RECENTS = parse_boolean(os.environ.get("REDASH_FEATURE_DUMB_RECENTS", "false")) # BigQuery BIGQUERY_HTTP_TIMEOUT = int(os.environ.get("REDASH_BIGQUERY_HTTP_TIMEOUT", "600")) From 1f1d7996ec2d9fe4b63cfd15d70b0656ccc96b3c Mon Sep 17 00:00:00 2001 From: Shimpei Kodama Date: Tue, 23 May 2017 12:24:03 +0900 Subject: [PATCH 093/243] Shorten celery task expiry time to avoid too many redis objects --- redash/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redash/settings.py b/redash/settings.py index e529bc881e..e1454db6ae 100644 --- a/redash/settings.py +++ b/redash/settings.py @@ -72,7 +72,7 @@ def all_settings(): # Celery related settings CELERY_BROKER = os.environ.get("REDASH_CELERY_BROKER", REDIS_URL) CELERY_BACKEND = os.environ.get("REDASH_CELERY_BACKEND", CELERY_BROKER) -CELERY_TASK_RESULT_EXPIRES = int(os.environ.get('REDASH_CELERY_TASK_RESULT_EXPIRES', 3600 * 12)) +CELERY_TASK_RESULT_EXPIRES = int(os.environ.get('REDASH_CELERY_TASK_RESULT_EXPIRES', 3600 * 4)) # The following enables periodic job (every 5 minutes) of removing unused query results. QUERY_RESULTS_CLEANUP_ENABLED = parse_boolean(os.environ.get("REDASH_QUERY_RESULTS_CLEANUP_ENABLED", "true")) From b2fea428dd4acfae6968161222ea476213bc97e6 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Tue, 23 May 2017 09:19:14 +0300 Subject: [PATCH 094/243] Fix: use correct APIs --- redash/handlers/dashboards.py | 2 +- redash/handlers/queries.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/redash/handlers/dashboards.py b/redash/handlers/dashboards.py index 52b75f2a98..aa0f664762 100644 --- a/redash/handlers/dashboards.py +++ b/redash/handlers/dashboards.py @@ -19,7 +19,7 @@ def get(self): Lists dashboards modified in the last 7 days. """ if settings.FEATURE_DUMB_RECENTS: - dashboards = models.Dashboard.all(self.current_org, self.current_user.groups, self.current_user).order_by(models.Dashboard.updated_at.desc()).limit(10) + dashboards = models.Dashboard.all(self.current_org, self.current_user.group_ids, self.current_user.id).order_by(models.Dashboard.updated_at.desc()).limit(10) dashboards = [d.to_dict() for d in dashboards] else: recent = [d.to_dict() for d in models.Dashboard.recent(self.current_org, self.current_user.group_ids, self.current_user.id, for_user=True)] diff --git a/redash/handlers/queries.py b/redash/handlers/queries.py index cd7103f826..a37d009631 100644 --- a/redash/handlers/queries.py +++ b/redash/handlers/queries.py @@ -59,7 +59,7 @@ def get(self): """ if settings.FEATURE_DUMB_RECENTS: - results = models.Query.by_user(self.current_user, False).order_by(models.Query.updated_at.desc()).limit(10) + results = models.Query.by_user(self.current_user).order_by(models.Query.updated_at.desc()).limit(10) queries = [q.to_dict(with_last_modified_by=False) for q in results] else: queries = models.Query.recent(self.current_user.group_ids, self.current_user.id) From 2f1b1a69bd15c8a875a7afb499e5bd6a1dca0fb7 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Tue, 23 May 2017 10:52:54 +0300 Subject: [PATCH 095/243] Add CHANGELOG entry. --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec2f240de1..5656b2f3c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,8 +39,11 @@ - Fix: page header wasn't updating on dashboards page @MichaelJAndy - Fix: keyboard shortcuts didn't work in parameter inputs -## v1.0.0-rc.2 - 2017-02-22 +### Other +- Change default job expiry times to: job lock expire after 12 hours (previously: 6 hours) and Celery task result object expire after 4 hours (previously: 1 hour). @shimpeko + +## v1.0.0-rc.2 - 2017-02-22 ### Changed From af8bdf4fd15c86d4d36da54f455b9bf78469cd37 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Mon, 29 May 2017 16:41:42 +0300 Subject: [PATCH 096/243] Fix: remove unneeded calls to app_context() When the extra app_context was popped from the stack, it was triggering flask-sqlalchemy's teardown handler, which was removing the session causing objects to become detached before they should be. --- redash/destinations/email.py | 19 +++++++++---------- redash/tasks/general.py | 16 ++++++++-------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/redash/destinations/email.py b/redash/destinations/email.py index 8f275d0b02..b66c9e9484 100644 --- a/redash/destinations/email.py +++ b/redash/destinations/email.py @@ -40,16 +40,15 @@ def notify(self, alert, query, user, new_state, app, host, options): logging.debug("Notifying: %s", recipients) try: - with app.app_context(): - alert_name = alert.name.encode('utf-8', 'ignore') - state = new_state.upper() - subject_template = options.get('subject_template', settings.ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE) - message = Message( - recipients=recipients, - subject=subject_template.format(alert_name=alert_name, state=state), - html=html - ) - mail.send(message) + alert_name = alert.name.encode('utf-8', 'ignore') + state = new_state.upper() + subject_template = options.get('subject_template', settings.ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE) + message = Message( + recipients=recipients, + subject=subject_template.format(alert_name=alert_name, state=state), + html=html + ) + mail.send(message) except Exception: logging.exception("Mail send error.") diff --git a/redash/tasks/general.py b/redash/tasks/general.py index 2393642492..1b0677364b 100644 --- a/redash/tasks/general.py +++ b/redash/tasks/general.py @@ -1,9 +1,10 @@ import requests + from celery.utils.log import get_task_logger from flask_mail import Message -from redash.worker import celery +from redash import mail, models, settings from redash.version_check import run_version_check -from redash import models, mail, settings +from redash.worker import celery logger = get_task_logger(__name__) @@ -50,12 +51,11 @@ def send_mail(to, subject, html, text): from redash.wsgi import app try: - with app.app_context(): - message = Message(recipients=to, - subject=subject, - html=html, - body=text) + message = Message(recipients=to, + subject=subject, + html=html, + body=text) - mail.send(message) + mail.send(message) except Exception: logger.exception('Failed sending message: %s', message.subject) From fc96e14a8f339c7be3164f59b7115fce54d4a02d Mon Sep 17 00:00:00 2001 From: hamza zia Date: Tue, 30 May 2017 12:26:09 +0500 Subject: [PATCH 097/243] Only run queries when given parameters --- redash/handlers/query_results.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/redash/handlers/query_results.py b/redash/handlers/query_results.py index f31fdcb623..0b07d0f906 100644 --- a/redash/handlers/query_results.py +++ b/redash/handlers/query_results.py @@ -179,6 +179,7 @@ def get(self, query_id=None, query_result_id=None, filetype='json'): should_cache = query_result_id is not None parameter_values = collect_parameters_from_request(request.args) + query_parameters = set(collect_query_parameters(query_text)) max_age = int(request.args.get('maxAge', 0)) query_result = None @@ -189,7 +190,7 @@ def get(self, query_id=None, query_result_id=None, filetype='json'): query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org) if query is not None: - if settings.ALLOW_PARAMETERS_IN_EMBEDS: + if settings.ALLOW_PARAMETERS_IN_EMBEDS and query_parameters: query_result = run_query_sync(query.data_source, parameter_values, query.to_dict()['query'], max_age=max_age) elif query.latest_query_data_id is not None: query_result = get_object_or_404(models.QueryResult.get_by_id_and_org, query.latest_query_data_id, self.current_org) From 8d125354d2b1d0c9e3cf01b4e5f6cad818c9ff95 Mon Sep 17 00:00:00 2001 From: hamza zia Date: Tue, 30 May 2017 14:19:18 +0500 Subject: [PATCH 098/243] merged changes --- redash/handlers/query_results.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/redash/handlers/query_results.py b/redash/handlers/query_results.py index 0b07d0f906..397a3b03f1 100644 --- a/redash/handlers/query_results.py +++ b/redash/handlers/query_results.py @@ -179,7 +179,6 @@ def get(self, query_id=None, query_result_id=None, filetype='json'): should_cache = query_result_id is not None parameter_values = collect_parameters_from_request(request.args) - query_parameters = set(collect_query_parameters(query_text)) max_age = int(request.args.get('maxAge', 0)) query_result = None @@ -190,7 +189,7 @@ def get(self, query_id=None, query_result_id=None, filetype='json'): query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org) if query is not None: - if settings.ALLOW_PARAMETERS_IN_EMBEDS and query_parameters: + if settings.ALLOW_PARAMETERS_IN_EMBEDS and parameter_values: query_result = run_query_sync(query.data_source, parameter_values, query.to_dict()['query'], max_age=max_age) elif query.latest_query_data_id is not None: query_result = get_object_or_404(models.QueryResult.get_by_id_and_org, query.latest_query_data_id, self.current_org) From e54fff402e37e31cd41fe225e01834f3de77e940 Mon Sep 17 00:00:00 2001 From: hamza zia Date: Tue, 30 May 2017 16:32:09 +0500 Subject: [PATCH 099/243] fixed import bug --- redash/handlers/embed.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/redash/handlers/embed.py b/redash/handlers/embed.py index 0a001ec6fb..3cf4f40473 100644 --- a/redash/handlers/embed.py +++ b/redash/handlers/embed.py @@ -14,7 +14,7 @@ record_event) from redash.handlers.query_results import collect_query_parameters, run_query_sync -from redash.permissions import require_access, view_only +from redash.handlers.static import render_index from redash.utils import (collect_parameters_from_request, json_dumps, gen_query_hash) @@ -89,7 +89,6 @@ def public_dashboard(token, org_slug=None): else: api_key = get_object_or_404(models.ApiKey.get_by_api_key, token) dashboard = api_key.object - record_event(current_org, current_user, { 'action': 'view', 'object_id': dashboard.id, From 6bc53c363811d4f183596fa90bd64875a63ed432 Mon Sep 17 00:00:00 2001 From: hamza zia Date: Tue, 30 May 2017 16:33:56 +0500 Subject: [PATCH 100/243] use default embed.py --- redash/handlers/embed.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/redash/handlers/embed.py b/redash/handlers/embed.py index 3cf4f40473..edd614560e 100644 --- a/redash/handlers/embed.py +++ b/redash/handlers/embed.py @@ -1,4 +1,3 @@ -import json import logging import time @@ -12,10 +11,9 @@ from redash.handlers import routes from redash.handlers.base import (get_object_or_404, org_scoped_rule, record_event) - -from redash.handlers.query_results import collect_query_parameters, run_query_sync +from redash.handlers.query_results import collect_query_parameters from redash.handlers.static import render_index -from redash.utils import (collect_parameters_from_request, json_dumps, gen_query_hash) +from redash.utils import gen_query_hash # @@ -81,6 +79,7 @@ def embed(query_id, visualization_id, org_slug=None): return render_index() + @routes.route(org_scoped_rule('/public/dashboards/'), methods=['GET']) @login_required def public_dashboard(token, org_slug=None): @@ -89,6 +88,7 @@ def public_dashboard(token, org_slug=None): else: api_key = get_object_or_404(models.ApiKey.get_by_api_key, token) dashboard = api_key.object + record_event(current_org, current_user, { 'action': 'view', 'object_id': dashboard.id, From 108137bd7ea52ee71302f66317eee600a19de709 Mon Sep 17 00:00:00 2001 From: shotat Date: Tue, 30 May 2017 23:51:21 +0900 Subject: [PATCH 101/243] fix CONTRIBUTING.md --- CONTRIBUTING.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bcc5d96442..f28a1cf440 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ The following is a set of guidelines for contributing to Redash. These are guide ## Quick Links: -- [Feature Roadmap](https://trello.com/b/b2LUHU7A/re-dash-roadmap) +- [Feature Roadmap](https://trello.com/b/b2LUHU7A/redash-roadmap) - [Feature Requests](https://discuss.redash.io/c/feature-requests) - [Gitter Chat](https://gitter.im/getredash/redash) or [Slack](https://slack.redash.io) - [Documentation](https://redash.io/help/) @@ -29,7 +29,7 @@ The following is a set of guidelines for contributing to Redash. These are guide - [Documentation](#documentation) - Design? -[Addtional Notes](#additional-notes) +[Additional Notes](#additional-notes) - [Release Method](#release-method) - [Code of Conduct](#code-of-conduct) @@ -46,9 +46,9 @@ When creating a new bug report, please make sure to: ### Suggesting Enhancements / Feature Requests -If you would like to suggest an enchancement or ask for a new feature: +If you would like to suggest an enhancement or ask for a new feature: -- Please check [the roadmap](https://trello.com/b/b2LUHU7A/re-dash-roadmap) for existing Trello card for what you want to suggest/ask. If there is, feel free to upvote it to signal interest or add your comments. +- Please check [the roadmap](https://trello.com/b/b2LUHU7A/redash-roadmap) for existing Trello card for what you want to suggest/ask. If there is, feel free to upvote it to signal interest or add your comments. - If there is no existing card, open a thread in [the forum](https://discuss.redash.io/c/feature-requests) to start a discussion about what you want to suggest. Try to provide as much details and context as possible and include information about *the problem you want to solve* rather only *your proposed solution*. ### Pull Requests @@ -56,7 +56,7 @@ If you would like to suggest an enchancement or ask for a new feature: - **Code contributions are welcomed**. For big changes or significant features, it's usually better to reach out first and discuss what you want to implement and how (we recommend reading: [Pull Request First](https://medium.com/practical-blend/pull-request-first-f6bb667a9b6#.ozlqxvj36)). This to make sure that what you want to implement is aligned with our goals for the project and that no one else is already working on it. - Include screenshots and animated GIFs in your pull request whenever possible. - Please add [documentation](#documentation) for new features or changes in functionality along with the code. -- Please follow existing code style. We use PEP8 for Python and sensible style for Javascript. +- Please follow existing code style. We use PEP8 for Python and sensible style for JavaScript. ### Documentation From 23cb92cf6d4d8c67e16657d9b31847eadb384e27 Mon Sep 17 00:00:00 2001 From: Daniele Rapati Date: Mon, 22 May 2017 18:25:07 +0100 Subject: [PATCH 102/243] safeguard alerts against empty query results alert will revert to UNKNOWN_STATE in case of no data --- redash/models.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/redash/models.py b/redash/models.py index 471f993473..0b0c809fbc 100644 --- a/redash/models.py +++ b/redash/models.py @@ -1143,18 +1143,20 @@ def to_dict(self, full=True): def evaluate(self): data = json.loads(self.query_rel.latest_query_data.data) - # todo: safe guard for empty - value = data['rows'][0][self.options['column']] - op = self.options['op'] - - if op == 'greater than' and value > self.options['value']: - new_state = self.TRIGGERED_STATE - elif op == 'less than' and value < self.options['value']: - new_state = self.TRIGGERED_STATE - elif op == 'equals' and value == self.options['value']: - new_state = self.TRIGGERED_STATE + if data['rows']: + value = data['rows'][0][self.options['column']] + op = self.options['op'] + + if op == 'greater than' and value > self.options['value']: + new_state = self.TRIGGERED_STATE + elif op == 'less than' and value < self.options['value']: + new_state = self.TRIGGERED_STATE + elif op == 'equals' and value == self.options['value']: + new_state = self.TRIGGERED_STATE + else: + new_state = self.OK_STATE else: - new_state = self.OK_STATE + new_state = self.UNKNOWN_STATE return new_state From eee38557d13a0586529a3dcf2ff4c338c4cf5624 Mon Sep 17 00:00:00 2001 From: OKUMURA Takahiro Date: Thu, 1 Jun 2017 14:46:18 +0900 Subject: [PATCH 103/243] Fix markdown the changelog markdown before v1.0 is broken. --- CHANGELOG.md | 315 ++++++++++++++++++++++++++------------------------- 1 file changed, 160 insertions(+), 155 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfbdaa7b21..1d2ae527a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -243,109 +243,110 @@ We're releasing a new upgrade script -- see [here](https://redash.io/help-onprem ### Added -61fe16e #1374: Add: allow '*' in REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN (Allen Short) -2f09043 #1113: Add: share modify/access permissions for queries and dashboard (whummer) -3db0eea #1341: Add: support for specifying SAML nameid-format (zoetrope) -b0ecd0e #1343: Add: support for local SAML metadata file (zoetrope) -0235d37 #1335: Add: allow changing alert email subject. (Arik Fraimovich) -2135dfd #1333: Add: control over y axis min/max values (Arik Fraimovich) -49e788a #1328: Add: support for snapshot generation service (Arik Fraimovich) -229ca6c #1323: Add: collect runtime metrics for Celery tasks (Arik Fraimovich) -931a1f3 #1315: Add: support for loading BigQuery schema (Arik Fraimovich) -39b4f9a #1314: Add: support MongoDB SSL connections (Arik Fraimovich) -ca1ca9b #1312: Add: additional configuration for Celery jobs (Arik Fraimovich) -fc00e61 #1310: Add: support for date/time with seconds parameters (Arik Fraimovich) -d72a198 #1307: Add: API to force refresh data source schema (Arik Fraimovich) -beb89ec #1305: Add: UI to edit dashboard text box widget (Kazuhito Hokamura) -808fdd4 #1298: Add: JIRA (JQL) query runner (Arik Fraimovich) -ff9e844 #1280: Add: configuration flag to disable scheduled queries (Hirotaka Suzuki) -ef4699a #1269: Add: Google Drive federated tables support in BigQuery query runner (Kurt Gooden) -2eeb947 #1236: Add: query runner for Cassandra and ScyllaDB (syerushalmy) -10b398e #1249: Add: override slack webhook parameters (mystelynx) -2b5e340 #1252: Add: Schema loading support for Presto query runner (using information_schema) (Rohan Dhupelia) -2aaf5dd #1250: Add: query snippets feature (Arik Fraimovich) -8d8af73 #1226: Add: Sankey visualization (Arik Fraimovich) -a02edda #1222: Add: additional results format for sunburst visualization (Arik Fraimovich) -0e70188 #1213: Add: new sunburst sequence visualization (Arik Fraimovich) -9a6d2d7 #1204: Add: show views in schema browser for Vertica data sources (Matthew Carter) -600afa5 #1138: Add: ability to register user defined function (UDF) resources for BigQuery DataSource/Query (fabito) -b410410 #1166: Add: "every 14 days" refresh option (Arik Fraimovich) -906365f #967: Add: extend ElasticSearch query_runner to support aggregations (lloydw) +- 61fe16e #1374: Add: allow '*' in REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN (Allen Short) +- 2f09043 #1113: Add: share modify/access permissions for queries and dashboard (whummer) +- 3db0eea #1341: Add: support for specifying SAML nameid-format (zoetrope) +- b0ecd0e #1343: Add: support for local SAML metadata file (zoetrope) +- 0235d37 #1335: Add: allow changing alert email subject. (Arik Fraimovich) +- 2135dfd #1333: Add: control over y axis min/max values (Arik Fraimovich) +- 49e788a #1328: Add: support for snapshot generation service (Arik Fraimovich) +- 229ca6c #1323: Add: collect runtime metrics for Celery tasks (Arik Fraimovich) +- 931a1f3 #1315: Add: support for loading BigQuery schema (Arik Fraimovich) +- 39b4f9a #1314: Add: support MongoDB SSL connections (Arik Fraimovich) +- ca1ca9b #1312: Add: additional configuration for Celery jobs (Arik Fraimovich) +- fc00e61 #1310: Add: support for date/time with seconds parameters (Arik Fraimovich) +- d72a198 #1307: Add: API to force refresh data source schema (Arik Fraimovich) +- beb89ec #1305: Add: UI to edit dashboard text box widget (Kazuhito Hokamura) +- 808fdd4 #1298: Add: JIRA (JQL) query runner (Arik Fraimovich) +- ff9e844 #1280: Add: configuration flag to disable scheduled queries (Hirotaka Suzuki) +- ef4699a #1269: Add: Google Drive federated tables support in BigQuery query runner (Kurt Gooden) +- 2eeb947 #1236: Add: query runner for Cassandra and ScyllaDB (syerushalmy) +- 10b398e #1249: Add: override slack webhook parameters (mystelynx) +- 2b5e340 #1252: Add: Schema loading support for Presto query runner (using information_schema) (Rohan Dhupelia) +- 2aaf5dd #1250: Add: query snippets feature (Arik Fraimovich) +- 8d8af73 #1226: Add: Sankey visualization (Arik Fraimovich) +- a02edda #1222: Add: additional results format for sunburst visualization (Arik Fraimovich) +- 0e70188 #1213: Add: new sunburst sequence visualization (Arik Fraimovich) +- 9a6d2d7 #1204: Add: show views in schema browser for Vertica data sources (Matthew Carter) +- 600afa5 #1138: Add: ability to register user defined function (UDF) resources for BigQuery DataSource/Query (fabito) +- b410410 #1166: Add: "every 14 days" refresh option (Arik Fraimovich) +- 906365f #967: Add: extend ElasticSearch query_runner to support aggregations (lloydw) ### Changed -2de4aa2 #1395: Change: switch to requests in URL query runner (Arik Fraimovich) -db1a941 #1392: Change: Update documentation links to point at the new location. (Arik Fraimovich) -002f794 #1368: Change: added ability to disable auto update in admin views (Arik Fraimovich) -aa5d14e #1366: Change: improve error message for exception in the Python query runner (deecay) -880627c #1355: Change: pass the user object to the run_query method (Arik Fraimovich) -23c605b #1342: SAML: specify entity id (zoetrope) -015b1dc #1334: Change: allow specifying recipient address when sending email test message (Arik Fraimovich) -39aaa2f #1292: Change: improvements to map visualization (Arik Fraimovich) -b22191b #1332: Change: upgrade Python packages (Arik Fraimovich) -23ba98b #1331: Celery: Upgrade Celery to more recent version. (Arik Fraimovich) -3283116 #1330: Change: upgrade Requests to latest version. (Arik Fraimovich) -39091e0 #1324: Change: add more logging and information for refresh schemas task (Arik Fraimovich) -462faea #1316: Change: remove deprecated settings (Arik Fraimovich) -73e1837 #1313: Change: more flexible column width calculation (Arik Fraimovich) -e8eb840 #1279: Change: update bootstrap.sh to support Ubuntu 16.04 (IllusiveMilkman) -8cf0252 #1262: Change: upgrade Plot.ly version and switch to smaller build (Arik Fraimovich) -0b79fb8 #1306: Change: paginate queries page & add explicit urls. (Arik Fraimovich) -41f99f5 #1299: Change: send Content-Type header (application/json) in query results responses (Tsuyoshi Tatsukawa) -dfb1a20 #1297: Change: update Slack configuration titles. (Arik Fraimovich) -8c1056c #1294: Change: don't annotate BigQuery queries (Arik Fraimovich) -a3cf92e #1289: Change: use key_as_string when available (ElasticSearch query runner) (Arik Fraimovich) -e155191 #1285: Change: do not display Oracle tablespace name in schema browser (Matthew Carter) -6cbc39c #1282: Change: deduplicate Google Spreadsheet columns (Arik Fraimovich) -4caf2e3 #1277: Set specific version of cryptography lib (Arik Fraimovich) -d22f0d4 #1216: Change: bootstrap.sh - use non interactive dist-upgrade (Atsushi Sasaki) -19530f4 #1245: Change: switch from CodeMirror to Ace editor (Arik Fraimovich) -dfb92db #1234: Change: MongoDB query runner set DB name as mandatory (Arik Fraimovich) -b750843 #1230: Change: annotate Presto queries with metadata (Noriaki Katayama) -5b20fe2 #1217: Change: install libffi-dev for Cryptography (Ubuntu setup script) (Atsushi Sasaki) -a9fac34 #1206: Change: update pymssql version to 2.1.3 (kitsuyui) -5d43cbe #1198: Change: add support for Standard SQL in BigQuery query runner (mystelynx) -84d0c22 #1193: Change: modify the argument order of moment.add function call (Kenya Yamaguchi) +- 2de4aa2 #1395: Change: switch to requests in URL query runner (Arik Fraimovich) +- db1a941 #1392: Change: Update documentation links to point at the new location. (Arik Fraimovich) +- 002f794 #1368: Change: added ability to disable auto update in admin views (Arik Fraimovich) +- aa5d14e #1366: Change: improve error message for exception in the Python query runner (deecay) +- 880627c #1355: Change: pass the user object to the run_query method (Arik Fraimovich) +- 23c605b #1342: SAML: specify entity id (zoetrope) +- 015b1dc #1334: Change: allow specifying recipient address when sending email test message (Arik Fraimovich) +- 39aaa2f #1292: Change: improvements to map visualization (Arik Fraimovich) +- b22191b #1332: Change: upgrade Python packages (Arik Fraimovich) +- 23ba98b #1331: Celery: Upgrade Celery to more recent version. (Arik Fraimovich) +- 3283116 #1330: Change: upgrade Requests to latest version. (Arik Fraimovich) +- 39091e0 #1324: Change: add more logging and information for refresh schemas task (Arik Fraimovich) +- 462faea #1316: Change: remove deprecated settings (Arik Fraimovich) +- 73e1837 #1313: Change: more flexible column width calculation (Arik Fraimovich) +- e8eb840 #1279: Change: update bootstrap.sh to support Ubuntu 16.04 (IllusiveMilkman) +- 8cf0252 #1262: Change: upgrade Plot.ly version and switch to smaller build (Arik Fraimovich) +- 0b79fb8 #1306: Change: paginate queries page & add explicit urls. (Arik Fraimovich) +- 41f99f5 #1299: Change: send Content-Type header (application/json) in query results responses (Tsuyoshi Tatsukawa) +- dfb1a20 #1297: Change: update Slack configuration titles. (Arik Fraimovich) +- 8c1056c #1294: Change: don't annotate BigQuery queries (Arik Fraimovich) +- a3cf92e #1289: Change: use key_as_string when available (ElasticSearch query runner) (Arik Fraimovich) +- e155191 #1285: Change: do not display Oracle tablespace name in schema browser (Matthew Carter) +- 6cbc39c #1282: Change: deduplicate Google Spreadsheet columns (Arik Fraimovich) +- 4caf2e3 #1277: Set specific version of cryptography lib (Arik Fraimovich) +- d22f0d4 #1216: Change: bootstrap.sh - use non interactive dist-upgrade (Atsushi Sasaki) +- 19530f4 #1245: Change: switch from CodeMirror to Ace editor (Arik Fraimovich) +- dfb92db #1234: Change: MongoDB query runner set DB name as mandatory (Arik Fraimovich) +- b750843 #1230: Change: annotate Presto queries with metadata (Noriaki Katayama) +- 5b20fe2 #1217: Change: install libffi-dev for Cryptography (Ubuntu setup script) (Atsushi Sasaki) +- a9fac34 #1206: Change: update pymssql version to 2.1.3 (kitsuyui) +- 5d43cbe #1198: Change: add support for Standard SQL in BigQuery query runner (mystelynx) +- 84d0c22 #1193: Change: modify the argument order of moment.add function call (Kenya Yamaguchi) ### Fixed -d6febb0 #1375: Fix: Download Dataset does not work when not logged in (Joshua Dechant) -96553ad #1369: Fix: missing format call in Elasticsearch test method (Adam Griffiths) -c57c765 #1365: Fix: compare retrieval times in UTC timezone (Allen Short) -37dff5f #1360: Fix: connection test was broken for MySQL (ichihara) -360028c #1359: Fix: schema loading query for Hive was wrong for non default schema (laughingman7743) -7ee41d4 #1358: Fix: make sure all calls to run_query updated with new parameter (Arik Fraimovich) -0d94479 #1329: Fix: Redis memory leak. (Arik Fraimovich) -7145aa2 #1325: Fix: queries API was doing N+1 queries in most cases (Arik Fraimovich) -cd2e927 #1311: Fix: BoxPlot visualization wasn't rendering on a dashboard (Arik Fraimovich) -a562ce7 #1309: Fix: properly render checkboxes in dynamic forms (Arik Fraimovich) -d48192c #1308: Fix: support for Unicode columns name in Google Spreadsheets (Arik Fraimovich) -e42f93f #1283: Fix: schema browser was unstable after opening a table (Arik Fraimovich) -170bd65 #1272: Fix: TreasureData get_schema method was returning array instead of string as column name (ariarijp) -4710c41 #1265: Fix: refresh modal not working for unsaved query (Arik Fraimovich) -bc3a5ab #1264: Fix: dashboard refresh not working (Arik Fraimovich) -6202d09 #1240: Fix: when shared dashboard token not found, return 404 (Wesley Batista) -93aac14 #1251: Fix: autocomplete went crazy when database has no autocomplete. (Arik Fraimovich) -b8eca28 #1246: Fix: support large schemas in schema browser (Arik Fraimovich) -b781003 #1223: Fix: Alert: when hipchat Alert.name is multibyte character, occur error. (toyama0919) -0b928e6 #1227: Fix: Bower install fails in vagrant (Kazuhito Hokamura) -a411af2 #1232: Fix: don't show warning when query string (parameters value) changes (Kazuhito Hokamura) -3dbb5a6 #1221: Fix: sunburst didn't handle all cases of path lengths (Arik Fraimovich) -a7cc1ee #1218: Fix: updated result not being saved when changing query text. (Arik Fraimovich) -0617833 #1215: Fix: email alerts not working (Arik Fraimovich) -78f65b1 #1187: Fix: read only users receive the permission error modal in query view (Arik Fraimovich) -bba801f #1167: Fix the version of setuptools on bootstrap script for Ubuntu (Takuya Arita) -ce81d69 #1160: Fix indentation in docker-compose-example.yml (Hirofumi Wakasugi) -dd759fe #1155: Fix: make all configuration values of Oracle required (Arik Fraimovich) +- d6febb0 #1375: Fix: Download Dataset does not work when not logged in (Joshua Dechant) +- 96553ad #1369: Fix: missing format call in Elasticsearch test method (Adam Griffiths) +- c57c765 #1365: Fix: compare retrieval times in UTC timezone (Allen Short) +- 37dff5f #1360: Fix: connection test was broken for MySQL (ichihara) +- 360028c #1359: Fix: schema loading query for Hive was wrong for non default schema (laughingman7743) +- 7ee41d4 #1358: Fix: make sure all calls to run_query updated with new parameter (Arik Fraimovich) +- 0d94479 #1329: Fix: Redis memory leak. (Arik Fraimovich) +- 7145aa2 #1325: Fix: queries API was doing N+1 queries in most cases (Arik Fraimovich) +- cd2e927 #1311: Fix: BoxPlot visualization wasn't rendering on a dashboard (Arik Fraimovich) +- a562ce7 #1309: Fix: properly render checkboxes in dynamic forms (Arik Fraimovich) +- d48192c #1308: Fix: support for Unicode columns name in Google Spreadsheets (Arik Fraimovich) +- e42f93f #1283: Fix: schema browser was unstable after opening a table (Arik Fraimovich) +- 170bd65 #1272: Fix: TreasureData get_schema method was returning array instead of string as column name (ariarijp) +- 4710c41 #1265: Fix: refresh modal not working for unsaved query (Arik Fraimovich) +- bc3a5ab #1264: Fix: dashboard refresh not working (Arik Fraimovich) +- 6202d09 #1240: Fix: when shared dashboard token not found, return 404 (Wesley Batista) +- 93aac14 #1251: Fix: autocomplete went crazy when database has no autocomplete. (Arik Fraimovich) +- b8eca28 #1246: Fix: support large schemas in schema browser (Arik Fraimovich) +- b781003 #1223: Fix: Alert: when hipchat Alert.name is multibyte character, occur error. (toyama0919) +- 0b928e6 #1227: Fix: Bower install fails in vagrant (Kazuhito Hokamura) +- a411af2 #1232: Fix: don't show warning when query string (parameters value) changes (Kazuhito Hokamura) +- 3dbb5a6 #1221: Fix: sunburst didn't handle all cases of path lengths (Arik Fraimovich) +- a7cc1ee #1218: Fix: updated result not being saved when changing query text. (Arik Fraimovich) +- 0617833 #1215: Fix: email alerts not working (Arik Fraimovich) +- 78f65b1 #1187: Fix: read only users receive the permission error modal in query view (Arik Fraimovich) +- bba801f #1167: Fix the version of setuptools on bootstrap script for Ubuntu (Takuya Arita) +- ce81d69 #1160: Fix indentation in docker-compose-example.yml (Hirofumi Wakasugi) +- dd759fe #1155: Fix: make all configuration values of Oracle required (Arik Fraimovich) ### Docs -a69ee0c #1225: Fix: RST formatting of the Vagrant documentation (Kazuhito Hokamura) -03837c0 #1242: Docs: add warning re. quotes on column names and BigQuery (Ereli) -9a98075 #1255: Docs: add documentation for InfluxDB (vishesh92) -e0485de #1195: Docs: fix typo in maintenance page title (Antoine Augusti) -7681d3e #1164: Docs: update permission documentation (Daniel Darabos) -bcd3670 #1156: Docs: add SSL parameters to nginx configuration (Josh Cox) + +- a69ee0c #1225: Fix: RST formatting of the Vagrant documentation (Kazuhito Hokamura) +- 03837c0 #1242: Docs: add warning re. quotes on column names and BigQuery (Ereli) +- 9a98075 #1255: Docs: add documentation for InfluxDB (vishesh92) +- e0485de #1195: Docs: fix typo in maintenance page title (Antoine Augusti) +- 7681d3e #1164: Docs: update permission documentation (Daniel Darabos) +- bcd3670 #1156: Docs: add SSL parameters to nginx configuration (Josh Cox) ## v0.11.1.b2095 - 2016-08-02 @@ -363,73 +364,77 @@ Also, this release includes numerous smaller features, improvements, and bug fix A big thank you goes to all who contributed code and documentation in this release: @AntoineAugusti, @James226, @adamlwgriffiths, @alexdebrie, @anthony-coble, @ariarijp, @dheerajrav, @edwardsharp, @machira, @nabilblk, @ninneko, @ordd, @tomerben, @toru-takahashi, @vishesh92, @vorakumar and @whummer. ### Added -d5e5b24 #1136: Feature: add --org option to all relevant CLI commands. (@adamlwgriffiths) -87e25f2 #1129: Feature: support for JSON query formatting (Mongo, ElasticSearch) (@arikfr) -6bb2716 #1121: Show error when failing to communicate with server (@arikfr) -f21276e #1119: Feature: add UI to delete alerts (@arikfr) -8656540 #1069: Feature: UI for query parameters (@arikfr) -790128c #1067: Feature: word cloud visualization (@anthony-coble) -8b73a2b #1098: Feature: UI for alert destinations & new destination types (@alexdebrie) -1fbeb5d #1092: Add Heroku support (@adamlwgriffiths) -f64622d #1089: Add support for serialising UUID type within MSSQL #961 (@James226) -857caab #1085: Feature: API to pause a data source (@arikfr) -214aa3b #1060: Feature: support configuring user's groups with SAML (@vorakumar) -e20a005 #1007: Issue#1006: Make bottom margin editable for Chart visualization (@vorakumar) -6e0dd2b #1063: Add support for date/time Y axis (@tomerben) -b5a4a6b #979: Feature: Add CLI to edit group permissions (@ninneko) -6d495d2 #1014: Add server-side parameter handling for embeds (@whummer) -5255804 #1091: Add caching for queries used in embeds (@whummer) + +- d5e5b24 #1136: Feature: add --org option to all relevant CLI commands. (@adamlwgriffiths) +- 87e25f2 #1129: Feature: support for JSON query formatting (Mongo, ElasticSearch) (@arikfr) +- 6bb2716 #1121: Show error when failing to communicate with server (@arikfr) +- f21276e #1119: Feature: add UI to delete alerts (@arikfr) +- 8656540 #1069: Feature: UI for query parameters (@arikfr) +- 790128c #1067: Feature: word cloud visualization (@anthony-coble) +- 8b73a2b #1098: Feature: UI for alert destinations & new destination types (@alexdebrie) +- 1fbeb5d #1092: Add Heroku support (@adamlwgriffiths) +- f64622d #1089: Add support for serialising UUID type within MSSQL #961 (@James226) +- 857caab #1085: Feature: API to pause a data source (@arikfr) +- 214aa3b #1060: Feature: support configuring user's groups with SAML (@vorakumar) +- e20a005 #1007: Issue#1006: Make bottom margin editable for Chart visualization (@vorakumar) +- 6e0dd2b #1063: Add support for date/time Y axis (@tomerben) +- b5a4a6b #979: Feature: Add CLI to edit group permissions (@ninneko) +- 6d495d2 #1014: Add server-side parameter handling for embeds (@whummer) +- 5255804 #1091: Add caching for queries used in embeds (@whummer) ### Changed -0314313 #1149: Presto QueryRunner supports tinyint and smallint (@toru-takahashi) -8fa6fdb #1030: Make sure data sources list ordered by id (@arikfr) -8df822e #1141: Make create data source button more prominent (@arikfr) -96dd811 #1127: Mark basic_auth_password as secret (@adamlwgriffiths) -ad65391 #1130: Improve Slack notification style (@AntoineAugusti) -df637e3 #1116: Return meaningful error when there is no cached result. (@arikfr) -65635ec #1102: Switch to HipChat V2 API (@arikfr) -14fcf01 #1072: Remove counter from the tasks Done tab (as it always shows 50). #1047 (@arikfr) -1a1160e #1062: DynamoDB: Better exception handling (@arikfr) -ed45dcb #1044: Improve vagrant flow (@staritza) -8b5dc8e #1036: Add optional block for more scripts in template (@arikfr) + +- 0314313 #1149: Presto QueryRunner supports tinyint and smallint (@toru-takahashi) +- 8fa6fdb #1030: Make sure data sources list ordered by id (@arikfr) +- 8df822e #1141: Make create data source button more prominent (@arikfr) +- 96dd811 #1127: Mark basic_auth_password as secret (@adamlwgriffiths) +- ad65391 #1130: Improve Slack notification style (@AntoineAugusti) +- df637e3 #1116: Return meaningful error when there is no cached result. (@arikfr) +- 65635ec #1102: Switch to HipChat V2 API (@arikfr) +- 14fcf01 #1072: Remove counter from the tasks Done tab (as it always shows 50). #1047 (@arikfr) +- 1a1160e #1062: DynamoDB: Better exception handling (@arikfr) +- ed45dcb #1044: Improve vagrant flow (@staritza) +- 8b5dc8e #1036: Add optional block for more scripts in template (@arikfr) ### Fixed -dbd48e1 #1143: Fix: use the email input type where needed (@ariarijp) -7445972 #1142: Fix: dates in filters might be duplicated (@arikfr) -5d0ed02 #1140: Fix: Hive should use the enabled variable (@arikfr) -392627d #1139: Fix: Impala data source referencing wrong variable (@arikfr) -c5bfbba #1133: Fix: query scrolling issues (@vishesh92) -c01d266 #1128: Fix: visualization options not updating after changing type (@arikfr) -6bc0e7a #1126: Fix #669: save fails when doing partial save of new query (@arikfr) -3ce27b9 #1118: Fix: remove alerts for archived queries (@arikfr) -4fabaae #1117: Fix #1052: filter not working for date/time values (@arikfr) -c107c94 #1077: Fix: install needed dependencies to use Hive in Docker image (@nabilblk) -abc790c #1115: Fix: allow non integers in alert reference value (@arikfr) -4ec473c #1110: Fix #1109: mixed group permissions resulting in wrong permission (@arikfr) -1ca5262 #1099: Fix RST syntax for links (@adamlwgriffiths) -daa6c1c #1096: Fix typo in env variable VERSION_CHECK (@AntoineAugusti) -cd06d27 #1095: Fix: use create_query permission for new query button. (@ordd) -2bc0b27 #1061: Fix: area chart stacking doesn't work (@machira) -8c21e91 #1108: Remove potnetially concurrency not safe code form enqueue_query (@arikfr) -e831218 #1084: Fix #1049: duplicate alerts when data source belongs to multiple groups (@arikfr) -6edb0ca #1080: Fix typo (@jeffwidman) -64d7538 #1074: Fix: ElasticSearch wasn't using correct type names (@toyama0919) -3f90dd9 #1064: Fix: old task trackers were not really removed (@arikfr) -e10ecd2 #1058: Bring back filters if dashboard filters are enabled (@AntoineAugusti) -701035f #1059: Fix: DynamoDB having issues when setting host (@arikfr) -2924d4f #1040: Small fixes to visualizations view (@arikfr) -fec0d5f #1037: Fix: multi filter wasn't working with __ syntax (@dheerajrav) -b066ce4 #1033: Fix: only ask for notification permissions if wasn't denied (@arikfr) -960c416 #1032: Fix: make sure we return dashboards only for current org only (@arikfr) -b3844d3 #1029: Hive: close connection only if it exists (@arikfr) + +- dbd48e1 #1143: Fix: use the email input type where needed (@ariarijp) +- 7445972 #1142: Fix: dates in filters might be duplicated (@arikfr) +- 5d0ed02 #1140: Fix: Hive should use the enabled variable (@arikfr) +- 392627d #1139: Fix: Impala data source referencing wrong variable (@arikfr) +- c5bfbba #1133: Fix: query scrolling issues (@vishesh92) +- c01d266 #1128: Fix: visualization options not updating after changing type (@arikfr) +- 6bc0e7a #1126: Fix #669: save fails when doing partial save of new query (@arikfr) +- 3ce27b9 #1118: Fix: remove alerts for archived queries (@arikfr) +- 4fabaae #1117: Fix #1052: filter not working for date/time values (@arikfr) +- c107c94 #1077: Fix: install needed dependencies to use Hive in Docker image (@nabilblk) +- abc790c #1115: Fix: allow non integers in alert reference value (@arikfr) +- 4ec473c #1110: Fix #1109: mixed group permissions resulting in wrong permission (@arikfr) +- 1ca5262 #1099: Fix RST syntax for links (@adamlwgriffiths) +- daa6c1c #1096: Fix typo in env variable VERSION_CHECK (@AntoineAugusti) +- cd06d27 #1095: Fix: use create_query permission for new query button. (@ordd) +- 2bc0b27 #1061: Fix: area chart stacking doesn't work (@machira) +- 8c21e91 #1108: Remove potnetially concurrency not safe code form enqueue_query (@arikfr) +- e831218 #1084: Fix #1049: duplicate alerts when data source belongs to multiple groups (@arikfr) +- 6edb0ca #1080: Fix typo (@jeffwidman) +- 64d7538 #1074: Fix: ElasticSearch wasn't using correct type names (@toyama0919) +- 3f90dd9 #1064: Fix: old task trackers were not really removed (@arikfr) +- e10ecd2 #1058: Bring back filters if dashboard filters are enabled (@AntoineAugusti) +- 701035f #1059: Fix: DynamoDB having issues when setting host (@arikfr) +- 2924d4f #1040: Small fixes to visualizations view (@arikfr) +- fec0d5f #1037: Fix: multi filter wasn't working with __ syntax (@dheerajrav) +- b066ce4 #1033: Fix: only ask for notification permissions if wasn't denied (@arikfr) +- 960c416 #1032: Fix: make sure we return dashboards only for current org only (@arikfr) +- b3844d3 #1029: Hive: close connection only if it exists (@arikfr) ### Docs -6bb09d8 #1146: Docs: add a link to settings documentation. (@adamlwgriffiths) -095e759 #1103: Docs: add section about monitoring (@AntoineAugusti) -e942486 #1090: Contributing Guide (@arikfr) -3037c4f #1066: Docs: command type-o fix. (@edwardsharp) -2ee0065 #1038: Add an ISSUE_TEMPLATE.md to direct people at the forum (@arikfr) -f7322a4 #1021: Vagrant docs: add purging the cache step (@ariarijp) + +- 6bb09d8 #1146: Docs: add a link to settings documentation. (@adamlwgriffiths) +- 095e759 #1103: Docs: add section about monitoring (@AntoineAugusti) +- e942486 #1090: Contributing Guide (@arikfr) +- 3037c4f #1066: Docs: command type-o fix. (@edwardsharp) +- 2ee0065 #1038: Add an ISSUE_TEMPLATE.md to direct people at the forum (@arikfr) +- f7322a4 #1021: Vagrant docs: add purging the cache step (@ariarijp) --- From fbd3b92ba039517107e3e9b26a1c759e835b3ca4 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Fri, 2 Jun 2017 18:08:13 +0300 Subject: [PATCH 104/243] Fix: delete data source doesn't work when query results referenced by queries. --- redash/handlers/data_sources.py | 14 ++++--- redash/models.py | 9 +++- tests/handlers/test_data_sources.py | 26 +----------- tests/models/test_data_sources.py | 65 ++++++++++++++++++++++++++++- tests/test_models.py | 36 ---------------- 5 files changed, 81 insertions(+), 69 deletions(-) diff --git a/redash/handlers/data_sources.py b/redash/handlers/data_sources.py index ebf19d9f18..d18e2f261b 100644 --- a/redash/handlers/data_sources.py +++ b/redash/handlers/data_sources.py @@ -1,13 +1,16 @@ import logging + from flask import make_response, request -from flask_restful import abort from funcy import project +from flask_restful import abort from redash import models -from redash.utils.configuration import ConfigurationContainer, ValidationError -from redash.permissions import require_admin, require_permission, require_access, view_only -from redash.query_runner import query_runners, get_configuration_schema_for_query_runner_type from redash.handlers.base import BaseResource, get_object_or_404 +from redash.permissions import (require_access, require_admin, + require_permission, view_only) +from redash.query_runner import (get_configuration_schema_for_query_runner_type, + query_runners) +from redash.utils.configuration import ConfigurationContainer, ValidationError class DataSourceTypeListResource(BaseResource): @@ -46,8 +49,7 @@ def post(self, data_source_id): @require_admin def delete(self, data_source_id): data_source = models.DataSource.get_by_id_and_org(data_source_id, self.current_org) - models.db.session.delete(data_source) - models.db.session.commit() + data_source.delete() return make_response('', 204) diff --git a/redash/models.py b/redash/models.py index 471f993473..d298267dd8 100644 --- a/redash/models.py +++ b/redash/models.py @@ -518,6 +518,13 @@ def all(cls, org, group_ids=None): def get_by_id(cls, _id): return cls.query.filter(cls.id == _id).one() + def delete(self): + Query.query.filter(Query.data_source == self).update(dict(data_source_id=None, latest_query_data_id=None)) + QueryResult.query.filter(QueryResult.data_source == self).delete() + res = db.session.delete(self) + db.session.commit() + return res + def get_schema(self, refresh=False): key = "data_source:schema:{}".format(self.id) @@ -604,7 +611,7 @@ class QueryResult(db.Model, BelongsToOrgMixin): org_id = Column(db.Integer, db.ForeignKey('organizations.id')) org = db.relationship(Organization) data_source_id = Column(db.Integer, db.ForeignKey("data_sources.id")) - data_source = db.relationship(DataSource, backref=backref('query_results', cascade="all, delete-orphan")) + data_source = db.relationship(DataSource, backref=backref('query_results')) query_hash = Column(db.String(32), index=True) query_text = Column('query', db.Text) data = Column(db.Text) diff --git a/tests/handlers/test_data_sources.py b/tests/handlers/test_data_sources.py index 2f33ad7184..786a2e6db1 100644 --- a/tests/handlers/test_data_sources.py +++ b/tests/handlers/test_data_sources.py @@ -1,8 +1,6 @@ -import json - from funcy import pairwise - from tests import BaseTestCase + from redash.models import DataSource, Query @@ -82,28 +80,6 @@ def test_deletes_the_data_source(self): self.assertEqual(204, rv.status_code) self.assertIsNone(DataSource.query.get(data_source.id)) - def test_sets_queries_data_source_to_null(self): - data_source = self.factory.create_data_source() - admin = self.factory.create_admin() - query = self.factory.create_query(data_source=data_source) - - rv = self.make_request('delete', '/api/data_sources/{}'.format(data_source.id), user=admin) - - self.assertEqual(204, rv.status_code) - self.assertIsNone(DataSource.query.get(data_source.id)) - - self.assertIsNone(Query.query.get(query.id).data_source_id) - - def test_deletes_child_models(self): - data_source = self.factory.create_data_source() - admin = self.factory.create_admin() - query_result = self.factory.create_query_result(data_source=data_source) - - rv = self.make_request('delete', '/api/data_sources/{}'.format(data_source.id), user=admin) - - self.assertEqual(204, rv.status_code) - self.assertIsNone(DataSource.query.get(data_source.id)) - class TestDataSourceListResourcePost(BaseTestCase): def test_returns_400_when_missing_fields(self): diff --git a/tests/models/test_data_sources.py b/tests/models/test_data_sources.py index 944284ac15..037ff77a05 100644 --- a/tests/models/test_data_sources.py +++ b/tests/models/test_data_sources.py @@ -1,8 +1,46 @@ +import mock from tests import BaseTestCase -from redash.models import DataSource + +from redash.models import DataSource, Query, QueryResult from redash.utils.configuration import ConfigurationContainer +class DataSourceTest(BaseTestCase): + def test_get_schema(self): + return_value = [{'name': 'table', 'columns': []}] + + with mock.patch('redash.query_runner.pg.PostgreSQL.get_schema') as patched_get_schema: + patched_get_schema.return_value = return_value + + schema = self.factory.data_source.get_schema() + + self.assertEqual(return_value, schema) + + def test_get_schema_uses_cache(self): + return_value = [{'name': 'table', 'columns': []}] + with mock.patch('redash.query_runner.pg.PostgreSQL.get_schema') as patched_get_schema: + patched_get_schema.return_value = return_value + + self.factory.data_source.get_schema() + schema = self.factory.data_source.get_schema() + + self.assertEqual(return_value, schema) + self.assertEqual(patched_get_schema.call_count, 1) + + def test_get_schema_skips_cache_with_refresh_true(self): + return_value = [{'name': 'table', 'columns': []}] + with mock.patch('redash.query_runner.pg.PostgreSQL.get_schema') as patched_get_schema: + patched_get_schema.return_value = return_value + + self.factory.data_source.get_schema() + new_return_value = [{'name': 'new_table', 'columns': []}] + patched_get_schema.return_value = new_return_value + schema = self.factory.data_source.get_schema(refresh=True) + + self.assertEqual(new_return_value, schema) + self.assertEqual(patched_get_schema.call_count, 2) + + class TestDataSourceCreate(BaseTestCase): def test_adds_data_source_to_default_group(self): data_source = DataSource.create_with_group(org=self.factory.org, name='test', options=ConfigurationContainer.from_json('{"dbname": "test"}'), type='pg') @@ -33,3 +71,28 @@ def test_resume_clears_reason(self): def test_reason_is_none_by_default(self): self.assertEqual(self.factory.data_source.pause_reason, None) + + +class TestDataSourceDelete(BaseTestCase): + def test_deletes_the_data_source(self): + data_source = self.factory.create_data_source() + data_source.delete() + + self.assertIsNone(DataSource.query.get(data_source.id)) + + def test_sets_queries_data_source_to_null(self): + data_source = self.factory.create_data_source() + query = self.factory.create_query(data_source=data_source) + + data_source.delete() + self.assertIsNone(DataSource.query.get(data_source.id)) + self.assertIsNone(Query.query.get(query.id).data_source_id) + + def test_deletes_child_models(self): + data_source = self.factory.create_data_source() + self.factory.create_query_result(data_source=data_source) + self.factory.create_query(data_source=data_source, latest_query_data=self.factory.create_query_result(data_source=data_source)) + + data_source.delete() + self.assertIsNone(DataSource.query.get(data_source.id)) + self.assertEqual(0, QueryResult.query.filter(QueryResult.data_source == data_source).count()) diff --git a/tests/test_models.py b/tests/test_models.py index f2c8770208..bac0202f5d 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -246,42 +246,6 @@ def test_deletes_alerts(self): self.assertEqual(db.session.query(models.AlertSubscription).get(subscription.id), None) -class DataSourceTest(BaseTestCase): - def test_get_schema(self): - return_value = [{'name': 'table', 'columns': []}] - - with mock.patch('redash.query_runner.pg.PostgreSQL.get_schema') as patched_get_schema: - patched_get_schema.return_value = return_value - - schema = self.factory.data_source.get_schema() - - self.assertEqual(return_value, schema) - - def test_get_schema_uses_cache(self): - return_value = [{'name': 'table', 'columns': []}] - with mock.patch('redash.query_runner.pg.PostgreSQL.get_schema') as patched_get_schema: - patched_get_schema.return_value = return_value - - self.factory.data_source.get_schema() - schema = self.factory.data_source.get_schema() - - self.assertEqual(return_value, schema) - self.assertEqual(patched_get_schema.call_count, 1) - - def test_get_schema_skips_cache_with_refresh_true(self): - return_value = [{'name': 'table', 'columns': []}] - with mock.patch('redash.query_runner.pg.PostgreSQL.get_schema') as patched_get_schema: - patched_get_schema.return_value = return_value - - self.factory.data_source.get_schema() - new_return_value = [{'name': 'new_table', 'columns': []}] - patched_get_schema.return_value = new_return_value - schema = self.factory.data_source.get_schema(refresh=True) - - self.assertEqual(new_return_value, schema) - self.assertEqual(patched_get_schema.call_count, 2) - - class QueryResultTest(BaseTestCase): def setUp(self): super(QueryResultTest, self).setUp() From 69825e001f204ff185af671a86691b6c8a1b7e00 Mon Sep 17 00:00:00 2001 From: Alexander Shepelin Date: Sun, 4 Jun 2017 17:24:47 +0300 Subject: [PATCH 105/243] fix redirect to /setup after install --- redash/handlers/authentication.py | 4 ++-- redash/handlers/setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/redash/handlers/authentication.py b/redash/handlers/authentication.py index 3e1c695567..c19b7d175e 100644 --- a/redash/handlers/authentication.py +++ b/redash/handlers/authentication.py @@ -93,9 +93,9 @@ def forgot_password(org_slug=None): def login(org_slug=None): # We intentionally use == as otherwise it won't actually use the proxy. So weird :O # noinspection PyComparisonWithNone - if current_org is None and not settings.MULTI_ORG: + if current_org == None and not settings.MULTI_ORG: return redirect('/setup') - elif current_org is None: + elif current_org == None: return redirect('/') index_url = url_for("redash.index", org_slug=org_slug) diff --git a/redash/handlers/setup.py b/redash/handlers/setup.py index 086541acb2..2e376246e8 100644 --- a/redash/handlers/setup.py +++ b/redash/handlers/setup.py @@ -38,7 +38,7 @@ def create_org(org_name, user_name, email, password): @routes.route('/setup', methods=['GET', 'POST']) def setup(): - if current_org is not None or settings.MULTI_ORG: + if current_org != None or settings.MULTI_ORG: return redirect('/') form = SetupForm(request.form) From fe42195b5a176b1c2c9fb39b296015c0fe4e185e Mon Sep 17 00:00:00 2001 From: laughingman7743 Date: Mon, 5 Jun 2017 22:09:30 +0900 Subject: [PATCH 106/243] Implement Athena query runner using RestAPI --- redash/query_runner/athena.py | 123 ++++++++++++++++++++-------------- requirements_all_ds.txt | 1 + 2 files changed, 75 insertions(+), 49 deletions(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index 23339446a1..2100a5ffe5 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -1,102 +1,127 @@ +import logging import json -import os -import requests +from redash.utils import JSONEncoder +from redash.query_runner import * -from redash.query_runner import BaseQueryRunner, register -from redash.settings import parse_boolean -PROXY_URL = os.environ.get('ATHENA_PROXY_URL') -ANNOTATE_QUERY = parse_boolean(os.environ.get('ATHENA_ANNOTATE_QUERY', 'true')) +logger = logging.getLogger(__name__) + + +try: + import pyathena + enabled = True +except ImportError: + enabled = False + + +_TYPE_MAPPINGS = { + 'boolean': TYPE_BOOLEAN, + 'tinyint': TYPE_INTEGER, + 'smallint': TYPE_INTEGER, + 'integer': TYPE_INTEGER, + 'bigint': TYPE_INTEGER, + 'double': TYPE_FLOAT, + 'varchar': TYPE_STRING, + 'timestamp': TYPE_DATETIME, + 'date': TYPE_DATE, + 'varbinary': TYPE_STRING, + 'array': TYPE_STRING, + 'map': TYPE_STRING, + 'row': TYPE_STRING, + 'decimal': TYPE_FLOAT, +} + class Athena(BaseQueryRunner): noop_query = 'SELECT 1' - @classmethod - def name(cls): - return "Amazon Athena" - @classmethod def configuration_schema(cls): return { 'type': 'object', 'properties': { - 'region': { + 'aws_access_key_id': { 'type': 'string', - 'title': 'AWS Region' + 'title': 'AWS Access Key ID', }, - 'aws_access_key': { + 'aws_secret_access_key': { 'type': 'string', - 'title': 'AWS Access Key' + 'title': 'AWS Secret Access Key', }, - 'aws_secret_key': { + 'schema_name': { 'type': 'string', - 'title': 'AWS Secret Key' + 'title': 'Schema Name', + }, + 'region_name': { + 'type': 'string', + 'title': 'Region Name', }, 's3_staging_dir': { 'type': 'string', - 'title': 'S3 Staging Path' - } + 'title': 'S3 Staging Directory', + }, + 'encryption_option': { + 'type': 'string', + 'title': 'Encryption Option', + }, + 'kms_key': { + 'type': 'string', + 'title': 'KMS Key', + }, }, - 'required': ['region', 'aws_access_key', 'aws_secret_key', 's3_staging_dir'], - 'secret': ['aws_secret_key'] + 'required': ['region_name', 'schema_name', 's3_staging_dir'] } @classmethod - def annotate_query(cls): - return ANNOTATE_QUERY + def enabled(cls): + return enabled + + @classmethod + def type(cls): + return "athena" + + def __init__(self, configuration): + super(Athena, self).__init__(configuration) def get_schema(self, get_stats=False): schema = {} query = """ SELECT table_schema, table_name, column_name FROM information_schema.columns - WHERE table_schema NOT IN ('pg_catalog', 'information_schema') + WHERE table_schema NOT IN ('information_schema') """ results, error = self.run_query(query, None) - if error is not None: raise Exception("Failed getting schema.") results = json.loads(results) - for row in results['rows']: - table_name = '{}.{}'.format(row['table_schema'], row['table_name']) - + table_name = '{0}.{1}'.format(row['table_schema'], row['table_name']) if table_name not in schema: schema[table_name] = {'name': table_name, 'columns': []} - schema[table_name]['columns'].append(row['column_name']) return schema.values() def run_query(self, query, user): + cursor = pyathena.connect(**self.configuration.to_dict()).cursor() + try: - data = { - 'athenaUrl': 'jdbc:awsathena://athena.{}.amazonaws.com:443/'.format(self.configuration['region'].lower()), - 'awsAccessKey': self.configuration['aws_access_key'], - 'awsSecretKey': self.configuration['aws_secret_key'], - 's3StagingDir': self.configuration['s3_staging_dir'], - 'query': query - } - - response = requests.post(PROXY_URL, json=data) - response.raise_for_status() - - json_data = response.content.strip() + cursor.execute(query) + column_tuples = [(i[0], _TYPE_MAPPINGS.get(i[1], None)) for i in cursor.description] + columns = self.fetch_columns(column_tuples) + rows = [dict(zip(([c['name'] for c in columns]), r)) for i, r in enumerate(cursor.fetchall())] + data = {'columns': columns, 'rows': rows} + json_data = json.dumps(data, cls=JSONEncoder) error = None - - return json_data, error - except requests.RequestException as e: - if e.response.status_code == 400: - return None, response.content - - return None, str(e) - except KeyboardInterrupt: - error = "Query cancelled by user." + except Exception, ex: + cursor.cancel() + error = ex.message json_data = None return json_data, error + register(Athena) diff --git a/requirements_all_ds.txt b/requirements_all_ds.txt index fc1ea7b521..7b0d1e7911 100644 --- a/requirements_all_ds.txt +++ b/requirements_all_ds.txt @@ -21,5 +21,6 @@ memsql==2.16.0 snowflake_connector_python==1.3.16 atsd_client==2.0.12 simple_salesforce==0.72.2 +PyAthena>=1.0.0 # certifi is needed to support MongoDB and SSL: certifi From c1f8e2a4e0e6f2deaa5369dcbbea62584ffaad62 Mon Sep 17 00:00:00 2001 From: laughingman7743 Date: Mon, 5 Jun 2017 23:03:32 +0900 Subject: [PATCH 107/243] Add query runner name --- redash/query_runner/athena.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index 2100a5ffe5..6253dd92f0 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -36,6 +36,10 @@ class Athena(BaseQueryRunner): noop_query = 'SELECT 1' + @classmethod + def name(cls): + return "Amazon Athena" + @classmethod def configuration_schema(cls): return { From d5c4d9336f3ae2e668d0139de95bcbad37b7d81c Mon Sep 17 00:00:00 2001 From: laughingman7743 Date: Mon, 5 Jun 2017 23:25:49 +0900 Subject: [PATCH 108/243] Fix configuration schema name to same name as previous query runner --- redash/query_runner/athena.py | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index 6253dd92f0..f314da7dd5 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -39,31 +39,32 @@ class Athena(BaseQueryRunner): @classmethod def name(cls): return "Amazon Athena" - + @classmethod def configuration_schema(cls): return { 'type': 'object', 'properties': { - 'aws_access_key_id': { + 'region': { 'type': 'string', - 'title': 'AWS Access Key ID', + 'title': 'AWS Region' }, - 'aws_secret_access_key': { + 'aws_access_key': { 'type': 'string', - 'title': 'AWS Secret Access Key', + 'title': 'AWS Access Key' }, - 'schema_name': { + 'aws_secret_key': { 'type': 'string', - 'title': 'Schema Name', + 'title': 'AWS Secret Key' }, - 'region_name': { + 's3_staging_dir': { 'type': 'string', - 'title': 'Region Name', + 'title': 'S3 Staging Path' }, - 's3_staging_dir': { + 'schema': { 'type': 'string', - 'title': 'S3 Staging Directory', + 'title': 'Schema Name', + 'default': 'default' }, 'encryption_option': { 'type': 'string', @@ -74,7 +75,7 @@ def configuration_schema(cls): 'title': 'KMS Key', }, }, - 'required': ['region_name', 'schema_name', 's3_staging_dir'] + 'required': ['region', 's3_staging_dir'] } @classmethod @@ -110,7 +111,14 @@ def get_schema(self, get_stats=False): return schema.values() def run_query(self, query, user): - cursor = pyathena.connect(**self.configuration.to_dict()).cursor() + cursor = pyathena.connect( + s3_staging_dir=self.configuration['s3_staging_dir'], + region_name=self.configuration['region_name'], + aws_access_key_id=self.configuration.get('aws_access_key', None), + aws_secret_access_key=self.configuration.get('aws_secret_key', None), + schema_name=self.configuration.get('schema', 'default'), + encryption_option=self.configuration.get('encryption_option', None), + kms_key=self.configuration.get('kms_key', None)).cursor() try: cursor.execute(query) From a6c45da2cab5a31a8f14c0201e3f1ec43cbc9ee3 Mon Sep 17 00:00:00 2001 From: laughingman7743 Date: Mon, 5 Jun 2017 23:26:56 +0900 Subject: [PATCH 109/243] Add KeyboardInterrupt handling --- redash/query_runner/athena.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index f314da7dd5..44e6d0d09e 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -128,6 +128,10 @@ def run_query(self, query, user): data = {'columns': columns, 'rows': rows} json_data = json.dumps(data, cls=JSONEncoder) error = None + except KeyboardInterrupt: + cursor.cancel() + error = "Query cancelled by user." + json_data = None except Exception, ex: cursor.cancel() error = ex.message From ad69a6be3f79516ab6dea6ad0d82517a1af9932c Mon Sep 17 00:00:00 2001 From: laughingman7743 Date: Mon, 5 Jun 2017 23:28:28 +0900 Subject: [PATCH 110/243] Add secret field --- redash/query_runner/athena.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index 44e6d0d09e..f17daf11d3 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -75,7 +75,8 @@ def configuration_schema(cls): 'title': 'KMS Key', }, }, - 'required': ['region', 's3_staging_dir'] + 'required': ['region', 's3_staging_dir'], + 'secret': ['aws_secret_key'] } @classmethod From e385a147f69bdc705618c4c0878273651344f691 Mon Sep 17 00:00:00 2001 From: laughingman7743 Date: Tue, 6 Jun 2017 22:01:44 +0900 Subject: [PATCH 111/243] Add Athena query runner as default query runners --- redash/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/redash/settings.py b/redash/settings.py index a43ed2fd21..8210f48624 100644 --- a/redash/settings.py +++ b/redash/settings.py @@ -165,6 +165,7 @@ def all_settings(): # Query Runners default_query_runners = [ + 'redash.query_runner.athena', 'redash.query_runner.big_query', 'redash.query_runner.google_spreadsheets', 'redash.query_runner.graphite', From 6b22c2c5417c7a6819c4f7802cf93dc38937c2ae Mon Sep 17 00:00:00 2001 From: Alexander Shepelin Date: Wed, 7 Jun 2017 23:03:05 +0300 Subject: [PATCH 112/243] Same view for input on search result page as in header --- .../pages/queries/queries-search-results-page.html | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/client/app/pages/queries/queries-search-results-page.html b/client/app/pages/queries/queries-search-results-page.html index 4f8c2aaf24..bc5ac9162c 100644 --- a/client/app/pages/queries/queries-search-results-page.html +++ b/client/app/pages/queries/queries-search-results-page.html @@ -1,12 +1,14 @@
-
- -
- +
+ + + + +
From 411ef7bd00c7165a4f5db4c68f64bcb019877652 Mon Sep 17 00:00:00 2001 From: Alexander Shepelin Date: Wed, 7 Jun 2017 23:27:39 +0300 Subject: [PATCH 113/243] Add some space between parameters --- client/app/components/parameters.html | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/client/app/components/parameters.html b/client/app/components/parameters.html index b2b493a67d..f0eadd3b41 100644 --- a/client/app/components/parameters.html +++ b/client/app/components/parameters.html @@ -1,7 +1,15 @@ -
-
+
+
- + From 94077ccafd7981ccb8ea86b08f23539723e75e4e Mon Sep 17 00:00:00 2001 From: Shalom Yerushalmy Date: Thu, 8 Jun 2017 15:44:09 +0300 Subject: [PATCH 114/243] Fixed cassandra DS bug by adding port to connection string --- redash/query_runner/cass.py | 2 ++ redash/query_runner/google_spanner.py | 0 2 files changed, 2 insertions(+) create mode 100644 redash/query_runner/google_spanner.py diff --git a/redash/query_runner/cass.py b/redash/query_runner/cass.py index b211cbe275..418e7f4cb8 100644 --- a/redash/query_runner/cass.py +++ b/redash/query_runner/cass.py @@ -92,9 +92,11 @@ def run_query(self, query, user): password='{}'.format(self.configuration.get('password', ''))) connection = Cluster([self.configuration.get('host', '')], auth_provider=auth_provider, + port=self.configuration.get('port', ''), protocol_version=self.configuration.get('protocol', 3)) else: connection = Cluster([self.configuration.get('host', '')], + port=self.configuration.get('port', ''), protocol_version=self.configuration.get('protocol', 3)) session = connection.connect() session.set_keyspace(self.configuration['keyspace']) diff --git a/redash/query_runner/google_spanner.py b/redash/query_runner/google_spanner.py new file mode 100644 index 0000000000..e69de29bb2 From 1fad874deeb5da022c3cdbebefe80abdcfddd47b Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Mon, 12 Jun 2017 09:47:08 +0300 Subject: [PATCH 115/243] Change: redirect to / when org not found --- redash/authentication/__init__.py | 6 +++--- redash/authentication/org_resolving.py | 2 +- tests/test_authentication.py | 18 +++++++++++++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/redash/authentication/__init__.py b/redash/authentication/__init__.py index ddbbedc8e3..1d75296f92 100644 --- a/redash/authentication/__init__.py +++ b/redash/authentication/__init__.py @@ -16,7 +16,9 @@ def get_login_url(external=False, next="/"): - if settings.MULTI_ORG: + if settings.MULTI_ORG and current_org == None: + login_url = '/' + elif settings.MULTI_ORG: login_url = url_for('redash.login', org_slug=current_org.slug, next=next, _external=external) else: login_url = url_for('redash.login', next=next, _external=external) @@ -155,5 +157,3 @@ def setup_authentication(app): else: logger.warning("Unknown authentication type ({}). Using default (HMAC).".format(settings.AUTH_TYPE)) login_manager.request_loader(hmac_load_user_from_request) - - diff --git a/redash/authentication/org_resolving.py b/redash/authentication/org_resolving.py index a79d4945a3..52f7309cd3 100644 --- a/redash/authentication/org_resolving.py +++ b/redash/authentication/org_resolving.py @@ -7,7 +7,7 @@ import logging -from flask import request, g +from flask import g, request from werkzeug.local import LocalProxy from redash.models import Organization diff --git a/tests/test_authentication.py b/tests/test_authentication.py index c06d6e78e3..f527e94c33 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -2,12 +2,14 @@ from flask import request from mock import patch -from redash import models +from tests import BaseTestCase + +from redash import models, settings from redash.authentication import (api_key_load_user_from_request, - hmac_load_user_from_request, sign) + get_login_url, hmac_load_user_from_request, + sign) from redash.authentication.google_oauth import (create_and_login_user, verify_profile) -from tests import BaseTestCase class TestApiKeyAuthentication(BaseTestCase): @@ -169,3 +171,13 @@ def test_user_not_in_domain_but_account_exists(self): self.factory.create_user(email='arik@example.com') self.factory.org.settings[models.Organization.SETTING_GOOGLE_APPS_DOMAINS] = ['example.org'] self.assertTrue(verify_profile(self.factory.org, profile)) + + +class TestGetLoginUrl(BaseTestCase): + def test_when_multi_org_enabled_and_org_exists(self): + with self.app.test_request_context('/{}/'.format(self.factory.org.slug)): + self.assertEqual(get_login_url(next=None), '/{}/login'.format(self.factory.org.slug)) + + def test_when_multi_org_enabled_and_org_doesnt_exist(self): + with self.app.test_request_context('/{}_notexists/'.format(self.factory.org.slug)): + self.assertEqual(get_login_url(next=None), '/') From 5056d2fa90f3d1a02c4d43bf3e5c2464d749b2c2 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Mon, 12 Jun 2017 11:35:05 +0300 Subject: [PATCH 116/243] Fix: table name wasn't found for count queries. --- redash/handlers/base.py | 5 ++--- redash/metrics/database.py | 31 ++++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/redash/handlers/base.py b/redash/handlers/base.py index 6c24f3bab8..c5917c923d 100644 --- a/redash/handlers/base.py +++ b/redash/handlers/base.py @@ -1,16 +1,15 @@ import time from flask import Blueprint, current_app, request + from flask_login import current_user, login_required from flask_restful import Resource, abort - -from sqlalchemy.orm.exc import NoResultFound - from redash import settings from redash.authentication import current_org from redash.models import ApiUser from redash.tasks import record_event as record_event_task from redash.utils import json_dumps +from sqlalchemy.orm.exc import NoResultFound routes = Blueprint('redash', __name__, template_folder=settings.fix_assets_path('templates')) diff --git a/redash/metrics/database.py b/redash/metrics/database.py index 405873a8c4..6ab1f4cb3b 100644 --- a/redash/metrics/database.py +++ b/redash/metrics/database.py @@ -1,15 +1,27 @@ -import time import logging +import time + +from flask import g, has_request_context +from redash import statsd_client from sqlalchemy.engine import Engine -from sqlalchemy.orm.util import _ORMJoin from sqlalchemy.event import listens_for +from sqlalchemy.orm.util import _ORMJoin +from sqlalchemy.sql.selectable import Alias -from flask import has_request_context, g +metrics_logger = logging.getLogger("metrics") -from redash import statsd_client -metrics_logger = logging.getLogger("metrics") +def _table_name_from_select_element(elt): + t = elt.froms[0] + + if isinstance(t, Alias): + t = t.original.froms[0] + + while isinstance(t, _ORMJoin): + t = t.left + + return t.name @listens_for(Engine, "before_execute") @@ -23,10 +35,11 @@ def after_execute(conn, elt, multiparams, params, result): action = elt.__class__.__name__ if action == 'Select': - t = elt.froms[0] - while isinstance(t, _ORMJoin): - t = t.left - name = t.name + name = 'unknown' + try: + name = _table_name_from_select_element(elt) + except Exception: + logging.exception('Failed finding table name.') elif action in ['Update', 'Insert', 'Delete']: name = elt.table.name else: From 7d5d7c4a6b6c609ed925f48bfebd114dbdf152dc Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Mon, 12 Jun 2017 11:35:44 +0300 Subject: [PATCH 117/243] Change: report endpoints without dots for metrics --- redash/metrics/request.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/redash/metrics/request.py b/redash/metrics/request.py index aa667c35b6..480625ac46 100644 --- a/redash/metrics/request.py +++ b/redash/metrics/request.py @@ -3,6 +3,7 @@ from collections import namedtuple from flask import g, request + from redash import statsd_client metrics_logger = logging.getLogger("metrics") @@ -19,19 +20,20 @@ def calculate_metrics(response): request_duration = (time.time() - g.start_time) * 1000 queries_duration = g.get('queries_duration', 0.0) queries_count = g.get('queries_count', 0.0) + endpoint = (request.endpoint or 'unknown').replace('.', '_') metrics_logger.info("method=%s path=%s endpoint=%s status=%d content_type=%s content_length=%d duration=%.2f query_count=%d query_duration=%.2f", request.method, request.path, - request.endpoint, + endpoint, response.status_code, response.content_type, - response.content_length, + response.content_length or -1, request_duration, queries_count, queries_duration) - statsd_client.timing('requests.{}.{}'.format(request.endpoint, request.method.lower()), request_duration) + statsd_client.timing('requests.{}.{}'.format(endpoint, request.method.lower()), request_duration) return response From 3dfab5009cf5286b992f4256c40917facfb72297 Mon Sep 17 00:00:00 2001 From: Alexander Shepelin Date: Mon, 12 Jun 2017 12:02:03 +0300 Subject: [PATCH 118/243] put parameter label on top of input --- client/app/assets/css/redash.css | 4 ++++ client/app/components/parameters.html | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/client/app/assets/css/redash.css b/client/app/assets/css/redash.css index 3ddd463a76..3cc6b9b545 100644 --- a/client/app/assets/css/redash.css +++ b/client/app/assets/css/redash.css @@ -437,6 +437,10 @@ counter-renderer counter-name { border: 1px solid rgba(0,0,0,.15); } +.parameter-label { + display: block; +} + div.table-name { overflow: hidden; text-overflow: ellipsis; diff --git a/client/app/components/parameters.html b/client/app/components/parameters.html index f0eadd3b41..3d069bc6a2 100644 --- a/client/app/components/parameters.html +++ b/client/app/components/parameters.html @@ -4,7 +4,7 @@ ng-model="parameters">
- +
diff --git a/client/app/visualizations/edit-visualization-dialog.js b/client/app/visualizations/edit-visualization-dialog.js index 6baefb2c6d..add51f1db1 100644 --- a/client/app/visualizations/edit-visualization-dialog.js +++ b/client/app/visualizations/edit-visualization-dialog.js @@ -19,6 +19,9 @@ const EditVisualizationDialog = { this.visualization = copy(this.originalVisualization); this.visTypes = Visualization.visualizationTypes; + this.warning_three_column_groupby = 'You have more than 2 columns in your result set. To ensure the chart is accurate, please do one of the following:
  • Change the SQL query to give 2 result columns. You can CONCAT() columns together if you wish.
  • Select column(s) to group by.
'; + this.warning_three_column_stacking = 'You have more than 2 columns in your result set. You may wish to make the Stacking option equal to `Enabled` or `Percent`.'; + this.newVisualization = () => ({ type: Visualization.defaultVisualization.type, @@ -47,6 +50,21 @@ const EditVisualizationDialog = { } }; + this.has3plusColumnsFunction = () => { + let has3plusColumns = false; + if ((JSON.stringify(this.visualization.options.columnMapping).match(/,/g) || []).length > 1) { + has3plusColumns = true; + } + return has3plusColumns; + }; + + this.disableSubmit = () => { + if (this.has3plusColumnsFunction() && JSON.stringify(this.visualization.options.columnMapping).includes('unused')) { + return true; + } + return false; + }; + this.submit = () => { if (this.visualization.id) { Events.record('update', 'visualization', this.visualization.id, { type: this.visualization.type }); From df945a12b0830fbe078f5efe6c0f3287ea15ac69 Mon Sep 17 00:00:00 2001 From: laughingman7743 Date: Sat, 24 Jun 2017 20:53:35 +0900 Subject: [PATCH 140/243] Fix query cancellation condition --- redash/query_runner/athena.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index 627acfbfc4..a4e4e5a6a6 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -130,11 +130,13 @@ def run_query(self, query, user): json_data = json.dumps(data, cls=JSONEncoder) error = None except KeyboardInterrupt: - cursor.cancel() + if cursor.query_id: + cursor.cancel() error = "Query cancelled by user." json_data = None except Exception, ex: - cursor.cancel() + if cursor.query_id: + cursor.cancel() error = ex.message json_data = None From dac7bcbe40bd1198af2d32dbe4dac2d09a3332db Mon Sep 17 00:00:00 2001 From: Alison Date: Sat, 24 Jun 2017 15:49:47 -0500 Subject: [PATCH 141/243] working version on data source config page --- client/app/pages/data-sources/show.js | 22 ++++++++++++++++++++++ client/app/services/data-source.js | 1 + redash/cli/data_sources.py | 24 ++++++++++++++++++++++++ redash/handlers/api.py | 3 ++- redash/handlers/data_sources.py | 13 +++++++++++++ redash/query_runner/__init__.py | 13 +++++++++++++ redash/query_runner/pg.py | 2 ++ 7 files changed, 77 insertions(+), 1 deletion(-) diff --git a/client/app/pages/data-sources/show.js b/client/app/pages/data-sources/show.js index 1b0d4e9478..3e23c575dc 100644 --- a/client/app/pages/data-sources/show.js +++ b/client/app/pages/data-sources/show.js @@ -50,9 +50,31 @@ function DataSourceCtrl($scope, $routeParams, $http, $location, toastr, }); } + function getDataSourceVersion(callback) { + Events.record('test', 'data_source_version', $scope.dataSource.id); + + DataSource.version( + { id: $scope.dataSource.id }, (httpResponse) => { + console.log(httpResponse); + if (httpResponse.ok) { + const versionNumber = httpResponse.message; + toastr.success(`Success. Verison: ${versionNumber}`); + } else { + toastr.error(httpResponse.message, 'Version Test Failed:', { timeOut: 10000 }); + } + callback(); + }, (httpResponse) => { + console.log(httpResponse); + logger('Failed to get data source version: ', httpResponse.status, httpResponse.statusText, httpResponse); + toastr.error('Unknown error occurred while performing data source version test. Please try again later.', 'Data Source Version Test Failed:', { timeOut: 10000 }); + callback(); + }); + } + $scope.actions = [ { name: 'Delete', class: 'btn-danger', callback: deleteDataSource }, { name: 'Test Connection', class: 'btn-default', callback: testConnection, disableWhenDirty: true }, + { name: 'Test Data Source Version', class: 'btn-default', callback: getDataSourceVersion, disableWhenDirty: true }, ]; } diff --git a/client/app/services/data-source.js b/client/app/services/data-source.js index b6d4237929..c8fd795059 100644 --- a/client/app/services/data-source.js +++ b/client/app/services/data-source.js @@ -4,6 +4,7 @@ function DataSource($resource) { query: { method: 'GET', cache: false, isArray: true }, test: { method: 'POST', cache: false, isArray: false, url: 'api/data_sources/:id/test' }, getSchema: { method: 'GET', cache: false, isArray: true, url: 'api/data_sources/:id/schema' }, + version: { method: 'GET', cache: false, isArray: false, url: 'api/data_sources/:id/version' }, }; const DataSourceResource = $resource('api/data_sources/:id', { id: '@id' }, actions); diff --git a/redash/cli/data_sources.py b/redash/cli/data_sources.py index ed1dd3de70..18d9f8939e 100644 --- a/redash/cli/data_sources.py +++ b/redash/cli/data_sources.py @@ -66,6 +66,30 @@ def test(name, organization='default'): print "Couldn't find data source named: {}".format(name) exit(1) +@manager.command() +@click.argument('name') +@click.option('--org', 'organization', default='default', + help="The organization the user belongs to " + "(leave blank for 'default').") +def get_data_source_version(name, organization='default'): + """Get version of data source connection by issuing a trivial query.""" + try: + org = models.Organization.get_by_slug(organization) + data_source = models.DataSource.query.filter( + models.DataSource.name == name, + models.DataSource.org == org).one() + print "Testing get connection data source version: {} (id={})".format( + name, data_source.id) + try: + info = data_source.query_runner.get_data_source_version() + except Exception, e: + print "Failure: {}".format(e) + exit(1) + else: + print info + except NoResultFound: + print "Couldn't find data source named: {}".format(name) + exit(1) @manager.command() @click.argument('name', default=None, required=False) diff --git a/redash/handlers/api.py b/redash/handlers/api.py index db77b43241..a6786c12d1 100644 --- a/redash/handlers/api.py +++ b/redash/handlers/api.py @@ -7,7 +7,7 @@ from redash.handlers.permissions import ObjectPermissionsListResource, CheckPermissionResource from redash.handlers.alerts import AlertResource, AlertListResource, AlertSubscriptionListResource, AlertSubscriptionResource from redash.handlers.dashboards import DashboardListResource, RecentDashboardsResource, DashboardResource, DashboardShareResource, PublicDashboardResource -from redash.handlers.data_sources import DataSourceTypeListResource, DataSourceListResource, DataSourceSchemaResource, DataSourceResource, DataSourcePauseResource, DataSourceTestResource +from redash.handlers.data_sources import DataSourceTypeListResource, DataSourceListResource, DataSourceSchemaResource, DataSourceResource, DataSourcePauseResource, DataSourceTestResource, DataSourceVersionResource from redash.handlers.events import EventResource from redash.handlers.queries import ( MyQueriesResource, QueryForkResource, QueryListResource, @@ -58,6 +58,7 @@ def json_representation(data, code, headers=None): api.add_org_resource(DataSourceSchemaResource, '/api/data_sources//schema') api.add_org_resource(DataSourcePauseResource, '/api/data_sources//pause') api.add_org_resource(DataSourceTestResource, '/api/data_sources//test') +api.add_org_resource(DataSourceVersionResource, '/api/data_sources//version') api.add_org_resource(DataSourceResource, '/api/data_sources/', endpoint='data_source') api.add_org_resource(GroupListResource, '/api/groups', endpoint='groups') diff --git a/redash/handlers/data_sources.py b/redash/handlers/data_sources.py index 6dcf17ef3f..fea3885642 100644 --- a/redash/handlers/data_sources.py +++ b/redash/handlers/data_sources.py @@ -164,3 +164,16 @@ def post(self, data_source_id): return {"message": unicode(e), "ok": False} else: return {"message": "success", "ok": True} + +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) + try: + version_info = data_source.query_runner.get_data_source_version() + except Exception as e: + return {"message": unicode(e), "ok": False} + else: + return {"message": version_info, "ok": True} + + diff --git a/redash/query_runner/__init__.py b/redash/query_runner/__init__.py index f1968a980c..175dc2e31f 100644 --- a/redash/query_runner/__init__.py +++ b/redash/query_runner/__init__.py @@ -73,6 +73,19 @@ def annotate_query(cls): def configuration_schema(cls): return {} + def get_data_source_version(self): + if self.data_source_version_query is None: + raise NotImplementedError + data, error = self.run_query(self.data_source_version_query, None) + version = json.loads(data)['rows'][0]['version'] + if(self.data_source_version_post_process == "split by space take second"): + version = version.split(" ")[1] + + if error is not None: + raise Exception(error) + + return version + def test_connection(self): if self.noop_query is None: raise NotImplementedError() diff --git a/redash/query_runner/pg.py b/redash/query_runner/pg.py index f2a1d4604f..ac97015f6f 100644 --- a/redash/query_runner/pg.py +++ b/redash/query_runner/pg.py @@ -48,6 +48,8 @@ def _wait(conn, timeout=None): class PostgreSQL(BaseSQLQueryRunner): noop_query = "SELECT 1" default_doc_url = "https://www.postgresql.org/docs/current/" + data_source_version_query = "select version()" + data_source_version_post_process = "split by space take second" @classmethod def configuration_schema(cls): From d6a589a4a2d0bf62e050d4979ae9ca199eaa42b1 Mon Sep 17 00:00:00 2001 From: Alison Date: Sat, 24 Jun 2017 20:29:03 -0500 Subject: [PATCH 142/243] working postgres version --- client/app/pages/data-sources/show.js | 2 -- .../pages/queries/get-data-source-version.js | 18 ++++++++++++++++++ client/app/pages/queries/index.js | 2 ++ client/app/pages/queries/query.html | 2 ++ 4 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 client/app/pages/queries/get-data-source-version.js diff --git a/client/app/pages/data-sources/show.js b/client/app/pages/data-sources/show.js index 3e23c575dc..5971478b56 100644 --- a/client/app/pages/data-sources/show.js +++ b/client/app/pages/data-sources/show.js @@ -55,7 +55,6 @@ function DataSourceCtrl($scope, $routeParams, $http, $location, toastr, DataSource.version( { id: $scope.dataSource.id }, (httpResponse) => { - console.log(httpResponse); if (httpResponse.ok) { const versionNumber = httpResponse.message; toastr.success(`Success. Verison: ${versionNumber}`); @@ -64,7 +63,6 @@ function DataSourceCtrl($scope, $routeParams, $http, $location, toastr, } callback(); }, (httpResponse) => { - console.log(httpResponse); logger('Failed to get data source version: ', httpResponse.status, httpResponse.statusText, httpResponse); toastr.error('Unknown error occurred while performing data source version test. Please try again later.', 'Data Source Version Test Failed:', { timeOut: 10000 }); callback(); diff --git a/client/app/pages/queries/get-data-source-version.js b/client/app/pages/queries/get-data-source-version.js new file mode 100644 index 0000000000..11f8dbccfa --- /dev/null +++ b/client/app/pages/queries/get-data-source-version.js @@ -0,0 +1,18 @@ +function GetDataSourceVersionCtrl(Events, toastr, $scope, DataSource) { + // 'ngInject'; + + this.getDataSourceVersion = DataSource.version({ id: 6 }); +} + +const GetDataSourceVersionInfo = { + bindings: { + schema: '<', + onRefresh: '&', + }, + controller: GetDataSourceVersionCtrl, + template: '{{ $ctrl.getDataSourceVersion.message }}', +}; + +export default function (ngModule) { + ngModule.component('getDataSourceVersion', GetDataSourceVersionInfo); +} diff --git a/client/app/pages/queries/index.js b/client/app/pages/queries/index.js index b1c679c134..cc0daab198 100644 --- a/client/app/pages/queries/index.js +++ b/client/app/pages/queries/index.js @@ -10,6 +10,7 @@ import registerAlertUnsavedChanges from './alert-unsaved-changes'; import registerQuerySearchResultsPage from './queries-search-results-page'; import registerVisualizationEmbed from './visualization-embed'; import registerCompareQueryDialog from './compare-query-dialog'; +import registerGetDataSourceVersion from './get-data-source-version'; export default function (ngModule) { registerQueryResultsLink(ngModule); @@ -21,6 +22,7 @@ export default function (ngModule) { registerVisualizationEmbed(ngModule); registerCompareQueryDialog(ngModule); registerApiKeyDialog(ngModule); + registerGetDataSourceVersion(ngModule); return Object.assign({}, registerQuerySearchResultsPage(ngModule), registerSourceView(ngModule), diff --git a/client/app/pages/queries/query.html b/client/app/pages/queries/query.html index 973740c79f..60fbdcc92e 100644 --- a/client/app/pages/queries/query.html +++ b/client/app/pages/queries/query.html @@ -104,6 +104,8 @@

ng-options="ds.id as ds.name for ds in dataSources"> {{dataSource.type_name}} documentation {{dataSource.type_name}} + +
diff --git a/client/app/pages/dashboards/edit-dashboard-dialog.js b/client/app/pages/dashboards/edit-dashboard-dialog.js index 5d0cc6ee13..14f7dfa51c 100644 --- a/client/app/pages/dashboards/edit-dashboard-dialog.js +++ b/client/app/pages/dashboards/edit-dashboard-dialog.js @@ -1,4 +1,4 @@ -import { sortBy } from 'underscore'; +import { isEmpty, sortBy } from 'underscore'; import template from './edit-dashboard-dialog.html'; const EditDashboardDialog = { @@ -45,6 +45,8 @@ const EditDashboardDialog = { }); } + this.isFormValid = () => !isEmpty(this.dashboard.name); + this.saveDashboard = () => { this.saveInProgress = true; From c8adf322a9a2734f2d743e2b184a5028cce1e655 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Wed, 28 Jun 2017 22:11:05 +0300 Subject: [PATCH 153/243] Allow sorting alerts --- client/app/components/index.js | 1 + client/app/components/sort-icon.js | 26 +++++++++++++++++++ client/app/pages/alerts-list/alerts-list.html | 12 ++++----- client/app/pages/alerts-list/index.js | 26 ++++++++++--------- client/app/utils/paginator.js | 20 ++++++++++++++ 5 files changed, 67 insertions(+), 18 deletions(-) create mode 100644 client/app/components/sort-icon.js diff --git a/client/app/components/index.js b/client/app/components/index.js index fae91fcfc7..17cbf07d64 100644 --- a/client/app/components/index.js +++ b/client/app/components/index.js @@ -18,3 +18,4 @@ export { default as rdTimeAgo } from './rd-time-ago'; export { default as overlay } from './overlay'; export { default as routeStatus } from './route-status'; export { default as filters } from './filters'; +export { default as sortIcon } from './sort-icon'; diff --git a/client/app/components/sort-icon.js b/client/app/components/sort-icon.js new file mode 100644 index 0000000000..adc1ca58d2 --- /dev/null +++ b/client/app/components/sort-icon.js @@ -0,0 +1,26 @@ +export default function (ngModule) { + ngModule.component('sortIcon', { + template: '', + bindings: { + column: '<', + sortColumn: '<', + reverse: '<', + }, + controller() { + this.$onChanges = (changes) => { + ['column', 'sortColumn', 'reverse'].forEach((v) => { + if (v in changes) { + this[v] = changes[v].currentValue; + } + }); + + this.showIcon = false; + + if (this.column === this.sortColumn) { + this.showIcon = true; + this.icon = this.reverse ? 'desc' : 'asc'; + } + }; + }, + }); +} diff --git a/client/app/pages/alerts-list/alerts-list.html b/client/app/pages/alerts-list/alerts-list.html index eeee9cee08..d85ea0ea92 100644 --- a/client/app/pages/alerts-list/alerts-list.html +++ b/client/app/pages/alerts-list/alerts-list.html @@ -4,20 +4,20 @@
-
+
- - - - + + + + - + diff --git a/client/app/pages/alerts-list/index.js b/client/app/pages/alerts-list/index.js index 4e315e9129..ac69bda693 100644 --- a/client/app/pages/alerts-list/index.js +++ b/client/app/pages/alerts-list/index.js @@ -1,24 +1,26 @@ import { Paginator } from '../../utils'; import template from './alerts-list.html'; +const stateClass = { + ok: 'label label-success', + triggered: 'label label-danger', + unknown: 'label label-warning', +}; + class AlertsListCtrl { constructor(Events, Alert) { Events.record('view', 'page', 'alerts'); this.alerts = new Paginator([], { itemsPerPage: 20 }); - Alert.query((alerts) => { - const stateClass = { - ok: 'label label-success', - triggered: 'label label-danger', - unknown: 'label label-warning', - }; - - alerts.forEach((alert) => { - alert.class = stateClass[alert.state]; - }); - - this.alerts.updateRows(alerts); + this.alerts.updateRows(alerts.map(alert => ({ + name: alert.name, + state: alert.state, + class: stateClass[alert.state], + created_by: alert.user.name, + created_at: alert.created_at, + updated_at: alert.updated_at, + }))); }); } } diff --git a/client/app/utils/paginator.js b/client/app/utils/paginator.js index 4d28c1fa95..546f9a0fd4 100644 --- a/client/app/utils/paginator.js +++ b/client/app/utils/paginator.js @@ -1,8 +1,12 @@ +import { sortBy } from 'underscore'; + export default class Paginator { constructor(rows, { page = 1, itemsPerPage = 20, totalCount = undefined } = {}) { this.page = page; this.itemsPerPage = itemsPerPage; this.updateRows(rows, totalCount); + this.orderByField = undefined; + this.orderByReverse = false; } setPage(page) { @@ -24,4 +28,20 @@ export default class Paginator { this.totalCount = 0; } } + + orderBy(column) { + if (column === this.orderByField) { + this.orderByReverse = !this.orderByReverse; + } else { + this.orderByField = column; + this.orderByReverse = false; + } + + if (this.orderByField) { + this.rows = sortBy(this.rows, this.orderByField); + if (this.orderByReverse) { + this.rows = this.rows.reverse(); + } + } + } } From f84068137709105b7a0de91f2da26f46cd7eb8a9 Mon Sep 17 00:00:00 2001 From: mitsuhiro_yamashita Date: Thu, 29 Jun 2017 08:21:22 +0900 Subject: [PATCH 154/243] change this to WRITER_ENCODING and REDASH_CSV_WRITER_ENCODING --- redash/utils/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/redash/utils/__init__.py b/redash/utils/__init__.py index 03b5cdfadc..a9cc954dc7 100644 --- a/redash/utils/__init__.py +++ b/redash/utils/__init__.py @@ -18,7 +18,7 @@ from redash import settings COMMENTS_REGEX = re.compile("/\*.*?\*/") -WRITER_ENCODE = os.environ.get('REDASH_WRITER_ENCODE', 'utf-8') +WRITER_ENCODING = os.environ.get('REDASH_CSV_WRITER_ENCODING', 'utf-8') def utcnow(): @@ -104,7 +104,7 @@ class UnicodeWriter: which is encoded in the given encoding. """ - def __init__(self, f, dialect=csv.excel, encoding=WRITER_ENCODE, **kwds): + def __init__(self, f, dialect=csv.excel, encoding=WRITER_ENCODING, **kwds): # Redirect output to a queue self.queue = cStringIO.StringIO() self.writer = csv.writer(self.queue, dialect=dialect, **kwds) @@ -113,7 +113,7 @@ def __init__(self, f, dialect=csv.excel, encoding=WRITER_ENCODE, **kwds): def _encode_utf8(self, val): if isinstance(val, (unicode, str)): - return val.encode(WRITER_ENCODE) + return val.encode(WRITER_ENCODING) return val @@ -121,7 +121,7 @@ def writerow(self, row): self.writer.writerow([self._encode_utf8(s) for s in row]) # Fetch UTF-8 output from the queue ... data = self.queue.getvalue() - data = data.decode(WRITER_ENCODE) + data = data.decode(WRITER_ENCODING) # ... and reencode it into the target encoding data = self.encoder.encode(data) # write to the target stream From 019a09945ecce7d4461a4bdb32cfc585cb3d9872 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 29 Jun 2017 09:49:06 +0300 Subject: [PATCH 155/243] Update boto version to support Athena --- requirements_all_ds.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_all_ds.txt b/requirements_all_ds.txt index 7b0d1e7911..382d0a91e1 100644 --- a/requirements_all_ds.txt +++ b/requirements_all_ds.txt @@ -12,7 +12,7 @@ td-client==0.8.0 pymssql==2.1.3 dql==0.5.16 dynamo3==0.4.7 -botocore==1.4.4 +botocore==1.5.72 sasl>=0.1.3 thrift>=0.8.0 thrift_sasl>=0.1.0 From 248c54054318c748c0ed6c4b2f5cc96c2f7bc28a Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 29 Jun 2017 10:57:32 +0300 Subject: [PATCH 156/243] Add: ability to set dashboard level filters from UI Closes #1855. --- client/app/pages/dashboards/dashboard.js | 10 +++++++++- client/app/pages/dashboards/edit-dashboard-dialog.html | 7 +++++++ client/app/pages/dashboards/edit-dashboard-dialog.js | 1 + redash/handlers/dashboards.py | 2 +- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/client/app/pages/dashboards/dashboard.js b/client/app/pages/dashboards/dashboard.js index 20caeebec0..7deae60197 100644 --- a/client/app/pages/dashboards/dashboard.js +++ b/client/app/pages/dashboards/dashboard.js @@ -155,12 +155,20 @@ function DashboardCtrl($rootScope, $routeParams, $location, $timeout, $q, $uibMo }; this.editDashboard = () => { + const previousFiltersState = this.dashboard.dashboard_filters_enabled; $uibModal.open({ component: 'editDashboardDialog', resolve: { dashboard: () => this.dashboard, }, - }).result.then((dashboard) => { this.dashboard = dashboard; }); + }).result.then((dashboard) => { + const shouldRenderDashboard = !previousFiltersState && dashboard.dashboard_filters_enabled; + this.dashboard = dashboard; + + if (shouldRenderDashboard) { + renderDashboard(this.dashboard); + } + }); }; this.addWidget = () => { diff --git a/client/app/pages/dashboards/edit-dashboard-dialog.html b/client/app/pages/dashboards/edit-dashboard-dialog.html index a8ee43001d..650e44f1e2 100644 --- a/client/app/pages/dashboards/edit-dashboard-dialog.html +++ b/client/app/pages/dashboards/edit-dashboard-dialog.html @@ -7,6 +7,13 @@

+

+ +

+
  • diff --git a/client/app/pages/dashboards/edit-dashboard-dialog.js b/client/app/pages/dashboards/edit-dashboard-dialog.js index 14f7dfa51c..ffa4c5fd5b 100644 --- a/client/app/pages/dashboards/edit-dashboard-dialog.js +++ b/client/app/pages/dashboards/edit-dashboard-dialog.js @@ -67,6 +67,7 @@ const EditDashboardDialog = { slug: this.dashboard.id, name: this.dashboard.name, version: this.dashboard.version, + dashboard_filters_enabled: this.dashboard.dashboard_filters_enabled, layout: JSON.stringify(layout), }; diff --git a/redash/handlers/dashboards.py b/redash/handlers/dashboards.py index aa0f664762..21564e05ee 100644 --- a/redash/handlers/dashboards.py +++ b/redash/handlers/dashboards.py @@ -129,7 +129,7 @@ def post(self, dashboard_slug): require_object_modify_permission(dashboard, self.current_user) updates = project(dashboard_properties, ('name', 'layout', 'version', - 'is_draft')) + 'is_draft', 'dashboard_filters_enabled')) # SQLAlchemy handles the case where a concurrent transaction beats us # to the update. But we still have to make sure that we're not starting From 831dfe6c8d8d5840fffd451ae357dfbc7200783e Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 29 Jun 2017 12:11:15 +0300 Subject: [PATCH 157/243] Enable strict checking for Angular DI --- client/app/index.html | 2 +- client/app/multi_org.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/app/index.html b/client/app/index.html index 424d101b0c..b7e2d2087c 100644 --- a/client/app/index.html +++ b/client/app/index.html @@ -1,5 +1,5 @@ - + diff --git a/client/app/multi_org.html b/client/app/multi_org.html index 44673f7a45..a8d673772b 100644 --- a/client/app/multi_org.html +++ b/client/app/multi_org.html @@ -1,5 +1,5 @@ - + From f64769cc80329aa331869cb54e00bf43f97b119a Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 29 Jun 2017 12:11:34 +0300 Subject: [PATCH 158/243] Disable Angular debug info (should improve performance) --- client/app/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/app/index.js b/client/app/index.js index 520719121d..2279cb7f8f 100644 --- a/client/app/index.js +++ b/client/app/index.js @@ -100,6 +100,7 @@ registerVisualizations(ngModule); ngModule.config(($routeProvider, $locationProvider, $compileProvider, uiSelectConfig, toastrConfig) => { + $compileProvider.debugInfoEnabled(false); $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|http|data):/); $locationProvider.html5Mode(true); uiSelectConfig.theme = 'bootstrap'; From c8ad866a53ab9e22d18a5df736a5f4c5775d5f36 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 29 Jun 2017 12:21:09 +0300 Subject: [PATCH 159/243] Add CLI command to open IPython shell --- redash/cli/__init__.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/redash/cli/__init__.py b/redash/cli/__init__.py index 7fb550eb31..0176bcdf0c 100644 --- a/redash/cli/__init__.py +++ b/redash/cli/__init__.py @@ -68,3 +68,23 @@ def send_test_mail(email=None): mail.send(Message(subject="Test Message from Redash", recipients=[email], body="Test message.")) + +@manager.command() +def ipython(): + """Starts IPython shell instead of the default Python shell.""" + import sys + import IPython + from flask.globals import _app_ctx_stack + app = _app_ctx_stack.top.app + + banner = 'Python %s on %s\nIPython: %s\nRedash version: %s\n' % ( + sys.version, + sys.platform, + IPython.__version__, + __version__ + ) + + ctx = {} + ctx.update(app.make_shell_context()) + + IPython.embed(banner1=banner, user_ns=ctx) From dfd16f3d7acdb07b616bb0848298f4e5509b58d6 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Thu, 29 Jun 2017 16:04:50 +0300 Subject: [PATCH 160/243] Add: ability to customize Athena configuration schema --- redash/query_runner/athena.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index 022f15ae8b..40eb89caeb 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -8,6 +8,8 @@ logger = logging.getLogger(__name__) ANNOTATE_QUERY = parse_boolean(os.environ.get('ATHENA_ANNOTATE_QUERY', 'true')) +SHOW_EXTRA_SETTINGS = parse_boolean(os.environ.get('ATHENA_SHOW_EXTRA_SETTINGS', 'true')) +OPTIONAL_CREDENTIALS = parse_boolean(os.environ.get('ATHENA_OPTIONAL_CREDENTIALS', 'true')) try: import pyathena @@ -48,7 +50,7 @@ def name(cls): @classmethod def configuration_schema(cls): - return { + schema = { 'type': 'object', 'properties': { 'region': { @@ -72,6 +74,14 @@ def configuration_schema(cls): 'title': 'Schema Name', 'default': 'default' }, + }, + 'required': ['region', 's3_staging_dir'], + 'order': ['region', 'aws_access_key', 'aws_secret_key', 's3_staging_dir', 'schema'], + 'secret': ['aws_secret_key'] + } + + if SHOW_EXTRA_SETTINGS: + schema['properties'].update({ 'encryption_option': { 'type': 'string', 'title': 'Encryption Option', @@ -80,10 +90,12 @@ def configuration_schema(cls): 'type': 'string', 'title': 'KMS Key', }, - }, - 'required': ['region', 's3_staging_dir'], - 'secret': ['aws_secret_key'] - } + }) + + if not OPTIONAL_CREDENTIALS: + schema['required'] += ['aws_access_key', 'aws_secret_key'] + + return schema @classmethod def enabled(cls): From e445fa436eb75303dd7cf42fd40e93618a54843c Mon Sep 17 00:00:00 2001 From: Amar Ramachandran Date: Thu, 29 Jun 2017 10:43:27 -0700 Subject: [PATCH 161/243] 404 on forgot endpoint if password disabled --- redash/handlers/authentication.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/redash/handlers/authentication.py b/redash/handlers/authentication.py index aebbf9a024..6ee518a2ed 100644 --- a/redash/handlers/authentication.py +++ b/redash/handlers/authentication.py @@ -1,7 +1,7 @@ import hashlib import logging -from flask import flash, redirect, render_template, request, url_for +from flask import abort, flash, redirect, render_template, request, url_for from flask_login import current_user, login_required, login_user, logout_user from redash import __version__, limiter, models, settings @@ -74,6 +74,9 @@ def reset(token, org_slug=None): @routes.route(org_scoped_rule('/forgot'), methods=['GET', 'POST']) def forgot_password(org_slug=None): + if not settings.PASSWORD_LOGIN_ENABLED: + abort(404) + submitted = False if request.method == 'POST' and request.form['email']: submitted = True From 812177a4e0331d3ac2b3af1f74ce68c7f3bc7941 Mon Sep 17 00:00:00 2001 From: Matt Snider Date: Thu, 29 Jun 2017 20:54:34 -0500 Subject: [PATCH 162/243] Fix bug getting the Salesforce sandbox parameter --- redash/query_runner/salesforce.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redash/query_runner/salesforce.py b/redash/query_runner/salesforce.py index 6d9678b067..0cfc1d8403 100644 --- a/redash/query_runner/salesforce.py +++ b/redash/query_runner/salesforce.py @@ -92,7 +92,7 @@ def _get_sf(self): sf = SimpleSalesforce(username=self.configuration['username'], password=self.configuration['password'], security_token=self.configuration['token'], - sandbox=self.configuration['sandbox'], + sandbox=self.configuration.get('sandbox', False), client_id='Redash') return sf From 2b44bc196496e4a9457abeab7325ecee0688061c Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 30 Jun 2017 23:43:10 -0500 Subject: [PATCH 163/243] fixes hard-coding but doesn't change per datasource --- client/app/pages/queries/get-data-source-version.js | 9 ++++++--- client/app/pages/queries/query.html | 3 +-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/client/app/pages/queries/get-data-source-version.js b/client/app/pages/queries/get-data-source-version.js index 11f8dbccfa..cd2363e025 100644 --- a/client/app/pages/queries/get-data-source-version.js +++ b/client/app/pages/queries/get-data-source-version.js @@ -1,12 +1,15 @@ -function GetDataSourceVersionCtrl(Events, toastr, $scope, DataSource) { +function GetDataSourceVersionCtrl(Events, toastr, $scope, DataSource, $route) { // 'ngInject'; - this.getDataSourceVersion = DataSource.version({ id: 6 }); + this.getDataSourceVersion = DataSource.version( + { + id: $route.current.locals.query.data_source_id, + } + ); } const GetDataSourceVersionInfo = { bindings: { - schema: '<', onRefresh: '&', }, controller: GetDataSourceVersionCtrl, diff --git a/client/app/pages/queries/query.html b/client/app/pages/queries/query.html index 60fbdcc92e..7ae0a529e8 100644 --- a/client/app/pages/queries/query.html +++ b/client/app/pages/queries/query.html @@ -100,10 +100,9 @@

    Data Source - {{dataSource.type_name}} documentation - {{dataSource.type_name}}
    From 9db0c53168c325d245e565e84abc29ea0fceff20 Mon Sep 17 00:00:00 2001 From: Alison Date: Sat, 1 Jul 2017 00:51:30 -0500 Subject: [PATCH 164/243] handle error for ds version --- client/app/pages/queries/get-data-source-version.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/pages/queries/get-data-source-version.js b/client/app/pages/queries/get-data-source-version.js index cd2363e025..66061ec50b 100644 --- a/client/app/pages/queries/get-data-source-version.js +++ b/client/app/pages/queries/get-data-source-version.js @@ -13,7 +13,7 @@ const GetDataSourceVersionInfo = { onRefresh: '&', }, controller: GetDataSourceVersionCtrl, - template: '{{ $ctrl.getDataSourceVersion.message }}', + template: '{{ $ctrl.getDataSourceVersion.message }}', }; export default function (ngModule) { From 1a53ba76314bbe2f023744b4c65e11ef7caac72c Mon Sep 17 00:00:00 2001 From: Alison Date: Sat, 1 Jul 2017 00:53:57 -0500 Subject: [PATCH 165/243] typo fix --- client/app/pages/data-sources/show.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/pages/data-sources/show.js b/client/app/pages/data-sources/show.js index 5971478b56..5cbd0bf16c 100644 --- a/client/app/pages/data-sources/show.js +++ b/client/app/pages/data-sources/show.js @@ -57,7 +57,7 @@ function DataSourceCtrl($scope, $routeParams, $http, $location, toastr, { id: $scope.dataSource.id }, (httpResponse) => { if (httpResponse.ok) { const versionNumber = httpResponse.message; - toastr.success(`Success. Verison: ${versionNumber}`); + toastr.success(`Success. Version: ${versionNumber}`); } else { toastr.error(httpResponse.message, 'Version Test Failed:', { timeOut: 10000 }); } From ab23e21f6f4ac780fe1bc1884dc3a0b25dfff5ca Mon Sep 17 00:00:00 2001 From: Alison Date: Mon, 3 Jul 2017 20:29:23 -0500 Subject: [PATCH 166/243] working in pg --- client/app/pages/queries/query.html | 8 ++++++++ migrations/versions/58f810489c47_.py | 28 ++++++++++++++++++++++++++++ redash/models.py | 17 +++++++++++++++-- redash/query_runner/pg.py | 2 +- 4 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 migrations/versions/58f810489c47_.py diff --git a/client/app/pages/queries/query.html b/client/app/pages/queries/query.html index 973740c79f..e4430d0a6a 100644 --- a/client/app/pages/queries/query.html +++ b/client/app/pages/queries/query.html @@ -198,6 +198,14 @@

  • +
  • + + + Data Scanned + + {{ queryResult.query_result.data_scanned }} + +
diff --git a/migrations/versions/58f810489c47_.py b/migrations/versions/58f810489c47_.py new file mode 100644 index 0000000000..37c5075285 --- /dev/null +++ b/migrations/versions/58f810489c47_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: 58f810489c47 +Revises: eb2f788f997e +Create Date: 2017-06-25 21:24:54.942119 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '58f810489c47' +down_revision = 'eb2f788f997e' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('query_results', sa.Column('data_scanned', sa.String(length=255), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('query_results', 'data_scanned') + # ### end Alembic commands ### diff --git a/redash/models.py b/redash/models.py index 6ba068db5e..dc7154b810 100644 --- a/redash/models.py +++ b/redash/models.py @@ -627,10 +627,16 @@ class QueryResult(db.Model, BelongsToOrgMixin): data = Column(db.Text) runtime = Column(postgresql.DOUBLE_PRECISION) retrieved_at = Column(db.DateTime(True)) + data_scanned = Column(db.String(255), nullable=True) __tablename__ = 'query_results' def to_dict(self): + if hasattr(self, 'data_scanned') and self.data_scanned != '' and self.data_scanned is not None: + data_scanned_info = self.data_scanned + else: + data_scanned_info = '' + return { 'id': self.id, 'query_hash': self.query_hash, @@ -638,7 +644,8 @@ def to_dict(self): 'data': json.loads(self.data), 'data_source_id': self.data_source_id, 'runtime': self.runtime, - 'retrieved_at': self.retrieved_at + 'retrieved_at': self.retrieved_at, + 'data_scanned': data_scanned_info } @classmethod @@ -673,13 +680,19 @@ def get_latest(cls, data_source, query, max_age=0): @classmethod def store_result(cls, org, data_source, query_hash, query, data, run_time, retrieved_at): + if json.loads(data)['data_scanned'] is not None: + data_scanned_information = json.loads(data)['data_scanned'] + else: + data_scanned_information = '' query_result = cls(org=org, query_hash=query_hash, query_text=query, runtime=run_time, data_source=data_source, retrieved_at=retrieved_at, - data=data) + data=data, + data_scanned=data_scanned_information + ) db.session.add(query_result) logging.info("Inserted query (%s) data; id=%s", query_hash, query_result.id) # TODO: Investigate how big an impact this select-before-update makes. diff --git a/redash/query_runner/pg.py b/redash/query_runner/pg.py index f2a1d4604f..fbe04e4229 100644 --- a/redash/query_runner/pg.py +++ b/redash/query_runner/pg.py @@ -157,7 +157,7 @@ def run_query(self, query, user): columns = self.fetch_columns([(i[0], types_map.get(i[1], None)) for i in cursor.description]) rows = [dict(zip((c['name'] for c in columns), row)) for row in cursor] - data = {'columns': columns, 'rows': rows} + data = {'columns': columns, 'rows': rows, 'data_scanned': 'N/A'} error = None json_data = json.dumps(data, cls=JSONEncoder) else: From 48ac6346aa623cc3c7c6eebd8ae2412f008382f0 Mon Sep 17 00:00:00 2001 From: Alison Date: Mon, 3 Jul 2017 20:32:30 -0500 Subject: [PATCH 167/243] added to Athena --- redash/query_runner/athena.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index 06816d883f..2612338588 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -210,6 +210,11 @@ def run_query(self, query, user): assert len(column_info_set) == 1, "Don't know what to do with inconsistent column info" rows.extend(result['ResultSet']['ResultRows']) cnames = [c['Name'] for c in column_info] + + #get data scanned in query + query_execution = client.get_query_execution(response['QueryExecutionId']) + qbytes = query_execution['QueryExecutionDetail']['Stats']['ProcessedBytes'] + data = {'columns': [{ 'name': name, @@ -217,8 +222,11 @@ def run_query(self, query, user): 'type': 'string', # XXX map athena types to redash types } for name in cnames], 'rows': - [{name: row['Data'][i] for (i, name) in enumerate(cnames)} - for row in rows[1:]] + [{ + name: row['Data'][i] for (i, name) in enumerate(cnames) + } for row in rows[1:]], + 'data_scanned': + [{ qbytes }] } return json.dumps(data, cls=JSONEncoder), None From 1be00c4fea9ac6e8f066a4874a1f279bfe9cbd94 Mon Sep 17 00:00:00 2001 From: Alison Date: Mon, 3 Jul 2017 23:01:09 -0500 Subject: [PATCH 168/243] w/ tests passing --- redash/models.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/redash/models.py b/redash/models.py index dc7154b810..78b2858e4c 100644 --- a/redash/models.py +++ b/redash/models.py @@ -680,10 +680,11 @@ def get_latest(cls, data_source, query, max_age=0): @classmethod def store_result(cls, org, data_source, query_hash, query, data, run_time, retrieved_at): - if json.loads(data)['data_scanned'] is not None: + try: data_scanned_information = json.loads(data)['data_scanned'] - else: - data_scanned_information = '' + except: + data_scanned_information ='' + query_result = cls(org=org, query_hash=query_hash, query_text=query, From 80d8fa980c008907da31fd98b2ff720ce4a93992 Mon Sep 17 00:00:00 2001 From: Alison Date: Tue, 4 Jul 2017 00:10:42 -0500 Subject: [PATCH 169/243] handle change data source case Not ideal solution but does prevent bad information from showing to the user and lets them know what to do to get the right information. --- client/app/pages/queries/query.html | 2 +- client/app/pages/queries/view.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/app/pages/queries/query.html b/client/app/pages/queries/query.html index 7ae0a529e8..a9832c3c00 100644 --- a/client/app/pages/queries/query.html +++ b/client/app/pages/queries/query.html @@ -103,7 +103,7 @@

{{dataSource.type_name}} documentation - +
+ {{ fields }} + {{ target.options }}
+
{{ field.property.info }}
diff --git a/client/app/pages/queries/schema-browser.js b/client/app/pages/queries/schema-browser.js index 2f0e71ce82..5f6a54edca 100644 --- a/client/app/pages/queries/schema-browser.js +++ b/client/app/pages/queries/schema-browser.js @@ -3,6 +3,8 @@ import template from './schema-browser.html'; function SchemaBrowserCtrl($scope) { 'ngInject'; + this.versionToggle = false; + this.showTable = (table) => { table.collapsed = !table.collapsed; $scope.$broadcast('vsRepeatTrigger'); @@ -17,12 +19,21 @@ function SchemaBrowserCtrl($scope) { return size; }; + + this.flipToggleVersionedTables = (versionToggle) => { + if (versionToggle === false) { + this.versionToggle = true; + } else { + this.versionToggle = false; + } + }; } const SchemaBrowser = { bindings: { schema: '<', onRefresh: '&', + flipToggleVersionedTables: '&', }, controller: SchemaBrowserCtrl, template, From ace4c346377721b502d68f1317392e6921b4c7a8 Mon Sep 17 00:00:00 2001 From: Alison Date: Tue, 4 Jul 2017 18:05:25 -0500 Subject: [PATCH 173/243] works for filtering _v literally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit but don’t name a table abcdefghijklmnop. --- client/app/pages/queries/schema-browser.html | 4 ++-- client/app/pages/queries/schema-browser.js | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/client/app/pages/queries/schema-browser.html b/client/app/pages/queries/schema-browser.html index 1e03783943..7c47019d62 100644 --- a/client/app/pages/queries/schema-browser.html +++ b/client/app/pages/queries/schema-browser.html @@ -10,14 +10,14 @@
-
+
diff --git a/client/app/pages/queries/schema-browser.js b/client/app/pages/queries/schema-browser.js index 5f6a54edca..2c7abbedd7 100644 --- a/client/app/pages/queries/schema-browser.js +++ b/client/app/pages/queries/schema-browser.js @@ -4,6 +4,7 @@ function SchemaBrowserCtrl($scope) { 'ngInject'; this.versionToggle = false; + this.versionFilter = 'abcdefghijklmnop'; this.showTable = (table) => { table.collapsed = !table.collapsed; @@ -23,8 +24,10 @@ function SchemaBrowserCtrl($scope) { this.flipToggleVersionedTables = (versionToggle) => { if (versionToggle === false) { this.versionToggle = true; + this.versionFilter = '_v'; } else { this.versionToggle = false; + this.versionFilter = 'abcdefghijklmnop'; } }; } From d44ca1e5c13c835cb95da43f9a1b9f883b8bbf93 Mon Sep 17 00:00:00 2001 From: Alison Date: Wed, 5 Jul 2017 18:55:47 -0500 Subject: [PATCH 174/243] updates based on Rob's PR review --- redash/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/redash/models.py b/redash/models.py index 78b2858e4c..bd8c2eb5b9 100644 --- a/redash/models.py +++ b/redash/models.py @@ -632,7 +632,7 @@ class QueryResult(db.Model, BelongsToOrgMixin): __tablename__ = 'query_results' def to_dict(self): - if hasattr(self, 'data_scanned') and self.data_scanned != '' and self.data_scanned is not None: + if hasattr(self, 'data_scanned') and self.data_scanned: data_scanned_info = self.data_scanned else: data_scanned_info = '' @@ -682,8 +682,8 @@ def get_latest(cls, data_source, query, max_age=0): def store_result(cls, org, data_source, query_hash, query, data, run_time, retrieved_at): try: data_scanned_information = json.loads(data)['data_scanned'] - except: - data_scanned_information ='' + except (ValueError, TypeError): + data_scanned_information = '' query_result = cls(org=org, query_hash=query_hash, From cca9b85e86b1d83c7d5aa74f70dec331b93dc659 Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 7 Jul 2017 14:54:19 -0500 Subject: [PATCH 175/243] update from reading diff of merge --- client/app/pages/queries/visualization-embed.js | 1 - redash/handlers/dashboards.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/client/app/pages/queries/visualization-embed.js b/client/app/pages/queries/visualization-embed.js index 489adc9507..12921a6a80 100644 --- a/client/app/pages/queries/visualization-embed.js +++ b/client/app/pages/queries/visualization-embed.js @@ -37,7 +37,6 @@ export default function (ngModule) { return session($http, $route, Auth).then(() => { const queryId = $route.current.params.queryId; const query = $http.get(`api/queries/${queryId}`).then(response => response.data); - const queryResult = $http.get(`api/queries/${queryId}/results.json`).then(response => response.data); const queryResult = $http.get(`api/queries/${queryId}/results.json${location.search}`).then(response => response.data); return $q.all([query, queryResult]); }); diff --git a/redash/handlers/dashboards.py b/redash/handlers/dashboards.py index 36df01b027..f47c59fb2b 100644 --- a/redash/handlers/dashboards.py +++ b/redash/handlers/dashboards.py @@ -2,9 +2,9 @@ import json from flask import request, url_for +from funcy import distinct, project, take from sqlalchemy.exc import IntegrityError from sqlalchemy.orm.exc import StaleDataError -from funcy import distinct, project, take from flask_restful import abort from redash import models, serializers, settings From 28c9bb959ed3e5be39a6fdda15386c968d96b92d Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 7 Jul 2017 15:21:21 -0500 Subject: [PATCH 176/243] updated based on PR comments --- client/app/visualizations/edit-visualization-dialog.css | 5 +++++ client/app/visualizations/edit-visualization-dialog.html | 4 ++-- client/app/visualizations/edit-visualization-dialog.js | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 client/app/visualizations/edit-visualization-dialog.css diff --git a/client/app/visualizations/edit-visualization-dialog.css b/client/app/visualizations/edit-visualization-dialog.css new file mode 100644 index 0000000000..3e84b755b2 --- /dev/null +++ b/client/app/visualizations/edit-visualization-dialog.css @@ -0,0 +1,5 @@ +/* Edit Visualization Dialog specific CSS */ + +.slight-padding { + padding: 5px; +} \ No newline at end of file diff --git a/client/app/visualizations/edit-visualization-dialog.html b/client/app/visualizations/edit-visualization-dialog.html index 77d359fe50..6d26e4b6bd 100644 --- a/client/app/visualizations/edit-visualization-dialog.html +++ b/client/app/visualizations/edit-visualization-dialog.html @@ -34,10 +34,10 @@
-
+
-
+
diff --git a/client/app/visualizations/edit-visualization-dialog.js b/client/app/visualizations/edit-visualization-dialog.js index add51f1db1..ee8d5236c8 100644 --- a/client/app/visualizations/edit-visualization-dialog.js +++ b/client/app/visualizations/edit-visualization-dialog.js @@ -1,6 +1,7 @@ import { pluck } from 'underscore'; import { copy } from 'angular'; import template from './edit-visualization-dialog.html'; +import './edit-visualization-dialog.css'; const EditVisualizationDialog = { template, @@ -20,7 +21,7 @@ const EditVisualizationDialog = { this.visTypes = Visualization.visualizationTypes; this.warning_three_column_groupby = 'You have more than 2 columns in your result set. To ensure the chart is accurate, please do one of the following:
  • Change the SQL query to give 2 result columns. You can CONCAT() columns together if you wish.
  • Select column(s) to group by.
'; - this.warning_three_column_stacking = 'You have more than 2 columns in your result set. You may wish to make the Stacking option equal to `Enabled` or `Percent`.'; + this.warning_three_column_stacking = 'You have more than 2 columns in your result set. You may wish to make the Stacking option equal to `Enabled` or `Percent`.'; this.newVisualization = () => ({ From 6271ebb25de87fc3d4e46ae24618451c200d2292 Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 7 Jul 2017 15:35:20 -0500 Subject: [PATCH 177/243] minor changes --- client/app/components/dynamic-form.html | 1 - redash/query_runner/activedata.py | 32 ++++++++++++------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/client/app/components/dynamic-form.html b/client/app/components/dynamic-form.html index 91abaf87b7..ed7b81d4ee 100644 --- a/client/app/components/dynamic-form.html +++ b/client/app/components/dynamic-form.html @@ -8,7 +8,6 @@
{{ fields }} - {{ target.options }}
Date: Fri, 7 Jul 2017 18:42:32 -0700 Subject: [PATCH 178/243] Deploy master to dockerhub as "latest" --- bin/deploy | 19 +++++++++++++++++++ circle.yml | 18 ++++++------------ 2 files changed, 25 insertions(+), 12 deletions(-) create mode 100755 bin/deploy diff --git a/bin/deploy b/bin/deploy new file mode 100755 index 0000000000..bccc6cb999 --- /dev/null +++ b/bin/deploy @@ -0,0 +1,19 @@ +#!/bin/bash + +set -eo pipefail + +[ ! -z $DOCKERHUB_REPO ] && [ $# -eq 1 ] + +VERSION="$1" + +printf '{"commit":"%s","version":"%s","source":"https://github.com/%s/%s","build":"%s"}\n' \ + "$CIRCLE_SHA1" \ + "$VERSION" \ + "$CIRCLE_PROJECT_USERNAME" \ + "$CIRCLE_PROJECT_REPONAME" \ + "$CIRCLE_BUILD_URL" \ +> version.json + +docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS +docker build -t $DOCKERHUB_REPO:$VERSION . +docker push $DOCKERHUB_REPO:$VERSION diff --git a/circle.yml b/circle.yml index 3e2bb02d94..9ff2963873 100644 --- a/circle.yml +++ b/circle.yml @@ -17,22 +17,16 @@ test: override: - nosetests --with-xunit --xunit-file=$CIRCLE_TEST_REPORTS/junit.xml --with-coverage --cover-package=redash tests/ deployment: + latest: + branch: master + owner: mozilla + commands: + - ./bin/deploy "latest" hub_releases: tag: /^m[0-9]+(\.[0-9]+)?$/ owner: mozilla commands: - - "[ ! -z $DOCKERHUB_REPO ]" - - > - printf '{"commit":"%s","version":"%s","source":"https://github.com/%s/%s","build":"%s"}\n' - "$CIRCLE_SHA1" - "$CIRCLE_TAG" - "$CIRCLE_PROJECT_USERNAME" - "$CIRCLE_PROJECT_REPONAME" - "$CIRCLE_BUILD_URL" - > version.json - - docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS - - docker build -t $DOCKERHUB_REPO:$CIRCLE_TAG . - - docker push $DOCKERHUB_REPO:$CIRCLE_TAG + - ./bin/deploy "$CIRCLE_TAG" general: branches: ignore: From 992ffd0aa3bc57da2a4edf3749e24187eb507c98 Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 7 Jul 2017 23:14:14 -0500 Subject: [PATCH 179/243] add a different init fx passes test suite --- redash/query_runner/activedata.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/redash/query_runner/activedata.py b/redash/query_runner/activedata.py index f2da4753bc..c4d3de740e 100644 --- a/redash/query_runner/activedata.py +++ b/redash/query_runner/activedata.py @@ -37,6 +37,9 @@ class ActiveData(BaseSQLQueryRunner): # configuration['host_url'] = url.scheme + "://" + url.hostname + ":" + unicode(url.port or 80) # BaseSQLQueryRunner.__init__(self, configuration) + def __init__(self, configuration): + super(ActiveData, self).__init__(configuration) + @classmethod def configuration_schema(cls): return { From ae00b6a42395ccc935dfff67c9572d8a6d81d4e0 Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 7 Jul 2017 23:28:22 -0500 Subject: [PATCH 180/243] avoid compile error delete duplicate let statement --- client/app/services/query-result.js | 1 - 1 file changed, 1 deletion(-) diff --git a/client/app/services/query-result.js b/client/app/services/query-result.js index b6288e796b..ddc54cf26d 100644 --- a/client/app/services/query-result.js +++ b/client/app/services/query-result.js @@ -94,7 +94,6 @@ function QueryResultService($resource, $timeout, $q) { let newType = null; each(row, (v, k) => { - let newType = null; if (isNumber(v)) { newType = 'float'; } else if (isString(v) && v.match(/^\d{4}-\d{2}-\d{2}T/)) { From 32069afedc541919718b107ee21a1188f4f33319 Mon Sep 17 00:00:00 2001 From: Alison Date: Sat, 8 Jul 2017 00:17:46 -0500 Subject: [PATCH 181/243] add pyup config document --- .pyup.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .pyup.yml diff --git a/.pyup.yml b/.pyup.yml new file mode 100644 index 0000000000..ce4a2cf804 --- /dev/null +++ b/.pyup.yml @@ -0,0 +1,8 @@ +schedule: "every day" +search: False +update: insecure +requirements: + - requirements.txt: + update: security + - requirements-newrelic.txt: + update: security \ No newline at end of file From f55219c4bbb3ea21b47264c49636c039e6440faa Mon Sep 17 00:00:00 2001 From: Alison Date: Mon, 10 Jul 2017 17:42:09 -0500 Subject: [PATCH 182/243] updates based on PR comments --- redash/cli/data_sources.py | 4 ++-- redash/query_runner/__init__.py | 19 ++++++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/redash/cli/data_sources.py b/redash/cli/data_sources.py index 18d9f8939e..adff62d8e8 100644 --- a/redash/cli/data_sources.py +++ b/redash/cli/data_sources.py @@ -57,7 +57,7 @@ def test(name, organization='default'): name, data_source.id) try: data_source.query_runner.test_connection() - except Exception, e: + except Exception as e: print "Failure: {}".format(e) exit(1) else: @@ -82,7 +82,7 @@ def get_data_source_version(name, organization='default'): name, data_source.id) try: info = data_source.query_runner.get_data_source_version() - except Exception, e: + except Exception as e: print "Failure: {}".format(e) exit(1) else: diff --git a/redash/query_runner/__init__.py b/redash/query_runner/__init__.py index ec9338367f..c3658a3d26 100644 --- a/redash/query_runner/__init__.py +++ b/redash/query_runner/__init__.py @@ -77,16 +77,21 @@ def get_data_source_version(self): if self.data_source_version_query is None: raise NotImplementedError data, error = self.run_query(self.data_source_version_query, None) - version = json.loads(data)['rows'][0]['version'] - if(self.data_source_version_post_process == "split by space take second"): + + if error is not None: + raise Exception(error) + + try: + version = json.loads(data)['rows'][0]['version'] + except KeyError as e: + raise Exception(e) + + if self.data_source_version_post_process == "split by space take second": version = version.split(" ")[1] - elif(self.data_source_version_post_process == "split by space take last"): + elif self.data_source_version_post_process == "split by space take last": version = version.split(" ")[-1] - elif(self.data_source_version_post_process == "none"): + elif self.data_source_version_post_process == "none": version = version - - if error is not None: - raise Exception(error) return version From ff8f4357b591e6153a58331541c59115df358fe0 Mon Sep 17 00:00:00 2001 From: Alison Date: Mon, 10 Jul 2017 20:51:09 -0500 Subject: [PATCH 183/243] updated value based on comment that there was a typo in the docs. --- .pyup.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pyup.yml b/.pyup.yml index ce4a2cf804..3694ef8f58 100644 --- a/.pyup.yml +++ b/.pyup.yml @@ -3,6 +3,6 @@ search: False update: insecure requirements: - requirements.txt: - update: security + update: insecure - requirements-newrelic.txt: update: security \ No newline at end of file From 4efc523a9f6aa618df60f9eb05f6e56a81cb389b Mon Sep 17 00:00:00 2001 From: Alison Date: Tue, 11 Jul 2017 21:05:34 -0500 Subject: [PATCH 184/243] fix not displaying datasource info issue --- redash/models.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/redash/models.py b/redash/models.py index bd8c2eb5b9..19e0227059 100644 --- a/redash/models.py +++ b/redash/models.py @@ -477,11 +477,11 @@ def to_dict(self, all=False, with_permissions_for=None): 'syntax': self.query_runner.syntax, 'paused': self.paused, 'pause_reason': self.pause_reason, - 'type_name': self.query_runner.name(), + 'type_name': self.query_runner.name() } - schema = get_configuration_schema_for_query_runner_type(self.type) if all: + schema = get_configuration_schema_for_query_runner_type(self.type) self.options.set_schema(schema) d['options'] = self.options.to_dict(mask_secrets=True) d['queue_name'] = self.queue_name @@ -493,8 +493,7 @@ def to_dict(self, all=False, with_permissions_for=None): DataSourceGroup.group == with_permissions_for, DataSourceGroup.data_source == self).one()[0] - doc_url = self.options.get('doc_url', schema['properties'].get( - 'doc_url', {}).get('default')) + doc_url = self.options.get('doc_url') if doc_url: d['options'] = {'doc_url': doc_url} @@ -570,9 +569,8 @@ def resume(self): def add_group(self, group, view_only=False): dsg = DataSourceGroup(group=group, data_source=self, view_only=view_only) db.session.add(dsg) - return dsg - setattr(self, 'data_source_groups', dsg) + return dsg def remove_group(self, group): db.session.query(DataSourceGroup).filter( From f26976caed6f5a5fabb7bcf137f9437a78d0b595 Mon Sep 17 00:00:00 2001 From: Alison Date: Tue, 11 Jul 2017 21:21:07 -0500 Subject: [PATCH 185/243] tests passing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I commented out `setattr(self, 'data_source_groups', dsg)` because it made 3 tests fail and shouldn’t have been being obeyed before (after the `return`). Other changes are minor to be production ready. --- client/app/components/dynamic-form.html | 1 - redash/models.py | 2 +- redash/query_runner/activedata.py | 5 ----- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/client/app/components/dynamic-form.html b/client/app/components/dynamic-form.html index ed7b81d4ee..8206117ea7 100644 --- a/client/app/components/dynamic-form.html +++ b/client/app/components/dynamic-form.html @@ -7,7 +7,6 @@
- {{ fields }}
Date: Tue, 11 Jul 2017 23:14:20 -0500 Subject: [PATCH 186/243] remove ng-strict-di --- client/app/index.html | 2 +- client/app/multi_org.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/app/index.html b/client/app/index.html index b7e2d2087c..424d101b0c 100644 --- a/client/app/index.html +++ b/client/app/index.html @@ -1,5 +1,5 @@ - + diff --git a/client/app/multi_org.html b/client/app/multi_org.html index a8d673772b..44673f7a45 100644 --- a/client/app/multi_org.html +++ b/client/app/multi_org.html @@ -1,5 +1,5 @@ - + From 39cb71f06621d0d7a6d05fa32fc38fa88e57b3c0 Mon Sep 17 00:00:00 2001 From: Alison Date: Wed, 12 Jul 2017 00:33:37 -0500 Subject: [PATCH 187/243] Revert "98 upstream merge" --- client/app/components/dynamic-form.js | 8 +- client/app/components/index.js | 1 - client/app/components/sort-icon.js | 26 --- client/app/index.js | 1 - client/app/pages/alerts-list/alerts-list.html | 12 +- client/app/pages/alerts-list/index.js | 26 ++- client/app/pages/dashboards/dashboard.js | 10 +- .../dashboards/edit-dashboard-dialog.html | 9 +- .../pages/dashboards/edit-dashboard-dialog.js | 5 +- client/app/pages/queries/view.js | 7 +- .../app/pages/queries/visualization-embed.js | 2 +- client/app/services/auth.js | 2 +- client/app/utils/paginator.js | 20 --- npm-shrinkwrap.json | 165 ++++++++++++++---- package.json | 31 ++-- redash/authentication/org_resolving.py | 7 + redash/cli/__init__.py | 20 --- redash/handlers/alerts.py | 7 +- redash/handlers/authentication.py | 8 +- redash/handlers/dashboards.py | 4 +- redash/handlers/data_sources.py | 26 +-- redash/handlers/destinations.py | 10 +- redash/handlers/queries.py | 6 +- redash/handlers/query_results.py | 72 +------- redash/handlers/static.py | 2 +- redash/models.py | 49 +++--- redash/query_runner/athena.py | 140 +++++---------- redash/query_runner/memsql_ds.py | 3 +- redash/query_runner/salesforce.py | 2 +- redash/settings.py | 3 - redash/tasks/queries.py | 1 - redash/utils/__init__.py | 8 +- requirements_all_ds.txt | 3 +- tests/factories.py | 7 +- tests/handlers/test_alerts.py | 7 +- tests/handlers/test_destinations.py | 13 +- webpack.config.js | 79 +++------ 37 files changed, 315 insertions(+), 487 deletions(-) delete mode 100644 client/app/components/sort-icon.js diff --git a/client/app/components/dynamic-form.js b/client/app/components/dynamic-form.js index a318f1380d..6e7708ff61 100644 --- a/client/app/components/dynamic-form.js +++ b/client/app/components/dynamic-form.js @@ -131,12 +131,8 @@ function DynamicForm($http, toastr, $q) { toastr.success('Saved.'); $scope.dataSourceForm.$setPristine(); }, - (error) => { - if (error.status === 400 && 'message' in error.data) { - toastr.error(error.data.message); - } else { - toastr.error('Failed saving.'); - } + () => { + toastr.error('Failed saving.'); } ); }; diff --git a/client/app/components/index.js b/client/app/components/index.js index 17cbf07d64..fae91fcfc7 100644 --- a/client/app/components/index.js +++ b/client/app/components/index.js @@ -18,4 +18,3 @@ export { default as rdTimeAgo } from './rd-time-ago'; export { default as overlay } from './overlay'; export { default as routeStatus } from './route-status'; export { default as filters } from './filters'; -export { default as sortIcon } from './sort-icon'; diff --git a/client/app/components/sort-icon.js b/client/app/components/sort-icon.js deleted file mode 100644 index adc1ca58d2..0000000000 --- a/client/app/components/sort-icon.js +++ /dev/null @@ -1,26 +0,0 @@ -export default function (ngModule) { - ngModule.component('sortIcon', { - template: '', - bindings: { - column: '<', - sortColumn: '<', - reverse: '<', - }, - controller() { - this.$onChanges = (changes) => { - ['column', 'sortColumn', 'reverse'].forEach((v) => { - if (v in changes) { - this[v] = changes[v].currentValue; - } - }); - - this.showIcon = false; - - if (this.column === this.sortColumn) { - this.showIcon = true; - this.icon = this.reverse ? 'desc' : 'asc'; - } - }; - }, - }); -} diff --git a/client/app/index.js b/client/app/index.js index 2279cb7f8f..520719121d 100644 --- a/client/app/index.js +++ b/client/app/index.js @@ -100,7 +100,6 @@ registerVisualizations(ngModule); ngModule.config(($routeProvider, $locationProvider, $compileProvider, uiSelectConfig, toastrConfig) => { - $compileProvider.debugInfoEnabled(false); $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|http|data):/); $locationProvider.html5Mode(true); uiSelectConfig.theme = 'bootstrap'; diff --git a/client/app/pages/alerts-list/alerts-list.html b/client/app/pages/alerts-list/alerts-list.html index d85ea0ea92..eeee9cee08 100644 --- a/client/app/pages/alerts-list/alerts-list.html +++ b/client/app/pages/alerts-list/alerts-list.html @@ -4,20 +4,20 @@
-
+

NameCreated ByStateCreated AtName Created By State Created By
{{row.name}}{{row.user.name}}{{row.created_by}} {{row.state | uppercase}} since
- - - - + + + + - + diff --git a/client/app/pages/alerts-list/index.js b/client/app/pages/alerts-list/index.js index ac69bda693..4e315e9129 100644 --- a/client/app/pages/alerts-list/index.js +++ b/client/app/pages/alerts-list/index.js @@ -1,26 +1,24 @@ import { Paginator } from '../../utils'; import template from './alerts-list.html'; -const stateClass = { - ok: 'label label-success', - triggered: 'label label-danger', - unknown: 'label label-warning', -}; - class AlertsListCtrl { constructor(Events, Alert) { Events.record('view', 'page', 'alerts'); this.alerts = new Paginator([], { itemsPerPage: 20 }); + Alert.query((alerts) => { - this.alerts.updateRows(alerts.map(alert => ({ - name: alert.name, - state: alert.state, - class: stateClass[alert.state], - created_by: alert.user.name, - created_at: alert.created_at, - updated_at: alert.updated_at, - }))); + const stateClass = { + ok: 'label label-success', + triggered: 'label label-danger', + unknown: 'label label-warning', + }; + + alerts.forEach((alert) => { + alert.class = stateClass[alert.state]; + }); + + this.alerts.updateRows(alerts); }); } } diff --git a/client/app/pages/dashboards/dashboard.js b/client/app/pages/dashboards/dashboard.js index 42c08bbd06..6b12126255 100644 --- a/client/app/pages/dashboards/dashboard.js +++ b/client/app/pages/dashboards/dashboard.js @@ -156,20 +156,12 @@ function DashboardCtrl($rootScope, $routeParams, $location, $timeout, $q, $uibMo this.editDashboard = () => { this.dashboard.existing_name = this.dashboard.name; - const previousFiltersState = this.dashboard.dashboard_filters_enabled; $uibModal.open({ component: 'editDashboardDialog', resolve: { dashboard: () => this.dashboard, }, - }).result.then((dashboard) => { - const shouldRenderDashboard = !previousFiltersState && dashboard.dashboard_filters_enabled; - this.dashboard = dashboard; - - if (shouldRenderDashboard) { - renderDashboard(this.dashboard); - } - }); + }).result.then((dashboard) => { this.dashboard = dashboard; }); }; this.addWidget = () => { diff --git a/client/app/pages/dashboards/edit-dashboard-dialog.html b/client/app/pages/dashboards/edit-dashboard-dialog.html index dffe25a71d..48be84d4e2 100644 --- a/client/app/pages/dashboards/edit-dashboard-dialog.html +++ b/client/app/pages/dashboards/edit-dashboard-dialog.html @@ -11,13 +11,6 @@

A name is required.

-

- -

-
  • @@ -28,5 +21,5 @@
diff --git a/client/app/pages/dashboards/edit-dashboard-dialog.js b/client/app/pages/dashboards/edit-dashboard-dialog.js index 7abd182155..70eab4bafd 100644 --- a/client/app/pages/dashboards/edit-dashboard-dialog.js +++ b/client/app/pages/dashboards/edit-dashboard-dialog.js @@ -1,4 +1,4 @@ -import { isEmpty, sortBy } from 'underscore'; +import { sortBy } from 'underscore'; import template from './edit-dashboard-dialog.html'; const EditDashboardDialog = { @@ -45,8 +45,6 @@ const EditDashboardDialog = { }); } - this.isFormValid = () => !isEmpty(this.dashboard.name); - this.saveDashboard = () => { this.saveInProgress = true; @@ -67,7 +65,6 @@ const EditDashboardDialog = { slug: this.dashboard.id, name: this.dashboard.name, version: this.dashboard.version, - dashboard_filters_enabled: this.dashboard.dashboard_filters_enabled, layout: JSON.stringify(layout), }; diff --git a/client/app/pages/queries/view.js b/client/app/pages/queries/view.js index 897ebc7911..574a80ba0d 100644 --- a/client/app/pages/queries/view.js +++ b/client/app/pages/queries/view.js @@ -211,12 +211,7 @@ function QueryViewCtrl($scope, Events, $route, $routeParams, $location, $window, $scope.saveName = () => { Events.record('edit_name', 'query', $scope.query.id); - - if ($scope.query.is_draft && clientConfig.autoPublishNamedQueries && $scope.query.name !== 'New Query') { - $scope.query.is_draft = false; - } - - $scope.saveQuery(undefined, { name: $scope.query.name, is_draft: $scope.query.is_draft }); + $scope.saveQuery(undefined, { name: $scope.query.name }); }; $scope.cancelExecution = () => { diff --git a/client/app/pages/queries/visualization-embed.js b/client/app/pages/queries/visualization-embed.js index 12921a6a80..65710cc74e 100644 --- a/client/app/pages/queries/visualization-embed.js +++ b/client/app/pages/queries/visualization-embed.js @@ -37,7 +37,7 @@ export default function (ngModule) { return session($http, $route, Auth).then(() => { const queryId = $route.current.params.queryId; const query = $http.get(`api/queries/${queryId}`).then(response => response.data); - const queryResult = $http.get(`api/queries/${queryId}/results.json${location.search}`).then(response => response.data); + const queryResult = $http.get(`api/queries/${queryId}/results.json`).then(response => response.data); return $q.all([query, queryResult]); }); } diff --git a/client/app/services/auth.js b/client/app/services/auth.js index 95d08bf1c7..074a7419aa 100644 --- a/client/app/services/auth.js +++ b/client/app/services/auth.js @@ -47,7 +47,7 @@ function AuthService($window, $location, $q, $http) { } this.setApiKey(null); - return $http.get('api/session').then((response) => { + return $http.get('/api/session').then((response) => { storeSession(response.data); return session; }); diff --git a/client/app/utils/paginator.js b/client/app/utils/paginator.js index 546f9a0fd4..4d28c1fa95 100644 --- a/client/app/utils/paginator.js +++ b/client/app/utils/paginator.js @@ -1,12 +1,8 @@ -import { sortBy } from 'underscore'; - export default class Paginator { constructor(rows, { page = 1, itemsPerPage = 20, totalCount = undefined } = {}) { this.page = page; this.itemsPerPage = itemsPerPage; this.updateRows(rows, totalCount); - this.orderByField = undefined; - this.orderByReverse = false; } setPage(page) { @@ -28,20 +24,4 @@ export default class Paginator { this.totalCount = 0; } } - - orderBy(column) { - if (column === this.orderByField) { - this.orderByReverse = !this.orderByReverse; - } else { - this.orderByField = column; - this.orderByReverse = false; - } - - if (this.orderByField) { - this.rows = sortBy(this.rows, this.orderByField); - if (this.orderByReverse) { - this.rows = this.rows.reverse(); - } - } - } } diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 88aa7a87b4..298af0afa0 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "redash-client", - "version": "2.0.0", + "version": "1.0.3", "dependencies": { "3d-view": { "version": "2.0.0", @@ -52,6 +52,11 @@ "from": "alpha-shape@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/alpha-shape/-/alpha-shape-1.0.0.tgz" }, + "alter": { + "version": "0.2.0", + "from": "alter@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz" + }, "amdefine": { "version": "1.0.0", "from": "amdefine@>=0.0.4", @@ -69,7 +74,7 @@ }, "angular-gridster": { "version": "0.13.14", - "from": "angular-gridster@>=0.13.14 <0.14.0", + "from": "angular-gridster@latest", "resolved": "https://registry.npmjs.org/angular-gridster/-/angular-gridster-0.13.14.tgz" }, "angular-messages": { @@ -84,7 +89,7 @@ }, "angular-resizable": { "version": "1.2.0", - "from": "angular-resizable@>=1.2.0 <2.0.0", + "from": "angular-resizable@latest", "resolved": "https://registry.npmjs.org/angular-resizable/-/angular-resizable-1.2.0.tgz" }, "angular-resource": { @@ -104,12 +109,12 @@ }, "angular-toastr": { "version": "2.1.1", - "from": "angular-toastr@>=2.1.1 <3.0.0", + "from": "angular-toastr@latest", "resolved": "https://registry.npmjs.org/angular-toastr/-/angular-toastr-2.1.1.tgz" }, "angular-ui-ace": { "version": "0.2.3", - "from": "angular-ui-ace@>=0.2.3 <0.3.0", + "from": "angular-ui-ace@latest", "resolved": "https://registry.npmjs.org/angular-ui-ace/-/angular-ui-ace-0.2.3.tgz" }, "angular-ui-bootstrap": { @@ -119,7 +124,7 @@ }, "angular-vs-repeat": { "version": "1.1.7", - "from": "angular-vs-repeat@>=1.1.7 <2.0.0", + "from": "angular-vs-repeat@latest", "resolved": "https://registry.npmjs.org/angular-vs-repeat/-/angular-vs-repeat-1.1.7.tgz" }, "ansi-regex": { @@ -134,7 +139,7 @@ }, "arraytools": { "version": "1.1.2", - "from": "arraytools@>=1.1.2 <2.0.0", + "from": "arraytools@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/arraytools/-/arraytools-1.1.2.tgz" }, "asn1": { @@ -521,7 +526,7 @@ }, "d3": { "version": "3.5.17", - "from": "d3@>=3.5.17 <4.0.0", + "from": "d3@>=3.5.6 <3.6.0", "resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz" }, "d3-cloud": { @@ -573,7 +578,7 @@ }, "defined": { "version": "1.0.0", - "from": "defined@>=1.0.0 <1.1.0", + "from": "defined@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz" }, "delaunay-triangulate": { @@ -629,6 +634,11 @@ "from": "edges-to-adjacency-list@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/edges-to-adjacency-list/-/edges-to-adjacency-list-1.0.0.tgz" }, + "emojis-list": { + "version": "2.1.0", + "from": "emojis-list@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz" + }, "es-abstract": { "version": "1.7.0", "from": "es-abstract@>=1.5.0 <2.0.0", @@ -694,7 +704,7 @@ }, "events": { "version": "1.1.1", - "from": "events@>=1.0.2 <2.0.0", + "from": "events@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz" }, "extend": { @@ -773,7 +783,7 @@ }, "font-awesome": { "version": "4.7.0", - "from": "font-awesome@>=4.7.0 <5.0.0", + "from": "font-awesome@latest", "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz" }, "for-each": { @@ -803,7 +813,7 @@ }, "function-bind": { "version": "1.1.0", - "from": "function-bind@>=1.1.0 <1.2.0", + "from": "function-bind@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz" }, "functional-red-black-tree": { @@ -1163,7 +1173,7 @@ }, "gl-plot2d": { "version": "1.2.0", - "from": "gl-plot2d@>=1.2.0 <2.0.0", + "from": "gl-plot2d@>=1.1.6 <2.0.0", "resolved": "https://registry.npmjs.org/gl-plot2d/-/gl-plot2d-1.2.0.tgz", "dependencies": { "binary-search-bounds": { @@ -1524,7 +1534,7 @@ }, "gl-surface3d": { "version": "1.3.0", - "from": "gl-surface3d@>=1.3.0 <2.0.0", + "from": "gl-surface3d@>=1.2.3 <2.0.0", "resolved": "https://registry.npmjs.org/gl-surface3d/-/gl-surface3d-1.3.0.tgz", "dependencies": { "bl": { @@ -1566,7 +1576,7 @@ }, "gl-vao": { "version": "1.3.0", - "from": "gl-vao@>=1.3.0 <2.0.0", + "from": "gl-vao@>=1.1.3 <2.0.0", "resolved": "https://registry.npmjs.org/gl-vao/-/gl-vao-1.3.0.tgz" }, "gl-vec3": { @@ -1725,7 +1735,7 @@ }, "has": { "version": "1.0.1", - "from": "has@>=1.0.1 <1.1.0", + "from": "has@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz" }, "has-ansi": { @@ -1760,7 +1770,7 @@ }, "ieee754": { "version": "1.1.8", - "from": "ieee754@>=1.1.6 <2.0.0", + "from": "ieee754@>=1.1.4 <2.0.0", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz" }, "incremental-convex-hull": { @@ -1775,7 +1785,7 @@ }, "inherits": { "version": "2.0.3", - "from": "inherits@>=2.0.3 <2.1.0", + "from": "inherits@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "interval-tree-1d": { @@ -1825,7 +1835,7 @@ }, "is-plain-obj": { "version": "1.1.0", - "from": "is-plain-obj@>=1.1.0 <2.0.0", + "from": "is-plain-obj@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz" }, "is-property": { @@ -1871,7 +1881,7 @@ }, "jquery-ui": { "version": "1.12.1", - "from": "jquery-ui@>=1.12.1 <2.0.0", + "from": "jquery-ui@latest", "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.12.1.tgz" }, "jsbn": { @@ -1890,6 +1900,11 @@ "from": "json-stringify-safe@>=5.0.1 <5.1.0", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" }, + "json5": { + "version": "0.5.0", + "from": "json5@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.0.tgz" + }, "jsonlint-lines-primitives": { "version": "1.6.0", "from": "jsonlint-lines-primitives@>=1.6.0 <1.7.0", @@ -1912,7 +1927,7 @@ }, "kdbush": { "version": "1.0.1", - "from": "kdbush@>=1.0.1 <2.0.0", + "from": "kdbush@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-1.0.1.tgz" }, "kind-of": { @@ -1940,6 +1955,11 @@ "from": "levn@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz" }, + "loader-utils": { + "version": "0.2.16", + "from": "loader-utils@>=0.2.11 <0.3.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.16.tgz" + }, "lodash._baseisequal": { "version": "3.0.7", "from": "lodash._baseisequal@>=3.0.0 <4.0.0", @@ -2031,7 +2051,7 @@ }, "marked": { "version": "0.3.6", - "from": "marked@>=0.3.6 <0.4.0", + "from": "marked@latest", "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz" }, "mat4-decompose": { @@ -2051,7 +2071,7 @@ }, "material-design-iconic-font": { "version": "2.2.0", - "from": "material-design-iconic-font@>=2.2.0 <3.0.0", + "from": "material-design-iconic-font@latest", "resolved": "https://registry.npmjs.org/material-design-iconic-font/-/material-design-iconic-font-2.2.0.tgz" }, "matrix-camera-controller": { @@ -2091,7 +2111,7 @@ }, "mouse-change": { "version": "1.4.0", - "from": "mouse-change@>=1.4.0 <2.0.0", + "from": "mouse-change@>=1.1.1 <2.0.0", "resolved": "https://registry.npmjs.org/mouse-change/-/mouse-change-1.4.0.tgz" }, "mouse-event": { @@ -2155,7 +2175,7 @@ }, "ndarray": { "version": "1.0.18", - "from": "ndarray@>=1.0.18 <2.0.0", + "from": "ndarray@>=1.0.16 <2.0.0", "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.18.tgz" }, "ndarray-extract-contour": { @@ -2213,6 +2233,28 @@ "from": "nextafter@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/nextafter/-/nextafter-1.0.0.tgz" }, + "ng-annotate": { + "version": "1.2.1", + "from": "ng-annotate@latest", + "resolved": "https://registry.npmjs.org/ng-annotate/-/ng-annotate-1.2.1.tgz", + "dependencies": { + "acorn": { + "version": "2.6.4", + "from": "acorn@>=2.6.4 <2.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.6.4.tgz" + }, + "convert-source-map": { + "version": "1.1.3", + "from": "convert-source-map@>=1.1.2 <1.2.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz" + } + } + }, + "ng-annotate-loader": { + "version": "0.2.0", + "from": "ng-annotate-loader@latest", + "resolved": "https://registry.npmjs.org/ng-annotate-loader/-/ng-annotate-loader-0.2.0.tgz" + }, "nomnom": { "version": "1.8.1", "from": "nomnom@>=1.5.0", @@ -2240,6 +2282,11 @@ } } }, + "normalize-path": { + "version": "2.0.1", + "from": "normalize-path@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.0.1.tgz" + }, "normals": { "version": "1.1.0", "from": "normals@>=1.0.1 <2.0.0", @@ -2275,9 +2322,14 @@ "from": "once@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" }, + "optimist": { + "version": "0.6.1", + "from": "optimist@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz" + }, "optionator": { "version": "0.8.2", - "from": "optionator@>=0.8.1 <0.9.0", + "from": "optionator@>=0.8.2 <0.9.0", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", "dependencies": { "wordwrap": { @@ -2292,6 +2344,16 @@ "from": "orbit-camera-controller@>=4.0.0 <5.0.0", "resolved": "https://registry.npmjs.org/orbit-camera-controller/-/orbit-camera-controller-4.0.0.tgz" }, + "ordered-ast-traverse": { + "version": "1.1.1", + "from": "ordered-ast-traverse@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/ordered-ast-traverse/-/ordered-ast-traverse-1.1.1.tgz" + }, + "ordered-esprima-props": { + "version": "1.1.0", + "from": "ordered-esprima-props@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/ordered-esprima-props/-/ordered-esprima-props-1.1.0.tgz" + }, "pace-progress": { "version": "1.0.2", "from": "git+https://github.com/getredash/pace.git", @@ -2394,7 +2456,7 @@ }, "punycode": { "version": "1.4.1", - "from": "punycode@>=1.4.1 <2.0.0", + "from": "punycode@>=1.2.4 <2.0.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" }, "quat-slerp": { @@ -2461,7 +2523,7 @@ }, "repeat-string": { "version": "1.6.1", - "from": "repeat-string@>=1.3.0 <2.0.0", + "from": "repeat-string@>=1.5.2 <2.0.0", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" }, "request": { @@ -2483,7 +2545,7 @@ }, "resolve": { "version": "1.1.7", - "from": "resolve@>=1.1.7 <1.2.0", + "from": "resolve@>=1.1.6 <2.0.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz" }, "resolve-protobuf-schema": { @@ -2591,6 +2653,16 @@ "from": "signum@>=0.0.0 <0.0.1", "resolved": "https://registry.npmjs.org/signum/-/signum-0.0.0.tgz" }, + "simple-fmt": { + "version": "0.1.0", + "from": "simple-fmt@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/simple-fmt/-/simple-fmt-0.1.0.tgz" + }, + "simple-is": { + "version": "0.2.0", + "from": "simple-is@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/simple-is/-/simple-is-0.2.0.tgz" + }, "simplicial-complex": { "version": "1.0.0", "from": "simplicial-complex@>=1.0.0 <2.0.0", @@ -2685,6 +2757,11 @@ } } }, + "stable": { + "version": "0.1.5", + "from": "stable@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.5.tgz" + }, "static-eval": { "version": "0.2.4", "from": "static-eval@>=0.2.0 <0.3.0", @@ -2754,6 +2831,16 @@ "from": "string.prototype.trim@>=1.1.2 <1.2.0", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz" }, + "stringmap": { + "version": "0.2.2", + "from": "stringmap@>=0.2.2 <0.3.0", + "resolved": "https://registry.npmjs.org/stringmap/-/stringmap-0.2.2.tgz" + }, + "stringset": { + "version": "0.2.1", + "from": "stringset@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/stringset/-/stringset-0.2.1.tgz" + }, "stringstream": { "version": "0.0.5", "from": "stringstream@>=0.0.4 <0.1.0", @@ -2798,7 +2885,7 @@ }, "through": { "version": "2.3.8", - "from": "through@>=2.3.8 <2.4.0", + "from": "through@>=2.3.6 <3.0.0", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" }, "through2": { @@ -2858,6 +2945,11 @@ "from": "triangulate-polyline@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/triangulate-polyline/-/triangulate-polyline-1.0.3.tgz" }, + "tryor": { + "version": "0.1.2", + "from": "tryor@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/tryor/-/tryor-0.1.2.tgz" + }, "tunnel-agent": { "version": "0.4.3", "from": "tunnel-agent@>=0.4.1 <0.5.0", @@ -2891,7 +2983,7 @@ }, "typedarray": { "version": "0.0.6", - "from": "typedarray@>=0.0.6 <0.0.7", + "from": "typedarray@>=0.0.5 <0.1.0", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" }, "typedarray-pool": { @@ -2958,12 +3050,12 @@ }, "underscore": { "version": "1.8.3", - "from": "underscore@>=1.8.3 <2.0.0", + "from": "underscore@latest", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz" }, "underscore.string": { "version": "3.3.4", - "from": "underscore.string@>=3.3.4 <4.0.0", + "from": "underscore.string@latest", "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.4.tgz" }, "union-find": { @@ -3046,6 +3138,11 @@ "from": "window-size@0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz" }, + "wordwrap": { + "version": "0.0.3", + "from": "wordwrap@>=0.0.2 <0.1.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz" + }, "world-calendars": { "version": "1.0.3", "from": "world-calendars@>=1.0.3 <2.0.0", @@ -3058,7 +3155,7 @@ }, "xtend": { "version": "4.0.1", - "from": "xtend@>=4.0.0 <4.1.0-0", + "from": "xtend@>=4.0.0 <5.0.0", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" }, "yargs": { diff --git a/package.json b/package.json index 371f69224b..2eea99e4b7 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "The frontend part of Redash.", "main": "index.js", "scripts": { - "start": "webpack-dev-server", + "start": "webpack-dev-server --content-base client/app", "build": "rm -rf ./client/dist/ && NODE_ENV=production node node_modules/.bin/webpack", "watch": "webpack --watch --progress --colors -d" }, @@ -22,12 +22,12 @@ "angular": "~1.5.8", "angular-base64-upload": "^0.1.19", "angular-gridster": "^0.13.14", - "angular-messages": "~1.5.8", + "angular-messages": "^1.5.8", "angular-moment": "^1.0.0", "angular-resizable": "^1.2.0", - "angular-resource": "~1.5.8", - "angular-route": "~1.5.8", - "angular-sanitize": "~1.5.8", + "angular-resource": "^1.5.8", + "angular-route": "^1.5.8", + "angular-sanitize": "^1.5.8", "angular-toastr": "^2.1.1", "angular-ui-ace": "^0.2.3", "angular-ui-bootstrap": "^2.2.0", @@ -49,6 +49,8 @@ "moment": "^2.15.2", "mousetrap": "^1.6.0", "mustache": "^2.2.1", + "ng-annotate": "^1.2.1", + "ng-annotate-loader": "^0.2.0", "pace-progress": "git+https://github.com/getredash/pace.git", "pivottable": "^2.3.0", "plotly.js": "1.26.1", @@ -62,21 +64,20 @@ "babel-plugin-transform-object-assign": "^6.22.0", "babel-preset-es2015": "^6.18.0", "babel-preset-stage-2": "^6.18.0", - "css-loader": "^0.28.4", + "css-loader": "^0.25.0", "eslint": "^3.9.0", "eslint-config-airbnb-base": "^9.0.0", "eslint-loader": "^1.6.0", "eslint-plugin-import": "^2.0.1", - "extract-text-webpack-plugin": "^2.1.2", - "file-loader": "^0.11.2", + "extract-text-webpack-plugin": "^1.0.1", + "file-loader": "^0.9.0", "html-webpack-plugin": "^2.24.0", - "ng-annotate-loader": "^0.6.1", - "node-sass": "^4.5.3", + "node-sass": "^4.3.0", "raw-loader": "^0.5.1", - "sass-loader": "^6.0.6", - "url-loader": "^0.5.9", - "webpack": "^2.6.1", - "webpack-build-notifier": "^0.1.14", - "webpack-dev-server": "^2.4.5" + "sass-loader": "^4.1.1", + "url-loader": "^0.5.7", + "webpack": "^1.13.3", + "webpack-build-notifier": "^0.1.13", + "webpack-dev-server": "^1.16.2" } } diff --git a/redash/authentication/org_resolving.py b/redash/authentication/org_resolving.py index da5e5f5638..52f7309cd3 100644 --- a/redash/authentication/org_resolving.py +++ b/redash/authentication/org_resolving.py @@ -1,3 +1,10 @@ +""" +This module implements different strategies to resolve the current Organization we are using. + +By default we use the simple single_org strategy, which assumes you have a +single Organization in your installation. +""" + import logging from flask import g, request diff --git a/redash/cli/__init__.py b/redash/cli/__init__.py index 0176bcdf0c..7fb550eb31 100644 --- a/redash/cli/__init__.py +++ b/redash/cli/__init__.py @@ -68,23 +68,3 @@ def send_test_mail(email=None): mail.send(Message(subject="Test Message from Redash", recipients=[email], body="Test message.")) - -@manager.command() -def ipython(): - """Starts IPython shell instead of the default Python shell.""" - import sys - import IPython - from flask.globals import _app_ctx_stack - app = _app_ctx_stack.top.app - - banner = 'Python %s on %s\nIPython: %s\nRedash version: %s\n' % ( - sys.version, - sys.platform, - IPython.__version__, - __version__ - ) - - ctx = {} - ctx.update(app.make_shell_context()) - - IPython.embed(banner1=banner, user_ns=ctx) diff --git a/redash/handlers/alerts.py b/redash/handlers/alerts.py index fa9b0b15fe..63ba0a1e79 100644 --- a/redash/handlers/alerts.py +++ b/redash/handlers/alerts.py @@ -4,10 +4,8 @@ from funcy import project from redash import models -from redash.handlers.base import (BaseResource, get_object_or_404, - require_fields) -from redash.permissions import (require_access, require_admin_or_owner, - require_permission, view_only) +from redash.permissions import require_access, require_admin_or_owner, view_only, require_permission +from redash.handlers.base import BaseResource, require_fields, get_object_or_404 class AlertResource(BaseResource): @@ -54,7 +52,6 @@ def post(self): name=req['name'], query_rel=query, user=self.current_user, - rearm=req.get('rearm'), options=req['options'] ) diff --git a/redash/handlers/authentication.py b/redash/handlers/authentication.py index 912988819d..c19b7d175e 100644 --- a/redash/handlers/authentication.py +++ b/redash/handlers/authentication.py @@ -2,7 +2,6 @@ import logging from flask import flash, redirect, render_template, request, url_for -from flask import abort, flash, redirect, render_template, request, url_for from flask_login import current_user, login_required, login_user, logout_user from redash import __version__, limiter, models, settings @@ -75,9 +74,6 @@ def reset(token, org_slug=None): @routes.route(org_scoped_rule('/forgot'), methods=['GET', 'POST']) def forgot_password(org_slug=None): - if not settings.PASSWORD_LOGIN_ENABLED: - abort(404) - submitted = False if request.method == 'POST' and request.form['email']: submitted = True @@ -133,7 +129,7 @@ def login(org_slug=None): return render_template("login.html", org_slug=org_slug, next=next_path, - email=request.form.get('email', ''), + username=request.form.get('username', ''), show_google_openid=settings.GOOGLE_OAUTH_ENABLED, google_auth_url=google_auth_url, show_saml_login=settings.SAML_LOGIN_ENABLED, @@ -171,6 +167,8 @@ def client_config(): return client_config + +# @routes.route(org_scoped_rule('/api/config'), methods=['GET']) @routes.route('/api/config', methods=['GET']) def config(org_slug=None): return json_response({ diff --git a/redash/handlers/dashboards.py b/redash/handlers/dashboards.py index f47c59fb2b..6ce7d6b0f2 100644 --- a/redash/handlers/dashboards.py +++ b/redash/handlers/dashboards.py @@ -2,7 +2,7 @@ import json from flask import request, url_for -from funcy import distinct, project, take +from funcy import distinct, project, take from sqlalchemy.exc import IntegrityError from sqlalchemy.orm.exc import StaleDataError @@ -140,7 +140,7 @@ def post(self, dashboard_slug): abort(400) updates = project(dashboard_properties, ('name', 'layout', 'version', - 'is_draft', 'dashboard_filters_enabled')) + 'is_draft')) # SQLAlchemy handles the case where a concurrent transaction beats us # to the update. But we still have to make sure that we're not starting diff --git a/redash/handlers/data_sources.py b/redash/handlers/data_sources.py index 17fb163133..fea3885642 100644 --- a/redash/handlers/data_sources.py +++ b/redash/handlers/data_sources.py @@ -2,7 +2,6 @@ from flask import make_response, request from funcy import project -from sqlalchemy.exc import IntegrityError from flask_restful import abort from redash import models @@ -44,14 +43,7 @@ def post(self, data_source_id): data_source.type = req['type'] data_source.name = req['name'] models.db.session.add(data_source) - - try: - models.db.session.commit() - except IntegrityError as e: - if req['name'] in e.message: - abort(400, message="Data source with the name {} already exists.".format(req['name'])) - - abort(400) + models.db.session.commit() return data_source.to_dict(all=True) @@ -103,18 +95,12 @@ def post(self): if not config.is_valid(): abort(400) - try: - datasource = models.DataSource.create_with_group(org=self.current_org, - name=req['name'], - type=req['type'], - options=config) - - models.db.session.commit() - except IntegrityError as e: - if req['name'] in e.message: - abort(400, message="Data source with the name {} already exists.".format(req['name'])) + datasource = models.DataSource.create_with_group(org=self.current_org, + name=req['name'], + type=req['type'], + options=config) - abort(400) + models.db.session.commit() self.record_event({ 'action': 'create', diff --git a/redash/handlers/destinations.py b/redash/handlers/destinations.py index c1895b7321..aac056376e 100644 --- a/redash/handlers/destinations.py +++ b/redash/handlers/destinations.py @@ -2,11 +2,10 @@ from flask_restful import abort from redash import models -from redash.destinations import (destinations, - get_configuration_schema_for_destination_type) -from redash.handlers.base import BaseResource from redash.permissions import require_admin +from redash.destinations import destinations, get_configuration_schema_for_destination_type from redash.utils.configuration import ConfigurationContainer, ValidationError +from redash.handlers.base import BaseResource class DestinationTypeListResource(BaseResource): @@ -31,8 +30,6 @@ def post(self, destination_id): abort(400) try: - destination.type = req['type'] - destination.name = req['name'] destination.options.set_schema(schema) destination.options.update(req['options']) models.db.session.add(destination) @@ -40,6 +37,9 @@ def post(self, destination_id): except ValidationError: abort(400) + destination.type = req['type'] + destination.name = req['name'] + return destination.to_dict(all=True) @require_admin diff --git a/redash/handlers/queries.py b/redash/handlers/queries.py index 48abbfba42..46b18b02d9 100644 --- a/redash/handlers/queries.py +++ b/redash/handlers/queries.py @@ -60,14 +60,14 @@ def get(self): if settings.FEATURE_DUMB_RECENTS: results = models.Query.by_user(self.current_user).order_by(models.Query.updated_at.desc()).limit(10) - queries = [q.to_dict(with_last_modified_by=False, with_user=False) for q in results] + queries = [q.to_dict(with_last_modified_by=False) for q in results] else: queries = models.Query.recent(self.current_user.group_ids, self.current_user.id) - recent = [d.to_dict(with_last_modified_by=False, with_user=False) for d in queries] + recent = [d.to_dict(with_last_modified_by=False) for d in queries] global_recent = [] if len(recent) < 10: - global_recent = [d.to_dict(with_last_modified_by=False, with_user=False) for d in models.Query.recent(self.current_user.group_ids)] + global_recent = [d.to_dict(with_last_modified_by=False) for d in models.Query.recent(self.current_user.group_ids)] queries = take(20, distinct(chain(recent, global_recent), key=lambda d: d['id'])) diff --git a/redash/handlers/query_results.py b/redash/handlers/query_results.py index 397a3b03f1..8193d57c71 100644 --- a/redash/handlers/query_results.py +++ b/redash/handlers/query_results.py @@ -1,4 +1,3 @@ -import logging import json import time @@ -10,7 +9,7 @@ from redash.tasks import QueryTask, record_event from redash.permissions import require_permission, not_view_only, has_access, require_access, view_only from redash.handlers.base import BaseResource, get_object_or_404 -from redash.utils import collect_query_parameters, collect_parameters_from_request, gen_query_hash +from redash.utils import collect_query_parameters, collect_parameters_from_request from redash.tasks.queries import enqueue_query @@ -18,55 +17,6 @@ def error_response(message): return {'job': {'status': 4, 'error': message}}, 400 -# -# Run a parameterized query synchronously and return the result -# DISCLAIMER: Temporary solution to support parameters in queries. Should be -# removed once we refactor the query results API endpoints and handling -# on the client side. Please don't reuse in other API handlers. -# -def run_query_sync(data_source, parameter_values, query_text, max_age=0): - query_parameters = set(collect_query_parameters(query_text)) - missing_params = set(query_parameters) - set(parameter_values.keys()) - if missing_params: - raise Exception('Missing parameter value for: {}'.format(", ".join(missing_params))) - - if query_parameters: - query_text = pystache.render(query_text, parameter_values) - - if max_age <= 0: - query_result = None - else: - query_result = models.QueryResult.get_latest(data_source, query_text, max_age) - - query_hash = gen_query_hash(query_text) - - if query_result: - logging.info("Returning cached result for query %s" % query_hash) - return query_result - - try: - started_at = time.time() - data, error = data_source.query_runner.run_query(query_text, current_user) - - if error: - logging.info('got bak error') - logging.info(error) - return None - - run_time = time.time() - started_at - query_result, updated_query_ids = models.QueryResult.store_result(data_source.org, data_source, - query_hash, query_text, data, - run_time, utils.utcnow()) - - models.db.session.commit() - return query_result - except Exception, e: - if max_age > 0: - abort(404, message="Unable to get result from the database, and no cached query result found.") - else: - abort(503, message="Unable to get result from the database.") - return None - def run_query(data_source, parameter_values, query_text, query_id, max_age=0): query_parameters = set(collect_query_parameters(query_text)) missing_params = set(query_parameters) - set(parameter_values.keys()) @@ -177,22 +127,15 @@ def get(self, query_id=None, query_result_id=None, filetype='json'): # They need to be split, as they have different logic (for example, retrieving by query id # should check for query parameters and shouldn't cache the result). should_cache = query_result_id is not None - - parameter_values = collect_parameters_from_request(request.args) - max_age = int(request.args.get('maxAge', 0)) - - query_result = None + if query_result_id is None and query_id is not None: + query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org) + if query: + query_result_id = query.latest_query_data_id if query_result_id: query_result = get_object_or_404(models.QueryResult.get_by_id_and_org, query_result_id, self.current_org) - elif query_id is not None: - query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org) - - if query is not None: - if settings.ALLOW_PARAMETERS_IN_EMBEDS and parameter_values: - query_result = run_query_sync(query.data_source, parameter_values, query.to_dict()['query'], max_age=max_age) - elif query.latest_query_data_id is not None: - query_result = get_object_or_404(models.QueryResult.get_by_id_and_org, query.latest_query_data_id, self.current_org) + else: + query_result = None if query_result: require_access(query_result.data_source.groups, self.current_user, view_only) @@ -266,3 +209,4 @@ def delete(self, job_id): """ job = QueryTask(job_id=job_id) job.cancel() + diff --git a/redash/handlers/static.py b/redash/handlers/static.py index 98dcb5e6b2..806e6c625a 100644 --- a/redash/handlers/static.py +++ b/redash/handlers/static.py @@ -27,7 +27,7 @@ def send_static(filename): def render_index(): - if settings.MULTI_ORG: + if settings.MULTI_ORG == "true": response = render_template("multi_org.html", base_href=base_href()) else: full_path = safe_join(settings.STATIC_ASSETS_PATHS[-2], 'index.html') diff --git a/redash/models.py b/redash/models.py index 775191857b..bd8c2eb5b9 100644 --- a/redash/models.py +++ b/redash/models.py @@ -8,20 +8,12 @@ import logging import time +from funcy import project + import xlsxwriter from flask_login import AnonymousUserMixin, UserMixin from flask_sqlalchemy import SQLAlchemy -from funcy import project from passlib.apps import custom_app_context as pwd_context -from sqlalchemy import or_ -from sqlalchemy.dialects import postgresql -from sqlalchemy.event import listens_for -from sqlalchemy.ext.mutable import Mutable -from sqlalchemy.inspection import inspect -from sqlalchemy.orm import backref, joinedload, object_session, subqueryload -from sqlalchemy.orm.exc import NoResultFound # noqa: F401 -from sqlalchemy.types import TypeDecorator - from redash import redis_connection, utils from redash.destinations import (get_configuration_schema_for_destination_type, get_destination) @@ -31,6 +23,14 @@ get_query_runner) from redash.utils import generate_token, json_dumps from redash.utils.configuration import ConfigurationContainer +from sqlalchemy import or_ +from sqlalchemy.dialects import postgresql +from sqlalchemy.event import listens_for +from sqlalchemy.ext.mutable import Mutable +from sqlalchemy.inspection import inspect +from sqlalchemy.orm import backref, joinedload, object_session, subqueryload +from sqlalchemy.orm.exc import NoResultFound # noqa: F401 +from sqlalchemy.types import TypeDecorator db = SQLAlchemy(session_options={ 'expire_on_commit': False @@ -943,7 +943,7 @@ def search(cls, term, group_ids, include_drafts=False): @classmethod def recent(cls, group_ids, user_id=None, limit=20): - query = (cls.query + query = (cls.query.options(subqueryload(Query.user)) .filter(Event.created_at > (db.func.current_date() - 7)) .join(Event, Query.id == Event.object_id.cast(db.Integer)) .join(DataSourceGroup, Query.data_source_id == DataSourceGroup.data_source_id) @@ -1184,20 +1184,18 @@ def to_dict(self, full=True): def evaluate(self): data = json.loads(self.query_rel.latest_query_data.data) - if data['rows']: - value = data['rows'][0][self.options['column']] - op = self.options['op'] - - if op == 'greater than' and value > self.options['value']: - new_state = self.TRIGGERED_STATE - elif op == 'less than' and value < self.options['value']: - new_state = self.TRIGGERED_STATE - elif op == 'equals' and value == self.options['value']: - new_state = self.TRIGGERED_STATE - else: - new_state = self.OK_STATE + # todo: safe guard for empty + value = data['rows'][0][self.options['column']] + op = self.options['op'] + + if op == 'greater than' and value > self.options['value']: + new_state = self.TRIGGERED_STATE + elif op == 'less than' and value < self.options['value']: + new_state = self.TRIGGERED_STATE + elif op == 'equals' and value == self.options['value']: + new_state = self.TRIGGERED_STATE else: - new_state = self.UNKNOWN_STATE + new_state = self.OK_STATE return new_state @@ -1517,9 +1515,8 @@ class NotificationDestination(BelongsToOrgMixin, db.Model): user = db.relationship(User, backref="notification_destinations") name = Column(db.String(255)) type = Column(db.String(255)) - options = Column(ConfigurationContainer.as_mutable(Configuration)) + options = Column(Configuration) created_at = Column(db.DateTime(True), default=db.func.now()) - __tablename__ = 'notification_destinations' __table_args__ = (db.Index('notification_destinations_org_id_name', 'org_id', 'name', unique=True),) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index 154c1d54ba..2612338588 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -1,45 +1,22 @@ import json -import logging import os import re -from redash.query_runner import * -from redash.settings import parse_boolean -from redash.utils import JSONEncoder - -logger = logging.getLogger(__name__) -ANNOTATE_QUERY = parse_boolean(os.environ.get('ATHENA_ANNOTATE_QUERY', 'true')) -SHOW_EXTRA_SETTINGS = parse_boolean(os.environ.get('ATHENA_SHOW_EXTRA_SETTINGS', 'true')) -OPTIONAL_CREDENTIALS = parse_boolean(os.environ.get('ATHENA_OPTIONAL_CREDENTIALS', 'true')) +import requests try: - import pyathena - enabled = True + import botocore.session + from botocore.exceptions import WaiterError + direct_enabled = True except ImportError: - enabled = False - - -_TYPE_MAPPINGS = { - 'boolean': TYPE_BOOLEAN, - 'tinyint': TYPE_INTEGER, - 'smallint': TYPE_INTEGER, - 'integer': TYPE_INTEGER, - 'bigint': TYPE_INTEGER, - 'double': TYPE_FLOAT, - 'varchar': TYPE_STRING, - 'timestamp': TYPE_DATETIME, - 'date': TYPE_DATE, - 'varbinary': TYPE_STRING, - 'array': TYPE_STRING, - 'map': TYPE_STRING, - 'row': TYPE_STRING, - 'decimal': TYPE_FLOAT, -} - - -class SimpleFormatter(object): - def format(self, operation, parameters=None): - return operation + direct_enabled = False + +from redash.query_runner import BaseQueryRunner, register +from redash.utils import JSONEncoder +from redash.settings import parse_boolean + +PROXY_URL = os.environ.get('ATHENA_PROXY_URL') +ANNOTATE_QUERY = parse_boolean(os.environ.get('ATHENA_ANNOTATE_QUERY', 'true')) class Athena(BaseQueryRunner): noop_query = 'SELECT 1' @@ -50,7 +27,7 @@ def name(cls): @classmethod def configuration_schema(cls): - schema = { + return { 'type': 'object', 'properties': { 'region': { @@ -68,104 +45,69 @@ def configuration_schema(cls): 's3_staging_dir': { 'type': 'string', 'title': 'S3 Staging Path' - }, - 'schema': { - 'type': 'string', - 'title': 'Schema Name', - 'default': 'default' - }, + } }, - 'required': ['region', 's3_staging_dir'], - 'order': ['region', 'aws_access_key', 'aws_secret_key', 's3_staging_dir', 'schema'], + 'required': ['region', 'aws_access_key', 'aws_secret_key', 's3_staging_dir'], 'secret': ['aws_secret_key'] } - if SHOW_EXTRA_SETTINGS: - schema['properties'].update({ - 'encryption_option': { - 'type': 'string', - 'title': 'Encryption Option', - }, - 'kms_key': { - 'type': 'string', - 'title': 'KMS Key', - }, - }) - - if not OPTIONAL_CREDENTIALS: - schema['required'] += ['aws_access_key', 'aws_secret_key'] - - return schema - - @classmethod - def enabled(cls): - return enabled - @classmethod def annotate_query(cls): return ANNOTATE_QUERY - @classmethod - def type(cls): - return "athena" - - def __init__(self, configuration): - super(Athena, self).__init__(configuration) - def get_schema(self, get_stats=False): schema = {} query = """ SELECT table_schema, table_name, column_name FROM information_schema.columns - WHERE table_schema NOT IN ('information_schema') + WHERE table_schema NOT IN ('pg_catalog', 'information_schema') """ results, error = self.run_query(query, None) + if error is not None: raise Exception("Failed getting schema.") results = json.loads(results) + for row in results['rows']: - table_name = '{0}.{1}'.format(row['table_schema'], row['table_name']) + table_name = '{}.{}'.format(row['table_schema'], row['table_name']) + if table_name not in schema: schema[table_name] = {'name': table_name, 'columns': []} + schema[table_name]['columns'].append(row['column_name']) return schema.values() def run_query(self, query, user): - cursor = pyathena.connect( - s3_staging_dir=self.configuration['s3_staging_dir'], - region_name=self.configuration['region'], - aws_access_key_id=self.configuration.get('aws_access_key', None), - aws_secret_access_key=self.configuration.get('aws_secret_key', None), - schema_name=self.configuration.get('schema', 'default'), - encryption_option=self.configuration.get('encryption_option', None), - kms_key=self.configuration.get('kms_key', None), - formatter=SimpleFormatter()).cursor() - try: - cursor.execute(query) - column_tuples = [(i[0], _TYPE_MAPPINGS.get(i[1], None)) for i in cursor.description] - columns = self.fetch_columns(column_tuples) - rows = [dict(zip(([c['name'] for c in columns]), r)) for i, r in enumerate(cursor.fetchall())] - data = {'columns': columns, 'rows': rows} - json_data = json.dumps(data, cls=JSONEncoder) + data = { + 'athenaUrl': 'jdbc:awsathena://athena.{}.amazonaws.com:443/'.format(self.configuration['region'].lower()), + 'awsAccessKey': self.configuration['aws_access_key'], + 'awsSecretKey': self.configuration['aws_secret_key'], + 's3StagingDir': self.configuration['s3_staging_dir'], + 'query': query + } + + response = requests.post(PROXY_URL, json=data) + response.raise_for_status() + + json_data = response.content.strip() error = None + + return json_data, error + except requests.RequestException as e: + if e.response.status_code == 400: + return None, response.content + + return None, str(e) except KeyboardInterrupt: - if cursor.query_id: - cursor.cancel() error = "Query cancelled by user." json_data = None - except Exception, ex: - if cursor.query_id: - cursor.cancel() - error = ex.message - json_data = None return json_data, error - register(Athena) class AthenaDirect(BaseQueryRunner): @@ -177,7 +119,7 @@ def name(cls): @classmethod def enabled(cls): - return enabled + return direct_enabled @classmethod def configuration_schema(cls): diff --git a/redash/query_runner/memsql_ds.py b/redash/query_runner/memsql_ds.py index 62728dda42..12b78d7645 100644 --- a/redash/query_runner/memsql_ds.py +++ b/redash/query_runner/memsql_ds.py @@ -93,7 +93,8 @@ def _get_tables(self, schema): table_name = '.'.join((schema_name, table_name)) columns = filter(lambda a: len(a) > 0, map(lambda a: str(a['Field']), self._run_query_internal(columns_query % table_name))) - schema[table_name] = {'name': table_name, 'columns': columns} + + schema[table_name] = {'name': table_name, 'columns': columns} return schema.values() def run_query(self, query, user): diff --git a/redash/query_runner/salesforce.py b/redash/query_runner/salesforce.py index 0cfc1d8403..6d9678b067 100644 --- a/redash/query_runner/salesforce.py +++ b/redash/query_runner/salesforce.py @@ -92,7 +92,7 @@ def _get_sf(self): sf = SimpleSalesforce(username=self.configuration['username'], password=self.configuration['password'], security_token=self.configuration['token'], - sandbox=self.configuration.get('sandbox', False), + sandbox=self.configuration['sandbox'], client_id='Redash') return sf diff --git a/redash/settings.py b/redash/settings.py index e995540e4b..7c3c677198 100644 --- a/redash/settings.py +++ b/redash/settings.py @@ -185,7 +185,6 @@ def all_settings(): 'redash.query_runner.sqlite', 'redash.query_runner.dynamodb_sql', 'redash.query_runner.mssql', - 'redash.query_runner.memsql_ds', 'redash.query_runner.jql', 'redash.query_runner.google_analytics', 'redash.query_runner.snowflake', @@ -229,7 +228,6 @@ def all_settings(): FEATURE_SHOW_PERMISSIONS_CONTROL = parse_boolean(os.environ.get("REDASH_FEATURE_SHOW_PERMISSIONS_CONTROL", "false")) FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS = parse_boolean(os.environ.get("REDASH_FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS", "false")) FEATURE_DUMB_RECENTS = parse_boolean(os.environ.get("REDASH_FEATURE_DUMB_RECENTS", "false")) -FEATURE_AUTO_PUBLISH_NAMED_QUERIES = parse_boolean(os.environ.get("REDASH_FEATURE_AUTO_PUBLISH_NAMED_QUERIES", "true")) # BigQuery BIGQUERY_HTTP_TIMEOUT = int(os.environ.get("REDASH_BIGQUERY_HTTP_TIMEOUT", "600")) @@ -246,7 +244,6 @@ def all_settings(): 'allowScriptsInUserInput': ALLOW_SCRIPTS_IN_USER_INPUT, 'showPermissionsControl': FEATURE_SHOW_PERMISSIONS_CONTROL, 'allowCustomJSVisualizations': FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS, - 'autoPublishNamedQueries': FEATURE_AUTO_PUBLISH_NAMED_QUERIES, 'dateFormat': DATE_FORMAT, 'dateTimeFormat': "{0} HH:mm".format(DATE_FORMAT), 'allowAllToEditQueries': FEATURE_ALLOW_ALL_TO_EDIT_QUERIES, diff --git a/redash/tasks/queries.py b/redash/tasks/queries.py index ab8279e365..77a8f319e0 100644 --- a/redash/tasks/queries.py +++ b/redash/tasks/queries.py @@ -278,7 +278,6 @@ def refresh_queries(): query_text = pystache.render(query.query_text, query_params) else: query_text = query.query_text - enqueue_query(query_text, query.data_source, query.user_id, scheduled_query=query, metadata={'Query ID': query.id, 'Username': 'Scheduled'}) diff --git a/redash/utils/__init__.py b/redash/utils/__init__.py index a9cc954dc7..60bc0199ec 100644 --- a/redash/utils/__init__.py +++ b/redash/utils/__init__.py @@ -9,7 +9,6 @@ import hashlib import pytz import pystache -import os from funcy import distinct, select_values from sqlalchemy.orm.query import Query @@ -18,7 +17,6 @@ from redash import settings COMMENTS_REGEX = re.compile("/\*.*?\*/") -WRITER_ENCODING = os.environ.get('REDASH_CSV_WRITER_ENCODING', 'utf-8') def utcnow(): @@ -104,7 +102,7 @@ class UnicodeWriter: which is encoded in the given encoding. """ - def __init__(self, f, dialect=csv.excel, encoding=WRITER_ENCODING, **kwds): + def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds): # Redirect output to a queue self.queue = cStringIO.StringIO() self.writer = csv.writer(self.queue, dialect=dialect, **kwds) @@ -113,7 +111,7 @@ def __init__(self, f, dialect=csv.excel, encoding=WRITER_ENCODING, **kwds): def _encode_utf8(self, val): if isinstance(val, (unicode, str)): - return val.encode(WRITER_ENCODING) + return val.encode('utf-8') return val @@ -121,7 +119,7 @@ def writerow(self, row): self.writer.writerow([self._encode_utf8(s) for s in row]) # Fetch UTF-8 output from the queue ... data = self.queue.getvalue() - data = data.decode(WRITER_ENCODING) + data = data.decode("utf-8") # ... and reencode it into the target encoding data = self.encoder.encode(data) # write to the target stream diff --git a/requirements_all_ds.txt b/requirements_all_ds.txt index 382d0a91e1..10fa5316fa 100644 --- a/requirements_all_ds.txt +++ b/requirements_all_ds.txt @@ -12,7 +12,7 @@ td-client==0.8.0 pymssql==2.1.3 dql==0.5.16 dynamo3==0.4.7 -botocore==1.5.72 +botocore==1.5.21 sasl>=0.1.3 thrift>=0.8.0 thrift_sasl>=0.1.0 @@ -21,6 +21,5 @@ memsql==2.16.0 snowflake_connector_python==1.3.16 atsd_client==2.0.12 simple_salesforce==0.72.2 -PyAthena>=1.0.0 # certifi is needed to support MongoDB and SSL: certifi diff --git a/tests/factories.py b/tests/factories.py index 04b8f2f271..216f868ec0 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -1,8 +1,8 @@ import redash.models from redash.models import db -from redash.permissions import ACCESS_TYPE_MODIFY from redash.utils import gen_query_hash, utcnow from redash.utils.configuration import ConfigurationContainer +from redash.permissions import ACCESS_TYPE_MODIFY class ModelFactory(object): @@ -167,6 +167,11 @@ def data_source(self): return self._data_source + def _init_org(self): + if self._org is None: + self._org, self._admin_group, self._default_group = redash.models.init_db() + self.org.domain = 'org0.example.org' + def create_org(self, **kwargs): org = org_factory.create(**kwargs) self.create_group(org=org, type=redash.models.Group.BUILTIN_GROUP, name="default") diff --git a/tests/handlers/test_alerts.py b/tests/handlers/test_alerts.py index 638595cb0d..9d7c185f3b 100644 --- a/tests/handlers/test_alerts.py +++ b/tests/handlers/test_alerts.py @@ -1,6 +1,5 @@ from tests import BaseTestCase - -from redash.models import Alert, AlertSubscription, db +from redash.models import AlertSubscription, Alert, db class TestAlertResourceGet(BaseTestCase): @@ -93,10 +92,8 @@ def test_returns_200_if_has_access_to_query(self): destination = self.factory.create_destination() db.session.commit() rv = self.make_request('post', "/api/alerts", data=dict(name='Alert', query_id=query.id, - destination_id=destination.id, options={}, - rearm=100)) + destination_id=destination.id, options={})) self.assertEqual(rv.status_code, 200) - self.assertEqual(rv.json['rearm'], 100) def test_fails_if_doesnt_have_access_to_query(self): data_source = self.factory.create_data_source(group=self.factory.create_group()) diff --git a/tests/handlers/test_destinations.py b/tests/handlers/test_destinations.py index 2e1533b7cb..c48bbfa507 100644 --- a/tests/handlers/test_destinations.py +++ b/tests/handlers/test_destinations.py @@ -1,5 +1,4 @@ from tests import BaseTestCase - from redash.models import NotificationDestination @@ -56,14 +55,10 @@ def test_post(self): data = { 'name': 'updated', 'type': d.type, - 'options': {"url": "https://www.slack.com/updated"} + 'options': d.options.to_dict() } - - with self.app.app_context(): - rv = self.make_request('post', '/api/destinations/{}'.format(d.id), user=self.factory.create_admin(), data=data) - + rv = self.make_request('post', '/api/destinations/{}'.format(d.id), user=self.factory.create_admin(), data=data) self.assertEqual(rv.status_code, 200) + self.assertEqual(NotificationDestination.query.get(d.id).name, data['name']) + - d = NotificationDestination.query.get(d.id) - self.assertEqual(d.name, data['name']) - self.assertEqual(d.options['url'], data['options']['url']) diff --git a/webpack.config.js b/webpack.config.js index 420dc43188..0760ad1132 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -13,7 +13,8 @@ var config = { app: './client/app/index.js' }, output: { - path: path.join(__dirname, 'client', 'dist'), + // path: process.env.NODE_ENV === 'production' ? './dist' : './dev', + path: './client/dist', filename: '[name].js', publicPath: '/' }, @@ -23,6 +24,7 @@ var config = { new webpack.DefinePlugin({ ON_TEST: process.env.NODE_ENV === 'test' }), + new webpack.optimize.DedupePlugin(), new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: function (module, count) { @@ -49,74 +51,42 @@ var config = { template: './client/app/multi_org.html', filename: 'multi_org.html' }), - new ExtractTextPlugin({ - filename: 'styles.[chunkhash].css' - }) + new ExtractTextPlugin('styles.[chunkhash].css') ], module: { - rules: [ - { - test: /\.js$/, - exclude: /node_modules/, - use: ['ng-annotate-loader', 'babel-loader', 'eslint-loader'] - }, - { - test: /\.html$/, - exclude: [/node_modules/, /index\.html/], - use: [{ - loader: 'raw-loader' - }] - }, - { - test: /\.css$/, - use: ExtractTextPlugin.extract([{ - loader: 'css-loader', - options: { - minimize: process.env.NODE_ENV === 'production' - } - }]) - }, + loaders: [ + {test: /\.js$/, loader: 'ng-annotate!babel!eslint', exclude: /node_modules/}, + {test: /\.html$/, loader: 'raw', exclude: [/node_modules/, /index\.html/]}, + // {test: /\.css$/, loader: 'style!css', exclude: /node_modules/}, + {test: /\.css$/, loader: ExtractTextPlugin.extract("css-loader")}, { test: /\.scss$/, - use: ExtractTextPlugin.extract([ - { - loader: 'css-loader', - options: { - minimize: process.env.NODE_ENV === 'production' - } - }, { - loader: 'sass-loader' - } - ]) + loader: ExtractTextPlugin.extract(["css-loader", "sass-loader"]) }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, - use: [{ - loader: 'url-loader', - options: { - limit: 10000, - name: 'img/[name].[hash:7].[ext]' - } - }] + loader: 'url', + query: { + limit: 10000, + name: 'img/[name].[hash:7].[ext]' + } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, - use: [{ - loader: 'url-loader', - options: { - limit: 10000, - name: 'fonts/[name].[hash:7].[ext]' - } - }] + loader: 'url', + query: { + limit: 10000, + name: 'fonts/[name].[hash:7].[ext]' + } } + ] }, devtool: 'cheap-eval-module-source-map', devServer: { inline: true, historyApiFallback: true, - contentBase: path.join(__dirname, 'client', 'app'), proxy: { '/login': { target: redashBackend + '/', @@ -165,12 +135,7 @@ if (process.env.DEV_SERVER_HOST) { if (process.env.NODE_ENV === 'production') { config.output.path = __dirname + '/client/dist'; config.output.filename = '[name].[chunkhash].js'; - config.plugins.push(new webpack.optimize.UglifyJsPlugin({ - sourceMap: true, - compress: { - warnings: true - } - })); + config.plugins.push(new webpack.optimize.UglifyJsPlugin()); config.devtool = 'source-map'; } From 16955ca54ea71a2b21f64cdf45362604c2beb850 Mon Sep 17 00:00:00 2001 From: Alison Date: Wed, 12 Jul 2017 07:31:45 -0500 Subject: [PATCH 188/243] fixes locally --- client/app/index.html | 2 +- client/app/multi_org.html | 2 +- client/app/pages/queries/get-data-source-version.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/app/index.html b/client/app/index.html index 424d101b0c..b7e2d2087c 100644 --- a/client/app/index.html +++ b/client/app/index.html @@ -1,5 +1,5 @@ - + diff --git a/client/app/multi_org.html b/client/app/multi_org.html index 44673f7a45..a8d673772b 100644 --- a/client/app/multi_org.html +++ b/client/app/multi_org.html @@ -1,5 +1,5 @@ - + diff --git a/client/app/pages/queries/get-data-source-version.js b/client/app/pages/queries/get-data-source-version.js index 66061ec50b..aa98e420b6 100644 --- a/client/app/pages/queries/get-data-source-version.js +++ b/client/app/pages/queries/get-data-source-version.js @@ -1,5 +1,5 @@ function GetDataSourceVersionCtrl(Events, toastr, $scope, DataSource, $route) { - // 'ngInject'; + 'ngInject'; this.getDataSourceVersion = DataSource.version( { From 630a838a84c8715d1001499818b69ae3c8d51314 Mon Sep 17 00:00:00 2001 From: Alison Date: Wed, 12 Jul 2017 11:12:26 -0500 Subject: [PATCH 189/243] handle ds w/ unimplemented version queries --- client/app/pages/queries/get-data-source-version.js | 2 +- client/app/pages/queries/view.js | 2 +- redash/query_runner/__init__.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/client/app/pages/queries/get-data-source-version.js b/client/app/pages/queries/get-data-source-version.js index aa98e420b6..fa760ad016 100644 --- a/client/app/pages/queries/get-data-source-version.js +++ b/client/app/pages/queries/get-data-source-version.js @@ -13,7 +13,7 @@ const GetDataSourceVersionInfo = { onRefresh: '&', }, controller: GetDataSourceVersionCtrl, - template: '{{ $ctrl.getDataSourceVersion.message }}', + template: '{{ $ctrl.getDataSourceVersion.message }}', }; export default function (ngModule) { diff --git a/client/app/pages/queries/view.js b/client/app/pages/queries/view.js index 574a80ba0d..12fd3261bd 100644 --- a/client/app/pages/queries/view.js +++ b/client/app/pages/queries/view.js @@ -254,7 +254,7 @@ function QueryViewCtrl($scope, Events, $route, $routeParams, $location, $window, updateSchema(); $scope.dataSource = find($scope.dataSources, ds => ds.id === $scope.query.data_source_id); - document.getElementById('data-source-version').innerHTML = ''; + document.getElementById('data-source-version').innerHTML = ''; }; $scope.setVisualizationTab = (visualization) => { diff --git a/redash/query_runner/__init__.py b/redash/query_runner/__init__.py index c3658a3d26..868da2b1dc 100644 --- a/redash/query_runner/__init__.py +++ b/redash/query_runner/__init__.py @@ -48,6 +48,7 @@ class InterruptException(Exception): class BaseQueryRunner(object): noop_query = None default_doc_url = None + data_source_version_query = None def __init__(self, configuration): self.syntax = 'sql' From 2c32c7923c29d2328672734f6a1dd2e62848f872 Mon Sep 17 00:00:00 2001 From: Alison Date: Wed, 12 Jul 2017 14:07:43 -0500 Subject: [PATCH 190/243] Revert "Revert "98 upstream merge"" --- client/app/components/dynamic-form.js | 8 +- client/app/components/index.js | 1 + client/app/components/sort-icon.js | 26 +++ client/app/index.js | 1 + client/app/pages/alerts-list/alerts-list.html | 12 +- client/app/pages/alerts-list/index.js | 26 +-- client/app/pages/dashboards/dashboard.js | 10 +- .../dashboards/edit-dashboard-dialog.html | 9 +- .../pages/dashboards/edit-dashboard-dialog.js | 5 +- client/app/pages/queries/view.js | 7 +- .../app/pages/queries/visualization-embed.js | 2 +- client/app/services/auth.js | 2 +- client/app/utils/paginator.js | 20 +++ npm-shrinkwrap.json | 165 ++++-------------- package.json | 31 ++-- redash/authentication/org_resolving.py | 7 - redash/cli/__init__.py | 20 +++ redash/handlers/alerts.py | 7 +- redash/handlers/authentication.py | 8 +- redash/handlers/dashboards.py | 4 +- redash/handlers/data_sources.py | 26 ++- redash/handlers/destinations.py | 10 +- redash/handlers/queries.py | 6 +- redash/handlers/query_results.py | 72 +++++++- redash/handlers/static.py | 2 +- redash/models.py | 49 +++--- redash/query_runner/athena.py | 140 ++++++++++----- redash/query_runner/memsql_ds.py | 3 +- redash/query_runner/salesforce.py | 2 +- redash/settings.py | 3 + redash/tasks/queries.py | 1 + redash/utils/__init__.py | 8 +- requirements_all_ds.txt | 3 +- tests/factories.py | 7 +- tests/handlers/test_alerts.py | 7 +- tests/handlers/test_destinations.py | 13 +- webpack.config.js | 79 ++++++--- 37 files changed, 487 insertions(+), 315 deletions(-) create mode 100644 client/app/components/sort-icon.js diff --git a/client/app/components/dynamic-form.js b/client/app/components/dynamic-form.js index 6e7708ff61..a318f1380d 100644 --- a/client/app/components/dynamic-form.js +++ b/client/app/components/dynamic-form.js @@ -131,8 +131,12 @@ function DynamicForm($http, toastr, $q) { toastr.success('Saved.'); $scope.dataSourceForm.$setPristine(); }, - () => { - toastr.error('Failed saving.'); + (error) => { + if (error.status === 400 && 'message' in error.data) { + toastr.error(error.data.message); + } else { + toastr.error('Failed saving.'); + } } ); }; diff --git a/client/app/components/index.js b/client/app/components/index.js index fae91fcfc7..17cbf07d64 100644 --- a/client/app/components/index.js +++ b/client/app/components/index.js @@ -18,3 +18,4 @@ export { default as rdTimeAgo } from './rd-time-ago'; export { default as overlay } from './overlay'; export { default as routeStatus } from './route-status'; export { default as filters } from './filters'; +export { default as sortIcon } from './sort-icon'; diff --git a/client/app/components/sort-icon.js b/client/app/components/sort-icon.js new file mode 100644 index 0000000000..adc1ca58d2 --- /dev/null +++ b/client/app/components/sort-icon.js @@ -0,0 +1,26 @@ +export default function (ngModule) { + ngModule.component('sortIcon', { + template: '', + bindings: { + column: '<', + sortColumn: '<', + reverse: '<', + }, + controller() { + this.$onChanges = (changes) => { + ['column', 'sortColumn', 'reverse'].forEach((v) => { + if (v in changes) { + this[v] = changes[v].currentValue; + } + }); + + this.showIcon = false; + + if (this.column === this.sortColumn) { + this.showIcon = true; + this.icon = this.reverse ? 'desc' : 'asc'; + } + }; + }, + }); +} diff --git a/client/app/index.js b/client/app/index.js index 520719121d..2279cb7f8f 100644 --- a/client/app/index.js +++ b/client/app/index.js @@ -100,6 +100,7 @@ registerVisualizations(ngModule); ngModule.config(($routeProvider, $locationProvider, $compileProvider, uiSelectConfig, toastrConfig) => { + $compileProvider.debugInfoEnabled(false); $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|http|data):/); $locationProvider.html5Mode(true); uiSelectConfig.theme = 'bootstrap'; diff --git a/client/app/pages/alerts-list/alerts-list.html b/client/app/pages/alerts-list/alerts-list.html index eeee9cee08..d85ea0ea92 100644 --- a/client/app/pages/alerts-list/alerts-list.html +++ b/client/app/pages/alerts-list/alerts-list.html @@ -4,20 +4,20 @@
-
+
Name Created By State Created By NameCreated ByStateCreated At
{{row.name}}{{row.created_by}}{{row.user.name}} {{row.state | uppercase}} since
- - - - + + + + - + diff --git a/client/app/pages/alerts-list/index.js b/client/app/pages/alerts-list/index.js index 4e315e9129..ac69bda693 100644 --- a/client/app/pages/alerts-list/index.js +++ b/client/app/pages/alerts-list/index.js @@ -1,24 +1,26 @@ import { Paginator } from '../../utils'; import template from './alerts-list.html'; +const stateClass = { + ok: 'label label-success', + triggered: 'label label-danger', + unknown: 'label label-warning', +}; + class AlertsListCtrl { constructor(Events, Alert) { Events.record('view', 'page', 'alerts'); this.alerts = new Paginator([], { itemsPerPage: 20 }); - Alert.query((alerts) => { - const stateClass = { - ok: 'label label-success', - triggered: 'label label-danger', - unknown: 'label label-warning', - }; - - alerts.forEach((alert) => { - alert.class = stateClass[alert.state]; - }); - - this.alerts.updateRows(alerts); + this.alerts.updateRows(alerts.map(alert => ({ + name: alert.name, + state: alert.state, + class: stateClass[alert.state], + created_by: alert.user.name, + created_at: alert.created_at, + updated_at: alert.updated_at, + }))); }); } } diff --git a/client/app/pages/dashboards/dashboard.js b/client/app/pages/dashboards/dashboard.js index 6b12126255..42c08bbd06 100644 --- a/client/app/pages/dashboards/dashboard.js +++ b/client/app/pages/dashboards/dashboard.js @@ -156,12 +156,20 @@ function DashboardCtrl($rootScope, $routeParams, $location, $timeout, $q, $uibMo this.editDashboard = () => { this.dashboard.existing_name = this.dashboard.name; + const previousFiltersState = this.dashboard.dashboard_filters_enabled; $uibModal.open({ component: 'editDashboardDialog', resolve: { dashboard: () => this.dashboard, }, - }).result.then((dashboard) => { this.dashboard = dashboard; }); + }).result.then((dashboard) => { + const shouldRenderDashboard = !previousFiltersState && dashboard.dashboard_filters_enabled; + this.dashboard = dashboard; + + if (shouldRenderDashboard) { + renderDashboard(this.dashboard); + } + }); }; this.addWidget = () => { diff --git a/client/app/pages/dashboards/edit-dashboard-dialog.html b/client/app/pages/dashboards/edit-dashboard-dialog.html index 48be84d4e2..dffe25a71d 100644 --- a/client/app/pages/dashboards/edit-dashboard-dialog.html +++ b/client/app/pages/dashboards/edit-dashboard-dialog.html @@ -11,6 +11,13 @@

A name is required.

+

+ +

+
  • @@ -21,5 +28,5 @@
diff --git a/client/app/pages/dashboards/edit-dashboard-dialog.js b/client/app/pages/dashboards/edit-dashboard-dialog.js index 70eab4bafd..7abd182155 100644 --- a/client/app/pages/dashboards/edit-dashboard-dialog.js +++ b/client/app/pages/dashboards/edit-dashboard-dialog.js @@ -1,4 +1,4 @@ -import { sortBy } from 'underscore'; +import { isEmpty, sortBy } from 'underscore'; import template from './edit-dashboard-dialog.html'; const EditDashboardDialog = { @@ -45,6 +45,8 @@ const EditDashboardDialog = { }); } + this.isFormValid = () => !isEmpty(this.dashboard.name); + this.saveDashboard = () => { this.saveInProgress = true; @@ -65,6 +67,7 @@ const EditDashboardDialog = { slug: this.dashboard.id, name: this.dashboard.name, version: this.dashboard.version, + dashboard_filters_enabled: this.dashboard.dashboard_filters_enabled, layout: JSON.stringify(layout), }; diff --git a/client/app/pages/queries/view.js b/client/app/pages/queries/view.js index 12fd3261bd..998bafb7cc 100644 --- a/client/app/pages/queries/view.js +++ b/client/app/pages/queries/view.js @@ -211,7 +211,12 @@ function QueryViewCtrl($scope, Events, $route, $routeParams, $location, $window, $scope.saveName = () => { Events.record('edit_name', 'query', $scope.query.id); - $scope.saveQuery(undefined, { name: $scope.query.name }); + + if ($scope.query.is_draft && clientConfig.autoPublishNamedQueries && $scope.query.name !== 'New Query') { + $scope.query.is_draft = false; + } + + $scope.saveQuery(undefined, { name: $scope.query.name, is_draft: $scope.query.is_draft }); }; $scope.cancelExecution = () => { diff --git a/client/app/pages/queries/visualization-embed.js b/client/app/pages/queries/visualization-embed.js index 65710cc74e..12921a6a80 100644 --- a/client/app/pages/queries/visualization-embed.js +++ b/client/app/pages/queries/visualization-embed.js @@ -37,7 +37,7 @@ export default function (ngModule) { return session($http, $route, Auth).then(() => { const queryId = $route.current.params.queryId; const query = $http.get(`api/queries/${queryId}`).then(response => response.data); - const queryResult = $http.get(`api/queries/${queryId}/results.json`).then(response => response.data); + const queryResult = $http.get(`api/queries/${queryId}/results.json${location.search}`).then(response => response.data); return $q.all([query, queryResult]); }); } diff --git a/client/app/services/auth.js b/client/app/services/auth.js index 074a7419aa..95d08bf1c7 100644 --- a/client/app/services/auth.js +++ b/client/app/services/auth.js @@ -47,7 +47,7 @@ function AuthService($window, $location, $q, $http) { } this.setApiKey(null); - return $http.get('/api/session').then((response) => { + return $http.get('api/session').then((response) => { storeSession(response.data); return session; }); diff --git a/client/app/utils/paginator.js b/client/app/utils/paginator.js index 4d28c1fa95..546f9a0fd4 100644 --- a/client/app/utils/paginator.js +++ b/client/app/utils/paginator.js @@ -1,8 +1,12 @@ +import { sortBy } from 'underscore'; + export default class Paginator { constructor(rows, { page = 1, itemsPerPage = 20, totalCount = undefined } = {}) { this.page = page; this.itemsPerPage = itemsPerPage; this.updateRows(rows, totalCount); + this.orderByField = undefined; + this.orderByReverse = false; } setPage(page) { @@ -24,4 +28,20 @@ export default class Paginator { this.totalCount = 0; } } + + orderBy(column) { + if (column === this.orderByField) { + this.orderByReverse = !this.orderByReverse; + } else { + this.orderByField = column; + this.orderByReverse = false; + } + + if (this.orderByField) { + this.rows = sortBy(this.rows, this.orderByField); + if (this.orderByReverse) { + this.rows = this.rows.reverse(); + } + } + } } diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 298af0afa0..88aa7a87b4 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "redash-client", - "version": "1.0.3", + "version": "2.0.0", "dependencies": { "3d-view": { "version": "2.0.0", @@ -52,11 +52,6 @@ "from": "alpha-shape@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/alpha-shape/-/alpha-shape-1.0.0.tgz" }, - "alter": { - "version": "0.2.0", - "from": "alter@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz" - }, "amdefine": { "version": "1.0.0", "from": "amdefine@>=0.0.4", @@ -74,7 +69,7 @@ }, "angular-gridster": { "version": "0.13.14", - "from": "angular-gridster@latest", + "from": "angular-gridster@>=0.13.14 <0.14.0", "resolved": "https://registry.npmjs.org/angular-gridster/-/angular-gridster-0.13.14.tgz" }, "angular-messages": { @@ -89,7 +84,7 @@ }, "angular-resizable": { "version": "1.2.0", - "from": "angular-resizable@latest", + "from": "angular-resizable@>=1.2.0 <2.0.0", "resolved": "https://registry.npmjs.org/angular-resizable/-/angular-resizable-1.2.0.tgz" }, "angular-resource": { @@ -109,12 +104,12 @@ }, "angular-toastr": { "version": "2.1.1", - "from": "angular-toastr@latest", + "from": "angular-toastr@>=2.1.1 <3.0.0", "resolved": "https://registry.npmjs.org/angular-toastr/-/angular-toastr-2.1.1.tgz" }, "angular-ui-ace": { "version": "0.2.3", - "from": "angular-ui-ace@latest", + "from": "angular-ui-ace@>=0.2.3 <0.3.0", "resolved": "https://registry.npmjs.org/angular-ui-ace/-/angular-ui-ace-0.2.3.tgz" }, "angular-ui-bootstrap": { @@ -124,7 +119,7 @@ }, "angular-vs-repeat": { "version": "1.1.7", - "from": "angular-vs-repeat@latest", + "from": "angular-vs-repeat@>=1.1.7 <2.0.0", "resolved": "https://registry.npmjs.org/angular-vs-repeat/-/angular-vs-repeat-1.1.7.tgz" }, "ansi-regex": { @@ -139,7 +134,7 @@ }, "arraytools": { "version": "1.1.2", - "from": "arraytools@>=1.0.0 <2.0.0", + "from": "arraytools@>=1.1.2 <2.0.0", "resolved": "https://registry.npmjs.org/arraytools/-/arraytools-1.1.2.tgz" }, "asn1": { @@ -526,7 +521,7 @@ }, "d3": { "version": "3.5.17", - "from": "d3@>=3.5.6 <3.6.0", + "from": "d3@>=3.5.17 <4.0.0", "resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz" }, "d3-cloud": { @@ -578,7 +573,7 @@ }, "defined": { "version": "1.0.0", - "from": "defined@>=1.0.0 <2.0.0", + "from": "defined@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz" }, "delaunay-triangulate": { @@ -634,11 +629,6 @@ "from": "edges-to-adjacency-list@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/edges-to-adjacency-list/-/edges-to-adjacency-list-1.0.0.tgz" }, - "emojis-list": { - "version": "2.1.0", - "from": "emojis-list@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz" - }, "es-abstract": { "version": "1.7.0", "from": "es-abstract@>=1.5.0 <2.0.0", @@ -704,7 +694,7 @@ }, "events": { "version": "1.1.1", - "from": "events@>=1.0.0 <2.0.0", + "from": "events@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz" }, "extend": { @@ -783,7 +773,7 @@ }, "font-awesome": { "version": "4.7.0", - "from": "font-awesome@latest", + "from": "font-awesome@>=4.7.0 <5.0.0", "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz" }, "for-each": { @@ -813,7 +803,7 @@ }, "function-bind": { "version": "1.1.0", - "from": "function-bind@>=1.0.2 <2.0.0", + "from": "function-bind@>=1.1.0 <1.2.0", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz" }, "functional-red-black-tree": { @@ -1173,7 +1163,7 @@ }, "gl-plot2d": { "version": "1.2.0", - "from": "gl-plot2d@>=1.1.6 <2.0.0", + "from": "gl-plot2d@>=1.2.0 <2.0.0", "resolved": "https://registry.npmjs.org/gl-plot2d/-/gl-plot2d-1.2.0.tgz", "dependencies": { "binary-search-bounds": { @@ -1534,7 +1524,7 @@ }, "gl-surface3d": { "version": "1.3.0", - "from": "gl-surface3d@>=1.2.3 <2.0.0", + "from": "gl-surface3d@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/gl-surface3d/-/gl-surface3d-1.3.0.tgz", "dependencies": { "bl": { @@ -1576,7 +1566,7 @@ }, "gl-vao": { "version": "1.3.0", - "from": "gl-vao@>=1.1.3 <2.0.0", + "from": "gl-vao@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/gl-vao/-/gl-vao-1.3.0.tgz" }, "gl-vec3": { @@ -1735,7 +1725,7 @@ }, "has": { "version": "1.0.1", - "from": "has@>=1.0.1 <2.0.0", + "from": "has@>=1.0.1 <1.1.0", "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz" }, "has-ansi": { @@ -1770,7 +1760,7 @@ }, "ieee754": { "version": "1.1.8", - "from": "ieee754@>=1.1.4 <2.0.0", + "from": "ieee754@>=1.1.6 <2.0.0", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz" }, "incremental-convex-hull": { @@ -1785,7 +1775,7 @@ }, "inherits": { "version": "2.0.3", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "inherits@>=2.0.3 <2.1.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "interval-tree-1d": { @@ -1835,7 +1825,7 @@ }, "is-plain-obj": { "version": "1.1.0", - "from": "is-plain-obj@>=1.0.0 <2.0.0", + "from": "is-plain-obj@>=1.1.0 <2.0.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz" }, "is-property": { @@ -1881,7 +1871,7 @@ }, "jquery-ui": { "version": "1.12.1", - "from": "jquery-ui@latest", + "from": "jquery-ui@>=1.12.1 <2.0.0", "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.12.1.tgz" }, "jsbn": { @@ -1900,11 +1890,6 @@ "from": "json-stringify-safe@>=5.0.1 <5.1.0", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" }, - "json5": { - "version": "0.5.0", - "from": "json5@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.0.tgz" - }, "jsonlint-lines-primitives": { "version": "1.6.0", "from": "jsonlint-lines-primitives@>=1.6.0 <1.7.0", @@ -1927,7 +1912,7 @@ }, "kdbush": { "version": "1.0.1", - "from": "kdbush@>=1.0.0 <2.0.0", + "from": "kdbush@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-1.0.1.tgz" }, "kind-of": { @@ -1955,11 +1940,6 @@ "from": "levn@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz" }, - "loader-utils": { - "version": "0.2.16", - "from": "loader-utils@>=0.2.11 <0.3.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.16.tgz" - }, "lodash._baseisequal": { "version": "3.0.7", "from": "lodash._baseisequal@>=3.0.0 <4.0.0", @@ -2051,7 +2031,7 @@ }, "marked": { "version": "0.3.6", - "from": "marked@latest", + "from": "marked@>=0.3.6 <0.4.0", "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz" }, "mat4-decompose": { @@ -2071,7 +2051,7 @@ }, "material-design-iconic-font": { "version": "2.2.0", - "from": "material-design-iconic-font@latest", + "from": "material-design-iconic-font@>=2.2.0 <3.0.0", "resolved": "https://registry.npmjs.org/material-design-iconic-font/-/material-design-iconic-font-2.2.0.tgz" }, "matrix-camera-controller": { @@ -2111,7 +2091,7 @@ }, "mouse-change": { "version": "1.4.0", - "from": "mouse-change@>=1.1.1 <2.0.0", + "from": "mouse-change@>=1.4.0 <2.0.0", "resolved": "https://registry.npmjs.org/mouse-change/-/mouse-change-1.4.0.tgz" }, "mouse-event": { @@ -2175,7 +2155,7 @@ }, "ndarray": { "version": "1.0.18", - "from": "ndarray@>=1.0.16 <2.0.0", + "from": "ndarray@>=1.0.18 <2.0.0", "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.18.tgz" }, "ndarray-extract-contour": { @@ -2233,28 +2213,6 @@ "from": "nextafter@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/nextafter/-/nextafter-1.0.0.tgz" }, - "ng-annotate": { - "version": "1.2.1", - "from": "ng-annotate@latest", - "resolved": "https://registry.npmjs.org/ng-annotate/-/ng-annotate-1.2.1.tgz", - "dependencies": { - "acorn": { - "version": "2.6.4", - "from": "acorn@>=2.6.4 <2.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.6.4.tgz" - }, - "convert-source-map": { - "version": "1.1.3", - "from": "convert-source-map@>=1.1.2 <1.2.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz" - } - } - }, - "ng-annotate-loader": { - "version": "0.2.0", - "from": "ng-annotate-loader@latest", - "resolved": "https://registry.npmjs.org/ng-annotate-loader/-/ng-annotate-loader-0.2.0.tgz" - }, "nomnom": { "version": "1.8.1", "from": "nomnom@>=1.5.0", @@ -2282,11 +2240,6 @@ } } }, - "normalize-path": { - "version": "2.0.1", - "from": "normalize-path@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.0.1.tgz" - }, "normals": { "version": "1.1.0", "from": "normals@>=1.0.1 <2.0.0", @@ -2322,14 +2275,9 @@ "from": "once@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" }, - "optimist": { - "version": "0.6.1", - "from": "optimist@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz" - }, "optionator": { "version": "0.8.2", - "from": "optionator@>=0.8.2 <0.9.0", + "from": "optionator@>=0.8.1 <0.9.0", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", "dependencies": { "wordwrap": { @@ -2344,16 +2292,6 @@ "from": "orbit-camera-controller@>=4.0.0 <5.0.0", "resolved": "https://registry.npmjs.org/orbit-camera-controller/-/orbit-camera-controller-4.0.0.tgz" }, - "ordered-ast-traverse": { - "version": "1.1.1", - "from": "ordered-ast-traverse@>=1.1.1 <1.2.0", - "resolved": "https://registry.npmjs.org/ordered-ast-traverse/-/ordered-ast-traverse-1.1.1.tgz" - }, - "ordered-esprima-props": { - "version": "1.1.0", - "from": "ordered-esprima-props@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/ordered-esprima-props/-/ordered-esprima-props-1.1.0.tgz" - }, "pace-progress": { "version": "1.0.2", "from": "git+https://github.com/getredash/pace.git", @@ -2456,7 +2394,7 @@ }, "punycode": { "version": "1.4.1", - "from": "punycode@>=1.2.4 <2.0.0", + "from": "punycode@>=1.4.1 <2.0.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" }, "quat-slerp": { @@ -2523,7 +2461,7 @@ }, "repeat-string": { "version": "1.6.1", - "from": "repeat-string@>=1.5.2 <2.0.0", + "from": "repeat-string@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" }, "request": { @@ -2545,7 +2483,7 @@ }, "resolve": { "version": "1.1.7", - "from": "resolve@>=1.1.6 <2.0.0", + "from": "resolve@>=1.1.7 <1.2.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz" }, "resolve-protobuf-schema": { @@ -2653,16 +2591,6 @@ "from": "signum@>=0.0.0 <0.0.1", "resolved": "https://registry.npmjs.org/signum/-/signum-0.0.0.tgz" }, - "simple-fmt": { - "version": "0.1.0", - "from": "simple-fmt@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/simple-fmt/-/simple-fmt-0.1.0.tgz" - }, - "simple-is": { - "version": "0.2.0", - "from": "simple-is@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/simple-is/-/simple-is-0.2.0.tgz" - }, "simplicial-complex": { "version": "1.0.0", "from": "simplicial-complex@>=1.0.0 <2.0.0", @@ -2757,11 +2685,6 @@ } } }, - "stable": { - "version": "0.1.5", - "from": "stable@>=0.1.5 <0.2.0", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.5.tgz" - }, "static-eval": { "version": "0.2.4", "from": "static-eval@>=0.2.0 <0.3.0", @@ -2831,16 +2754,6 @@ "from": "string.prototype.trim@>=1.1.2 <1.2.0", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz" }, - "stringmap": { - "version": "0.2.2", - "from": "stringmap@>=0.2.2 <0.3.0", - "resolved": "https://registry.npmjs.org/stringmap/-/stringmap-0.2.2.tgz" - }, - "stringset": { - "version": "0.2.1", - "from": "stringset@>=0.2.1 <0.3.0", - "resolved": "https://registry.npmjs.org/stringset/-/stringset-0.2.1.tgz" - }, "stringstream": { "version": "0.0.5", "from": "stringstream@>=0.0.4 <0.1.0", @@ -2885,7 +2798,7 @@ }, "through": { "version": "2.3.8", - "from": "through@>=2.3.6 <3.0.0", + "from": "through@>=2.3.8 <2.4.0", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" }, "through2": { @@ -2945,11 +2858,6 @@ "from": "triangulate-polyline@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/triangulate-polyline/-/triangulate-polyline-1.0.3.tgz" }, - "tryor": { - "version": "0.1.2", - "from": "tryor@>=0.1.2 <0.2.0", - "resolved": "https://registry.npmjs.org/tryor/-/tryor-0.1.2.tgz" - }, "tunnel-agent": { "version": "0.4.3", "from": "tunnel-agent@>=0.4.1 <0.5.0", @@ -2983,7 +2891,7 @@ }, "typedarray": { "version": "0.0.6", - "from": "typedarray@>=0.0.5 <0.1.0", + "from": "typedarray@>=0.0.6 <0.0.7", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" }, "typedarray-pool": { @@ -3050,12 +2958,12 @@ }, "underscore": { "version": "1.8.3", - "from": "underscore@latest", + "from": "underscore@>=1.8.3 <2.0.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz" }, "underscore.string": { "version": "3.3.4", - "from": "underscore.string@latest", + "from": "underscore.string@>=3.3.4 <4.0.0", "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.4.tgz" }, "union-find": { @@ -3138,11 +3046,6 @@ "from": "window-size@0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz" }, - "wordwrap": { - "version": "0.0.3", - "from": "wordwrap@>=0.0.2 <0.1.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz" - }, "world-calendars": { "version": "1.0.3", "from": "world-calendars@>=1.0.3 <2.0.0", @@ -3155,7 +3058,7 @@ }, "xtend": { "version": "4.0.1", - "from": "xtend@>=4.0.0 <5.0.0", + "from": "xtend@>=4.0.0 <4.1.0-0", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" }, "yargs": { diff --git a/package.json b/package.json index 2eea99e4b7..371f69224b 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "The frontend part of Redash.", "main": "index.js", "scripts": { - "start": "webpack-dev-server --content-base client/app", + "start": "webpack-dev-server", "build": "rm -rf ./client/dist/ && NODE_ENV=production node node_modules/.bin/webpack", "watch": "webpack --watch --progress --colors -d" }, @@ -22,12 +22,12 @@ "angular": "~1.5.8", "angular-base64-upload": "^0.1.19", "angular-gridster": "^0.13.14", - "angular-messages": "^1.5.8", + "angular-messages": "~1.5.8", "angular-moment": "^1.0.0", "angular-resizable": "^1.2.0", - "angular-resource": "^1.5.8", - "angular-route": "^1.5.8", - "angular-sanitize": "^1.5.8", + "angular-resource": "~1.5.8", + "angular-route": "~1.5.8", + "angular-sanitize": "~1.5.8", "angular-toastr": "^2.1.1", "angular-ui-ace": "^0.2.3", "angular-ui-bootstrap": "^2.2.0", @@ -49,8 +49,6 @@ "moment": "^2.15.2", "mousetrap": "^1.6.0", "mustache": "^2.2.1", - "ng-annotate": "^1.2.1", - "ng-annotate-loader": "^0.2.0", "pace-progress": "git+https://github.com/getredash/pace.git", "pivottable": "^2.3.0", "plotly.js": "1.26.1", @@ -64,20 +62,21 @@ "babel-plugin-transform-object-assign": "^6.22.0", "babel-preset-es2015": "^6.18.0", "babel-preset-stage-2": "^6.18.0", - "css-loader": "^0.25.0", + "css-loader": "^0.28.4", "eslint": "^3.9.0", "eslint-config-airbnb-base": "^9.0.0", "eslint-loader": "^1.6.0", "eslint-plugin-import": "^2.0.1", - "extract-text-webpack-plugin": "^1.0.1", - "file-loader": "^0.9.0", + "extract-text-webpack-plugin": "^2.1.2", + "file-loader": "^0.11.2", "html-webpack-plugin": "^2.24.0", - "node-sass": "^4.3.0", + "ng-annotate-loader": "^0.6.1", + "node-sass": "^4.5.3", "raw-loader": "^0.5.1", - "sass-loader": "^4.1.1", - "url-loader": "^0.5.7", - "webpack": "^1.13.3", - "webpack-build-notifier": "^0.1.13", - "webpack-dev-server": "^1.16.2" + "sass-loader": "^6.0.6", + "url-loader": "^0.5.9", + "webpack": "^2.6.1", + "webpack-build-notifier": "^0.1.14", + "webpack-dev-server": "^2.4.5" } } diff --git a/redash/authentication/org_resolving.py b/redash/authentication/org_resolving.py index 52f7309cd3..da5e5f5638 100644 --- a/redash/authentication/org_resolving.py +++ b/redash/authentication/org_resolving.py @@ -1,10 +1,3 @@ -""" -This module implements different strategies to resolve the current Organization we are using. - -By default we use the simple single_org strategy, which assumes you have a -single Organization in your installation. -""" - import logging from flask import g, request diff --git a/redash/cli/__init__.py b/redash/cli/__init__.py index 7fb550eb31..0176bcdf0c 100644 --- a/redash/cli/__init__.py +++ b/redash/cli/__init__.py @@ -68,3 +68,23 @@ def send_test_mail(email=None): mail.send(Message(subject="Test Message from Redash", recipients=[email], body="Test message.")) + +@manager.command() +def ipython(): + """Starts IPython shell instead of the default Python shell.""" + import sys + import IPython + from flask.globals import _app_ctx_stack + app = _app_ctx_stack.top.app + + banner = 'Python %s on %s\nIPython: %s\nRedash version: %s\n' % ( + sys.version, + sys.platform, + IPython.__version__, + __version__ + ) + + ctx = {} + ctx.update(app.make_shell_context()) + + IPython.embed(banner1=banner, user_ns=ctx) diff --git a/redash/handlers/alerts.py b/redash/handlers/alerts.py index 63ba0a1e79..fa9b0b15fe 100644 --- a/redash/handlers/alerts.py +++ b/redash/handlers/alerts.py @@ -4,8 +4,10 @@ from funcy import project from redash import models -from redash.permissions import require_access, require_admin_or_owner, view_only, require_permission -from redash.handlers.base import BaseResource, require_fields, get_object_or_404 +from redash.handlers.base import (BaseResource, get_object_or_404, + require_fields) +from redash.permissions import (require_access, require_admin_or_owner, + require_permission, view_only) class AlertResource(BaseResource): @@ -52,6 +54,7 @@ def post(self): name=req['name'], query_rel=query, user=self.current_user, + rearm=req.get('rearm'), options=req['options'] ) diff --git a/redash/handlers/authentication.py b/redash/handlers/authentication.py index c19b7d175e..912988819d 100644 --- a/redash/handlers/authentication.py +++ b/redash/handlers/authentication.py @@ -2,6 +2,7 @@ import logging from flask import flash, redirect, render_template, request, url_for +from flask import abort, flash, redirect, render_template, request, url_for from flask_login import current_user, login_required, login_user, logout_user from redash import __version__, limiter, models, settings @@ -74,6 +75,9 @@ def reset(token, org_slug=None): @routes.route(org_scoped_rule('/forgot'), methods=['GET', 'POST']) def forgot_password(org_slug=None): + if not settings.PASSWORD_LOGIN_ENABLED: + abort(404) + submitted = False if request.method == 'POST' and request.form['email']: submitted = True @@ -129,7 +133,7 @@ def login(org_slug=None): return render_template("login.html", org_slug=org_slug, next=next_path, - username=request.form.get('username', ''), + email=request.form.get('email', ''), show_google_openid=settings.GOOGLE_OAUTH_ENABLED, google_auth_url=google_auth_url, show_saml_login=settings.SAML_LOGIN_ENABLED, @@ -167,8 +171,6 @@ def client_config(): return client_config - -# @routes.route(org_scoped_rule('/api/config'), methods=['GET']) @routes.route('/api/config', methods=['GET']) def config(org_slug=None): return json_response({ diff --git a/redash/handlers/dashboards.py b/redash/handlers/dashboards.py index 6ce7d6b0f2..f47c59fb2b 100644 --- a/redash/handlers/dashboards.py +++ b/redash/handlers/dashboards.py @@ -2,7 +2,7 @@ import json from flask import request, url_for -from funcy import distinct, project, take +from funcy import distinct, project, take from sqlalchemy.exc import IntegrityError from sqlalchemy.orm.exc import StaleDataError @@ -140,7 +140,7 @@ def post(self, dashboard_slug): abort(400) updates = project(dashboard_properties, ('name', 'layout', 'version', - 'is_draft')) + 'is_draft', 'dashboard_filters_enabled')) # SQLAlchemy handles the case where a concurrent transaction beats us # to the update. But we still have to make sure that we're not starting diff --git a/redash/handlers/data_sources.py b/redash/handlers/data_sources.py index fea3885642..17fb163133 100644 --- a/redash/handlers/data_sources.py +++ b/redash/handlers/data_sources.py @@ -2,6 +2,7 @@ from flask import make_response, request from funcy import project +from sqlalchemy.exc import IntegrityError from flask_restful import abort from redash import models @@ -43,7 +44,14 @@ def post(self, data_source_id): data_source.type = req['type'] data_source.name = req['name'] models.db.session.add(data_source) - models.db.session.commit() + + try: + models.db.session.commit() + except IntegrityError as e: + if req['name'] in e.message: + abort(400, message="Data source with the name {} already exists.".format(req['name'])) + + abort(400) return data_source.to_dict(all=True) @@ -95,12 +103,18 @@ def post(self): if not config.is_valid(): abort(400) - datasource = models.DataSource.create_with_group(org=self.current_org, - name=req['name'], - type=req['type'], - options=config) + try: + datasource = models.DataSource.create_with_group(org=self.current_org, + name=req['name'], + type=req['type'], + options=config) + + models.db.session.commit() + except IntegrityError as e: + if req['name'] in e.message: + abort(400, message="Data source with the name {} already exists.".format(req['name'])) - models.db.session.commit() + abort(400) self.record_event({ 'action': 'create', diff --git a/redash/handlers/destinations.py b/redash/handlers/destinations.py index aac056376e..c1895b7321 100644 --- a/redash/handlers/destinations.py +++ b/redash/handlers/destinations.py @@ -2,10 +2,11 @@ from flask_restful import abort from redash import models +from redash.destinations import (destinations, + get_configuration_schema_for_destination_type) +from redash.handlers.base import BaseResource from redash.permissions import require_admin -from redash.destinations import destinations, get_configuration_schema_for_destination_type from redash.utils.configuration import ConfigurationContainer, ValidationError -from redash.handlers.base import BaseResource class DestinationTypeListResource(BaseResource): @@ -30,6 +31,8 @@ def post(self, destination_id): abort(400) try: + destination.type = req['type'] + destination.name = req['name'] destination.options.set_schema(schema) destination.options.update(req['options']) models.db.session.add(destination) @@ -37,9 +40,6 @@ def post(self, destination_id): except ValidationError: abort(400) - destination.type = req['type'] - destination.name = req['name'] - return destination.to_dict(all=True) @require_admin diff --git a/redash/handlers/queries.py b/redash/handlers/queries.py index 46b18b02d9..48abbfba42 100644 --- a/redash/handlers/queries.py +++ b/redash/handlers/queries.py @@ -60,14 +60,14 @@ def get(self): if settings.FEATURE_DUMB_RECENTS: results = models.Query.by_user(self.current_user).order_by(models.Query.updated_at.desc()).limit(10) - queries = [q.to_dict(with_last_modified_by=False) for q in results] + queries = [q.to_dict(with_last_modified_by=False, with_user=False) for q in results] else: queries = models.Query.recent(self.current_user.group_ids, self.current_user.id) - recent = [d.to_dict(with_last_modified_by=False) for d in queries] + recent = [d.to_dict(with_last_modified_by=False, with_user=False) for d in queries] global_recent = [] if len(recent) < 10: - global_recent = [d.to_dict(with_last_modified_by=False) for d in models.Query.recent(self.current_user.group_ids)] + global_recent = [d.to_dict(with_last_modified_by=False, with_user=False) for d in models.Query.recent(self.current_user.group_ids)] queries = take(20, distinct(chain(recent, global_recent), key=lambda d: d['id'])) diff --git a/redash/handlers/query_results.py b/redash/handlers/query_results.py index 8193d57c71..397a3b03f1 100644 --- a/redash/handlers/query_results.py +++ b/redash/handlers/query_results.py @@ -1,3 +1,4 @@ +import logging import json import time @@ -9,7 +10,7 @@ from redash.tasks import QueryTask, record_event from redash.permissions import require_permission, not_view_only, has_access, require_access, view_only from redash.handlers.base import BaseResource, get_object_or_404 -from redash.utils import collect_query_parameters, collect_parameters_from_request +from redash.utils import collect_query_parameters, collect_parameters_from_request, gen_query_hash from redash.tasks.queries import enqueue_query @@ -17,6 +18,55 @@ def error_response(message): return {'job': {'status': 4, 'error': message}}, 400 +# +# Run a parameterized query synchronously and return the result +# DISCLAIMER: Temporary solution to support parameters in queries. Should be +# removed once we refactor the query results API endpoints and handling +# on the client side. Please don't reuse in other API handlers. +# +def run_query_sync(data_source, parameter_values, query_text, max_age=0): + query_parameters = set(collect_query_parameters(query_text)) + missing_params = set(query_parameters) - set(parameter_values.keys()) + if missing_params: + raise Exception('Missing parameter value for: {}'.format(", ".join(missing_params))) + + if query_parameters: + query_text = pystache.render(query_text, parameter_values) + + if max_age <= 0: + query_result = None + else: + query_result = models.QueryResult.get_latest(data_source, query_text, max_age) + + query_hash = gen_query_hash(query_text) + + if query_result: + logging.info("Returning cached result for query %s" % query_hash) + return query_result + + try: + started_at = time.time() + data, error = data_source.query_runner.run_query(query_text, current_user) + + if error: + logging.info('got bak error') + logging.info(error) + return None + + run_time = time.time() - started_at + query_result, updated_query_ids = models.QueryResult.store_result(data_source.org, data_source, + query_hash, query_text, data, + run_time, utils.utcnow()) + + models.db.session.commit() + return query_result + except Exception, e: + if max_age > 0: + abort(404, message="Unable to get result from the database, and no cached query result found.") + else: + abort(503, message="Unable to get result from the database.") + return None + def run_query(data_source, parameter_values, query_text, query_id, max_age=0): query_parameters = set(collect_query_parameters(query_text)) missing_params = set(query_parameters) - set(parameter_values.keys()) @@ -127,15 +177,22 @@ def get(self, query_id=None, query_result_id=None, filetype='json'): # They need to be split, as they have different logic (for example, retrieving by query id # should check for query parameters and shouldn't cache the result). should_cache = query_result_id is not None - if query_result_id is None and query_id is not None: - query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org) - if query: - query_result_id = query.latest_query_data_id + + parameter_values = collect_parameters_from_request(request.args) + max_age = int(request.args.get('maxAge', 0)) + + query_result = None if query_result_id: query_result = get_object_or_404(models.QueryResult.get_by_id_and_org, query_result_id, self.current_org) - else: - query_result = None + elif query_id is not None: + query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org) + + if query is not None: + if settings.ALLOW_PARAMETERS_IN_EMBEDS and parameter_values: + query_result = run_query_sync(query.data_source, parameter_values, query.to_dict()['query'], max_age=max_age) + elif query.latest_query_data_id is not None: + query_result = get_object_or_404(models.QueryResult.get_by_id_and_org, query.latest_query_data_id, self.current_org) if query_result: require_access(query_result.data_source.groups, self.current_user, view_only) @@ -209,4 +266,3 @@ def delete(self, job_id): """ job = QueryTask(job_id=job_id) job.cancel() - diff --git a/redash/handlers/static.py b/redash/handlers/static.py index 806e6c625a..98dcb5e6b2 100644 --- a/redash/handlers/static.py +++ b/redash/handlers/static.py @@ -27,7 +27,7 @@ def send_static(filename): def render_index(): - if settings.MULTI_ORG == "true": + if settings.MULTI_ORG: response = render_template("multi_org.html", base_href=base_href()) else: full_path = safe_join(settings.STATIC_ASSETS_PATHS[-2], 'index.html') diff --git a/redash/models.py b/redash/models.py index bd8c2eb5b9..775191857b 100644 --- a/redash/models.py +++ b/redash/models.py @@ -8,12 +8,20 @@ import logging import time -from funcy import project - import xlsxwriter from flask_login import AnonymousUserMixin, UserMixin from flask_sqlalchemy import SQLAlchemy +from funcy import project from passlib.apps import custom_app_context as pwd_context +from sqlalchemy import or_ +from sqlalchemy.dialects import postgresql +from sqlalchemy.event import listens_for +from sqlalchemy.ext.mutable import Mutable +from sqlalchemy.inspection import inspect +from sqlalchemy.orm import backref, joinedload, object_session, subqueryload +from sqlalchemy.orm.exc import NoResultFound # noqa: F401 +from sqlalchemy.types import TypeDecorator + from redash import redis_connection, utils from redash.destinations import (get_configuration_schema_for_destination_type, get_destination) @@ -23,14 +31,6 @@ get_query_runner) from redash.utils import generate_token, json_dumps from redash.utils.configuration import ConfigurationContainer -from sqlalchemy import or_ -from sqlalchemy.dialects import postgresql -from sqlalchemy.event import listens_for -from sqlalchemy.ext.mutable import Mutable -from sqlalchemy.inspection import inspect -from sqlalchemy.orm import backref, joinedload, object_session, subqueryload -from sqlalchemy.orm.exc import NoResultFound # noqa: F401 -from sqlalchemy.types import TypeDecorator db = SQLAlchemy(session_options={ 'expire_on_commit': False @@ -943,7 +943,7 @@ def search(cls, term, group_ids, include_drafts=False): @classmethod def recent(cls, group_ids, user_id=None, limit=20): - query = (cls.query.options(subqueryload(Query.user)) + query = (cls.query .filter(Event.created_at > (db.func.current_date() - 7)) .join(Event, Query.id == Event.object_id.cast(db.Integer)) .join(DataSourceGroup, Query.data_source_id == DataSourceGroup.data_source_id) @@ -1184,18 +1184,20 @@ def to_dict(self, full=True): def evaluate(self): data = json.loads(self.query_rel.latest_query_data.data) - # todo: safe guard for empty - value = data['rows'][0][self.options['column']] - op = self.options['op'] - - if op == 'greater than' and value > self.options['value']: - new_state = self.TRIGGERED_STATE - elif op == 'less than' and value < self.options['value']: - new_state = self.TRIGGERED_STATE - elif op == 'equals' and value == self.options['value']: - new_state = self.TRIGGERED_STATE + if data['rows']: + value = data['rows'][0][self.options['column']] + op = self.options['op'] + + if op == 'greater than' and value > self.options['value']: + new_state = self.TRIGGERED_STATE + elif op == 'less than' and value < self.options['value']: + new_state = self.TRIGGERED_STATE + elif op == 'equals' and value == self.options['value']: + new_state = self.TRIGGERED_STATE + else: + new_state = self.OK_STATE else: - new_state = self.OK_STATE + new_state = self.UNKNOWN_STATE return new_state @@ -1515,8 +1517,9 @@ class NotificationDestination(BelongsToOrgMixin, db.Model): user = db.relationship(User, backref="notification_destinations") name = Column(db.String(255)) type = Column(db.String(255)) - options = Column(Configuration) + options = Column(ConfigurationContainer.as_mutable(Configuration)) created_at = Column(db.DateTime(True), default=db.func.now()) + __tablename__ = 'notification_destinations' __table_args__ = (db.Index('notification_destinations_org_id_name', 'org_id', 'name', unique=True),) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index 2612338588..154c1d54ba 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -1,22 +1,45 @@ import json +import logging import os import re -import requests - -try: - import botocore.session - from botocore.exceptions import WaiterError - direct_enabled = True -except ImportError: - direct_enabled = False - -from redash.query_runner import BaseQueryRunner, register -from redash.utils import JSONEncoder +from redash.query_runner import * from redash.settings import parse_boolean +from redash.utils import JSONEncoder -PROXY_URL = os.environ.get('ATHENA_PROXY_URL') +logger = logging.getLogger(__name__) ANNOTATE_QUERY = parse_boolean(os.environ.get('ATHENA_ANNOTATE_QUERY', 'true')) +SHOW_EXTRA_SETTINGS = parse_boolean(os.environ.get('ATHENA_SHOW_EXTRA_SETTINGS', 'true')) +OPTIONAL_CREDENTIALS = parse_boolean(os.environ.get('ATHENA_OPTIONAL_CREDENTIALS', 'true')) + +try: + import pyathena + enabled = True +except ImportError: + enabled = False + + +_TYPE_MAPPINGS = { + 'boolean': TYPE_BOOLEAN, + 'tinyint': TYPE_INTEGER, + 'smallint': TYPE_INTEGER, + 'integer': TYPE_INTEGER, + 'bigint': TYPE_INTEGER, + 'double': TYPE_FLOAT, + 'varchar': TYPE_STRING, + 'timestamp': TYPE_DATETIME, + 'date': TYPE_DATE, + 'varbinary': TYPE_STRING, + 'array': TYPE_STRING, + 'map': TYPE_STRING, + 'row': TYPE_STRING, + 'decimal': TYPE_FLOAT, +} + + +class SimpleFormatter(object): + def format(self, operation, parameters=None): + return operation class Athena(BaseQueryRunner): noop_query = 'SELECT 1' @@ -27,7 +50,7 @@ def name(cls): @classmethod def configuration_schema(cls): - return { + schema = { 'type': 'object', 'properties': { 'region': { @@ -45,69 +68,104 @@ def configuration_schema(cls): 's3_staging_dir': { 'type': 'string', 'title': 'S3 Staging Path' - } + }, + 'schema': { + 'type': 'string', + 'title': 'Schema Name', + 'default': 'default' + }, }, - 'required': ['region', 'aws_access_key', 'aws_secret_key', 's3_staging_dir'], + 'required': ['region', 's3_staging_dir'], + 'order': ['region', 'aws_access_key', 'aws_secret_key', 's3_staging_dir', 'schema'], 'secret': ['aws_secret_key'] } + if SHOW_EXTRA_SETTINGS: + schema['properties'].update({ + 'encryption_option': { + 'type': 'string', + 'title': 'Encryption Option', + }, + 'kms_key': { + 'type': 'string', + 'title': 'KMS Key', + }, + }) + + if not OPTIONAL_CREDENTIALS: + schema['required'] += ['aws_access_key', 'aws_secret_key'] + + return schema + + @classmethod + def enabled(cls): + return enabled + @classmethod def annotate_query(cls): return ANNOTATE_QUERY + @classmethod + def type(cls): + return "athena" + + def __init__(self, configuration): + super(Athena, self).__init__(configuration) + def get_schema(self, get_stats=False): schema = {} query = """ SELECT table_schema, table_name, column_name FROM information_schema.columns - WHERE table_schema NOT IN ('pg_catalog', 'information_schema') + WHERE table_schema NOT IN ('information_schema') """ results, error = self.run_query(query, None) - if error is not None: raise Exception("Failed getting schema.") results = json.loads(results) - for row in results['rows']: - table_name = '{}.{}'.format(row['table_schema'], row['table_name']) - + table_name = '{0}.{1}'.format(row['table_schema'], row['table_name']) if table_name not in schema: schema[table_name] = {'name': table_name, 'columns': []} - schema[table_name]['columns'].append(row['column_name']) return schema.values() def run_query(self, query, user): + cursor = pyathena.connect( + s3_staging_dir=self.configuration['s3_staging_dir'], + region_name=self.configuration['region'], + aws_access_key_id=self.configuration.get('aws_access_key', None), + aws_secret_access_key=self.configuration.get('aws_secret_key', None), + schema_name=self.configuration.get('schema', 'default'), + encryption_option=self.configuration.get('encryption_option', None), + kms_key=self.configuration.get('kms_key', None), + formatter=SimpleFormatter()).cursor() + try: - data = { - 'athenaUrl': 'jdbc:awsathena://athena.{}.amazonaws.com:443/'.format(self.configuration['region'].lower()), - 'awsAccessKey': self.configuration['aws_access_key'], - 'awsSecretKey': self.configuration['aws_secret_key'], - 's3StagingDir': self.configuration['s3_staging_dir'], - 'query': query - } - - response = requests.post(PROXY_URL, json=data) - response.raise_for_status() - - json_data = response.content.strip() + cursor.execute(query) + column_tuples = [(i[0], _TYPE_MAPPINGS.get(i[1], None)) for i in cursor.description] + columns = self.fetch_columns(column_tuples) + rows = [dict(zip(([c['name'] for c in columns]), r)) for i, r in enumerate(cursor.fetchall())] + data = {'columns': columns, 'rows': rows} + json_data = json.dumps(data, cls=JSONEncoder) error = None - - return json_data, error - except requests.RequestException as e: - if e.response.status_code == 400: - return None, response.content - - return None, str(e) except KeyboardInterrupt: + if cursor.query_id: + cursor.cancel() error = "Query cancelled by user." json_data = None + except Exception, ex: + if cursor.query_id: + cursor.cancel() + error = ex.message + json_data = None return json_data, error + register(Athena) class AthenaDirect(BaseQueryRunner): @@ -119,7 +177,7 @@ def name(cls): @classmethod def enabled(cls): - return direct_enabled + return enabled @classmethod def configuration_schema(cls): diff --git a/redash/query_runner/memsql_ds.py b/redash/query_runner/memsql_ds.py index 12b78d7645..62728dda42 100644 --- a/redash/query_runner/memsql_ds.py +++ b/redash/query_runner/memsql_ds.py @@ -93,8 +93,7 @@ def _get_tables(self, schema): table_name = '.'.join((schema_name, table_name)) columns = filter(lambda a: len(a) > 0, map(lambda a: str(a['Field']), self._run_query_internal(columns_query % table_name))) - - schema[table_name] = {'name': table_name, 'columns': columns} + schema[table_name] = {'name': table_name, 'columns': columns} return schema.values() def run_query(self, query, user): diff --git a/redash/query_runner/salesforce.py b/redash/query_runner/salesforce.py index 6d9678b067..0cfc1d8403 100644 --- a/redash/query_runner/salesforce.py +++ b/redash/query_runner/salesforce.py @@ -92,7 +92,7 @@ def _get_sf(self): sf = SimpleSalesforce(username=self.configuration['username'], password=self.configuration['password'], security_token=self.configuration['token'], - sandbox=self.configuration['sandbox'], + sandbox=self.configuration.get('sandbox', False), client_id='Redash') return sf diff --git a/redash/settings.py b/redash/settings.py index 7c3c677198..e995540e4b 100644 --- a/redash/settings.py +++ b/redash/settings.py @@ -185,6 +185,7 @@ def all_settings(): 'redash.query_runner.sqlite', 'redash.query_runner.dynamodb_sql', 'redash.query_runner.mssql', + 'redash.query_runner.memsql_ds', 'redash.query_runner.jql', 'redash.query_runner.google_analytics', 'redash.query_runner.snowflake', @@ -228,6 +229,7 @@ def all_settings(): FEATURE_SHOW_PERMISSIONS_CONTROL = parse_boolean(os.environ.get("REDASH_FEATURE_SHOW_PERMISSIONS_CONTROL", "false")) FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS = parse_boolean(os.environ.get("REDASH_FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS", "false")) FEATURE_DUMB_RECENTS = parse_boolean(os.environ.get("REDASH_FEATURE_DUMB_RECENTS", "false")) +FEATURE_AUTO_PUBLISH_NAMED_QUERIES = parse_boolean(os.environ.get("REDASH_FEATURE_AUTO_PUBLISH_NAMED_QUERIES", "true")) # BigQuery BIGQUERY_HTTP_TIMEOUT = int(os.environ.get("REDASH_BIGQUERY_HTTP_TIMEOUT", "600")) @@ -244,6 +246,7 @@ def all_settings(): 'allowScriptsInUserInput': ALLOW_SCRIPTS_IN_USER_INPUT, 'showPermissionsControl': FEATURE_SHOW_PERMISSIONS_CONTROL, 'allowCustomJSVisualizations': FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS, + 'autoPublishNamedQueries': FEATURE_AUTO_PUBLISH_NAMED_QUERIES, 'dateFormat': DATE_FORMAT, 'dateTimeFormat': "{0} HH:mm".format(DATE_FORMAT), 'allowAllToEditQueries': FEATURE_ALLOW_ALL_TO_EDIT_QUERIES, diff --git a/redash/tasks/queries.py b/redash/tasks/queries.py index 77a8f319e0..ab8279e365 100644 --- a/redash/tasks/queries.py +++ b/redash/tasks/queries.py @@ -278,6 +278,7 @@ def refresh_queries(): query_text = pystache.render(query.query_text, query_params) else: query_text = query.query_text + enqueue_query(query_text, query.data_source, query.user_id, scheduled_query=query, metadata={'Query ID': query.id, 'Username': 'Scheduled'}) diff --git a/redash/utils/__init__.py b/redash/utils/__init__.py index 60bc0199ec..a9cc954dc7 100644 --- a/redash/utils/__init__.py +++ b/redash/utils/__init__.py @@ -9,6 +9,7 @@ import hashlib import pytz import pystache +import os from funcy import distinct, select_values from sqlalchemy.orm.query import Query @@ -17,6 +18,7 @@ from redash import settings COMMENTS_REGEX = re.compile("/\*.*?\*/") +WRITER_ENCODING = os.environ.get('REDASH_CSV_WRITER_ENCODING', 'utf-8') def utcnow(): @@ -102,7 +104,7 @@ class UnicodeWriter: which is encoded in the given encoding. """ - def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds): + def __init__(self, f, dialect=csv.excel, encoding=WRITER_ENCODING, **kwds): # Redirect output to a queue self.queue = cStringIO.StringIO() self.writer = csv.writer(self.queue, dialect=dialect, **kwds) @@ -111,7 +113,7 @@ def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds): def _encode_utf8(self, val): if isinstance(val, (unicode, str)): - return val.encode('utf-8') + return val.encode(WRITER_ENCODING) return val @@ -119,7 +121,7 @@ def writerow(self, row): self.writer.writerow([self._encode_utf8(s) for s in row]) # Fetch UTF-8 output from the queue ... data = self.queue.getvalue() - data = data.decode("utf-8") + data = data.decode(WRITER_ENCODING) # ... and reencode it into the target encoding data = self.encoder.encode(data) # write to the target stream diff --git a/requirements_all_ds.txt b/requirements_all_ds.txt index 10fa5316fa..382d0a91e1 100644 --- a/requirements_all_ds.txt +++ b/requirements_all_ds.txt @@ -12,7 +12,7 @@ td-client==0.8.0 pymssql==2.1.3 dql==0.5.16 dynamo3==0.4.7 -botocore==1.5.21 +botocore==1.5.72 sasl>=0.1.3 thrift>=0.8.0 thrift_sasl>=0.1.0 @@ -21,5 +21,6 @@ memsql==2.16.0 snowflake_connector_python==1.3.16 atsd_client==2.0.12 simple_salesforce==0.72.2 +PyAthena>=1.0.0 # certifi is needed to support MongoDB and SSL: certifi diff --git a/tests/factories.py b/tests/factories.py index 216f868ec0..04b8f2f271 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -1,8 +1,8 @@ import redash.models from redash.models import db +from redash.permissions import ACCESS_TYPE_MODIFY from redash.utils import gen_query_hash, utcnow from redash.utils.configuration import ConfigurationContainer -from redash.permissions import ACCESS_TYPE_MODIFY class ModelFactory(object): @@ -167,11 +167,6 @@ def data_source(self): return self._data_source - def _init_org(self): - if self._org is None: - self._org, self._admin_group, self._default_group = redash.models.init_db() - self.org.domain = 'org0.example.org' - def create_org(self, **kwargs): org = org_factory.create(**kwargs) self.create_group(org=org, type=redash.models.Group.BUILTIN_GROUP, name="default") diff --git a/tests/handlers/test_alerts.py b/tests/handlers/test_alerts.py index 9d7c185f3b..638595cb0d 100644 --- a/tests/handlers/test_alerts.py +++ b/tests/handlers/test_alerts.py @@ -1,5 +1,6 @@ from tests import BaseTestCase -from redash.models import AlertSubscription, Alert, db + +from redash.models import Alert, AlertSubscription, db class TestAlertResourceGet(BaseTestCase): @@ -92,8 +93,10 @@ def test_returns_200_if_has_access_to_query(self): destination = self.factory.create_destination() db.session.commit() rv = self.make_request('post', "/api/alerts", data=dict(name='Alert', query_id=query.id, - destination_id=destination.id, options={})) + destination_id=destination.id, options={}, + rearm=100)) self.assertEqual(rv.status_code, 200) + self.assertEqual(rv.json['rearm'], 100) def test_fails_if_doesnt_have_access_to_query(self): data_source = self.factory.create_data_source(group=self.factory.create_group()) diff --git a/tests/handlers/test_destinations.py b/tests/handlers/test_destinations.py index c48bbfa507..2e1533b7cb 100644 --- a/tests/handlers/test_destinations.py +++ b/tests/handlers/test_destinations.py @@ -1,4 +1,5 @@ from tests import BaseTestCase + from redash.models import NotificationDestination @@ -55,10 +56,14 @@ def test_post(self): data = { 'name': 'updated', 'type': d.type, - 'options': d.options.to_dict() + 'options': {"url": "https://www.slack.com/updated"} } - rv = self.make_request('post', '/api/destinations/{}'.format(d.id), user=self.factory.create_admin(), data=data) - self.assertEqual(rv.status_code, 200) - self.assertEqual(NotificationDestination.query.get(d.id).name, data['name']) + with self.app.app_context(): + rv = self.make_request('post', '/api/destinations/{}'.format(d.id), user=self.factory.create_admin(), data=data) + + self.assertEqual(rv.status_code, 200) + d = NotificationDestination.query.get(d.id) + self.assertEqual(d.name, data['name']) + self.assertEqual(d.options['url'], data['options']['url']) diff --git a/webpack.config.js b/webpack.config.js index 0760ad1132..420dc43188 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -13,8 +13,7 @@ var config = { app: './client/app/index.js' }, output: { - // path: process.env.NODE_ENV === 'production' ? './dist' : './dev', - path: './client/dist', + path: path.join(__dirname, 'client', 'dist'), filename: '[name].js', publicPath: '/' }, @@ -24,7 +23,6 @@ var config = { new webpack.DefinePlugin({ ON_TEST: process.env.NODE_ENV === 'test' }), - new webpack.optimize.DedupePlugin(), new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: function (module, count) { @@ -51,42 +49,74 @@ var config = { template: './client/app/multi_org.html', filename: 'multi_org.html' }), - new ExtractTextPlugin('styles.[chunkhash].css') + new ExtractTextPlugin({ + filename: 'styles.[chunkhash].css' + }) ], module: { - loaders: [ - {test: /\.js$/, loader: 'ng-annotate!babel!eslint', exclude: /node_modules/}, - {test: /\.html$/, loader: 'raw', exclude: [/node_modules/, /index\.html/]}, - // {test: /\.css$/, loader: 'style!css', exclude: /node_modules/}, - {test: /\.css$/, loader: ExtractTextPlugin.extract("css-loader")}, + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + use: ['ng-annotate-loader', 'babel-loader', 'eslint-loader'] + }, + { + test: /\.html$/, + exclude: [/node_modules/, /index\.html/], + use: [{ + loader: 'raw-loader' + }] + }, + { + test: /\.css$/, + use: ExtractTextPlugin.extract([{ + loader: 'css-loader', + options: { + minimize: process.env.NODE_ENV === 'production' + } + }]) + }, { test: /\.scss$/, - loader: ExtractTextPlugin.extract(["css-loader", "sass-loader"]) + use: ExtractTextPlugin.extract([ + { + loader: 'css-loader', + options: { + minimize: process.env.NODE_ENV === 'production' + } + }, { + loader: 'sass-loader' + } + ]) }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, - loader: 'url', - query: { - limit: 10000, - name: 'img/[name].[hash:7].[ext]' - } + use: [{ + loader: 'url-loader', + options: { + limit: 10000, + name: 'img/[name].[hash:7].[ext]' + } + }] }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, - loader: 'url', - query: { - limit: 10000, - name: 'fonts/[name].[hash:7].[ext]' - } + use: [{ + loader: 'url-loader', + options: { + limit: 10000, + name: 'fonts/[name].[hash:7].[ext]' + } + }] } - ] }, devtool: 'cheap-eval-module-source-map', devServer: { inline: true, historyApiFallback: true, + contentBase: path.join(__dirname, 'client', 'app'), proxy: { '/login': { target: redashBackend + '/', @@ -135,7 +165,12 @@ if (process.env.DEV_SERVER_HOST) { if (process.env.NODE_ENV === 'production') { config.output.path = __dirname + '/client/dist'; config.output.filename = '[name].[chunkhash].js'; - config.plugins.push(new webpack.optimize.UglifyJsPlugin()); + config.plugins.push(new webpack.optimize.UglifyJsPlugin({ + sourceMap: true, + compress: { + warnings: true + } + })); config.devtool = 'source-map'; } From 6e1338495c40293292382632395904eebfd713b9 Mon Sep 17 00:00:00 2001 From: Alison Date: Wed, 12 Jul 2017 15:48:36 -0500 Subject: [PATCH 191/243] an attempt Original PR was based on boto3, which is python 3 but this is a python 2.7 project. Based on this: https://github.com/laughingman7743/PyAthena/blob/master/pyathena/cursor. py this should work in theory. --- redash/query_runner/athena.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index 154c1d54ba..bf2e13c497 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -149,7 +149,12 @@ def run_query(self, query, user): column_tuples = [(i[0], _TYPE_MAPPINGS.get(i[1], None)) for i in cursor.description] columns = self.fetch_columns(column_tuples) rows = [dict(zip(([c['name'] for c in columns]), r)) for i, r in enumerate(cursor.fetchall())] - data = {'columns': columns, 'rows': rows} + qbytes = None + try: + qbytes = cursor.data_scanned_in_bytes() + except AttributeError as e: + debug("Athena Direct can't get data_scanned_in_bytes: %s", e) + data = { 'columns': columns, 'rows': rows, 'data_scanned': [{ qbytes }] } json_data = json.dumps(data, cls=JSONEncoder) error = None except KeyboardInterrupt: @@ -269,10 +274,6 @@ def run_query(self, query, user): rows.extend(result['ResultSet']['ResultRows']) cnames = [c['Name'] for c in column_info] - #get data scanned in query - query_execution = client.get_query_execution(response['QueryExecutionId']) - qbytes = query_execution['QueryExecutionDetail']['Stats']['ProcessedBytes'] - data = {'columns': [{ 'name': name, @@ -282,9 +283,7 @@ def run_query(self, query, user): 'rows': [{ name: row['Data'][i] for (i, name) in enumerate(cnames) - } for row in rows[1:]], - 'data_scanned': - [{ qbytes }] + } for row in rows[1:]] } return json.dumps(data, cls=JSONEncoder), None From 0ffbe32c8a4771831a57304421ff2d70808ee3dd Mon Sep 17 00:00:00 2001 From: Alison Date: Wed, 12 Jul 2017 17:53:09 -0500 Subject: [PATCH 192/243] update syntax for results return --- redash/query_runner/athena.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index bf2e13c497..33f85a206f 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -154,7 +154,7 @@ def run_query(self, query, user): qbytes = cursor.data_scanned_in_bytes() except AttributeError as e: debug("Athena Direct can't get data_scanned_in_bytes: %s", e) - data = { 'columns': columns, 'rows': rows, 'data_scanned': [{ qbytes }] } + data = { 'columns': columns, 'rows': rows, 'data_scanned': qbytes } json_data = json.dumps(data, cls=JSONEncoder) error = None except KeyboardInterrupt: From 268bd970cfc7361ffb6f3cdbaea4ac96fde97844 Mon Sep 17 00:00:00 2001 From: Alison Date: Thu, 13 Jul 2017 09:42:27 -0500 Subject: [PATCH 193/243] add imports for athenadirect --- redash/query_runner/athena.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index 33f85a206f..e4282e084d 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -14,6 +14,10 @@ try: import pyathena + + #for AthenaDirect + import botocore.session + from botocore.exceptions import WaiterError enabled = True except ImportError: enabled = False From 039632ba0f1c52a854696b08ffcad52c5e11cc75 Mon Sep 17 00:00:00 2001 From: Alison Date: Thu, 13 Jul 2017 19:18:43 -0500 Subject: [PATCH 194/243] updates from PR review --- redash/models.py | 6 ++++-- redash/query_runner/activedata.py | 27 +++++++++------------------ 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/redash/models.py b/redash/models.py index df0af84163..9e6b48e050 100644 --- a/redash/models.py +++ b/redash/models.py @@ -494,7 +494,10 @@ def to_dict(self, all=False, with_permissions_for=None): DataSourceGroup.data_source == self).one()[0] doc_url = self.options.get('doc_url') - if doc_url: + try: + if doc_url: + d['options'].update(doc_url=doc_url) + except: d['options'] = {'doc_url': doc_url} return d @@ -569,7 +572,6 @@ def resume(self): def add_group(self, group, view_only=False): dsg = DataSourceGroup(group=group, data_source=self, view_only=view_only) db.session.add(dsg) - #setattr(self, 'data_source_groups', dsg) return dsg def remove_group(self, group): diff --git a/redash/query_runner/activedata.py b/redash/query_runner/activedata.py index 3e9fa21c8f..73a10fe97a 100644 --- a/redash/query_runner/activedata.py +++ b/redash/query_runner/activedata.py @@ -1,22 +1,16 @@ import json import logging -from urlparse import urlparse import requests -# from mo_dots import wrap, listwrap -# from mo_logs import Except -# from mo_logs.exceptions import ERROR from redash.query_runner import TYPE_INTEGER, TYPE_STRING, TYPE_FLOAT, BaseSQLQueryRunner, register from redash.utils import JSONEncoder -#originally from: https://github.com/klahnakoski/ActiveData-redash-query-runner/blob/c0e7286c09c6f1eb6746a6c7cca581bea79f4757/active_data.py +#Originally written by Github user @klahnakoski +#Original link: https://github.com/klahnakoski/ActiveData-redash-query-runner/blob/c0e7286c09c6f1eb6746a6c7cca581bea79f4757/active_data.py logger = logging.getLogger(__name__) -if not unicode: - unicode = str - types_map = { bool: TYPE_INTEGER, str: TYPE_STRING, @@ -32,9 +26,6 @@ class ActiveData(BaseSQLQueryRunner): noop_query = "SELECT 1" - def __init__(self, configuration): - super(ActiveData, self).__init__(configuration) - @classmethod def configuration_schema(cls): return { @@ -44,7 +35,7 @@ def configuration_schema(cls): "type": "string", "title": "Host URL", "default": "https://activedata.allizom.org:80", - "info": "Do not end with a trailing slash." + "info": "Please include a port. Do not end with a trailing slash." }, "doc_url": { "type": "string", @@ -103,9 +94,9 @@ def run_jx_query(self, query, user): def run_query(self, annotated_query, user): request = {} comment, request["sql"] = annotated_query.split("*/", 2) - meta = request['meta'] ={} + meta = request['meta'] = {} for kv in comment.strip()[2:].split(","): - k, v = map(unicode.strip, kv.split(":")) + k, v = [s.strip() for s in kv.split(':')] meta[k] = v logger.debug("Send ActiveData a SQL query: %s", request['sql']) @@ -143,12 +134,12 @@ def get_unique_name(name, type): new_row = {} for i, cname in enumerate(table['header']): val = r[i] - if val == None: + if val is None: continue type_ = val.__class__ - if type_ in [dict, list]: + if isinstance(val, (dict, list)): val = json.dumps(val, cls=JSONEncoder) - col = get_unique_name(cname, types_map[type_]) + col = get_unique_name(cname, types_map.get(type(val), TYPE_STRING)) new_row[col] = val output.append(new_row) @@ -177,4 +168,4 @@ def find_cause(e): e = c return e.get('template') -register(ActiveData) \ No newline at end of file +register(ActiveData) From c34b9c04c06a6756149ded0f4b91822485f76609 Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 14 Jul 2017 13:59:57 -0500 Subject: [PATCH 195/243] known working Athena from https://github.com/mozilla/redash/blob/aa6329aa1885191f209de4819080217cd de3287f/redash/query_runner/athena.py --- redash/query_runner/athena.py | 155 ++++++++++------------------------ 1 file changed, 43 insertions(+), 112 deletions(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index e4282e084d..ad7db1d49e 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -1,49 +1,22 @@ import json -import logging import os import re -from redash.query_runner import * -from redash.settings import parse_boolean -from redash.utils import JSONEncoder - -logger = logging.getLogger(__name__) -ANNOTATE_QUERY = parse_boolean(os.environ.get('ATHENA_ANNOTATE_QUERY', 'true')) -SHOW_EXTRA_SETTINGS = parse_boolean(os.environ.get('ATHENA_SHOW_EXTRA_SETTINGS', 'true')) -OPTIONAL_CREDENTIALS = parse_boolean(os.environ.get('ATHENA_OPTIONAL_CREDENTIALS', 'true')) +import requests try: - import pyathena - - #for AthenaDirect - import botocore.session + import botocore.session from botocore.exceptions import WaiterError - enabled = True + direct_enabled = True except ImportError: - enabled = False - - -_TYPE_MAPPINGS = { - 'boolean': TYPE_BOOLEAN, - 'tinyint': TYPE_INTEGER, - 'smallint': TYPE_INTEGER, - 'integer': TYPE_INTEGER, - 'bigint': TYPE_INTEGER, - 'double': TYPE_FLOAT, - 'varchar': TYPE_STRING, - 'timestamp': TYPE_DATETIME, - 'date': TYPE_DATE, - 'varbinary': TYPE_STRING, - 'array': TYPE_STRING, - 'map': TYPE_STRING, - 'row': TYPE_STRING, - 'decimal': TYPE_FLOAT, -} - - -class SimpleFormatter(object): - def format(self, operation, parameters=None): - return operation + direct_enabled = False + +from redash.query_runner import BaseQueryRunner, register +from redash.utils import JSONEncoder +from redash.settings import parse_boolean + +PROXY_URL = os.environ.get('ATHENA_PROXY_URL') +ANNOTATE_QUERY = parse_boolean(os.environ.get('ATHENA_ANNOTATE_QUERY', 'true')) class Athena(BaseQueryRunner): noop_query = 'SELECT 1' @@ -54,7 +27,7 @@ def name(cls): @classmethod def configuration_schema(cls): - schema = { + return { 'type': 'object', 'properties': { 'region': { @@ -72,109 +45,69 @@ def configuration_schema(cls): 's3_staging_dir': { 'type': 'string', 'title': 'S3 Staging Path' - }, - 'schema': { - 'type': 'string', - 'title': 'Schema Name', - 'default': 'default' - }, + } }, - 'required': ['region', 's3_staging_dir'], - 'order': ['region', 'aws_access_key', 'aws_secret_key', 's3_staging_dir', 'schema'], + 'required': ['region', 'aws_access_key', 'aws_secret_key', 's3_staging_dir'], 'secret': ['aws_secret_key'] } - if SHOW_EXTRA_SETTINGS: - schema['properties'].update({ - 'encryption_option': { - 'type': 'string', - 'title': 'Encryption Option', - }, - 'kms_key': { - 'type': 'string', - 'title': 'KMS Key', - }, - }) - - if not OPTIONAL_CREDENTIALS: - schema['required'] += ['aws_access_key', 'aws_secret_key'] - - return schema - - @classmethod - def enabled(cls): - return enabled - @classmethod def annotate_query(cls): return ANNOTATE_QUERY - @classmethod - def type(cls): - return "athena" - - def __init__(self, configuration): - super(Athena, self).__init__(configuration) - def get_schema(self, get_stats=False): schema = {} query = """ SELECT table_schema, table_name, column_name FROM information_schema.columns - WHERE table_schema NOT IN ('information_schema') + WHERE table_schema NOT IN ('pg_catalog', 'information_schema') """ results, error = self.run_query(query, None) + if error is not None: raise Exception("Failed getting schema.") results = json.loads(results) + for row in results['rows']: - table_name = '{0}.{1}'.format(row['table_schema'], row['table_name']) + table_name = '{}.{}'.format(row['table_schema'], row['table_name']) + if table_name not in schema: schema[table_name] = {'name': table_name, 'columns': []} + schema[table_name]['columns'].append(row['column_name']) return schema.values() def run_query(self, query, user): - cursor = pyathena.connect( - s3_staging_dir=self.configuration['s3_staging_dir'], - region_name=self.configuration['region'], - aws_access_key_id=self.configuration.get('aws_access_key', None), - aws_secret_access_key=self.configuration.get('aws_secret_key', None), - schema_name=self.configuration.get('schema', 'default'), - encryption_option=self.configuration.get('encryption_option', None), - kms_key=self.configuration.get('kms_key', None), - formatter=SimpleFormatter()).cursor() - try: - cursor.execute(query) - column_tuples = [(i[0], _TYPE_MAPPINGS.get(i[1], None)) for i in cursor.description] - columns = self.fetch_columns(column_tuples) - rows = [dict(zip(([c['name'] for c in columns]), r)) for i, r in enumerate(cursor.fetchall())] - qbytes = None - try: - qbytes = cursor.data_scanned_in_bytes() - except AttributeError as e: - debug("Athena Direct can't get data_scanned_in_bytes: %s", e) - data = { 'columns': columns, 'rows': rows, 'data_scanned': qbytes } - json_data = json.dumps(data, cls=JSONEncoder) + data = { + 'athenaUrl': 'jdbc:awsathena://athena.{}.amazonaws.com:443/'.format(self.configuration['region'].lower()), + 'awsAccessKey': self.configuration['aws_access_key'], + 'awsSecretKey': self.configuration['aws_secret_key'], + 's3StagingDir': self.configuration['s3_staging_dir'], + 'query': query + } + + response = requests.post(PROXY_URL, json=data) + response.raise_for_status() + + json_data = response.content.strip() error = None + + return json_data, error + except requests.RequestException as e: + if e.response.status_code == 400: + return None, response.content + + return None, str(e) except KeyboardInterrupt: - if cursor.query_id: - cursor.cancel() error = "Query cancelled by user." json_data = None - except Exception, ex: - if cursor.query_id: - cursor.cancel() - error = ex.message - json_data = None return json_data, error - register(Athena) class AthenaDirect(BaseQueryRunner): @@ -186,7 +119,7 @@ def name(cls): @classmethod def enabled(cls): - return enabled + return direct_enabled @classmethod def configuration_schema(cls): @@ -277,7 +210,6 @@ def run_query(self, query, user): assert len(column_info_set) == 1, "Don't know what to do with inconsistent column info" rows.extend(result['ResultSet']['ResultRows']) cnames = [c['Name'] for c in column_info] - data = {'columns': [{ 'name': name, @@ -285,11 +217,10 @@ def run_query(self, query, user): 'type': 'string', # XXX map athena types to redash types } for name in cnames], 'rows': - [{ - name: row['Data'][i] for (i, name) in enumerate(cnames) - } for row in rows[1:]] + [{name: row['Data'][i] for (i, name) in enumerate(cnames)} + for row in rows[1:]] } return json.dumps(data, cls=JSONEncoder), None -register(AthenaDirect) +register(AthenaDirect) \ No newline at end of file From 82dc47d185a4b5838588e16c70abac6b6ae79647 Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 14 Jul 2017 14:05:53 -0500 Subject: [PATCH 196/243] adding AthenaUpstream query_runner --- redash/query_runner/athena.py | 159 ++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index ad7db1d49e..d8f266028c 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -1,22 +1,181 @@ import json +import logging import os import re import requests try: + import pyathena + + #for AthenaDirect import botocore.session from botocore.exceptions import WaiterError direct_enabled = True + enabled = True except ImportError: direct_enabled = False + enabled = False from redash.query_runner import BaseQueryRunner, register from redash.utils import JSONEncoder from redash.settings import parse_boolean +logger = logging.getLogger(__name__) PROXY_URL = os.environ.get('ATHENA_PROXY_URL') ANNOTATE_QUERY = parse_boolean(os.environ.get('ATHENA_ANNOTATE_QUERY', 'true')) +SHOW_EXTRA_SETTINGS = parse_boolean(os.environ.get('ATHENA_SHOW_EXTRA_SETTINGS', 'true')) +OPTIONAL_CREDENTIALS = parse_boolean(os.environ.get('ATHENA_OPTIONAL_CREDENTIALS', 'true')) + +_TYPE_MAPPINGS = { + 'boolean': TYPE_BOOLEAN, + 'tinyint': TYPE_INTEGER, + 'smallint': TYPE_INTEGER, + 'integer': TYPE_INTEGER, + 'bigint': TYPE_INTEGER, + 'double': TYPE_FLOAT, + 'varchar': TYPE_STRING, + 'timestamp': TYPE_DATETIME, + 'date': TYPE_DATE, + 'varbinary': TYPE_STRING, + 'array': TYPE_STRING, + 'map': TYPE_STRING, + 'row': TYPE_STRING, + 'decimal': TYPE_FLOAT, +} + + +class SimpleFormatter(object): + def format(self, operation, parameters=None): + return operation + +class AthenaUpstream(BaseQueryRunner): + noop_query = 'SELECT 1' + + @classmethod + def name(cls): + return "Amazon Athena (Upstream)" + + @classmethod + def configuration_schema(cls): + schema = { + 'type': 'object', + 'properties': { + 'region': { + 'type': 'string', + 'title': 'AWS Region' + }, + 'aws_access_key': { + 'type': 'string', + 'title': 'AWS Access Key' + }, + 'aws_secret_key': { + 'type': 'string', + 'title': 'AWS Secret Key' + }, + 's3_staging_dir': { + 'type': 'string', + 'title': 'S3 Staging Path' + }, + 'schema': { + 'type': 'string', + 'title': 'Schema Name', + 'default': 'default' + }, + }, + 'required': ['region', 's3_staging_dir'], + 'order': ['region', 'aws_access_key', 'aws_secret_key', 's3_staging_dir', 'schema'], + 'secret': ['aws_secret_key'] + } + + if SHOW_EXTRA_SETTINGS: + schema['properties'].update({ + 'encryption_option': { + 'type': 'string', + 'title': 'Encryption Option', + }, + 'kms_key': { + 'type': 'string', + 'title': 'KMS Key', + }, + }) + + if not OPTIONAL_CREDENTIALS: + schema['required'] += ['aws_access_key', 'aws_secret_key'] + + return schema + + @classmethod + def enabled(cls): + return enabled + + @classmethod + def annotate_query(cls): + return ANNOTATE_QUERY + + @classmethod + def type(cls): + return "athena" + + def __init__(self, configuration): + super(AthenaUpstream, self).__init__(configuration) + + def get_schema(self, get_stats=False): + schema = {} + query = """ + SELECT table_schema, table_name, column_name + FROM information_schema.columns + WHERE table_schema NOT IN ('information_schema') + """ + + results, error = self.run_query(query, None) + if error is not None: + raise Exception("Failed getting schema.") + + results = json.loads(results) + for row in results['rows']: + table_name = '{0}.{1}'.format(row['table_schema'], row['table_name']) + if table_name not in schema: + schema[table_name] = {'name': table_name, 'columns': []} + schema[table_name]['columns'].append(row['column_name']) + + return schema.values() + + def run_query(self, query, user): + cursor = pyathena.connect( + s3_staging_dir=self.configuration['s3_staging_dir'], + region_name=self.configuration['region'], + aws_access_key_id=self.configuration.get('aws_access_key', None), + aws_secret_access_key=self.configuration.get('aws_secret_key', None), + schema_name=self.configuration.get('schema', 'default'), + encryption_option=self.configuration.get('encryption_option', None), + kms_key=self.configuration.get('kms_key', None), + formatter=SimpleFormatter()).cursor() + + try: + cursor.execute(query) + column_tuples = [(i[0], _TYPE_MAPPINGS.get(i[1], None)) for i in cursor.description] + columns = self.fetch_columns(column_tuples) + rows = [dict(zip(([c['name'] for c in columns]), r)) for i, r in enumerate(cursor.fetchall())] + data = {'columns': columns, 'rows': rows} + json_data = json.dumps(data, cls=JSONEncoder) + error = None + except KeyboardInterrupt: + if cursor.query_id: + cursor.cancel() + error = "Query cancelled by user." + json_data = None + except Exception, ex: + if cursor.query_id: + cursor.cancel() + error = ex.message + json_data = None + + return json_data, error + + +register(AthenaUpstream) + class Athena(BaseQueryRunner): noop_query = 'SELECT 1' From 364a7cd22b632ac5c721d000c6b053991e9f9b42 Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 14 Jul 2017 15:07:01 -0500 Subject: [PATCH 197/243] update imports tests pass --- redash/query_runner/athena.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index d8f266028c..3560a4502f 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -17,7 +17,7 @@ direct_enabled = False enabled = False -from redash.query_runner import BaseQueryRunner, register +from redash.query_runner import * from redash.utils import JSONEncoder from redash.settings import parse_boolean From c4674968be39a1239a65bc884414bd5c160702be Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 14 Jul 2017 16:39:55 -0500 Subject: [PATCH 198/243] back to working --- webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index 0760ad1132..f0dc5fa649 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -56,7 +56,7 @@ var config = { module: { loaders: [ - {test: /\.js$/, loader: 'ng-annotate!babel!eslint', exclude: /node_modules/}, + {test: /\.js$/, loader: 'ng-annotate!babel-loader!eslint', exclude: /node_modules/}, {test: /\.html$/, loader: 'raw', exclude: [/node_modules/, /index\.html/]}, // {test: /\.css$/, loader: 'style!css', exclude: /node_modules/}, {test: /\.css$/, loader: ExtractTextPlugin.extract("css-loader")}, From 8833d042563a31bc338d7f37a3bc2ed20677c9db Mon Sep 17 00:00:00 2001 From: Alison Date: Tue, 4 Jul 2017 12:58:02 -0500 Subject: [PATCH 199/243] allow unpublished queries to be used as alerts Still can not use unpublished queries on dashboards. --- client/app/pages/alert/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/pages/alert/index.js b/client/app/pages/alert/index.js index 00b9c2fc9c..5ab3bdcdef 100644 --- a/client/app/pages/alert/index.js +++ b/client/app/pages/alert/index.js @@ -48,7 +48,7 @@ function AlertCtrl($routeParams, $location, $sce, toastr, currentUser, Query, Ev return; } - Query.search({ q: term }, (results) => { + Query.search({ q: term, include_drafts: true }, (results) => { this.queries = results; }); }; From 2434d5f5d7a9b2845344cec7fea5f02ca9e40171 Mon Sep 17 00:00:00 2001 From: Alison Date: Sat, 24 Jun 2017 21:15:41 -0500 Subject: [PATCH 200/243] dashboard textbox edit close fix addresses issue Madalin found that allows a click off the dialog box to close it without resetting the textbox #54 --- client/app/pages/dashboards/widget.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/app/pages/dashboards/widget.js b/client/app/pages/dashboards/widget.js index e8c4f3ae86..5e44e6cbb6 100644 --- a/client/app/pages/dashboards/widget.js +++ b/client/app/pages/dashboards/widget.js @@ -40,6 +40,8 @@ function DashboardWidgetCtrl($location, $uibModal, $window, Events, currentUser) resolve: { widget: this.widget, }, + backdrop: 'static', + keyboard: false, }); }; From 57506e92463c81340162d6a93c27b8ab2483e113 Mon Sep 17 00:00:00 2001 From: Alison Date: Tue, 4 Jul 2017 18:05:25 -0500 Subject: [PATCH 201/243] works for filtering _v literally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit but don’t name a table abcdefghijklmnop. --- client/app/pages/queries/schema-browser.html | 10 +++++++++- client/app/pages/queries/schema-browser.js | 13 +++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/client/app/pages/queries/schema-browser.html b/client/app/pages/queries/schema-browser.html index 9ef734b025..7c47019d62 100644 --- a/client/app/pages/queries/schema-browser.html +++ b/client/app/pages/queries/schema-browser.html @@ -6,10 +6,18 @@ ng-click="$ctrl.onRefresh()"> + +
-
+
diff --git a/client/app/pages/queries/schema-browser.js b/client/app/pages/queries/schema-browser.js index 2f0e71ce82..e137c2a020 100644 --- a/client/app/pages/queries/schema-browser.js +++ b/client/app/pages/queries/schema-browser.js @@ -3,6 +3,9 @@ import template from './schema-browser.html'; function SchemaBrowserCtrl($scope) { 'ngInject'; + this.versionToggle = false; + this.versionFilter = 'abcdefghijklmnop'; + this.showTable = (table) => { table.collapsed = !table.collapsed; $scope.$broadcast('vsRepeatTrigger'); @@ -17,6 +20,16 @@ function SchemaBrowserCtrl($scope) { return size; }; + + this.flipToggleVersionedTables = (versionToggle) => { + if (versionToggle === false) { + this.versionToggle = true; + this.versionFilter = '_v'; + } else { + this.versionToggle = false; + this.versionFilter = 'abcdefghijklmnop'; + } + }; } const SchemaBrowser = { From c9068299c2374c789dc1a10cf8a8aa45ed16ed1f Mon Sep 17 00:00:00 2001 From: Alison Date: Tue, 4 Jul 2017 16:03:21 -0500 Subject: [PATCH 202/243] UI working --- client/app/pages/queries/schema-browser.html | 2 +- client/app/pages/queries/schema-browser.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/app/pages/queries/schema-browser.html b/client/app/pages/queries/schema-browser.html index 7c47019d62..4598a2a3c0 100644 --- a/client/app/pages/queries/schema-browser.html +++ b/client/app/pages/queries/schema-browser.html @@ -10,7 +10,7 @@ diff --git a/client/app/pages/queries/schema-browser.js b/client/app/pages/queries/schema-browser.js index e137c2a020..2c7abbedd7 100644 --- a/client/app/pages/queries/schema-browser.js +++ b/client/app/pages/queries/schema-browser.js @@ -36,6 +36,7 @@ const SchemaBrowser = { bindings: { schema: '<', onRefresh: '&', + flipToggleVersionedTables: '&', }, controller: SchemaBrowserCtrl, template, From efb3d8aed5b310ccde24dbdd7651ccd80f2822d1 Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 14 Jul 2017 16:55:37 -0500 Subject: [PATCH 203/243] fix cherry-pick conflict --- client/app/pages/queries/schema-browser.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/pages/queries/schema-browser.html b/client/app/pages/queries/schema-browser.html index 4598a2a3c0..7c47019d62 100644 --- a/client/app/pages/queries/schema-browser.html +++ b/client/app/pages/queries/schema-browser.html @@ -10,7 +10,7 @@ From 4cd2ac288936d9b2e2fe9c1a24898d6626791258 Mon Sep 17 00:00:00 2001 From: Blake Imsland Date: Fri, 7 Jul 2017 18:42:32 -0700 Subject: [PATCH 204/243] Deploy master to dockerhub as "latest" --- bin/deploy | 19 +++++++++++++++++++ circle.yml | 18 ++++++------------ 2 files changed, 25 insertions(+), 12 deletions(-) create mode 100755 bin/deploy diff --git a/bin/deploy b/bin/deploy new file mode 100755 index 0000000000..bccc6cb999 --- /dev/null +++ b/bin/deploy @@ -0,0 +1,19 @@ +#!/bin/bash + +set -eo pipefail + +[ ! -z $DOCKERHUB_REPO ] && [ $# -eq 1 ] + +VERSION="$1" + +printf '{"commit":"%s","version":"%s","source":"https://github.com/%s/%s","build":"%s"}\n' \ + "$CIRCLE_SHA1" \ + "$VERSION" \ + "$CIRCLE_PROJECT_USERNAME" \ + "$CIRCLE_PROJECT_REPONAME" \ + "$CIRCLE_BUILD_URL" \ +> version.json + +docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS +docker build -t $DOCKERHUB_REPO:$VERSION . +docker push $DOCKERHUB_REPO:$VERSION diff --git a/circle.yml b/circle.yml index 3e2bb02d94..9ff2963873 100644 --- a/circle.yml +++ b/circle.yml @@ -17,22 +17,16 @@ test: override: - nosetests --with-xunit --xunit-file=$CIRCLE_TEST_REPORTS/junit.xml --with-coverage --cover-package=redash tests/ deployment: + latest: + branch: master + owner: mozilla + commands: + - ./bin/deploy "latest" hub_releases: tag: /^m[0-9]+(\.[0-9]+)?$/ owner: mozilla commands: - - "[ ! -z $DOCKERHUB_REPO ]" - - > - printf '{"commit":"%s","version":"%s","source":"https://github.com/%s/%s","build":"%s"}\n' - "$CIRCLE_SHA1" - "$CIRCLE_TAG" - "$CIRCLE_PROJECT_USERNAME" - "$CIRCLE_PROJECT_REPONAME" - "$CIRCLE_BUILD_URL" - > version.json - - docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS - - docker build -t $DOCKERHUB_REPO:$CIRCLE_TAG . - - docker push $DOCKERHUB_REPO:$CIRCLE_TAG + - ./bin/deploy "$CIRCLE_TAG" general: branches: ignore: From 5a725ccc03e4a085c5506b642a855d881dbef3a8 Mon Sep 17 00:00:00 2001 From: Alison Date: Sat, 8 Jul 2017 00:17:46 -0500 Subject: [PATCH 205/243] add pyup config document --- .pyup.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .pyup.yml diff --git a/.pyup.yml b/.pyup.yml new file mode 100644 index 0000000000..ce4a2cf804 --- /dev/null +++ b/.pyup.yml @@ -0,0 +1,8 @@ +schedule: "every day" +search: False +update: insecure +requirements: + - requirements.txt: + update: security + - requirements-newrelic.txt: + update: security \ No newline at end of file From 0692158166075585f2e3a23d71a30572f1bd113d Mon Sep 17 00:00:00 2001 From: Alison Date: Mon, 10 Jul 2017 20:51:09 -0500 Subject: [PATCH 206/243] updated value based on comment that there was a typo in the docs. --- .pyup.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pyup.yml b/.pyup.yml index ce4a2cf804..3694ef8f58 100644 --- a/.pyup.yml +++ b/.pyup.yml @@ -3,6 +3,6 @@ search: False update: insecure requirements: - requirements.txt: - update: security + update: insecure - requirements-newrelic.txt: update: security \ No newline at end of file From 8e76e4ffc7fe881bb875bab09f07b4a5c5dfc898 Mon Sep 17 00:00:00 2001 From: Alison Date: Sat, 24 Jun 2017 15:49:47 -0500 Subject: [PATCH 207/243] working version on data source config page --- client/app/pages/data-sources/show.js | 22 ++++++++++++++++++++++ client/app/services/data-source.js | 1 + redash/cli/data_sources.py | 24 ++++++++++++++++++++++++ redash/handlers/api.py | 3 ++- redash/handlers/data_sources.py | 13 +++++++++++++ redash/query_runner/__init__.py | 13 +++++++++++++ redash/query_runner/pg.py | 2 ++ 7 files changed, 77 insertions(+), 1 deletion(-) diff --git a/client/app/pages/data-sources/show.js b/client/app/pages/data-sources/show.js index 1b0d4e9478..3e23c575dc 100644 --- a/client/app/pages/data-sources/show.js +++ b/client/app/pages/data-sources/show.js @@ -50,9 +50,31 @@ function DataSourceCtrl($scope, $routeParams, $http, $location, toastr, }); } + function getDataSourceVersion(callback) { + Events.record('test', 'data_source_version', $scope.dataSource.id); + + DataSource.version( + { id: $scope.dataSource.id }, (httpResponse) => { + console.log(httpResponse); + if (httpResponse.ok) { + const versionNumber = httpResponse.message; + toastr.success(`Success. Verison: ${versionNumber}`); + } else { + toastr.error(httpResponse.message, 'Version Test Failed:', { timeOut: 10000 }); + } + callback(); + }, (httpResponse) => { + console.log(httpResponse); + logger('Failed to get data source version: ', httpResponse.status, httpResponse.statusText, httpResponse); + toastr.error('Unknown error occurred while performing data source version test. Please try again later.', 'Data Source Version Test Failed:', { timeOut: 10000 }); + callback(); + }); + } + $scope.actions = [ { name: 'Delete', class: 'btn-danger', callback: deleteDataSource }, { name: 'Test Connection', class: 'btn-default', callback: testConnection, disableWhenDirty: true }, + { name: 'Test Data Source Version', class: 'btn-default', callback: getDataSourceVersion, disableWhenDirty: true }, ]; } diff --git a/client/app/services/data-source.js b/client/app/services/data-source.js index b6d4237929..c8fd795059 100644 --- a/client/app/services/data-source.js +++ b/client/app/services/data-source.js @@ -4,6 +4,7 @@ function DataSource($resource) { query: { method: 'GET', cache: false, isArray: true }, test: { method: 'POST', cache: false, isArray: false, url: 'api/data_sources/:id/test' }, getSchema: { method: 'GET', cache: false, isArray: true, url: 'api/data_sources/:id/schema' }, + version: { method: 'GET', cache: false, isArray: false, url: 'api/data_sources/:id/version' }, }; const DataSourceResource = $resource('api/data_sources/:id', { id: '@id' }, actions); diff --git a/redash/cli/data_sources.py b/redash/cli/data_sources.py index ed1dd3de70..18d9f8939e 100644 --- a/redash/cli/data_sources.py +++ b/redash/cli/data_sources.py @@ -66,6 +66,30 @@ def test(name, organization='default'): print "Couldn't find data source named: {}".format(name) exit(1) +@manager.command() +@click.argument('name') +@click.option('--org', 'organization', default='default', + help="The organization the user belongs to " + "(leave blank for 'default').") +def get_data_source_version(name, organization='default'): + """Get version of data source connection by issuing a trivial query.""" + try: + org = models.Organization.get_by_slug(organization) + data_source = models.DataSource.query.filter( + models.DataSource.name == name, + models.DataSource.org == org).one() + print "Testing get connection data source version: {} (id={})".format( + name, data_source.id) + try: + info = data_source.query_runner.get_data_source_version() + except Exception, e: + print "Failure: {}".format(e) + exit(1) + else: + print info + except NoResultFound: + print "Couldn't find data source named: {}".format(name) + exit(1) @manager.command() @click.argument('name', default=None, required=False) diff --git a/redash/handlers/api.py b/redash/handlers/api.py index db77b43241..a6786c12d1 100644 --- a/redash/handlers/api.py +++ b/redash/handlers/api.py @@ -7,7 +7,7 @@ from redash.handlers.permissions import ObjectPermissionsListResource, CheckPermissionResource from redash.handlers.alerts import AlertResource, AlertListResource, AlertSubscriptionListResource, AlertSubscriptionResource from redash.handlers.dashboards import DashboardListResource, RecentDashboardsResource, DashboardResource, DashboardShareResource, PublicDashboardResource -from redash.handlers.data_sources import DataSourceTypeListResource, DataSourceListResource, DataSourceSchemaResource, DataSourceResource, DataSourcePauseResource, DataSourceTestResource +from redash.handlers.data_sources import DataSourceTypeListResource, DataSourceListResource, DataSourceSchemaResource, DataSourceResource, DataSourcePauseResource, DataSourceTestResource, DataSourceVersionResource from redash.handlers.events import EventResource from redash.handlers.queries import ( MyQueriesResource, QueryForkResource, QueryListResource, @@ -58,6 +58,7 @@ def json_representation(data, code, headers=None): api.add_org_resource(DataSourceSchemaResource, '/api/data_sources//schema') api.add_org_resource(DataSourcePauseResource, '/api/data_sources//pause') api.add_org_resource(DataSourceTestResource, '/api/data_sources//test') +api.add_org_resource(DataSourceVersionResource, '/api/data_sources//version') api.add_org_resource(DataSourceResource, '/api/data_sources/', endpoint='data_source') api.add_org_resource(GroupListResource, '/api/groups', endpoint='groups') diff --git a/redash/handlers/data_sources.py b/redash/handlers/data_sources.py index 6dcf17ef3f..fea3885642 100644 --- a/redash/handlers/data_sources.py +++ b/redash/handlers/data_sources.py @@ -164,3 +164,16 @@ def post(self, data_source_id): return {"message": unicode(e), "ok": False} else: return {"message": "success", "ok": True} + +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) + try: + version_info = data_source.query_runner.get_data_source_version() + except Exception as e: + return {"message": unicode(e), "ok": False} + else: + return {"message": version_info, "ok": True} + + diff --git a/redash/query_runner/__init__.py b/redash/query_runner/__init__.py index f1968a980c..175dc2e31f 100644 --- a/redash/query_runner/__init__.py +++ b/redash/query_runner/__init__.py @@ -73,6 +73,19 @@ def annotate_query(cls): def configuration_schema(cls): return {} + def get_data_source_version(self): + if self.data_source_version_query is None: + raise NotImplementedError + data, error = self.run_query(self.data_source_version_query, None) + version = json.loads(data)['rows'][0]['version'] + if(self.data_source_version_post_process == "split by space take second"): + version = version.split(" ")[1] + + if error is not None: + raise Exception(error) + + return version + def test_connection(self): if self.noop_query is None: raise NotImplementedError() diff --git a/redash/query_runner/pg.py b/redash/query_runner/pg.py index f2a1d4604f..ac97015f6f 100644 --- a/redash/query_runner/pg.py +++ b/redash/query_runner/pg.py @@ -48,6 +48,8 @@ def _wait(conn, timeout=None): class PostgreSQL(BaseSQLQueryRunner): noop_query = "SELECT 1" default_doc_url = "https://www.postgresql.org/docs/current/" + data_source_version_query = "select version()" + data_source_version_post_process = "split by space take second" @classmethod def configuration_schema(cls): From c3e972da6c555c5db79af61d7033ea0732595de4 Mon Sep 17 00:00:00 2001 From: Alison Date: Sat, 24 Jun 2017 20:29:03 -0500 Subject: [PATCH 208/243] working postgres version --- client/app/pages/data-sources/show.js | 2 -- .../pages/queries/get-data-source-version.js | 18 ++++++++++++++++++ client/app/pages/queries/index.js | 2 ++ client/app/pages/queries/query.html | 2 ++ 4 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 client/app/pages/queries/get-data-source-version.js diff --git a/client/app/pages/data-sources/show.js b/client/app/pages/data-sources/show.js index 3e23c575dc..5971478b56 100644 --- a/client/app/pages/data-sources/show.js +++ b/client/app/pages/data-sources/show.js @@ -55,7 +55,6 @@ function DataSourceCtrl($scope, $routeParams, $http, $location, toastr, DataSource.version( { id: $scope.dataSource.id }, (httpResponse) => { - console.log(httpResponse); if (httpResponse.ok) { const versionNumber = httpResponse.message; toastr.success(`Success. Verison: ${versionNumber}`); @@ -64,7 +63,6 @@ function DataSourceCtrl($scope, $routeParams, $http, $location, toastr, } callback(); }, (httpResponse) => { - console.log(httpResponse); logger('Failed to get data source version: ', httpResponse.status, httpResponse.statusText, httpResponse); toastr.error('Unknown error occurred while performing data source version test. Please try again later.', 'Data Source Version Test Failed:', { timeOut: 10000 }); callback(); diff --git a/client/app/pages/queries/get-data-source-version.js b/client/app/pages/queries/get-data-source-version.js new file mode 100644 index 0000000000..11f8dbccfa --- /dev/null +++ b/client/app/pages/queries/get-data-source-version.js @@ -0,0 +1,18 @@ +function GetDataSourceVersionCtrl(Events, toastr, $scope, DataSource) { + // 'ngInject'; + + this.getDataSourceVersion = DataSource.version({ id: 6 }); +} + +const GetDataSourceVersionInfo = { + bindings: { + schema: '<', + onRefresh: '&', + }, + controller: GetDataSourceVersionCtrl, + template: '{{ $ctrl.getDataSourceVersion.message }}', +}; + +export default function (ngModule) { + ngModule.component('getDataSourceVersion', GetDataSourceVersionInfo); +} diff --git a/client/app/pages/queries/index.js b/client/app/pages/queries/index.js index b1c679c134..cc0daab198 100644 --- a/client/app/pages/queries/index.js +++ b/client/app/pages/queries/index.js @@ -10,6 +10,7 @@ import registerAlertUnsavedChanges from './alert-unsaved-changes'; import registerQuerySearchResultsPage from './queries-search-results-page'; import registerVisualizationEmbed from './visualization-embed'; import registerCompareQueryDialog from './compare-query-dialog'; +import registerGetDataSourceVersion from './get-data-source-version'; export default function (ngModule) { registerQueryResultsLink(ngModule); @@ -21,6 +22,7 @@ export default function (ngModule) { registerVisualizationEmbed(ngModule); registerCompareQueryDialog(ngModule); registerApiKeyDialog(ngModule); + registerGetDataSourceVersion(ngModule); return Object.assign({}, registerQuerySearchResultsPage(ngModule), registerSourceView(ngModule), diff --git a/client/app/pages/queries/query.html b/client/app/pages/queries/query.html index 973740c79f..60fbdcc92e 100644 --- a/client/app/pages/queries/query.html +++ b/client/app/pages/queries/query.html @@ -104,6 +104,8 @@

ng-options="ds.id as ds.name for ds in dataSources"> {{dataSource.type_name}} documentation {{dataSource.type_name}} + +

diff --git a/client/app/visualizations/edit-visualization-dialog.js b/client/app/visualizations/edit-visualization-dialog.js index 6baefb2c6d..add51f1db1 100644 --- a/client/app/visualizations/edit-visualization-dialog.js +++ b/client/app/visualizations/edit-visualization-dialog.js @@ -19,6 +19,9 @@ const EditVisualizationDialog = { this.visualization = copy(this.originalVisualization); this.visTypes = Visualization.visualizationTypes; + this.warning_three_column_groupby = 'You have more than 2 columns in your result set. To ensure the chart is accurate, please do one of the following:
  • Change the SQL query to give 2 result columns. You can CONCAT() columns together if you wish.
  • Select column(s) to group by.
'; + this.warning_three_column_stacking = 'You have more than 2 columns in your result set. You may wish to make the Stacking option equal to `Enabled` or `Percent`.'; + this.newVisualization = () => ({ type: Visualization.defaultVisualization.type, @@ -47,6 +50,21 @@ const EditVisualizationDialog = { } }; + this.has3plusColumnsFunction = () => { + let has3plusColumns = false; + if ((JSON.stringify(this.visualization.options.columnMapping).match(/,/g) || []).length > 1) { + has3plusColumns = true; + } + return has3plusColumns; + }; + + this.disableSubmit = () => { + if (this.has3plusColumnsFunction() && JSON.stringify(this.visualization.options.columnMapping).includes('unused')) { + return true; + } + return false; + }; + this.submit = () => { if (this.visualization.id) { Events.record('update', 'visualization', this.visualization.id, { type: this.visualization.type }); From 8fac6b6374f5d3ea42a10a6dae0efabd9097736e Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 7 Jul 2017 15:21:21 -0500 Subject: [PATCH 216/243] updated based on PR comments --- client/app/visualizations/edit-visualization-dialog.css | 5 +++++ client/app/visualizations/edit-visualization-dialog.html | 4 ++-- client/app/visualizations/edit-visualization-dialog.js | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 client/app/visualizations/edit-visualization-dialog.css diff --git a/client/app/visualizations/edit-visualization-dialog.css b/client/app/visualizations/edit-visualization-dialog.css new file mode 100644 index 0000000000..3e84b755b2 --- /dev/null +++ b/client/app/visualizations/edit-visualization-dialog.css @@ -0,0 +1,5 @@ +/* Edit Visualization Dialog specific CSS */ + +.slight-padding { + padding: 5px; +} \ No newline at end of file diff --git a/client/app/visualizations/edit-visualization-dialog.html b/client/app/visualizations/edit-visualization-dialog.html index 77d359fe50..6d26e4b6bd 100644 --- a/client/app/visualizations/edit-visualization-dialog.html +++ b/client/app/visualizations/edit-visualization-dialog.html @@ -34,10 +34,10 @@
-
+
-
+
diff --git a/client/app/visualizations/edit-visualization-dialog.js b/client/app/visualizations/edit-visualization-dialog.js index add51f1db1..ee8d5236c8 100644 --- a/client/app/visualizations/edit-visualization-dialog.js +++ b/client/app/visualizations/edit-visualization-dialog.js @@ -1,6 +1,7 @@ import { pluck } from 'underscore'; import { copy } from 'angular'; import template from './edit-visualization-dialog.html'; +import './edit-visualization-dialog.css'; const EditVisualizationDialog = { template, @@ -20,7 +21,7 @@ const EditVisualizationDialog = { this.visTypes = Visualization.visualizationTypes; this.warning_three_column_groupby = 'You have more than 2 columns in your result set. To ensure the chart is accurate, please do one of the following:
  • Change the SQL query to give 2 result columns. You can CONCAT() columns together if you wish.
  • Select column(s) to group by.
'; - this.warning_three_column_stacking = 'You have more than 2 columns in your result set. You may wish to make the Stacking option equal to `Enabled` or `Percent`.'; + this.warning_three_column_stacking = 'You have more than 2 columns in your result set. You may wish to make the Stacking option equal to `Enabled` or `Percent`.'; this.newVisualization = () => ({ From e7f294e0929290207f37590aebac4016057e7b36 Mon Sep 17 00:00:00 2001 From: Alison Date: Wed, 12 Jul 2017 11:12:26 -0500 Subject: [PATCH 217/243] handle ds w/ unimplemented version queries --- client/app/pages/queries/get-data-source-version.js | 2 +- client/app/pages/queries/view.js | 2 +- redash/query_runner/__init__.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/client/app/pages/queries/get-data-source-version.js b/client/app/pages/queries/get-data-source-version.js index 66061ec50b..1d1de07c08 100644 --- a/client/app/pages/queries/get-data-source-version.js +++ b/client/app/pages/queries/get-data-source-version.js @@ -13,7 +13,7 @@ const GetDataSourceVersionInfo = { onRefresh: '&', }, controller: GetDataSourceVersionCtrl, - template: '{{ $ctrl.getDataSourceVersion.message }}', + template: '{{ $ctrl.getDataSourceVersion.message }}', }; export default function (ngModule) { diff --git a/client/app/pages/queries/view.js b/client/app/pages/queries/view.js index 574a80ba0d..12fd3261bd 100644 --- a/client/app/pages/queries/view.js +++ b/client/app/pages/queries/view.js @@ -254,7 +254,7 @@ function QueryViewCtrl($scope, Events, $route, $routeParams, $location, $window, updateSchema(); $scope.dataSource = find($scope.dataSources, ds => ds.id === $scope.query.data_source_id); - document.getElementById('data-source-version').innerHTML = ''; + document.getElementById('data-source-version').innerHTML = ''; }; $scope.setVisualizationTab = (visualization) => { diff --git a/redash/query_runner/__init__.py b/redash/query_runner/__init__.py index c3658a3d26..868da2b1dc 100644 --- a/redash/query_runner/__init__.py +++ b/redash/query_runner/__init__.py @@ -48,6 +48,7 @@ class InterruptException(Exception): class BaseQueryRunner(object): noop_query = None default_doc_url = None + data_source_version_query = None def __init__(self, configuration): self.syntax = 'sql' From 8222d22d73107c6872e42e9d849a13c40fe1d511 Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 14 Jul 2017 17:04:50 -0500 Subject: [PATCH 218/243] bug fix for ds version showing --- client/app/pages/queries/get-data-source-version.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/pages/queries/get-data-source-version.js b/client/app/pages/queries/get-data-source-version.js index 1d1de07c08..fa760ad016 100644 --- a/client/app/pages/queries/get-data-source-version.js +++ b/client/app/pages/queries/get-data-source-version.js @@ -1,5 +1,5 @@ function GetDataSourceVersionCtrl(Events, toastr, $scope, DataSource, $route) { - // 'ngInject'; + 'ngInject'; this.getDataSourceVersion = DataSource.version( { From 2500b53ca86803bc59b0f87dea17cdfdcfe4a4fe Mon Sep 17 00:00:00 2001 From: Alison Date: Tue, 4 Jul 2017 12:14:32 -0500 Subject: [PATCH 219/243] adding activedata query_runner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The thing that doesn’t work is the ability to save the host field in the configuration. --- client/app/components/dynamic-form.html | 3 + redash/query_runner/activedata.py | 182 ++++++++++++++++++++++++ redash/settings.py | 3 +- 3 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 redash/query_runner/activedata.py diff --git a/client/app/components/dynamic-form.html b/client/app/components/dynamic-form.html index 4e333075c1..91abaf87b7 100644 --- a/client/app/components/dynamic-form.html +++ b/client/app/components/dynamic-form.html @@ -7,10 +7,13 @@ + {{ fields }} + {{ target.options }}
+
{{ field.property.info }}
{{ fields }} - {{ target.options }}
Date: Fri, 7 Jul 2017 23:14:14 -0500 Subject: [PATCH 221/243] add a different init fx passes test suite --- redash/query_runner/activedata.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/redash/query_runner/activedata.py b/redash/query_runner/activedata.py index f2da4753bc..c4d3de740e 100644 --- a/redash/query_runner/activedata.py +++ b/redash/query_runner/activedata.py @@ -37,6 +37,9 @@ class ActiveData(BaseSQLQueryRunner): # configuration['host_url'] = url.scheme + "://" + url.hostname + ":" + unicode(url.port or 80) # BaseSQLQueryRunner.__init__(self, configuration) + def __init__(self, configuration): + super(ActiveData, self).__init__(configuration) + @classmethod def configuration_schema(cls): return { From f5de9469955182a1153a8d3b9313be266f007cc0 Mon Sep 17 00:00:00 2001 From: Alison Date: Tue, 11 Jul 2017 21:05:34 -0500 Subject: [PATCH 222/243] fix not displaying datasource info issue --- redash/models.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/redash/models.py b/redash/models.py index 6ba068db5e..962effaf1d 100644 --- a/redash/models.py +++ b/redash/models.py @@ -477,11 +477,11 @@ def to_dict(self, all=False, with_permissions_for=None): 'syntax': self.query_runner.syntax, 'paused': self.paused, 'pause_reason': self.pause_reason, - 'type_name': self.query_runner.name(), + 'type_name': self.query_runner.name() } - schema = get_configuration_schema_for_query_runner_type(self.type) if all: + schema = get_configuration_schema_for_query_runner_type(self.type) self.options.set_schema(schema) d['options'] = self.options.to_dict(mask_secrets=True) d['queue_name'] = self.queue_name @@ -493,8 +493,7 @@ def to_dict(self, all=False, with_permissions_for=None): DataSourceGroup.group == with_permissions_for, DataSourceGroup.data_source == self).one()[0] - doc_url = self.options.get('doc_url', schema['properties'].get( - 'doc_url', {}).get('default')) + doc_url = self.options.get('doc_url') if doc_url: d['options'] = {'doc_url': doc_url} @@ -570,9 +569,8 @@ def resume(self): def add_group(self, group, view_only=False): dsg = DataSourceGroup(group=group, data_source=self, view_only=view_only) db.session.add(dsg) - return dsg - setattr(self, 'data_source_groups', dsg) + return dsg def remove_group(self, group): db.session.query(DataSourceGroup).filter( From 746b7615aafd0541ffe64eeb166193b127ca4a7a Mon Sep 17 00:00:00 2001 From: Alison Date: Tue, 11 Jul 2017 21:21:07 -0500 Subject: [PATCH 223/243] tests passing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I commented out `setattr(self, 'data_source_groups', dsg)` because it made 3 tests fail and shouldn’t have been being obeyed before (after the `return`). Other changes are minor to be production ready. --- client/app/components/dynamic-form.html | 1 - redash/models.py | 2 +- redash/query_runner/activedata.py | 5 ----- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/client/app/components/dynamic-form.html b/client/app/components/dynamic-form.html index ed7b81d4ee..8206117ea7 100644 --- a/client/app/components/dynamic-form.html +++ b/client/app/components/dynamic-form.html @@ -7,7 +7,6 @@
- {{ fields }}
Date: Thu, 13 Jul 2017 19:18:43 -0500 Subject: [PATCH 224/243] updates from PR review --- redash/models.py | 6 ++++-- redash/query_runner/activedata.py | 27 +++++++++------------------ 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/redash/models.py b/redash/models.py index caa94dd014..a040a4c8a0 100644 --- a/redash/models.py +++ b/redash/models.py @@ -494,7 +494,10 @@ def to_dict(self, all=False, with_permissions_for=None): DataSourceGroup.data_source == self).one()[0] doc_url = self.options.get('doc_url') - if doc_url: + try: + if doc_url: + d['options'].update(doc_url=doc_url) + except: d['options'] = {'doc_url': doc_url} return d @@ -569,7 +572,6 @@ def resume(self): def add_group(self, group, view_only=False): dsg = DataSourceGroup(group=group, data_source=self, view_only=view_only) db.session.add(dsg) - #setattr(self, 'data_source_groups', dsg) return dsg def remove_group(self, group): diff --git a/redash/query_runner/activedata.py b/redash/query_runner/activedata.py index 3e9fa21c8f..73a10fe97a 100644 --- a/redash/query_runner/activedata.py +++ b/redash/query_runner/activedata.py @@ -1,22 +1,16 @@ import json import logging -from urlparse import urlparse import requests -# from mo_dots import wrap, listwrap -# from mo_logs import Except -# from mo_logs.exceptions import ERROR from redash.query_runner import TYPE_INTEGER, TYPE_STRING, TYPE_FLOAT, BaseSQLQueryRunner, register from redash.utils import JSONEncoder -#originally from: https://github.com/klahnakoski/ActiveData-redash-query-runner/blob/c0e7286c09c6f1eb6746a6c7cca581bea79f4757/active_data.py +#Originally written by Github user @klahnakoski +#Original link: https://github.com/klahnakoski/ActiveData-redash-query-runner/blob/c0e7286c09c6f1eb6746a6c7cca581bea79f4757/active_data.py logger = logging.getLogger(__name__) -if not unicode: - unicode = str - types_map = { bool: TYPE_INTEGER, str: TYPE_STRING, @@ -32,9 +26,6 @@ class ActiveData(BaseSQLQueryRunner): noop_query = "SELECT 1" - def __init__(self, configuration): - super(ActiveData, self).__init__(configuration) - @classmethod def configuration_schema(cls): return { @@ -44,7 +35,7 @@ def configuration_schema(cls): "type": "string", "title": "Host URL", "default": "https://activedata.allizom.org:80", - "info": "Do not end with a trailing slash." + "info": "Please include a port. Do not end with a trailing slash." }, "doc_url": { "type": "string", @@ -103,9 +94,9 @@ def run_jx_query(self, query, user): def run_query(self, annotated_query, user): request = {} comment, request["sql"] = annotated_query.split("*/", 2) - meta = request['meta'] ={} + meta = request['meta'] = {} for kv in comment.strip()[2:].split(","): - k, v = map(unicode.strip, kv.split(":")) + k, v = [s.strip() for s in kv.split(':')] meta[k] = v logger.debug("Send ActiveData a SQL query: %s", request['sql']) @@ -143,12 +134,12 @@ def get_unique_name(name, type): new_row = {} for i, cname in enumerate(table['header']): val = r[i] - if val == None: + if val is None: continue type_ = val.__class__ - if type_ in [dict, list]: + if isinstance(val, (dict, list)): val = json.dumps(val, cls=JSONEncoder) - col = get_unique_name(cname, types_map[type_]) + col = get_unique_name(cname, types_map.get(type(val), TYPE_STRING)) new_row[col] = val output.append(new_row) @@ -177,4 +168,4 @@ def find_cause(e): e = c return e.get('template') -register(ActiveData) \ No newline at end of file +register(ActiveData) From 97b58860de40332222f77ff51eccd5a45ee8d88f Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 14 Jul 2017 17:52:24 -0500 Subject: [PATCH 225/243] getredash PR #1809 https://github.com/getredash/redash/pull/1809 Plus, a) I added aws_secret_key and aws_access_key to required fields. b) I named the 3rd query_runner something different. tests pass --- redash/query_runner/athena.py | 146 +++++++++++++++++++++++++++++++++- requirements_all_ds.txt | 1 + 2 files changed, 146 insertions(+), 1 deletion(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index 06816d883f..30a4617af4 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -1,9 +1,16 @@ +import logging import json import os import re import requests +try: + import pyathena + enabled = True +except ImportError: + enabled = False + try: import botocore.session from botocore.exceptions import WaiterError @@ -11,13 +18,142 @@ except ImportError: direct_enabled = False -from redash.query_runner import BaseQueryRunner, register +from redash.query_runner import * from redash.utils import JSONEncoder from redash.settings import parse_boolean +logger = logging.getLogger(__name__) PROXY_URL = os.environ.get('ATHENA_PROXY_URL') ANNOTATE_QUERY = parse_boolean(os.environ.get('ATHENA_ANNOTATE_QUERY', 'true')) +_TYPE_MAPPINGS = { + 'boolean': TYPE_BOOLEAN, + 'tinyint': TYPE_INTEGER, + 'smallint': TYPE_INTEGER, + 'integer': TYPE_INTEGER, + 'bigint': TYPE_INTEGER, + 'double': TYPE_FLOAT, + 'varchar': TYPE_STRING, + 'timestamp': TYPE_DATETIME, + 'date': TYPE_DATE, + 'varbinary': TYPE_STRING, + 'array': TYPE_STRING, + 'map': TYPE_STRING, + 'row': TYPE_STRING, + 'decimal': TYPE_FLOAT, +} + +class AthenaUpstream(BaseQueryRunner): + noop_query = 'SELECT 1' + + @classmethod + def name(cls): + return "Amazon Athena (Upstream PyAthena)" + + @classmethod + def configuration_schema(cls): + return { + 'type': 'object', + 'properties': { + 'region': { + 'type': 'string', + 'title': 'AWS Region' + }, + 'aws_access_key': { + 'type': 'string', + 'title': 'AWS Access Key' + }, + 'aws_secret_key': { + 'type': 'string', + 'title': 'AWS Secret Key' + }, + 's3_staging_dir': { + 'type': 'string', + 'title': 'S3 Staging Path' + }, + 'schema': { + 'type': 'string', + 'title': 'Schema Name', + 'default': 'default' + }, + 'encryption_option': { + 'type': 'string', + 'title': 'Encryption Option', + }, + 'kms_key': { + 'type': 'string', + 'title': 'KMS Key', + }, + }, + 'required': ['region', 'aws_access_key', 'aws_secret_key', 's3_staging_dir'], + 'secret': ['aws_secret_key'] + } + + @classmethod + def enabled(cls): + return enabled + + @classmethod + def type(cls): + return "athena_upstream" + + def __init__(self, configuration): + super(AthenaUpstream, self).__init__(configuration) + + def get_schema(self, get_stats=False): + schema = {} + query = """ + SELECT table_schema, table_name, column_name + FROM information_schema.columns + WHERE table_schema NOT IN ('information_schema') + """ + + results, error = self.run_query(query, None) + if error is not None: + raise Exception("Failed getting schema.") + + results = json.loads(results) + for row in results['rows']: + table_name = '{0}.{1}'.format(row['table_schema'], row['table_name']) + if table_name not in schema: + schema[table_name] = {'name': table_name, 'columns': []} + schema[table_name]['columns'].append(row['column_name']) + + return schema.values() + + def run_query(self, query, user): + cursor = pyathena.connect( + s3_staging_dir=self.configuration['s3_staging_dir'], + region_name=self.configuration['region'], + aws_access_key_id=self.configuration.get('aws_access_key', None), + aws_secret_access_key=self.configuration.get('aws_secret_key', None), + schema_name=self.configuration.get('schema', 'default'), + encryption_option=self.configuration.get('encryption_option', None), + kms_key=self.configuration.get('kms_key', None)).cursor() + + try: + cursor.execute(query) + column_tuples = [(i[0], _TYPE_MAPPINGS.get(i[1], None)) for i in cursor.description] + columns = self.fetch_columns(column_tuples) + rows = [dict(zip(([c['name'] for c in columns]), r)) for i, r in enumerate(cursor.fetchall())] + data = {'columns': columns, 'rows': rows} + json_data = json.dumps(data, cls=JSONEncoder) + error = None + except KeyboardInterrupt: + cursor.cancel() + error = "Query cancelled by user." + json_data = None + except Exception, ex: + cursor.cancel() + error = ex.message + json_data = None + + return json_data, error + + +register(AthenaUpstream) + + class Athena(BaseQueryRunner): noop_query = 'SELECT 1' @@ -25,6 +161,10 @@ class Athena(BaseQueryRunner): def name(cls): return "Amazon Athena (via JDBC)" + @classmethod + def type(cls): + return "athena" + @classmethod def configuration_schema(cls): return { @@ -117,6 +257,10 @@ class AthenaDirect(BaseQueryRunner): def name(cls): return "Amazon Athena (direct)" + @classmethod + def type(cls): + return "athena" + @classmethod def enabled(cls): return direct_enabled diff --git a/requirements_all_ds.txt b/requirements_all_ds.txt index 10fa5316fa..36cb43d28e 100644 --- a/requirements_all_ds.txt +++ b/requirements_all_ds.txt @@ -21,5 +21,6 @@ memsql==2.16.0 snowflake_connector_python==1.3.16 atsd_client==2.0.12 simple_salesforce==0.72.2 +PyAthena>=1.0.0 # certifi is needed to support MongoDB and SSL: certifi From 4fc9782876ab404fbed9db76ac488aa020cf66af Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 14 Jul 2017 18:39:58 -0500 Subject: [PATCH 226/243] fixing doc_url model issue v3 --- redash/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redash/models.py b/redash/models.py index a040a4c8a0..85990576f5 100644 --- a/redash/models.py +++ b/redash/models.py @@ -495,10 +495,10 @@ def to_dict(self, all=False, with_permissions_for=None): doc_url = self.options.get('doc_url') try: - if doc_url: + if doc_url and all != False: d['options'].update(doc_url=doc_url) except: - d['options'] = {'doc_url': doc_url} + print d return d From 950c86c4debba2c8e89af01fc4081fbf5e717687 Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 14 Jul 2017 18:41:53 -0500 Subject: [PATCH 227/243] fix query cancellation condition for athena getredash PR #1841 https://github.com/getredash/redash/pull/1841 --- redash/query_runner/athena.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index 30a4617af4..bb9890ae08 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -140,11 +140,13 @@ def run_query(self, query, user): json_data = json.dumps(data, cls=JSONEncoder) error = None except KeyboardInterrupt: - cursor.cancel() + if cursor.query_id: + cursor.cancel() error = "Query cancelled by user." json_data = None except Exception, ex: - cursor.cancel() + if cursor.query_id: + cursor.cancel() error = ex.message json_data = None From 60c8e400e1762b230332dc59a9712aa4dc8c3998 Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 14 Jul 2017 18:45:15 -0500 Subject: [PATCH 228/243] Athena: bring back disable annotations & disable formatter getredash PR #1846 https://github.com/getredash/redash/pull/1846 --- redash/query_runner/athena.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index bb9890ae08..05fcb2cf31 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -43,6 +43,10 @@ 'decimal': TYPE_FLOAT, } +class SimpleFormatter(object): + def format(self, operation, parameters=None): + return operation + class AthenaUpstream(BaseQueryRunner): noop_query = 'SELECT 1' @@ -93,6 +97,10 @@ def configuration_schema(cls): def enabled(cls): return enabled + @classmethod + def annotate_query(cls): + return ANNOTATE_QUERY + @classmethod def type(cls): return "athena_upstream" @@ -129,7 +137,8 @@ def run_query(self, query, user): aws_secret_access_key=self.configuration.get('aws_secret_key', None), schema_name=self.configuration.get('schema', 'default'), encryption_option=self.configuration.get('encryption_option', None), - kms_key=self.configuration.get('kms_key', None)).cursor() + kms_key=self.configuration.get('kms_key', None), + formatter=SimpleFormatter()).cursor() try: cursor.execute(query) From 303e9673718ec63c53eee81b01f965bf9fee096b Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 14 Jul 2017 18:47:40 -0500 Subject: [PATCH 229/243] Add: ability to customize Athena configuration schema getredash PR #1859 https://github.com/getredash/redash/pull/1859 --- redash/query_runner/athena.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index 05fcb2cf31..a65426d68f 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -25,6 +25,8 @@ logger = logging.getLogger(__name__) PROXY_URL = os.environ.get('ATHENA_PROXY_URL') ANNOTATE_QUERY = parse_boolean(os.environ.get('ATHENA_ANNOTATE_QUERY', 'true')) +SHOW_EXTRA_SETTINGS = parse_boolean(os.environ.get('ATHENA_SHOW_EXTRA_SETTINGS', 'true')) +OPTIONAL_CREDENTIALS = parse_boolean(os.environ.get('ATHENA_OPTIONAL_CREDENTIALS', 'true')) _TYPE_MAPPINGS = { 'boolean': TYPE_BOOLEAN, @@ -56,7 +58,7 @@ def name(cls): @classmethod def configuration_schema(cls): - return { + schema = return { 'type': 'object', 'properties': { 'region': { @@ -79,7 +81,15 @@ def configuration_schema(cls): 'type': 'string', 'title': 'Schema Name', 'default': 'default' - }, + } + }, + 'required': ['region', 's3_staging_dir'], + 'order': ['region', 'aws_access_key', 'aws_secret_key', 's3_staging_dir', 'schema'], + 'secret': ['aws_secret_key'] + } + + if SHOW_EXTRA_SETTINGS: + schema['properties'].update({ 'encryption_option': { 'type': 'string', 'title': 'Encryption Option', @@ -88,10 +98,13 @@ def configuration_schema(cls): 'type': 'string', 'title': 'KMS Key', }, - }, - 'required': ['region', 'aws_access_key', 'aws_secret_key', 's3_staging_dir'], - 'secret': ['aws_secret_key'] - } + }) + + if not OPTIONAL_CREDENTIALS: + schema['required'] += ['aws_access_key', 'aws_secret_key'] + + return schema + @classmethod def enabled(cls): From 7f38d32c7f5e9ffe42b16d75df046b64c916f01d Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 14 Jul 2017 18:48:43 -0500 Subject: [PATCH 230/243] update goto version to support athena getredash pr #1853 https://github.com/getredash/redash/pull/1853 --- requirements_all_ds.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_all_ds.txt b/requirements_all_ds.txt index 36cb43d28e..382d0a91e1 100644 --- a/requirements_all_ds.txt +++ b/requirements_all_ds.txt @@ -12,7 +12,7 @@ td-client==0.8.0 pymssql==2.1.3 dql==0.5.16 dynamo3==0.4.7 -botocore==1.5.21 +botocore==1.5.72 sasl>=0.1.3 thrift>=0.8.0 thrift_sasl>=0.1.0 From 0302414362fb0b02ae4058b2e659a7d87b963455 Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 14 Jul 2017 18:50:09 -0500 Subject: [PATCH 231/243] small bug fix --- redash/query_runner/athena.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index a65426d68f..9cef2528ab 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -58,7 +58,7 @@ def name(cls): @classmethod def configuration_schema(cls): - schema = return { + schema = { 'type': 'object', 'properties': { 'region': { From a92fc86e6fab27c54aa4152e2926c858da63cc5a Mon Sep 17 00:00:00 2001 From: Alison Date: Fri, 14 Jul 2017 19:47:07 -0500 Subject: [PATCH 232/243] don't double import logging --- redash/query_runner/athena.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index cbdd4980de..f0f763b0a3 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -1,6 +1,5 @@ import logging import json -import logging import os import re @@ -391,4 +390,4 @@ def run_query(self, query, user): return json.dumps(data, cls=JSONEncoder), None -register(AthenaDirect) \ No newline at end of file +register(AthenaDirect) From c57e64b9a5df4742c1496926520ff0bbcd07ab9f Mon Sep 17 00:00:00 2001 From: Alison Date: Sat, 15 Jul 2017 00:09:58 -0500 Subject: [PATCH 233/243] undo bug fix on webpack config --- webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index 2189dfb336..138218aefa 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -56,7 +56,7 @@ var config = { module: { loaders: [ - {test: /\.js$/, loader: 'ng-annotate!babel-loader!eslint', exclude: /node_modules/}, + {test: /\.js$/, loader: 'ng-annotate!babel!eslint', exclude: /node_modules/}, {test: /\.html$/, loader: 'raw', exclude: [/node_modules/, /index\.html/]}, // {test: /\.css$/, loader: 'style!css', exclude: /node_modules/}, {test: /\.css$/, loader: ExtractTextPlugin.extract("css-loader")}, From 4a5f01c2aa7bcd2f9b2f96e9a51df011149994a2 Mon Sep 17 00:00:00 2001 From: Alison Date: Sat, 15 Jul 2017 01:36:52 -0500 Subject: [PATCH 234/243] babel and raw -loader for npm --- webpack.config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index 138218aefa..562c480280 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -56,8 +56,8 @@ var config = { module: { loaders: [ - {test: /\.js$/, loader: 'ng-annotate!babel!eslint', exclude: /node_modules/}, - {test: /\.html$/, loader: 'raw', exclude: [/node_modules/, /index\.html/]}, + {test: /\.js$/, loader: 'ng-annotate!babel-loader!eslint', exclude: /node_modules/}, + {test: /\.html$/, loader: 'raw-loader', exclude: [/node_modules/, /index\.html/]}, // {test: /\.css$/, loader: 'style!css', exclude: /node_modules/}, {test: /\.css$/, loader: ExtractTextPlugin.extract("css-loader")}, { From c3f0a5808dc9b3e052a595ebdcb577326eb6fa65 Mon Sep 17 00:00:00 2001 From: Alexander Shepelin Date: Sun, 18 Jun 2017 23:45:43 +0300 Subject: [PATCH 235/243] update config file to webpack2 format --- webpack.config.js | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index 562c480280..9e155cd768 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -55,23 +55,26 @@ var config = { ], module: { - loaders: [ - {test: /\.js$/, loader: 'ng-annotate!babel-loader!eslint', exclude: /node_modules/}, - {test: /\.html$/, loader: 'raw-loader', exclude: [/node_modules/, /index\.html/]}, - // {test: /\.css$/, loader: 'style!css', exclude: /node_modules/}, - {test: /\.css$/, loader: ExtractTextPlugin.extract("css-loader")}, + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + use: ['ng-annotate-loader', 'babel-loader', 'eslint-loader'] + }, + { + test: /\.html$/, + exclude: [/node_modules/, /index\.html/], + use: [{ + loader: 'raw-loader' + }] + }, + { + test: /\.css$/, + use: ExtractTextPlugin.extract('css-loader') + }, { test: /\.scss$/, - use: ExtractTextPlugin.extract([ - { - loader: 'css-loader', - options: { - minimize: process.env.NODE_ENV === 'production' - } - }, { - loader: 'sass-loader' - } - ]) + use: ExtractTextPlugin.extract(['css-loader', 'sass-loader']) }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, From 56725bc6af88a2a211fc09bdc205407acaf0ab41 Mon Sep 17 00:00:00 2001 From: Alexander Shepelin Date: Mon, 19 Jun 2017 00:11:00 +0300 Subject: [PATCH 236/243] update css-related loaders --- webpack.config.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index 9e155cd768..420dc43188 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -70,11 +70,25 @@ var config = { }, { test: /\.css$/, - use: ExtractTextPlugin.extract('css-loader') + use: ExtractTextPlugin.extract([{ + loader: 'css-loader', + options: { + minimize: process.env.NODE_ENV === 'production' + } + }]) }, { test: /\.scss$/, - use: ExtractTextPlugin.extract(['css-loader', 'sass-loader']) + use: ExtractTextPlugin.extract([ + { + loader: 'css-loader', + options: { + minimize: process.env.NODE_ENV === 'production' + } + }, { + loader: 'sass-loader' + } + ]) }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, From cd9ba8ab0bece491332d7b30f2c748f55781e2c8 Mon Sep 17 00:00:00 2001 From: Alison Date: Tue, 18 Jul 2017 18:06:11 -0500 Subject: [PATCH 237/243] get more info on athena status --- client/app/pages/queries/query.html | 2 +- redash/models.py | 8 ++++---- redash/query_runner/athena.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/app/pages/queries/query.html b/client/app/pages/queries/query.html index 4525ae3578..285e90bf64 100644 --- a/client/app/pages/queries/query.html +++ b/client/app/pages/queries/query.html @@ -204,7 +204,7 @@

Data Scanned - {{ queryResult.query_result.data_scanned }} + {{ queryResult.query_result.data_scanned }} diff --git a/redash/models.py b/redash/models.py index f04e3f6a74..39f9e74e7e 100644 --- a/redash/models.py +++ b/redash/models.py @@ -632,10 +632,10 @@ class QueryResult(db.Model, BelongsToOrgMixin): __tablename__ = 'query_results' def to_dict(self): - if hasattr(self, 'data_scanned') and self.data_scanned: + if hasattr(self, 'data_scanned'): data_scanned_info = self.data_scanned else: - data_scanned_info = '' + data_scanned_info = 'to_dict' return { 'id': self.id, @@ -682,8 +682,8 @@ def get_latest(cls, data_source, query, max_age=0): def store_result(cls, org, data_source, query_hash, query, data, run_time, retrieved_at): try: data_scanned_information = json.loads(data)['data_scanned'] - except (ValueError, TypeError): - data_scanned_information = '' + except (ValueError, TypeError) as e: + data_scanned_information = e query_result = cls(org=org, query_hash=query_hash, diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index f0f763b0a3..acf54b93f8 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -157,7 +157,7 @@ def run_query(self, query, user): column_tuples = [(i[0], _TYPE_MAPPINGS.get(i[1], None)) for i in cursor.description] columns = self.fetch_columns(column_tuples) rows = [dict(zip(([c['name'] for c in columns]), r)) for i, r in enumerate(cursor.fetchall())] - data = {'columns': columns, 'rows': rows} + data = {'columns': columns, 'rows': rows, 'data_scanned': 'upstream'} json_data = json.dumps(data, cls=JSONEncoder) error = None except KeyboardInterrupt: @@ -282,7 +282,7 @@ def name(cls): @classmethod def type(cls): - return "athena" + return "athenadirect" @classmethod def enabled(cls): From bcfe37acfaeb38b4221c647e57474848ac88bb08 Mon Sep 17 00:00:00 2001 From: Alison Date: Tue, 18 Jul 2017 19:04:45 -0500 Subject: [PATCH 238/243] fix tests --- redash/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redash/models.py b/redash/models.py index 39f9e74e7e..9082ac9ce3 100644 --- a/redash/models.py +++ b/redash/models.py @@ -683,7 +683,7 @@ def store_result(cls, org, data_source, query_hash, query, data, run_time, retri try: data_scanned_information = json.loads(data)['data_scanned'] except (ValueError, TypeError) as e: - data_scanned_information = e + data_scanned_information = 'error' query_result = cls(org=org, query_hash=query_hash, From 0b091449d14f0ff60255dbfae37afe95882612d2 Mon Sep 17 00:00:00 2001 From: Alison Date: Wed, 19 Jul 2017 03:14:57 -0500 Subject: [PATCH 239/243] adds data_scanned attempt to athenaupstream --- redash/query_runner/athena.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index acf54b93f8..94e717b672 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -157,7 +157,12 @@ def run_query(self, query, user): column_tuples = [(i[0], _TYPE_MAPPINGS.get(i[1], None)) for i in cursor.description] columns = self.fetch_columns(column_tuples) rows = [dict(zip(([c['name'] for c in columns]), r)) for i, r in enumerate(cursor.fetchall())] - data = {'columns': columns, 'rows': rows, 'data_scanned': 'upstream'} + qbytes = 'upstream2' + try: + qbytes = cursor.data_scanned_in_bytes() + except AttributeError as e: + debug("Athena Upstream can't get data_scanned_in_bytes: %s", e) + data = {'columns': columns, 'rows': rows, 'data_scanned': qbytes } json_data = json.dumps(data, cls=JSONEncoder) error = None except KeyboardInterrupt: From 02df0b642936af3db46252610bdb48f468a0a024 Mon Sep 17 00:00:00 2001 From: Alison Date: Wed, 19 Jul 2017 04:58:31 -0500 Subject: [PATCH 240/243] poll first and call attribute instead was getting "'int' object is not callable" error, so changing to attribute instead of function. also calling the function to populate the variable first. --- redash/query_runner/athena.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py index 94e717b672..81560b8552 100644 --- a/redash/query_runner/athena.py +++ b/redash/query_runner/athena.py @@ -159,7 +159,8 @@ def run_query(self, query, user): rows = [dict(zip(([c['name'] for c in columns]), r)) for i, r in enumerate(cursor.fetchall())] qbytes = 'upstream2' try: - qbytes = cursor.data_scanned_in_bytes() + cursor._poll() + qbytes = cursor._data_scanned_in_bytes except AttributeError as e: debug("Athena Upstream can't get data_scanned_in_bytes: %s", e) data = {'columns': columns, 'rows': rows, 'data_scanned': qbytes } From a1726c612dee3e753fbf70b4a25e78f3f75cd62c Mon Sep 17 00:00:00 2001 From: Alison Date: Wed, 19 Jul 2017 13:10:41 -0500 Subject: [PATCH 241/243] add data_scanned to active data, mysql, and presto --- redash/query_runner/activedata.py | 1 + redash/query_runner/mysql.py | 2 +- redash/query_runner/presto.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/redash/query_runner/activedata.py b/redash/query_runner/activedata.py index 73a10fe97a..e21e559d1c 100644 --- a/redash/query_runner/activedata.py +++ b/redash/query_runner/activedata.py @@ -109,6 +109,7 @@ def run_query(self, annotated_query, user): return None, cause output = normalize(response) + output.update({'data_scanned':'N/A'}) json_data = json.dumps(output, cls=JSONEncoder) return json_data, None diff --git a/redash/query_runner/mysql.py b/redash/query_runner/mysql.py index 58fd32b4ee..ab05f81607 100644 --- a/redash/query_runner/mysql.py +++ b/redash/query_runner/mysql.py @@ -157,7 +157,7 @@ def run_query(self, query, user): columns = self.fetch_columns([(i[0], types_map.get(i[1], None)) for i in cursor.description]) rows = [dict(zip((c['name'] for c in columns), row)) for row in data] - data = {'columns': columns, 'rows': rows} + data = {'columns': columns, 'rows': rows, 'data_scanned': 'N/A'} json_data = json.dumps(data, cls=JSONEncoder) error = None else: diff --git a/redash/query_runner/presto.py b/redash/query_runner/presto.py index 1326f2beae..4e6d84dd34 100644 --- a/redash/query_runner/presto.py +++ b/redash/query_runner/presto.py @@ -116,7 +116,7 @@ def run_query(self, query, user): column_tuples = [(i[0], PRESTO_TYPES_MAPPING.get(i[1], None)) for i in cursor.description] columns = self.fetch_columns(column_tuples) rows = [dict(zip(([c['name'] for c in columns]), r)) for i, r in enumerate(cursor.fetchall())] - data = {'columns': columns, 'rows': rows} + data = {'columns': columns, 'rows': rows, 'data_scanned': 'N/A'} json_data = json.dumps(data, cls=JSONEncoder) error = None except DatabaseError, db: From e7ddfcdfb3b5901bf0342032db613cbf2c792de7 Mon Sep 17 00:00:00 2001 From: Alison Date: Thu, 20 Jul 2017 09:31:18 -0500 Subject: [PATCH 242/243] fix bug #139 --- client/app/pages/alerts-list/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/app/pages/alerts-list/index.js b/client/app/pages/alerts-list/index.js index ac69bda693..8619ac5b91 100644 --- a/client/app/pages/alerts-list/index.js +++ b/client/app/pages/alerts-list/index.js @@ -14,6 +14,7 @@ class AlertsListCtrl { this.alerts = new Paginator([], { itemsPerPage: 20 }); Alert.query((alerts) => { this.alerts.updateRows(alerts.map(alert => ({ + id: alert.id, name: alert.name, state: alert.state, class: stateClass[alert.state], From 90c3b9f0b199c8ee223b94fa64423c0a203c312e Mon Sep 17 00:00:00 2001 From: Alison Date: Thu, 20 Jul 2017 09:32:14 -0500 Subject: [PATCH 243/243] add bigquery. sheets, and url for data_scanned --- client/app/pages/queries/query.html | 2 +- redash/query_runner/big_query.py | 1 + redash/query_runner/google_spreadsheets.py | 1 + redash/query_runner/url.py | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/client/app/pages/queries/query.html b/client/app/pages/queries/query.html index 285e90bf64..d42447341c 100644 --- a/client/app/pages/queries/query.html +++ b/client/app/pages/queries/query.html @@ -204,7 +204,7 @@

Data Scanned - {{ queryResult.query_result.data_scanned }} + {{ queryResult.query_result.data_scanned }} diff --git a/redash/query_runner/big_query.py b/redash/query_runner/big_query.py index 530093490d..c1f941a52e 100644 --- a/redash/query_runner/big_query.py +++ b/redash/query_runner/big_query.py @@ -248,6 +248,7 @@ def run_query(self, query, user): return None, "Larger than %d MBytes will be processed (%f MBytes)" % (limitMB, processedMB) data = self._get_query_result(jobs, query) + data.update({'data_scanned':'N/A'}) error = None json_data = json.dumps(data, cls=JSONEncoder) diff --git a/redash/query_runner/google_spreadsheets.py b/redash/query_runner/google_spreadsheets.py index 6c13044e0f..5caf179e01 100644 --- a/redash/query_runner/google_spreadsheets.py +++ b/redash/query_runner/google_spreadsheets.py @@ -205,6 +205,7 @@ def run_query(self, query, user): spreadsheet = spreadsheet_service.open_by_key(key) data = parse_spreadsheet(spreadsheet, worksheet_num) + data.update({'data_scanned': 'N/A'}) json_data = json.dumps(data, cls=JSONEncoder) error = None diff --git a/redash/query_runner/url.py b/redash/query_runner/url.py index 5da7659390..ac4bdd886f 100644 --- a/redash/query_runner/url.py +++ b/redash/query_runner/url.py @@ -48,6 +48,7 @@ def run_query(self, query, user): response = requests.get(url) response.raise_for_status() + response.update({'data_scanned':'N/A'}) json_data = response.content.strip() if not json_data:

NameCreated ByStateCreated AtName Created By State Created By
{{row.name}}{{row.user.name}}{{row.created_by}} {{row.state | uppercase}} since