diff --git a/frontend/summary/summary/ExternalSiteVisual.js b/frontend/summary/summary/ExternalSiteVisual.js
index 54160a1b40..1ffafd7266 100644
--- a/frontend/summary/summary/ExternalSiteVisual.js
+++ b/frontend/summary/summary/ExternalSiteVisual.js
@@ -22,6 +22,7 @@ class ExternalWebsite extends BaseVisual {
hostUrl={settings.external_url_hostname}
path={settings.external_url_path}
queryArgs={settings.external_url_query_args}
+ filters={settings.filters}
/>,
el
);
diff --git a/frontend/summary/summary/TableauDashboard.js b/frontend/summary/summary/TableauDashboard.js
index 3513f67d9d..1324c1bc80 100644
--- a/frontend/summary/summary/TableauDashboard.js
+++ b/frontend/summary/summary/TableauDashboard.js
@@ -22,7 +22,7 @@ class TableauDashboard extends Component {
}
render() {
- const {hostUrl, path, queryArgs} = this.props,
+ const {hostUrl, path, queryArgs, filters} = this.props,
contentSize = h.getHawcContentSize(),
MIN_HEIGHT = 600,
MIN_WIDTH = 700,
@@ -31,7 +31,15 @@ class TableauDashboard extends Component {
let fullPath = queryArgs && queryArgs.length > 0 ? `${path}?${queryArgs.join("&")}` : path;
- return
https://public.tableau.com/shared/JWH9N8XGN
.If you'd like to link to another website, please contact us.
""", ) + filters = forms.CharField( + label="Data filters", + initial="[]", + validators=[validate_json_pydantic(constants.TableauFilterList)], + help_text="""
+ Data are expected to be in JSON format, where the each key is a filter name and value is a filter value.
+ For more details, view the Tableau documentation. For example: [{"field": "Category", "value": "Technology"}, {"field": "State", "value": "North Carolina,Virginia"}]
. To remove filters, set string to []
.
+
iris (flowers)
published: true sort_order: short_citation diff --git a/tests/hawc/apps/common/test_validators.py b/tests/hawc/apps/common/test_validators.py index 32ef3609e7..7f5c384b93 100644 --- a/tests/hawc/apps/common/test_validators.py +++ b/tests/hawc/apps/common/test_validators.py @@ -1,11 +1,13 @@ import pytest from django.core.exceptions import ValidationError +from pydantic import BaseModel from hawc.apps.common.validators import ( NumericTextValidator, validate_exact_ids, validate_html_tags, validate_hyperlinks, + validate_json_pydantic, ) @@ -78,3 +80,18 @@ def test_numeric_text_validator(): for value in ["non-numeric", "< 3.2 LOD", "<", "<<2", "1 2", "e-4"]: with pytest.raises(ValidationError, match="Must be number-like"): validator(value) + + +class PersonSchema(BaseModel): + name: str + age: int + + +def test_validate_json_pydantic(): + validator = validate_json_pydantic(PersonSchema) + # invalid + for data in ["", "not JSON", "[]", "{}", '{"name": "johnny"}']: + with pytest.raises(ValidationError): + validator(data) + # valid + assert validator('{"name": "johnny", "age": 30}') is None diff --git a/tests/hawc/apps/summary/test_forms.py b/tests/hawc/apps/summary/test_forms.py index 61aeca87b6..eff22636bd 100644 --- a/tests/hawc/apps/summary/test_forms.py +++ b/tests/hawc/apps/summary/test_forms.py @@ -6,52 +6,78 @@ @pytest.mark.django_db -def test_ExternalSiteForm(db_keys): - assessment = Assessment.objects.get(id=db_keys.assessment_working) - visual_type = VisualType.EXTERNAL_SITE - - data = dict( - title="title", - slug="slug", - description="hi", - external_url="https://public.tableau.com/views/foo1/foo2?:display_count=y&:origin=viz_share_link", - ) - - # demo what success looks like - form = ExternalSiteForm( - data=data, - parent=assessment, - visual_type=visual_type, - ) - assert form.is_valid() - assert form.cleaned_data == { - "title": "title", - "slug": "slug", - "caption": "", - "published": False, - "external_url": "https://public.tableau.com/views/foo1/foo2", - "external_url_hostname": "https://public.tableau.com", - "external_url_path": "/views/foo1/foo2", - "external_url_query_args": [":showVizHome=no", ":embed=y"], - } - - # make sure our site allowlist works - for url in [ - "google.com", - "http://google.com", - "https://google.com", - ]: - data["external_url"] = url - form = ExternalSiteForm(data=data, parent=assessment, visual_type=visual_type) - assert form.is_valid() is False - assert "not on the list of accepted domains" in form.errors["external_url"][0] - - # make sure our path check works - for url in [ - "public.tableau.com", - "public.tableau.com/", - ]: - data["external_url"] = url - form = ExternalSiteForm(data=data, parent=assessment, visual_type=visual_type) - assert form.is_valid() is False - assert "A URL path must be specified." == form.errors["external_url"][0] +class TestExternalSiteForm: + def valid_data(self): + return dict( + title="title", + slug="slug", + description="hi", + external_url="https://public.tableau.com/views/foo1/foo2?:display_count=y&:origin=viz_share_link", + filters="[]", + ) + + def test_success(self, db_keys): + assessment = Assessment.objects.get(id=db_keys.assessment_working) + visual_type = VisualType.EXTERNAL_SITE + + data = self.valid_data() + form = ExternalSiteForm( + data=data, + parent=assessment, + visual_type=visual_type, + ) + assert form.is_valid() + assert form.cleaned_data == { + "title": "title", + "slug": "slug", + "caption": "", + "published": False, + "external_url": "https://public.tableau.com/views/foo1/foo2", + "external_url_hostname": "https://public.tableau.com", + "external_url_path": "/views/foo1/foo2", + "external_url_query_args": [":showVizHome=no", ":embed=y"], + "filters": "[]", + } + + def test_urls(self, db_keys): + assessment = Assessment.objects.get(id=db_keys.assessment_working) + visual_type = VisualType.EXTERNAL_SITE + data = self.valid_data() + # make sure our site allowlist works + for url in [ + "google.com", + "http://google.com", + "https://google.com", + ]: + data["external_url"] = url + form = ExternalSiteForm(data=data, parent=assessment, visual_type=visual_type) + assert form.is_valid() is False + assert "not on the list of accepted domains" in form.errors["external_url"][0] + + # make sure our path check works + for url in [ + "public.tableau.com", + "public.tableau.com/", + ]: + data["external_url"] = url + form = ExternalSiteForm(data=data, parent=assessment, visual_type=visual_type) + assert form.is_valid() is False + assert "A URL path must be specified." == form.errors["external_url"][0] + + def test_filters(self, db_keys): + assessment = Assessment.objects.get(id=db_keys.assessment_working) + visual_type = VisualType.EXTERNAL_SITE + data = self.valid_data() + + # test valid filters + for filters in ["[]", '[{"field":"hi", "value":"ho"}]']: + data["filters"] = filters + form = ExternalSiteForm(data=data, parent=assessment, visual_type=visual_type) + assert form.is_valid() is True + + # test invalid filters + for filters in ["[123]", '[{"field":"hi"}]']: + data["filters"] = filters + form = ExternalSiteForm(data=data, parent=assessment, visual_type=visual_type) + assert form.is_valid() is False + assert "filters" in form.errors