Compare commits

..

1 Commits

Author SHA1 Message Date
Evan
6162c90ee9 chore(ci): correct actions/cache version comment to match pinned SHA
The pinned SHA 27d5ce7 actually corresponds to actions/cache v5.0.5,
but the inline comment read "# v5". zizmor's ref-version-mismatch rule
flags this discrepancy. Update the comment to the truthful version.

Resolves code-scanning alert #2551

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 01:41:51 -07:00
9 changed files with 19 additions and 98 deletions

View File

@@ -63,7 +63,7 @@ jobs:
yarn install --immutable
- name: Cache pre-commit environments
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.cache/pre-commit
key: pre-commit-v2-${{ runner.os }}-py${{ matrix.python-version }}-${{ hashFiles('.pre-commit-config.yaml') }}

View File

@@ -56,7 +56,7 @@ jobs:
- name: Cache npm
if: env.HAS_TAGS
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS
key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -70,7 +70,7 @@ jobs:
run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
- name: Cache npm
if: env.HAS_TAGS
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
id: npm-cache # use this to check for `cache-hit` (`steps.npm-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.npm-cache-dir-path.outputs.dir }}

View File

@@ -45,7 +45,6 @@ export const IconTooltip = forwardRef<HTMLElement, IconTooltipProps>(
}}
buttonStyle="link"
className={`IconTooltip ${className}`}
aria-label={tooltip ?? undefined}
>
{children}
</Button>

View File

@@ -2963,17 +2963,6 @@ class ExploreMixin: # pylint: disable=too-many-public-methods
tp = self.get_template_processor()
processed_expression = self._process_expression_template(expression, tp)
# Apply the same parsing policy used for stored adhoc column and
# metric expressions (single statement, no set operations, and no
# sub-queries unless ALLOW_ADHOC_SUBQUERY is enabled), so expression
# validation follows one policy across the query pipeline. Imported
# locally to avoid a circular import with the connectors package.
from superset.connectors.sqla.models import validate_stored_expression
validate_stored_expression(
self.database, self.catalog, self.schema or "", processed_expression
)
# Build validation query
tbl, cte = self.get_from_clause(tp)
validation_query = self._build_validation_query(

View File

@@ -3164,6 +3164,9 @@ msgstr "Culoare după"
msgid "Color for breakpoint"
msgstr "Culoare pentru punct de întrerupere"
msgid "Color Metric"
msgstr "Indicator culoare"
msgid "Color of the source location"
msgstr "Culoarea locației sursă"
@@ -4641,7 +4644,7 @@ msgid "Deleted %(num)d theme"
msgid_plural "Deleted %(num)d themes"
msgstr[0] "Ștearsă %(num)d temă"
msgstr[1] "Șterse %(num)d teme"
msgstr[2] "Șterse %(num)d de teme"
msgstr[2] "Șterse %(num)d de teme""
#, python-format
msgid "Deleted %s"
@@ -10215,7 +10218,7 @@ msgid "Saved expressions"
msgstr "Expresii salvate"
msgid "Saved metric"
msgstr "Indicator salvat"
msgstr "Indicator salvat""
msgid "Saved queries"
msgstr "Interogări salvate"
@@ -13419,7 +13422,7 @@ msgid "This was triggered by:"
msgid_plural "This may be triggered by:"
msgstr[0] "Aceasta a fost declanșată de:"
msgstr[1] "Acestea pot fi declanșate de:"
msgstr[2] "Acestea pot fi declanșate de către:"
msgstr[2] "Acestea pot fi declanșate de către:""
msgid ""
"This will be applied to the whole table. Arrows (↑ and ↓) will be added "
@@ -14444,7 +14447,7 @@ msgstr ""
"Vizualizează un indicator corelat pentru perechi de grupuri. Hartile "
"termice (Heatmaps) excelează în evidențierea corelației sau a "
"intensității dintre două grupuri. Culoarea este utilizată pentru a "
"sublinia intensitatea legăturii dintre fiecare pereche de grupuri."
"sublinia intensitatea legăturii dintre fiecare pereche de grupuri.""
msgid ""
"Visualize geospatial data like 3D buildings, landscapes, or objects in "

View File

@@ -54,13 +54,6 @@ NUMPY_FUNCTIONS: dict[str, Callable[..., Any]] = {
"var": np.var,
}
# Operators that pandas GroupBy.agg accepts as string names. Passing the string
# avoids a FutureWarning raised when pandas receives a numpy callable it internally
# maps to its own method (e.g. np.mean → SeriesGroupBy.mean).
_PANDAS_STRING_AGGREGATORS: frozenset[str] = frozenset(
{"max", "mean", "median", "min", "prod", "std", "sum", "var"}
)
DENYLIST_ROLLING_FUNCTIONS = (
"count",
"corr",
@@ -173,7 +166,7 @@ def _get_aggregate_funcs(
)
operator = agg_obj["operator"]
if callable(operator):
aggfunc: str | Callable[..., Any] = operator
aggfunc = operator
else:
func = NUMPY_FUNCTIONS.get(operator)
if not func:
@@ -184,10 +177,7 @@ def _get_aggregate_funcs(
)
)
options = agg_obj.get("options", {})
if not options and operator in _PANDAS_STRING_AGGREGATORS:
aggfunc = operator
else:
aggfunc = partial(func, **options)
aggfunc = partial(func, **options)
agg_funcs[name] = NamedAgg(column=column, aggfunc=aggfunc)
return agg_funcs

View File

@@ -440,9 +440,9 @@ class BaseViz: # pylint: disable=too-many-public-methods
"metrics": metrics,
"row_limit": row_limit,
"filter": self.form_data.get("filters", []),
"series_limit": limit,
"timeseries_limit": limit,
"extras": extras,
"series_limit_metric": timeseries_limit_metric,
"timeseries_limit_metric": timeseries_limit_metric,
"order_desc": order_desc,
}

