From d6a327b052a50a8099313376763e4df4aa48432b Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Thu, 1 Jul 2021 19:27:26 -0700 Subject: [PATCH] feat: extra table metadata for Google Sheets (#14775) * feat: GSheets extra table metadata * Bump version --- setup.py | 2 +- .../src/SqlLab/components/TableElement.tsx | 42 ++++++++++++++----- .../src/SqlLab/reducers/getInitialState.js | 4 ++ superset/db_engine_specs/gsheets.py | 25 ++++++++++- 4 files changed, 60 insertions(+), 13 deletions(-) diff --git a/setup.py b/setup.py index ef66af9e0f94d..bc4dbe7d84ca3 100644 --- a/setup.py +++ b/setup.py @@ -135,7 +135,7 @@ def get_git_sha(): "exasol": ["sqlalchemy-exasol>=2.1.0, <2.2"], "excel": ["xlrd>=1.2.0, <1.3"], "firebird": ["sqlalchemy-firebird>=0.7.0, <0.8"], - "gsheets": ["shillelagh[gsheetsapi]>=0.5, <0.6"], + "gsheets": ["shillelagh[gsheetsapi]>=0.7.1, <0.8"], "hana": ["hdbcli==2.4.162", "sqlalchemy_hana==0.4.0"], "hive": ["pyhive[hive]>=0.6.1", "tableschema", "thrift>=0.11.0, <1.0.0"], "impala": ["impyla>0.16.2, <0.17"], diff --git a/superset-frontend/src/SqlLab/components/TableElement.tsx b/superset-frontend/src/SqlLab/components/TableElement.tsx index 32a000dad4c72..775c9252fc4e9 100644 --- a/superset-frontend/src/SqlLab/components/TableElement.tsx +++ b/superset-frontend/src/SqlLab/components/TableElement.tsx @@ -44,6 +44,7 @@ interface Table { partitionQuery: string; latest: object[]; }; + metadata?: Record; indexes?: object[]; selectStar?: string; view?: string; @@ -91,7 +92,8 @@ const TableElement = ({ table, actions, ...props }: TableElementProps) => { }; const renderWell = () => { - let header; + let partitions; + let metadata; if (table.partitions) { let partitionQuery; let partitionClipBoard; @@ -111,18 +113,36 @@ const TableElement = ({ table, actions, ...props }: TableElementProps) => { .map(([key, value]) => `${key}=${value}`) .join('/'); - header = ( - -
- - {t('latest partition:')} {latest} - {' '} - {partitionClipBoard} -
-
+ partitions = ( +
+ + {t('latest partition:')} {latest} + {' '} + {partitionClipBoard} +
); } - return header; + + if (table.metadata) { + metadata = Object.entries(table.metadata).map(([key, value]) => ( +
+ + {key}: {value} + +
+ )); + } + + if (!partitions && !metadata) { + return null; + } + + return ( + + {partitions} + {metadata} + + ); }; const renderControls = () => { diff --git a/superset-frontend/src/SqlLab/reducers/getInitialState.js b/superset-frontend/src/SqlLab/reducers/getInitialState.js index 8052476f9bd61..92ffa0d6decf6 100644 --- a/superset-frontend/src/SqlLab/reducers/getInitialState.js +++ b/superset-frontend/src/SqlLab/reducers/getInitialState.js @@ -117,6 +117,8 @@ export default function getInitialState({ foreignKeys, indexes, dataPreviewQueryId, + partitions, + metadata, } = tableSchema.description; const table = { dbId: tableSchema.database_id, @@ -133,6 +135,8 @@ export default function getInitialState({ primaryKey, foreignKeys, indexes, + partitions, + metadata, }; tables.push(table); }); diff --git a/superset/db_engine_specs/gsheets.py b/superset/db_engine_specs/gsheets.py index 1245c9ba0e0cb..0f5f6ef3b1d96 100644 --- a/superset/db_engine_specs/gsheets.py +++ b/superset/db_engine_specs/gsheets.py @@ -14,8 +14,10 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +import json import re -from typing import Any, Dict, Optional, Pattern, Tuple +from contextlib import closing +from typing import Any, Dict, Optional, Pattern, Tuple, TYPE_CHECKING from flask_babel import gettext as __ from sqlalchemy.engine.url import URL @@ -24,6 +26,10 @@ from superset.db_engine_specs.sqlite import SqliteEngineSpec from superset.errors import SupersetErrorType +if TYPE_CHECKING: + from superset.models.core import Database + + SYNTAX_ERROR_REGEX = re.compile('SQLError: near "(?P.*?)": syntax error') @@ -54,3 +60,20 @@ def modify_url_for_impersonation( user = security_manager.find_user(username=username) if user and user.email: url.query["subject"] = user.email + + @classmethod + def extra_table_metadata( + cls, database: "Database", table_name: str, schema_name: str, + ) -> Dict[str, Any]: + engine = cls.get_engine(database, schema=schema_name) + with closing(engine.raw_connection()) as conn: + cursor = conn.cursor() + cursor.execute(f'SELECT GET_METADATA("{table_name}")') + results = cursor.fetchone()[0] + + try: + metadata = json.loads(results) + except Exception: # pylint: disable=broad-except + metadata = {} + + return {"metadata": metadata["extra"]}