Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 50 additions & 27 deletions superset/charts/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
from superset.charts.schemas import (
CHART_SCHEMAS,
ChartCacheWarmUpRequestSchema,
ChartGetResponseSchema,
ChartPostSchema,
ChartPutSchema,
get_delete_ids_schema,
Expand Down Expand Up @@ -131,34 +132,7 @@ def ensure_thumbnails_enabled(self) -> Optional[Response]:
}
class_permission_name = "Chart"
method_permission_name = MODEL_API_RW_METHOD_PERMISSION_MAP
show_columns = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this removal was not intended as it's still used by list methods?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's useless since we specified them in ChartGetResponseSchema. I reused similar approach to the dashboard API PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, I checked to make sure the related objects have the same fields/schema, LGTM

"cache_timeout",
"certified_by",
"certification_details",
"changed_on_delta_humanized",
"dashboards.dashboard_title",
"dashboards.id",
"dashboards.json_metadata",
"description",
"id",
"owners.first_name",
"owners.id",
"owners.last_name",
"dashboards.id",
"dashboards.dashboard_title",
"params",
"slice_name",
"thumbnail_url",
"url",
"viz_type",
"query_context",
"is_managed_externally",
"tags.id",
"tags.name",
"tags.type",
]

show_select_columns = show_columns + ["table.id"]
list_columns = [
"is_managed_externally",
"certified_by",
Expand Down Expand Up @@ -230,6 +204,7 @@ def ensure_thumbnails_enabled(self) -> Optional[Response]:
"datasource_type",
"description",
"id",
"uuid",
"owners",
"dashboards",
"slice_name",
Expand All @@ -255,6 +230,7 @@ def ensure_thumbnails_enabled(self) -> Optional[Response]:

add_model_schema = ChartPostSchema()
edit_model_schema = ChartPutSchema()
chart_get_response_schema = ChartGetResponseSchema()

openapi_spec_tag = "Charts"
""" Override the name set for this collection of endpoints """
Expand Down Expand Up @@ -287,6 +263,53 @@ def ensure_thumbnails_enabled(self) -> Optional[Response]:

allowed_rel_fields = {"owners", "created_by", "changed_by"}

@expose("/<id_or_uuid>", methods=["GET"])
@protect()
@safe
@statsd_metrics
@event_logger.log_this_with_context(
action=lambda self, *args, **kwargs: f"{self.__class__.__name__}.get",
log_to_statsd=False,
)
def get(self, id_or_uuid: str) -> Response:
"""Gets a chart
---
get:
description: >-
Get a chart
parameters:
- in: path
schema:
type: string
name: id_or_uuid
description: Either the id of the chart, or its uuid
responses:
200:
description: Chart
content:
application/json:
schema:
type: object
properties:
result:
$ref: '#/components/schemas/ChartGetResponseSchema'
302:
description: Redirects to the current digest
400:
$ref: '#/components/responses/400'
401:
$ref: '#/components/responses/401'
404:
$ref: '#/components/responses/404'
"""
# pylint: disable=arguments-differ
try:
dash = ChartDAO.get_by_id_or_uuid(id_or_uuid)
result = self.chart_get_response_schema.dump(dash)
return self.response(200, result=result)
except ChartNotFoundError:
return self.response_404()

@expose("/", methods=("POST",))
@protect()
@safe
Expand Down
47 changes: 47 additions & 0 deletions superset/charts/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

from superset.common.chart_data import ChartDataResultFormat, ChartDataResultType
from superset.db_engine_specs.base import builtin_time_grains
from superset.tags.models import TagType
from superset.utils import pandas_postprocessing, schema as utils
from superset.utils.core import (
AnnotationType,
Expand Down Expand Up @@ -241,6 +242,7 @@ class ChartPostSchema(Schema):
)
is_managed_externally = fields.Boolean(allow_none=True, dump_default=False)
external_url = fields.String(allow_none=True)
uuid = fields.UUID(allow_none=True)


class ChartPutSchema(Schema):
Expand Down Expand Up @@ -297,6 +299,7 @@ class ChartPutSchema(Schema):
is_managed_externally = fields.Boolean(allow_none=True, dump_default=False)
external_url = fields.String(allow_none=True)
tags = fields.List(fields.Integer(metadata={"description": tags_description}))
uuid = fields.UUID(allow_none=True)


class ChartGetDatasourceObjectDataResponseSchema(Schema):
Expand Down Expand Up @@ -1617,6 +1620,49 @@ class ChartCacheWarmUpResponseSchema(Schema):
)


class TagSchema(Schema):
id = fields.Int()
name = fields.String()
type = fields.Enum(TagType, by_value=True)


