sadpandajoe commented on code in PR #45103: URL: https://github.com/apache/superset/pull/45103#discussion_r4253621017
########## tests/e2e_extensions/semantic_stub/dist/backend/src/superset_e2e_semantic_stub/__init__.py: ########## @@ -0,0 +1,259 @@ +# 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. +""" +In-memory semantic layer for end-to-end tests. + +Loaded only as a ``LOCAL_EXTENSIONS`` entry by the Playwright semantic-view +configuration (``tests.integration_tests.superset_test_config_semantic``). It +registers through the public ``@semantic_layer`` decorator like any provider, +so the host code under test is the code that runs in production; only the +warehouse is replaced by a fixed table. + +Anything the stub does not implement raises instead of being ignored, so a +host change that starts sending a new query shape fails the browser test +rather than rendering plausible but unfiltered data. +""" + +from __future__ import annotations + +from collections import defaultdict +from operator import itemgetter +from typing import Any + +import pyarrow as pa +from pydantic import BaseModel, ConfigDict +from superset_core.semantic_layers.decorators import semantic_layer +from superset_core.semantic_layers.layer import SemanticLayer +from superset_core.semantic_layers.types import ( + AggregationType, + Dimension, + Filter, + FilterValues, + Metric, + Operator, + OrderDirection, + PredicateType, + SemanticQuery, + SemanticRequest, + SemanticResult, +) +from superset_core.semantic_layers.view import SemanticView + +VIEW_NAME: str = "orders" + +CATEGORY: Dimension = Dimension( + "category", "category", pa.string(), verbose_name="Category" +) +REGION: Dimension = Dimension("region", "region", pa.string(), verbose_name="Region") +TOTAL_AMOUNT: Metric = Metric( + "total_amount", + "total_amount", + pa.int64(), + "SUM(amount)", + aggregation=AggregationType.SUM, + verbose_name="Total amount", +) +ORDER_COUNT: Metric = Metric( + "order_count", + "order_count", + pa.int64(), + "COUNT(*)", + aggregation=AggregationType.COUNT, + verbose_name="Order count", +) + +# Small enough to reason about in an assertion: Books totals 10 + 5 = 15. +ROWS: tuple[dict[str, Any], ...] = ( + {"category": "Books", "region": "East", "amount": 10}, + {"category": "Books", "region": "West", "amount": 5}, + {"category": "Games", "region": "East", "amount": 7}, + {"category": "Music", "region": "West", "amount": 3}, +) + +SUPPORTED_OPERATORS: frozenset[Operator] = frozenset( + {Operator.EQUALS, Operator.NOT_EQUALS, Operator.IN, Operator.NOT_IN} +) + + +class StubConfiguration(BaseModel): + """The stub needs no connection settings; reject anything supplied.""" + + model_config = ConfigDict(extra="forbid") + + +def _matches(row: dict[str, Any], filter_: Filter) -> bool: + if filter_.type != PredicateType.WHERE or not isinstance(filter_.column, Dimension): + raise ValueError("The E2E stub only supports WHERE filters on dimensions") + if filter_.operator not in SUPPORTED_OPERATORS: + raise ValueError(f"The E2E stub does not support {filter_.operator.value}") + + actual: Any = row[filter_.column.id] + values: frozenset[FilterValues] = ( + frozenset(filter_.value) + if isinstance(filter_.value, (frozenset, tuple)) + else frozenset({filter_.value}) + ) + if filter_.operator in {Operator.EQUALS, Operator.IN}: + return actual in values + return actual not in values + + +def _filtered_rows(filters: set[Filter] | None) -> list[dict[str, Any]]: + return [row for row in ROWS if all(_matches(row, f) for f in filters or set())] Review Comment: The stub can return a successful empty result for a query shape it is meant to reject. `all(...)` short-circuits, and `filters` is a set with no defined order. For a request with `region IN ['North']` plus an unsupported `category LIKE 'B%'`, if the `IN` filter is evaluated first it rejects every row and `_matches` never runs on the `LIKE` filter, so no error is raised. Whether it fails then depends on set iteration order. Should the filters be validated once up front, before the row scan, so an unsupported predicate always raises? -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
