Ejemplo n.º 1
0
    def external_metadata_by_name(self, **kwargs: Any) -> FlaskResponse:
        """Gets table metadata from the source system and SQLAlchemy inspector"""
        try:
            params: ExternalMetadataParams = ExternalMetadataSchema().load(
                kwargs.get("rison"))
        except ValidationError as err:
            return json_error_response(str(err), status=400)

        datasource = SqlaTable.get_datasource_by_name(
            session=db.session,
            database_name=params["database_name"],
            schema=params["schema_name"],
            datasource_name=params["table_name"],
        )
        try:
            if datasource is not None:
                # Get columns from Superset metadata
                external_metadata = datasource.external_metadata()
            else:
                # Use the SQLAlchemy inspector to get columns
                database = (db.session.query(Database).filter_by(
                    database_name=params["database_name"]).one())
                external_metadata = get_physical_table_metadata(
                    database=database,
                    table_name=params["table_name"],
                    schema_name=params["schema_name"],
                )
        except (NoResultFound, NoSuchTableError) as ex:
            raise DatasetNotFoundError() from ex
        return self.json_response(external_metadata)
Ejemplo n.º 2
0
def import_chart(
    slc_to_import: Slice,
    slc_to_override: Optional[Slice],
    import_time: Optional[int] = None,
) -> int:
    """Inserts or overrides slc in the database.

    remote_id and import_time fields in params_dict are set to track the
    slice origin and ensure correct overrides for multiple imports.
    Slice.perm is used to find the datasources and connect them.

    :param Slice slc_to_import: Slice object to import
    :param Slice slc_to_override: Slice to replace, id matches remote_id
    :returns: The resulting id for the imported slice
    :rtype: int
    """
    session = db.session
    make_transient(slc_to_import)
    slc_to_import.dashboards = []
    slc_to_import.alter_params(remote_id=slc_to_import.id, import_time=import_time)

    slc_to_import = slc_to_import.copy()
    slc_to_import.reset_ownership()
    params = slc_to_import.params_dict
    datasource = SqlaTable.get_datasource_by_name(
        session=session,
        datasource_name=params["datasource_name"],
        database_name=params["database_name"],
        schema=params["schema"],
    )
    slc_to_import.datasource_id = datasource.id  # type: ignore
    if slc_to_override:
        slc_to_override.override(slc_to_import)
        session.flush()
        return slc_to_override.id
    session.add(slc_to_import)
    logger.info("Final slice: %s", str(slc_to_import.to_json()))
    session.flush()
    return slc_to_import.id
Ejemplo n.º 3
0
    def invalidate(self) -> Response:
        """
        Takes a list of datasources, finds the associated cache records and
        invalidates them and removes the database records

        ---
        post:
          description: >-
            Takes a list of datasources, finds the associated cache records and
            invalidates them and removes the database records
          requestBody:
            description: >-
              A list of datasources uuid or the tuples of database and datasource names
            required: true
            content:
              application/json:
                schema:
                  $ref: "#/components/schemas/CacheInvalidationRequestSchema"
          responses:
            201:
              description: cache was successfully invalidated
            400:
              $ref: '#/components/responses/400'
            500:
              $ref: '#/components/responses/500'
        """
        try:
            datasources = CacheInvalidationRequestSchema().load(request.json)
        except KeyError:
            return self.response_400(message="Request is incorrect")
        except ValidationError as error:
            return self.response_400(message=str(error))
        datasource_uids = set(datasources.get("datasource_uids", []))
        for ds in datasources.get("datasources", []):
            ds_obj = SqlaTable.get_datasource_by_name(
                session=db.session,
                datasource_name=ds.get("datasource_name"),
                schema=ds.get("schema"),
                database_name=ds.get("database_name"),
            )

            if ds_obj:
                datasource_uids.add(ds_obj.uid)

        cache_key_objs = (db.session.query(CacheKey).filter(
            CacheKey.datasource_uid.in_(datasource_uids)).all())
        cache_keys = [c.cache_key for c in cache_key_objs]
        if cache_key_objs:
            all_keys_deleted = cache_manager.cache.delete_many(*cache_keys)

            if not all_keys_deleted:
                # expected behavior as keys may expire and cache is not a
                # persistent storage
                logger.info(
                    "Some of the cache keys were not deleted in the list %s",
                    cache_keys)

            try:
                delete_stmt = (
                    CacheKey.__table__.delete().where(  # pylint: disable=no-member
                        CacheKey.cache_key.in_(cache_keys)))
                db.session.execute(delete_stmt)
                db.session.commit()
                self.stats_logger.gauge("invalidated_cache", len(cache_keys))
                logger.info(
                    "Invalidated %s cache records for %s datasources",
                    len(cache_keys),
                    len(datasource_uids),
                )
            except SQLAlchemyError as ex:  # pragma: no cover
                logger.error(ex, exc_info=True)
                db.session.rollback()
                return self.response_500(str(ex))
            db.session.commit()
        return self.response(201)