feat: deprecate created_slices API endpoint (#21664)

Co-authored-by: Ville Brofeldt <33317356+villebro@users.noreply.github.com>
This commit is contained in:
Daniel Vaz Gaspar 2022-10-04 09:13:11 +01:00 committed by GitHub
parent 5da20f449d
commit 3057e4270c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 132 additions and 28 deletions

View File

@ -18,12 +18,14 @@
*/
import rison from 'rison';
import React from 'react';
import moment from 'moment';
import { t } from '@superset-ui/core';
import TableLoader from '../../components/TableLoader';
import { Slice } from '../types';
import { User, DashboardResponse } from '../../types/bootstrapTypes';
import TableLoader from 'src/components/TableLoader';
import {
User,
DashboardResponse,
ChartResponse,
} from 'src/types/bootstrapTypes';
interface CreatedContentProps {
user: User;
@ -31,17 +33,30 @@ interface CreatedContentProps {
class CreatedContent extends React.PureComponent<CreatedContentProps> {
renderSliceTable() {
const mutator = (data: Slice[]) =>
data.map(slice => ({
slice: <a href={slice.url}>{slice.title}</a>,
created: moment.utc(slice.dttm).fromNow(),
_created: slice.dttm,
const search = [
{ col: 'created_by', opr: 'chart_created_by_me', value: 'me' },
];
const query = rison.encode({
keys: ['none'],
columns: ['created_on_delta_humanized', 'slice_name', 'url'],
filters: search,
order_column: 'changed_on_delta_humanized',
order_direction: 'desc',
page: 0,
page_size: 100,
});
const mutator = (data: ChartResponse) =>
data.result.map(chart => ({
chart: <a href={chart.url}>{chart.slice_name}</a>,
created: chart.created_on_delta_humanized,
_created: chart.created_on_delta_humanized,
}));
return (
<TableLoader
dataEndpoint={`/superset/created_slices/${this.props.user.userId}/`}
dataEndpoint={`/api/v1/chart/?q=${query}`}
className="table-condensed"
columns={['slice', 'created']}
columns={['chart', 'created']}
mutator={mutator}
noDataText={t('No charts')}
sortable
@ -50,7 +65,9 @@ class CreatedContent extends React.PureComponent<CreatedContentProps> {
}
renderDashboardTable() {
const search = [{ col: 'created_by', opr: 'created_by_me', value: 'me' }];
const search = [
{ col: 'created_by', opr: 'dashboard_created_by_me', value: 'me' },
];
const query = rison.encode({
keys: ['none'],
columns: ['created_on_delta_humanized', 'dashboard_title', 'url'],

View File

@ -64,6 +64,16 @@ export type DashboardResponse = {
result: DashboardData[];
};
export type ChartData = {
slice_name: string;
created_on_delta_humanized?: string;
url: string;
};
export type ChartResponse = {
result: ChartData[];
};
export interface CommonBootstrapData {
flash_messages: string[][];
conf: JsonObject;

View File

@ -50,6 +50,7 @@ from superset.charts.dao import ChartDAO
from superset.charts.filters import (
ChartAllTextFilter,
ChartCertifiedFilter,
ChartCreatedByMeFilter,
ChartFavoriteFilter,
ChartFilter,
ChartHasCreatedByFilter,
@ -150,6 +151,7 @@ class ChartRestApi(BaseSupersetModelRestApi):
"created_by.first_name",
"created_by.id",
"created_by.last_name",
"created_on_delta_humanized",
"datasource_id",
"datasource_name_text",
"datasource_type",
@ -211,7 +213,7 @@ class ChartRestApi(BaseSupersetModelRestApi):
search_filters = {
"id": [ChartFavoriteFilter, ChartCertifiedFilter],
"slice_name": [ChartAllTextFilter],
"created_by": [ChartHasCreatedByFilter],
"created_by": [ChartHasCreatedByFilter, ChartCreatedByMeFilter],
}
# Will just affect _info endpoint

View File

@ -24,6 +24,7 @@ from superset import db, security_manager
from superset.connectors.sqla import models
from superset.connectors.sqla.models import SqlaTable
from superset.models.slice import Slice
from superset.utils.core import get_user_id
from superset.views.base import BaseFilter
from superset.views.base_api import BaseFavoriteFilter
@ -109,3 +110,18 @@ class ChartHasCreatedByFilter(BaseFilter): # pylint: disable=too-few-public-met
if value is False:
return query.filter(and_(Slice.created_by_fk.is_(None)))
return query
class ChartCreatedByMeFilter(BaseFilter): # pylint: disable=too-few-public-methods
name = _("Created by me")
arg_name = "chart_created_by_me"
def apply(self, query: Query, value: Any) -> Query:
return query.filter(
or_(
Slice.created_by_fk # pylint: disable=comparison-with-callable
== get_user_id(),
Slice.changed_by_fk # pylint: disable=comparison-with-callable
== get_user_id(),
)
)

View File

@ -52,7 +52,7 @@ class DashboardTitleOrSlugFilter(BaseFilter): # pylint: disable=too-few-public-
class DashboardCreatedByMeFilter(BaseFilter): # pylint: disable=too-few-public-methods
name = _("Created by me")
arg_name = "created_by_me"
arg_name = "dashboard_created_by_me"
def apply(self, query: Query, value: Any) -> Query:
return query.filter(

View File

@ -1708,6 +1708,11 @@ class Superset(BaseSupersetView): # pylint: disable=too-many-public-methods
@expose("/created_slices/<int:user_id>/", methods=["GET"])
def created_slices(self, user_id: Optional[int] = None) -> FlaskResponse:
"""List of slices created by this user"""
logger.warning(
"%s.created_slices "
"This API endpoint is deprecated and will be removed in version 3.0.0",
self.__class__.__name__,
)
if not user_id:
user_id = cast(int, get_user_id())
error_obj = self.get_user_activity_access_error(user_id)

View File

@ -101,6 +101,19 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixin):
db.session.delete(fav_chart)
db.session.commit()
@pytest.fixture()
def create_charts_created_by_gamma(self):
with self.create_app().app_context():
charts = []
user = self.get_user("gamma")
for cx in range(CHARTS_FIXTURE_COUNT - 1):
charts.append(self.insert_chart(f"gamma{cx}", [user.id], 1))
yield charts
# rollback changes
for chart in charts:
db.session.delete(chart)
db.session.commit()
@pytest.fixture()
def create_certified_charts(self):
with self.create_app().app_context():
@ -1124,6 +1137,33 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixin):
assert rv.status_code == 200
assert len(expected_models) == data["count"]
@pytest.mark.usefixtures("create_charts_created_by_gamma")
def test_get_charts_created_by_me_filter(self):
"""
Chart API: Test get charts with created by me special filter
"""
gamma_user = self.get_user("gamma")
expected_models = (
db.session.query(Slice).filter(Slice.created_by_fk == gamma_user.id).all()
)
arguments = {
"filters": [
{"col": "created_by", "opr": "chart_created_by_me", "value": "me"}
],
"order_column": "slice_name",
"order_direction": "asc",
"keys": ["none"],
"columns": ["slice_name"],
}
self.login(username="gamma")
uri = f"api/v1/chart/?q={prison.dumps(arguments)}"
rv = self.client.get(uri)
data = json.loads(rv.data.decode("utf-8"))
assert rv.status_code == 200
assert len(expected_models) == data["count"]
for i, expected_model in enumerate(expected_models):
assert expected_model.slice_name == data["result"][i]["slice_name"]
@pytest.mark.usefixtures("create_charts")
def test_get_current_user_favorite_status(self):
"""

View File

@ -161,16 +161,16 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixi
db.session.commit()
@pytest.fixture()
def create_created_by_admin_dashboards(self):
def create_created_by_gamma_dashboards(self):
with self.create_app().app_context():
dashboards = []
admin = self.get_user("admin")
gamma = self.get_user("gamma")
for cx in range(2):
dashboard = self.insert_dashboard(
f"create_title{cx}",
f"create_slug{cx}",
[admin.id],
created_by=admin,
[gamma.id],
created_by=gamma,
)
sleep(1)
dashboards.append(dashboard)
@ -697,21 +697,23 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixi
data = json.loads(rv.data.decode("utf-8"))
self.assertEqual(data["count"], 5)
@pytest.mark.usefixtures("create_created_by_admin_dashboards")
@pytest.mark.usefixtures("create_created_by_gamma_dashboards")
def test_get_dashboards_created_by_me(self):
"""
Dashboard API: Test get dashboards created by current user
"""
query = {
"columns": ["created_on_delta_humanized", "dashboard_title", "url"],
"filters": [{"col": "created_by", "opr": "created_by_me", "value": "me"}],
"filters": [
{"col": "created_by", "opr": "dashboard_created_by_me", "value": "me"}
],
"order_column": "changed_on",
"order_direction": "desc",
"page": 0,
"page_size": 100,
}
uri = f"api/v1/dashboard/?q={prison.dumps(query)}"
self.login(username="admin")
self.login(username="gamma")
rv = self.client.get(uri)
data = json.loads(rv.data.decode("utf-8"))
assert rv.status_code == 200
@ -1837,11 +1839,17 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixi
resp = self.get_assert_metric(uri, "get_embedded")
self.assertEqual(resp.status_code, 404)
@pytest.mark.usefixtures("create_created_by_admin_dashboards")
@pytest.mark.usefixtures("create_created_by_gamma_dashboards")
def test_gets_created_by_user_dashboards_filter(self):
expected_models = (
db.session.query(Dashboard)
.filter(Dashboard.created_by_fk.isnot(None))
.all()
)
arguments = {
"filters": [
{"col": "id", "opr": "dashboard_has_created_by", "value": True}
{"col": "created_by", "opr": "dashboard_has_created_by", "value": True}
],
"keys": ["none"],
"columns": ["dashboard_title"],
@ -1852,23 +1860,27 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixi
rv = self.get_assert_metric(uri, "get_list")
self.assertEqual(rv.status_code, 200)
data = json.loads(rv.data.decode("utf-8"))
self.assertEqual(data["count"], 7)
self.assertEqual(data["count"], len(expected_models))
def test_gets_not_created_by_user_dashboards_filter(self):
dashboard = self.insert_dashboard(f"title", f"slug", [])
expected_models = (
db.session.query(Dashboard).filter(Dashboard.created_by_fk.is_(None)).all()
)
arguments = {
"filters": [
{"col": "id", "opr": "dashboard_has_created_by", "value": False}
{"col": "created_by", "opr": "dashboard_has_created_by", "value": False}
],
"keys": ["none"],
"columns": ["dashboard_title"],
}
dashboard = self.insert_dashboard(f"title", f"slug", [])
self.login(username="admin")
uri = f"api/v1/dashboard/?q={prison.dumps(arguments)}"
rv = self.get_assert_metric(uri, "get_list")
self.assertEqual(rv.status_code, 200)
data = json.loads(rv.data.decode("utf-8"))
self.assertEqual(data["count"], 6)
self.assertEqual(data["count"], len(expected_models))
db.session.delete(dashboard)
db.session.commit()

View File

@ -592,7 +592,9 @@ class TestSqlaTableModel(SupersetTestCase):
assert len(data_for_slices["metrics"]) == 1
assert len(data_for_slices["columns"]) == 2
assert data_for_slices["metrics"][0]["metric_name"] == "sum__num"
assert data_for_slices["columns"][0]["column_name"] == "name"
column_names = [col["column_name"] for col in data_for_slices["columns"]]
assert "name" in column_names
assert "state" in column_names
assert set(data_for_slices["verbose_map"].keys()) == {
"__timestamp",
"sum__num",