codeant-ai-for-open-source[bot] commented on code in PR #41611: URL: https://github.com/apache/superset/pull/41611#discussion_r3533487844
########## tests/unit_tests/mcp_service/semantic_layer/tool/test_get_compatible_dimensions.py: ########## @@ -0,0 +1,232 @@ +# 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. + +"""Unit tests for the get_compatible_dimensions MCP tool.""" + +from __future__ import annotations + +import importlib +from collections.abc import Generator +from types import ModuleType +from unittest.mock import MagicMock, Mock, patch + +import pytest +from fastmcp import Client, FastMCP + +from superset.errors import ErrorLevel, SupersetError, SupersetErrorType +from superset.exceptions import SupersetSecurityException +from superset.mcp_service.app import mcp +from superset.utils import json + +get_compatible_dimensions_module: ModuleType = importlib.import_module( + "superset.mcp_service.semantic_layer.tool.get_compatible_dimensions" +) + + [email protected] +def mcp_server() -> FastMCP: + return mcp + + [email protected](autouse=True) +def mock_auth() -> Generator[MagicMock, None, None]: + with ( + patch("superset.mcp_service.auth.get_user_from_request") as mock_get_user, + patch.object( + get_compatible_dimensions_module, + "user_can_view_data_model_metadata", + return_value=True, + ), + ): + mock_user = Mock() + mock_user.id = 1 + mock_user.username = "admin" + mock_get_user.return_value = mock_user + yield mock_get_user + + +def _make_column(name: str, groupby: bool = True) -> MagicMock: + col = MagicMock() + col.column_name = name + col.verbose_name = None + col.description = None + col.type = "VARCHAR" + col.is_dttm = False + col.groupby = groupby + col.filterable = True + return col + + +def _make_dataset(dataset_id: int = 42) -> MagicMock: + ds = MagicMock() + ds.id = dataset_id + ds.table_name = f"table_{dataset_id}" + ds.metrics = [] + ds.columns = [ + _make_column("region"), + _make_column("category"), + _make_column("internal_only", groupby=False), + ] + return ds + + +def _make_view(view_id: int = 5) -> MagicMock: + view = MagicMock() Review Comment: **Suggestion:** Provide an explicit type annotation for this view mock variable to satisfy the rule requiring type hints on relevant variables. [custom_rule] **Severity Level:** Minor ⚠️ <details> <summary><b>Why it matters? 🤔 </b></summary> The variable is a locally assigned Python object in a new file and can be annotated. Since it lacks a type hint, the suggestion is a valid match for the rule. </details> [](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=e663bb1acb1d4853abb8c9ffcf6ac06a&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) [](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=e663bb1acb1d4853abb8c9ffcf6ac06a&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) *(Use Cmd/Ctrl + Click for best experience)* <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** tests/unit_tests/mcp_service/semantic_layer/tool/test_get_compatible_dimensions.py **Line:** 88:88 **Comment:** *Custom Rule: Provide an explicit type annotation for this view mock variable to satisfy the rule requiring type hints on relevant variables. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=f5de0e51c85158fa789f1b60463fd0a3f107dde4fb8f12017a53cb3b7ddfcb69&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=f5de0e51c85158fa789f1b60463fd0a3f107dde4fb8f12017a53cb3b7ddfcb69&reaction=dislike'>👎</a> ########## tests/unit_tests/mcp_service/semantic_layer/tool/test_get_compatible_dimensions.py: ########## @@ -0,0 +1,232 @@ +# 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. + +"""Unit tests for the get_compatible_dimensions MCP tool.""" + +from __future__ import annotations + +import importlib +from collections.abc import Generator +from types import ModuleType +from unittest.mock import MagicMock, Mock, patch + +import pytest +from fastmcp import Client, FastMCP + +from superset.errors import ErrorLevel, SupersetError, SupersetErrorType +from superset.exceptions import SupersetSecurityException +from superset.mcp_service.app import mcp +from superset.utils import json + +get_compatible_dimensions_module: ModuleType = importlib.import_module( + "superset.mcp_service.semantic_layer.tool.get_compatible_dimensions" +) + + [email protected] +def mcp_server() -> FastMCP: + return mcp + + [email protected](autouse=True) +def mock_auth() -> Generator[MagicMock, None, None]: + with ( + patch("superset.mcp_service.auth.get_user_from_request") as mock_get_user, + patch.object( + get_compatible_dimensions_module, + "user_can_view_data_model_metadata", + return_value=True, + ), + ): + mock_user = Mock() + mock_user.id = 1 + mock_user.username = "admin" + mock_get_user.return_value = mock_user + yield mock_get_user + + +def _make_column(name: str, groupby: bool = True) -> MagicMock: + col = MagicMock() Review Comment: **Suggestion:** Add an explicit type annotation to this local mock variable to comply with the type-hint requirement for relevant variables. [custom_rule] **Severity Level:** Minor ⚠️ <details> <summary><b>Why it matters? 🤔 </b></summary> The rule requires type hints for relevant variables that can be annotated. This local variable is created in a helper function and could be explicitly typed, so the suggestion correctly identifies a real omission. </details> [](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=2c113dc881b341f1991fd46c3d9a4daf&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) [](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=2c113dc881b341f1991fd46c3d9a4daf&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) *(Use Cmd/Ctrl + Click for best experience)* <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** tests/unit_tests/mcp_service/semantic_layer/tool/test_get_compatible_dimensions.py **Line:** 63:63 **Comment:** *Custom Rule: Add an explicit type annotation to this local mock variable to comply with the type-hint requirement for relevant variables. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=8ebf90ba1d4c7b46ce97a9ceac83be31ef1be2cdf0a5bea3388750c8e992f425&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=8ebf90ba1d4c7b46ce97a9ceac83be31ef1be2cdf0a5bea3388750c8e992f425&reaction=dislike'>👎</a> ########## tests/unit_tests/mcp_service/semantic_layer/tool/test_get_compatible_dimensions.py: ########## @@ -0,0 +1,232 @@ +# 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. + +"""Unit tests for the get_compatible_dimensions MCP tool.""" + +from __future__ import annotations + +import importlib +from collections.abc import Generator +from types import ModuleType +from unittest.mock import MagicMock, Mock, patch + +import pytest +from fastmcp import Client, FastMCP + +from superset.errors import ErrorLevel, SupersetError, SupersetErrorType +from superset.exceptions import SupersetSecurityException +from superset.mcp_service.app import mcp +from superset.utils import json + +get_compatible_dimensions_module: ModuleType = importlib.import_module( + "superset.mcp_service.semantic_layer.tool.get_compatible_dimensions" +) + + [email protected] +def mcp_server() -> FastMCP: + return mcp + + [email protected](autouse=True) +def mock_auth() -> Generator[MagicMock, None, None]: + with ( + patch("superset.mcp_service.auth.get_user_from_request") as mock_get_user, + patch.object( + get_compatible_dimensions_module, + "user_can_view_data_model_metadata", + return_value=True, + ), + ): + mock_user = Mock() + mock_user.id = 1 + mock_user.username = "admin" + mock_get_user.return_value = mock_user + yield mock_get_user + + +def _make_column(name: str, groupby: bool = True) -> MagicMock: + col = MagicMock() + col.column_name = name + col.verbose_name = None + col.description = None + col.type = "VARCHAR" + col.is_dttm = False + col.groupby = groupby + col.filterable = True + return col + + +def _make_dataset(dataset_id: int = 42) -> MagicMock: + ds = MagicMock() Review Comment: **Suggestion:** Add a concrete type annotation to this dataset mock variable so the helper function remains fully type-hinted. [custom_rule] **Severity Level:** Minor ⚠️ <details> <summary><b>Why it matters? 🤔 </b></summary> This is a new local variable in modified Python code that can be annotated but is not. That matches the type-hint rule for relevant variables. </details> [](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=119927b445ba47f39ab7a6834907320d&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) [](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=119927b445ba47f39ab7a6834907320d&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) *(Use Cmd/Ctrl + Click for best experience)* <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** tests/unit_tests/mcp_service/semantic_layer/tool/test_get_compatible_dimensions.py **Line:** 75:75 **Comment:** *Custom Rule: Add a concrete type annotation to this dataset mock variable so the helper function remains fully type-hinted. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=ccf086e5022b58393d71f7e798b967b78e8e448abeab7b21e21a8bac3f86cede&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=ccf086e5022b58393d71f7e798b967b78e8e448abeab7b21e21a8bac3f86cede&reaction=dislike'>👎</a> ########## tests/unit_tests/mcp_service/semantic_layer/tool/test_get_compatible_metrics.py: ########## @@ -0,0 +1,247 @@ +# 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. + +"""Unit tests for the get_compatible_metrics MCP tool.""" + +from __future__ import annotations + +import importlib +from collections.abc import Generator +from types import ModuleType +from unittest.mock import MagicMock, Mock, patch + +import pytest +from fastmcp import Client, FastMCP + +from superset.errors import ErrorLevel, SupersetError, SupersetErrorType +from superset.exceptions import SupersetSecurityException +from superset.mcp_service.app import mcp +from superset.utils import json + +get_compatible_metrics_module: ModuleType = importlib.import_module( + "superset.mcp_service.semantic_layer.tool.get_compatible_metrics" +) + + [email protected] +def mcp_server() -> FastMCP: + return mcp + + [email protected](autouse=True) +def mock_auth() -> Generator[MagicMock, None, None]: + with ( + patch("superset.mcp_service.auth.get_user_from_request") as mock_get_user, + patch.object( + get_compatible_metrics_module, + "user_can_view_data_model_metadata", + return_value=True, + ), + ): + mock_user = Mock() + mock_user.id = 1 + mock_user.username = "admin" + mock_get_user.return_value = mock_user + yield mock_get_user + + +def _make_metric(name: str, expression: str = "COUNT(*)") -> MagicMock: + m = MagicMock() Review Comment: **Suggestion:** Add a type annotation for this local metric mock variable so relevant variables are explicitly typed. [custom_rule] **Severity Level:** Minor ⚠️ <details> <summary><b>Why it matters? 🤔 </b></summary> This local helper variable is introduced without a type hint, and it is clearly annotatable. The custom rule requires type hints for relevant variables in new Python code, so this is a real violation. </details> [](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=2f5b7efa08f94e5c8f8aa0fc0755b5c8&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) [](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=2f5b7efa08f94e5c8f8aa0fc0755b5c8&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) *(Use Cmd/Ctrl + Click for best experience)* <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** tests/unit_tests/mcp_service/semantic_layer/tool/test_get_compatible_metrics.py **Line:** 63:63 **Comment:** *Custom Rule: Add a type annotation for this local metric mock variable so relevant variables are explicitly typed. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=8ba5b6364f98a83da800b5057ffe870a3f5dc7d0ac6c822b44e0cc5dad8bb2be&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=8ba5b6364f98a83da800b5057ffe870a3f5dc7d0ac6c822b44e0cc5dad8bb2be&reaction=dislike'>👎</a> ########## tests/unit_tests/mcp_service/semantic_layer/tool/test_get_compatible_metrics.py: ########## @@ -0,0 +1,247 @@ +# 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. + +"""Unit tests for the get_compatible_metrics MCP tool.""" + +from __future__ import annotations + +import importlib +from collections.abc import Generator +from types import ModuleType +from unittest.mock import MagicMock, Mock, patch + +import pytest +from fastmcp import Client, FastMCP + +from superset.errors import ErrorLevel, SupersetError, SupersetErrorType +from superset.exceptions import SupersetSecurityException +from superset.mcp_service.app import mcp +from superset.utils import json + +get_compatible_metrics_module: ModuleType = importlib.import_module( + "superset.mcp_service.semantic_layer.tool.get_compatible_metrics" +) + + [email protected] +def mcp_server() -> FastMCP: + return mcp + + [email protected](autouse=True) +def mock_auth() -> Generator[MagicMock, None, None]: + with ( + patch("superset.mcp_service.auth.get_user_from_request") as mock_get_user, + patch.object( + get_compatible_metrics_module, + "user_can_view_data_model_metadata", + return_value=True, + ), + ): + mock_user = Mock() Review Comment: **Suggestion:** Add an explicit type annotation for this mock user variable to satisfy the requirement for annotating relevant variables. [custom_rule] **Severity Level:** Minor ⚠️ <details> <summary><b>Why it matters? 🤔 </b></summary> This is a new Python variable assignment without a type annotation in a file that adds new code. Since the variable is a meaningful local object and can be annotated, it matches the type-hint rule violation. </details> [](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=f825630673a746cd866730e5ab6c25d5&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) [](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=f825630673a746cd866730e5ab6c25d5&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) *(Use Cmd/Ctrl + Click for best experience)* <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** tests/unit_tests/mcp_service/semantic_layer/tool/test_get_compatible_metrics.py **Line:** 55:55 **Comment:** *Custom Rule: Add an explicit type annotation for this mock user variable to satisfy the requirement for annotating relevant variables. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=95d64e12b3c045fc414018edf5731bbf3f3ee6455ff04956b77f2834c5af5171&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=95d64e12b3c045fc414018edf5731bbf3f3ee6455ff04956b77f2834c5af5171&reaction=dislike'>👎</a> ########## tests/unit_tests/mcp_service/semantic_layer/tool/test_get_compatible_metrics.py: ########## @@ -0,0 +1,247 @@ +# 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. + +"""Unit tests for the get_compatible_metrics MCP tool.""" + +from __future__ import annotations + +import importlib +from collections.abc import Generator +from types import ModuleType +from unittest.mock import MagicMock, Mock, patch + +import pytest +from fastmcp import Client, FastMCP + +from superset.errors import ErrorLevel, SupersetError, SupersetErrorType +from superset.exceptions import SupersetSecurityException +from superset.mcp_service.app import mcp +from superset.utils import json + +get_compatible_metrics_module: ModuleType = importlib.import_module( + "superset.mcp_service.semantic_layer.tool.get_compatible_metrics" +) + + [email protected] +def mcp_server() -> FastMCP: + return mcp + + [email protected](autouse=True) +def mock_auth() -> Generator[MagicMock, None, None]: + with ( + patch("superset.mcp_service.auth.get_user_from_request") as mock_get_user, + patch.object( + get_compatible_metrics_module, + "user_can_view_data_model_metadata", + return_value=True, + ), + ): + mock_user = Mock() + mock_user.id = 1 + mock_user.username = "admin" + mock_get_user.return_value = mock_user + yield mock_get_user + + +def _make_metric(name: str, expression: str = "COUNT(*)") -> MagicMock: + m = MagicMock() + m.metric_name = name + m.verbose_name = None + m.expression = expression + m.description = None + m.d3format = None + m.warning_text = None + return m + + +def _make_dataset(dataset_id: int = 42) -> MagicMock: + ds = MagicMock() + ds.id = dataset_id + ds.table_name = f"table_{dataset_id}" + ds.columns = [] + ds.metrics = [_make_metric("count"), _make_metric("revenue", "SUM(revenue)")] + return ds + + +def _make_view(view_id: int = 5) -> MagicMock: + view = MagicMock() Review Comment: **Suggestion:** Annotate this local view mock variable with its type to align with mandatory type hints on relevant variables. [custom_rule] **Severity Level:** Minor ⚠️ <details> <summary><b>Why it matters? 🤔 </b></summary> This variable is a new local Python assignment without an explicit type hint. It is a relevant annotatable variable in new code, so it falls under the type-hint requirement. </details> [](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=4299adc283cb4eacbb5fa7af3c587dec&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) [](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=4299adc283cb4eacbb5fa7af3c587dec&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) *(Use Cmd/Ctrl + Click for best experience)* <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** tests/unit_tests/mcp_service/semantic_layer/tool/test_get_compatible_metrics.py **Line:** 83:83 **Comment:** *Custom Rule: Annotate this local view mock variable with its type to align with mandatory type hints on relevant variables. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=c4ba83257cda0f57a7fda8869ff98c75b07f973f4e1b73d621df7135dceae246&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=c4ba83257cda0f57a7fda8869ff98c75b07f973f4e1b73d621df7135dceae246&reaction=dislike'>👎</a> ########## tests/unit_tests/mcp_service/semantic_layer/tool/test_list_metrics.py: ########## @@ -0,0 +1,305 @@ +# 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. + +"""Unit tests for the list_metrics MCP tool.""" + +from __future__ import annotations + +import importlib +from collections.abc import Generator +from types import ModuleType +from unittest.mock import call, MagicMock, Mock, patch + +import pytest +from fastmcp import Client, FastMCP + +from superset.errors import ErrorLevel, SupersetError, SupersetErrorType +from superset.exceptions import SupersetSecurityException +from superset.mcp_service.app import mcp +from superset.utils import json + +list_metrics_module: ModuleType = importlib.import_module( + "superset.mcp_service.semantic_layer.tool.list_metrics" +) + + [email protected] +def mcp_server() -> FastMCP: + return mcp + + [email protected](autouse=True) +def mock_auth() -> Generator[MagicMock, None, None]: + with ( + patch("superset.mcp_service.auth.get_user_from_request") as mock_get_user, + patch.object( + list_metrics_module, + "user_can_view_data_model_metadata", + return_value=True, + ), + ): + mock_user = Mock() Review Comment: **Suggestion:** Add an explicit type annotation to this fixture-local mock variable to comply with the type-hint requirement for relevant variables. [custom_rule] **Severity Level:** Minor ⚠️ <details> <summary><b>Why it matters? 🤔 </b></summary> This new Python local variable is created without a type annotation, and it is a relevant mock object that can be annotated, so it matches the type-hint rule. </details> [](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=53fd80a8f2434ec5a2a540c5064bfe57&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) [](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=53fd80a8f2434ec5a2a540c5064bfe57&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) *(Use Cmd/Ctrl + Click for best experience)* <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** tests/unit_tests/mcp_service/semantic_layer/tool/test_list_metrics.py **Line:** 55:55 **Comment:** *Custom Rule: Add an explicit type annotation to this fixture-local mock variable to comply with the type-hint requirement for relevant variables. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=d147e567848384a964f4b69f578c003adc338f3d358535aec481bf84f28c38e3&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=d147e567848384a964f4b69f578c003adc338f3d358535aec481bf84f28c38e3&reaction=dislike'>👎</a> ########## tests/unit_tests/mcp_service/semantic_layer/tool/test_list_metrics.py: ########## @@ -0,0 +1,305 @@ +# 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. + +"""Unit tests for the list_metrics MCP tool.""" + +from __future__ import annotations + +import importlib +from collections.abc import Generator +from types import ModuleType +from unittest.mock import call, MagicMock, Mock, patch + +import pytest +from fastmcp import Client, FastMCP + +from superset.errors import ErrorLevel, SupersetError, SupersetErrorType +from superset.exceptions import SupersetSecurityException +from superset.mcp_service.app import mcp +from superset.utils import json + +list_metrics_module: ModuleType = importlib.import_module( + "superset.mcp_service.semantic_layer.tool.list_metrics" +) + + [email protected] +def mcp_server() -> FastMCP: + return mcp + + [email protected](autouse=True) +def mock_auth() -> Generator[MagicMock, None, None]: + with ( + patch("superset.mcp_service.auth.get_user_from_request") as mock_get_user, + patch.object( + list_metrics_module, + "user_can_view_data_model_metadata", + return_value=True, + ), + ): + mock_user = Mock() + mock_user.id = 1 + mock_user.username = "admin" + mock_get_user.return_value = mock_user + yield mock_get_user + + +def _make_metric(name: str, expression: str = "COUNT(*)") -> MagicMock: + m = MagicMock() + m.metric_name = name + m.verbose_name = None + m.expression = expression + m.description = None + m.d3format = None + m.warning_text = None + return m + + +def _make_column(name: str) -> MagicMock: + col = MagicMock() + col.column_name = name + col.verbose_name = None + col.description = None + col.type = "VARCHAR" + col.is_dttm = False + col.groupby = True + col.filterable = True + return col + + +def _make_dataset(dataset_id: int = 1) -> MagicMock: + ds = MagicMock() + ds.id = dataset_id + ds.table_name = f"table_{dataset_id}" + ds.metrics = [_make_metric("count"), _make_metric("revenue", "SUM(revenue)")] + ds.columns = [_make_column("region"), _make_column("category")] + return ds + + +def _make_view(view_id: int = 5) -> MagicMock: + view = MagicMock() + view.id = view_id + view.name = f"view_{view_id}" + view.raise_for_access = MagicMock(return_value=None) + view.metrics = [_make_metric("bookings"), _make_metric("revenue", "SUM(revenue)")] + view.columns = [_make_column("listing__country_name"), _make_column("channel")] + view.get_compatible_dimensions = MagicMock(return_value=["listing__country_name"]) + return view + + +def _access_denied_exc(message: str = "Access denied") -> SupersetSecurityException: + return SupersetSecurityException( + SupersetError( + message=message, + error_type=SupersetErrorType.DATASOURCE_SECURITY_ACCESS_ERROR, + level=ErrorLevel.ERROR, + ) + ) + + [email protected] +async def test_list_metrics_builtin_happy_path(mcp_server: FastMCP) -> None: + """list_metrics returns builtin metrics when only datasets exist.""" + mock_ds = _make_dataset(42) + + with ( + patch( + "superset.mcp_service.semantic_layer.tool.list_metrics.DatasetDAO" + ) as mock_dao, + patch( + "superset.mcp_service.semantic_layer.tool.list_metrics.SemanticViewDAO" + ) as mock_view_dao, + ): + mock_dao.find_by_id.return_value = mock_ds + mock_view_dao.find_accessible.return_value = [] + + async with Client(mcp_server) as client: + result = await client.call_tool( + "list_metrics", + {"request": {"dataset_id": 42, "include_compatible_dimensions": False}}, + ) + data = json.loads(result.content[0].text) + + assert data["success"] is True + assert data["total_count"] == 2 + metrics = data["metrics"] + assert {m["name"] for m in metrics} == {"count", "revenue"} + assert all(m["source"] == "builtin" for m in metrics) + assert all(m["dataset_id"] == 42 for m in metrics) + + [email protected] +async def test_list_metrics_mutual_exclusion_validation(mcp_server: FastMCP) -> None: + """list_metrics returns a validation error when dataset_id and view_id coexist.""" + async with Client(mcp_server) as client: + result = await client.call_tool( + "list_metrics", + {"request": {"dataset_id": 1, "view_id": 2}}, + ) + data = json.loads(result.content[0].text) + + assert data["success"] is False + assert data["error_type"] == "ValidationError" + + [email protected] +async def test_list_metrics_privacy_check(mcp_server: FastMCP) -> None: + """list_metrics returns an error when the user lacks data-model metadata access.""" + with patch.object( + list_metrics_module, + "user_can_view_data_model_metadata", + return_value=False, + ): + async with Client(mcp_server) as client: + result = await client.call_tool("list_metrics", {}) + data = json.loads(result.content[0].text) + + assert data["success"] is False + assert data["error_type"] == "DataModelMetadataRestricted" + + [email protected] +async def test_list_metrics_search_filter(mcp_server: FastMCP) -> None: + """list_metrics filters metrics by search term.""" + mock_ds = _make_dataset(1) Review Comment: **Suggestion:** Add an explicit type annotation for this dataset mock variable to satisfy the rule requiring type hints on relevant variables. [custom_rule] **Severity Level:** Minor ⚠️ <details> <summary><b>Why it matters? 🤔 </b></summary> The local dataset mock variable is introduced without a type annotation in new Python code, so this is a real omission covered by the type-hint rule. </details> [](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=b0d35f78cedc41a88ee62c0187b6cfa3&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) [](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=b0d35f78cedc41a88ee62c0187b6cfa3&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) *(Use Cmd/Ctrl + Click for best experience)* <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** tests/unit_tests/mcp_service/semantic_layer/tool/test_list_metrics.py **Line:** 179:179 **Comment:** *Custom Rule: Add an explicit type annotation for this dataset mock variable to satisfy the rule requiring type hints on relevant variables. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=bd25455714ba57a35845bd9f9345e128c600eea1fb41eac9ce4c83f32cb5e445&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=bd25455714ba57a35845bd9f9345e128c600eea1fb41eac9ce4c83f32cb5e445&reaction=dislike'>👎</a> ########## tests/unit_tests/mcp_service/semantic_layer/tool/test_list_metrics.py: ########## @@ -0,0 +1,305 @@ +# 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. + +"""Unit tests for the list_metrics MCP tool.""" + +from __future__ import annotations + +import importlib +from collections.abc import Generator +from types import ModuleType +from unittest.mock import call, MagicMock, Mock, patch + +import pytest +from fastmcp import Client, FastMCP + +from superset.errors import ErrorLevel, SupersetError, SupersetErrorType +from superset.exceptions import SupersetSecurityException +from superset.mcp_service.app import mcp +from superset.utils import json + +list_metrics_module: ModuleType = importlib.import_module( + "superset.mcp_service.semantic_layer.tool.list_metrics" +) + + [email protected] +def mcp_server() -> FastMCP: + return mcp + + [email protected](autouse=True) +def mock_auth() -> Generator[MagicMock, None, None]: + with ( + patch("superset.mcp_service.auth.get_user_from_request") as mock_get_user, + patch.object( + list_metrics_module, + "user_can_view_data_model_metadata", + return_value=True, + ), + ): + mock_user = Mock() + mock_user.id = 1 + mock_user.username = "admin" + mock_get_user.return_value = mock_user + yield mock_get_user + + +def _make_metric(name: str, expression: str = "COUNT(*)") -> MagicMock: + m = MagicMock() + m.metric_name = name + m.verbose_name = None + m.expression = expression + m.description = None + m.d3format = None + m.warning_text = None + return m + + +def _make_column(name: str) -> MagicMock: + col = MagicMock() + col.column_name = name + col.verbose_name = None + col.description = None + col.type = "VARCHAR" + col.is_dttm = False + col.groupby = True + col.filterable = True + return col + + +def _make_dataset(dataset_id: int = 1) -> MagicMock: + ds = MagicMock() + ds.id = dataset_id + ds.table_name = f"table_{dataset_id}" + ds.metrics = [_make_metric("count"), _make_metric("revenue", "SUM(revenue)")] + ds.columns = [_make_column("region"), _make_column("category")] + return ds + + +def _make_view(view_id: int = 5) -> MagicMock: + view = MagicMock() + view.id = view_id + view.name = f"view_{view_id}" + view.raise_for_access = MagicMock(return_value=None) + view.metrics = [_make_metric("bookings"), _make_metric("revenue", "SUM(revenue)")] + view.columns = [_make_column("listing__country_name"), _make_column("channel")] + view.get_compatible_dimensions = MagicMock(return_value=["listing__country_name"]) + return view + + +def _access_denied_exc(message: str = "Access denied") -> SupersetSecurityException: + return SupersetSecurityException( + SupersetError( + message=message, + error_type=SupersetErrorType.DATASOURCE_SECURITY_ACCESS_ERROR, + level=ErrorLevel.ERROR, + ) + ) + + [email protected] +async def test_list_metrics_builtin_happy_path(mcp_server: FastMCP) -> None: + """list_metrics returns builtin metrics when only datasets exist.""" + mock_ds = _make_dataset(42) + + with ( + patch( + "superset.mcp_service.semantic_layer.tool.list_metrics.DatasetDAO" + ) as mock_dao, + patch( + "superset.mcp_service.semantic_layer.tool.list_metrics.SemanticViewDAO" + ) as mock_view_dao, + ): + mock_dao.find_by_id.return_value = mock_ds + mock_view_dao.find_accessible.return_value = [] + + async with Client(mcp_server) as client: + result = await client.call_tool( + "list_metrics", + {"request": {"dataset_id": 42, "include_compatible_dimensions": False}}, + ) + data = json.loads(result.content[0].text) + + assert data["success"] is True + assert data["total_count"] == 2 + metrics = data["metrics"] + assert {m["name"] for m in metrics} == {"count", "revenue"} + assert all(m["source"] == "builtin" for m in metrics) + assert all(m["dataset_id"] == 42 for m in metrics) + + [email protected] +async def test_list_metrics_mutual_exclusion_validation(mcp_server: FastMCP) -> None: + """list_metrics returns a validation error when dataset_id and view_id coexist.""" + async with Client(mcp_server) as client: + result = await client.call_tool( + "list_metrics", + {"request": {"dataset_id": 1, "view_id": 2}}, + ) + data = json.loads(result.content[0].text) + + assert data["success"] is False + assert data["error_type"] == "ValidationError" + + [email protected] +async def test_list_metrics_privacy_check(mcp_server: FastMCP) -> None: + """list_metrics returns an error when the user lacks data-model metadata access.""" + with patch.object( + list_metrics_module, + "user_can_view_data_model_metadata", + return_value=False, + ): + async with Client(mcp_server) as client: + result = await client.call_tool("list_metrics", {}) + data = json.loads(result.content[0].text) + + assert data["success"] is False + assert data["error_type"] == "DataModelMetadataRestricted" + + [email protected] +async def test_list_metrics_search_filter(mcp_server: FastMCP) -> None: + """list_metrics filters metrics by search term.""" + mock_ds = _make_dataset(1) + + with ( + patch( + "superset.mcp_service.semantic_layer.tool.list_metrics.DatasetDAO" + ) as mock_dao, + patch( + "superset.mcp_service.semantic_layer.tool.list_metrics.SemanticViewDAO" + ) as mock_view_dao, + patch("superset.mcp_service.semantic_layer.tool.list_metrics.db") as mock_db, + ): + mock_view_dao.find_accessible.return_value = [] + mock_query = MagicMock() Review Comment: **Suggestion:** Add a concrete type annotation to this query mock variable so the new test code does not leave relevant variables untyped. [custom_rule] **Severity Level:** Minor ⚠️ <details> <summary><b>Why it matters? 🤔 </b></summary> This variable is introduced in new Python code without any type annotation, and it is a relevant intermediate object that can be annotated under the rule. </details> [](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=727fde7be9334349940ec3ecf9557493&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) [](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=727fde7be9334349940ec3ecf9557493&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) *(Use Cmd/Ctrl + Click for best experience)* <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** tests/unit_tests/mcp_service/semantic_layer/tool/test_list_metrics.py **Line:** 191:191 **Comment:** *Custom Rule: Add a concrete type annotation to this query mock variable so the new test code does not leave relevant variables untyped. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=f8bee4fe80c538857e0e6abcb086a9da247987990e581f0e5ce3331733edf407&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=f8bee4fe80c538857e0e6abcb086a9da247987990e581f0e5ce3331733edf407&reaction=dislike'>👎</a> ########## tests/unit_tests/mcp_service/semantic_layer/tool/test_get_compatible_metrics.py: ########## @@ -0,0 +1,247 @@ +# 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. + +"""Unit tests for the get_compatible_metrics MCP tool.""" + +from __future__ import annotations + +import importlib +from collections.abc import Generator +from types import ModuleType +from unittest.mock import MagicMock, Mock, patch + +import pytest +from fastmcp import Client, FastMCP + +from superset.errors import ErrorLevel, SupersetError, SupersetErrorType +from superset.exceptions import SupersetSecurityException +from superset.mcp_service.app import mcp +from superset.utils import json + +get_compatible_metrics_module: ModuleType = importlib.import_module( + "superset.mcp_service.semantic_layer.tool.get_compatible_metrics" +) + + [email protected] +def mcp_server() -> FastMCP: + return mcp + + [email protected](autouse=True) +def mock_auth() -> Generator[MagicMock, None, None]: + with ( + patch("superset.mcp_service.auth.get_user_from_request") as mock_get_user, + patch.object( + get_compatible_metrics_module, + "user_can_view_data_model_metadata", + return_value=True, + ), + ): + mock_user = Mock() + mock_user.id = 1 + mock_user.username = "admin" + mock_get_user.return_value = mock_user + yield mock_get_user + + +def _make_metric(name: str, expression: str = "COUNT(*)") -> MagicMock: + m = MagicMock() + m.metric_name = name + m.verbose_name = None + m.expression = expression + m.description = None + m.d3format = None + m.warning_text = None + return m + + +def _make_dataset(dataset_id: int = 42) -> MagicMock: + ds = MagicMock() Review Comment: **Suggestion:** Add an explicit type annotation for this dataset mock variable to comply with the type-hint rule for relevant variables. [custom_rule] **Severity Level:** Minor ⚠️ <details> <summary><b>Why it matters? 🤔 </b></summary> This is a new unannotated local variable in Python code. Given the rule to annotate relevant variables when possible, the missing type hint is a genuine violation. </details> [](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=4450d44ec4b44f118a1dffbd17162d87&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) [](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=4450d44ec4b44f118a1dffbd17162d87&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) *(Use Cmd/Ctrl + Click for best experience)* <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** tests/unit_tests/mcp_service/semantic_layer/tool/test_get_compatible_metrics.py **Line:** 74:74 **Comment:** *Custom Rule: Add an explicit type annotation for this dataset mock variable to comply with the type-hint rule for relevant variables. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=b233b2440bfe0feb46cf6ad5612b097fb6e274b012869975de45e7bea16a15e1&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=b233b2440bfe0feb46cf6ad5612b097fb6e274b012869975de45e7bea16a15e1&reaction=dislike'>👎</a> ########## superset/mcp_service/semantic_layer/tool/get_compatible_dimensions.py: ########## @@ -0,0 +1,230 @@ +# 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. + +"""MCP tool: get_compatible_dimensions + +Returns dimensions compatible with the current metric/dimension selection. +""" + +import logging + +from fastmcp import Context +from superset_core.mcp.decorators import tool, ToolAnnotations + +from superset.extensions import event_logger +from superset.mcp_service.privacy import ( + DATA_MODEL_METADATA_ERROR_TYPE, + requires_data_model_metadata_access, + user_can_view_data_model_metadata, +) +from superset.mcp_service.semantic_layer.schemas import ( + CompatibleDimensionsResponse, + DimensionInfo, + GetCompatibleDimensionsRequest, + SemanticLayerError, +) + +logger = logging.getLogger(__name__) + + +@tool( + tags=["data", "semantic"], + class_permission_name="Dataset", + annotations=ToolAnnotations( + title="Get compatible dimensions", + readOnlyHint=True, + destructiveHint=False, + ), +) +@requires_data_model_metadata_access +async def get_compatible_dimensions( + request: GetCompatibleDimensionsRequest, + ctx: Context, +) -> CompatibleDimensionsResponse | SemanticLayerError: + """Return dimensions compatible with the current metric/dimension selection. + + Used to drive progressive disclosure in query builders: after the user + selects one or more metrics (and optionally some dimensions), this tool + returns the dimensions that can validly be added without breaking the + underlying query. + + Provide exactly one of ``dataset_id`` (built-in) or ``view_id`` (external). + + For built-in datasets, returns all groupby-enabled columns from the dataset. + SQL datasets have no semantic compatibility constraints between metrics and + dimensions, so all groupby columns are always returned regardless of the + selected metrics. + + For external semantic views, delegates to the view's + ``get_compatible_dimensions`` implementation. + + Example: + ```json + { + "selected_metrics": ["revenue"], + "selected_dimensions": [], + "view_id": 5 + } + ``` + """ + await ctx.info( + "Getting compatible dimensions: dataset_id=%s, view_id=%s, " + "metrics=%s, dims=%s" + % ( + request.dataset_id, + request.view_id, + request.selected_metrics, + request.selected_dimensions, + ) + ) + + if not user_can_view_data_model_metadata(): + return SemanticLayerError.create( + error="You don't have permission to access dataset details for your role.", + error_type=DATA_MODEL_METADATA_ERROR_TYPE, + ) + + if request.dataset_id is None and request.view_id is None: + return SemanticLayerError.create( + error="Provide either dataset_id (built-in) or view_id (external).", + error_type="ValidationError", + ) + if request.dataset_id is not None and request.view_id is not None: + return SemanticLayerError.create( + error="Provide only one of dataset_id or view_id, not both.", + error_type="ValidationError", + ) + + try: + # ------------------------------------------------------------------ + # Built-in dataset path + # ------------------------------------------------------------------ + if request.dataset_id is not None: + from sqlalchemy.orm import subqueryload + + from superset.connectors.sqla.models import SqlaTable + from superset.daos.dataset import DatasetDAO + + dataset_id: int = request.dataset_id + with event_logger.log_context( + action="mcp.get_compatible_dimensions.builtin" + ): + dataset = DatasetDAO.find_by_id( + dataset_id, + query_options=[ + subqueryload(SqlaTable.columns), + subqueryload(SqlaTable.metrics), + ], + ) Review Comment: **Suggestion:** Add an explicit type annotation to this DAO result variable (including its optionality) to satisfy the requirement for annotating relevant local variables. [custom_rule] **Severity Level:** Minor ⚠️ <details> <summary><b>Why it matters? 🤔 </b></summary> The code assigns the DAO result to a local variable without any type annotation, and this local value can reasonably be annotated as required by the Python type-hints rule. </details> [](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=66f20852b7cf42848053ad58d22db89a&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) [](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=66f20852b7cf42848053ad58d22db89a&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) *(Use Cmd/Ctrl + Click for best experience)* <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** superset/mcp_service/semantic_layer/tool/get_compatible_dimensions.py **Line:** 126:132 **Comment:** *Custom Rule: Add an explicit type annotation to this DAO result variable (including its optionality) to satisfy the requirement for annotating relevant local variables. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=e68864c35e30627dd5c551c500162b58a29e3747071977ba4a31d470a10a16ec&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=e68864c35e30627dd5c551c500162b58a29e3747071977ba4a31d470a10a16ec&reaction=dislike'>👎</a> ########## superset/daos/semantic_layer.py: ########## @@ -201,6 +201,40 @@ def validate_update_uniqueness( for c in candidates ) + @classmethod + def find_accessible(cls) -> list[SemanticView]: Review Comment: **Suggestion:** Add an explicit type annotation for the classmethod receiver to fully satisfy the type-hint requirement for method parameters. [custom_rule] **Severity Level:** Minor ⚠️ <details> <summary><b>Why it matters? 🤔 </b></summary> The new classmethod omits a type hint for the `cls` receiver, which is a parameter that can be annotated. This matches the Python type-hint requirement rule for newly added code. </details> [](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=32ec01b4c3ee4162a432c525de94f531&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) [](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=32ec01b4c3ee4162a432c525de94f531&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) *(Use Cmd/Ctrl + Click for best experience)* <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** superset/daos/semantic_layer.py **Line:** 204:205 **Comment:** *Custom Rule: Add an explicit type annotation for the classmethod receiver to fully satisfy the type-hint requirement for method parameters. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=c255bb99924e301bb2a4b982254c085fc4189aa2b76a30f701ed136641339d4a&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=c255bb99924e301bb2a4b982254c085fc4189aa2b76a30f701ed136641339d4a&reaction=dislike'>👎</a> ########## superset/mcp_service/semantic_layer/tool/get_compatible_dimensions.py: ########## @@ -0,0 +1,230 @@ +# 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. + +"""MCP tool: get_compatible_dimensions + +Returns dimensions compatible with the current metric/dimension selection. +""" + +import logging + +from fastmcp import Context +from superset_core.mcp.decorators import tool, ToolAnnotations + +from superset.extensions import event_logger +from superset.mcp_service.privacy import ( + DATA_MODEL_METADATA_ERROR_TYPE, + requires_data_model_metadata_access, + user_can_view_data_model_metadata, +) +from superset.mcp_service.semantic_layer.schemas import ( + CompatibleDimensionsResponse, + DimensionInfo, + GetCompatibleDimensionsRequest, + SemanticLayerError, +) + +logger = logging.getLogger(__name__) + + +@tool( + tags=["data", "semantic"], + class_permission_name="Dataset", + annotations=ToolAnnotations( + title="Get compatible dimensions", + readOnlyHint=True, + destructiveHint=False, + ), +) +@requires_data_model_metadata_access +async def get_compatible_dimensions( + request: GetCompatibleDimensionsRequest, + ctx: Context, +) -> CompatibleDimensionsResponse | SemanticLayerError: + """Return dimensions compatible with the current metric/dimension selection. + + Used to drive progressive disclosure in query builders: after the user + selects one or more metrics (and optionally some dimensions), this tool + returns the dimensions that can validly be added without breaking the + underlying query. + + Provide exactly one of ``dataset_id`` (built-in) or ``view_id`` (external). + + For built-in datasets, returns all groupby-enabled columns from the dataset. + SQL datasets have no semantic compatibility constraints between metrics and + dimensions, so all groupby columns are always returned regardless of the + selected metrics. + + For external semantic views, delegates to the view's + ``get_compatible_dimensions`` implementation. + + Example: + ```json + { + "selected_metrics": ["revenue"], + "selected_dimensions": [], + "view_id": 5 + } + ``` + """ + await ctx.info( + "Getting compatible dimensions: dataset_id=%s, view_id=%s, " + "metrics=%s, dims=%s" + % ( + request.dataset_id, + request.view_id, + request.selected_metrics, + request.selected_dimensions, + ) + ) + + if not user_can_view_data_model_metadata(): + return SemanticLayerError.create( + error="You don't have permission to access dataset details for your role.", + error_type=DATA_MODEL_METADATA_ERROR_TYPE, + ) + + if request.dataset_id is None and request.view_id is None: + return SemanticLayerError.create( + error="Provide either dataset_id (built-in) or view_id (external).", + error_type="ValidationError", + ) + if request.dataset_id is not None and request.view_id is not None: + return SemanticLayerError.create( + error="Provide only one of dataset_id or view_id, not both.", + error_type="ValidationError", + ) + + try: + # ------------------------------------------------------------------ + # Built-in dataset path + # ------------------------------------------------------------------ + if request.dataset_id is not None: + from sqlalchemy.orm import subqueryload + + from superset.connectors.sqla.models import SqlaTable + from superset.daos.dataset import DatasetDAO + + dataset_id: int = request.dataset_id + with event_logger.log_context( + action="mcp.get_compatible_dimensions.builtin" + ): + dataset = DatasetDAO.find_by_id( + dataset_id, + query_options=[ + subqueryload(SqlaTable.columns), + subqueryload(SqlaTable.metrics), + ], + ) + + if dataset is None: + return SemanticLayerError.create( + error=f"No dataset found with id: {request.dataset_id}.", + error_type="NotFound", + ) + + # For built-in datasets all groupby columns are always compatible; + # there's no per-metric compatibility constraint at the SQL level. + dims: list[DimensionInfo] = [ + DimensionInfo( + name=col.column_name, + verbose_name=col.verbose_name or None, + description=col.description or None, + type=col.type or None, + is_dttm=bool(col.is_dttm), + groupby=bool(col.groupby), + filterable=bool(col.filterable), + source="builtin", + ) + for col in dataset.columns + if col.groupby + ] + + await ctx.info("Compatible dimensions (builtin): count=%d" % len(dims)) + return CompatibleDimensionsResponse( + compatible_dimensions=dims, + source="builtin", + ) + + # ------------------------------------------------------------------ + # External semantic view path + # ------------------------------------------------------------------ + from superset.daos.semantic_layer import SemanticViewDAO + from superset.exceptions import SupersetSecurityException + from superset.semantic_layers.models import ColumnMetadata + + view_id: int = request.view_id # type: ignore[assignment] + with event_logger.log_context(action="mcp.get_compatible_dimensions.external"): + view = SemanticViewDAO.find_by_id(view_id) Review Comment: **Suggestion:** Add a type annotation to the semantic view lookup variable so this relevant local value is explicitly typed. [custom_rule] **Severity Level:** Minor ⚠️ <details> <summary><b>Why it matters? 🤔 </b></summary> The semantic view lookup result is stored in a local variable without a type annotation, and the rule requires annotating relevant variables that can be typed. </details> [](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=ed80cb75f10742db931f32125c02da0c&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) [](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=ed80cb75f10742db931f32125c02da0c&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) *(Use Cmd/Ctrl + Click for best experience)* <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** superset/mcp_service/semantic_layer/tool/get_compatible_dimensions.py **Line:** 171:172 **Comment:** *Custom Rule: Add a type annotation to the semantic view lookup variable so this relevant local value is explicitly typed. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=d835c5ee43bfb943e628d7c12cc52b8270ef02e5a32e05f3579a68b931532831&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=d835c5ee43bfb943e628d7c12cc52b8270ef02e5a32e05f3579a68b931532831&reaction=dislike'>👎</a> ########## superset/mcp_service/semantic_layer/tool/get_compatible_dimensions.py: ########## @@ -0,0 +1,230 @@ +# 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. + +"""MCP tool: get_compatible_dimensions + +Returns dimensions compatible with the current metric/dimension selection. +""" + +import logging + +from fastmcp import Context +from superset_core.mcp.decorators import tool, ToolAnnotations + +from superset.extensions import event_logger +from superset.mcp_service.privacy import ( + DATA_MODEL_METADATA_ERROR_TYPE, + requires_data_model_metadata_access, + user_can_view_data_model_metadata, +) +from superset.mcp_service.semantic_layer.schemas import ( + CompatibleDimensionsResponse, + DimensionInfo, + GetCompatibleDimensionsRequest, + SemanticLayerError, +) + +logger = logging.getLogger(__name__) + + +@tool( + tags=["data", "semantic"], + class_permission_name="Dataset", + annotations=ToolAnnotations( + title="Get compatible dimensions", + readOnlyHint=True, + destructiveHint=False, + ), +) +@requires_data_model_metadata_access +async def get_compatible_dimensions( + request: GetCompatibleDimensionsRequest, + ctx: Context, +) -> CompatibleDimensionsResponse | SemanticLayerError: + """Return dimensions compatible with the current metric/dimension selection. + + Used to drive progressive disclosure in query builders: after the user + selects one or more metrics (and optionally some dimensions), this tool + returns the dimensions that can validly be added without breaking the + underlying query. + + Provide exactly one of ``dataset_id`` (built-in) or ``view_id`` (external). + + For built-in datasets, returns all groupby-enabled columns from the dataset. + SQL datasets have no semantic compatibility constraints between metrics and + dimensions, so all groupby columns are always returned regardless of the + selected metrics. + + For external semantic views, delegates to the view's + ``get_compatible_dimensions`` implementation. + + Example: + ```json + { + "selected_metrics": ["revenue"], + "selected_dimensions": [], + "view_id": 5 + } + ``` + """ + await ctx.info( + "Getting compatible dimensions: dataset_id=%s, view_id=%s, " + "metrics=%s, dims=%s" + % ( + request.dataset_id, + request.view_id, + request.selected_metrics, + request.selected_dimensions, + ) + ) + + if not user_can_view_data_model_metadata(): + return SemanticLayerError.create( + error="You don't have permission to access dataset details for your role.", + error_type=DATA_MODEL_METADATA_ERROR_TYPE, + ) + + if request.dataset_id is None and request.view_id is None: + return SemanticLayerError.create( + error="Provide either dataset_id (built-in) or view_id (external).", + error_type="ValidationError", + ) + if request.dataset_id is not None and request.view_id is not None: + return SemanticLayerError.create( + error="Provide only one of dataset_id or view_id, not both.", + error_type="ValidationError", + ) + + try: + # ------------------------------------------------------------------ + # Built-in dataset path + # ------------------------------------------------------------------ + if request.dataset_id is not None: + from sqlalchemy.orm import subqueryload + + from superset.connectors.sqla.models import SqlaTable + from superset.daos.dataset import DatasetDAO + + dataset_id: int = request.dataset_id + with event_logger.log_context( + action="mcp.get_compatible_dimensions.builtin" + ): + dataset = DatasetDAO.find_by_id( + dataset_id, + query_options=[ + subqueryload(SqlaTable.columns), + subqueryload(SqlaTable.metrics), + ], + ) + + if dataset is None: + return SemanticLayerError.create( + error=f"No dataset found with id: {request.dataset_id}.", + error_type="NotFound", + ) + + # For built-in datasets all groupby columns are always compatible; + # there's no per-metric compatibility constraint at the SQL level. + dims: list[DimensionInfo] = [ + DimensionInfo( + name=col.column_name, + verbose_name=col.verbose_name or None, + description=col.description or None, + type=col.type or None, + is_dttm=bool(col.is_dttm), + groupby=bool(col.groupby), + filterable=bool(col.filterable), + source="builtin", + ) + for col in dataset.columns + if col.groupby + ] + + await ctx.info("Compatible dimensions (builtin): count=%d" % len(dims)) + return CompatibleDimensionsResponse( + compatible_dimensions=dims, + source="builtin", + ) + + # ------------------------------------------------------------------ + # External semantic view path + # ------------------------------------------------------------------ + from superset.daos.semantic_layer import SemanticViewDAO + from superset.exceptions import SupersetSecurityException + from superset.semantic_layers.models import ColumnMetadata + + view_id: int = request.view_id # type: ignore[assignment] + with event_logger.log_context(action="mcp.get_compatible_dimensions.external"): + view = SemanticViewDAO.find_by_id(view_id) + + if view is None: + return SemanticLayerError.create( + error=f"No semantic view found with id: {view_id}.", + error_type="NotFound", + ) + + try: + view.raise_for_access() + except SupersetSecurityException as ex: + return SemanticLayerError.create( + error=str(ex.error.message), + error_type="AccessDenied", + ) + + compatible_names: list[str] = view.get_compatible_dimensions( + request.selected_metrics, + request.selected_dimensions, + ) + + # Enrich with full column metadata + all_cols: dict[str, ColumnMetadata] = { + col.column_name: col for col in view.columns + } + dims = [ Review Comment: **Suggestion:** Add an explicit type annotation to this dimensions collection assignment to keep relevant local variables fully typed. [custom_rule] **Severity Level:** Minor ⚠️ <details> <summary><b>Why it matters? 🤔 </b></summary> The dimensions collection is assigned without an explicit local variable type annotation, which matches the rule requiring type hints for relevant variables that can be annotated. </details> [](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=41d1232cdecc4c91a7272f2d2b88a2a9&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) [](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=41d1232cdecc4c91a7272f2d2b88a2a9&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) *(Use Cmd/Ctrl + Click for best experience)* <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** superset/mcp_service/semantic_layer/tool/get_compatible_dimensions.py **Line:** 197:197 **Comment:** *Custom Rule: Add an explicit type annotation to this dimensions collection assignment to keep relevant local variables fully typed. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=56386bba6c1821c4a28865772ebc63a1ccc7e7d92270515d2d69f2b48ee46a2c&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41611&comment_hash=56386bba6c1821c4a28865772ebc63a1ccc7e7d92270515d2d69f2b48ee46a2c&reaction=dislike'>👎</a> -- 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]
