mirror of
https://github.com/apache/superset.git
synced 2026-04-19 16:14:52 +00:00
fix(cache): Add cache warmup for non-legacy charts (#24671)
This commit is contained in:
@@ -14,37 +14,41 @@
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# isort:skip_file
|
||||
"""Unit tests for Superset"""
|
||||
import json
|
||||
from io import BytesIO
|
||||
from unittest import mock
|
||||
from zipfile import is_zipfile, ZipFile
|
||||
|
||||
import prison
|
||||
import pytest
|
||||
import yaml
|
||||
from flask_babel import lazy_gettext as _
|
||||
from parameterized import parameterized
|
||||
from sqlalchemy import and_
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
from superset.charts.commands.exceptions import ChartDataQueryFailedError
|
||||
from superset.charts.data.commands.get_data_command import ChartDataCommand
|
||||
from superset.connectors.sqla.models import SqlaTable
|
||||
from superset.extensions import cache_manager, db, security_manager
|
||||
from superset.models.core import Database, FavStar, FavStarClassName
|
||||
from superset.models.dashboard import Dashboard
|
||||
from superset.reports.models import ReportSchedule, ReportScheduleType
|
||||
from superset.models.slice import Slice
|
||||
from superset.reports.models import ReportSchedule, ReportScheduleType
|
||||
from superset.utils.core import get_example_default_schema
|
||||
from superset.utils.database import get_example_database
|
||||
|
||||
from tests.integration_tests.conftest import with_feature_flags
|
||||
from superset.viz import viz_types
|
||||
from tests.integration_tests.base_api_tests import ApiOwnersTestCaseMixin
|
||||
from tests.integration_tests.base_tests import SupersetTestCase
|
||||
from tests.integration_tests.conftest import with_feature_flags
|
||||
from tests.integration_tests.fixtures.birth_names_dashboard import (
|
||||
load_birth_names_dashboard_with_slices,
|
||||
load_birth_names_data,
|
||||
)
|
||||
from tests.integration_tests.fixtures.energy_dashboard import (
|
||||
load_energy_table_with_slice,
|
||||
load_energy_table_data,
|
||||
load_energy_table_with_slice,
|
||||
)
|
||||
from tests.integration_tests.fixtures.importexport import (
|
||||
chart_config,
|
||||
@@ -1710,12 +1714,16 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixin):
|
||||
assert data["result"][0]["slice_name"] == "name0"
|
||||
assert data["result"][0]["datasource_id"] == 1
|
||||
|
||||
@pytest.mark.usefixtures(
|
||||
"load_energy_table_with_slice", "load_birth_names_dashboard_with_slices"
|
||||
@parameterized.expand(
|
||||
[
|
||||
"Top 10 Girl Name Share", # Legacy chart
|
||||
"Pivot Table v2", # Non-legacy chart
|
||||
],
|
||||
)
|
||||
def test_warm_up_cache(self):
|
||||
@pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
|
||||
def test_warm_up_cache(self, slice_name):
|
||||
self.login()
|
||||
slc = self.get_slice("Top 10 Girl Name Share", db.session)
|
||||
slc = self.get_slice(slice_name, db.session)
|
||||
rv = self.client.put("/api/v1/chart/warm_up_cache", json={"chart_id": slc.id})
|
||||
self.assertEqual(rv.status_code, 200)
|
||||
data = json.loads(rv.data.decode("utf-8"))
|
||||
@@ -1780,7 +1788,6 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixin):
|
||||
)
|
||||
self.assertEqual(rv.status_code, 400)
|
||||
data = json.loads(rv.data.decode("utf-8"))
|
||||
print(data)
|
||||
self.assertEqual(
|
||||
data,
|
||||
{
|
||||
@@ -1791,3 +1798,81 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixin):
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
|
||||
def test_warm_up_cache_error(self) -> None:
|
||||
self.login()
|
||||
slc = self.get_slice("Pivot Table v2", db.session)
|
||||
|
||||
with mock.patch.object(ChartDataCommand, "run") as mock_run:
|
||||
mock_run.side_effect = ChartDataQueryFailedError(
|
||||
_(
|
||||
"Error: %(error)s",
|
||||
error=_("Empty query?"),
|
||||
)
|
||||
)
|
||||
|
||||
assert json.loads(
|
||||
self.client.put(
|
||||
"/api/v1/chart/warm_up_cache",
|
||||
json={"chart_id": slc.id},
|
||||
).data
|
||||
) == {
|
||||
"result": [
|
||||
{
|
||||
"chart_id": slc.id,
|
||||
"viz_error": "Error: Empty query?",
|
||||
"viz_status": None,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
|
||||
def test_warm_up_cache_no_query_context(self) -> None:
|
||||
self.login()
|
||||
slc = self.get_slice("Pivot Table v2", db.session)
|
||||
|
||||
with mock.patch.object(Slice, "get_query_context") as mock_get_query_context:
|
||||
mock_get_query_context.return_value = None
|
||||
|
||||
assert json.loads(
|
||||
self.client.put(
|
||||
f"/api/v1/chart/warm_up_cache",
|
||||
json={"chart_id": slc.id},
|
||||
).data
|
||||
) == {
|
||||
"result": [
|
||||
{
|
||||
"chart_id": slc.id,
|
||||
"viz_error": "Chart's query context does not exist",
|
||||
"viz_status": None,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
|
||||
def test_warm_up_cache_no_datasource(self) -> None:
|
||||
self.login()
|
||||
slc = self.get_slice("Top 10 Girl Name Share", db.session)
|
||||
|
||||
with mock.patch.object(
|
||||
Slice,
|
||||
"datasource",
|
||||
new_callable=mock.PropertyMock,
|
||||
) as mock_datasource:
|
||||
mock_datasource.return_value = None
|
||||
|
||||
assert json.loads(
|
||||
self.client.put(
|
||||
f"/api/v1/chart/warm_up_cache",
|
||||
json={"chart_id": slc.id},
|
||||
).data
|
||||
) == {
|
||||
"result": [
|
||||
{
|
||||
"chart_id": slc.id,
|
||||
"viz_error": "Chart's datasource does not exist",
|
||||
"viz_status": None,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -96,52 +96,7 @@ class TestExportChartsCommand(SupersetTestCase):
|
||||
"dataset_uuid": str(example_chart.table.uuid),
|
||||
"uuid": str(example_chart.uuid),
|
||||
"version": "1.0.0",
|
||||
}
|
||||
|
||||
@patch("superset.security.manager.g")
|
||||
@pytest.mark.usefixtures("load_energy_table_with_slice")
|
||||
def test_export_chart_with_query_context(self, mock_g):
|
||||
"""Test that charts that have a query_context are exported correctly"""
|
||||
|
||||
mock_g.user = security_manager.find_user("alpha")
|
||||
example_chart = db.session.query(Slice).filter_by(slice_name="Heatmap").one()
|
||||
command = ExportChartsCommand([example_chart.id])
|
||||
|
||||
contents = dict(command.run())
|
||||
|
||||
expected = [
|
||||
"metadata.yaml",
|
||||
f"charts/Heatmap_{example_chart.id}.yaml",
|
||||
"datasets/examples/energy_usage.yaml",
|
||||
"databases/examples.yaml",
|
||||
]
|
||||
assert expected == list(contents.keys())
|
||||
|
||||
metadata = yaml.safe_load(contents[f"charts/Heatmap_{example_chart.id}.yaml"])
|
||||
|
||||
assert metadata == {
|
||||
"slice_name": "Heatmap",
|
||||
"description": None,
|
||||
"certified_by": None,
|
||||
"certification_details": None,
|
||||
"viz_type": "heatmap",
|
||||
"params": {
|
||||
"all_columns_x": "source",
|
||||
"all_columns_y": "target",
|
||||
"canvas_image_rendering": "pixelated",
|
||||
"collapsed_fieldsets": "",
|
||||
"linear_color_scheme": "blue_white_yellow",
|
||||
"metric": "sum__value",
|
||||
"normalize_across": "heatmap",
|
||||
"slice_name": "Heatmap",
|
||||
"viz_type": "heatmap",
|
||||
"xscale_interval": "1",
|
||||
"yscale_interval": "1",
|
||||
},
|
||||
"cache_timeout": None,
|
||||
"dataset_uuid": str(example_chart.table.uuid),
|
||||
"uuid": str(example_chart.uuid),
|
||||
"version": "1.0.0",
|
||||
"query_context": None,
|
||||
}
|
||||
|
||||
@patch("superset.security.manager.g")
|
||||
@@ -187,6 +142,7 @@ class TestExportChartsCommand(SupersetTestCase):
|
||||
"certification_details",
|
||||
"viz_type",
|
||||
"params",
|
||||
"query_context",
|
||||
"cache_timeout",
|
||||
"uuid",
|
||||
"version",
|
||||
|
||||
@@ -14,72 +14,67 @@
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# isort:skip_file
|
||||
"""Unit tests for Superset"""
|
||||
import datetime
|
||||
import doctest
|
||||
import html
|
||||
import json
|
||||
import logging
|
||||
from urllib.parse import quote
|
||||
|
||||
import prison
|
||||
import superset.utils.database
|
||||
from superset.utils.core import backend
|
||||
from tests.integration_tests.fixtures.public_role import public_role_like_gamma
|
||||
from tests.integration_tests.fixtures.birth_names_dashboard import (
|
||||
load_birth_names_dashboard_with_slices,
|
||||
load_birth_names_data,
|
||||
)
|
||||
from sqlalchemy import Table
|
||||
|
||||
import pytest
|
||||
import pytz
|
||||
import random
|
||||
import unittest
|
||||
from unittest import mock
|
||||
from urllib.parse import quote
|
||||
|
||||
import pandas as pd
|
||||
import prison
|
||||
import pytest
|
||||
import pytz
|
||||
import sqlalchemy as sqla
|
||||
from flask_babel import lazy_gettext as _
|
||||
from sqlalchemy import Table
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from superset.models.cache import CacheKey
|
||||
from superset.utils.database import get_example_database
|
||||
from tests.integration_tests.conftest import with_feature_flags
|
||||
from tests.integration_tests.fixtures.energy_dashboard import (
|
||||
load_energy_table_with_slice,
|
||||
load_energy_table_data,
|
||||
)
|
||||
from tests.integration_tests.insert_chart_mixin import InsertChartMixin
|
||||
from tests.integration_tests.test_app import app
|
||||
|
||||
import superset.utils.database
|
||||
import superset.views.utils
|
||||
from superset import (
|
||||
dataframe,
|
||||
db,
|
||||
security_manager,
|
||||
sql_lab,
|
||||
)
|
||||
from superset import dataframe, db, security_manager, sql_lab
|
||||
from superset.charts.commands.exceptions import ChartDataQueryFailedError
|
||||
from superset.charts.data.commands.get_data_command import ChartDataCommand
|
||||
from superset.common.db_query_status import QueryStatus
|
||||
from superset.connectors.sqla.models import SqlaTable
|
||||
from superset.db_engine_specs.base import BaseEngineSpec
|
||||
from superset.db_engine_specs.mssql import MssqlEngineSpec
|
||||
from superset.exceptions import SupersetException
|
||||
from superset.exceptions import QueryObjectValidationError, SupersetException
|
||||
from superset.extensions import async_query_manager, cache_manager
|
||||
from superset.models import core as models
|
||||
from superset.models.annotations import Annotation, AnnotationLayer
|
||||
from superset.models.cache import CacheKey
|
||||
from superset.models.dashboard import Dashboard
|
||||
from superset.models.slice import Slice
|
||||
from superset.models.sql_lab import Query
|
||||
from superset.result_set import SupersetResultSet
|
||||
from superset.utils import core as utils
|
||||
from superset.utils.core import backend
|
||||
from superset.utils.database import get_example_database
|
||||
from superset.views import core as views
|
||||
from superset.views.database.views import DatabaseView
|
||||
|
||||
from .base_tests import SupersetTestCase
|
||||
from tests.integration_tests.conftest import CTAS_SCHEMA_NAME, with_feature_flags
|
||||
from tests.integration_tests.fixtures.birth_names_dashboard import (
|
||||
load_birth_names_dashboard_with_slices,
|
||||
load_birth_names_data,
|
||||
)
|
||||
from tests.integration_tests.fixtures.energy_dashboard import (
|
||||
load_energy_table_data,
|
||||
load_energy_table_with_slice,
|
||||
)
|
||||
from tests.integration_tests.fixtures.public_role import public_role_like_gamma
|
||||
from tests.integration_tests.fixtures.world_bank_dashboard import (
|
||||
load_world_bank_dashboard_with_slices,
|
||||
load_world_bank_data,
|
||||
)
|
||||
from tests.integration_tests.conftest import CTAS_SCHEMA_NAME
|
||||
from tests.integration_tests.insert_chart_mixin import InsertChartMixin
|
||||
from tests.integration_tests.test_app import app
|
||||
|
||||
from .base_tests import SupersetTestCase
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -389,7 +384,8 @@ class TestCore(SupersetTestCase, InsertChartMixin):
|
||||
db.session.commit()
|
||||
|
||||
@pytest.mark.usefixtures(
|
||||
"load_energy_table_with_slice", "load_birth_names_dashboard_with_slices"
|
||||
"load_birth_names_dashboard_with_slices",
|
||||
"load_energy_table_with_slice",
|
||||
)
|
||||
def test_warm_up_cache(self):
|
||||
self.login()
|
||||
@@ -415,6 +411,29 @@ class TestCore(SupersetTestCase, InsertChartMixin):
|
||||
+ quote(json.dumps([{"col": "name", "op": "in", "val": ["Jennifer"]}]))
|
||||
) == [{"slice_id": slc.id, "viz_error": None, "viz_status": "success"}]
|
||||
|
||||
@pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
|
||||
def test_warm_up_cache_error(self) -> None:
|
||||
self.login()
|
||||
slc = self.get_slice("Pivot Table v2", db.session)
|
||||
|
||||
with mock.patch.object(
|
||||
ChartDataCommand,
|
||||
"run",
|
||||
side_effect=ChartDataQueryFailedError(
|
||||
_(
|
||||
"Error: %(error)s",
|
||||
error=_("Empty query?"),
|
||||
)
|
||||
),
|
||||
):
|
||||
assert self.get_json_resp(f"/superset/warm_up_cache?slice_id={slc.id}") == [
|
||||
{
|
||||
"slice_id": slc.id,
|
||||
"viz_error": "Error: Empty query?",
|
||||
"viz_status": None,
|
||||
}
|
||||
]
|
||||
|
||||
@pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
|
||||
def test_cache_logging(self):
|
||||
self.login("admin")
|
||||
|
||||
Reference in New Issue
Block a user