def test_get_form_data_globals(self) -> None: with app.test_request_context(): g.form_data = {"foo": "bar"} form_data, slc = get_form_data() delattr(g, "form_data") self.assertEqual(form_data, {"foo": "bar"}) self.assertEqual(slc, None)
def url_param( self, param: str, default: Optional[str] = None, add_to_cache_keys: bool = True ) -> Optional[Any]: """ Read a url or post parameter and use it in your SQL Lab query. When in SQL Lab, it's possible to add arbitrary URL "query string" parameters, and use those in your SQL code. For instance you can alter your url and add `?foo=bar`, as in `{domain}/superset/sqllab?foo=bar`. Then if your query is something like SELECT * FROM foo = '{{ url_param('foo') }}', it will be parsed at runtime and replaced by the value in the URL. As you create a visualization form this SQL Lab query, you can pass parameters in the explore view as well as from the dashboard, and it should carry through to your queries. Default values for URL parameters can be defined in chart metadata by adding the key-value pair `url_params: {'foo': 'bar'}` :param param: the parameter to lookup :param default: the value to return in the absence of the parameter :param add_to_cache_keys: Whether the value should be included in the cache key :returns: The URL parameters """ from superset.views.utils import get_form_data if request.args.get(param): return request.args.get(param, default) form_data, _ = get_form_data() url_params = form_data.get("url_params") or {} result = url_params.get(param, default) if add_to_cache_keys: self.cache_key_wrapper(result) return result
def test_get_form_data_request_form_with_queries(self) -> None: # the CSV export uses for requests, even when sending requests to # /api/v1/chart/data with app.test_request_context(data={ "form_data": json.dumps({"queries": [{ "url_params": { "foo": "bar" } }]}) }): form_data, slc = get_form_data() self.assertEqual( form_data, { "url_params": { "foo": "bar" }, "time_range_endpoints": get_time_range_endpoints(form_data={}), }, ) self.assertEqual(slc, None)
def test_get_form_data_request_args(self) -> None: with app.test_request_context( query_string={"form_data": json.dumps({"foo": "bar"})} ): form_data, slc = get_form_data() self.assertEqual(form_data, {"foo": "bar"}) self.assertEqual(slc, None)
def test_get_form_data_corrupted_json(self) -> None: with app.test_request_context( data={"form_data": "{x: '2324'}"}, query_string={"form_data": '{"baz": "bar"'}, ): form_data, slc = get_form_data() self.assertEqual(form_data, {}) self.assertEqual(slc, None)
def test_get_form_data_default(self) -> None: with app.test_request_context(): form_data, slc = get_form_data() self.assertEqual( form_data, {"time_range_endpoints": get_time_range_endpoints(form_data={})}, ) self.assertEqual(slc, None)
def test_get_form_data_request_form(self) -> None: with app.test_request_context(data={"form_data": json.dumps({"foo": "bar"})}): form_data, slc = get_form_data() self.assertEqual( form_data, { "foo": "bar", "time_range_endpoints": get_time_range_endpoints(form_data={}), }, ) self.assertEqual(slc, None)
def filter_values(column: str, default: Optional[str] = None) -> List[str]: """ Gets a values for a particular filter as a list This is useful if: - you want to use a filter box to filter a query where the name of filter box column doesn't match the one in the select statement - you want to have the ability for filter inside the main query for speed purposes Usage example:: SELECT action, count(*) as times FROM logs WHERE action in ( {{ "'" + "','".join(filter_values('action_type')) + "'" }} ) GROUP BY action :param column: column/filter name to lookup :param default: default value to return if there's no matching columns :return: returns a list of filter values """ from superset.views.utils import get_form_data form_data, _ = get_form_data() convert_legacy_filters_into_adhoc(form_data) merge_extra_filters(form_data) return_val = [ comparator for filter in form_data.get("adhoc_filters", []) for comparator in ( filter["comparator"] if isinstance(filter["comparator"], list) else [filter["comparator"]] ) if ( filter.get("expressionType") == "SIMPLE" and filter.get("clause") == "WHERE" and filter.get("subject") == column and filter.get("comparator") ) ] if return_val: return return_val if default: return [default] return []
def test_get_form_data_globals(self) -> None: with app.test_request_context(): g.form_data = {"foo": "bar"} form_data, slc = get_form_data() delattr(g, "form_data") self.assertEqual( form_data, { "foo": "bar", "time_range_endpoints": get_time_range_endpoints(form_data={}), }, ) self.assertEqual(slc, None)
def url_param( self, param: str, default: Optional[str] = None, add_to_cache_keys: bool = True, escape_result: bool = True, ) -> Optional[str]: """ Read a url or post parameter and use it in your SQL Lab query. When in SQL Lab, it's possible to add arbitrary URL "query string" parameters, and use those in your SQL code. For instance you can alter your url and add `?foo=bar`, as in `{domain}/superset/sqllab?foo=bar`. Then if your query is something like SELECT * FROM foo = '{{ url_param('foo') }}', it will be parsed at runtime and replaced by the value in the URL. As you create a visualization form this SQL Lab query, you can pass parameters in the explore view as well as from the dashboard, and it should carry through to your queries. Default values for URL parameters can be defined in chart metadata by adding the key-value pair `url_params: {'foo': 'bar'}` :param param: the parameter to lookup :param default: the value to return in the absence of the parameter :param add_to_cache_keys: Whether the value should be included in the cache key :param escape_result: Should special characters in the result be escaped :returns: The URL parameters """ # pylint: disable=import-outside-toplevel from superset.views.utils import get_form_data if has_request_context() and request.args.get(param): # type: ignore return request.args.get(param, default) form_data, _ = get_form_data() url_params = form_data.get("url_params") or {} result = url_params.get(param, default) if result and escape_result and self.dialect: # use the dialect specific quoting logic to escape string result = String().literal_processor(dialect=self.dialect)( value=result)[1:-1] if add_to_cache_keys: self.cache_key_wrapper(result) return result
def test_get_form_data_default(self) -> None: with app.test_request_context(): form_data, slc = get_form_data() self.assertEqual(slc, None)
def get_filters(self, column: str, remove_filter: bool = False) -> List[Filter]: """Get the filters applied to the given column. In addition to returning values like the filter_values function the get_filters function returns the operator specified in the explorer UI. This is useful if: - you want to handle more than the IN operator in your SQL clause - you want to handle generating custom SQL conditions for a filter - you want to have the ability for filter inside the main query for speed purposes Usage example:: WITH RECURSIVE superiors(employee_id, manager_id, full_name, level, lineage) AS ( SELECT employee_id, manager_id, full_name, 1 as level, employee_id as lineage FROM employees WHERE 1=1 {# Render a blank line #} {%- for filter in get_filters('full_name', remove_filter=True) -%} {%- if filter.get('op') == 'IN' -%} AND full_name IN ( {{ "'" + "', '".join(filter.get('val')) + "'" }} ) {%- endif -%} {%- if filter.get('op') == 'LIKE' -%} AND full_name LIKE {{ "'" + filter.get('val') + "'" }} {%- endif -%} {%- endfor -%} UNION ALL SELECT e.employee_id, e.manager_id, e.full_name, s.level + 1 as level, s.lineage FROM employees e, superiors s WHERE s.manager_id = e.employee_id ) SELECT employee_id, manager_id, full_name, level, lineage FROM superiors order by lineage, level :param column: column/filter name to lookup :param remove_filter: When set to true, mark the filter as processed, removing it from the outer query. Useful when a filter should only apply to the inner query :return: returns a list of filters """ # pylint: disable=import-outside-toplevel from superset.utils.core import FilterOperator from superset.views.utils import get_form_data form_data, _ = get_form_data() convert_legacy_filters_into_adhoc(form_data) merge_extra_filters(form_data) filters: List[Filter] = [] for flt in form_data.get("adhoc_filters", []): val: Union[Any, List[Any]] = flt.get("comparator") op: str = flt["operator"].upper() if flt.get("operator") else None # fltOpName: str = flt.get("filterOptionName") if (flt.get("expressionType") == "SIMPLE" and flt.get("clause") == "WHERE" and flt.get("subject") == column and val): if remove_filter: if column not in self.removed_filters: self.removed_filters.append(column) if column not in self.applied_filters: self.applied_filters.append(column) if op in ( FilterOperator.IN.value, FilterOperator.NOT_IN.value, ) and not isinstance(val, list): val = [val] filters.append({"op": op, "col": column, "val": val}) return filters
def run(self) -> Optional[Dict[str, Any]]: initial_form_data = {} if self._permalink_key is not None: command = GetExplorePermalinkCommand(self._permalink_key) permalink_value = command.run() if not permalink_value: raise ExplorePermalinkGetFailedError() state = permalink_value["state"] initial_form_data = state["formData"] url_params = state.get("urlParams") if url_params: initial_form_data["url_params"] = dict(url_params) elif self._form_data_key: parameters = FormDataCommandParameters(key=self._form_data_key) value = GetFormDataCommand(parameters).run() initial_form_data = json.loads(value) if value else {} message = None if not initial_form_data: if self._slice_id: initial_form_data["slice_id"] = self._slice_id if self._form_data_key: message = _( "Form data not found in cache, reverting to chart metadata." ) elif self._dataset_id: initial_form_data[ "datasource" ] = f"{self._dataset_id}__{self._dataset_type}" if self._form_data_key: message = _( "Form data not found in cache, reverting to dataset metadata." ) form_data, slc = get_form_data( use_slice_data=True, initial_form_data=initial_form_data ) try: self._dataset_id, self._dataset_type = get_datasource_info( self._dataset_id, self._dataset_type, form_data ) except SupersetException: self._dataset_id = None # fallback unkonw datasource to table type self._dataset_type = SqlaTable.type dataset: Optional[BaseDatasource] = None if self._dataset_id is not None: try: dataset = DatasourceDAO.get_datasource( db.session, cast(str, self._dataset_type), self._dataset_id ) except DatasetNotFoundError: pass dataset_name = dataset.name if dataset else _("[Missing Dataset]") if dataset: if app.config["ENABLE_ACCESS_REQUEST"] and ( not security_manager.can_access_datasource(dataset) ): message = __(security_manager.get_datasource_access_error_msg(dataset)) raise DatasetAccessDeniedError( message=message, dataset_type=self._dataset_type, dataset_id=self._dataset_id, ) viz_type = form_data.get("viz_type") if not viz_type and dataset and dataset.default_endpoint: raise WrongEndpointError(redirect=dataset.default_endpoint) form_data["datasource"] = ( str(self._dataset_id) + "__" + cast(str, self._dataset_type) ) # On explore, merge legacy and extra filters into the form data utils.convert_legacy_filters_into_adhoc(form_data) utils.merge_extra_filters(form_data) dummy_dataset_data: Dict[str, Any] = { "type": self._dataset_type, "name": dataset_name, "columns": [], "metrics": [], "database": {"id": 0, "backend": ""}, } try: dataset_data = dataset.data if dataset else dummy_dataset_data except (SupersetException, SQLAlchemyError): dataset_data = dummy_dataset_data return { "dataset": sanitize_datasource_data(dataset_data), "form_data": form_data, "slice": slc.data if slc else None, "message": message, }