mirror of https://github.com/apache/superset.git
fix: dashboard extra filters (#10692)
Co-authored-by: John Bodley <john.bodley@airbnb.com>
This commit is contained in:
parent
45f4c689a2
commit
1ee87cc4d1
|
@ -153,7 +153,7 @@ def load_world_bank_health_n_pop( # pylint: disable=too-many-locals, too-many-s
|
||||||
"column": "region",
|
"column": "region",
|
||||||
"key": "2s98dfu",
|
"key": "2s98dfu",
|
||||||
"metric": "sum__SP_POP_TOTL",
|
"metric": "sum__SP_POP_TOTL",
|
||||||
"multiple": True,
|
"multiple": False,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"asc": False,
|
"asc": False,
|
||||||
|
|
|
@ -336,7 +336,7 @@ def get_dashboard_extra_filters(
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def build_extra_filters(
|
def build_extra_filters( # pylint: disable=too-many-locals,too-many-nested-blocks
|
||||||
layout: Dict[str, Dict[str, Any]],
|
layout: Dict[str, Dict[str, Any]],
|
||||||
filter_scopes: Dict[str, Dict[str, Any]],
|
filter_scopes: Dict[str, Dict[str, Any]],
|
||||||
default_filters: Dict[str, Dict[str, List[Any]]],
|
default_filters: Dict[str, Dict[str, List[Any]]],
|
||||||
|
@ -344,11 +344,22 @@ def build_extra_filters(
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
extra_filters = []
|
extra_filters = []
|
||||||
|
|
||||||
# do not apply filters if chart is not in filter's scope or
|
# do not apply filters if chart is not in filter's scope or chart is immune to the
|
||||||
# chart is immune to the filter
|
# filter.
|
||||||
for filter_id, columns in default_filters.items():
|
for filter_id, columns in default_filters.items():
|
||||||
|
filter_slice = db.session.query(Slice).filter_by(id=filter_id).one_or_none()
|
||||||
|
|
||||||
|
filter_configs = (
|
||||||
|
json.loads(filter_slice.params or "{}").get("filter_configs") or []
|
||||||
|
if filter_slice
|
||||||
|
else []
|
||||||
|
)
|
||||||
|
|
||||||
scopes_by_filter_field = filter_scopes.get(filter_id, {})
|
scopes_by_filter_field = filter_scopes.get(filter_id, {})
|
||||||
for col, val in columns.items():
|
for col, val in columns.items():
|
||||||
|
if not val:
|
||||||
|
continue
|
||||||
|
|
||||||
current_field_scopes = scopes_by_filter_field.get(col, {})
|
current_field_scopes = scopes_by_filter_field.get(col, {})
|
||||||
scoped_container_ids = current_field_scopes.get("scope", ["ROOT_ID"])
|
scoped_container_ids = current_field_scopes.get("scope", ["ROOT_ID"])
|
||||||
immune_slice_ids = current_field_scopes.get("immune", [])
|
immune_slice_ids = current_field_scopes.get("immune", [])
|
||||||
|
@ -357,7 +368,25 @@ def build_extra_filters(
|
||||||
if slice_id not in immune_slice_ids and is_slice_in_container(
|
if slice_id not in immune_slice_ids and is_slice_in_container(
|
||||||
layout, container_id, slice_id
|
layout, container_id, slice_id
|
||||||
):
|
):
|
||||||
extra_filters.append({"col": col, "op": "in", "val": val})
|
# Ensure that the filter value encoding adheres to the filter select
|
||||||
|
# type.
|
||||||
|
for filter_config in filter_configs:
|
||||||
|
if filter_config["column"] == col:
|
||||||
|
is_multiple = filter_config["multiple"]
|
||||||
|
|
||||||
|
if not is_multiple and isinstance(val, list):
|
||||||
|
val = val[0]
|
||||||
|
elif is_multiple and not isinstance(val, list):
|
||||||
|
val = [val]
|
||||||
|
break
|
||||||
|
|
||||||
|
extra_filters.append(
|
||||||
|
{
|
||||||
|
"col": col,
|
||||||
|
"op": "in" if isinstance(val, list) else "==",
|
||||||
|
"val": val,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return extra_filters
|
return extra_filters
|
||||||
|
|
||||||
|
|
|
@ -168,7 +168,7 @@ class TestCacheWarmUp(SupersetTestCase):
|
||||||
"slice_id": chart_id,
|
"slice_id": chart_id,
|
||||||
"extra_filters": [
|
"extra_filters": [
|
||||||
{"col": "name", "op": "in", "val": ["Alice", "Bob"]},
|
{"col": "name", "op": "in", "val": ["Alice", "Bob"]},
|
||||||
{"col": "__time_range", "op": "in", "val": "100 years ago : today"},
|
{"col": "__time_range", "op": "==", "val": "100 years ago : today"},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
|
|
|
@ -35,6 +35,8 @@ import tests.test_app
|
||||||
from superset import app, db, security_manager
|
from superset import app, db, security_manager
|
||||||
from superset.exceptions import CertificateException, SupersetException
|
from superset.exceptions import CertificateException, SupersetException
|
||||||
from superset.models.core import Database, Log
|
from superset.models.core import Database, Log
|
||||||
|
from superset.models.dashboard import Dashboard
|
||||||
|
from superset.models.slice import Slice
|
||||||
from superset.utils.cache_manager import CacheManager
|
from superset.utils.cache_manager import CacheManager
|
||||||
from superset.utils.core import (
|
from superset.utils.core import (
|
||||||
base_json_conv,
|
base_json_conv,
|
||||||
|
@ -965,268 +967,47 @@ class TestUtils(SupersetTestCase):
|
||||||
self.assertListEqual(get_iterable("foo"), ["foo"])
|
self.assertListEqual(get_iterable("foo"), ["foo"])
|
||||||
|
|
||||||
def test_build_extra_filters(self):
|
def test_build_extra_filters(self):
|
||||||
layout = {
|
world_health = db.session.query(Dashboard).filter_by(slug="world_health").one()
|
||||||
"CHART-2ee52f30": {
|
layout = json.loads(world_health.position_json)
|
||||||
"children": [],
|
filter_ = db.session.query(Slice).filter_by(slice_name="Region Filter").one()
|
||||||
"id": "CHART-2ee52f30",
|
world = db.session.query(Slice).filter_by(slice_name="World's Population").one()
|
||||||
"meta": {
|
box_plot = db.session.query(Slice).filter_by(slice_name="Box plot").one()
|
||||||
"chartId": 1020,
|
treemap = db.session.query(Slice).filter_by(slice_name="Treemap").one()
|
||||||
"height": 38,
|
|
||||||
"sliceName": "Chart 927",
|
|
||||||
"width": 6,
|
|
||||||
},
|
|
||||||
"parents": [
|
|
||||||
"ROOT_ID",
|
|
||||||
"TABS-Qq4sdkANSY",
|
|
||||||
"TAB-VrhTX2WUlO",
|
|
||||||
"TABS-N1zN4CIZP0",
|
|
||||||
"TAB-asWdJzKmTN",
|
|
||||||
"ROW-i_sG4ccXE",
|
|
||||||
],
|
|
||||||
"type": "CHART",
|
|
||||||
},
|
|
||||||
"CHART-36bfc934": {
|
|
||||||
"children": [],
|
|
||||||
"id": "CHART-36bfc934",
|
|
||||||
"meta": {
|
|
||||||
"chartId": 1018,
|
|
||||||
"height": 26,
|
|
||||||
"sliceName": "Region Filter",
|
|
||||||
"width": 2,
|
|
||||||
},
|
|
||||||
"parents": [
|
|
||||||
"ROOT_ID",
|
|
||||||
"TABS-Qq4sdkANSY",
|
|
||||||
"TAB-W62P60D88",
|
|
||||||
"ROW-1e064e3c",
|
|
||||||
"COLUMN-fe3914b8",
|
|
||||||
],
|
|
||||||
"type": "CHART",
|
|
||||||
},
|
|
||||||
"CHART-E_y2cuNHTv": {
|
|
||||||
"children": [],
|
|
||||||
"id": "CHART-E_y2cuNHTv",
|
|
||||||
"meta": {"chartId": 998, "height": 55, "sliceName": "MAP", "width": 6},
|
|
||||||
"parents": [
|
|
||||||
"ROOT_ID",
|
|
||||||
"TABS-Qq4sdkANSY",
|
|
||||||
"TAB-W62P60D88",
|
|
||||||
"ROW-1e064e3c",
|
|
||||||
],
|
|
||||||
"type": "CHART",
|
|
||||||
},
|
|
||||||
"CHART-JNxDOsAfEb": {
|
|
||||||
"children": [],
|
|
||||||
"id": "CHART-JNxDOsAfEb",
|
|
||||||
"meta": {
|
|
||||||
"chartId": 1015,
|
|
||||||
"height": 27,
|
|
||||||
"sliceName": "Population",
|
|
||||||
"width": 4,
|
|
||||||
},
|
|
||||||
"parents": [
|
|
||||||
"ROOT_ID",
|
|
||||||
"TABS-Qq4sdkANSY",
|
|
||||||
"TAB-W62P60D88",
|
|
||||||
"ROW-1e064e3c",
|
|
||||||
"COLUMN-fe3914b8",
|
|
||||||
],
|
|
||||||
"type": "CHART",
|
|
||||||
},
|
|
||||||
"CHART-KoOwqalV80": {
|
|
||||||
"children": [],
|
|
||||||
"id": "CHART-KoOwqalV80",
|
|
||||||
"meta": {
|
|
||||||
"chartId": 927,
|
|
||||||
"height": 20,
|
|
||||||
"sliceName": "Chart 927",
|
|
||||||
"width": 4,
|
|
||||||
},
|
|
||||||
"parents": [
|
|
||||||
"ROOT_ID",
|
|
||||||
"TABS-Qq4sdkANSY",
|
|
||||||
"TAB-VrhTX2WUlO",
|
|
||||||
"TABS-N1zN4CIZP0",
|
|
||||||
"TAB-cHNWcBZC9",
|
|
||||||
"ROW-9b9vrWKPY",
|
|
||||||
],
|
|
||||||
"type": "CHART",
|
|
||||||
},
|
|
||||||
"CHART-YCQAPVK7mQ": {
|
|
||||||
"children": [],
|
|
||||||
"id": "CHART-YCQAPVK7mQ",
|
|
||||||
"meta": {
|
|
||||||
"chartId": 1023,
|
|
||||||
"height": 38,
|
|
||||||
"sliceName": "World's Population",
|
|
||||||
"width": 4,
|
|
||||||
},
|
|
||||||
"parents": [
|
|
||||||
"ROOT_ID",
|
|
||||||
"TABS-Qq4sdkANSY",
|
|
||||||
"TAB-VrhTX2WUlO",
|
|
||||||
"ROW-UfxFT36oV5",
|
|
||||||
],
|
|
||||||
"type": "CHART",
|
|
||||||
},
|
|
||||||
"COLUMN-fe3914b8": {
|
|
||||||
"children": ["CHART-36bfc934", "CHART-JNxDOsAfEb"],
|
|
||||||
"id": "COLUMN-fe3914b8",
|
|
||||||
"meta": {"background": "BACKGROUND_TRANSPARENT", "width": 6},
|
|
||||||
"parents": [
|
|
||||||
"ROOT_ID",
|
|
||||||
"TABS-Qq4sdkANSY",
|
|
||||||
"TAB-W62P60D88",
|
|
||||||
"ROW-1e064e3c",
|
|
||||||
],
|
|
||||||
"type": "COLUMN",
|
|
||||||
},
|
|
||||||
"DASHBOARD_VERSION_KEY": "v2",
|
|
||||||
"GRID_ID": {
|
|
||||||
"children": [],
|
|
||||||
"id": "GRID_ID",
|
|
||||||
"parents": ["ROOT_ID"],
|
|
||||||
"type": "GRID",
|
|
||||||
},
|
|
||||||
"HEADER_ID": {
|
|
||||||
"id": "HEADER_ID",
|
|
||||||
"meta": {"text": "Test warmup 1023"},
|
|
||||||
"type": "HEADER",
|
|
||||||
},
|
|
||||||
"ROOT_ID": {
|
|
||||||
"children": ["TABS-Qq4sdkANSY"],
|
|
||||||
"id": "ROOT_ID",
|
|
||||||
"type": "ROOT",
|
|
||||||
},
|
|
||||||
"ROW-1e064e3c": {
|
|
||||||
"children": ["COLUMN-fe3914b8", "CHART-E_y2cuNHTv"],
|
|
||||||
"id": "ROW-1e064e3c",
|
|
||||||
"meta": {"background": "BACKGROUND_TRANSPARENT"},
|
|
||||||
"parents": ["ROOT_ID", "TABS-Qq4sdkANSY", "TAB-W62P60D88"],
|
|
||||||
"type": "ROW",
|
|
||||||
},
|
|
||||||
"ROW-9b9vrWKPY": {
|
|
||||||
"children": ["CHART-KoOwqalV80"],
|
|
||||||
"id": "ROW-9b9vrWKPY",
|
|
||||||
"meta": {"background": "BACKGROUND_TRANSPARENT"},
|
|
||||||
"parents": [
|
|
||||||
"ROOT_ID",
|
|
||||||
"TABS-Qq4sdkANSY",
|
|
||||||
"TAB-VrhTX2WUlO",
|
|
||||||
"TABS-N1zN4CIZP0",
|
|
||||||
"TAB-cHNWcBZC9",
|
|
||||||
],
|
|
||||||
"type": "ROW",
|
|
||||||
},
|
|
||||||
"ROW-UfxFT36oV5": {
|
|
||||||
"children": ["CHART-YCQAPVK7mQ"],
|
|
||||||
"id": "ROW-UfxFT36oV5",
|
|
||||||
"meta": {"background": "BACKGROUND_TRANSPARENT"},
|
|
||||||
"parents": ["ROOT_ID", "TABS-Qq4sdkANSY", "TAB-VrhTX2WUlO"],
|
|
||||||
"type": "ROW",
|
|
||||||
},
|
|
||||||
"ROW-i_sG4ccXE": {
|
|
||||||
"children": ["CHART-2ee52f30"],
|
|
||||||
"id": "ROW-i_sG4ccXE",
|
|
||||||
"meta": {"background": "BACKGROUND_TRANSPARENT"},
|
|
||||||
"parents": [
|
|
||||||
"ROOT_ID",
|
|
||||||
"TABS-Qq4sdkANSY",
|
|
||||||
"TAB-VrhTX2WUlO",
|
|
||||||
"TABS-N1zN4CIZP0",
|
|
||||||
"TAB-asWdJzKmTN",
|
|
||||||
],
|
|
||||||
"type": "ROW",
|
|
||||||
},
|
|
||||||
"TAB-VrhTX2WUlO": {
|
|
||||||
"children": ["ROW-UfxFT36oV5", "TABS-N1zN4CIZP0"],
|
|
||||||
"id": "TAB-VrhTX2WUlO",
|
|
||||||
"meta": {"text": "New Tab"},
|
|
||||||
"parents": ["ROOT_ID", "TABS-Qq4sdkANSY"],
|
|
||||||
"type": "TAB",
|
|
||||||
},
|
|
||||||
"TAB-W62P60D88": {
|
|
||||||
"children": ["ROW-1e064e3c"],
|
|
||||||
"id": "TAB-W62P60D88",
|
|
||||||
"meta": {"text": "Tab 2"},
|
|
||||||
"parents": ["ROOT_ID", "TABS-Qq4sdkANSY"],
|
|
||||||
"type": "TAB",
|
|
||||||
},
|
|
||||||
"TAB-asWdJzKmTN": {
|
|
||||||
"children": ["ROW-i_sG4ccXE"],
|
|
||||||
"id": "TAB-asWdJzKmTN",
|
|
||||||
"meta": {"text": "nested tab 1"},
|
|
||||||
"parents": [
|
|
||||||
"ROOT_ID",
|
|
||||||
"TABS-Qq4sdkANSY",
|
|
||||||
"TAB-VrhTX2WUlO",
|
|
||||||
"TABS-N1zN4CIZP0",
|
|
||||||
],
|
|
||||||
"type": "TAB",
|
|
||||||
},
|
|
||||||
"TAB-cHNWcBZC9": {
|
|
||||||
"children": ["ROW-9b9vrWKPY"],
|
|
||||||
"id": "TAB-cHNWcBZC9",
|
|
||||||
"meta": {"text": "test2d tab 2"},
|
|
||||||
"parents": [
|
|
||||||
"ROOT_ID",
|
|
||||||
"TABS-Qq4sdkANSY",
|
|
||||||
"TAB-VrhTX2WUlO",
|
|
||||||
"TABS-N1zN4CIZP0",
|
|
||||||
],
|
|
||||||
"type": "TAB",
|
|
||||||
},
|
|
||||||
"TABS-N1zN4CIZP0": {
|
|
||||||
"children": ["TAB-asWdJzKmTN", "TAB-cHNWcBZC9"],
|
|
||||||
"id": "TABS-N1zN4CIZP0",
|
|
||||||
"meta": {},
|
|
||||||
"parents": ["ROOT_ID", "TABS-Qq4sdkANSY", "TAB-VrhTX2WUlO"],
|
|
||||||
"type": "TABS",
|
|
||||||
},
|
|
||||||
"TABS-Qq4sdkANSY": {
|
|
||||||
"children": ["TAB-VrhTX2WUlO", "TAB-W62P60D88"],
|
|
||||||
"id": "TABS-Qq4sdkANSY",
|
|
||||||
"meta": {},
|
|
||||||
"parents": ["ROOT_ID"],
|
|
||||||
"type": "TABS",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
filter_scopes = {
|
filter_scopes = {
|
||||||
"1018": {
|
str(filter_.id): {
|
||||||
"region": {"scope": ["TAB-W62P60D88"], "immune": [998]},
|
"region": {"scope": ["ROOT_ID"], "immune": [treemap.id]},
|
||||||
"country_name": {"scope": ["ROOT_ID"], "immune": [927, 998]},
|
"country_name": {
|
||||||
|
"scope": ["ROOT_ID"],
|
||||||
|
"immune": [treemap.id, box_plot.id],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default_filters = {
|
default_filters = {
|
||||||
"1018": {"region": ["North America"], "country_name": ["United States"]}
|
str(filter_.id): {
|
||||||
|
"region": ["North America"],
|
||||||
|
"country_name": ["United States"],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# immune to all filters
|
# immune to all filters
|
||||||
slice_id = 998
|
assert (
|
||||||
extra_filters = build_extra_filters(
|
build_extra_filters(layout, filter_scopes, default_filters, treemap.id)
|
||||||
layout, filter_scopes, default_filters, slice_id
|
== []
|
||||||
)
|
)
|
||||||
expected = []
|
|
||||||
self.assertEqual(extra_filters, expected)
|
|
||||||
|
|
||||||
# in scope
|
# in scope
|
||||||
slice_id = 1015
|
assert build_extra_filters(
|
||||||
extra_filters = build_extra_filters(
|
layout, filter_scopes, default_filters, world.id
|
||||||
layout, filter_scopes, default_filters, slice_id
|
) == [
|
||||||
)
|
{"col": "region", "op": "==", "val": "North America"},
|
||||||
expected = [
|
|
||||||
{"col": "region", "op": "in", "val": ["North America"]},
|
|
||||||
{"col": "country_name", "op": "in", "val": ["United States"]},
|
{"col": "country_name", "op": "in", "val": ["United States"]},
|
||||||
]
|
]
|
||||||
self.assertEqual(extra_filters, expected)
|
|
||||||
|
|
||||||
# not in scope
|
assert build_extra_filters(
|
||||||
slice_id = 927
|
layout, filter_scopes, default_filters, box_plot.id
|
||||||
extra_filters = build_extra_filters(
|
) == [{"col": "region", "op": "==", "val": "North America"}]
|
||||||
layout, filter_scopes, default_filters, slice_id
|
|
||||||
)
|
|
||||||
expected = []
|
|
||||||
self.assertEqual(extra_filters, expected)
|
|
||||||
|
|
||||||
def test_ssl_certificate_parse(self):
|
def test_ssl_certificate_parse(self):
|
||||||
parsed_certificate = parse_ssl_cert(ssl_certificate)
|
parsed_certificate = parse_ssl_cert(ssl_certificate)
|
||||||
|
|
Loading…
Reference in New Issue