From a1261d7f9ad3ab52f4ad63cdf0d6d99dd5709c0f Mon Sep 17 00:00:00 2001 From: Ville Brofeldt <33317356+villebro@users.noreply.github.com> Date: Mon, 5 Aug 2019 17:08:05 +0300 Subject: [PATCH 01/40] Bump sqlparse to 0.3.0 (#7973) * Black * Bump sqlparse to 0.3.0 * Convert str.format() to f-string --- requirements.txt | 2 +- setup.py | 2 +- superset/sql_parse.py | 14 ++++++-------- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/requirements.txt b/requirements.txt index 38d4922fd663d..d1454eb2bc4a5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -75,7 +75,7 @@ simplejson==3.16.0 six==1.12.0 # via bleach, cryptography, flask-jwt-extended, flask-talisman, isodate, jsonschema, pathlib2, polyline, prison, pydruid, pyrsistent, python-dateutil, sqlalchemy-utils, wtforms-json sqlalchemy-utils==0.34.1 sqlalchemy==1.3.6 -sqlparse==0.2.4 +sqlparse==0.3.0 urllib3==1.25.3 # via requests, selenium vine==1.3.0 # via amqp, celery webencodings==0.5.1 # via bleach diff --git a/setup.py b/setup.py index 11712c919d482..a0c02ff3566aa 100644 --- a/setup.py +++ b/setup.py @@ -98,7 +98,7 @@ def get_git_sha(): "simplejson>=3.15.0", "sqlalchemy>=1.3.5,<2.0", "sqlalchemy-utils>=0.33.2", - "sqlparse<0.3", + "sqlparse>=0.3.0,<0.4", "wtforms-json", ], extras_require={ diff --git a/superset/sql_parse.py b/superset/sql_parse.py index d68ac5ea4cfd2..0619ba17f096f 100644 --- a/superset/sql_parse.py +++ b/superset/sql_parse.py @@ -195,21 +195,19 @@ def get_query_with_new_limit(self, new_limit): if not self._limit: return f"{self.stripped()}\nLIMIT {new_limit}" limit_pos = None - tokens = self._parsed[0].tokens + statement = self._parsed[0] # Add all items to before_str until there is a limit - for pos, item in enumerate(tokens): + for pos, item in enumerate(statement.tokens): if item.ttype in Keyword and item.value.lower() == "limit": limit_pos = pos break - limit = tokens[limit_pos + 2] + _, limit = statement.token_next(idx=limit_pos) if limit.ttype == sqlparse.tokens.Literal.Number.Integer: - tokens[limit_pos + 2].value = new_limit + limit.value = new_limit elif limit.is_group: - tokens[limit_pos + 2].value = "{}, {}".format( - next(limit.get_identifiers()), new_limit - ) + limit.value = f"{next(limit.get_identifiers())}, {new_limit}" str_res = "" - for i in tokens: + for i in statement.tokens: str_res += str(i.value) return str_res From b856666ae221df3a09d5aa5c3c9c83dd5cb5e681 Mon Sep 17 00:00:00 2001 From: Ville Brofeldt <33317356+villebro@users.noreply.github.com> Date: Mon, 5 Aug 2019 17:08:58 +0300 Subject: [PATCH 02/40] Remove collation info from MSSQL column type (#7963) --- superset/db_engine_specs/mssql.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/superset/db_engine_specs/mssql.py b/superset/db_engine_specs/mssql.py index bde1185ef0f92..17511462a0350 100644 --- a/superset/db_engine_specs/mssql.py +++ b/superset/db_engine_specs/mssql.py @@ -66,3 +66,14 @@ def get_sqla_column_type(cls, type_): if regex.match(type_): return sqla_type return None + + @classmethod + def column_datatype_to_string(cls, sqla_column_type, dialect): + datatype = super().column_datatype_to_string(sqla_column_type, dialect) + # MSSQL returns long overflowing datatype + # as in 'VARCHAR(255) COLLATE SQL_LATIN1_GENERAL_CP1_CI_AS' + # and we don't need the verbose collation type + str_cutoff = " COLLATE " + if str_cutoff in datatype: + datatype = datatype.split(str_cutoff)[0] + return datatype From f7af50c399c39e0e9ee210d0db17ce3dfa8a2630 Mon Sep 17 00:00:00 2001 From: John Bodley <4567245+john-bodley@users.noreply.github.com> Date: Mon, 5 Aug 2019 11:24:13 -0700 Subject: [PATCH 03/40] [security] Adding docstrings and type hints (#7952) --- superset/security.py | 463 ++++++++++++++++++++++++++++++++-------- superset/views/core.py | 6 +- tests/security_tests.py | 26 +-- 3 files changed, 394 insertions(+), 101 deletions(-) diff --git a/superset/security.py b/superset/security.py index f6d1354f1678b..88adc82d67dcd 100644 --- a/superset/security.py +++ b/superset/security.py @@ -17,9 +17,10 @@ # pylint: disable=C,R,W """A set of constants and methods to manage permissions and security""" import logging -from typing import List +from typing import Callable, List, Optional, Set, Tuple, TYPE_CHECKING, Union from flask import current_app, g +from flask_appbuilder import Model from flask_appbuilder.security.sqla import models as ab_models from flask_appbuilder.security.sqla.manager import SecurityManager from flask_appbuilder.security.views import ( @@ -30,11 +31,17 @@ ) from flask_appbuilder.widgets import ListWidget from sqlalchemy import or_ +from sqlalchemy.engine.base import Connection +from sqlalchemy.orm.mapper import Mapper from superset import sql_parse from superset.connectors.connector_registry import ConnectorRegistry from superset.exceptions import SupersetSecurityException -from superset.utils.core import DatasourceName + +if TYPE_CHECKING: + from superset.models.core import Database, BaseDatasource + +from superset.utils.core import DatasourceName # noqa: I202 class SupersetSecurityListWidget(ListWidget): @@ -124,12 +131,35 @@ class SupersetSecurityManager(SecurityManager): ACCESSIBLE_PERMS = {"can_userinfo"} - def get_schema_perm(self, database, schema): + def get_schema_perm( + self, database: Union["Database", str], schema: Optional[str] = None + ) -> Optional[str]: + """ + Return the database specific schema permission. + + :param database: The Superset database or database name + :param schema: The Superset schema name + :return: The database specific schema permission + """ + if schema: - return "[{}].[{}]".format(database, schema) + return f"[{database}].[{schema}]" + + return None + + def can_access(self, permission_name: str, view_name: str) -> bool: + """ + Return True if the user can access the FAB permission/view, False + otherwise. + + Note this method adds protection from has_access failing from missing + permission/view entries. + + :param permission_name: The FAB permission name + :param view_name: The FAB view-menu name + :returns: Whether the use can access the FAB permission/view + """ - def can_access(self, permission_name, view_name): - """Protecting from has_access failing from missing perms/view""" user = g.user if user.is_anonymous: return self.is_item_public(permission_name, view_name) @@ -137,100 +167,223 @@ def can_access(self, permission_name, view_name): def can_only_access_owned_queries(self) -> bool: """ - can_access check for custom can_only_access_owned_queries permissions. + Return True if the user can only access owned queries, False otherwise. - :returns: True if current user can access custom permissions + :returns: Whether the use can only access owned queries """ return self.can_access( "can_only_access_owned_queries", "can_only_access_owned_queries" ) - def all_datasource_access(self): + def all_datasource_access(self) -> bool: + """ + Return True if the user can access all Superset datasources, False otherwise. + + :returns: Whether the user can access all Superset datasources + """ + return self.can_access("all_datasource_access", "all_datasource_access") - def all_database_access(self): + def all_database_access(self) -> bool: + """ + Return True if the user can access all Superset databases, False otherwise. + + :returns: Whether the user can access all Superset databases + """ + return self.can_access("all_database_access", "all_database_access") - def database_access(self, database): + def database_access(self, database: "Database") -> bool: + """ + Return True if the user can access the Superset database, False otherwise. + + :param database: The Superset database + :returns: Whether the user can access the Superset database + """ + return ( - self.all_database_access() + self.all_datasource_access() + or self.all_database_access() or self.can_access("database_access", database.perm) - or self.all_datasource_access() ) - def schema_access(self, datasource): + def schema_access(self, datasource: "BaseDatasource") -> bool: + """ + Return True if the user can access the schema associated with the Superset + datasource, False otherwise. + + Note for Druid datasources the database and schema are akin to the Druid cluster + and datasource name prefix, i.e., [schema.]datasource, respectively. + + :param datasource: The Superset datasource + :returns: Whether the user can access the datasource's schema + """ + return ( - self.database_access(datasource.database) - or self.all_datasource_access() + self.all_datasource_access() + or self.database_access(datasource.database) or self.can_access("schema_access", datasource.schema_perm) ) - def datasource_access(self, datasource): + def datasource_access(self, datasource: "BaseDatasource") -> bool: + """ + Return True if the user can access the Superset datasource, False otherwise. + + :param datasource: The Superset datasource + :returns: Whether the use can access the Superset datasource + """ + return self.schema_access(datasource) or self.can_access( "datasource_access", datasource.perm ) - def get_datasource_access_error_msg(self, datasource): - return """This endpoint requires the datasource {}, database or - `all_datasource_access` permission""".format( - datasource.name - ) + def get_datasource_access_error_msg(self, datasource: "BaseDatasource") -> str: + """ + Return the error message for the denied Superset datasource. + + :param datasource: The denied Superset datasource + :returns: The error message + """ + + return f"""This endpoint requires the datasource {datasource.name}, database or + `all_datasource_access` permission""" + + def get_datasource_access_link(self, datasource: "BaseDatasource") -> Optional[str]: + """ + Return the link for the denied Superset datasource. + + :param datasource: The denied Superset datasource + :returns: The access URL + """ - def get_datasource_access_link(self, datasource): from superset import conf return conf.get("PERMISSION_INSTRUCTIONS_LINK") - def get_table_access_error_msg(self, table_name): - return """You need access to the following tables: {}, all database access or - `all_datasource_access` permission""".format( - table_name - ) + def get_table_access_error_msg(self, tables: List[str]) -> str: + """ + Return the error message for the denied SQL tables. + + Note the table names conform to the [[cluster.]schema.]table construct. + + :param tables: The list of denied SQL table names + :returns: The error message + """ + + return f"""You need access to the following tables: {", ".join(tables)}, all + database access or `all_datasource_access` permission""" + + def get_table_access_link(self, tables: List[str]) -> Optional[str]: + """ + Return the access link for the denied SQL tables. + + Note the table names conform to the [[cluster.]schema.]table construct. + + :param tables: The list of denied SQL table names + :returns: The access URL + """ - def get_table_access_link(self, tables): from superset import conf return conf.get("PERMISSION_INSTRUCTIONS_LINK") - def datasource_access_by_name(self, database, datasource_name, schema=None): + def _datasource_access_by_name( + self, database: "Database", table_name: str, schema: str = None + ) -> bool: + """ + Return True if the user can access the SQL table, False otherwise. + + :param database: The SQL database + :param table_name: The SQL table name + :param schema: The Superset schema + :returns: Whether the use can access the SQL table + """ + from superset import db if self.database_access(database) or self.all_datasource_access(): return True schema_perm = self.get_schema_perm(database, schema) - if schema and self.can_access("schema_access", schema_perm): + if schema_perm and self.can_access("schema_access", schema_perm): return True datasources = ConnectorRegistry.query_datasources_by_name( - db.session, database, datasource_name, schema=schema + db.session, database, table_name, schema=schema ) for datasource in datasources: if self.can_access("datasource_access", datasource.perm): return True return False - def get_schema_and_table(self, table_in_query, schema): + def _get_schema_and_table( + self, table_in_query: str, schema: str + ) -> Tuple[str, str]: + """ + Return the SQL schema/table tuple associated with the table extracted from the + SQL query. + + Note the table name conforms to the [[cluster.]schema.]table construct. + + :param table_in_query: The SQL table name + :param schema: The fallback SQL schema if not present in the table name + :returns: The SQL schema/table tuple + """ + table_name_pieces = table_in_query.split(".") if len(table_name_pieces) == 3: - return tuple(table_name_pieces[1:]) + return tuple(table_name_pieces[1:]) # noqa: T484 elif len(table_name_pieces) == 2: - return tuple(table_name_pieces) + return tuple(table_name_pieces) # noqa: T484 return (schema, table_name_pieces[0]) - def datasource_access_by_fullname(self, database, table_in_query, schema): - table_schema, table_name = self.get_schema_and_table(table_in_query, schema) - return self.datasource_access_by_name(database, table_name, schema=table_schema) + def _datasource_access_by_fullname( + self, database: "Database", table_in_query: str, schema: str + ) -> bool: + """ + Return True if the user can access the table extracted from the SQL query, False + otherwise. + + Note the table name conforms to the [[cluster.]schema.]table construct. + + :param database: The Superset database + :param table_in_query: The SQL table name + :param schema: The fallback SQL schema, i.e., if not present in the table name + :returns: Whether the user can access the SQL table + """ + + table_schema, table_name = self._get_schema_and_table(table_in_query, schema) + return self._datasource_access_by_name( + database, table_name, schema=table_schema + ) + + def rejected_tables(self, sql: str, database: "Database", schema: str) -> List[str]: + """ + Return the list of rejected SQL table names. + + Note the rejected table names conform to the [[cluster.]schema.]table construct. + + :param sql: The SQL statement + :param database: The SQL database + :param schema: The SQL database schema + :returns: The rejected table names + """ - def rejected_datasources(self, sql, database, schema): superset_query = sql_parse.ParsedQuery(sql) + return [ t for t in superset_query.tables - if not self.datasource_access_by_fullname(database, t, schema) + if not self._datasource_access_by_fullname(database, t, schema) ] - def user_datasource_perms(self): + def _user_datasource_perms(self) -> Set[str]: + """ + Return the set of FAB permission view-menu names the user can access. + + :returns: The set of FAB permission view-menu names + """ + datasource_perms = set() for r in g.user.roles: for perm in r.permissions: @@ -238,7 +391,18 @@ def user_datasource_perms(self): datasource_perms.add(perm.view_menu.name) return datasource_perms - def schemas_accessible_by_user(self, database, schemas, hierarchical=True): + def schemas_accessible_by_user( + self, database: "Database", schemas: List[str], hierarchical: bool = True + ) -> List[str]: + """ + Return the sorted list of SQL schemas accessible by the user. + + :param database: The SQL database + :param schemas: The list of eligible SQL schemas + :param hierarchical: Whether to check using the hierarchical permission logic + :returns: The list of accessible SQL schemas + """ + from superset import db from superset.connectors.sqla.models import SqlaTable @@ -250,10 +414,10 @@ def schemas_accessible_by_user(self, database, schemas, hierarchical=True): subset = set() for schema in schemas: schema_perm = self.get_schema_perm(database, schema) - if self.can_access("schema_access", schema_perm): + if schema_perm and self.can_access("schema_access", schema_perm): subset.add(schema) - perms = self.user_datasource_perms() + perms = self._user_datasource_perms() if perms: tables = ( db.session.query(SqlaTable) @@ -266,8 +430,20 @@ def schemas_accessible_by_user(self, database, schemas, hierarchical=True): return sorted(list(subset)) def get_datasources_accessible_by_user( - self, database, datasource_names: List[DatasourceName], schema: str = None + self, + database: "Database", + datasource_names: List[DatasourceName], + schema: Optional[str] = None, ) -> List[DatasourceName]: + """ + Return the list of SQL tables accessible by the user. + + :param database: The SQL database + :param datasource_names: The list of eligible SQL tables w/ schema + :param schema: The fallback SQL schema if not present in the table name + :returns: The list of accessible SQL tables w/ schema + """ + from superset import db if self.database_access(database) or self.all_datasource_access(): @@ -275,10 +451,10 @@ def get_datasources_accessible_by_user( if schema: schema_perm = self.get_schema_perm(database, schema) - if self.can_access("schema_access", schema_perm): + if schema_perm and self.can_access("schema_access", schema_perm): return datasource_names - user_perms = self.user_datasource_perms() + user_perms = self._user_datasource_perms() user_datasources = ConnectorRegistry.query_datasources_by_permissions( db.session, database, user_perms ) @@ -289,26 +465,48 @@ def get_datasources_accessible_by_user( full_names = {d.full_name for d in user_datasources} return [d for d in datasource_names if d in full_names] - def merge_perm(self, permission_name, view_menu_name): + def merge_perm(self, permission_name: str, view_menu_name: str) -> None: + """ + Add the FAB permission/view-menu. + + :param permission_name: The FAB permission name + :param view_menu_names: The FAB view-menu name + :see: SecurityManager.add_permission_view_menu + """ + logging.warning( "This method 'merge_perm' is deprecated use add_permission_view_menu" ) self.add_permission_view_menu(permission_name, view_menu_name) - def is_user_defined_permission(self, perm): + def _is_user_defined_permission(self, perm: Model) -> bool: + """ + Return True if the FAB permission is user defined, False otherwise. + + :param perm: The FAB permission + :returns: Whether the FAB permission is user defined + """ + return perm.permission.name in self.OBJECT_SPEC_PERMISSIONS - def create_custom_permissions(self): - # Global perms + def create_custom_permissions(self) -> None: + """ + Create custom FAB permissions. + """ + self.add_permission_view_menu("all_datasource_access", "all_datasource_access") self.add_permission_view_menu("all_database_access", "all_database_access") self.add_permission_view_menu( "can_only_access_owned_queries", "can_only_access_owned_queries" ) - def create_missing_perms(self): - """Creates missing perms for datasources, schemas and metrics""" + def create_missing_perms(self) -> None: + """ + Creates missing FAB permissions for datasources, schemas and metrics. + """ + from superset import db + from superset.connectors.base.models import BaseMetric from superset.models import core as models logging.info("Fetching a set of all perms to lookup which ones are missing") @@ -334,7 +532,7 @@ def merge_pv(view_menu, perm): merge_pv("database_access", database.perm) logging.info("Creating missing metrics permissions") - metrics = [] + metrics: List[BaseMetric] = [] for datasource_class in ConnectorRegistry.sources.values(): metrics += list(db.session.query(datasource_class.metric_class).all()) @@ -342,14 +540,17 @@ def merge_pv(view_menu, perm): if metric.is_restricted: merge_pv("metric_access", metric.perm) - def clean_perms(self): - """FAB leaves faulty permissions that need to be cleaned up""" + def clean_perms(self) -> None: + """ + Clean up the FAB faulty permissions. + """ + logging.info("Cleaning faulty perms") sesh = self.get_session pvms = sesh.query(ab_models.PermissionView).filter( or_( - ab_models.PermissionView.permission == None, # NOQA - ab_models.PermissionView.view_menu == None, # NOQA + ab_models.PermissionView.permission == None, # noqa + ab_models.PermissionView.view_menu == None, # noqa ) ) deleted_count = pvms.delete() @@ -357,8 +558,11 @@ def clean_perms(self): if deleted_count: logging.info("Deleted {} faulty permissions".format(deleted_count)) - def sync_role_definitions(self): - """Inits the Superset application with security roles and such""" + def sync_role_definitions(self) -> None: + """ + Initialize the Superset application with security roles and such. + """ + from superset import conf logging.info("Syncing role definition") @@ -366,14 +570,14 @@ def sync_role_definitions(self): self.create_custom_permissions() # Creating default roles - self.set_role("Admin", self.is_admin_pvm) - self.set_role("Alpha", self.is_alpha_pvm) - self.set_role("Gamma", self.is_gamma_pvm) - self.set_role("granter", self.is_granter_pvm) - self.set_role("sql_lab", self.is_sql_lab_pvm) + self.set_role("Admin", self._is_admin_pvm) + self.set_role("Alpha", self._is_alpha_pvm) + self.set_role("Gamma", self._is_gamma_pvm) + self.set_role("granter", self._is_granter_pvm) + self.set_role("sql_lab", self._is_sql_lab_pvm) if conf.get("PUBLIC_ROLE_LIKE_GAMMA", False): - self.set_role("Public", self.is_gamma_pvm) + self.set_role("Public", self._is_gamma_pvm) self.create_missing_perms() @@ -381,7 +585,14 @@ def sync_role_definitions(self): self.get_session.commit() self.clean_perms() - def set_role(self, role_name, pvm_check): + def set_role(self, role_name: str, pvm_check: Callable) -> None: + """ + Set the FAB permission/views for the role. + + :param role_name: The FAB role name + :param pvm_check: The FAB permission/view check + """ + logging.info("Syncing {} perms".format(role_name)) sesh = self.get_session pvms = sesh.query(ab_models.PermissionView).all() @@ -392,8 +603,17 @@ def set_role(self, role_name, pvm_check): sesh.merge(role) sesh.commit() - def is_admin_only(self, pvm): - # not readonly operations on read only model views allowed only for admins + def _is_admin_only(self, pvm: Model) -> bool: + """ + Return True if the FAB permission/view is accessible to only Admin users, + False otherwise. + + Note readonly operations on read only model views are allowed only for admins. + + :param pvm: The FAB permission/view + :returns: Whether the FAB object is accessible to only Admin users + """ + if ( pvm.view_menu.name in self.READ_ONLY_MODEL_VIEWS and pvm.permission.name not in self.READ_ONLY_PERMISSION @@ -404,7 +624,15 @@ def is_admin_only(self, pvm): or pvm.permission.name in self.ADMIN_ONLY_PERMISSIONS ) - def is_alpha_only(self, pvm): + def _is_alpha_only(self, pvm: PermissionModelView) -> bool: + """ + Return True if the FAB permission/view is accessible to only Alpha users, + False otherwise. + + :param pvm: The FAB permission/view + :returns: Whether the FAB object is accessible to only Alpha users + """ + if ( pvm.view_menu.name in self.GAMMA_READ_ONLY_MODEL_VIEWS and pvm.permission.name not in self.READ_ONLY_PERMISSION @@ -415,25 +643,65 @@ def is_alpha_only(self, pvm): or pvm.permission.name in self.ALPHA_ONLY_PERMISSIONS ) - def is_accessible_to_all(self, pvm): + def _is_accessible_to_all(self, pvm: PermissionModelView) -> bool: + """ + Return True if the FAB permission/view is accessible to all, False + otherwise. + + :param pvm: The FAB permission/view + :returns: Whether the FAB object is accessible to all users + """ + return pvm.permission.name in self.ACCESSIBLE_PERMS - def is_admin_pvm(self, pvm): - return not self.is_user_defined_permission(pvm) + def _is_admin_pvm(self, pvm: PermissionModelView) -> bool: + """ + Return True if the FAB permission/view is Admin user related, False + otherwise. + + :param pvm: The FAB permission/view + :returns: Whether the FAB object is Admin related + """ + + return not self._is_user_defined_permission(pvm) + + def _is_alpha_pvm(self, pvm: PermissionModelView) -> bool: + """ + Return True if the FAB permission/view is Alpha user related, False + otherwise. + + :param pvm: The FAB permission/view + :returns: Whether the FAB object is Alpha related + """ - def is_alpha_pvm(self, pvm): return not ( - self.is_user_defined_permission(pvm) or self.is_admin_only(pvm) - ) or self.is_accessible_to_all(pvm) + self._is_user_defined_permission(pvm) or self._is_admin_only(pvm) + ) or self._is_accessible_to_all(pvm) + + def _is_gamma_pvm(self, pvm: PermissionModelView) -> bool: + """ + Return True if the FAB permission/view is Gamma user related, False + otherwise. + + :param pvm: The FAB permission/view + :returns: Whether the FAB object is Gamma related + """ - def is_gamma_pvm(self, pvm): return not ( - self.is_user_defined_permission(pvm) - or self.is_admin_only(pvm) - or self.is_alpha_only(pvm) - ) or self.is_accessible_to_all(pvm) + self._is_user_defined_permission(pvm) + or self._is_admin_only(pvm) + or self._is_alpha_only(pvm) + ) or self._is_accessible_to_all(pvm) + + def _is_sql_lab_pvm(self, pvm: PermissionModelView) -> bool: + """ + Return True if the FAB permission/view is SQL Lab related, False + otherwise. + + :param pvm: The FAB permission/view + :returns: Whether the FAB object is SQL Lab related + """ - def is_sql_lab_pvm(self, pvm): return ( pvm.view_menu.name in {"SQL Lab", "SQL Editor", "Query Search", "Saved Queries"} @@ -451,10 +719,28 @@ def is_sql_lab_pvm(self, pvm): ) ) - def is_granter_pvm(self, pvm): + def _is_granter_pvm(self, pvm: PermissionModelView) -> bool: + """ + Return True if the user can grant the FAB permission/view, False + otherwise. + + :param pvm: The FAB permission/view + :returns: Whether the user can grant the FAB permission/view + """ + return pvm.permission.name in {"can_override_role_permissions", "can_approve"} - def set_perm(self, mapper, connection, target): # noqa + def set_perm( + self, mapper: Mapper, connection: Connection, target: "BaseDatasource" + ) -> None: + """ + Set the datasource permissions. + + :param mapper: The table mappper + :param connection: The DB-API connection + :param target: The mapped instance being persisted + """ + if target.perm != target.get_perm(): link_table = target.__table__ connection.execute( @@ -497,7 +783,14 @@ def set_perm(self, mapper, connection, target): # noqa ) ) - def assert_datasource_permission(self, datasource): + def assert_datasource_permission(self, datasource: "BaseDatasource") -> None: + """ + Assert the the user has permission to access the Superset datasource. + + :param datasource: The Superset datasource + :rasies SupersetSecurityException: If the user does not have permission + """ + if not self.datasource_access(datasource): raise SupersetSecurityException( self.get_datasource_access_error_msg(datasource), diff --git a/superset/views/core.py b/superset/views/core.py index eaf78915461e0..55f5a2b0bfd88 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -2399,7 +2399,7 @@ def results(self, key): ) query = db.session.query(Query).filter_by(results_key=key).one() - rejected_tables = security_manager.rejected_datasources( + rejected_tables = security_manager.rejected_tables( query.sql, query.database, query.schema ) if rejected_tables: @@ -2521,7 +2521,7 @@ def sql_json(self): if not mydb: json_error_response("Database with id {} is missing.".format(database_id)) - rejected_tables = security_manager.rejected_datasources(sql, mydb, schema) + rejected_tables = security_manager.rejected_tables(sql, mydb, schema) if rejected_tables: return json_error_response( security_manager.get_table_access_error_msg(rejected_tables), @@ -2645,7 +2645,7 @@ def csv(self, client_id): logging.info("Exporting CSV file [{}]".format(client_id)) query = db.session.query(Query).filter_by(client_id=client_id).one() - rejected_tables = security_manager.rejected_datasources( + rejected_tables = security_manager.rejected_tables( query.sql, query.database, query.schema ) if rejected_tables: diff --git a/tests/security_tests.py b/tests/security_tests.py index 56c222d2bf224..f9c9ad90ebace 100644 --- a/tests/security_tests.py +++ b/tests/security_tests.py @@ -113,12 +113,12 @@ def assert_can_admin(self, perm_set): def test_is_admin_only(self): self.assertFalse( - security_manager.is_admin_only( + security_manager._is_admin_only( security_manager.find_permission_view_menu("can_show", "TableModelView") ) ) self.assertFalse( - security_manager.is_admin_only( + security_manager._is_admin_only( security_manager.find_permission_view_menu( "all_datasource_access", "all_datasource_access" ) @@ -126,27 +126,27 @@ def test_is_admin_only(self): ) self.assertTrue( - security_manager.is_admin_only( + security_manager._is_admin_only( security_manager.find_permission_view_menu("can_delete", "DatabaseView") ) ) if app.config.get("ENABLE_ACCESS_REQUEST"): self.assertTrue( - security_manager.is_admin_only( + security_manager._is_admin_only( security_manager.find_permission_view_menu( "can_show", "AccessRequestsModelView" ) ) ) self.assertTrue( - security_manager.is_admin_only( + security_manager._is_admin_only( security_manager.find_permission_view_menu( "can_edit", "UserDBModelView" ) ) ) self.assertTrue( - security_manager.is_admin_only( + security_manager._is_admin_only( security_manager.find_permission_view_menu("can_approve", "Superset") ) ) @@ -156,41 +156,41 @@ def test_is_admin_only(self): ) def test_is_alpha_only(self): self.assertFalse( - security_manager.is_alpha_only( + security_manager._is_alpha_only( security_manager.find_permission_view_menu("can_show", "TableModelView") ) ) self.assertTrue( - security_manager.is_alpha_only( + security_manager._is_alpha_only( security_manager.find_permission_view_menu( "muldelete", "TableModelView" ) ) ) self.assertTrue( - security_manager.is_alpha_only( + security_manager._is_alpha_only( security_manager.find_permission_view_menu( "all_datasource_access", "all_datasource_access" ) ) ) self.assertTrue( - security_manager.is_alpha_only( + security_manager._is_alpha_only( security_manager.find_permission_view_menu( "can_edit", "SqlMetricInlineView" ) ) ) self.assertTrue( - security_manager.is_alpha_only( + security_manager._is_alpha_only( security_manager.find_permission_view_menu( "can_delete", "DruidMetricInlineView" ) ) ) self.assertTrue( - security_manager.is_alpha_only( + security_manager._is_alpha_only( security_manager.find_permission_view_menu( "all_database_access", "all_database_access" ) @@ -199,7 +199,7 @@ def test_is_alpha_only(self): def test_is_gamma_pvm(self): self.assertTrue( - security_manager.is_gamma_pvm( + security_manager._is_gamma_pvm( security_manager.find_permission_view_menu("can_show", "TableModelView") ) ) From d58dbad076f61cfd05fa8280ad13442d29360873 Mon Sep 17 00:00:00 2001 From: Erik Ritter Date: Mon, 5 Aug 2019 11:56:56 -0700 Subject: [PATCH 04/40] [DB Engine] Support old and new Presto syntax (#7977) --- docs/installation.rst | 12 ++++++++++++ superset/db_engine_specs/presto.py | 27 +++++++++++++++++++++------ superset/views/database/__init__.py | 4 +++- tests/db_engine_specs_test.py | 2 ++ 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 68bc5ce06b189..919d6ed77fa53 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -647,6 +647,18 @@ Note that you can run the ``superset refresh_druid`` command to refresh the metadata from your Druid cluster(s) +Presto +------ + +By default Superset assumes the most recent version of Presto is being used when +querying the datasource. If you're using an older version of presto, you can configure +it in the ``extra`` parameter:: + + { + "version": "0.123" + } + + CORS ---- diff --git a/superset/db_engine_specs/presto.py b/superset/db_engine_specs/presto.py index 4f4b03ef7793b..6708b0671a2c9 100644 --- a/superset/db_engine_specs/presto.py +++ b/superset/db_engine_specs/presto.py @@ -16,6 +16,7 @@ # under the License. # pylint: disable=C,R,W from collections import OrderedDict +from distutils.version import StrictVersion import logging import re import textwrap @@ -797,7 +798,7 @@ def extra_table_metadata(cls, database, table_name, schema_name): full_table_name = table_name if schema_name and "." not in table_name: full_table_name = "{}.{}".format(schema_name, table_name) - pql = cls._partition_query(full_table_name) + pql = cls._partition_query(full_table_name, database) col_names, latest_parts = cls.latest_partition( table_name, schema_name, database, show_first=True ) @@ -872,7 +873,9 @@ def extract_error_message(cls, e): return utils.error_msg_from_exception(e) @classmethod - def _partition_query(cls, table_name, limit=0, order_by=None, filters=None): + def _partition_query( + cls, table_name, database, limit=0, order_by=None, filters=None + ): """Returns a partition query :param table_name: the name of the table to get partitions from @@ -900,10 +903,20 @@ def _partition_query(cls, table_name, limit=0, order_by=None, filters=None): l.append(f"{field} = '{value}'") where_clause = "WHERE " + " AND ".join(l) + presto_version = database.get_extra().get("version") + + # Partition select syntax changed in v0.199, so check here. + # Default to the new syntax if version is unset. + partition_select_clause = ( + f'SELECT * FROM "{table_name}$partitions"' + if not presto_version + or StrictVersion(presto_version) >= StrictVersion("0.199") + else f"SHOW PARTITIONS FROM {table_name}" + ) + sql = textwrap.dedent( f"""\ - SELECT * FROM "{table_name}$partitions" - + {partition_select_clause} {where_clause} {order_by_clause} {limit_clause} @@ -965,7 +978,7 @@ def latest_partition(cls, table_name, schema, database, show_first=False): ) column_names = indexes[0]["column_names"] part_fields = [(column_name, True) for column_name in column_names] - sql = cls._partition_query(table_name, 1, part_fields) + sql = cls._partition_query(table_name, database, 1, part_fields) df = database.get_df(sql, schema) return column_names, cls._latest_partition_from_df(df) @@ -1012,7 +1025,9 @@ def latest_sub_partition(cls, table_name, schema, database, **kwargs): if field not in kwargs.keys(): field_to_return = field - sql = cls._partition_query(table_name, 1, [(field_to_return, True)], kwargs) + sql = cls._partition_query( + table_name, database, 1, [(field_to_return, True)], kwargs + ) df = database.get_df(sql, schema) if df.empty: return "" diff --git a/superset/views/database/__init__.py b/superset/views/database/__init__.py index 15b0d5228fb1d..9b7c8cf891210 100644 --- a/superset/views/database/__init__.py +++ b/superset/views/database/__init__.py @@ -143,7 +143,9 @@ class DatabaseMixin: # noqa 'Specify it as **"schemas_allowed_for_csv_upload": ' '["public", "csv_upload"]**. ' "If database flavor does not support schema or any schema is allowed " - "to be accessed, just leave the list empty", + "to be accessed, just leave the list empty" + "4. the ``version`` field is a string specifying the this db's version. " + "This should be used with Presto DBs so that the syntax is correct", True, ), "impersonate_user": _( diff --git a/tests/db_engine_specs_test.py b/tests/db_engine_specs_test.py index c9c398f8de304..a70c8cdb2ad87 100644 --- a/tests/db_engine_specs_test.py +++ b/tests/db_engine_specs_test.py @@ -766,6 +766,7 @@ def test_presto_expand_data_with_complex_array_columns(self): def test_presto_extra_table_metadata(self): db = mock.Mock() db.get_indexes = mock.Mock(return_value=[{"column_names": ["ds", "hour"]}]) + db.get_extra = mock.Mock(return_value={}) df = pd.DataFrame({"ds": ["01-01-19"], "hour": [1]}) db.get_df = mock.Mock(return_value=df) result = PrestoEngineSpec.extra_table_metadata(db, "test_table", "test_schema") @@ -774,6 +775,7 @@ def test_presto_extra_table_metadata(self): def test_presto_where_latest_partition(self): db = mock.Mock() db.get_indexes = mock.Mock(return_value=[{"column_names": ["ds", "hour"]}]) + db.get_extra = mock.Mock(return_value={}) df = pd.DataFrame({"ds": ["01-01-19"], "hour": [1]}) db.get_df = mock.Mock(return_value=df) columns = [{"name": "ds"}, {"name": "hour"}] From fdb62e9865104f2729f13db879675a2c932bbef8 Mon Sep 17 00:00:00 2001 From: John Bodley <4567245+john-bodley@users.noreply.github.com> Date: Mon, 5 Aug 2019 15:01:33 -0700 Subject: [PATCH 05/40] [flake8] Ignoring I202 (#7988) --- superset/connectors/druid/models.py | 1 - superset/security.py | 2 +- superset/utils/core.py | 1 - tests/druid_tests.py | 1 - tox.ini | 4 ++-- 5 files changed, 3 insertions(+), 6 deletions(-) diff --git a/superset/connectors/druid/models.py b/superset/connectors/druid/models.py index 9f37237cf5893..4eb38dc9ac615 100644 --- a/superset/connectors/druid/models.py +++ b/superset/connectors/druid/models.py @@ -16,7 +16,6 @@ # under the License. # pylint: disable=C,R,W # pylint: disable=invalid-unary-operand-type -# flake8: noqa I202 from collections import OrderedDict from copy import deepcopy from datetime import datetime, timedelta diff --git a/superset/security.py b/superset/security.py index 88adc82d67dcd..bea15715baea7 100644 --- a/superset/security.py +++ b/superset/security.py @@ -41,7 +41,7 @@ if TYPE_CHECKING: from superset.models.core import Database, BaseDatasource -from superset.utils.core import DatasourceName # noqa: I202 +from superset.utils.core import DatasourceName # noqa: E402 class SupersetSecurityListWidget(ListWidget): diff --git a/superset/utils/core.py b/superset/utils/core.py index eb43e3abe97b8..69c70f575c381 100644 --- a/superset/utils/core.py +++ b/superset/utils/core.py @@ -15,7 +15,6 @@ # specific language governing permissions and limitations # under the License. # pylint: disable=C,R,W -# flake8: noqa I202 """Utility functions used across Superset""" from datetime import date, datetime, time, timedelta import decimal diff --git a/tests/druid_tests.py b/tests/druid_tests.py index e2759739132c5..d2f231ebbf7f8 100644 --- a/tests/druid_tests.py +++ b/tests/druid_tests.py @@ -14,7 +14,6 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -# flake8: noqa I202 """Unit tests for Superset""" from datetime import datetime import json diff --git a/tox.ini b/tox.ini index e79e1bbc9292f..c5d2cf8eebe3a 100644 --- a/tox.ini +++ b/tox.ini @@ -28,6 +28,7 @@ exclude = ignore = E203 E501 + I202 W503 W605 import-order-style = google @@ -55,7 +56,7 @@ whitelist_externals = [testenv:black] commands = black --check setup.py superset tests -deps = +deps = -rrequirements-dev.txt [testenv:cypress-dashboard] @@ -154,4 +155,3 @@ envlist = pylint license-check skipsdist = true - From 0c817e5fa9f015962fd7d6eac2af37201f118511 Mon Sep 17 00:00:00 2001 From: Daniel Vaz Gaspar Date: Tue, 6 Aug 2019 06:52:17 +0100 Subject: [PATCH 06/40] Bump FAB to 2.1.8 (#7986) --- requirements.txt | 11 +++-------- setup.py | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/requirements.txt b/requirements.txt index d1454eb2bc4a5..7dc8d99f3de07 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,9 +13,7 @@ babel==2.7.0 # via flask-babel billiard==3.6.0.0 # via celery bleach==3.1.0 celery==4.3.0 -certifi==2019.6.16 # via requests cffi==1.12.3 # via cryptography -chardet==3.0.4 # via requests click==6.7 colorama==0.4.1 contextlib2==0.5.5 @@ -23,7 +21,7 @@ croniter==0.3.30 cryptography==2.7 decorator==4.4.0 # via retry defusedxml==0.6.0 # via python3-openid -flask-appbuilder==2.1.7 +flask-appbuilder==2.1.8 flask-babel==0.12.2 # via flask-appbuilder flask-caching==1.7.2 flask-compress==1.4.0 @@ -72,16 +70,13 @@ pyyaml==5.1.2 retry==0.9.2 selenium==3.141.0 simplejson==3.16.0 -six==1.12.0 # via bleach, cryptography, flask-jwt-extended, flask-talisman, isodate, jsonschema, pathlib2, polyline, prison, pydruid, pyrsistent, python-dateutil, sqlalchemy-utils, wtforms-json +six==1.12.0 # via bleach, cryptography, flask-jwt-extended, flask-talisman, isodate, jsonschema, pathlib2, polyline, prison, pyrsistent, python-dateutil, sqlalchemy-utils, wtforms-json sqlalchemy-utils==0.34.1 sqlalchemy==1.3.6 sqlparse==0.3.0 -urllib3==1.25.3 # via requests, selenium +urllib3==1.25.3 # via selenium vine==1.3.0 # via amqp, celery webencodings==0.5.1 # via bleach werkzeug==0.15.5 # via flask, flask-jwt-extended wtforms-json==0.3.3 wtforms==2.2.1 # via flask-wtf, wtforms-json - -# The following packages are considered to be unsafe in a requirements file: -# setuptools==41.0.1 # via jsonschema, markdown diff --git a/setup.py b/setup.py index a0c02ff3566aa..1cfa9f2312ecc 100644 --- a/setup.py +++ b/setup.py @@ -73,7 +73,7 @@ def get_git_sha(): "croniter>=0.3.28", "cryptography>=2.4.2", "flask>=1.0.0, <2.0.0", - "flask-appbuilder>=2.1.6, <2.3.0", + "flask-appbuilder>=2.1.8, <2.3.0", "flask-caching", "flask-compress", "flask-talisman", From 8db419fe99ce3e3c940d6ba41ddae2528fd67995 Mon Sep 17 00:00:00 2001 From: Daniel Vaz Gaspar Date: Tue, 6 Aug 2019 06:52:43 +0100 Subject: [PATCH 07/40] [database] Fix, Removes the limit for the page size (#7987) Old FAB API had no limits by default, this will keep this behaviour but only for this endpoint --- superset/assets/src/components/TableSelector.jsx | 3 ++- superset/views/database/api.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/superset/assets/src/components/TableSelector.jsx b/superset/assets/src/components/TableSelector.jsx index d380241b70ed7..9ead6dd7f767e 100644 --- a/superset/assets/src/components/TableSelector.jsx +++ b/superset/assets/src/components/TableSelector.jsx @@ -218,7 +218,8 @@ export default class TableSelector extends React.PureComponent { '/api/v1/database/?q=' + '(keys:!(none),' + 'filters:!((col:expose_in_sqllab,opr:eq,value:!t)),' + - 'order_columns:database_name,order_direction:asc)' + 'order_columns:database_name,order_direction:asc,' + + 'page:0,page_size:-1)' } onChange={this.onDatabaseChange} onAsyncError={() => this.props.handleError(t('Error while fetching database list'))} diff --git a/superset/views/database/api.py b/superset/views/database/api.py index dea17ba565ed1..2bb8ea5f35ec4 100644 --- a/superset/views/database/api.py +++ b/superset/views/database/api.py @@ -50,6 +50,8 @@ class DatabaseRestApi(DatabaseMixin, ModelRestApi): "allows_subquery", "backend", ] + # Removes the local limit for the page size + max_page_size = -1 appbuilder.add_api(DatabaseRestApi) From e455f1d86a5aac783f2f3b2c1594facafb4df0ff Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Tue, 6 Aug 2019 10:41:37 -0700 Subject: [PATCH 08/40] Bump @superset-ui versions (#7989) * starting * fix: upgrade chart error * fix: address comments * fix: package lock * fix: bump typescript version --- superset/assets/package-lock.json | 537 +++++++++++--------- superset/assets/package.json | 28 +- superset/assets/src/chart/ChartRenderer.jsx | 60 +-- 3 files changed, 331 insertions(+), 294 deletions(-) diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json index 0574d8a67aa3c..875d63ca3771a 100644 --- a/superset/assets/package-lock.json +++ b/superset/assets/package-lock.json @@ -1347,7 +1347,7 @@ }, "@data-ui/event-flow": { "version": "0.0.54", - "resolved": "http://registry.npmjs.org/@data-ui/event-flow/-/event-flow-0.0.54.tgz", + "resolved": "https://registry.npmjs.org/@data-ui/event-flow/-/event-flow-0.0.54.tgz", "integrity": "sha1-uwPh/StWNCSGVbjfnTxsOKdH5l4=", "requires": { "@data-ui/forms": "0.0.50", @@ -1638,7 +1638,7 @@ }, "@data-ui/radial-chart": { "version": "0.0.54", - "resolved": "http://registry.npmjs.org/@data-ui/radial-chart/-/radial-chart-0.0.54.tgz", + "resolved": "https://registry.npmjs.org/@data-ui/radial-chart/-/radial-chart-0.0.54.tgz", "integrity": "sha1-DSiwdoHZtgJ9msI7cpJBgn1RMAE=", "requires": { "@data-ui/shared": "0.0.54", @@ -1653,7 +1653,7 @@ }, "@data-ui/shared": { "version": "0.0.54", - "resolved": "http://registry.npmjs.org/@data-ui/shared/-/shared-0.0.54.tgz", + "resolved": "https://registry.npmjs.org/@data-ui/shared/-/shared-0.0.54.tgz", "integrity": "sha1-L7DW3ukNrCC/jzwpE8aFCoIj1Zs=", "requires": { "@data-ui/theme": "0.0.48", @@ -1726,7 +1726,7 @@ }, "@data-ui/sparkline": { "version": "0.0.54", - "resolved": "http://registry.npmjs.org/@data-ui/sparkline/-/sparkline-0.0.54.tgz", + "resolved": "https://registry.npmjs.org/@data-ui/sparkline/-/sparkline-0.0.54.tgz", "integrity": "sha1-zj0WbZ4LI5oLoC84lMuejIQXHO8=", "requires": { "@data-ui/shared": "0.0.54", @@ -1812,12 +1812,12 @@ "integrity": "sha1-8hUy0T2n5sXXJcrbD1p210bzvYU=" }, "@data-ui/xy-chart": { - "version": "0.0.75", - "resolved": "https://registry.npmjs.org/@data-ui/xy-chart/-/xy-chart-0.0.75.tgz", - "integrity": "sha512-x3XaziNKuW1M2KrCjsXoiUBr43zH9oWkY8s/gJ7B94wImhj2PUpmPfaC5Pttm8mo8CBplCBQ4gz7c0/vOBG0yw==", + "version": "0.0.78", + "resolved": "https://registry.npmjs.org/@data-ui/xy-chart/-/xy-chart-0.0.78.tgz", + "integrity": "sha512-/AhEApEcb9s5VZoC+fRYNZY8hEl0+c2TdGkZ2A1nEIc10NFRoyVqaqAEvQw2wI/h9kISQL2x1wztAAyab1S7rg==", "requires": { - "@data-ui/shared": "^0.0.75", - "@data-ui/theme": "^0.0.75", + "@data-ui/shared": "^0.0.78", + "@data-ui/theme": "^0.0.78", "@vx/axis": "^0.0.168", "@vx/curve": "^0.0.165", "@vx/event": "^0.0.165", @@ -1831,7 +1831,7 @@ "@vx/scale": "^0.0.165", "@vx/shape": "^0.0.165", "@vx/stats": "^0.0.165", - "@vx/text": "0.0.179", + "@vx/text": "0.0.183", "@vx/threshold": "0.0.170", "@vx/tooltip": "^0.0.165", "@vx/voronoi": "^0.0.165", @@ -1840,11 +1840,11 @@ }, "dependencies": { "@data-ui/shared": { - "version": "0.0.75", - "resolved": "https://registry.npmjs.org/@data-ui/shared/-/shared-0.0.75.tgz", - "integrity": "sha512-zL8Mtcc2sdr1MIWJKqrf6sbRVVS+whNwQtiZkKaxqP7XNnzbRBQhZl3/1G2LqF1eeKzxebNV3jwnBVRkz7PpXQ==", + "version": "0.0.78", + "resolved": "https://registry.npmjs.org/@data-ui/shared/-/shared-0.0.78.tgz", + "integrity": "sha512-IetTiW89B7v1BqINN8+0uDnqqF06ulAcPivxgm0WeQOIVsgQdtOxTNfbaeC19reI1UTiF+xm5on0mN4F1At3XQ==", "requires": { - "@data-ui/theme": "^0.0.75", + "@data-ui/theme": "^0.0.78", "@vx/event": "^0.0.165", "@vx/group": "^0.0.165", "@vx/shape": "^0.0.168", @@ -1870,9 +1870,9 @@ } }, "@data-ui/theme": { - "version": "0.0.75", - "resolved": "https://registry.npmjs.org/@data-ui/theme/-/theme-0.0.75.tgz", - "integrity": "sha512-Kr6M/3mGMR2Mhqb4+YFSe4x40UCKdK4OpreYa553EAsS5ngpcOk6u3ZQjRyNTuZ4d+Zjk7Fvn8fQKsUqQlwG1Q==" + "version": "0.0.78", + "resolved": "https://registry.npmjs.org/@data-ui/theme/-/theme-0.0.78.tgz", + "integrity": "sha512-DyGqH2kIHONvTfg3E810PFSUPDBLgQtZUFOvAMDLrSsO9KClX4XchNzEsewQoKQK4MP/pdxuJuX/LCRglsGj6g==" }, "@vx/axis": { "version": "0.0.168", @@ -2047,6 +2047,19 @@ "prop-types": "^15.5.10" } }, + "@vx/text": { + "version": "0.0.183", + "resolved": "https://registry.npmjs.org/@vx/text/-/text-0.0.183.tgz", + "integrity": "sha512-SM97C6I2Oy3FdbjM0zb2oZ8xgPskQE3r0FdGHZgq6Dk1b3lYwuW3KqdXn598BRl3iL9jfSyR6vFN9z6NV0FFww==", + "requires": { + "@babel/core": "^7.0.0", + "babel-plugin-lodash": "^3.3.2", + "classnames": "^2.2.5", + "lodash": "^4.17.4", + "prop-types": "^15.6.2", + "reduce-css-calc": "^1.3.0" + } + }, "@vx/tooltip": { "version": "0.0.165", "resolved": "https://registry.npmjs.org/@vx/tooltip/-/tooltip-0.0.165.tgz", @@ -2518,7 +2531,7 @@ }, "@sinonjs/formatio": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", "dev": true, "requires": { @@ -2537,54 +2550,75 @@ } }, "@superset-ui/chart": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@superset-ui/chart/-/chart-0.11.3.tgz", - "integrity": "sha512-uEk3h8pgxpYGr5rEVV3BgVG4uDsVJMQlqfii0kHjvq0y3LV+ba/GyYdMgqcTfF6agcqJVWlbfPdq9znDtvhOcg==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@superset-ui/chart/-/chart-0.11.14.tgz", + "integrity": "sha512-3E6JXdt161Id9Ac13nZlgRyH26SL2WP5SNhg3UeFj8VAFJF3FDT3V0CsJOeVZmsnjRxGF6KHYGMhtsDN4KBAgA==", "requires": { "@types/react": "^16.7.17", "@types/react-loadable": "^5.4.2", + "@vx/responsive": "^0.0.189", "prop-types": "^15.6.2", + "react-error-boundary": "^1.2.5", "react-loadable": "^5.5.0", "reselect": "^4.0.0" + }, + "dependencies": { + "@vx/responsive": { + "version": "0.0.189", + "resolved": "https://registry.npmjs.org/@vx/responsive/-/responsive-0.0.189.tgz", + "integrity": "sha512-46phRmRMrcRvUgAwOCMkZdqSEyo8SdXdV3Fh1Zsdgrtzqs3Zg8xtd3UX9t0piXw0tJqeZ16JrOQAZbPxUp9jEw==", + "requires": { + "lodash": "^4.17.10", + "prop-types": "^15.6.1", + "resize-observer-polyfill": "1.5.0" + } + } } }, "@superset-ui/color": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@superset-ui/color/-/color-0.11.3.tgz", - "integrity": "sha512-1rJzimtkd7M5iRW2PDFHNGqU8EW5SQHywMygGdd+Oxk4jmlmJgpRMP1Igx//ghX+nS2c+k3VTDfYEHs7ozgyxA==", + "version": "0.11.9", + "resolved": "https://registry.npmjs.org/@superset-ui/color/-/color-0.11.9.tgz", + "integrity": "sha512-fHP2DgkVyuRKF1DBoUAKV4MW7dXH9JAJWNMwAz+x5rUMsQ1OBt7gk+nh/+6CiSQ+7lV4GfxmP+XGPBwFym6HKg==", "requires": { "@types/d3-scale": "^2.0.2", - "d3-scale": "^2.1.2" + "d3-scale": "^3.0.0" + }, + "dependencies": { + "d3-scale": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.0.1.tgz", + "integrity": "sha512-f+OsXyd0claf6ufjI52zBHyOnm6mmfFvYiGUU8UB2VumZpqCcxds5iWN1rcOACIHgw9MntTFLXmi4LBRmY4DwQ==", + "requires": { + "d3-array": "1.2.0 - 2", + "d3-format": "1", + "d3-interpolate": "1", + "d3-time": "1", + "d3-time-format": "2" + } + } } }, "@superset-ui/connection": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@superset-ui/connection/-/connection-0.11.0.tgz", - "integrity": "sha512-S5fe3DVUKehhGvhjR/jOxitKJ6W91PlB1jYRCdTrDz7/FcrQSgV3okUBnyMeQApInh2Civ/vI0ehcb4+7dNqOg==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@superset-ui/connection/-/connection-0.11.14.tgz", + "integrity": "sha512-M3FuAO00afvuksn4CAlRCJUUBMP7i1N8K3N1sas51Spknmo76Y7wbGzmJ15BPzGVT3mjkgNZZ/pu8pAct2/KeA==", "requires": { "@babel/runtime": "^7.1.2", - "whatwg-fetch": "^2.0.4" - }, - "dependencies": { - "whatwg-fetch": { - "version": "2.0.4", - "resolved": "http://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", - "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" - } + "whatwg-fetch": "^3.0.0" } }, "@superset-ui/core": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.11.0.tgz", - "integrity": "sha512-Dmq+bbsZFlJka677y0kBGgpW+HL5VVZ1RRYNXIUgoVH0+dHmUprJshhSv3i4pzgNrwq1/1VEXwqQtrGboxIcZw==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.11.10.tgz", + "integrity": "sha512-mpaYpCHjEI6QppV0G52UdwyX79aTbdROC6WNBRXy3aGLHv2DG38IYMlgiDNQ+UQa+ZYDq1BSGUYt3GucOilQCw==", "requires": { "lodash": "^4.17.11" } }, "@superset-ui/dimension": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@superset-ui/dimension/-/dimension-0.11.0.tgz", - "integrity": "sha512-o7I3tFRphK5KnOJPANTWbgB2LZCGRKUTQX667lsBEKpGFRwz4fbGHZlEIHx6o9kb1PEhc44XJ94hs7mMGr1FDg==" + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@superset-ui/dimension/-/dimension-0.11.10.tgz", + "integrity": "sha512-gOP32Ibf0FwElD8ImDbfUkw0e+Sw9x0cP2ZXlgp2n00360inq8XC4Bq62t1B6oFrMRRoeCDGzYb5pe6QrIPvSg==" }, "@superset-ui/legacy-plugin-chart-calendar": { "version": "0.10.11", @@ -2900,11 +2934,11 @@ } }, "@superset-ui/legacy-preset-chart-big-number": { - "version": "0.10.11", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.10.11.tgz", - "integrity": "sha512-rNZWvY/jYmDgHaY+VZbNbUMmJXtiwYIhZAH6X5gOa6GRF2Cu1tS96yj5DdwdeH2IJk47jGzcSaRLxRHkcyUeKw==", + "version": "0.10.17", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.10.17.tgz", + "integrity": "sha512-v2iwhEzEVAHOKxe8PWFLgTJ1PTVJsRrT81qyOZVmKthmuVBNeF7NA23/QpJh/5wlO1uLtmAk3ChrSPfwMStIWg==", "requires": { - "@data-ui/xy-chart": "^0.0.75", + "@data-ui/xy-chart": "^0.0.78", "d3-color": "^1.2.3", "prop-types": "^15.6.2", "shortid": "^2.2.14" @@ -3190,18 +3224,18 @@ } }, "@superset-ui/number-format": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@superset-ui/number-format/-/number-format-0.11.3.tgz", - "integrity": "sha512-xGQ4PTis5dNmTfcVSYJpQD/hGE00Jo9SrBVeZY3lodGDkyAPvTlpaEBOCJY3VWwfnpn1WzNDrz0+ca+pKx6eeA==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@superset-ui/number-format/-/number-format-0.11.14.tgz", + "integrity": "sha512-MP8kz8D797s2ZvoOI2040gchY3wILRU5g5dddgQMmecTZJOkX2ej6itj3S+3wmjOeQrydYPdyW4TZLQ+gvH7DA==", "requires": { "@types/d3-format": "^1.3.0", "d3-format": "^1.3.2" } }, "@superset-ui/time-format": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@superset-ui/time-format/-/time-format-0.11.3.tgz", - "integrity": "sha512-+9ybQEmnRgA4wirrPxZ1tWIfnlA6QAemgZsG3wam4Vjw2CMldrTiWmgYT3CvTzvFab3fZ3M4FSNN5N+GavZDHQ==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@superset-ui/time-format/-/time-format-0.11.14.tgz", + "integrity": "sha512-HUxPf3SE+6/kYXFFI6g9t8GlsMbAe1Pc7fLtY9Lzv26M49IR8xFnHOOa7g6AvhkIGshapkyBpCYd1jitTyZePw==", "requires": { "@types/d3-time": "^1.0.9", "@types/d3-time-format": "^2.1.0", @@ -3210,9 +3244,9 @@ } }, "@superset-ui/translation": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@superset-ui/translation/-/translation-0.11.0.tgz", - "integrity": "sha512-enL7Qw/kVfUziZrQQcNorvhJBmJBmXCxq8rFjPtsNiPE5sRgxSb8ac5SNLBgD8pW1IbJxjoXlHFoufIPeXkiCQ==", + "version": "0.11.9", + "resolved": "https://registry.npmjs.org/@superset-ui/translation/-/translation-0.11.9.tgz", + "integrity": "sha512-kkoKwFwuD0M3XVcb4xU1exILdOO+4ec7d3cS5tqsWqBvZ/TCxrUaZcsNKxJ3scJuk9fsGPTyoQbDHx2L3aO3fg==", "requires": { "jed": "^1.1.1" } @@ -3399,9 +3433,9 @@ "integrity": "sha512-iHI60IbyfQilNubmxsq4zqSjdynlmc2Q/QvH9kjzg9+CCYVVzq1O6tc7VBzSygIwnmOt07w80IG6HDQvjv3Liw==" }, "@types/webpack": { - "version": "4.4.27", - "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.4.27.tgz", - "integrity": "sha512-xSll/4UXnLQ0xjdAoTRIFxA6NPC2abJ8nHxRH6SqTymHrfGCc8er7qH0npwCP8q3VFoJh2Hjz1wH8oTjwx9/jQ==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.32.1.tgz", + "integrity": "sha512-9n38CBx9uga1FEAdTipnt0EkbKpsCJFh7xJb1LE65FFb/A6OOLFX022vYsGC1IyVCZ/GroNg9u/RMmlDxGcLIw==", "requires": { "@types/anymatch": "*", "@types/node": "*", @@ -3965,7 +3999,7 @@ }, "acorn-jsx": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", "dev": true, "requires": { @@ -3974,7 +4008,7 @@ "dependencies": { "acorn": { "version": "3.3.0", - "resolved": "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", "dev": true } @@ -4415,7 +4449,7 @@ }, "array-equal": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, @@ -4538,7 +4572,7 @@ }, "util": { "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -5028,7 +5062,7 @@ }, "babel-plugin-syntax-dynamic-import": { "version": "6.18.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", "dev": true }, @@ -5205,23 +5239,15 @@ } }, "bfj": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.1.tgz", - "integrity": "sha512-+GUNvzHR4nRyGybQc2WpNJL4MJazMuvf92ueIyA0bIkPRwhhQu3IfZQ2PSoVPpCBJfmoSdOxu5rnotfFLlvYRQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.2.tgz", + "integrity": "sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw==", "dev": true, "requires": { - "bluebird": "^3.5.1", - "check-types": "^7.3.0", - "hoopy": "^0.1.2", - "tryer": "^1.0.0" - }, - "dependencies": { - "bluebird": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", - "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", - "dev": true - } + "bluebird": "^3.5.5", + "check-types": "^8.0.3", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" } }, "big.js": { @@ -5240,6 +5266,12 @@ "integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==", "dev": true }, + "bluebird": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", + "dev": true + }, "bmp-js": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.0.3.tgz", @@ -5301,9 +5333,9 @@ "dev": true }, "bootstrap": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.0.tgz", - "integrity": "sha512-F1yTDO9OHKH0xjl03DsOe8Nu1OWBIeAugGMhy3UTIYDdbbIPesQIhCEbj+HEr6wqlwweGAlP8F3OBC6kEuhFuw==" + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz", + "integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA==" }, "bootstrap-slider": { "version": "10.4.0", @@ -5317,7 +5349,7 @@ }, "brace": { "version": "0.11.1", - "resolved": "http://registry.npmjs.org/brace/-/brace-0.11.1.tgz", + "resolved": "https://registry.npmjs.org/brace/-/brace-0.11.1.tgz", "integrity": "sha1-SJb8ydVE7vRfS7dmDbMg07N5/lg=" }, "brace-expansion": { @@ -5349,7 +5381,7 @@ }, "brfs": { "version": "1.6.1", - "resolved": "http://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz", + "resolved": "https://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz", "integrity": "sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ==", "requires": { "quote-stream": "^1.0.1", @@ -5389,7 +5421,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -5426,7 +5458,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -5512,7 +5544,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -5662,7 +5694,7 @@ }, "callsites": { "version": "0.2.0", - "resolved": "http://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", "dev": true }, @@ -5758,9 +5790,9 @@ "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" }, "check-types": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/check-types/-/check-types-7.4.0.tgz", - "integrity": "sha512-YbulWHdfP99UfZ73NcUDlNJhEIDgm9Doq9GhpyXbF+7Aegi3CVV7qqMCKTTqJxlvEvnQBp9IA+dxsGN6xK/nSg==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz", + "integrity": "sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==", "dev": true }, "cheerio": { @@ -5972,7 +6004,7 @@ }, "clean-webpack-plugin": { "version": "0.1.19", - "resolved": "http://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-0.1.19.tgz", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-0.1.19.tgz", "integrity": "sha512-M1Li5yLHECcN2MahoreuODul5LkjohJGFxLPTjl3j1ttKrF5rgjZET1SJduuqxLAuT1gAPOdkhg03qcaaU1KeA==", "dev": true, "requires": { @@ -6379,7 +6411,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -6392,7 +6424,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -6528,7 +6560,7 @@ }, "css-in-js-utils": { "version": "2.0.1", - "resolved": "http://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==", "requires": { "hyphenate-style-name": "^1.0.2", @@ -6588,7 +6620,7 @@ }, "css-select": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { @@ -7009,7 +7041,7 @@ }, "d3-geo-projection": { "version": "0.2.16", - "resolved": "http://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-0.2.16.tgz", + "resolved": "https://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-0.2.16.tgz", "integrity": "sha1-SZTs0QM92xUztsTFUoocgdzClCc=", "requires": { "brfs": "^1.3.0" @@ -7427,7 +7459,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -7457,7 +7489,7 @@ }, "dnd-core": { "version": "2.6.0", - "resolved": "http://registry.npmjs.org/dnd-core/-/dnd-core-2.6.0.tgz", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-2.6.0.tgz", "integrity": "sha1-ErrWbVh0LG5ffPKUP7aFlED4CcQ=", "requires": { "asap": "^2.0.6", @@ -7587,7 +7619,7 @@ }, "duplexer": { "version": "0.1.1", - "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, @@ -7632,9 +7664,9 @@ "dev": true }, "ejs": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", - "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.2.tgz", + "integrity": "sha512-PcW2a0tyTuPHz3tWyYqtK6r1fZ3gp+3Sop8Ph+ZYN81Ob5rwmbHEzaqs10N3BEsaGTkh/ooniXK+WwszGlc2+Q==", "dev": true }, "electron-to-chromium": { @@ -7829,7 +7861,7 @@ }, "es6-promise": { "version": "3.3.1", - "resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=" }, "es6bindall": { @@ -7875,7 +7907,7 @@ }, "eslint": { "version": "4.19.1", - "resolved": "http://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", "dev": true, "requires": { @@ -7979,7 +8011,7 @@ }, "fast-deep-equal": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", "dev": true }, @@ -8153,7 +8185,7 @@ "dependencies": { "doctrine": { "version": "1.5.0", - "resolved": "http://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", "dev": true, "requires": { @@ -8163,7 +8195,7 @@ }, "load-json-file": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, "requires": { @@ -8301,7 +8333,7 @@ }, "espree": { "version": "3.5.4", - "resolved": "http://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", "dev": true, "requires": { @@ -8479,7 +8511,7 @@ "dependencies": { "source-map": { "version": "0.5.0", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", "integrity": "sha1-D+llA6yGpa213mP05BKuSHLNvoY=", "dev": true } @@ -8525,7 +8557,7 @@ "dependencies": { "array-flatten": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, @@ -8553,7 +8585,7 @@ }, "external-editor": { "version": "2.2.0", - "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", "dev": true, "requires": { @@ -8775,7 +8807,7 @@ }, "file-loader": { "version": "1.1.11", - "resolved": "http://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", "dev": true, "requires": { @@ -8785,7 +8817,7 @@ }, "file-type": { "version": "3.9.0", - "resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" }, "fileset": { @@ -8818,7 +8850,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { @@ -9434,7 +9466,7 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "resolved": false, "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true @@ -9447,7 +9479,7 @@ }, "aproba": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true @@ -9504,14 +9536,14 @@ }, "core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, "debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "resolved": false, "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "optional": true, @@ -9528,28 +9560,28 @@ }, "delegates": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "resolved": false, "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "resolved": false, "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "resolved": false, "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, @@ -9581,7 +9613,7 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "resolved": false, "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true @@ -9598,7 +9630,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "resolved": false, "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, @@ -9608,7 +9640,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, @@ -9625,7 +9657,7 @@ }, "ini": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "resolved": false, "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true @@ -9641,7 +9673,7 @@ }, "isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "resolved": false, "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true @@ -9680,7 +9712,7 @@ }, "ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "resolved": false, "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true, "optional": true @@ -9718,7 +9750,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "resolved": false, "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, @@ -9747,7 +9779,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "resolved": false, "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, @@ -9766,7 +9798,7 @@ }, "object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "resolved": false, "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true @@ -9782,21 +9814,21 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "resolved": false, "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, @@ -9807,14 +9839,14 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "resolved": false, "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true @@ -9834,7 +9866,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": false, "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true @@ -9843,7 +9875,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, @@ -9875,14 +9907,14 @@ }, "safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "resolved": false, "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "resolved": false, "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true @@ -9896,14 +9928,14 @@ }, "set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "resolved": false, "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "resolved": false, "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true @@ -9921,7 +9953,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, @@ -9940,14 +9972,14 @@ }, "strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "resolved": false, "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": false, "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true @@ -10075,7 +10107,7 @@ }, "gettext-parser": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/gettext-parser/-/gettext-parser-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-1.1.0.tgz", "integrity": "sha1-LFpmONiTk0ubVQN9CtgstwBLJnk=", "dev": true, "requires": { @@ -10167,7 +10199,7 @@ }, "globby": { "version": "6.1.0", - "resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { @@ -10205,19 +10237,19 @@ "dev": true }, "gzip-size": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.0.0.tgz", - "integrity": "sha512-5iI7omclyqrnWw4XbXAmGhPsABkSIDQonv2K0h61lybgofWa6iZyvrI3r2zsJH4P8Nb64fFVzlvfhs0g7BBxAA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", "dev": true, "requires": { "duplexer": "^0.1.1", - "pify": "^3.0.0" + "pify": "^4.0.1" }, "dependencies": { "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true } } @@ -10434,7 +10466,7 @@ }, "hoist-non-react-statics": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", "integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs=" }, "homedir-polyfill": { @@ -10538,7 +10570,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -11286,7 +11318,7 @@ }, "is-accessor-descriptor": { "version": "0.1.6", - "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { @@ -11383,7 +11415,7 @@ }, "is-data-descriptor": { "version": "0.1.4", - "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { @@ -13020,9 +13052,9 @@ "integrity": "sha1-U+RI7J0mPmgyZkZ+lELSxaLvVII=" }, "jquery": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.1.1.tgz", - "integrity": "sha1-NHwcIcfgBBFeCk2jLOzgQfrTyKM=" + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz", + "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==" }, "js-levenshtein": { "version": "1.1.4", @@ -13419,9 +13451,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "lodash-es": { "version": "4.17.11", @@ -13485,9 +13517,9 @@ "dev": true }, "lodash.merge": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", - "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==" + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "lodash.sortby": { "version": "4.7.0", @@ -13558,7 +13590,7 @@ }, "magic-string": { "version": "0.22.5", - "resolved": "http://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==", "requires": { "vlq": "^0.2.2" @@ -13681,7 +13713,7 @@ }, "minimist": { "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "quickselect": { @@ -13739,7 +13771,7 @@ }, "mathjs": { "version": "3.20.2", - "resolved": "http://registry.npmjs.org/mathjs/-/mathjs-3.20.2.tgz", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-3.20.2.tgz", "integrity": "sha512-3f6/+uf1cUtIz1rYFz775wekl/UEDSQ3mU6xdxW7qzpvvhc2v28i3UtLsGTRB+u8OqDWoSX6Dz8gehaGFs6tCA==", "requires": { "complex.js": "2.0.4", @@ -13789,7 +13821,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, @@ -13989,7 +14021,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "minipass": { @@ -14037,9 +14069,9 @@ } }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -14067,7 +14099,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -14075,7 +14107,7 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" } } @@ -14742,7 +14774,7 @@ }, "os-tmpdir": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, @@ -14929,7 +14961,7 @@ }, "path-browserify": { "version": "0.0.0", - "resolved": "http://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", "dev": true }, @@ -14947,7 +14979,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { @@ -15088,7 +15120,7 @@ }, "po2json": { "version": "0.4.5", - "resolved": "http://registry.npmjs.org/po2json/-/po2json-0.4.5.tgz", + "resolved": "https://registry.npmjs.org/po2json/-/po2json-0.4.5.tgz", "integrity": "sha1-R7spUtoy1Yob4vJWpZjuvAt0URg=", "dev": true, "requires": { @@ -15150,7 +15182,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -17551,19 +17583,30 @@ "integrity": "sha512-dye+7rERqNf/6mDT1iwps+4Gf42420xuZgygF33uX178DxffqcyeuHbBuJ382FIcB5iP6mMZOhfW7kI0uXwb/Q==" }, "react": { - "version": "16.6.3", - "resolved": "https://registry.npmjs.org/react/-/react-16.6.3.tgz", - "integrity": "sha512-zCvmH2vbEolgKxtqXL2wmGCUxUyNheYn/C+PD1YAjfxHC54+MhdruyhO7QieQrYsYeTxrn93PM2y0jRH1zEExw==", + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz", + "integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "scheduler": "^0.11.2" + "scheduler": "^0.13.6" + }, + "dependencies": { + "scheduler": { + "version": "0.13.6", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz", + "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + } } }, "react-ace": { "version": "5.10.0", - "resolved": "http://registry.npmjs.org/react-ace/-/react-ace-5.10.0.tgz", + "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-5.10.0.tgz", "integrity": "sha512-aEK/XZCowP8IXq91e2DYqOtGhabk1bbjt+fyeW0UBcIkzDzP/RX/MeJKeyW7wsZcwElACVwyy9nnwXBTqgky3A==", "requires": { "brace": "^0.11.0", @@ -17653,7 +17696,7 @@ }, "react-dnd": { "version": "2.6.0", - "resolved": "http://registry.npmjs.org/react-dnd/-/react-dnd-2.6.0.tgz", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-2.6.0.tgz", "integrity": "sha1-f6JWds+CfViokSk+PBq1naACVFo=", "requires": { "disposables": "^1.0.1", @@ -17673,7 +17716,7 @@ }, "react-dnd-html5-backend": { "version": "2.6.0", - "resolved": "http://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-2.6.0.tgz", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-2.6.0.tgz", "integrity": "sha1-WQzRzKeEQbsnTt1XH+9MCxbdz44=", "requires": { "lodash": "^4.2.0" @@ -17690,6 +17733,11 @@ "scheduler": "^0.11.2" } }, + "react-error-boundary": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-1.2.5.tgz", + "integrity": "sha512-5CPSeLJA2igJNppAgFRwnTL9aK3ojenk65enNzhVyoxYNbHpIJXnChUO7+4vPhkncRA9wvQMXq6Azp2XeXd+iQ==" + }, "react-gravatar": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/react-gravatar/-/react-gravatar-2.6.3.tgz", @@ -17845,7 +17893,7 @@ }, "minimist": { "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "mjolnir.js": { @@ -18164,7 +18212,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -18515,7 +18563,7 @@ }, "reduce-css-calc": { "version": "1.3.0", - "resolved": "http://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", "requires": { "balanced-match": "^0.4.2", @@ -18638,7 +18686,7 @@ }, "regexpp": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", "dev": true }, @@ -18670,7 +18718,7 @@ "dependencies": { "jsesc": { "version": "0.5.0", - "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true } @@ -18786,7 +18834,7 @@ }, "require-uncached": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { @@ -18953,7 +19001,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -19149,9 +19197,9 @@ "dev": true }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -19173,7 +19221,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { @@ -19208,7 +19256,7 @@ }, "iconv-lite": { "version": "0.2.11", - "resolved": "http://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=" } } @@ -19228,7 +19276,7 @@ "dependencies": { "minimist": { "version": "0.0.5", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz", "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=" } } @@ -19287,7 +19335,7 @@ }, "sinon": { "version": "4.5.0", - "resolved": "http://registry.npmjs.org/sinon/-/sinon-4.5.0.tgz", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-4.5.0.tgz", "integrity": "sha512-trdx+mB0VBBgoYucy6a9L7/jfQOmvGeaKZT4OOJ+lPAtI8623xyGr8wLiE4eojzBS8G9yXbhx42GHUOVLr4X2w==", "dev": true, "requires": { @@ -19679,7 +19727,7 @@ }, "split": { "version": "0.2.10", - "resolved": "http://registry.npmjs.org/split/-/split-0.2.10.tgz", + "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz", "integrity": "sha1-Zwl8YB1pfOE2j0GPBs0gHPBSGlc=", "requires": { "through": "2" @@ -19884,7 +19932,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { "core-util-is": "~1.0.0", @@ -19895,7 +19943,7 @@ }, "string_decoder": { "version": "0.10.31", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" } } @@ -19974,7 +20022,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -19982,7 +20030,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -19997,7 +20045,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, @@ -20277,7 +20325,7 @@ }, "fast-deep-equal": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", "dev": true }, @@ -20334,7 +20382,7 @@ }, "tapable": { "version": "0.1.10", - "resolved": "http://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=", "dev": true }, @@ -20547,7 +20595,7 @@ }, "through": { "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { @@ -21242,7 +21290,7 @@ }, "tty-browserify": { "version": "0.0.0", - "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, @@ -21294,9 +21342,9 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "typescript": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz", - "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", + "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", "dev": true }, "typpy": { @@ -21400,29 +21448,15 @@ } }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "uniq": { @@ -21775,7 +21809,7 @@ }, "vm-browserify": { "version": "0.0.4", - "resolved": "http://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", "dev": true, "requires": { @@ -22631,12 +22665,13 @@ } }, "webpack-bundle-analyzer": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.0.3.tgz", - "integrity": "sha512-naLWiRfmtH4UJgtUktRTLw6FdoZJ2RvCR9ePbwM9aRMsS/KjFerkPZG9epEvXRAw5d5oPdrs9+3p+afNjxW8Xw==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.4.1.tgz", + "integrity": "sha512-Bs8D/1zF+17lhqj2OYmzi7HEVYqEVxu7lCO9Ff8BwajenOU0vAwEoV8e4ICCPNZAcqR1PCR/7o2SkW+cnCmF0A==", "dev": true, "requires": { - "acorn": "^5.7.3", + "acorn": "^6.0.7", + "acorn-walk": "^6.1.1", "bfj": "^6.1.1", "chalk": "^2.4.1", "commander": "^2.18.0", @@ -22644,12 +22679,18 @@ "express": "^4.16.3", "filesize": "^3.6.1", "gzip-size": "^5.0.0", - "lodash": "^4.17.10", + "lodash": "^4.17.15", "mkdirp": "^0.5.1", "opener": "^1.5.1", "ws": "^6.0.0" }, "dependencies": { + "acorn": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.2.1.tgz", + "integrity": "sha512-JD0xT5FCRDNyjDda3Lrg/IxFscp9q4tiYtxE1/nOzlKCk7hIRuYjhq1kCNkbPjMRMZuFq20HNQn1I9k8Oj0E+Q==", + "dev": true + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -22660,9 +22701,9 @@ } }, "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -22680,9 +22721,9 @@ } }, "ws": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.2.tgz", - "integrity": "sha512-rfUqzvz0WxmSXtJpPMX2EeASXabOrSMk1ruMOV3JBTBjo4ac2lDjGGsbQSyxj8Odhw5fBib8ZKEjDNvgouNKYw==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", "dev": true, "requires": { "async-limiter": "~1.0.0" @@ -23347,7 +23388,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { @@ -23432,7 +23473,7 @@ }, "xmlbuilder": { "version": "9.0.7", - "resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" }, "xregexp": { diff --git a/superset/assets/package.json b/superset/assets/package.json index 6d50f9d05e151..4074f223a25ec 100644 --- a/superset/assets/package.json +++ b/superset/assets/package.json @@ -47,11 +47,11 @@ "homepage": "https://superset.apache.org/", "dependencies": { "@data-ui/sparkline": "^0.0.54", - "@superset-ui/chart": "^0.11.3", - "@superset-ui/color": "^0.11.3", - "@superset-ui/connection": "^0.11.0", - "@superset-ui/core": "^0.11.0", - "@superset-ui/dimension": "^0.11.0", + "@superset-ui/chart": "^0.11.14", + "@superset-ui/color": "^0.11.9", + "@superset-ui/connection": "^0.11.14", + "@superset-ui/core": "^0.11.10", + "@superset-ui/dimension": "^0.11.10", "@superset-ui/legacy-plugin-chart-calendar": "^0.10.11", "@superset-ui/legacy-plugin-chart-chord": "^0.10.11", "@superset-ui/legacy-plugin-chart-country-map": "^0.10.11", @@ -76,13 +76,13 @@ "@superset-ui/legacy-plugin-chart-world-map": "^0.10.11", "@superset-ui/legacy-preset-chart-big-number": "^0.10.11", "@superset-ui/legacy-preset-chart-nvd3": "^0.10.38", - "@superset-ui/number-format": "^0.11.3", - "@superset-ui/time-format": "^0.11.3", - "@superset-ui/translation": "^0.11.0", + "@superset-ui/number-format": "^0.11.14", + "@superset-ui/time-format": "^0.11.14", + "@superset-ui/translation": "^0.11.9", "@types/react-json-tree": "^0.6.11", "@vx/responsive": "0.0.172", "abortcontroller-polyfill": "^1.1.9", - "bootstrap": "^3.3.6", + "bootstrap": "^3.4.1", "bootstrap-slider": "^10.0.0", "brace": "^0.11.1", "chrono-node": "^1.3.11", @@ -96,9 +96,9 @@ "dompurify": "^1.0.3", "geolib": "^2.0.24", "immutable": "^3.8.2", - "jquery": "3.1.1", + "jquery": "^3.4.1", "json-bigint": "^0.3.0", - "lodash": "^4.17.11", + "lodash": "^4.17.15", "mapbox-gl": "^0.53.0", "mathjs": "^3.20.2", "moment": "^2.20.1", @@ -107,7 +107,7 @@ "omnibar": "^2.1.1", "prop-types": "^15.6.0", "re-resizable": "^4.3.1", - "react": "^16.4.1", + "react": "^16.8.6", "react-ace": "^5.10.0", "react-bootstrap": "^0.31.5", "react-bootstrap-dialog": "^0.10.0", @@ -206,11 +206,11 @@ "ts-loader": "^5.2.0", "tslint": "^5.11.0", "tslint-react": "^3.6.0", - "typescript": "^3.1.3", + "typescript": "^3.5.3", "url-loader": "^1.0.1", "webpack": "^4.19.0", "webpack-assets-manifest": "^3.0.1", - "webpack-bundle-analyzer": "^3.0.2", + "webpack-bundle-analyzer": "^3.4.1", "webpack-cli": "^3.1.1", "webpack-dev-server": "^3.1.14", "webpack-sources": "^1.1.0" diff --git a/superset/assets/src/chart/ChartRenderer.jsx b/superset/assets/src/chart/ChartRenderer.jsx index 2f64957fb3b09..3e699872e8508 100644 --- a/superset/assets/src/chart/ChartRenderer.jsx +++ b/superset/assets/src/chart/ChartRenderer.jsx @@ -20,7 +20,7 @@ import dompurify from 'dompurify'; import { snakeCase } from 'lodash'; import PropTypes from 'prop-types'; import React from 'react'; -import { ChartProps, SuperChart } from '@superset-ui/chart'; +import { SuperChart } from '@superset-ui/chart'; import { Tooltip } from 'react-bootstrap'; import { Logger, LOG_ACTIONS_RENDER_CHART } from '../logger/LogUtils'; @@ -60,7 +60,6 @@ class ChartRenderer extends React.Component { super(props); this.state = {}; - this.createChartProps = ChartProps.createSelector(); this.hasQueryResponseChange = false; this.setTooltip = this.setTooltip.bind(this); @@ -97,33 +96,6 @@ class ChartRenderer extends React.Component { this.setState({ tooltip }); } - prepareChartProps() { - const { - width, - height, - annotationData, - datasource, - initialValues, - formData, - queryResponse, - setControlValue, - } = this.props; - - return this.createChartProps({ - width, - height, - annotationData, - datasource, - filters: initialValues, - formData, - onAddFilter: this.handleAddFilter, - onError: this.handleRenderFailure, - payload: queryResponse, - setControlValue, - setTooltip: this.setTooltip, - }); - } - handleAddFilter(col, vals, merge = true, refresh = true) { this.props.addFilter(col, vals, merge, refresh); } @@ -197,10 +169,24 @@ class ChartRenderer extends React.Component { chartId, } = this.props; - const isLoading = chartStatus === 'loading'; + // Skip chart rendering + if (chartStatus === 'loading' || !!chartAlert || chartStatus === null) { + return null; + } - const skipChartRendering = isLoading || !!chartAlert || chartStatus === null; this.renderStartTime = Logger.getTimestamp(); + + const { + width, + height, + annotationData, + datasource, + initialValues, + formData, + queryResponse, + setControlValue, + } = this.props; + return ( {this.renderTooltip()} @@ -208,7 +194,17 @@ class ChartRenderer extends React.Component { id={`chart-id-${chartId}`} className={`${snakeCase(vizType)}`} chartType={vizType} - chartProps={skipChartRendering ? null : this.prepareChartProps()} + width={width} + height={height} + annotationData={annotationData} + datasource={datasource} + filters={initialValues} + formData={formData} + payload={queryResponse} + onAddFilter={this.handleAddFilter} + onError={this.handleRenderFailure} + setControlValue={setControlValue} + setTooltip={this.setTooltip} onRenderSuccess={this.handleRenderSuccess} onRenderFailure={this.handleRenderFailure} /> From c6867d22b9a1174d635ddf746c2f8404c4648d95 Mon Sep 17 00:00:00 2001 From: John Bodley <4567245+john-bodley@users.noreply.github.com> Date: Tue, 6 Aug 2019 14:45:39 -0700 Subject: [PATCH 09/40] [init] Setting up cache before registering blueprints (#7992) * [init] Setting up cache before registering blueprints * Update __init__.py --- superset/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/superset/__init__.py b/superset/__init__.py index d1981c082eb01..12d8cf6118485 100644 --- a/superset/__init__.py +++ b/superset/__init__.py @@ -105,6 +105,10 @@ def get_manifest(): ################################################################# +# Setup the cache prior to registering the blueprints. +cache = setup_cache(app, conf.get("CACHE_CONFIG")) +tables_cache = setup_cache(app, conf.get("TABLE_NAMES_CACHE_CONFIG")) + for bp in conf.get("BLUEPRINTS"): try: print("Registering blueprint: '{}'".format(bp.name)) @@ -134,9 +138,6 @@ def get_manifest(): pessimistic_connection_handling(db.engine) -cache = setup_cache(app, conf.get("CACHE_CONFIG")) -tables_cache = setup_cache(app, conf.get("TABLE_NAMES_CACHE_CONFIG")) - migrate = Migrate(app, db, directory=APP_DIR + "/migrations") # Logging configuration From 4543898e2916cc3685ccf0c0276a2253aabcf816 Mon Sep 17 00:00:00 2001 From: Erik Ritter Date: Tue, 6 Aug 2019 15:23:54 -0700 Subject: [PATCH 10/40] Bump nvd3 charts for tooltip fix (#7994) --- superset/assets/package-lock.json | 267 +----------------------------- superset/assets/package.json | 2 +- 2 files changed, 4 insertions(+), 265 deletions(-) diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json index 875d63ca3771a..6d6b236f09c7f 100644 --- a/superset/assets/package-lock.json +++ b/superset/assets/package-lock.json @@ -2945,9 +2945,9 @@ } }, "@superset-ui/legacy-preset-chart-nvd3": { - "version": "0.10.38", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.10.38.tgz", - "integrity": "sha512-MtgAY6ZbrvZsb38iODL4XXxEe2+S+KYfWDeS/c+2Loy6lWCqjUaygq1JvXQB6iAKdDAurzFEmisgLD570hJ61A==", + "version": "0.10.39", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.10.39.tgz", + "integrity": "sha512-byBlTZtGvIpKvbhAMVweS4FFn3R8ceUo8aVAlD3F+XvyI0svMRoo4I5RpHL0vqJSkgirION77EGDFiJBM31KXQ==", "requires": { "@data-ui/xy-chart": "^0.0.78", "d3": "^3.5.17", @@ -2960,267 +2960,6 @@ "nvd3": "1.8.6", "prop-types": "^15.6.2", "urijs": "^1.18.10" - }, - "dependencies": { - "@data-ui/shared": { - "version": "0.0.78", - "resolved": "https://registry.npmjs.org/@data-ui/shared/-/shared-0.0.78.tgz", - "integrity": "sha512-IetTiW89B7v1BqINN8+0uDnqqF06ulAcPivxgm0WeQOIVsgQdtOxTNfbaeC19reI1UTiF+xm5on0mN4F1At3XQ==", - "requires": { - "@data-ui/theme": "^0.0.78", - "@vx/event": "^0.0.165", - "@vx/group": "^0.0.165", - "@vx/shape": "^0.0.168", - "@vx/tooltip": "0.0.165", - "d3-array": "^1.2.1", - "prop-types": "^15.5.10" - }, - "dependencies": { - "@vx/shape": { - "version": "0.0.168", - "resolved": "https://registry.npmjs.org/@vx/shape/-/shape-0.0.168.tgz", - "integrity": "sha512-urKZkwSafMpPQ0wI/L5FJmufRiAR4UsgYUCKxROjfE1Cf4jWNlK6mlVIIASxCdHlh9CGBbIrRMdl5Yv5lzqhjA==", - "requires": { - "@vx/curve": "0.0.165", - "@vx/group": "0.0.165", - "@vx/point": "0.0.165", - "classnames": "^2.2.5", - "d3-path": "^1.0.5", - "d3-shape": "^1.2.0", - "prop-types": "^15.5.10" - } - } - } - }, - "@data-ui/theme": { - "version": "0.0.78", - "resolved": "https://registry.npmjs.org/@data-ui/theme/-/theme-0.0.78.tgz", - "integrity": "sha512-DyGqH2kIHONvTfg3E810PFSUPDBLgQtZUFOvAMDLrSsO9KClX4XchNzEsewQoKQK4MP/pdxuJuX/LCRglsGj6g==" - }, - "@data-ui/xy-chart": { - "version": "0.0.78", - "resolved": "https://registry.npmjs.org/@data-ui/xy-chart/-/xy-chart-0.0.78.tgz", - "integrity": "sha512-/AhEApEcb9s5VZoC+fRYNZY8hEl0+c2TdGkZ2A1nEIc10NFRoyVqaqAEvQw2wI/h9kISQL2x1wztAAyab1S7rg==", - "requires": { - "@data-ui/shared": "^0.0.78", - "@data-ui/theme": "^0.0.78", - "@vx/axis": "^0.0.168", - "@vx/curve": "^0.0.165", - "@vx/event": "^0.0.165", - "@vx/glyph": "^0.0.165", - "@vx/gradient": "^0.0.165", - "@vx/grid": "^0.0.180", - "@vx/group": "^0.0.165", - "@vx/pattern": "^0.0.165", - "@vx/point": "^0.0.165", - "@vx/responsive": "^0.0.165", - "@vx/scale": "^0.0.165", - "@vx/shape": "^0.0.165", - "@vx/stats": "^0.0.165", - "@vx/text": "0.0.183", - "@vx/threshold": "0.0.170", - "@vx/tooltip": "^0.0.165", - "@vx/voronoi": "^0.0.165", - "d3-array": "^1.2.0", - "prop-types": "^15.5.10" - } - }, - "@vx/axis": { - "version": "0.0.168", - "resolved": "https://registry.npmjs.org/@vx/axis/-/axis-0.0.168.tgz", - "integrity": "sha512-/HAieKFXwa39x8mFQbKkuofacvhMLl6g8qOjBBIIvgXyPiXY8ZqQ/+7ZnvPjpf4qgtH4B+yIMEmu29cMGM/jKQ==", - "requires": { - "@vx/group": "0.0.165", - "@vx/point": "0.0.165", - "@vx/shape": "0.0.168", - "@vx/text": "0.0.165", - "classnames": "^2.2.5", - "prop-types": "^15.6.0" - }, - "dependencies": { - "@vx/shape": { - "version": "0.0.168", - "resolved": "https://registry.npmjs.org/@vx/shape/-/shape-0.0.168.tgz", - "integrity": "sha512-urKZkwSafMpPQ0wI/L5FJmufRiAR4UsgYUCKxROjfE1Cf4jWNlK6mlVIIASxCdHlh9CGBbIrRMdl5Yv5lzqhjA==", - "requires": { - "@vx/curve": "0.0.165", - "@vx/group": "0.0.165", - "@vx/point": "0.0.165", - "classnames": "^2.2.5", - "d3-path": "^1.0.5", - "d3-shape": "^1.2.0", - "prop-types": "^15.5.10" - } - }, - "@vx/text": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@vx/text/-/text-0.0.165.tgz", - "integrity": "sha512-r8vvryhEHJVsMbQpX0OH0ow5+8xdIqcbCJ6wVB2H6NzLoXbSTxK0GfGzV6T8CMiWbPuZT0oVJWF54zQnOqxOIQ==", - "requires": { - "babel-plugin-lodash": "^3.3.2", - "classnames": "^2.2.5", - "lodash": "^4.17.4", - "reduce-css-calc": "^1.3.0" - } - } - } - }, - "@vx/bounds": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@vx/bounds/-/bounds-0.0.165.tgz", - "integrity": "sha512-ZvRb72/4QNs1ZrytZTZxd0hfAb/KKfhsdkcYtIQkmdF6dTsjigMQZ+h2bLvLnbZb/RxyCCoxdiZSGXd+T1c//Q==", - "requires": { - "prop-types": "^15.5.10" - } - }, - "@vx/curve": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@vx/curve/-/curve-0.0.165.tgz", - "integrity": "sha512-fiQAGrKNGjJbL+eixUckJqIZDWXH/1NtIyyDbSz3J7ksk0QpYr5BgWcNJN76HLNt7wfcLwNzCHeNs4iVYyFGTg==", - "requires": { - "d3-shape": "^1.0.6" - } - }, - "@vx/event": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@vx/event/-/event-0.0.165.tgz", - "integrity": "sha512-FsQiw0f3s5DQB6aBQmBcoWk9e4q65LcDobHIyV8qrmpW2QgV2NvQFM1w0Q300ohpRMgJDzGk68HHHQgFOJvApw==", - "requires": { - "@vx/point": "0.0.165" - } - }, - "@vx/glyph": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@vx/glyph/-/glyph-0.0.165.tgz", - "integrity": "sha512-kccUm40e/VCtayxqvcwc2K2M6oNXO7IafwIfw1RRv6Fj4Iutto9ZpI+PGOf/zPnYVueoLnWBXT/HE7IRS+C2gw==", - "requires": { - "@vx/group": "0.0.165", - "classnames": "^2.2.5", - "d3-shape": "^1.2.0" - } - }, - "@vx/gradient": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@vx/gradient/-/gradient-0.0.165.tgz", - "integrity": "sha512-FjRXMTmcy7k0TWsfDzWWXw6T9WXKP+6LS/GRgnguq271pab/P+AdOJThsVxtBgUc8ZOAPbub3/2Gggz9d8tocg==", - "requires": { - "classnames": "^2.2.5", - "prop-types": "^15.5.7" - } - }, - "@vx/grid": { - "version": "0.0.180", - "resolved": "https://registry.npmjs.org/@vx/grid/-/grid-0.0.180.tgz", - "integrity": "sha512-+ugS0c6GbwHr6pFU0znnOG3/zTwRRadvWwj3E4ZOHmKUSz6ZEN6JNo+rD3WSZckYwLis6UivmYfJ5cV6AM4ufg==", - "requires": { - "@vx/group": "0.0.170", - "@vx/point": "0.0.165", - "@vx/shape": "0.0.179", - "classnames": "^2.2.5", - "prop-types": "^15.6.2" - }, - "dependencies": { - "@vx/group": { - "version": "0.0.170", - "resolved": "https://registry.npmjs.org/@vx/group/-/group-0.0.170.tgz", - "integrity": "sha512-RnDdRoy0YI5hokk+YWXc8t39Kp51i4BdCpiwkDJU4YypGycTYnDFjicam6jigUmZ/6wyMirDf/aQboWviFLt2Q==", - "requires": { - "classnames": "^2.2.5" - } - }, - "@vx/shape": { - "version": "0.0.179", - "resolved": "https://registry.npmjs.org/@vx/shape/-/shape-0.0.179.tgz", - "integrity": "sha512-YHVNx4xGpbjolkW3Lb5pEgJB0+u349vfnLI976DJlinY0hRNa4TZbWXOB4ywLIrYzQEXXPMUR8WtdubNxg6g0w==", - "requires": { - "@vx/curve": "0.0.165", - "@vx/group": "0.0.170", - "@vx/point": "0.0.165", - "classnames": "^2.2.5", - "d3-path": "^1.0.5", - "d3-shape": "^1.2.0", - "prop-types": "^15.5.10" - } - } - } - }, - "@vx/group": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@vx/group/-/group-0.0.165.tgz", - "integrity": "sha512-gi1DSg8AAaVRseyWiq8y4bzyvKiQIXT6vDUYBVRmv2LBcpHocBGaxNiNK0X602RgLG0XmNyRv6qSCWLOaBs3Mg==", - "requires": { - "classnames": "^2.2.5" - } - }, - "@vx/pattern": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@vx/pattern/-/pattern-0.0.165.tgz", - "integrity": "sha512-h5nmfcYlQYYzNhlhqaYUvVnkmGnC0yWv5yU1snjHweGmIHTovV3RAbKgVFAP7kB3i2rbEtC3O8WkJN++cZdLzA==", - "requires": { - "classnames": "^2.2.5", - "prop-types": "^15.5.10" - } - }, - "@vx/point": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@vx/point/-/point-0.0.165.tgz", - "integrity": "sha512-spoHilhjcWNgccrSzBUPw+PXV81tYxeyEWBkgr35aGVU4m7YT86Ywvfemwp7AVVGPn+XJHrhB0ujAhDoyqFPoA==" - }, - "@vx/responsive": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@vx/responsive/-/responsive-0.0.165.tgz", - "integrity": "sha512-b5PYEzsjgTGuH4qN2ujghq2uKQsPGBEtOAO1791WdA0j6rr0zbVsHVmJeEhvoOg0b3xhdNN1mXAzQr4K9lDaDw==", - "requires": { - "lodash": "^4.17.10", - "prop-types": "^15.6.1", - "resize-observer-polyfill": "1.5.0" - } - }, - "@vx/scale": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@vx/scale/-/scale-0.0.165.tgz", - "integrity": "sha512-5jSgXJDU6J/KWIyCbpjHqysPCddp7tG3LbTV7UmtB1Qleb4m4slShTVSE7+EKU+zgiQPDGm0+E2ht4cet+7F7A==", - "requires": { - "d3-scale": "^2.0.0" - } - }, - "@vx/shape": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@vx/shape/-/shape-0.0.165.tgz", - "integrity": "sha512-D9naH/glDtw8J8IcdumpRz1ihaoCAYMwFNh2KTv73HiTKrLQSXvIjwYFv9C0b8BCPNOXkDZS8s+AlgMSqGlZNQ==", - "requires": { - "@vx/curve": "0.0.165", - "@vx/group": "0.0.165", - "@vx/point": "0.0.165", - "classnames": "^2.2.5", - "d3-path": "^1.0.5", - "d3-shape": "^1.2.0", - "prop-types": "^15.5.10" - } - }, - "@vx/text": { - "version": "0.0.183", - "resolved": "https://registry.npmjs.org/@vx/text/-/text-0.0.183.tgz", - "integrity": "sha512-SM97C6I2Oy3FdbjM0zb2oZ8xgPskQE3r0FdGHZgq6Dk1b3lYwuW3KqdXn598BRl3iL9jfSyR6vFN9z6NV0FFww==", - "requires": { - "@babel/core": "^7.0.0", - "babel-plugin-lodash": "^3.3.2", - "classnames": "^2.2.5", - "lodash": "^4.17.4", - "prop-types": "^15.6.2", - "reduce-css-calc": "^1.3.0" - } - }, - "@vx/tooltip": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@vx/tooltip/-/tooltip-0.0.165.tgz", - "integrity": "sha512-/x1NZc67QGQ4e/WNT7Ks5LYRyeLSqp8lG04gX5J6leUS0zscAVzo3aE5u65Qqbc0cnMyMPRZ2Qtb4klWTLg+eQ==", - "requires": { - "@vx/bounds": "0.0.165", - "classnames": "^2.2.5", - "prop-types": "^15.5.10" - } - } } }, "@superset-ui/number-format": { diff --git a/superset/assets/package.json b/superset/assets/package.json index 4074f223a25ec..e3b528e3f11c4 100644 --- a/superset/assets/package.json +++ b/superset/assets/package.json @@ -75,7 +75,7 @@ "@superset-ui/legacy-plugin-chart-word-cloud": "^0.10.11", "@superset-ui/legacy-plugin-chart-world-map": "^0.10.11", "@superset-ui/legacy-preset-chart-big-number": "^0.10.11", - "@superset-ui/legacy-preset-chart-nvd3": "^0.10.38", + "@superset-ui/legacy-preset-chart-nvd3": "^0.10.39", "@superset-ui/number-format": "^0.11.14", "@superset-ui/time-format": "^0.11.14", "@superset-ui/translation": "^0.11.9", From b62c122b4d482028c20c6a9f12363c4f8fa30299 Mon Sep 17 00:00:00 2001 From: Erik Ritter Date: Tue, 6 Aug 2019 16:14:27 -0700 Subject: [PATCH 11/40] Revert "[database] Fix, Removes the limit for the page size (#7987)" (#7995) This reverts commit 8db419fe99ce3e3c940d6ba41ddae2528fd67995. --- superset/assets/src/components/TableSelector.jsx | 3 +-- superset/views/database/api.py | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/superset/assets/src/components/TableSelector.jsx b/superset/assets/src/components/TableSelector.jsx index 9ead6dd7f767e..d380241b70ed7 100644 --- a/superset/assets/src/components/TableSelector.jsx +++ b/superset/assets/src/components/TableSelector.jsx @@ -218,8 +218,7 @@ export default class TableSelector extends React.PureComponent { '/api/v1/database/?q=' + '(keys:!(none),' + 'filters:!((col:expose_in_sqllab,opr:eq,value:!t)),' + - 'order_columns:database_name,order_direction:asc,' + - 'page:0,page_size:-1)' + 'order_columns:database_name,order_direction:asc)' } onChange={this.onDatabaseChange} onAsyncError={() => this.props.handleError(t('Error while fetching database list'))} diff --git a/superset/views/database/api.py b/superset/views/database/api.py index 2bb8ea5f35ec4..dea17ba565ed1 100644 --- a/superset/views/database/api.py +++ b/superset/views/database/api.py @@ -50,8 +50,6 @@ class DatabaseRestApi(DatabaseMixin, ModelRestApi): "allows_subquery", "backend", ] - # Removes the local limit for the page size - max_page_size = -1 appbuilder.add_api(DatabaseRestApi) From e830474e5b01109837d42b7ff8948434a95c3a8b Mon Sep 17 00:00:00 2001 From: Erik Ritter Date: Tue, 6 Aug 2019 16:56:02 -0700 Subject: [PATCH 12/40] Revert "Bump FAB to 2.1.8 (#7986)" (#7996) This reverts commit 0c817e5fa9f015962fd7d6eac2af37201f118511. --- requirements.txt | 11 ++++++++--- setup.py | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 7dc8d99f3de07..d1454eb2bc4a5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,9 @@ babel==2.7.0 # via flask-babel billiard==3.6.0.0 # via celery bleach==3.1.0 celery==4.3.0 +certifi==2019.6.16 # via requests cffi==1.12.3 # via cryptography +chardet==3.0.4 # via requests click==6.7 colorama==0.4.1 contextlib2==0.5.5 @@ -21,7 +23,7 @@ croniter==0.3.30 cryptography==2.7 decorator==4.4.0 # via retry defusedxml==0.6.0 # via python3-openid -flask-appbuilder==2.1.8 +flask-appbuilder==2.1.7 flask-babel==0.12.2 # via flask-appbuilder flask-caching==1.7.2 flask-compress==1.4.0 @@ -70,13 +72,16 @@ pyyaml==5.1.2 retry==0.9.2 selenium==3.141.0 simplejson==3.16.0 -six==1.12.0 # via bleach, cryptography, flask-jwt-extended, flask-talisman, isodate, jsonschema, pathlib2, polyline, prison, pyrsistent, python-dateutil, sqlalchemy-utils, wtforms-json +six==1.12.0 # via bleach, cryptography, flask-jwt-extended, flask-talisman, isodate, jsonschema, pathlib2, polyline, prison, pydruid, pyrsistent, python-dateutil, sqlalchemy-utils, wtforms-json sqlalchemy-utils==0.34.1 sqlalchemy==1.3.6 sqlparse==0.3.0 -urllib3==1.25.3 # via selenium +urllib3==1.25.3 # via requests, selenium vine==1.3.0 # via amqp, celery webencodings==0.5.1 # via bleach werkzeug==0.15.5 # via flask, flask-jwt-extended wtforms-json==0.3.3 wtforms==2.2.1 # via flask-wtf, wtforms-json + +# The following packages are considered to be unsafe in a requirements file: +# setuptools==41.0.1 # via jsonschema, markdown diff --git a/setup.py b/setup.py index 1cfa9f2312ecc..a0c02ff3566aa 100644 --- a/setup.py +++ b/setup.py @@ -73,7 +73,7 @@ def get_git_sha(): "croniter>=0.3.28", "cryptography>=2.4.2", "flask>=1.0.0, <2.0.0", - "flask-appbuilder>=2.1.8, <2.3.0", + "flask-appbuilder>=2.1.6, <2.3.0", "flask-caching", "flask-compress", "flask-talisman", From af2b92d1477f12936233080daf0a98a9618536de Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Tue, 6 Aug 2019 21:15:47 -0700 Subject: [PATCH 13/40] Fix #7984 (#7985) See https://github.com/apache/incubator-superset/issues/7984 for more details --- superset/viz.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/superset/viz.py b/superset/viz.py index 959b64f468eb6..d6d0259f74d75 100644 --- a/superset/viz.py +++ b/superset/viz.py @@ -1069,6 +1069,9 @@ def query_obj(self): raise Exception(_("Pick a metric!")) d["metrics"] = [self.form_data.get("metric")] self.form_data["metric"] = metric + + # Limiting rows is not required as only one cell is returned + d["row_limit"] = None return d From b380879c41e785efbaa155a5b20254f619f08093 Mon Sep 17 00:00:00 2001 From: Grace Guo Date: Tue, 6 Aug 2019 21:23:40 -0700 Subject: [PATCH 14/40] [fix] reduce content in sql lab localStorage (#7998) --- .../spec/javascripts/sqllab/fixtures.js | 7 +++++ .../sqllab/utils/emptyQueryResults_spec.js | 14 +++++++--- superset/assets/src/SqlLab/App.jsx | 4 ++- ...s.js => reduxStateToLocalStorageHelper.js} | 27 ++++++++++++++++++- 4 files changed, 47 insertions(+), 5 deletions(-) rename superset/assets/src/SqlLab/utils/{emptyQueryResults.js => reduxStateToLocalStorageHelper.js} (69%) diff --git a/superset/assets/spec/javascripts/sqllab/fixtures.js b/superset/assets/spec/javascripts/sqllab/fixtures.js index 2b737fef94da8..30cff0b9dc443 100644 --- a/superset/assets/spec/javascripts/sqllab/fixtures.js +++ b/superset/assets/spec/javascripts/sqllab/fixtures.js @@ -179,6 +179,13 @@ export const defaultQueryEditor = { selectedText: null, sql: 'SELECT *\nFROM\nWHERE', title: 'Untitled Query', + schemaOptions: [ + { + value: 'main', + label: 'main', + title: 'main', + }, + ], }; export const queries = [ { diff --git a/superset/assets/spec/javascripts/sqllab/utils/emptyQueryResults_spec.js b/superset/assets/spec/javascripts/sqllab/utils/emptyQueryResults_spec.js index f20243096feb1..44806272f33fc 100644 --- a/superset/assets/spec/javascripts/sqllab/utils/emptyQueryResults_spec.js +++ b/superset/assets/spec/javascripts/sqllab/utils/emptyQueryResults_spec.js @@ -16,11 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -import emptyQueryResults from '../../../../src/SqlLab/utils/emptyQueryResults'; +import { emptyQueryResults, clearQueryEditors } from '../../../../src/SqlLab/utils/reduxStateToLocalStorageHelper'; import { LOCALSTORAGE_MAX_QUERY_AGE_MS } from '../../../../src/SqlLab/constants'; -import { queries } from '../fixtures'; +import { queries, defaultQueryEditor } from '../fixtures'; -describe('emptyQueryResults', () => { +describe('reduxStateToLocalStorageHelper', () => { const queriesObj = {}; beforeEach(() => { queries.forEach((q) => { @@ -39,4 +39,12 @@ describe('emptyQueryResults', () => { expect(emptiedQuery[id].startDttm).toBe(startDttm); expect(emptiedQuery[id].results).toEqual({}); }); + + it('should only return selected keys for query editor', () => { + const queryEditors = [defaultQueryEditor]; + expect(Object.keys(queryEditors[0])).toContain('schemaOptions'); + + const clearedQueryEditors = clearQueryEditors(queryEditors); + expect(Object.keys(clearedQueryEditors)[0]).not.toContain('schemaOptions'); + }); }); diff --git a/superset/assets/src/SqlLab/App.jsx b/superset/assets/src/SqlLab/App.jsx index 3883591687908..af35a1e50b773 100644 --- a/superset/assets/src/SqlLab/App.jsx +++ b/superset/assets/src/SqlLab/App.jsx @@ -27,7 +27,7 @@ import getInitialState from './reducers/getInitialState'; import rootReducer from './reducers/index'; import { initEnhancer } from '../reduxUtils'; import App from './components/App'; -import emptyQueryResults from './utils/emptyQueryResults'; +import { emptyQueryResults, clearQueryEditors } from './utils/reduxStateToLocalStorageHelper'; import { BYTES_PER_CHAR, KB_STORAGE } from './constants'; import setupApp from '../setup/setupApp'; @@ -57,6 +57,8 @@ const sqlLabPersistStateConfig = { subset[path] = { ...state[path], queries: emptyQueryResults(state[path].queries), + queryEditors: clearQueryEditors(state[path].queryEditors), + tables: [], }; } }); diff --git a/superset/assets/src/SqlLab/utils/emptyQueryResults.js b/superset/assets/src/SqlLab/utils/reduxStateToLocalStorageHelper.js similarity index 69% rename from superset/assets/src/SqlLab/utils/emptyQueryResults.js rename to superset/assets/src/SqlLab/utils/reduxStateToLocalStorageHelper.js index 2798168044a82..6a517f611b3b4 100644 --- a/superset/assets/src/SqlLab/utils/emptyQueryResults.js +++ b/superset/assets/src/SqlLab/utils/reduxStateToLocalStorageHelper.js @@ -18,7 +18,19 @@ */ import { LOCALSTORAGE_MAX_QUERY_AGE_MS } from '../constants'; -export default function emptyQueryResults(queries) { +const PERSISTENT_QUERY_EDITOR_KEYS = new Set([ + 'autorun', + 'dbId', + 'id', + 'latestQueryId', + 'queryLimit', + 'selectedText', + 'sql', + 'templateParams', + 'title', +]); + +export function emptyQueryResults(queries) { return Object.keys(queries) .reduce((accu, key) => { const { startDttm, results } = queries[key]; @@ -35,3 +47,16 @@ export default function emptyQueryResults(queries) { return updatedQueries; }, {}); } + +export function clearQueryEditors(queryEditors) { + return queryEditors + .map(editor => + // only return selected keys + Object.keys(editor) + .filter(key => PERSISTENT_QUERY_EDITOR_KEYS.has(key)) + .reduce((accumulator, key) => ({ + ...accumulator, + [key]: editor[key], + }), {}), + ); +} From cd6de3a1d8120d34e290931017764ec3d3965719 Mon Sep 17 00:00:00 2001 From: Erik Ritter Date: Wed, 7 Aug 2019 09:35:16 -0700 Subject: [PATCH 15/40] [Jinja] Make Presto template functions backwards compatible (#7993) --- docs/sqllab.rst | 2 +- superset/jinja_context.py | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/docs/sqllab.rst b/docs/sqllab.rst index c60123fa71714..c69e5848167cf 100644 --- a/docs/sqllab.rst +++ b/docs/sqllab.rst @@ -56,7 +56,7 @@ Templating with Jinja SELECT * FROM some_table - WHERE partition_key = '{{ presto.latest_partition('some_table') }}' + WHERE partition_key = '{{ presto.first_latest_partition('some_table') }}' Templating unleashes the power and capabilities of a programming language within your SQL code. diff --git a/superset/jinja_context.py b/superset/jinja_context.py index 97b4dfe30cdb3..2fdcb1424d72c 100644 --- a/superset/jinja_context.py +++ b/superset/jinja_context.py @@ -240,7 +240,25 @@ def _schema_table( schema, table_name = table_name.split(".") return table_name, schema - def latest_partition(self, table_name: str): + def first_latest_partition(self, table_name: str) -> str: + """ + Gets the first value in the array of all latest partitions + + :param table_name: table name in the format `schema.table` + :return: the first (or only) value in the latest partition array + :raises IndexError: If no partition exists + """ + + return self.latest_partitions(table_name)[0] + + def latest_partitions(self, table_name: str) -> List[str]: + """ + Gets the array of all latest partitions + + :param table_name: table name in the format `schema.table` + :return: the latest partition array + """ + table_name, schema = self._schema_table(table_name, self.schema) return self.database.db_engine_spec.latest_partition( table_name, schema, self.database @@ -252,6 +270,8 @@ def latest_sub_partition(self, table_name, **kwargs): table_name=table_name, schema=schema, database=self.database, **kwargs ) + latest_partition = first_latest_partition + class HiveTemplateProcessor(PrestoTemplateProcessor): engine = "hive" From 8cd8ec16d5017076b639a76514069cc276f930ad Mon Sep 17 00:00:00 2001 From: Ville Brofeldt <33317356+villebro@users.noreply.github.com> Date: Thu, 8 Aug 2019 00:17:23 +0300 Subject: [PATCH 16/40] Fix Pandas 0.24 DateOffset bug pt. 2 (#7981) * Fix pandas 0.24 DateOffset bug * Add try-catch for DateOffsets that don't support normalize --- superset/viz.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/superset/viz.py b/superset/viz.py index d6d0259f74d75..abd908c1918ab 100644 --- a/superset/viz.py +++ b/superset/viz.py @@ -42,7 +42,6 @@ import numpy as np import pandas as pd from pandas.tseries.frequencies import to_offset -from pandas.tseries.offsets import DateOffset import polyline import simplejson as json @@ -1402,7 +1401,10 @@ def get_data(self, df): fd = self.form_data df = self.process_data(df) freq = to_offset(fd.get("freq")) - freq = DateOffset(normalize=True, **freq.kwds) + try: + freq = type(freq)(freq.n, normalize=True, **freq.kwds) + except ValueError: + freq = type(freq)(freq.n, **freq.kwds) df.index.name = None df[DTTM_ALIAS] = df.index.map(freq.rollback) df["ranked"] = df[DTTM_ALIAS].rank(method="dense", ascending=False) - 1 From cbfd1302c0ac436cfd25730c6a8f5437c28cc40f Mon Sep 17 00:00:00 2001 From: Matthew Mutiso <6734562+mmutiso@users.noreply.github.com> Date: Thu, 8 Aug 2019 00:31:22 +0300 Subject: [PATCH 17/40] Update to fix the broken blueprints link (#7949) * Updated to fix the broken blueprints link The current link is http://flask.pocoo.org/docs/0.12/blueprints/ which redirects to a non-existent page. The new link is https://flask.palletsprojects.com/en/1.1.x/tutorial/views/. * Link to the current Flask version(1.0.x) --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 919d6ed77fa53..a012ab9da94ac 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -1054,7 +1054,7 @@ your environment. See `CONTRIBUTING.md#setup-local-environment-for-development < Blueprints ---------- -`Blueprints are Flask's reusable apps `_. +`Blueprints are Flask's reusable apps `_. Superset allows you to specify an array of Blueprints in your ``superset_config`` module. Here's an example of how this can work with a simple Blueprint. By doing From 2f2a3b79b4532fd8565303b44169e22497ed2196 Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Wed, 7 Aug 2019 21:08:10 -0700 Subject: [PATCH 18/40] Setup FOSSA as part of CI (#7999) * Setup FOSSA as part of CI * Add comments and links to FOSSA docs on script --- .fossa.yml | 38 ++++++++++++++++++++++++++++++++++++++ .travis.yml | 7 +++++++ scripts/fossa.sh | 26 ++++++++++++++++++++++++++ tox.ini | 7 +++++++ 4 files changed, 78 insertions(+) create mode 100755 .fossa.yml create mode 100755 scripts/fossa.sh diff --git a/.fossa.yml b/.fossa.yml new file mode 100755 index 0000000000000..fd56e6793e179 --- /dev/null +++ b/.fossa.yml @@ -0,0 +1,38 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Generated by FOSSA CLI (https://github.com/fossas/fossa-cli) +# Visit https://fossa.com to learn more + +version: 2 +cli: + server: https://app.fossa.com + fetcher: custom +analyze: + modules: + - name: assets + type: npm + target: superset/assets + path: superset/assets + - name: docs + type: pip + target: docs + path: docs + - name: . + type: pip + target: . + path: . diff --git a/.travis.yml b/.travis.yml index cacef8273a53f..ec11b25b4b528 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,13 @@ # jobs: include: + - language: python + python: 3.6 + env: + - TOXENV=fossa + install: + - pip install --upgrade pip + - pip install tox - language: python python: 3.6 env: diff --git a/scripts/fossa.sh b/scripts/fossa.sh new file mode 100755 index 0000000000000..7ba54ded40b70 --- /dev/null +++ b/scripts/fossa.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is the recommended way to install FOSSA's cli per the docs: +# https://docs.fossa.com/docs/travisci#section-add-fossa-steps-to-travisyml +curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | sudo bash + +# This key is a push-only API key, also recommended for public projects +# https://docs.fossa.com/docs/api-reference#section-push-only-api-token +FOSSA_API_KEY="f72e93645bdfeab94bd227c7bbdda4ef" fossa diff --git a/tox.ini b/tox.ini index c5d2cf8eebe3a..1c33d69e5d25a 100644 --- a/tox.ini +++ b/tox.ini @@ -124,6 +124,12 @@ whitelist_externals = {toxinidir}/scripts/check_license.sh deps = +[testenv:fossa] +commands = + {toxinidir}/scripts/fossa.sh +passenv = * +deps = + [testenv:py36-mysql] deps = -rrequirements.txt @@ -145,6 +151,7 @@ deps = [tox] envlist = + fossa black cypress-dashboard cypress-explore From 73cdad2375d0e87809e4a12c180b89747a83cc4f Mon Sep 17 00:00:00 2001 From: Daniel Vaz Gaspar Date: Thu, 8 Aug 2019 19:37:00 +0100 Subject: [PATCH 19/40] [SQLLab] Fix, database api unlimited page size v2 (#8002) * [database] Fix, Removes the limit for the page size, Bump FAB to 2.1.8 Old FAB API had no limits by default, this will keep this behaviour but only for this endpoint * [sqllab] Add test for database API * [sqllab] Add test for database API * [sqllab] Include page zero on request * [sqllab] Fix, Black and requirements * [sqllab] Make database API return unlimited results * [sqllab] just a test * [sqllab] Bump FAB to 2.1.9 * [sqllab] Remove unused import --- requirements.txt | 11 +++-------- setup.py | 2 +- .../assets/src/components/TableSelector.jsx | 2 +- superset/views/database/api.py | 2 ++ tests/sqllab_tests.py | 18 ++++++++++++++++++ 5 files changed, 25 insertions(+), 10 deletions(-) diff --git a/requirements.txt b/requirements.txt index d1454eb2bc4a5..74c327e0b4886 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,9 +13,7 @@ babel==2.7.0 # via flask-babel billiard==3.6.0.0 # via celery bleach==3.1.0 celery==4.3.0 -certifi==2019.6.16 # via requests cffi==1.12.3 # via cryptography -chardet==3.0.4 # via requests click==6.7 colorama==0.4.1 contextlib2==0.5.5 @@ -23,7 +21,7 @@ croniter==0.3.30 cryptography==2.7 decorator==4.4.0 # via retry defusedxml==0.6.0 # via python3-openid -flask-appbuilder==2.1.7 +flask-appbuilder==2.1.9 flask-babel==0.12.2 # via flask-appbuilder flask-caching==1.7.2 flask-compress==1.4.0 @@ -72,16 +70,13 @@ pyyaml==5.1.2 retry==0.9.2 selenium==3.141.0 simplejson==3.16.0 -six==1.12.0 # via bleach, cryptography, flask-jwt-extended, flask-talisman, isodate, jsonschema, pathlib2, polyline, prison, pydruid, pyrsistent, python-dateutil, sqlalchemy-utils, wtforms-json +six==1.12.0 # via bleach, cryptography, flask-jwt-extended, flask-talisman, isodate, jsonschema, pathlib2, polyline, prison, pyrsistent, python-dateutil, sqlalchemy-utils, wtforms-json sqlalchemy-utils==0.34.1 sqlalchemy==1.3.6 sqlparse==0.3.0 -urllib3==1.25.3 # via requests, selenium +urllib3==1.25.3 # via selenium vine==1.3.0 # via amqp, celery webencodings==0.5.1 # via bleach werkzeug==0.15.5 # via flask, flask-jwt-extended wtforms-json==0.3.3 wtforms==2.2.1 # via flask-wtf, wtforms-json - -# The following packages are considered to be unsafe in a requirements file: -# setuptools==41.0.1 # via jsonschema, markdown diff --git a/setup.py b/setup.py index a0c02ff3566aa..c3a40ad364cee 100644 --- a/setup.py +++ b/setup.py @@ -73,7 +73,7 @@ def get_git_sha(): "croniter>=0.3.28", "cryptography>=2.4.2", "flask>=1.0.0, <2.0.0", - "flask-appbuilder>=2.1.6, <2.3.0", + "flask-appbuilder>=2.1.9, <2.3.0", "flask-caching", "flask-compress", "flask-talisman", diff --git a/superset/assets/src/components/TableSelector.jsx b/superset/assets/src/components/TableSelector.jsx index d380241b70ed7..bd66eb87f0d25 100644 --- a/superset/assets/src/components/TableSelector.jsx +++ b/superset/assets/src/components/TableSelector.jsx @@ -218,7 +218,7 @@ export default class TableSelector extends React.PureComponent { '/api/v1/database/?q=' + '(keys:!(none),' + 'filters:!((col:expose_in_sqllab,opr:eq,value:!t)),' + - 'order_columns:database_name,order_direction:asc)' + 'order_columns:database_name,order_direction:asc,page:0,page_size:-1)' } onChange={this.onDatabaseChange} onAsyncError={() => this.props.handleError(t('Error while fetching database list'))} diff --git a/superset/views/database/api.py b/superset/views/database/api.py index dea17ba565ed1..2bb8ea5f35ec4 100644 --- a/superset/views/database/api.py +++ b/superset/views/database/api.py @@ -50,6 +50,8 @@ class DatabaseRestApi(DatabaseMixin, ModelRestApi): "allows_subquery", "backend", ] + # Removes the local limit for the page size + max_page_size = -1 appbuilder.add_api(DatabaseRestApi) diff --git a/tests/sqllab_tests.py b/tests/sqllab_tests.py index b16b796abdaac..1774c265e17e0 100644 --- a/tests/sqllab_tests.py +++ b/tests/sqllab_tests.py @@ -20,6 +20,7 @@ import unittest from flask_appbuilder.security.sqla import models as ab_models +import prison from superset import db, security_manager from superset.dataframe import SupersetDataFrame @@ -403,6 +404,23 @@ def test_queryview_filter_owner_only(self) -> None: session.commit() + def test_api_database(self): + self.login("admin") + + arguments = { + "keys": [], + "filters": [{"col": "expose_in_sqllab", "opr": "eq", "value": True}], + "order_column": "database_name", + "order_direction": "asc", + "page": 0, + "page_size": -1, + } + expected_results = ["examples", "fake_db_100", "main"] + url = "api/v1/database/?{}={}".format("q", prison.dumps(arguments)) + data = self.get_json_resp(url) + for i, expected_result in enumerate(expected_results): + self.assertEquals(expected_result, data["result"][i]["database_name"]) + if __name__ == "__main__": unittest.main() From cd544fa6bcb7c3192d693f63232e38e83b97f598 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Thu, 8 Aug 2019 13:36:27 -0700 Subject: [PATCH 20/40] Local config no longer fails to import silently (#8006) * Local config no longer fails to import silently * fix types, use f-strings * fix unhelpful pylint import error * tweaked comment --- superset/config.py | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/superset/config.py b/superset/config.py index 01bc16a587d53..bcdd0f63c6805 100644 --- a/superset/config.py +++ b/superset/config.py @@ -23,7 +23,9 @@ """ from collections import OrderedDict import imp +import importlib.util import json +import logging import os import sys @@ -622,29 +624,29 @@ class CeleryConfig(object): # SQLALCHEMY_DATABASE_URI by default if set to `None` SQLALCHEMY_EXAMPLES_URI = None -try: - if CONFIG_PATH_ENV_VAR in os.environ: - # Explicitly import config module that is not in pythonpath; useful - # for case where app is being executed via pex. - print( - "Loaded your LOCAL configuration at [{}]".format( - os.environ[CONFIG_PATH_ENV_VAR] - ) - ) +if CONFIG_PATH_ENV_VAR in os.environ: + # Explicitly import config module that is not necessarily in pythonpath; useful + # for case where app is being executed via pex. + try: + cfg_path = os.environ[CONFIG_PATH_ENV_VAR] module = sys.modules[__name__] - override_conf = imp.load_source( - "superset_config", os.environ[CONFIG_PATH_ENV_VAR] - ) + override_conf = imp.load_source("superset_config", cfg_path) for key in dir(override_conf): if key.isupper(): setattr(module, key, getattr(override_conf, key)) - else: - from superset_config import * # noqa - import superset_config - - print( - "Loaded your LOCAL configuration at [{}]".format(superset_config.__file__) + print(f"Loaded your LOCAL configuration at [{cfg_path}]") + except Exception: + logging.exception( + f"Failed to import config for {CONFIG_PATH_ENV_VAR}={cfg_path}" ) -except ImportError: - pass + raise +elif importlib.util.find_spec("superset_config"): + try: + from superset_config import * # noqa pylint: disable=import-error + import superset_config # noqa pylint: disable=import-error + + print(f"Loaded your LOCAL configuration at [{superset_config.__file__}]") + except Exception: + logging.exception("Found but failed to import local superset_config") + raise From 9233a63a16311a7fe2012e97a949b9b3fd6434a1 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Thu, 8 Aug 2019 13:47:18 -0700 Subject: [PATCH 21/40] Event logger config takes instance instead of class (#7997) * allow preconfigured event logger instance; deprecate specifying class * add func docs and simplify conditions * modify docs to reflect EVENT_LOGGER cfg change * commit black formatting fixes and license header * add type checking, fix other pre-commit failues * remove superfluous/wordy condition * fix flake8 failure * fix new black failure * dedent warning msg; use f-strings --- docs/installation.rst | 4 ++-- superset/__init__.py | 6 +++-- superset/utils/log.py | 43 ++++++++++++++++++++++++++++++++++++ tests/event_logger_tests.py | 44 +++++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 tests/event_logger_tests.py diff --git a/docs/installation.rst b/docs/installation.rst index a012ab9da94ac..e5606574fe857 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -738,9 +738,9 @@ Example of a simple JSON to Stdout class:: print(json.dumps(log)) -Then on Superset's config reference the class you want to use:: +Then on Superset's config pass an instance of the logger type you want to use. - EVENT_LOGGER = JSONStdOutEventLogger + EVENT_LOGGER = JSONStdOutEventLogger() Upgrading diff --git a/superset/__init__.py b/superset/__init__.py index 12d8cf6118485..3b503a48c84e2 100644 --- a/superset/__init__.py +++ b/superset/__init__.py @@ -36,7 +36,7 @@ from superset.connectors.connector_registry import ConnectorRegistry from superset.security import SupersetSecurityManager from superset.utils.core import pessimistic_connection_handling, setup_cache -from superset.utils.log import DBEventLogger +from superset.utils.log import DBEventLogger, get_event_logger_from_cfg_value wtforms_json.init() @@ -220,7 +220,9 @@ def index(self): _feature_flags.update(app.config.get("FEATURE_FLAGS") or {}) # Event Logger -event_logger = app.config.get("EVENT_LOGGER", DBEventLogger)() +event_logger = get_event_logger_from_cfg_value( + app.config.get("EVENT_LOGGER", DBEventLogger()) +) def get_feature_flags(): diff --git a/superset/utils/log.py b/superset/utils/log.py index 94cdfb4706592..768e6b3039d64 100644 --- a/superset/utils/log.py +++ b/superset/utils/log.py @@ -18,7 +18,11 @@ from abc import ABC, abstractmethod from datetime import datetime import functools +import inspect import json +import logging +import textwrap +from typing import Any, cast, Type from flask import current_app, g, request @@ -83,6 +87,45 @@ def stats_logger(self): return current_app.config.get("STATS_LOGGER") +def get_event_logger_from_cfg_value(cfg_value: object) -> AbstractEventLogger: + """ + This function implements the deprecation of assignment of class objects to EVENT_LOGGER + configuration, and validates type of configured loggers. + + The motivation for this method is to gracefully deprecate the ability to configure + EVENT_LOGGER with a class type, in favor of preconfigured instances which may have + required construction-time injection of proprietary or locally-defined dependencies. + + :param cfg_value: The configured EVENT_LOGGER value to be validated + :return: if cfg_value is a class type, will return a new instance created using a + default con + """ + result: Any = cfg_value + if inspect.isclass(cfg_value): + logging.warning( + textwrap.dedent( + """ + In superset private config, EVENT_LOGGER has been assigned a class object. In order to + accomodate pre-configured instances without a default constructor, assignment of a class + is deprecated and may no longer work at some point in the future. Please assign an object + instance of a type that implements superset.utils.log.AbstractEventLogger. + """ + ) + ) + + event_logger_type = cast(Type, cfg_value) + result = event_logger_type() + + # Verify that we have a valid logger impl + if not isinstance(result, AbstractEventLogger): + raise TypeError( + "EVENT_LOGGER must be configured with a concrete instance of superset.utils.log.AbstractEventLogger." + ) + + logging.info(f"Configured event logger of type {type(result)}") + return cast(AbstractEventLogger, result) + + class DBEventLogger(AbstractEventLogger): def log(self, user_id, action, *args, **kwargs): from superset.models.core import Log diff --git a/tests/event_logger_tests.py b/tests/event_logger_tests.py new file mode 100644 index 0000000000000..a7a1f222a013b --- /dev/null +++ b/tests/event_logger_tests.py @@ -0,0 +1,44 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +import logging +import unittest + +from superset.utils.log import DBEventLogger, get_event_logger_from_cfg_value + + +class TestEventLogger(unittest.TestCase): + def test_returns_configured_object_if_correct(self): + # test that assignment of concrete AbstractBaseClass impl returns unmodified object + obj = DBEventLogger() + res = get_event_logger_from_cfg_value(obj) + self.assertTrue(obj is res) + + def test_event_logger_config_class_deprecation(self): + # test that assignment of a class object to EVENT_LOGGER is correctly deprecated + res = None + + # print warning if a class is assigned to EVENT_LOGGER + with self.assertLogs(level="WARNING"): + res = get_event_logger_from_cfg_value(DBEventLogger) + + # class is instantiated and returned + self.assertIsInstance(res, DBEventLogger) + + def test_raises_typerror_if_not_abc_impl(self): + # test that assignment of non AbstractEventLogger derived type raises TypeError + with self.assertRaises(TypeError): + get_event_logger_from_cfg_value(logging.getLogger()) From aebffe0b4455a333d60fa2855438c68a270c0626 Mon Sep 17 00:00:00 2001 From: semantiDan <41392410+semantiDan@users.noreply.github.com> Date: Fri, 9 Aug 2019 19:07:59 +0300 Subject: [PATCH 22/40] Added better display of NULL values in FilterableTable (as in SQL Lab Results) (#8003) * Added better display of NULL values in FilterableTable (Reults table as in SQL Lab results) and changed sorting order so that NULL values always come last * fixed syntax according to elint recommendations * changed code style and logic in getContentCell according to @etr2460 code review * remvoved 'null-cell' class and replaced it with 'text-muted' as per @mistercrunch review --- .../FilterableTable/FilterableTable.jsx | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/superset/assets/src/components/FilterableTable/FilterableTable.jsx b/superset/assets/src/components/FilterableTable/FilterableTable.jsx index e68c998e85030..698ce20b6633a 100644 --- a/superset/assets/src/components/FilterableTable/FilterableTable.jsx +++ b/superset/assets/src/components/FilterableTable/FilterableTable.jsx @@ -121,6 +121,7 @@ export default class FilterableTable extends PureComponent { this.renderGrid = this.renderGrid.bind(this); this.renderTableCell = this.renderTableCell.bind(this); this.renderTableHeader = this.renderTableHeader.bind(this); + this.sortResults = this.sortResults.bind(this); this.renderTable = this.renderTable.bind(this); this.rowClassName = this.rowClassName.bind(this); this.sort = this.sort.bind(this); @@ -173,6 +174,13 @@ export default class FilterableTable extends PureComponent { } getCellContent({ cellData, columnKey }) { + if (cellData === null) { + return ( + + NULL + + ); + } const content = String(cellData); const firstCharacter = content.substring(0, 1); let truncated; @@ -194,7 +202,7 @@ export default class FilterableTable extends PureComponent { if (['string', 'number'].indexOf(typeof (val)) >= 0) { newRow[k] = val; } else { - newRow[k] = JSONbig.stringify(val); + newRow[k] = val === null ? null : JSONbig.stringify(val); } } return newRow; @@ -250,6 +258,23 @@ export default class FilterableTable extends PureComponent { ); } + sortResults(sortBy, descending) { + return (a, b) => { + if (a[sortBy] === b[sortBy]) { + // equal items sort equally + return 0; + } else if (a[sortBy] === null) { + // nulls sort after anything else + return 1; + } else if (b[sortBy] === null) { + return -1; + } else if (descending) { + return a[sortBy] < b[sortBy] ? 1 : -1; + } + return a[sortBy] < b[sortBy] ? -1 : 1; + }; + } + renderTableHeader({ dataKey, label, sortBy, sortDirection }) { const className = this.props.expandedColumns.indexOf(label) > -1 ? 'header-style-disabled' @@ -386,8 +411,7 @@ export default class FilterableTable extends PureComponent { // sort list if (sortBy) { sortedAndFilteredList = sortedAndFilteredList - .sortBy(item => item[sortBy]) - .update(list => sortDirection === SortDirection.DESC ? list.reverse() : list); + .sort(this.sortResults(sortBy, sortDirection === SortDirection.DESC)); } let { height } = this.props; From 6df2a713e4189dc19168cd7ace754451e9c38d01 Mon Sep 17 00:00:00 2001 From: schoel-bis Date: Fri, 9 Aug 2019 22:31:51 +0200 Subject: [PATCH 23/40] Fix tooltips not visible for NVD3 charts on Firefox (#7822) (#7929) This bug was introduced by #7102 Using `position:absolute` on body gives `document.documentElement` a height of 0 which propagates to `clientHeight` on Firefox. This leads to a wrong calculation of the tooltip position in NVD3. The solution proposed here is to use `min-height: 100vh` instead of the current technique for stretching the body element to the full window height, thus keeping body and html together. --- superset/assets/stylesheets/less/index.less | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/superset/assets/stylesheets/less/index.less b/superset/assets/stylesheets/less/index.less index b1fc604323c0e..680c134b59915 100644 --- a/superset/assets/stylesheets/less/index.less +++ b/superset/assets/stylesheets/less/index.less @@ -25,11 +25,7 @@ @stroke-primary: @brand-primary; body { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; + min-height: 100vh; display: flex; flex-direction: column; } From 2ab8e15ddf1acf9e7b2b6902ea6b2d07422e79a1 Mon Sep 17 00:00:00 2001 From: Arthur P <44376569+arthurpduarte@users.noreply.github.com> Date: Fri, 9 Aug 2019 15:44:38 -0700 Subject: [PATCH 24/40] Fix: There was an issue fetching the favorite status of this dashboard #6824 (#8013) Issue: When Superset is running behind a reverse proxy and the dashboard page is requested with HTTPS protocol, the frontend can't retrieve the favorite status of the dashboard and, instead, displays the following toast message: 'There was an issue fetching the favorite status of this dashboard.' Cause: While the API exposes the /favestar/Dashboard/{id}/count/ endpoint, the frontend executes an API call to /favestar/Dashboard/{id}/count, which leads to a redirect to an endpoint with HTTP protocol Fix: Call the correct endpoint --- superset/assets/src/dashboard/actions/dashboardState.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset/assets/src/dashboard/actions/dashboardState.js b/superset/assets/src/dashboard/actions/dashboardState.js index 3318c33eb6c45..db4d3695a6c2e 100644 --- a/superset/assets/src/dashboard/actions/dashboardState.js +++ b/superset/assets/src/dashboard/actions/dashboardState.js @@ -64,7 +64,7 @@ export const FETCH_FAVE_STAR = 'FETCH_FAVE_STAR'; export function fetchFaveStar(id) { return function fetchFaveStarThunk(dispatch) { return SupersetClient.get({ - endpoint: `${FAVESTAR_BASE_URL}/${id}/count`, + endpoint: `${FAVESTAR_BASE_URL}/${id}/count/`, }) .then(({ json }) => { if (json.count > 0) dispatch(toggleFaveStar(true)); From 2476814a6ae00856510d774ddc7ad9eb5e25107b Mon Sep 17 00:00:00 2001 From: Tom Hunter Date: Sat, 10 Aug 2019 21:55:56 -0400 Subject: [PATCH 25/40] [feat] Add d3 legend formatting for Arc, Polygon and Scatter deck.gl maps (#7951) * Legend formatting for Arc, Polygon and Scatter * fix typo * refactor from d3-format to superset-ui --- .../src/explore/controlPanels/DeckArc.js | 1 + .../src/explore/controlPanels/DeckPolygon.js | 2 +- .../src/explore/controlPanels/DeckScatter.js | 1 + superset/assets/src/explore/controls.jsx | 10 +++++++ superset/assets/src/visualizations/Legend.jsx | 30 ++++++++++++++++++- .../deckgl/CategoricalDeckGLContainer.jsx | 1 + .../deckgl/layers/Polygon/Polygon.jsx | 1 + 7 files changed, 44 insertions(+), 2 deletions(-) diff --git a/superset/assets/src/explore/controlPanels/DeckArc.js b/superset/assets/src/explore/controlPanels/DeckArc.js index e0bee756fa994..6341673db125d 100644 --- a/superset/assets/src/explore/controlPanels/DeckArc.js +++ b/superset/assets/src/explore/controlPanels/DeckArc.js @@ -44,6 +44,7 @@ export default { ['color_picker', 'target_color_picker'], ['dimension', 'color_scheme', 'label_colors'], ['stroke_width', 'legend_position'], + ['legend_format', null], ], }, { diff --git a/superset/assets/src/explore/controlPanels/DeckPolygon.js b/superset/assets/src/explore/controlPanels/DeckPolygon.js index 25790216fb55a..cc29b84c5caf2 100644 --- a/superset/assets/src/explore/controlPanels/DeckPolygon.js +++ b/superset/assets/src/explore/controlPanels/DeckPolygon.js @@ -52,7 +52,7 @@ export default { ['linear_color_scheme', 'opacity'], ['num_buckets', 'break_points'], ['table_filter', 'toggle_polygons'], - ['legend_position', null], + ['legend_position', 'legend_format'], ], }, { diff --git a/superset/assets/src/explore/controlPanels/DeckScatter.js b/superset/assets/src/explore/controlPanels/DeckScatter.js index 8e60029fe2a59..e2c63084176ec 100644 --- a/superset/assets/src/explore/controlPanels/DeckScatter.js +++ b/superset/assets/src/explore/controlPanels/DeckScatter.js @@ -62,6 +62,7 @@ export default { label: t('Point Color'), controlSetRows: [ ['color_picker', 'legend_position'], + [null, 'legend_format'], ['dimension', 'color_scheme', 'label_colors'], ], }, diff --git a/superset/assets/src/explore/controls.jsx b/superset/assets/src/explore/controls.jsx index 3ddabd21aea79..ae673de545e28 100644 --- a/superset/assets/src/explore/controls.jsx +++ b/superset/assets/src/explore/controls.jsx @@ -305,6 +305,16 @@ export const controls = { renderTrigger: true, }, + legend_format: { + label: t('Legend Format'), + description: t('Choose the format for legend values'), + type: 'SelectControl', + clearable: false, + default: D3_FORMAT_OPTIONS[0], + choices: D3_FORMAT_OPTIONS, + renderTrigger: true, + }, + fill_color_picker: { label: t('Fill Color'), description: t(' Set the opacity to 0 if you do not want to override the color specified in the GeoJSON'), diff --git a/superset/assets/src/visualizations/Legend.jsx b/superset/assets/src/visualizations/Legend.jsx index fcaef02c84b82..355f632d34b9a 100644 --- a/superset/assets/src/visualizations/Legend.jsx +++ b/superset/assets/src/visualizations/Legend.jsx @@ -18,13 +18,17 @@ */ import React from 'react'; import PropTypes from 'prop-types'; +import { formatNumber } from '@superset-ui/number-format'; import './Legend.css'; +const categoryDelimiter = ' - '; + const propTypes = { categories: PropTypes.object, toggleCategory: PropTypes.func, showSingleCategory: PropTypes.func, + format: PropTypes.string, position: PropTypes.oneOf([null, 'tl', 'tr', 'bl', 'br']), }; @@ -32,10 +36,34 @@ const defaultProps = { categories: {}, toggleCategory: () => {}, showSingleCategory: () => {}, + format: null, position: 'tr', }; export default class Legend extends React.PureComponent { + format(value) { + if (!this.props.format) { + return value; + } + + const numValue = parseFloat(value); + return formatNumber(this.props.format, numValue); + + } + + formatCategoryLabel(k) { + if (!this.props.format) { + return k; + } + + if (k.includes(categoryDelimiter)) { + const values = k.split(categoryDelimiter); + return this.format(values[0]) + categoryDelimiter + this.format(values[1]); + } + + return this.format(k); + } + render() { if (Object.keys(this.props.categories).length === 0 || this.props.position === null) { return null; @@ -51,7 +79,7 @@ export default class Legend extends React.PureComponent { onClick={() => this.props.toggleCategory(k)} onDoubleClick={() => this.props.showSingleCategory(k)} > - {icon} {k} + {icon} {this.formatCategoryLabel(k)} ); diff --git a/superset/assets/src/visualizations/deckgl/CategoricalDeckGLContainer.jsx b/superset/assets/src/visualizations/deckgl/CategoricalDeckGLContainer.jsx index e1933a4b1b50e..7352b3d49512c 100644 --- a/superset/assets/src/visualizations/deckgl/CategoricalDeckGLContainer.jsx +++ b/superset/assets/src/visualizations/deckgl/CategoricalDeckGLContainer.jsx @@ -237,6 +237,7 @@ export default class CategoricalDeckGLContainer extends React.PureComponent { toggleCategory={this.toggleCategory} showSingleCategory={this.showSingleCategory} position={this.props.formData.legend_position} + format={this.props.formData.legend_format} /> diff --git a/superset/assets/src/visualizations/deckgl/layers/Polygon/Polygon.jsx b/superset/assets/src/visualizations/deckgl/layers/Polygon/Polygon.jsx index 3797d7e7ebba7..891856d04302a 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Polygon/Polygon.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Polygon/Polygon.jsx @@ -274,6 +274,7 @@ class DeckGLPolygon extends React.Component { } From f039e17cd3a75911353668dd08fe9e2553300b91 Mon Sep 17 00:00:00 2001 From: Erik Ritter Date: Mon, 12 Aug 2019 12:24:45 -0700 Subject: [PATCH 26/40] [SQL Lab] Improve perf for filterable table rendering (#8011) --- superset/assets/package-lock.json | 6 +-- superset/assets/package.json | 2 +- .../FilterableTable/FilterableTable.jsx | 38 +++++++++++-------- .../FilterableTable/FilterableTableStyles.css | 4 ++ 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json index 6d6b236f09c7f..556e318855d4a 100644 --- a/superset/assets/package-lock.json +++ b/superset/assets/package-lock.json @@ -2616,9 +2616,9 @@ } }, "@superset-ui/dimension": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@superset-ui/dimension/-/dimension-0.11.10.tgz", - "integrity": "sha512-gOP32Ibf0FwElD8ImDbfUkw0e+Sw9x0cP2ZXlgp2n00360inq8XC4Bq62t1B6oFrMRRoeCDGzYb5pe6QrIPvSg==" + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/@superset-ui/dimension/-/dimension-0.11.15.tgz", + "integrity": "sha512-VpEggw1taDSNOfYGlh1HovCDRdkSvfG5UMe40j5iqOLcxqCfXAUbmMc8Z9ganZsoQMTA1F9H31kCdSsc7lUfMw==" }, "@superset-ui/legacy-plugin-chart-calendar": { "version": "0.10.11", diff --git a/superset/assets/package.json b/superset/assets/package.json index e3b528e3f11c4..91db5ad1facf0 100644 --- a/superset/assets/package.json +++ b/superset/assets/package.json @@ -51,7 +51,7 @@ "@superset-ui/color": "^0.11.9", "@superset-ui/connection": "^0.11.14", "@superset-ui/core": "^0.11.10", - "@superset-ui/dimension": "^0.11.10", + "@superset-ui/dimension": "^0.11.15", "@superset-ui/legacy-plugin-chart-calendar": "^0.10.11", "@superset-ui/legacy-plugin-chart-chord": "^0.10.11", "@superset-ui/legacy-plugin-chart-country-map": "^0.10.11", diff --git a/superset/assets/src/components/FilterableTable/FilterableTable.jsx b/superset/assets/src/components/FilterableTable/FilterableTable.jsx index 698ce20b6633a..0e17b3315f279 100644 --- a/superset/assets/src/components/FilterableTable/FilterableTable.jsx +++ b/superset/assets/src/components/FilterableTable/FilterableTable.jsx @@ -29,7 +29,7 @@ import { SortIndicator, Table, } from 'react-virtualized'; -import { getTextDimension } from '@superset-ui/dimension'; +import { getMultipleTextDimensions } from '@superset-ui/dimension'; import { t } from '@superset-ui/translation'; import Button from '../Button'; @@ -37,10 +37,6 @@ import CopyToClipboard from '../CopyToClipboard'; import ModalTrigger from '../ModalTrigger'; import TooltipWrapper from '../TooltipWrapper'; -function getTextWidth(text, font = '12px Roboto') { - return getTextDimension({ text, style: { font } }).width; -} - function safeJsonObjectParse(data) { // First perform a cheap proxy to avoid calling JSON.parse on data that is clearly not a // JSON object or array @@ -159,17 +155,27 @@ export default class FilterableTable extends PureComponent { getWidthsForColumns() { const PADDING = 40; // accounts for cell padding and width of sorting icon const widthsByColumnKey = {}; - this.props.orderedColumnKeys.forEach((key) => { - const colWidths = this.list - // get width for each value for a key - .map(d => getTextWidth( - this.getCellContent({ cellData: d[key], columnKey: key })) + PADDING, - ) - // add width of column key to end of list - .push(getTextWidth(key) + PADDING); - // set max width as value for key - widthsByColumnKey[key] = Math.max(...colWidths); + const cellContent = [].concat(...this.props.orderedColumnKeys.map(key => + this.list + .map(data => this.getCellContent({ cellData: data[key], columnKey: key })) + .push(key) + .toJS(), + )); + + const colWidths = getMultipleTextDimensions( + { + className: 'cell-text-for-measuring', + texts: cellContent, + }, + ).map(dimension => dimension.width); + + this.props.orderedColumnKeys.forEach((key, index) => { + widthsByColumnKey[key] = Math.max(...colWidths.slice( + index * (this.list.size + 1), + (index + 1) * (this.list.size + 1), + )) + PADDING; }); + return widthsByColumnKey; } @@ -411,7 +417,7 @@ export default class FilterableTable extends PureComponent { // sort list if (sortBy) { sortedAndFilteredList = sortedAndFilteredList - .sort(this.sortResults(sortBy, sortDirection === SortDirection.DESC)); + .sort(this.sortResults(sortBy, sortDirection === SortDirection.DESC)); } let { height } = this.props; diff --git a/superset/assets/src/components/FilterableTable/FilterableTableStyles.css b/superset/assets/src/components/FilterableTable/FilterableTableStyles.css index 3db6e3a7c7667..4bf21072cf469 100644 --- a/superset/assets/src/components/FilterableTable/FilterableTableStyles.css +++ b/superset/assets/src/components/FilterableTable/FilterableTableStyles.css @@ -88,3 +88,7 @@ white-space: nowrap; color: #aaa; } +.cell-text-for-measuring { + font-family: Helvetica, Arial, sans-serif; + font-size: 12px; +} From 537574519341988338c1dc35bb23cbbf909decf4 Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Mon, 12 Aug 2019 14:01:15 -0700 Subject: [PATCH 27/40] Improve release instructions (#8016) --- RELEASING/README.md | 114 ++++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 62 deletions(-) diff --git a/RELEASING/README.md b/RELEASING/README.md index 7a2f0aa0d62ed..db53bcc51040b 100644 --- a/RELEASING/README.md +++ b/RELEASING/README.md @@ -16,57 +16,6 @@ KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> -# Apache Releases - -To make a fresh tarball of a git reference on apache/incubator-superset -(push your tag first!) - -```bash -docker build -t make_tarball -f Dockerfile.make_tarball . --build-arg VERSION=0.33.0rc1 -docker run make_tarball -f Dockerfile.make_tarball --env VERSION=0.33.0rc1 -``` - -To make a working build given a tarball -```bash -# Building a docker from a tarball -VERSION=0.33.0rc2 && \ -docker build -t apache-superset:$VERSION -f Dockerfile.from_tarball . --build-arg VERSION=$VERSION - -# testing the resulting docker -docker run -p 5001:8088 apache-superset:0.33.0rc2 -# you should be able to access localhost:5001 on your browser -# login using admin/admin -``` - -## Refresh documentation website - -Every once in a while we want to compile the documentation and publish it. -Here's how to do it. - -```bash -# install doc dependencies -pip install -r docs/requirements.txt - -# build the docs -python setup.py build_sphinx - -# copy html files to temp folder -cp -r docs/_build/html/ /tmp/tmp_superset_docs/ - -# clone the docs repo -cd ~/ -git clone https://git-wip-us.apache.org/repos/asf/incubator-superset-site.git - -# copy -cp -r /tmp/tmp_superset_docs/ ~/incubator-superset-site.git/ - -# commit and push to `asf-site` branch -cd ~/incubator-superset-site.git/ -git checkout asf-site -git add . -git commit -a -m "New doc version" -git push origin master -``` # Apache Releases @@ -81,20 +30,20 @@ need to be done at every release. ```bash # Create PGP Key, and use your @apache.org email address gpg --gen-key - + # Checkout ASF dist repo svn checkout https://dist.apache.org/repos/dist/dev/incubator/superset/ ~/svn/superset_dev svn checkout https://dist.apache.org/repos/dist/release/incubator/superset/ ~/svn/superset cd ~/svn/superset - - + + # Add your GPG pub key to KEYS file. Replace "Maxime Beauchemin" with your name export FULLNAME="Maxime Beauchemin" (gpg --list-sigs $FULLNAME && gpg --armor --export $FULLNAME ) >> KEYS - - + + # Commit the changes svn commit -m "Add PGP keys of new Superset committer" ``` @@ -107,7 +56,7 @@ Now let's craft a source release # Setting a VERSION var will be useful export VERSION=0.31.0rc18 export RELEASE=apache-superset-incubating-${VERSION} - export RELEASE_TARBAL=${RELEASE}-source.tar.gz + export RELEASE_TARBALL=${RELEASE}-source.tar.gz # Let's create a git tag git tag -f ${VERSION} @@ -122,10 +71,10 @@ Now let's craft a source release mkdir -p ~/svn/superset_dev/${VERSION}/ git archive \ --format=tar.gz ${VERSION} \ - --prefix=${RELEASE}/ \ + --prefix="${RELEASE}/" \ -o ~/svn/superset_dev/${VERSION}/${RELEASE_TARBALL} - cd ~/svn/superset_dev/ + cd ~/svn/superset_dev/${VERSION}/ scripts/sign.sh ${RELEASE}-source.tar.gz ``` @@ -134,9 +83,6 @@ Now let's craft a source release Now let's ship this RC into svn's dev folder ```bash - # cp or mv the files over to the svn repo - mkdir ~/svn/superset_dev/${VERSION}/ - cp ${RELEASE_TARBALL} ~/svn/superset_dev/${VERSION}/ cd ~/svn/superset_dev/ svn add ${VERSION} svn commit @@ -161,3 +107,47 @@ folder. Now you can announce the release on the mailing list, make sure to use the proper template + +## Build from source tarball + +To make a working build given a tarball +```bash +# Building a docker from a tarball +VERSION=0.33.0rc2 && \ +docker build -t apache-superset:$VERSION -f Dockerfile.from_tarball . --build-arg VERSION=$VERSION + +# testing the resulting docker +docker run -p 5001:8088 apache-superset:$VERSION +# you should be able to access localhost:5001 on your browser +# login using admin/admin +``` + +# Refresh documentation website + +Every once in a while we want to compile the documentation and publish it. +Here's how to do it. + +```bash +# install doc dependencies +pip install -r docs/requirements.txt + +# build the docs +python setup.py build_sphinx + +# copy html files to temp folder +cp -r docs/_build/html/ /tmp/tmp_superset_docs/ + +# clone the docs repo +cd ~/ +git clone https://git-wip-us.apache.org/repos/asf/incubator-superset-site.git + +# copy +cp -r /tmp/tmp_superset_docs/ ~/incubator-superset-site.git/ + +# commit and push to `asf-site` branch +cd ~/incubator-superset-site.git/ +git checkout asf-site +git add . +git commit -a -m "New doc version" +git push origin master +``` From 0754f294e9d6195f9f233606cedf7fb5ec489271 Mon Sep 17 00:00:00 2001 From: Erik Ritter Date: Mon, 12 Aug 2019 21:55:46 -0700 Subject: [PATCH 28/40] [Dashboard] Hide slice titles when they're empty (#8018) --- superset/assets/src/components/EditableTitle.jsx | 12 +++++++----- .../assets/src/dashboard/components/SliceHeader.jsx | 1 + 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/superset/assets/src/components/EditableTitle.jsx b/superset/assets/src/components/EditableTitle.jsx index 428f9953baf5b..80e91fdf71744 100644 --- a/superset/assets/src/components/EditableTitle.jsx +++ b/superset/assets/src/components/EditableTitle.jsx @@ -84,6 +84,8 @@ export default class EditableTitle extends React.PureComponent { } handleBlur() { + const title = this.state.title.trim(); + if (!this.props.canEdit) { return; } @@ -92,7 +94,7 @@ export default class EditableTitle extends React.PureComponent { isEditing: false, }); - if (!this.state.title.length) { + if (!title.length) { this.setState({ title: this.state.lastTitle, }); @@ -100,14 +102,14 @@ export default class EditableTitle extends React.PureComponent { return; } - if (this.state.lastTitle !== this.state.title) { + if (this.state.lastTitle !== title) { this.setState({ - lastTitle: this.state.title, + lastTitle: title, }); } - if (this.props.title !== this.state.title) { - this.props.onSaveTitle(this.state.title); + if (this.props.title !== title) { + this.props.onSaveTitle(title); } } diff --git a/superset/assets/src/dashboard/components/SliceHeader.jsx b/superset/assets/src/dashboard/components/SliceHeader.jsx index 723ae3e04ac47..100a0703c46bf 100644 --- a/superset/assets/src/dashboard/components/SliceHeader.jsx +++ b/superset/assets/src/dashboard/components/SliceHeader.jsx @@ -109,6 +109,7 @@ class SliceHeader extends React.PureComponent { : '') } canEdit={editMode} + emptyText="" onSaveTitle={updateSliceName} showTooltip={false} /> From 075b5a5d33e78c6da6ec72d9c2ece37d8a0adce7 Mon Sep 17 00:00:00 2001 From: Kshira Saagar Date: Tue, 13 Aug 2019 13:37:11 +0800 Subject: [PATCH 29/40] Two more organisations using Superset - Zalora, GFG (#8026) --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 341b9567565ac..72b859ce24749 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,7 @@ the world know they are using Superset. Join our growing community! 1. [FBK - ICT center](http://ict.fbk.eu) 1. [Faasos](http://faasos.com/) 1. [Fordeal](http://www.fordeal.com) + 1. [GFG - Global Fashion Group](https://global-fashion-group.com) 1. [GfK Data Lab](http://datalab.gfk.com) 1. [Grassroot](https://www.grassrootinstitute.org/) 1. [Hostnfly](https://www.hostnfly.com/) @@ -197,7 +198,7 @@ the world know they are using Superset. Join our growing community! 1. [Showmax](https://tech.showmax.com) 1. [Tails.com](https://tails.com) 1. [Tenable](https://www.tenable.com) - 1. [THEICONIC](http://theiconic.com.au/) + 1. [THE ICONIC](http://theiconic.com.au/) 1. [Tobii](http://www.tobii.com/) 1. [Tooploox](https://www.tooploox.com/) 1. [TrustMedis](https://trustmedis.com) @@ -209,3 +210,4 @@ the world know they are using Superset. Join our growing community! 1. [Yahoo!](https://yahoo.com/) 1. [Zaihang](http://www.zaih.com/) 1. [Zalando](https://www.zalando.com) + 1. [Zalora](https://www.zalora.com) From 17f07406926bdbb548e87c29dc3dc16177a8f0cf Mon Sep 17 00:00:00 2001 From: serenajiang Date: Tue, 13 Aug 2019 13:06:44 -0700 Subject: [PATCH 30/40] Fix bug where lists in queries cannot contain multiple types (#8032) * use set instead of sorted to check equality of lists * run black --- superset/utils/core.py | 4 +--- tests/utils_tests.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/superset/utils/core.py b/superset/utils/core.py index 69c70f575c381..fa3462aa0cb43 100644 --- a/superset/utils/core.py +++ b/superset/utils/core.py @@ -907,9 +907,7 @@ def get_filter_key(f): if isinstance(existing_filters[filter_key], list): # Add filters for unequal lists # order doesn't matter - if sorted(existing_filters[filter_key]) != sorted( - filtr["val"] - ): + if set(existing_filters[filter_key]) != set(filtr["val"]): form_data["adhoc_filters"].append(to_adhoc(filtr)) else: form_data["adhoc_filters"].append(to_adhoc(filtr)) diff --git a/tests/utils_tests.py b/tests/utils_tests.py index 1df71a7788c19..47f8b297334ba 100644 --- a/tests/utils_tests.py +++ b/tests/utils_tests.py @@ -301,6 +301,7 @@ def test_merge_extra_filters_ignores_equal_filters(self): "extra_filters": [ {"col": "a", "op": "in", "val": "someval"}, {"col": "B", "op": "==", "val": ["c1", "c2"]}, + {"col": "c", "op": "in", "val": ["c1", 1, None]}, ], "adhoc_filters": [ { @@ -317,6 +318,13 @@ def test_merge_extra_filters_ignores_equal_filters(self): "operator": "==", "subject": "B", }, + { + "clause": "WHERE", + "comparator": ["c1", 1, None], + "expressionType": "SIMPLE", + "operator": "in", + "subject": "c", + }, ], } expected = { @@ -335,6 +343,13 @@ def test_merge_extra_filters_ignores_equal_filters(self): "operator": "==", "subject": "B", }, + { + "clause": "WHERE", + "comparator": ["c1", 1, None], + "expressionType": "SIMPLE", + "operator": "in", + "subject": "c", + }, ] } merge_extra_filters(form_data) From 613dcf5def6b6c7e3eedbaac442614edd38a95b8 Mon Sep 17 00:00:00 2001 From: Grace Guo Date: Tue, 13 Aug 2019 13:49:39 -0700 Subject: [PATCH 31/40] [fix] Allow dashboard viewer auto refresh dashboard (#8014) --- .../components/HeaderActionsDropdown_spec.jsx | 8 +++--- .../components/RefreshIntervalModal_spec.jsx | 8 ++++++ .../src/dashboard/actions/dashboardState.js | 4 +-- .../components/HeaderActionsDropdown.jsx | 25 ++++++++++++------- .../components/RefreshIntervalModal.jsx | 19 ++++++++------ .../src/dashboard/reducers/dashboardState.js | 2 +- 6 files changed, 43 insertions(+), 23 deletions(-) diff --git a/superset/assets/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx b/superset/assets/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx index f89c9ed942eb0..cd1c0156c28ca 100644 --- a/superset/assets/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx +++ b/superset/assets/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx @@ -71,9 +71,9 @@ describe('HeaderActionsDropdown', () => { expect(wrapper.find(MenuItem)).toHaveLength(1); }); - it('should not render the RefreshIntervalModal', () => { + it('should render the RefreshIntervalModal', () => { const wrapper = setup(overrideProps); - expect(wrapper.find(RefreshIntervalModal)).toHaveLength(0); + expect(wrapper.find(RefreshIntervalModal)).toHaveLength(1); }); it('should render the URLShortLinkModal', () => { @@ -105,9 +105,9 @@ describe('HeaderActionsDropdown', () => { expect(wrapper.find(MenuItem)).toHaveLength(2); }); - it('should not render the RefreshIntervalModal', () => { + it('should render the RefreshIntervalModal', () => { const wrapper = setup(overrideProps); - expect(wrapper.find(RefreshIntervalModal)).toHaveLength(0); + expect(wrapper.find(RefreshIntervalModal)).toHaveLength(1); }); it('should render the URLShortLinkModal', () => { diff --git a/superset/assets/spec/javascripts/dashboard/components/RefreshIntervalModal_spec.jsx b/superset/assets/spec/javascripts/dashboard/components/RefreshIntervalModal_spec.jsx index 8dfb4016f07c9..9e0c39879d4e9 100644 --- a/superset/assets/spec/javascripts/dashboard/components/RefreshIntervalModal_spec.jsx +++ b/superset/assets/spec/javascripts/dashboard/components/RefreshIntervalModal_spec.jsx @@ -25,6 +25,8 @@ describe('RefreshIntervalModal', () => { const mockedProps = { triggerNode: , refreshFrequency: 10, + onChange: jest.fn(), + editMode: true, }; it('is valid', () => { expect( @@ -39,4 +41,10 @@ describe('RefreshIntervalModal', () => { const wrapper = mount(); expect(wrapper.prop('refreshFrequency')).toEqual(10); }); + it('should change refreshFrequency with edit mode', () => { + const wrapper = mount(); + wrapper.instance().handleFrequencyChange({ value: 30 }); + expect(mockedProps.onChange).toHaveBeenCalled(); + expect(mockedProps.onChange).toHaveBeenCalledWith(30, mockedProps.editMode); + }); }); diff --git a/superset/assets/src/dashboard/actions/dashboardState.js b/superset/assets/src/dashboard/actions/dashboardState.js index db4d3695a6c2e..02270809abd3e 100644 --- a/superset/assets/src/dashboard/actions/dashboardState.js +++ b/superset/assets/src/dashboard/actions/dashboardState.js @@ -151,8 +151,8 @@ export function onSave() { } export const SET_REFRESH_FREQUENCY = 'SET_REFRESH_FREQUENCY'; -export function setRefreshFrequency(refreshFrequency) { - return { type: SET_REFRESH_FREQUENCY, refreshFrequency }; +export function setRefreshFrequency(refreshFrequency, isPersistent = false) { + return { type: SET_REFRESH_FREQUENCY, refreshFrequency, isPersistent }; } export function saveDashboardRequestSuccess() { diff --git a/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx b/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx index b96096517964c..af82267f1d221 100644 --- a/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx +++ b/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx @@ -103,8 +103,8 @@ class HeaderActionsDropdown extends React.PureComponent { this.props.updateCss(css); } - changeRefreshInterval(refreshInterval) { - this.props.setRefreshFrequency(refreshInterval); + changeRefreshInterval(refreshInterval, isPersistent) { + this.props.setRefreshFrequency(refreshInterval, isPersistent); this.props.startPeriodicRender(refreshInterval * 1000); } @@ -177,13 +177,20 @@ class HeaderActionsDropdown extends React.PureComponent { {t('Force refresh dashboard')} - {editMode && ( - {t('Set auto-refresh interval')}} - /> - )} + + + {editMode + ? t('Set auto-refresh interval') + : t('Auto-refresh dashboard')} + + } + /> + {editMode && ( {t('Edit dashboard metadata')} diff --git a/superset/assets/src/dashboard/components/RefreshIntervalModal.jsx b/superset/assets/src/dashboard/components/RefreshIntervalModal.jsx index b314431b3ba90..485f4091743cd 100644 --- a/superset/assets/src/dashboard/components/RefreshIntervalModal.jsx +++ b/superset/assets/src/dashboard/components/RefreshIntervalModal.jsx @@ -27,6 +27,7 @@ const propTypes = { triggerNode: PropTypes.node.isRequired, refreshFrequency: PropTypes.number.isRequired, onChange: PropTypes.func.isRequired, + editMode: PropTypes.bool.isRequired, }; const options = [ @@ -48,7 +49,17 @@ class RefreshIntervalModal extends React.PureComponent { this.state = { refreshFrequency: props.refreshFrequency, }; + this.handleFrequencyChange = this.handleFrequencyChange.bind(this); } + + handleFrequencyChange(opt) { + const value = opt ? opt.value : options[0].value; + this.setState({ + refreshFrequency: value, + }); + this.props.onChange(value, this.props.editMode); + } + render() { return ( { - const value = opt ? opt.value : options[0].value; - this.setState({ - refreshFrequency: value, - }); - this.props.onChange(value); - }} + onChange={this.handleFrequencyChange} /> } diff --git a/superset/assets/src/dashboard/reducers/dashboardState.js b/superset/assets/src/dashboard/reducers/dashboardState.js index 4a12ce05fcefb..f1c8ee820d64d 100644 --- a/superset/assets/src/dashboard/reducers/dashboardState.js +++ b/superset/assets/src/dashboard/reducers/dashboardState.js @@ -169,7 +169,7 @@ export default function dashboardStateReducer(state = {}, action) { return { ...state, refreshFrequency: action.refreshFrequency, - hasUnsavedChanges: true, + hasUnsavedChanges: action.isPersistent, }; }, }; From a852eaaa59159c336331c14c60e4d6cdd2989478 Mon Sep 17 00:00:00 2001 From: semantiDan <41392410+semantiDan@users.noreply.github.com> Date: Wed, 14 Aug 2019 17:28:03 +0300 Subject: [PATCH 32/40] Added WPSemantix to list of companies using Apache Superset in README.md file (#8045) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 72b859ce24749..1d4a45efe97c6 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,7 @@ the world know they are using Superset. Join our growing community! 1. [VIPKID](https://www.vipkid.com.cn/) 1. [WeSure](https://www.wesure.cn/) 1. [Windsor.ai](https://www.windsor.ai/) + 1. [WP-Semantix](https://wpsemantix.com/) 1. [Yahoo!](https://yahoo.com/) 1. [Zaihang](http://www.zaih.com/) 1. [Zalando](https://www.zalando.com) From 40c21dc1d7557ac5d46a8dd97edea4cd1427fd2e Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Wed, 14 Aug 2019 07:44:55 -0700 Subject: [PATCH 33/40] Disable flaky cypress test (#8039) ref: https://travis-ci.org/mistercrunch/superset/jobs/571524160 ``` 1) Dashboard top-level controls should allow dashboard level force refresh: CypressError: Timed out retrying: expected '
  • ' to have class 'disabled' at Object.cypressErr (http://localhost:8081/__cypress/runner/cypress_runner.js:65283:11) at Object.throwErr (http://localhost:8081/__cypress/runner/cypress_runner.js:65248:18) at Object.throwErrByPath (http://localhost:8081/__cypress/runner/cypress_runner.js:65275:17) at retry (http://localhost:8081/__cypress/runner/cypress_runner.js:58816:16) at http://localhost:8081/__cypress/runner/cypress_runner.js:50924:18 at tryCatcher (http://localhost:8081/__cypress/runner/cypress_runner.js:127195:23) at Promise._settlePromiseFromHandler (http://localhost:8081/__cypress/runner/cypress_runner.js:125213:31) at Promise._settlePromise (http://localhost:8081/__cypress/runner/cypress_runner.js:125270:18) at Promise._settlePromise0 (http://localhost:8081/__cypress/runner/cypress_runner.js:125315:10) at Promise._settlePromises (http://localhost:8081/__cypress/runner/cypress_runner.js:125390:18) at Async._drainQueue (http://localhost:8081/__cypress/runner/cypress_runner.js:122119:16) at Async._drainQueues (http://localhost:8081/__cypress/runner/cypress_runner.js:122129:10) at Async.drainQueues (http://localhost:8081/__cypress/runner/cypress_runner.js:122003:14) at ``` --- superset/assets/cypress/integration/dashboard/controls.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset/assets/cypress/integration/dashboard/controls.js b/superset/assets/cypress/integration/dashboard/controls.js index 77974a0b8c16d..c569aad85eeb6 100644 --- a/superset/assets/cypress/integration/dashboard/controls.js +++ b/superset/assets/cypress/integration/dashboard/controls.js @@ -74,7 +74,7 @@ export default () => describe('top-level controls', () => { cy.contains('Force refresh dashboard').parent().not('have.class', 'disabled'); }); - it('should allow dashboard level force refresh', () => { + it.skip('should allow dashboard level force refresh', () => { // when charts are not start loading, for example, under a secondary tab, // should allow force refresh cy.get('#save-dash-split-button').trigger('click'); From ed8bb1b694e95df661bb99ff877097d749a0c9e0 Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Wed, 14 Aug 2019 09:27:18 -0700 Subject: [PATCH 34/40] fix: issues #8041 - bubble support for complex metrics (#8044) --- superset/viz.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/superset/viz.py b/superset/viz.py index abd908c1918ab..ae4b0a7095c1e 100644 --- a/superset/viz.py +++ b/superset/viz.py @@ -966,7 +966,9 @@ def query_obj(self): self.series = form_data.get("series") or self.entity d["row_limit"] = form_data.get("limit") - d["metrics"] = list(set([self.z_metric, self.x_metric, self.y_metric])) + d["metrics"] = [self.z_metric, self.x_metric, self.y_metric] + if len(set(self.metric_labels)) < 3: + raise Exception(_("Please use 3 different metric labels")) if not all(d["metrics"] + [self.entity]): raise Exception(_("Pick a metric for x, y and size")) return d From 8773fdceb283c5ad830918e9b81ac2fbe2269bdf Mon Sep 17 00:00:00 2001 From: Ville Brofeldt <33317356+villebro@users.noreply.github.com> Date: Thu, 15 Aug 2019 19:20:49 +0300 Subject: [PATCH 35/40] [bugfix] Fix deck_polygon metric bug and update examples chart (#8025) * Fix deck_polygon bug and update examples chart * Change js elevation to native ad-hoc metric * Move scale from metric to native multiplier * Remove redundant js_columns and change color scheme --- superset/examples/deck.py | 50 ++++++++++++++++++++++++++++++++------- superset/viz.py | 4 +++- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/superset/examples/deck.py b/superset/examples/deck.py index 6cd0441990056..f90689d832529 100644 --- a/superset/examples/deck.py +++ b/superset/examples/deck.py @@ -335,8 +335,30 @@ def load_deck_dash(): "time_grain_sqla": None, "time_range": " : ", "line_column": "contour", - "metric": None, + "metric": { + "aggregate": "SUM", + "column": { + "column_name": "population", + "description": None, + "expression": None, + "filterable": True, + "groupby": True, + "id": 1332, + "is_dttm": False, + "optionName": "_col_population", + "python_date_format": None, + "type": "BIGINT", + "verbose_name": None, + }, + "expressionType": "SIMPLE", + "fromFormData": True, + "hasCustomLabel": True, + "label": "Population", + "optionName": "metric_t2v4qbfiz1_w6qgpx4h2p", + "sqlExpression": None, + }, "line_type": "json", + "linear_color_scheme": "oranges", "mapbox_style": "mapbox://styles/mapbox/light-v9", "viewport": { "longitude": -122.43388541747726, @@ -360,16 +382,26 @@ def load_deck_dash(): "filled": True, "stroked": False, "extruded": True, - "point_radius_scale": 100, - "point_radius_fixed": {"type": "metric", "value": "count"}, - "multiplier": 1, - "js_columns": ["population", "area"], - "js_data_mutator": "data => data.map(d => ({\n" - " ...d,\n" - " elevation: d.extraProps.population/d.extraProps.area/10,\n" - "}));", + "multiplier": 0.1, + "point_radius_fixed": { + "type": "metric", + "value": { + "aggregate": None, + "column": None, + "expressionType": "SQL", + "fromFormData": None, + "hasCustomLabel": None, + "label": "Density", + "optionName": "metric_c5rvwrzoo86_293h6yrv2ic", + "sqlExpression": "SUM(population)/SUM(area)", + }, + }, + "js_columns": [], + "js_data_mutator": "", "js_tooltip": "", "js_onclick_href": "", + "legend_format": ".1s", + "legend_position": "tr", "where": "", "having": "", "filters": [], diff --git a/superset/viz.py b/superset/viz.py index ae4b0a7095c1e..cecfe2ddebdab 100644 --- a/superset/viz.py +++ b/superset/viz.py @@ -2434,7 +2434,9 @@ def get_properties(self, d): fd = self.form_data elevation = fd["point_radius_fixed"]["value"] type_ = fd["point_radius_fixed"]["type"] - d["elevation"] = d.get(elevation["label"]) if type_ == "metric" else elevation + d["elevation"] = ( + d.get(utils.get_metric_name(elevation)) if type_ == "metric" else elevation + ) return d From 5e0c91ef49c29d7b5de8164ccdd0f91cf21ddc0d Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Thu, 15 Aug 2019 14:13:18 -0700 Subject: [PATCH 36/40] fix: onSave datasource raises React error (#8049) * fix: datasource save raises React error * add test --- .../javascripts/explore/components/MetricsControl_spec.jsx | 7 +++++++ .../src/explore/components/controls/MetricsControl.jsx | 6 ++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/superset/assets/spec/javascripts/explore/components/MetricsControl_spec.jsx b/superset/assets/spec/javascripts/explore/components/MetricsControl_spec.jsx index 143bf8f99f900..b6fbf1bacd3bc 100644 --- a/superset/assets/spec/javascripts/explore/components/MetricsControl_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/MetricsControl_spec.jsx @@ -368,5 +368,12 @@ describe('MetricsControl', () => { wrapper.setProps({ ...props, columns: [] }); expect(onChange.calledOnce).toEqual(false); }); + it('Does not fail if no columns or savedMetrics are passed', () => { + const { wrapper } = setup({ + savedMetrics: null, + columns: null, + }); + expect(wrapper.exists('.metrics-select')).toEqual(true); + }); }); }); diff --git a/superset/assets/src/explore/components/controls/MetricsControl.jsx b/superset/assets/src/explore/components/controls/MetricsControl.jsx index 9498b08752402..a998283f0ccfd 100644 --- a/superset/assets/src/explore/components/controls/MetricsControl.jsx +++ b/superset/assets/src/explore/components/controls/MetricsControl.jsx @@ -54,6 +54,8 @@ const propTypes = { const defaultProps = { onChange: () => {}, clearable: true, + savedMetrics: [], + columns: [], }; function isDictionaryForAdhocMetric(value) { @@ -62,7 +64,7 @@ function isDictionaryForAdhocMetric(value) { function columnsContainAllMetrics(value, nextProps) { const columnNames = new Set( - [...nextProps.columns, ...nextProps.savedMetrics] + [...(nextProps.columns || []), ...(nextProps.savedMetrics || [])] // eslint-disable-next-line camelcase .map(({ column_name, metric_name }) => (column_name || metric_name)), ); @@ -243,7 +245,7 @@ export default class MetricsControl extends React.PureComponent { Object.keys(AGGREGATES).map(aggregate => ({ aggregate_name: aggregate })) : []; const options = [ - ...columns, + ...(columns || []), ...aggregates, ...(savedMetrics || []), ]; From ef1d4a6aa16c8ae510cc63a4b04ee158c4be7a30 Mon Sep 17 00:00:00 2001 From: michellethomas Date: Thu, 15 Aug 2019 14:42:03 -0700 Subject: [PATCH 37/40] Set disableErrorBoundary in SuperChart to fix chart error handling (#8052) --- superset/assets/src/chart/ChartRenderer.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/superset/assets/src/chart/ChartRenderer.jsx b/superset/assets/src/chart/ChartRenderer.jsx index 3e699872e8508..626bffc5bfe21 100644 --- a/superset/assets/src/chart/ChartRenderer.jsx +++ b/superset/assets/src/chart/ChartRenderer.jsx @@ -191,6 +191,7 @@ class ChartRenderer extends React.Component { {this.renderTooltip()} Date: Thu, 15 Aug 2019 20:10:05 -0700 Subject: [PATCH 38/40] Add feature flag for Presto expand data (#8056) * Add feature flag for Presto expand data * Fix unit tests * Fix black * Revert temporary file change --- docs/installation.rst | 7 ++++++- superset/config.py | 1 + superset/db_engine_specs/presto.py | 4 ++++ tests/db_engine_specs_test.py | 9 +++++++++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index e5606574fe857..3264ab1707753 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -1185,7 +1185,8 @@ You can enable or disable features with flag from ``superset_config.py``: DEFAULT_FEATURE_FLAGS = { 'CLIENT_CACHE': False, - 'ENABLE_EXPLORE_JSON_CSRF_PROTECTION': False + 'ENABLE_EXPLORE_JSON_CSRF_PROTECTION': False, + 'PRESTO_EXPAND_DATA': False, } Here is a list of flags and descriptions: @@ -1195,3 +1196,7 @@ Here is a list of flags and descriptions: * For some security concerns, you may need to enforce CSRF protection on all query request to explore_json endpoint. In Superset, we use `flask-csrf `_ add csrf protection for all POST requests, but this protection doesn't apply to GET method. * When ENABLE_EXPLORE_JSON_CSRF_PROTECTION is set to true, your users cannot make GET request to explore_json. The default value for this feature False (current behavior), explore_json accepts both GET and POST request. See `PR 7935 `_ for more details. + +* PRESTO_EXPAND_DATA + + * When this feature is enabled, nested types in Presto will be expanded into extra columns and/or arrays. This is experimental, and doesn't work with all nested types. diff --git a/superset/config.py b/superset/config.py index bcdd0f63c6805..085c8e2e56bc9 100644 --- a/superset/config.py +++ b/superset/config.py @@ -208,6 +208,7 @@ # Experimental feature introducing a client (browser) cache "CLIENT_CACHE": False, "ENABLE_EXPLORE_JSON_CSRF_PROTECTION": False, + "PRESTO_EXPAND_DATA": False, } # A function that receives a dict of all feature flags diff --git a/superset/db_engine_specs/presto.py b/superset/db_engine_specs/presto.py index 6708b0671a2c9..a2b02cdcf409d 100644 --- a/superset/db_engine_specs/presto.py +++ b/superset/db_engine_specs/presto.py @@ -30,6 +30,7 @@ from sqlalchemy.engine.result import RowProxy from sqlalchemy.sql.expression import ColumnClause +from superset import is_feature_enabled from superset.db_engine_specs.base import BaseEngineSpec from superset.exceptions import SupersetTemplateException from superset.models.sql_types.presto_sql_types import type_map as presto_type_map @@ -749,6 +750,9 @@ def expand_data( :return: list of all columns(selected columns and their nested fields), expanded data set, listed of nested fields """ + if not is_feature_enabled("PRESTO_EXPAND_DATA"): + return columns, data, [] + all_columns: List[dict] = [] # Get the list of all columns (selected fields and their nested fields) for column in columns: diff --git a/tests/db_engine_specs_test.py b/tests/db_engine_specs_test.py index a70c8cdb2ad87..acd7180f1ca4f 100644 --- a/tests/db_engine_specs_test.py +++ b/tests/db_engine_specs_test.py @@ -614,6 +614,9 @@ def test_presto_remove_processed_array_columns(self): } self.assertEqual(array_col_hierarchy, expected_array_col_hierarchy) + @mock.patch.dict( + "superset._feature_flags", {"PRESTO_EXPAND_DATA": True}, clear=True + ) def test_presto_expand_data_with_simple_structural_columns(self): cols = [ {"name": "row_column", "type": "ROW(NESTED_OBJ VARCHAR)"}, @@ -644,6 +647,9 @@ def test_presto_expand_data_with_simple_structural_columns(self): self.assertEqual(actual_data, expected_data) self.assertEqual(actual_expanded_cols, expected_expanded_cols) + @mock.patch.dict( + "superset._feature_flags", {"PRESTO_EXPAND_DATA": True}, clear=True + ) def test_presto_expand_data_with_complex_row_columns(self): cols = [ { @@ -684,6 +690,9 @@ def test_presto_expand_data_with_complex_row_columns(self): self.assertEqual(actual_data, expected_data) self.assertEqual(actual_expanded_cols, expected_expanded_cols) + @mock.patch.dict( + "superset._feature_flags", {"PRESTO_EXPAND_DATA": True}, clear=True + ) def test_presto_expand_data_with_complex_array_columns(self): cols = [ {"name": "int_column", "type": "BIGINT"}, From 46f4da5fbe3e7c0f27c673af3f8d2e32396fd4da Mon Sep 17 00:00:00 2001 From: John Bodley <4567245+john-bodley@users.noreply.github.com> Date: Fri, 16 Aug 2019 11:25:57 -0700 Subject: [PATCH 39/40] [viz] Revert dropna logic for pivot tables (#8040) --- superset/viz.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/superset/viz.py b/superset/viz.py index cecfe2ddebdab..68d343ee8ca0e 100644 --- a/superset/viz.py +++ b/superset/viz.py @@ -629,9 +629,7 @@ def get_data(self, df): if fd.get("groupby"): values = self.metric_labels[0] columns = fd.get("groupby") - pt = df.pivot_table( - index=DTTM_ALIAS, columns=columns, values=values, dropna=False - ) + pt = df.pivot_table(index=DTTM_ALIAS, columns=columns, values=values) pt.index = pt.index.map(str) pt = pt.sort_index() return dict( @@ -697,7 +695,6 @@ def get_data(self, df): values=[utils.get_metric_name(m) for m in self.form_data.get("metrics")], aggfunc=aggfunc, margins=self.form_data.get("pivot_margins"), - dropna=False, ) # Display metrics side by side with each column if self.form_data.get("combine_metric"): @@ -1153,14 +1150,10 @@ def process_data(self, df, aggregate=False): values=self.metric_labels, fill_value=0, aggfunc=sum, - dropna=False, ) else: df = df.pivot_table( - index=DTTM_ALIAS, - columns=fd.get("groupby"), - values=self.metric_labels, - dropna=False, + index=DTTM_ALIAS, columns=fd.get("groupby"), values=self.metric_labels ) rule = fd.get("resample_rule") @@ -1371,7 +1364,7 @@ def get_data(self, df): metric = utils.get_metric_name(fd.get("metric")) metric_2 = utils.get_metric_name(fd.get("metric_2")) - df = df.pivot_table(index=DTTM_ALIAS, values=[metric, metric_2], dropna=False) + df = df.pivot_table(index=DTTM_ALIAS, values=[metric, metric_2]) chart_data = self.to_series(df) return chart_data @@ -1423,7 +1416,6 @@ def get_data(self, df): index=DTTM_ALIAS, columns="series", values=utils.get_metric_name(fd.get("metric")), - dropna=False, ) chart_data = self.to_series(df) for serie in chart_data: @@ -1459,7 +1451,7 @@ class DistributionPieViz(NVD3Viz): def get_data(self, df): metric = self.metric_labels[0] - df = df.pivot_table(index=self.groupby, values=[metric], dropna=False) + df = df.pivot_table(index=self.groupby, values=[metric]) df.sort_values(by=metric, ascending=False, inplace=True) df = df.reset_index() df.columns = ["x", "y"] @@ -1547,9 +1539,7 @@ def get_data(self, df): row = df.groupby(self.groupby).sum()[metrics[0]].copy() row.sort_values(ascending=False, inplace=True) columns = fd.get("columns") or [] - pt = df.pivot_table( - index=self.groupby, columns=columns, values=metrics, dropna=False - ) + pt = df.pivot_table(index=self.groupby, columns=columns, values=metrics) if fd.get("contribution"): pt = pt.T pt = (pt / pt.sum()).T From 51bd34704cd0c6e6f3b49150743c32de16765673 Mon Sep 17 00:00:00 2001 From: Grace Guo Date: Fri, 16 Aug 2019 14:12:35 -0700 Subject: [PATCH 40/40] [sql lab] persist tables list in localStorage (#8054) * [sql lab] persist tables list in localStorage * persist ediotor panel size setting --- superset/assets/src/SqlLab/App.jsx | 1 - superset/assets/src/SqlLab/actions/sqlLab.js | 4 ++-- superset/assets/src/SqlLab/components/SqlEditor.jsx | 6 +++--- superset/assets/src/SqlLab/reducers/sqlLab.js | 3 ++- .../src/SqlLab/utils/reduxStateToLocalStorageHelper.js | 4 ++++ 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/superset/assets/src/SqlLab/App.jsx b/superset/assets/src/SqlLab/App.jsx index af35a1e50b773..54c4711e4b524 100644 --- a/superset/assets/src/SqlLab/App.jsx +++ b/superset/assets/src/SqlLab/App.jsx @@ -58,7 +58,6 @@ const sqlLabPersistStateConfig = { ...state[path], queries: emptyQueryResults(state[path].queries), queryEditors: clearQueryEditors(state[path].queryEditors), - tables: [], }; } }); diff --git a/superset/assets/src/SqlLab/actions/sqlLab.js b/superset/assets/src/SqlLab/actions/sqlLab.js index d7b15c58b7526..ca364284fddc0 100644 --- a/superset/assets/src/SqlLab/actions/sqlLab.js +++ b/superset/assets/src/SqlLab/actions/sqlLab.js @@ -455,8 +455,8 @@ export function setUserOffline(offline) { return { type: SET_USER_OFFLINE, offline }; } -export function persistEditorHeight(queryEditor, currentHeight) { - return { type: QUERY_EDITOR_PERSIST_HEIGHT, queryEditor, currentHeight }; +export function persistEditorHeight(queryEditor, northPercent, southPercent) { + return { type: QUERY_EDITOR_PERSIST_HEIGHT, queryEditor, northPercent, southPercent }; } export function popStoredQuery(urlId) { diff --git a/superset/assets/src/SqlLab/components/SqlEditor.jsx b/superset/assets/src/SqlLab/components/SqlEditor.jsx index 9448a0b716875..8524daaea6483 100644 --- a/superset/assets/src/SqlLab/components/SqlEditor.jsx +++ b/superset/assets/src/SqlLab/components/SqlEditor.jsx @@ -87,8 +87,8 @@ class SqlEditor extends React.PureComponent { this.state = { autorun: props.queryEditor.autorun, ctas: '', - northPercent: INITIAL_NORTH_PERCENT, - southPercent: INITIAL_SOUTH_PERCENT, + northPercent: props.queryEditor.northPercent || INITIAL_NORTH_PERCENT, + southPercent: props.queryEditor.southPercent || INITIAL_SOUTH_PERCENT, sql: props.queryEditor.sql, }; this.sqlEditorRef = React.createRef(); @@ -143,7 +143,7 @@ class SqlEditor extends React.PureComponent { if (this.northPaneRef.current && this.northPaneRef.current.clientHeight) { this.props.actions.persistEditorHeight(this.props.queryEditor, - this.northPaneRef.current.clientHeight); + northPercent, southPercent); } } onSqlChanged(sql) { diff --git a/superset/assets/src/SqlLab/reducers/sqlLab.js b/superset/assets/src/SqlLab/reducers/sqlLab.js index 2244096d471f0..700c982d3640b 100644 --- a/superset/assets/src/SqlLab/reducers/sqlLab.js +++ b/superset/assets/src/SqlLab/reducers/sqlLab.js @@ -311,7 +311,8 @@ export default function sqlLabReducer(state = {}, action) { }, [actions.QUERY_EDITOR_PERSIST_HEIGHT]() { return alterInArr(state, 'queryEditors', action.queryEditor, { - height: action.currentHeight, + northPercent: action.northPercent, + southPercent: action.southPercent, }); }, [actions.SET_DATABASES]() { diff --git a/superset/assets/src/SqlLab/utils/reduxStateToLocalStorageHelper.js b/superset/assets/src/SqlLab/utils/reduxStateToLocalStorageHelper.js index 6a517f611b3b4..aef0867d10c82 100644 --- a/superset/assets/src/SqlLab/utils/reduxStateToLocalStorageHelper.js +++ b/superset/assets/src/SqlLab/utils/reduxStateToLocalStorageHelper.js @@ -21,10 +21,14 @@ import { LOCALSTORAGE_MAX_QUERY_AGE_MS } from '../constants'; const PERSISTENT_QUERY_EDITOR_KEYS = new Set([ 'autorun', 'dbId', + 'height', 'id', 'latestQueryId', + 'northPercent', 'queryLimit', + 'schema', 'selectedText', + 'southPercent', 'sql', 'templateParams', 'title',