def run(self) -> str: self.validate() try: d_id, d_type = self.datasource.split("__") datasource_id = int(d_id) datasource_type = DatasourceType(d_type) check_chart_access(datasource_id, self.chart_id, datasource_type) value = { "chartId": self.chart_id, "datasourceId": datasource_id, "datasourceType": datasource_type, "datasource": self.datasource, "state": self.state, } command = CreateKeyValueCommand( resource=self.resource, value=value, ) key = command.run() if key.id is None: raise ExplorePermalinkCreateFailedError( "Unexpected missing key id") return encode_permalink_key(key=key.id, salt=self.salt) except SQLAlchemyError as ex: logger.exception("Error running create command") raise ExplorePermalinkCreateFailedError() from ex
def run(self) -> str: self.validate() try: datasource_id = self._cmd_params.datasource_id datasource_type = self._cmd_params.datasource_type chart_id = self._cmd_params.chart_id tab_id = self._cmd_params.tab_id form_data = self._cmd_params.form_data check_access(datasource_id, chart_id, datasource_type) contextual_key = cache_key(session.get("_id"), tab_id, datasource_id, chart_id, datasource_type) key = cache_manager.explore_form_data_cache.get(contextual_key) if not key or not tab_id: key = random_key() if form_data: state: TemporaryExploreState = { "owner": get_user_id(), "datasource_id": datasource_id, "datasource_type": DatasourceType(datasource_type), "chart_id": chart_id, "form_data": form_data, } cache_manager.explore_form_data_cache.set(key, state) cache_manager.explore_form_data_cache.set(contextual_key, key) return key except SQLAlchemyError as ex: logger.exception("Error running create command") raise TemporaryCacheCreateFailedError() from ex
def test_query_cache_key_changes_when_datasource_is_updated(self): self.login(username="******") payload = get_query_context("birth_names") # construct baseline query_cache_key query_context = ChartDataQueryContextSchema().load(payload) query_object = query_context.queries[0] cache_key_original = query_context.query_cache_key(query_object) # make temporary change and revert it to refresh the changed_on property datasource = DatasourceDAO.get_datasource( session=db.session, datasource_type=DatasourceType(payload["datasource"]["type"]), datasource_id=payload["datasource"]["id"], ) description_original = datasource.description datasource.description = "temporary description" db.session.commit() datasource.description = description_original db.session.commit() # create new QueryContext with unchanged attributes, extract new query_cache_key query_context = ChartDataQueryContextSchema().load(payload) query_object = query_context.queries[0] cache_key_new = query_context.query_cache_key(query_object) # the new cache_key should be different due to updated datasource self.assertNotEqual(cache_key_original, cache_key_new)
def test_query_cache_key_changes_when_metric_is_updated(self): self.login(username="******") payload = get_query_context("birth_names") # make temporary change and revert it to refresh the changed_on property datasource = DatasourceDAO.get_datasource( session=db.session, datasource_type=DatasourceType(payload["datasource"]["type"]), datasource_id=payload["datasource"]["id"], ) datasource.metrics.append(SqlMetric(metric_name="foo", expression="select 1;")) db.session.commit() # construct baseline query_cache_key query_context = ChartDataQueryContextSchema().load(payload) query_object = query_context.queries[0] cache_key_original = query_context.query_cache_key(query_object) # wait a second since mysql records timestamps in second granularity time.sleep(1) datasource.metrics[0].expression = "select 2;" db.session.commit() # create new QueryContext with unchanged attributes, extract new query_cache_key query_context = ChartDataQueryContextSchema().load(payload) query_object = query_context.queries[0] cache_key_new = query_context.query_cache_key(query_object) datasource.metrics = [] db.session.commit() # the new cache_key should be different due to updated datasource self.assertNotEqual(cache_key_original, cache_key_new)
def run(self) -> Optional[ExplorePermalinkValue]: self.validate() try: key = decode_permalink_id(self.key, salt=self.salt) value: Optional[ExplorePermalinkValue] = GetKeyValueCommand( resource=self.resource, key=key, ).run() if value: chart_id: Optional[int] = value.get("chartId") # keep this backward compatible for old permalinks datasource_id: int = ( value.get("datasourceId") or value.get("datasetId") or 0 ) datasource_type = DatasourceType( value.get("datasourceType", DatasourceType.TABLE) ) check_chart_access(datasource_id, chart_id, datasource_type) return value return None except ( DatasetNotFoundError, KeyValueGetFailedError, KeyValueParseKeyError, ) as ex: raise ExplorePermalinkGetFailedError(message=ex.message) from ex except SQLAlchemyError as ex: logger.exception("Error running get command") raise ExplorePermalinkGetFailedError() from ex
def _convert_to_model(self, datasource: DatasourceDict) -> BaseDatasource: return DatasourceDAO.get_datasource( session=db.session, datasource_type=DatasourceType(datasource["type"]), datasource_id=int(datasource["id"]), )
def get_datasource_by_id(datasource_id: int, datasource_type: str) -> BaseDatasource: try: return DatasourceDAO.get_datasource(db.session, DatasourceType(datasource_type), datasource_id) except DatasourceNotFound as ex: raise DatasourceNotFoundValidationError() from ex
def external_metadata(self, datasource_type: str, datasource_id: int) -> FlaskResponse: """Gets column info from the source system""" datasource = DatasourceDAO.get_datasource( db.session, DatasourceType(datasource_type), datasource_id, ) try: external_metadata = datasource.external_metadata() except SupersetException as ex: return json_error_response(str(ex), status=400) return self.json_response(external_metadata)
def test_delete_not_owner(test_client, login_as_admin, chart_id: int, datasource: SqlaTable, admin_id: int): another_key = "another_key" another_owner = admin_id + 1 entry: TemporaryExploreState = { "owner": another_owner, "datasource_id": datasource.id, "datasource_type": DatasourceType(datasource.type), "chart_id": chart_id, "form_data": INITIAL_FORM_DATA, } cache_manager.explore_form_data_cache.set(another_key, entry) resp = test_client.delete(f"api/v1/explore/form_data/{another_key}") assert resp.status_code == 403
def get_viz( form_data: FormData, datasource_type: str, datasource_id: int, force: bool = False, force_cached: bool = False, ) -> BaseViz: viz_type = form_data.get("viz_type", "table") datasource = DatasourceDAO.get_datasource( db.session, DatasourceType(datasource_type), datasource_id, ) viz_obj = viz.viz_types[viz_type]( datasource, form_data=form_data, force=force, force_cached=force_cached ) return viz_obj
def run(self) -> Optional[str]: try: key = self._cmd_params.key state: TemporaryExploreState = cache_manager.explore_form_data_cache.get( key) if state: check_access( state["datasource_id"], state["chart_id"], DatasourceType(state["datasource_type"]), ) if self._refresh_timeout: cache_manager.explore_form_data_cache.set(key, state) return state["form_data"] return None except SQLAlchemyError as ex: logger.exception("Error running get command") raise TemporaryCacheGetFailedError() from ex
def save(self) -> FlaskResponse: data = request.form.get("data") if not isinstance(data, str): return json_error_response(_("Request missing data field."), status=500) datasource_dict = json.loads(data) datasource_id = datasource_dict.get("id") datasource_type = datasource_dict.get("type") database_id = datasource_dict["database"].get("id") orm_datasource = DatasourceDAO.get_datasource( db.session, DatasourceType(datasource_type), datasource_id) orm_datasource.database_id = database_id if "owners" in datasource_dict and orm_datasource.owner_class is not None: # Check ownership try: security_manager.raise_for_ownership(orm_datasource) except SupersetSecurityException as ex: raise DatasetForbiddenError() from ex datasource_dict["owners"] = populate_owners(datasource_dict["owners"], default_to_user=False) duplicates = [ name for name, count in Counter( [col["column_name"] for col in datasource_dict["columns"]]).items() if count > 1 ] if duplicates: return json_error_response( _( "Duplicate column name(s): %(columns)s", columns=",".join(duplicates), ), status=409, ) orm_datasource.update_from_object(datasource_dict) data = orm_datasource.data db.session.commit() return self.json_response(sanitize_datasource_data(data))
def run(self) -> Optional[str]: self.validate() try: datasource_id = self._cmd_params.datasource_id chart_id = self._cmd_params.chart_id datasource_type = self._cmd_params.datasource_type key = self._cmd_params.key form_data = self._cmd_params.form_data check_access(datasource_id, chart_id, datasource_type) state: TemporaryExploreState = cache_manager.explore_form_data_cache.get( key) owner = get_user_id() if state and form_data: if state["owner"] != owner: raise TemporaryCacheAccessDeniedError() # Generate a new key if tab_id changes or equals 0 tab_id = self._cmd_params.tab_id contextual_key = cache_key(session.get("_id"), tab_id, datasource_id, chart_id, datasource_type) key = cache_manager.explore_form_data_cache.get(contextual_key) if not key or not tab_id: key = random_key() cache_manager.explore_form_data_cache.set( contextual_key, key) new_state: TemporaryExploreState = { "owner": owner, "datasource_id": datasource_id, "datasource_type": DatasourceType(datasource_type), "chart_id": chart_id, "form_data": form_data, } cache_manager.explore_form_data_cache.set(key, new_state) return key except SQLAlchemyError as ex: logger.exception("Error running update command") raise TemporaryCacheUpdateFailedError() from ex
def run(self) -> bool: try: key = self._cmd_params.key state: TemporaryExploreState = cache_manager.explore_form_data_cache.get( key) if state: datasource_id: int = state["datasource_id"] chart_id: Optional[int] = state["chart_id"] datasource_type = DatasourceType(state["datasource_type"]) check_access(datasource_id, chart_id, datasource_type) if state["owner"] != get_user_id(): raise TemporaryCacheAccessDeniedError() tab_id = self._cmd_params.tab_id contextual_key = cache_key(session.get("_id"), tab_id, datasource_id, chart_id, datasource_type) cache_manager.explore_form_data_cache.delete(contextual_key) return cache_manager.explore_form_data_cache.delete(key) return False except SQLAlchemyError as ex: logger.exception("Error running delete command") raise TemporaryCacheDeleteFailedError() from ex
def get(self, datasource_type: str, datasource_id: int) -> FlaskResponse: datasource = DatasourceDAO.get_datasource( db.session, DatasourceType(datasource_type), datasource_id) return self.json_response(sanitize_datasource_data(datasource.data))
def _convert_to_model(self, datasource: DatasourceDict) -> BaseDatasource: return self._datasource_dao.get_datasource( datasource_type=DatasourceType(datasource["type"]), datasource_id=int(datasource["id"]), session=self._session_maker(), )