diff --git a/requirements/base.txt b/requirements/base.txt
index 34478f1052ce5..4ffcc24b1bc1d 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -35,7 +35,7 @@ cffi==1.15.1
# via
# cryptography
# pynacl
-click==8.0.4
+click==8.1.3
# via
# apache-superset
# celery
@@ -75,7 +75,7 @@ dnspython==2.1.0
# via email-validator
email-validator==1.1.3
# via flask-appbuilder
-flask==2.1.3
+flask==2.2.5
# via
# apache-superset
# flask-appbuilder
@@ -122,8 +122,6 @@ geographiclib==1.52
# via geopy
geopy==2.2.0
# via apache-superset
-greenlet==2.0.2
- # via sqlalchemy
gunicorn==20.1.0
# via apache-superset
hashids==1.3.1
@@ -142,11 +140,11 @@ importlib-resources==5.12.0
# via limits
isodate==0.6.0
# via apache-superset
-itsdangerous==2.1.1
+itsdangerous==2.1.2
# via
# flask
# flask-wtf
-jinja2==3.0.3
+jinja2==3.1.2
# via
# flask
# flask-babel
@@ -168,6 +166,7 @@ markupsafe==2.1.1
# via
# jinja2
# mako
+ # werkzeug
# wtforms
marshmallow==3.13.0
# via
@@ -305,8 +304,9 @@ vine==5.0.0
# kombu
wcwidth==0.2.5
# via prompt-toolkit
-werkzeug==2.1.2
+werkzeug==2.3.3
# via
+ # apache-superset
# flask
# flask-jwt-extended
# flask-login
diff --git a/requirements/docker.txt b/requirements/docker.txt
index cd2a6e175c581..64d3e46ec26d3 100644
--- a/requirements/docker.txt
+++ b/requirements/docker.txt
@@ -12,6 +12,10 @@
# -r requirements/docker.in
gevent==22.10.2
# via -r requirements/docker.in
+greenlet==2.0.2
+ # via
+ # -r requirements/docker.in
+ # gevent
psycopg2-binary==2.9.6
# via apache-superset
zope-event==4.5.0
diff --git a/requirements/integration.txt b/requirements/integration.txt
index 542422d985cfc..b9fdb03e0c22c 100644
--- a/requirements/integration.txt
+++ b/requirements/integration.txt
@@ -13,7 +13,7 @@ cfgv==3.3.1
# via pre-commit
chardet==5.1.0
# via tox
-click==8.0.4
+click==8.1.3
# via
# pip-compile-multi
# pip-tools
diff --git a/requirements/testing.txt b/requirements/testing.txt
index 1e54713546831..a4fd3f2731d72 100644
--- a/requirements/testing.txt
+++ b/requirements/testing.txt
@@ -107,6 +107,8 @@ pydata-google-auth==1.7.0
# via pandas-gbq
pyfakefs==5.2.2
# via -r requirements/testing.in
+pyhive[presto]==0.6.5
+ # via apache-superset
pytest==7.3.1
# via
# -r requirements/testing.in
diff --git a/setup.py b/setup.py
index b66b871818aba..379be0697834b 100644
--- a/setup.py
+++ b/setup.py
@@ -82,7 +82,7 @@ def get_git_sha() -> str:
"cron-descriptor",
"cryptography>=39.0.1, <40",
"deprecation>=2.1.0, <2.2.0",
- "flask>=2.1.3, <2.2",
+ "flask>=2.2.5, <3.0.0",
"flask-appbuilder>=4.3.0, <5.0.0",
"flask-caching>=1.10.1, <1.11",
"flask-compress>=1.13, <2.0",
@@ -125,6 +125,7 @@ def get_git_sha() -> str:
"tabulate>=0.8.9, <0.9",
"typing-extensions>=4, <5",
"waitress; sys_platform == 'win32'",
+ "werkzeug>=2.3.3, <3",
"wtforms>=2.3.3, <4",
"wtforms-json",
"xlsxwriter>=3.0.7, <3.1",
diff --git a/tests/integration_tests/async_events/api_tests.py b/tests/integration_tests/async_events/api_tests.py
index a63f540dd0f84..5c12b29af49c0 100644
--- a/tests/integration_tests/async_events/api_tests.py
+++ b/tests/integration_tests/async_events/api_tests.py
@@ -33,6 +33,7 @@ def fetch_events(self, last_id: Optional[str] = None):
@mock.patch("uuid.uuid4", return_value=UUID)
def test_events(self, mock_uuid4):
+ app._got_first_request = False
async_query_manager.init_app(app)
self.login(username="admin")
with mock.patch.object(async_query_manager._redis, "xrange") as mock_xrange:
@@ -46,6 +47,7 @@ def test_events(self, mock_uuid4):
@mock.patch("uuid.uuid4", return_value=UUID)
def test_events_last_id(self, mock_uuid4):
+ app._got_first_request = False
async_query_manager.init_app(app)
self.login(username="admin")
with mock.patch.object(async_query_manager._redis, "xrange") as mock_xrange:
@@ -59,6 +61,7 @@ def test_events_last_id(self, mock_uuid4):
@mock.patch("uuid.uuid4", return_value=UUID)
def test_events_results(self, mock_uuid4):
+ app._got_first_request = False
async_query_manager.init_app(app)
self.login(username="admin")
with mock.patch.object(async_query_manager._redis, "xrange") as mock_xrange:
@@ -107,6 +110,7 @@ def test_events_results(self, mock_uuid4):
self.assertEqual(response, expected)
def test_events_no_login(self):
+ app._got_first_request = False
async_query_manager.init_app(app)
rv = self.fetch_events()
assert rv.status_code == 401
diff --git a/tests/integration_tests/charts/data/api_tests.py b/tests/integration_tests/charts/data/api_tests.py
index 5315bbbaed700..2dd25b8844076 100644
--- a/tests/integration_tests/charts/data/api_tests.py
+++ b/tests/integration_tests/charts/data/api_tests.py
@@ -28,10 +28,7 @@
from flask import Response
from tests.integration_tests.conftest import with_feature_flags
from superset.models.sql_lab import Query
-from tests.integration_tests.base_tests import (
- SupersetTestCase,
- test_client,
-)
+from tests.integration_tests.base_tests import SupersetTestCase, test_client
from tests.integration_tests.annotation_layers.fixtures import create_annotation_layers
from tests.integration_tests.fixtures.birth_names_dashboard import (
load_birth_names_dashboard_with_slices,
@@ -626,6 +623,7 @@ def test_when_where_parameter_is_template_and_query_result_type__query_is_templa
@pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
def test_chart_data_async(self):
self.logout()
+ app._got_first_request = False
async_query_manager.init_app(app)
self.login("admin")
rv = self.post_assert_metric(CHART_DATA_URI, self.query_context_payload, "data")
@@ -643,6 +641,7 @@ def test_chart_data_async_cached_sync_response(self):
Chart data API: Test chart data query returns results synchronously
when results are already cached.
"""
+ app._got_first_request = False
async_query_manager.init_app(app)
class QueryContext:
@@ -672,6 +671,7 @@ def test_chart_data_async_results_type(self):
"""
Chart data API: Test chart data query non-JSON format (async)
"""
+ app._got_first_request = False
async_query_manager.init_app(app)
self.query_context_payload["result_type"] = "results"
rv = self.post_assert_metric(CHART_DATA_URI, self.query_context_payload, "data")
@@ -683,6 +683,7 @@ def test_chart_data_async_invalid_token(self):
"""
Chart data API: Test chart data query (async)
"""
+ app._got_first_request = False
async_query_manager.init_app(app)
test_client.set_cookie(
"localhost", app.config["GLOBAL_ASYNC_QUERIES_JWT_COOKIE_NAME"], "foo"
@@ -998,6 +999,7 @@ def test_chart_data_cache(self, cache_loader):
"""
Chart data cache API: Test chart data async cache request
"""
+ app._got_first_request = False
async_query_manager.init_app(app)
cache_loader.load.return_value = self.query_context_payload
orig_run = ChartDataCommand.run
@@ -1024,6 +1026,7 @@ def test_chart_data_cache_run_failed(self, cache_loader):
"""
Chart data cache API: Test chart data async cache request with run failure
"""
+ app._got_first_request = False
async_query_manager.init_app(app)
cache_loader.load.return_value = self.query_context_payload
rv = self.get_assert_metric(
@@ -1041,8 +1044,9 @@ def test_chart_data_cache_no_login(self, cache_loader):
"""
Chart data cache API: Test chart data async cache request (no login)
"""
- self.logout()
+ app._got_first_request = False
async_query_manager.init_app(app)
+ self.logout()
cache_loader.load.return_value = self.query_context_payload
orig_run = ChartDataCommand.run
@@ -1063,6 +1067,7 @@ def test_chart_data_cache_key_error(self):
"""
Chart data cache API: Test chart data async cache request with invalid cache key
"""
+ app._got_first_request = False
async_query_manager.init_app(app)
rv = self.get_assert_metric(
f"{CHART_DATA_URI}/test-cache-key", "data_from_cache"
@@ -1188,10 +1193,10 @@ def test_data_cache_default_timeout(
def test_chart_cache_timeout(
+ load_energy_table_with_slice: List[Slice],
test_client,
login_as_admin,
physical_query_context,
- load_energy_table_with_slice: List[Slice],
):
# should override datasource cache timeout
@@ -1210,7 +1215,6 @@ def test_chart_cache_timeout(
db.session.commit()
physical_query_context["form_data"] = {"slice_id": slice_with_cache_timeout.id}
-
rv = test_client.post(CHART_DATA_URI, json=physical_query_context)
assert rv.json["result"][0]["cache_timeout"] == 20
diff --git a/tests/integration_tests/core_tests.py b/tests/integration_tests/core_tests.py
index f036f18bf6aa3..2e9e287620417 100644
--- a/tests/integration_tests/core_tests.py
+++ b/tests/integration_tests/core_tests.py
@@ -1088,6 +1088,7 @@ def test_explore_json_async(self):
"groupby": ["gender"],
"row_limit": 100,
}
+ app._got_first_request = False
async_query_manager.init_app(app)
self.login(username="admin")
rv = self.client.post(
@@ -1119,6 +1120,7 @@ def test_explore_json_async_results_format(self):
"groupby": ["gender"],
"row_limit": 100,
}
+ app._got_first_request = False
async_query_manager.init_app(app)
self.login(username="admin")
rv = self.client.post(
diff --git a/tests/integration_tests/dashboards/permalink/api_tests.py b/tests/integration_tests/dashboards/permalink/api_tests.py
index 2323b56d6155c..b20112334d1d8 100644
--- a/tests/integration_tests/dashboards/permalink/api_tests.py
+++ b/tests/integration_tests/dashboards/permalink/api_tests.py
@@ -66,7 +66,7 @@ def permalink_salt() -> Iterator[str]:
def test_post(
- test_client, login_as_admin, dashboard_id: int, permalink_salt: str
+ dashboard_id: int, permalink_salt: str, test_client, login_as_admin
) -> None:
resp = test_client.post(f"api/v1/dashboard/{dashboard_id}/permalink", json=STATE)
assert resp.status_code == 201
@@ -93,14 +93,14 @@ def test_post_access_denied(test_client, login_as, dashboard_id: int):
assert resp.status_code == 404
-def test_post_invalid_schema(test_client, login_as_admin, dashboard_id: int):
+def test_post_invalid_schema(dashboard_id: int, test_client, login_as_admin):
resp = test_client.post(
f"api/v1/dashboard/{dashboard_id}/permalink", json={"foo": "bar"}
)
assert resp.status_code == 400
-def test_get(test_client, login_as_admin, dashboard_id: int, permalink_salt: str):
+def test_get(dashboard_id: int, permalink_salt: str, test_client, login_as_admin):
key = test_client.post(
f"api/v1/dashboard/{dashboard_id}/permalink", json=STATE
).json["key"]
diff --git a/tests/integration_tests/datasource_tests.py b/tests/integration_tests/datasource_tests.py
index 52bd9ec244cc3..2e42c32c8b19e 100644
--- a/tests/integration_tests/datasource_tests.py
+++ b/tests/integration_tests/datasource_tests.py
@@ -543,7 +543,7 @@ def test_get_samples_with_filters(test_client, login_as_admin, virtual_dataset):
f"/datasource/samples?datasource_id={virtual_dataset.id}&datasource_type=table"
)
rv = test_client.post(uri, json=None)
- assert rv.status_code == 400
+ assert rv.status_code == 415
rv = test_client.post(uri, json={})
assert rv.status_code == 200
diff --git a/tests/integration_tests/db_engine_specs/base_engine_spec_tests.py b/tests/integration_tests/db_engine_specs/base_engine_spec_tests.py
index 188fc94946a90..15465d8a79c79 100644
--- a/tests/integration_tests/db_engine_specs/base_engine_spec_tests.py
+++ b/tests/integration_tests/db_engine_specs/base_engine_spec_tests.py
@@ -447,17 +447,19 @@ def test_validate_parameters_missing():
"query": {},
}
}
- errors = BasicParametersMixin.validate_parameters(properties)
- assert errors == [
- SupersetError(
- message=(
- "One or more parameters are missing: " "database, host, port, username"
+ with app.app_context():
+ errors = BasicParametersMixin.validate_parameters(properties)
+ assert errors == [
+ SupersetError(
+ message=(
+ "One or more parameters are missing: "
+ "database, host, port, username"
+ ),
+ error_type=SupersetErrorType.CONNECTION_MISSING_PARAMETERS_ERROR,
+ level=ErrorLevel.WARNING,
+ extra={"missing": ["database", "host", "port", "username"]},
),
- error_type=SupersetErrorType.CONNECTION_MISSING_PARAMETERS_ERROR,
- level=ErrorLevel.WARNING,
- extra={"missing": ["database", "host", "port", "username"]},
- ),
- ]
+ ]
@mock.patch("superset.db_engine_specs.base.is_hostname_valid")
@@ -474,21 +476,22 @@ def test_validate_parameters_invalid_host(is_hostname_valid):
"query": {"sslmode": "verify-full"},
}
}
- errors = BasicParametersMixin.validate_parameters(properties)
- assert errors == [
- SupersetError(
- message="One or more parameters are missing: port",
- error_type=SupersetErrorType.CONNECTION_MISSING_PARAMETERS_ERROR,
- level=ErrorLevel.WARNING,
- extra={"missing": ["port"]},
- ),
- SupersetError(
- message="The hostname provided can't be resolved.",
- error_type=SupersetErrorType.CONNECTION_INVALID_HOSTNAME_ERROR,
- level=ErrorLevel.ERROR,
- extra={"invalid": ["host"]},
- ),
- ]
+ with app.app_context():
+ errors = BasicParametersMixin.validate_parameters(properties)
+ assert errors == [
+ SupersetError(
+ message="One or more parameters are missing: port",
+ error_type=SupersetErrorType.CONNECTION_MISSING_PARAMETERS_ERROR,
+ level=ErrorLevel.WARNING,
+ extra={"missing": ["port"]},
+ ),
+ SupersetError(
+ message="The hostname provided can't be resolved.",
+ error_type=SupersetErrorType.CONNECTION_INVALID_HOSTNAME_ERROR,
+ level=ErrorLevel.ERROR,
+ extra={"invalid": ["host"]},
+ ),
+ ]
@mock.patch("superset.db_engine_specs.base.is_hostname_valid")
@@ -507,20 +510,21 @@ def test_validate_parameters_port_closed(is_port_open, is_hostname_valid):
"query": {"sslmode": "verify-full"},
}
}
- errors = BasicParametersMixin.validate_parameters(properties)
- assert errors == [
- SupersetError(
- message="The port is closed.",
- error_type=SupersetErrorType.CONNECTION_PORT_CLOSED_ERROR,
- level=ErrorLevel.ERROR,
- extra={
- "invalid": ["port"],
- "issue_codes": [
- {"code": 1008, "message": "Issue 1008 - The port is closed."}
- ],
- },
- )
- ]
+ with app.app_context():
+ errors = BasicParametersMixin.validate_parameters(properties)
+ assert errors == [
+ SupersetError(
+ message="The port is closed.",
+ error_type=SupersetErrorType.CONNECTION_PORT_CLOSED_ERROR,
+ level=ErrorLevel.ERROR,
+ extra={
+ "invalid": ["port"],
+ "issue_codes": [
+ {"code": 1008, "message": "Issue 1008 - The port is closed."}
+ ],
+ },
+ )
+ ]
def test_get_indexes():
diff --git a/tests/integration_tests/explore/permalink/api_tests.py b/tests/integration_tests/explore/permalink/api_tests.py
index 4c6a3c12ddfdb..29a39f0d4ac08 100644
--- a/tests/integration_tests/explore/permalink/api_tests.py
+++ b/tests/integration_tests/explore/permalink/api_tests.py
@@ -67,7 +67,7 @@ def permalink_salt() -> Iterator[str]:
def test_post(
- test_client, login_as_admin, form_data: Dict[str, Any], permalink_salt: str
+ form_data: Dict[str, Any], permalink_salt: str, test_client, login_as_admin
):
resp = test_client.post(f"api/v1/explore/permalink", json={"formData": form_data})
assert resp.status_code == 201
@@ -80,14 +80,14 @@ def test_post(
db.session.commit()
-def test_post_access_denied(test_client, login_as, form_data):
+def test_post_access_denied(form_data, test_client, login_as):
login_as("gamma")
resp = test_client.post(f"api/v1/explore/permalink", json={"formData": form_data})
assert resp.status_code == 403
def test_get_missing_chart(
- test_client, login_as_admin, chart, permalink_salt: str
+ chart, permalink_salt: str, test_client, login_as_admin
) -> None:
from superset.key_value.models import KeyValueEntry
@@ -121,7 +121,7 @@ def test_post_invalid_schema(test_client, login_as_admin) -> None:
def test_get(
- test_client, login_as_admin, form_data: Dict[str, Any], permalink_salt: str
+ form_data: Dict[str, Any], permalink_salt: str, test_client, login_as_admin
) -> None:
resp = test_client.post(f"api/v1/explore/permalink", json={"formData": form_data})
data = json.loads(resp.data.decode("utf-8"))
diff --git a/tests/integration_tests/reports/commands_tests.py b/tests/integration_tests/reports/commands_tests.py
index cad6a75a5d25e..a81bc6fa66adc 100644
--- a/tests/integration_tests/reports/commands_tests.py
+++ b/tests/integration_tests/reports/commands_tests.py
@@ -659,7 +659,7 @@ def test_email_chart_report_schedule(
)
# assert that the link sent is correct
assert (
- 'Explore in Superset' in email_mock.call_args[0][2]
)
@@ -714,7 +714,7 @@ def _screenshot_side_effect(user: User) -> Optional[bytes]:
# assert that the link sent is correct
assert (
- 'Explore in Superset' in email_mock.call_args[0][2]
)
@@ -759,7 +759,7 @@ def test_email_chart_report_schedule_force_screenshot(
)
# assert that the link sent is correct
assert (
- 'Explore in Superset' in email_mock.call_args[0][2]
)
@@ -796,7 +796,7 @@ def test_email_chart_alert_schedule(
notification_targets = get_target_from_report_schedule(create_alert_email_chart)
# assert that the link sent is correct
assert (
- 'Explore in Superset' in email_mock.call_args[0][2]
)
@@ -868,7 +868,7 @@ def test_email_chart_report_schedule_with_csv(
)
# assert that the link sent is correct
assert (
- 'Explore in Superset' in email_mock.call_args[0][2]
)
@@ -1296,7 +1296,7 @@ def test_slack_chart_report_schedule_with_text(
| 1 | c21 | c22 | c23 |"""
assert table_markdown in post_message_mock.call_args[1]["text"]
assert (
- f""
+ f""
in post_message_mock.call_args[1]["text"]
)
diff --git a/tests/integration_tests/security/analytics_db_safety_tests.py b/tests/integration_tests/security/analytics_db_safety_tests.py
index 7e36268e30286..9c40050c0ad96 100644
--- a/tests/integration_tests/security/analytics_db_safety_tests.py
+++ b/tests/integration_tests/security/analytics_db_safety_tests.py
@@ -21,6 +21,7 @@
from superset.exceptions import SupersetSecurityException
from superset.security.analytics_db_safety import check_sqlalchemy_uri
+from tests.integration_tests.test_app import app
@pytest.mark.parametrize(
@@ -83,9 +84,10 @@
def test_check_sqlalchemy_uri(
sqlalchemy_uri: str, error: bool, error_message: Optional[str]
):
- if error:
- with pytest.raises(SupersetSecurityException) as excinfo:
+ with app.app_context():
+ if error:
+ with pytest.raises(SupersetSecurityException) as excinfo:
+ check_sqlalchemy_uri(make_url(sqlalchemy_uri))
+ assert str(excinfo.value) == error_message
+ else:
check_sqlalchemy_uri(make_url(sqlalchemy_uri))
- assert str(excinfo.value) == error_message
- else:
- check_sqlalchemy_uri(make_url(sqlalchemy_uri))
diff --git a/tests/integration_tests/tasks/async_queries_tests.py b/tests/integration_tests/tasks/async_queries_tests.py
index 9e5c9657cf936..50806ee677394 100644
--- a/tests/integration_tests/tasks/async_queries_tests.py
+++ b/tests/integration_tests/tasks/async_queries_tests.py
@@ -47,6 +47,7 @@ class TestAsyncQueries(SupersetTestCase):
@mock.patch.object(async_query_manager, "update_job")
@mock.patch.object(async_queries, "set_form_data")
def test_load_chart_data_into_cache(self, mock_set_form_data, mock_update_job):
+ app._got_first_request = False
async_query_manager.init_app(app)
query_context = get_query_context("birth_names")
user = security_manager.find_user("gamma")
@@ -69,6 +70,7 @@ def test_load_chart_data_into_cache(self, mock_set_form_data, mock_update_job):
)
@mock.patch.object(async_query_manager, "update_job")
def test_load_chart_data_into_cache_error(self, mock_update_job, mock_run_command):
+ app._got_first_request = False
async_query_manager.init_app(app)
query_context = get_query_context("birth_names")
user = security_manager.find_user("gamma")
@@ -91,6 +93,7 @@ def test_load_chart_data_into_cache_error(self, mock_update_job, mock_run_comman
def test_soft_timeout_load_chart_data_into_cache(
self, mock_update_job, mock_run_command
):
+ app._got_first_request = False
async_query_manager.init_app(app)
user = security_manager.find_user("gamma")
form_data = {}
@@ -115,6 +118,7 @@ def test_soft_timeout_load_chart_data_into_cache(
@pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
@mock.patch.object(async_query_manager, "update_job")
def test_load_explore_json_into_cache(self, mock_update_job):
+ app._got_first_request = False
async_query_manager.init_app(app)
table = self.get_table(name="birth_names")
user = security_manager.find_user("gamma")
@@ -146,6 +150,7 @@ def test_load_explore_json_into_cache(self, mock_update_job):
def test_load_explore_json_into_cache_error(
self, mock_set_form_data, mock_update_job
):
+ app._got_first_request = False
async_query_manager.init_app(app)
user = security_manager.find_user("gamma")
form_data = {}
@@ -169,6 +174,7 @@ def test_load_explore_json_into_cache_error(
def test_soft_timeout_load_explore_json_into_cache(
self, mock_update_job, mock_run_command
):
+ app._got_first_request = False
async_query_manager.init_app(app)
user = security_manager.find_user("gamma")
form_data = {}
diff --git a/tests/integration_tests/utils/core_tests.py b/tests/integration_tests/utils/core_tests.py
index 29b94d6d37eef..1a2fa6a5218c1 100644
--- a/tests/integration_tests/utils/core_tests.py
+++ b/tests/integration_tests/utils/core_tests.py
@@ -17,6 +17,7 @@
import pytest
from superset.utils.core import form_data_to_adhoc, simple_filter_to_adhoc
+from tests.integration_tests.test_app import app
def test_simple_filter_to_adhoc_generates_deterministic_values():
@@ -81,4 +82,5 @@ def test_form_data_to_adhoc_incorrect_clause_type():
form_data = {"where": "1 = 1", "having": "count(*) > 1"}
with pytest.raises(ValueError):
- form_data_to_adhoc(form_data, "foobar")
+ with app.app_context():
+ form_data_to_adhoc(form_data, "foobar")