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)
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
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)