View File

@@ -33,7 +33,6 @@ class TestValidateExpression:
self.table.schema = "test_schema"
self.table.catalog = None
self.table.database = MagicMock()
self.table.database.backend = "sqlite"
self.table.database.db_engine_spec = MagicMock()
self.table.database.db_engine_spec.make_sqla_column_compatible = lambda x, _: x
self.table.columns = []
@@ -106,8 +105,10 @@ class TestValidateExpression:
@patch("superset.connectors.sqla.models.SqlaTable._execute_validation_query")
def test_validate_invalid_expression(self, mock_execute):
"""Unparseable SQL is rejected by the shared expression parser before the
validation query is built or executed."""
"""Test validation of invalid SQL expressions"""
# Mock _execute_validation_query to raise an exception
mock_execute.side_effect = Exception("Invalid SQL syntax")
result = self.table.validate_expression(
expression="INVALID SQL HERE",
expression_type=SqlExpressionType.COLUMN,
@@ -115,8 +116,7 @@ class TestValidateExpression:
assert result["valid"] is False
assert len(result["errors"]) == 1
assert result["errors"][0]["message"]
mock_execute.assert_not_called()
assert "Invalid SQL syntax" in result["errors"][0]["message"]
@patch("superset.connectors.sqla.models.SqlaTable._execute_validation_query")
def test_validate_having_with_non_aggregated_column(self, mock_execute):
@@ -152,38 +152,6 @@ class TestValidateExpression:
# The actual error message will come from the exception
assert "empty" in result["errors"][0]["message"].lower()
@patch("superset.models.helpers.is_feature_enabled", return_value=False)
@patch("superset.connectors.sqla.models.SqlaTable._execute_validation_query")
def test_validate_expression_rejects_subquery(
self, mock_execute: MagicMock, mock_ff: MagicMock
) -> None:
"""A sub-query expression is rejected by the same validate_adhoc_subquery
gate used for stored adhoc expressions, before any validation query is
built or run (with ALLOW_ADHOC_SUBQUERY off, the default). Locks in that
expression validation never executes the sub-query."""
result = self.table.validate_expression(
expression="(SELECT 1) IS NOT NULL OR 1 = 1",
expression_type=SqlExpressionType.WHERE,
)
assert result["valid"] is False
mock_execute.assert_not_called()
@patch("superset.models.helpers.is_feature_enabled", return_value=False)
@patch("superset.connectors.sqla.models.SqlaTable._execute_validation_query")
def test_validate_expression_rejects_set_operation(
self, mock_execute: MagicMock, mock_ff: MagicMock
) -> None:
"""A set-operation expression is rejected before the validation query is
built or run, matching the stored-adhoc-expression policy."""
result = self.table.validate_expression(
expression="1 UNION SELECT 1",
expression_type=SqlExpressionType.WHERE,
)
assert result["valid"] is False
mock_execute.assert_not_called()
@patch("superset.connectors.sqla.models.SqlaTable._execute_validation_query")
def test_validate_expression_with_rls(self, mock_execute):
"""Test that RLS filters are applied during validation"""

View File

@@ -38,31 +38,3 @@ def test_aggregate():
assert series_to_list(df["asc sum"])[0] == 5050
assert series_to_list(df["asc q2"])[0] == 75
assert series_to_list(df["desc q1"])[0] == 25
def test_aggregate_string_operators():
"""mean, median, and other operators in _PANDAS_STRING_AGGREGATORS use the
pandas string path; verify results match expected values on asc_idx [0..100]."""
aggregates = {
"asc mean": {"column": "asc_idx", "operator": "mean"},
"asc median": {"column": "asc_idx", "operator": "median"},
"asc max": {"column": "asc_idx", "operator": "max"},
"asc min": {"column": "asc_idx", "operator": "min"},
}
df = aggregate(df=categories_df, groupby=["constant"], aggregates=aggregates)
assert series_to_list(df["asc mean"])[0] == 50.0
assert series_to_list(df["asc median"])[0] == 50.0
assert series_to_list(df["asc max"])[0] == 100
assert series_to_list(df["asc min"])[0] == 0
def test_aggregate_count_includes_nulls():
"""'count' operator uses np.ma.count, which counts all rows including NaN.
It is intentionally excluded from _PANDAS_STRING_AGGREGATORS to preserve this
behavior (pandas SeriesGroupBy.count excludes NaN)."""
aggregates = {
"null_count": {"column": "idx_nulls", "operator": "count"},
}
df = aggregate(df=categories_df, groupby=["constant"], aggregates=aggregates)
# idx_nulls has 101 rows total; np.ma.count returns all 101 (NaN included)
assert series_to_list(df["null_count"])[0] == 101