This is an automated email from the ASF dual-hosted git repository.
johnbodley 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 7f89f12 [debug] Debugging caching issue (#9665)
7f89f12 is described below
commit 7f89f12c4a80f95282c0c5b4807b20dbd688de43
Author: John Bodley <[email protected]>
AuthorDate: Wed Apr 29 12:16:47 2020 -0700
[debug] Debugging caching issue (#9665)
Co-authored-by: John Bodley <[email protected]>
---
superset/jinja_context.py | 23 ++++-----
superset/views/core.py | 4 ++
superset/views/utils.py | 21 +++++---
tests/jinja_context_tests.py | 117 +++++++++++++++++++++++--------------------
tests/macro_tests.py | 77 ----------------------------
tests/utils_tests.py | 85 +++++++++++++++++++++++++++++--
6 files changed, 173 insertions(+), 154 deletions(-)
diff --git a/superset/jinja_context.py b/superset/jinja_context.py
index 5438f0f..28eb6e2 100644
--- a/superset/jinja_context.py
+++ b/superset/jinja_context.py
@@ -16,7 +16,6 @@
# under the License.
"""Defines the templating context for SQL Lab"""
import inspect
-import json
import re
from typing import Any, List, Optional, Tuple
@@ -49,7 +48,9 @@ def filter_values(column: str, default: Optional[str] = None)
-> List[str]:
:return: returns a list of filter values
"""
- form_data = json.loads(request.form.get("form_data", "{}"))
+ from superset.views.utils import get_form_data
+
+ form_data, _ = get_form_data()
convert_legacy_filters_into_adhoc(form_data)
merge_extra_filters(form_data)
@@ -168,18 +169,16 @@ class ExtraCache:
:returns: The URL parameters
"""
+ from superset.views.utils import get_form_data
+
if request.args.get(param):
return request.args.get(param, default)
- # Supporting POST as well as get
- form_data = request.form.get("form_data")
- if isinstance(form_data, str):
- form_data = json.loads(form_data)
- url_params = form_data.get("url_params") or {}
- result = url_params.get(param, default)
- if add_to_cache_keys:
- self.cache_key_wrapper(result)
- return result
- return default
+ form_data, _ = get_form_data()
+ url_params = form_data.get("url_params") or {}
+ result = url_params.get(param, default)
+ if add_to_cache_keys:
+ self.cache_key_wrapper(result)
+ return result
class BaseTemplateProcessor: # pylint: disable=too-few-public-methods
diff --git a/superset/views/core.py b/superset/views/core.py
index e6c9ac9..9688b89 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -1707,13 +1707,17 @@ class Superset(BaseSupersetView):
form_data["extra_filters"] = get_dashboard_extra_filters(
slc.id, dashboard_id
)
+
obj = get_viz(
datasource_type=slc.datasource.type,
datasource_id=slc.datasource.id,
form_data=form_data,
force=True,
)
+
+ g.form_data = form_data
payload = obj.get_payload()
+ delattr(g, "form_data")
error = payload["error"]
status = payload["status"]
except Exception as ex:
diff --git a/superset/views/utils.py b/superset/views/utils.py
index edb2987..5ed7e48 100644
--- a/superset/views/utils.py
+++ b/superset/views/utils.py
@@ -20,7 +20,7 @@ from typing import Any, Dict, List, Optional, Tuple
from urllib import parse
import simplejson as json
-from flask import request
+from flask import g, request
import superset.models.core as models
from superset import app, db, is_feature_enabled
@@ -99,23 +99,28 @@ def get_viz(
return viz_obj
-def get_form_data(slice_id=None, use_slice_data=False):
+def get_form_data(
+ slice_id: Optional[int] = None, use_slice_data: bool = False
+) -> Tuple[Dict[str, Any], Optional[Slice]]:
form_data = {}
- post_data = request.form.get("form_data")
+ request_form_data = request.form.get("form_data")
request_args_data = request.args.get("form_data")
- # Supporting POST
- if post_data:
- form_data.update(json.loads(post_data))
- # request params can overwrite post body
+ if request_form_data:
+ form_data.update(json.loads(request_form_data))
+ # request params can overwrite the body
if request_args_data:
form_data.update(json.loads(request_args_data))
+ # Fallback to using the Flask globals (used for cache warmup) if defined.
+ if not form_data and hasattr(g, "form_data"):
+ form_data = getattr(g, "form_data")
+
url_id = request.args.get("r")
if url_id:
saved_url = db.session.query(models.Url).filter_by(id=url_id).first()
if saved_url:
url_str = parse.unquote_plus(
- saved_url.url.split("?")[1][10:], encoding="utf-8", errors=None
+ saved_url.url.split("?")[1][10:], encoding="utf-8"
)
url_form_data = json.loads(url_str)
# allow form_date in request override saved url
diff --git a/tests/jinja_context_tests.py b/tests/jinja_context_tests.py
index 91cb5e4..431d3b6 100644
--- a/tests/jinja_context_tests.py
+++ b/tests/jinja_context_tests.py
@@ -15,76 +15,85 @@
# specific language governing permissions and limitations
# under the License.
import json
-from unittest import mock
-from superset.jinja_context import filter_values
+import tests.test_app
+from superset import app
+from superset.jinja_context import ExtraCache, filter_values
from tests.base_tests import SupersetTestCase
class Jinja2ContextTests(SupersetTestCase):
def test_filter_values_default(self) -> None:
- request = mock.MagicMock()
- request.form = {}
-
- with mock.patch("superset.jinja_context.request", request):
+ with app.test_request_context():
self.assertEquals(filter_values("name", "foo"), ["foo"])
def test_filter_values_no_default(self) -> None:
- request = mock.MagicMock()
- request.form = {}
-
- with mock.patch("superset.jinja_context.request", request):
+ with app.test_request_context():
self.assertEquals(filter_values("name"), [])
def test_filter_values_adhoc_filters(self) -> None:
- request = mock.MagicMock()
-
- request.form = {
- "form_data": json.dumps(
- {
- "adhoc_filters": [
- {
- "clause": "WHERE",
- "comparator": "foo",
- "expressionType": "SIMPLE",
- "operator": "in",
- "subject": "name",
- }
- ],
- }
- )
- }
-
- with mock.patch("superset.jinja_context.request", request):
+ with app.test_request_context(
+ data={
+ "form_data": json.dumps(
+ {
+ "adhoc_filters": [
+ {
+ "clause": "WHERE",
+ "comparator": "foo",
+ "expressionType": "SIMPLE",
+ "operator": "in",
+ "subject": "name",
+ }
+ ],
+ }
+ )
+ }
+ ):
self.assertEquals(filter_values("name"), ["foo"])
- request.form = {
- "form_data": json.dumps(
- {
- "adhoc_filters": [
- {
- "clause": "WHERE",
- "comparator": ["foo", "bar"],
- "expressionType": "SIMPLE",
- "operator": "in",
- "subject": "name",
- }
- ],
- }
- )
- }
-
- with mock.patch("superset.jinja_context.request", request):
+ with app.test_request_context(
+ data={
+ "form_data": json.dumps(
+ {
+ "adhoc_filters": [
+ {
+ "clause": "WHERE",
+ "comparator": ["foo", "bar"],
+ "expressionType": "SIMPLE",
+ "operator": "in",
+ "subject": "name",
+ }
+ ],
+ }
+ )
+ }
+ ):
self.assertEquals(filter_values("name"), ["foo", "bar"])
def test_filter_values_extra_filters(self) -> None:
- request = mock.MagicMock()
+ with app.test_request_context(
+ data={
+ "form_data": json.dumps(
+ {"extra_filters": [{"col": "name", "op": "in", "val":
"foo"}]}
+ )
+ }
+ ):
+ self.assertEquals(filter_values("name"), ["foo"])
- request.form = {
- "form_data": json.dumps(
- {"extra_filters": [{"col": "name", "op": "in", "val": "foo"}]}
- )
- }
+ def test_url_param_default(self) -> None:
+ with app.test_request_context():
+ self.assertEquals(ExtraCache().url_param("foo", "bar"), "bar")
- with mock.patch("superset.jinja_context.request", request):
- self.assertEquals(filter_values("name"), ["foo"])
+ def test_url_param_no_default(self) -> None:
+ with app.test_request_context():
+ self.assertEquals(ExtraCache().url_param("foo"), None)
+
+ def test_url_param_query(self) -> None:
+ with app.test_request_context(query_string={"foo": "bar"}):
+ self.assertEquals(ExtraCache().url_param("foo"), "bar")
+
+ def test_url_param_form_data(self) -> None:
+ with app.test_request_context(
+ query_string={"form_data": json.dumps({"url_params": {"foo":
"bar"}})}
+ ):
+ self.assertEquals(ExtraCache().url_param("foo"), "bar")
diff --git a/tests/macro_tests.py b/tests/macro_tests.py
deleted file mode 100644
index 1b9b66e..0000000
--- a/tests/macro_tests.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# 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.
-from flask import json
-
-from superset import app, jinja_context
-from tests.base_tests import SupersetTestCase
-
-
-class MacroTestCase(SupersetTestCase):
- def test_filter_values_macro(self):
- form_data1 = {
- "extra_filters": [{"col": "my_special_filter", "op": "in", "val":
["foo"]}],
- "filters": [{"col": "my_special_filter2", "op": "in", "val":
["bar"]}],
- }
-
- form_data2 = {
- "extra_filters": [
- {"col": "my_special_filter", "op": "in", "val": ["foo", "bar"]}
- ]
- }
-
- form_data3 = {
- "extra_filters": [
- {"col": "my_special_filter", "op": "in", "val": ["foo", "bar"]}
- ],
- "filters": [{"col": "my_special_filter", "op": "in", "val":
["savage"]}],
- }
-
- form_data4 = {
- "extra_filters": [{"col": "my_special_filter", "op": "in", "val":
"foo"}],
- "filters": [{"col": "my_special_filter", "op": "in", "val":
"savage"}],
- }
-
- data1 = {"form_data": json.dumps(form_data1)}
- data2 = {"form_data": json.dumps(form_data2)}
- data3 = {"form_data": json.dumps(form_data3)}
- data4 = {"form_data": json.dumps(form_data4)}
-
- with app.test_request_context(data=data1):
- filter_values = jinja_context.filter_values("my_special_filter")
- self.assertEqual(filter_values, ["foo"])
-
- filter_values = jinja_context.filter_values("my_special_filter2")
- self.assertEqual(filter_values, ["bar"])
-
- filter_values = jinja_context.filter_values("")
- self.assertEqual(filter_values, [])
-
- with app.test_request_context(data=data2):
- filter_values = jinja_context.filter_values("my_special_filter")
- self.assertEqual(filter_values, ["foo", "bar"])
-
- with app.test_request_context(data=data3):
- filter_values = jinja_context.filter_values("my_special_filter")
- self.assertEqual(filter_values, ["savage", "foo", "bar"])
-
- with app.test_request_context():
- filter_values = jinja_context.filter_values("nonexistent_filter",
"foo")
- self.assertEqual(filter_values, ["foo"])
-
- with app.test_request_context(data=data4):
- filter_values = jinja_context.filter_values("my_special_filter")
- self.assertEqual(filter_values, ["savage", "foo"])
diff --git a/tests/utils_tests.py b/tests/utils_tests.py
index 70b77af..9f3d0f9 100644
--- a/tests/utils_tests.py
+++ b/tests/utils_tests.py
@@ -20,11 +20,12 @@ import uuid
from datetime import date, datetime, time, timedelta
from decimal import Decimal
import hashlib
+import json
import os
from unittest.mock import Mock, patch
import numpy
-from flask import Flask
+from flask import Flask, g
from flask_caching import Cache
from sqlalchemy.exc import ArgumentError
@@ -60,8 +61,11 @@ from superset.utils.core import (
zlib_compress,
zlib_decompress,
)
-from superset.views.utils import get_time_range_endpoints
-from superset.views.utils import build_extra_filters
+from superset.views.utils import (
+ build_extra_filters,
+ get_form_data,
+ get_time_range_endpoints,
+)
from tests.base_tests import SupersetTestCase
from .fixtures.certificates import ssl_certificate
@@ -1249,3 +1253,78 @@ class UtilsTestCase(SupersetTestCase):
get_email_address_list(",a@a; b@b c@c a-c@c; d@d, f@f"),
["a@a", "b@b", "c@c", "a-c@c", "d@d", "f@f"],
)
+
+ def test_get_form_data_default(self) -> None:
+ with app.test_request_context():
+ form_data, slc = get_form_data()
+
+ self.assertEqual(
+ form_data,
+ {"time_range_endpoints":
get_time_range_endpoints(form_data={}),},
+ )
+
+ self.assertEqual(slc, None)
+
+ def test_get_form_data_request_args(self) -> None:
+ with app.test_request_context(
+ query_string={"form_data": json.dumps({"foo": "bar"})}
+ ):
+ form_data, slc = get_form_data()
+
+ self.assertEqual(
+ form_data,
+ {
+ "foo": "bar",
+ "time_range_endpoints":
get_time_range_endpoints(form_data={}),
+ },
+ )
+
+ self.assertEqual(slc, None)
+
+ def test_get_form_data_request_form(self) -> None:
+ with app.test_request_context(data={"form_data": json.dumps({"foo":
"bar"})}):
+ form_data, slc = get_form_data()
+
+ self.assertEqual(
+ form_data,
+ {
+ "foo": "bar",
+ "time_range_endpoints":
get_time_range_endpoints(form_data={}),
+ },
+ )
+
+ self.assertEqual(slc, None)
+
+ def test_get_form_data_request_args_and_form(self) -> None:
+ with app.test_request_context(
+ data={"form_data": json.dumps({"foo": "bar"})},
+ query_string={"form_data": json.dumps({"baz": "bar"})},
+ ):
+ form_data, slc = get_form_data()
+
+ self.assertEqual(
+ form_data,
+ {
+ "baz": "bar",
+ "foo": "bar",
+ "time_range_endpoints":
get_time_range_endpoints(form_data={}),
+ },
+ )
+
+ self.assertEqual(slc, None)
+
+ def test_get_form_data_globals(self) -> None:
+ with app.test_request_context():
+ g.form_data = {"foo": "bar"}
+ form_data, slc = get_form_data()
+ delattr(g, "form_data")
+
+ self.assertEqual(
+ form_data,
+ {
+ "foo": "bar",
+ "time_range_endpoints":
get_time_range_endpoints(form_data={}),
+ },
+ )
+
+ self.assertEqual(slc, None)