You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by er...@apache.org on 2020/11/24 20:14:22 UTC

[incubator-superset] branch master updated: feat: add certification info to table selector (#11785)

This is an automated email from the ASF dual-hosted git repository.

erikrit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new c0224aa  feat: add certification info to table selector (#11785)
c0224aa is described below

commit c0224aa92819e45f86dabf693da9fe7aaba9391a
Author: Erik Ritter <er...@airbnb.com>
AuthorDate: Tue Nov 24 12:13:52 2020 -0800

    feat: add certification info to table selector (#11785)
---
 .../javascripts/components/TableSelector_spec.jsx  |  3 ++-
 .../src/components/CertifiedIconWithTooltip.tsx    |  9 +++++++-
 superset-frontend/src/components/TableSelector.tsx | 27 ++++++++++++++++------
 superset/connectors/sqla/models.py                 |  7 ++++++
 superset/views/core.py                             |  5 ++++
 tests/core_tests.py                                |  7 +++---
 6 files changed, 46 insertions(+), 12 deletions(-)

diff --git a/superset-frontend/spec/javascripts/components/TableSelector_spec.jsx b/superset-frontend/spec/javascripts/components/TableSelector_spec.jsx
index 96b1d2e..320e4f5 100644
--- a/superset-frontend/spec/javascripts/components/TableSelector_spec.jsx
+++ b/superset-frontend/spec/javascripts/components/TableSelector_spec.jsx
@@ -60,11 +60,12 @@ const schemaOptions = {
 };
 const selectedSchema = { label: 'main', title: 'main', value: 'main' };
 const selectedTable = {
+  extra: null,
   label: 'birth_names',
   schema: 'main',
   title: 'birth_names',
-  value: 'birth_names',
   type: undefined,
+  value: 'birth_names',
 };
 
 async function mountAndWait(props = mockedProps) {
diff --git a/superset-frontend/src/components/CertifiedIconWithTooltip.tsx b/superset-frontend/src/components/CertifiedIconWithTooltip.tsx
index e96b35f..e4132dc 100644
--- a/superset-frontend/src/components/CertifiedIconWithTooltip.tsx
+++ b/superset-frontend/src/components/CertifiedIconWithTooltip.tsx
@@ -24,11 +24,13 @@ import TooltipWrapper from 'src/components/TooltipWrapper';
 interface CertifiedIconWithTooltipProps {
   certifiedBy?: string;
   details?: string;
+  size?: number;
 }
 
 function CertifiedIconWithTooltip({
   certifiedBy,
   details,
+  size = 24,
 }: CertifiedIconWithTooltipProps) {
   return (
     <TooltipWrapper
@@ -40,7 +42,12 @@ function CertifiedIconWithTooltip({
         </>
       }
     >
-      <Icon color={supersetTheme.colors.primary.base} name="certified" />
+      <Icon
+        color={supersetTheme.colors.primary.base}
+        height={size}
+        width={size}
+        name="certified"
+      />
     </TooltipWrapper>
   );
 }
diff --git a/superset-frontend/src/components/TableSelector.tsx b/superset-frontend/src/components/TableSelector.tsx
index 4c59359..c5c3a52 100644
--- a/superset-frontend/src/components/TableSelector.tsx
+++ b/superset-frontend/src/components/TableSelector.tsx
@@ -29,6 +29,7 @@ import FormLabel from 'src/components/FormLabel';
 
 import DatabaseSelector from './DatabaseSelector';
 import RefreshLabel from './RefreshLabel';
+import CertifiedIconWithTooltip from './CertifiedIconWithTooltip';
 
 const FieldTitle = styled.p`
   color: ${({ theme }) => theme.colors.secondary.light2};
@@ -65,7 +66,14 @@ const TableSelectorWrapper = styled.div`
 `;
 
 const TableLabel = styled.span`
+  align-items: center;
+  display: flex;
   white-space: nowrap;
+
+  > svg,
+  > small {
+    margin-right: ${({ theme }) => theme.gridUnit}px;
+  }
 `;
 
 interface TableSelectorProps {
@@ -146,6 +154,7 @@ const TableSelector: FunctionComponent<TableSelectorProps> = ({
             label: o.label,
             title: o.title,
             type: o.type,
+            extra: o?.extra,
           }));
           setTableLoading(false);
           setTableOptions(options);
@@ -244,13 +253,16 @@ const TableSelector: FunctionComponent<TableSelectorProps> = ({
   function renderTableOption(option: any) {
     return (
       <TableLabel title={option.label}>
-        <span className="m-r-5">
-          <small className="text-muted">
-            <i
-              className={`fa fa-${option.type === 'view' ? 'eye' : 'table'}`}
-            />
-          </small>
-        </span>
+        {option.extra?.certification && (
+          <CertifiedIconWithTooltip
+            certifiedBy={option.extra.certification.certified_by}
+            details={option.extra.certification.details}
+            size={20}
+          />
+        )}
+        <small className="text-muted">
+          <i className={`fa fa-${option.type === 'view' ? 'eye' : 'table'}`} />
+        </small>
         {option.label}
       </TableLabel>
     );
@@ -308,6 +320,7 @@ const TableSelector: FunctionComponent<TableSelectorProps> = ({
           // @ts-ignore
           value={currentTableName}
           optionRenderer={renderTableOption}
+          valueRenderer={renderTableOption}
         />
       );
     } else if (formMode) {
diff --git a/superset/connectors/sqla/models.py b/superset/connectors/sqla/models.py
index c75c081..e19c1b9 100644
--- a/superset/connectors/sqla/models.py
+++ b/superset/connectors/sqla/models.py
@@ -700,6 +700,13 @@ class SqlaTable(  # pylint: disable=too-many-public-methods,too-many-instance-at
             data_["is_sqllab_view"] = self.is_sqllab_view
         return data_
 
+    @property
+    def extra_dict(self) -> Dict[str, Any]:
+        try:
+            return json.loads(self.extra)
+        except (TypeError, json.JSONDecodeError):
+            return {}
+
     def values_for_column(self, column_name: str, limit: int = 10000) -> List[Any]:
         """Runs query against sqla to retrieve some
         sample values for the given column.
diff --git a/superset/views/core.py b/superset/views/core.py
index e12e50c..e5730e5 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -981,6 +981,8 @@ class Superset(BaseSupersetView):  # pylint: disable=too-many-public-methods
             max_tables = max_items * len(tables) // total_items
             max_views = max_items * len(views) // total_items
 
+        dataset_tables = {table.name: table for table in database.tables}
+
         table_options = [
             {
                 "value": tn.table,
@@ -988,6 +990,9 @@ class Superset(BaseSupersetView):  # pylint: disable=too-many-public-methods
                 "label": get_datasource_label(tn),
                 "title": get_datasource_label(tn),
                 "type": "table",
+                "extra": dataset_tables[f"{tn.schema}.{tn.table}"].extra_dict
+                if (f"{tn.schema}.{tn.table}" in dataset_tables)
+                else None,
             }
             for tn in tables[:max_tables]
         ]
diff --git a/tests/core_tests.py b/tests/core_tests.py
index 87b698f..94f3c29 100644
--- a/tests/core_tests.py
+++ b/tests/core_tests.py
@@ -155,7 +155,7 @@ class TestCore(SupersetTestCase):
         response = json.loads(rv.data.decode("utf-8"))
         self.assertEqual(rv.status_code, 200)
 
-        expeted_response = {
+        expected_response = {
             "options": [
                 {
                     "label": "ab_role",
@@ -163,11 +163,12 @@ class TestCore(SupersetTestCase):
                     "title": "ab_role",
                     "type": "table",
                     "value": "ab_role",
+                    "extra": None,
                 }
             ],
             "tableLength": 1,
         }
-        self.assertEqual(response, expeted_response)
+        self.assertEqual(response, expected_response)
 
     def test_get_superset_tables_not_found(self):
         self.login(username="admin")
@@ -862,7 +863,7 @@ class TestCore(SupersetTestCase):
 
     def test_get_select_star_not_allowed(self):
         """
-            Database API: Test get select star not allowed
+        Database API: Test get select star not allowed
         """
         self.login(username="gamma")
         example_db = utils.get_example_database()