Ejemplo n.º 1
0
 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
Ejemplo n.º 2
0
 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
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
    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)
Ejemplo n.º 5
0
 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
Ejemplo n.º 6
0
    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"]),
        )
Ejemplo n.º 7
0
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
Ejemplo n.º 8
0
 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)
Ejemplo n.º 9
0
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
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
 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
Ejemplo n.º 12
0
    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))
Ejemplo n.º 13
0
    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
Ejemplo n.º 14
0
 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
Ejemplo n.º 15
0
 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))
Ejemplo n.º 16
0
 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(),
     )