-
Notifications
You must be signed in to change notification settings - Fork 16.3k
fix(charts): add query_context validation to prevent incomplete metadata #36076
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
287674d
f4a1bdf
0b67409
567c150
b267f2f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,8 +22,11 @@ | |
| from superset.charts.schemas import ( | ||
| ChartDataProphetOptionsSchema, | ||
| ChartDataQueryObjectSchema, | ||
| ChartPostSchema, | ||
| ChartPutSchema, | ||
| get_time_grain_choices, | ||
| ) | ||
| from superset.utils import json | ||
|
|
||
|
|
||
| def test_get_time_grain_choices(app_context: None) -> None: | ||
|
|
@@ -152,3 +155,120 @@ def test_time_grain_validation_with_config_addons(app_context: None) -> None: | |
| } | ||
| result = schema.load(custom_data) | ||
| assert result["time_grain"] == "PT10M" | ||
|
|
||
|
|
||
| def test_chart_post_schema_query_context_validation(app_context: None) -> None: | ||
| """Test that ChartPostSchema validates query_context contains required metadata""" | ||
| schema = ChartPostSchema() | ||
|
|
||
| # Valid query_context with datasource and queries should pass | ||
| valid_query_context = json.dumps( | ||
| { | ||
| "datasource": {"type": "table", "id": 1}, | ||
| "queries": [{"metrics": ["count"], "columns": []}], | ||
| } | ||
| ) | ||
| valid_data = { | ||
| "slice_name": "Test Chart", | ||
| "datasource_id": 1, | ||
| "datasource_type": "table", | ||
| "query_context": valid_query_context, | ||
| } | ||
| result = schema.load(valid_data) | ||
| assert result["query_context"] == valid_query_context | ||
|
|
||
| # None query_context should be allowed (allow_none=True) | ||
| none_data = { | ||
| "slice_name": "Test Chart", | ||
| "datasource_id": 1, | ||
| "datasource_type": "table", | ||
| "query_context": None, | ||
| } | ||
| result = schema.load(none_data) | ||
| assert result["query_context"] is None | ||
|
|
||
| # Query context missing 'datasource' field should fail | ||
| missing_datasource = json.dumps( | ||
| {"queries": [{"metrics": ["count"], "columns": []}]} | ||
| ) | ||
| invalid_data_1 = { | ||
| "slice_name": "Test Chart", | ||
| "datasource_id": 1, | ||
| "datasource_type": "table", | ||
| "query_context": missing_datasource, | ||
| } | ||
| with pytest.raises(ValidationError) as exc_info: | ||
| schema.load(invalid_data_1) | ||
| assert "query_context" in exc_info.value.messages | ||
| assert "datasource" in str(exc_info.value.messages["query_context"]) | ||
|
|
||
| # Query context missing 'queries' field should fail | ||
| missing_queries = json.dumps({"datasource": {"type": "table", "id": 1}}) | ||
| invalid_data_2 = { | ||
| "slice_name": "Test Chart", | ||
| "datasource_id": 1, | ||
| "datasource_type": "table", | ||
| "query_context": missing_queries, | ||
| } | ||
| with pytest.raises(ValidationError) as exc_info: | ||
| schema.load(invalid_data_2) | ||
| assert "query_context" in exc_info.value.messages | ||
| assert "queries" in str(exc_info.value.messages["query_context"]) | ||
|
|
||
| # Query context missing both 'datasource' and 'queries' should fail | ||
| empty_query_context = json.dumps({}) | ||
| invalid_data_3 = { | ||
| "slice_name": "Test Chart", | ||
| "datasource_id": 1, | ||
| "datasource_type": "table", | ||
| "query_context": empty_query_context, | ||
| } | ||
| with pytest.raises(ValidationError) as exc_info: | ||
| schema.load(invalid_data_3) | ||
| assert "query_context" in exc_info.value.messages | ||
| assert "datasource" in str(exc_info.value.messages["query_context"]) | ||
| assert "queries" in str(exc_info.value.messages["query_context"]) | ||
|
|
||
| # Invalid JSON should fail | ||
| invalid_json = "not valid json" | ||
| invalid_data_4 = { | ||
| "slice_name": "Test Chart", | ||
| "datasource_id": 1, | ||
| "datasource_type": "table", | ||
| "query_context": invalid_json, | ||
| } | ||
| with pytest.raises(ValidationError) as exc_info: | ||
| schema.load(invalid_data_4) | ||
| assert "query_context" in exc_info.value.messages | ||
|
|
||
|
|
||
| def test_chart_put_schema_query_context_validation(app_context: None) -> None: | ||
| """Test that ChartPutSchema validates query_context contains required metadata""" | ||
| schema = ChartPutSchema() | ||
|
|
||
| # Valid query_context with datasource and queries should pass | ||
| valid_query_context = json.dumps( | ||
| { | ||
| "datasource": {"type": "table", "id": 1}, | ||
| "queries": [{"metrics": ["count"], "columns": []}], | ||
| } | ||
| ) | ||
| valid_data = { | ||
| "slice_name": "Updated Chart", | ||
| "query_context": valid_query_context, | ||
| } | ||
| result = schema.load(valid_data) | ||
| assert result["query_context"] == valid_query_context | ||
|
|
||
| # Query context missing required fields should fail | ||
| missing_datasource = json.dumps( | ||
| {"queries": [{"metrics": ["count"], "columns": []}]} | ||
| ) | ||
| invalid_data = { | ||
| "slice_name": "Updated Chart", | ||
| "query_context": missing_datasource, | ||
| } | ||
| with pytest.raises(ValidationError) as exc_info: | ||
| schema.load(invalid_data) | ||
| assert "query_context" in exc_info.value.messages | ||
| assert "datasource" in str(exc_info.value.messages["query_context"]) | ||
|
Comment on lines
+245
to
+274
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The JSON value is being parsed twice - once in
validate_json()at line 71 and again at line 74. Consider optimizing by parsing once and validating the structure. For example:This would avoid the redundant parsing operation.