class UserSchema(Schema):
id = fields.Int()
first_name = fields.String()
last_name = fields.String()


class DashboardSchema(Schema):
id = fields.Int()
dashboard_title = fields.String()
json_metadata = fields.String()


class ChartGetResponseSchema(Schema):
id = fields.Int(description=id_description)
url = fields.String()
cache_timeout = fields.String()
certified_by = fields.String()
certification_details = fields.String()
changed_on_humanized = fields.String(data_key="changed_on_delta_humanized")
description = fields.String()
params = fields.String()
slice_name = fields.String()
thumbnail_url = fields.String()
viz_type = fields.String()
query_context = fields.String()
is_managed_externally = fields.Boolean()
tags = fields.Nested(TagSchema, many=True)
owners = fields.List(fields.Nested(UserSchema))
dashboards = fields.List(fields.Nested(DashboardSchema))
uuid = fields.UUID()
datasource_id = fields.Int()
datasource_name_text = fields.Function(lambda obj: obj.datasource_name_text())
datasource_type = fields.String()
datasource_url = fields.Function(lambda obj: obj.datasource_url())
datasource_uuid = fields.UUID(attribute="table.uuid")


CHART_SCHEMAS = (
ChartCacheWarmUpRequestSchema,
ChartCacheWarmUpResponseSchema,
Expand All @@ -1640,6 +1686,7 @@ class ChartCacheWarmUpResponseSchema(Schema):
ChartDataGeodeticParseOptionsSchema,
ChartEntityResponseSchema,
ChartGetDatasourceResponseSchema,
ChartGetResponseSchema,
ChartCacheScreenshotResponseSchema,
GetFavStarIdsSchema,
)
4 changes: 2 additions & 2 deletions superset/common/query_context_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
get_since_until_from_time_range,
)
from superset.connectors.sqla.models import BaseDatasource
from superset.constants import CacheRegion, TimeGrain
from superset.constants import CACHE_DISABLED_TIMEOUT, CacheRegion, TimeGrain
from superset.daos.annotation_layer import AnnotationLayerDAO
from superset.daos.chart import ChartDAO
from superset.exceptions import (
Expand Down Expand Up @@ -131,7 +131,7 @@ def get_df_payload(
"""Handles caching around the df payload retrieval"""
cache_key = self.query_cache_key(query_obj)
timeout = self.get_cache_timeout()
force_query = self._query_context.force or timeout == -1
force_query = self._query_context.force or timeout == CACHE_DISABLED_TIMEOUT
cache = QueryCacheManager.get(
key=cache_key,
region=CacheRegion.DATA,
Expand Down
4 changes: 4 additions & 0 deletions superset/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,7 @@ class CacheRegion(StrEnum):
DEFAULT = "default"
DATA = "data"
THUMBNAIL = "thumbnail"


# Cache timeout constants
CACHE_DISABLED_TIMEOUT = -1 # Special value indicating no caching should occur
15 changes: 14 additions & 1 deletion superset/daos/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@
from datetime import datetime
from typing import TYPE_CHECKING

from flask_appbuilder.models.sqla.interface import SQLAInterface

from superset.charts.filters import ChartFilter
from superset.commands.chart.exceptions import ChartNotFoundError
from superset.daos.base import BaseDAO
from superset.extensions import db
from superset.models.core import FavStar, FavStarClassName
from superset.models.slice import Slice
from superset.models.slice import id_or_uuid_filter, Slice
from superset.utils.core import get_user_id

if TYPE_CHECKING:
Expand All @@ -36,6 +39,16 @@
class ChartDAO(BaseDAO[Slice]):
base_filter = ChartFilter

@staticmethod
def get_by_id_or_uuid(id_or_uuid: str) -> Slice:
query = db.session.query(Slice).filter(id_or_uuid_filter(id_or_uuid))
# Apply chart base filters
query = ChartFilter("id", SQLAInterface(Slice, db.session)).apply(query, None)
chart = query.one_or_none()
if not chart:
raise ChartNotFoundError()
return chart

@staticmethod
def favorited_ids(charts: list[Slice]) -> list[FavStar]:
ids = [chart.id for chart in charts]
Expand Down
2 changes: 2 additions & 0 deletions superset/dashboards/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ class DashboardRestApi(BaseSupersetModelRestApi):

list_columns = [
"id",
"uuid",
"published",
"status",
"slug",
Expand Down Expand Up @@ -251,6 +252,7 @@ class DashboardRestApi(BaseSupersetModelRestApi):
"changed_by",
"dashboard_title",
"id",
"uuid",
"owners",
"published",
"roles",
Expand Down
3 changes: 3 additions & 0 deletions superset/dashboards/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ class DashboardGetResponseSchema(Schema):
changed_on_humanized = fields.String(data_key="changed_on_delta_humanized")
created_on_humanized = fields.String(data_key="created_on_delta_humanized")
is_managed_externally = fields.Boolean(allow_none=True, dump_default=False)
uuid = fields.UUID(allow_none=True)

# pylint: disable=unused-argument
@post_dump()
Expand Down Expand Up @@ -365,6 +366,7 @@ class DashboardPostSchema(BaseDashboardSchema):
)
is_managed_externally = fields.Boolean(allow_none=True, dump_default=False)
external_url = fields.String(allow_none=True)
uuid = fields.UUID(allow_none=True)


class DashboardCopySchema(Schema):
Expand Down Expand Up @@ -431,6 +433,7 @@ class DashboardPutSchema(BaseDashboardSchema):
tags = fields.List(
fields.Integer(metadata={"description": tags_description}, allow_none=True)
)
uuid = fields.UUID(allow_none=True)


class DashboardNativeFiltersConfigUpdateSchema(BaseDashboardSchema):
Expand Down
5 changes: 5 additions & 0 deletions superset/datasets/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,10 @@ class DatasetRestApi(BaseSupersetModelRestApi):
}
list_columns = [
"id",
"uuid",
"database.id",
"database.database_name",
"database.uuid",
"changed_by_name",
"changed_by.first_name",
"changed_by.last_name",
Expand Down Expand Up @@ -153,6 +155,7 @@ class DatasetRestApi(BaseSupersetModelRestApi):
"id",
"database.database_name",
"database.id",
"database.uuid",
"table_name",
"sql",
"filter_select_enabled",
Expand Down Expand Up @@ -222,6 +225,7 @@ class DatasetRestApi(BaseSupersetModelRestApi):
"columns.advanced_data_type",
"is_managed_externally",
"uid",
"uuid",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference between the uid field and uuid?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"datasource_name",
"name",
"column_formats",
Expand Down Expand Up @@ -273,6 +277,7 @@ class DatasetRestApi(BaseSupersetModelRestApi):
}
search_columns = [
"id",
"uuid",
"database",
"owners",
"catalog",
Expand Down
2 changes: 2 additions & 0 deletions superset/datasets/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ class DatasetPostSchema(Schema):
normalize_columns = fields.Boolean(load_default=False)
always_filter_main_dttm = fields.Boolean(load_default=False)
template_params = fields.String(allow_none=True)
uuid = fields.UUID(allow_none=True)


class DatasetPutSchema(Schema):
Expand All @@ -176,6 +177,7 @@ class DatasetPutSchema(Schema):
extra = fields.String(allow_none=True)
is_managed_externally = fields.Boolean(allow_none=True, dump_default=False)
external_url = fields.String(allow_none=True)
uuid = fields.UUID(allow_none=True)

def handle_error(
self,
Expand Down
11 changes: 9 additions & 2 deletions superset/models/slice.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from sqlalchemy.engine.base import Connection
from sqlalchemy.orm import relationship
from sqlalchemy.orm.mapper import Mapper
from sqlalchemy.sql.elements import BinaryExpression

from superset import db, is_feature_enabled, security_manager
from superset.legacy import update_time_range
Expand Down Expand Up @@ -361,11 +362,17 @@ def get_query_context_factory(self) -> QueryContextFactory:
return self.query_context_factory

@classmethod
def get(cls, id_: int) -> Slice:
qry = db.session.query(Slice).filter_by(id=id_)
def get(cls, id_or_uuid: str) -> Slice:
qry = db.session.query(Slice).filter_by(id_or_uuid_filter(id_or_uuid))
return qry.one_or_none()


def id_or_uuid_filter(id_or_uuid: str) -> BinaryExpression:
if id_or_uuid.isdigit():
return Slice.id == int(id_or_uuid)
return Slice.uuid == id_or_uuid


def set_related_perm(_mapper: Mapper, _connection: Connection, target: Slice) -> None:
src_class = target.cls_model
if id_ := target.datasource_id:
Expand Down
2 changes: 1 addition & 1 deletion superset/tasks/thumbnails.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
@celery_app.task(name="cache_chart_thumbnail", soft_time_limit=300)
def cache_chart_thumbnail(
current_user: Optional[str],
chart_id: int,
chart_id: str,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this required? I don't see the call to cache_chart_thumbnail having been changed to support uuids (?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

force: bool,
window_size: Optional[WindowSize] = None,
thumb_size: Optional[WindowSize] = None,
Expand Down
Loading
Loading