chore: Migrate /superset/favstar to API v1 (#23165)
Co-authored-by: hughhhh <hughmil3s@gmail.com>
This commit is contained in:
parent
97b5cdd588
commit
f2be53dd53
|
|
@ -184,8 +184,12 @@ describe('Charts list', () => {
|
|||
});
|
||||
|
||||
it('should allow to favorite/unfavorite', () => {
|
||||
cy.intercept(`/superset/favstar/slice/*/select/`).as('select');
|
||||
cy.intercept(`/superset/favstar/slice/*/unselect/`).as('unselect');
|
||||
cy.intercept({ url: `/api/v1/chart/*/favorites/`, method: 'POST' }).as(
|
||||
'select',
|
||||
);
|
||||
cy.intercept({ url: `/api/v1/chart/*/favorites/`, method: 'DELETE' }).as(
|
||||
'unselect',
|
||||
);
|
||||
|
||||
setGridMode('card');
|
||||
orderAlphabetical();
|
||||
|
|
|
|||
|
|
@ -139,11 +139,15 @@ export function interceptLog() {
|
|||
}
|
||||
|
||||
export function interceptFav() {
|
||||
cy.intercept(`/superset/favstar/Dashboard/*/select/`).as('select');
|
||||
cy.intercept({ url: `/api/v1/dashboard/*/favorites/`, method: 'POST' }).as(
|
||||
'select',
|
||||
);
|
||||
}
|
||||
|
||||
export function interceptUnfav() {
|
||||
cy.intercept(`/superset/favstar/Dashboard/*/unselect/`).as('unselect');
|
||||
cy.intercept({ url: `/api/v1/dashboard/*/favorites/`, method: 'POST' }).as(
|
||||
'unselect',
|
||||
);
|
||||
}
|
||||
|
||||
export function interceptDataset() {
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ test('render content on tooltip', async () => {
|
|||
expect(screen.getByRole('button')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Call fetchFaveStar only on the first render', async () => {
|
||||
test('Call fetchFaveStar on first render and on itemId change', async () => {
|
||||
const props = {
|
||||
itemId: 3,
|
||||
fetchFaveStar: jest.fn(),
|
||||
|
|
@ -92,5 +92,5 @@ test('Call fetchFaveStar only on the first render', async () => {
|
|||
expect(props.fetchFaveStar).toBeCalledWith(props.itemId);
|
||||
|
||||
rerender(<FaveStar {...{ ...props, itemId: 2 }} />);
|
||||
expect(props.fetchFaveStar).toBeCalledTimes(1);
|
||||
expect(props.fetchFaveStar).toBeCalledTimes(2);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import { css, t, styled, useComponentDidMount } from '@superset-ui/core';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { css, t, styled } from '@superset-ui/core';
|
||||
import { Tooltip } from 'src/components/Tooltip';
|
||||
import Icons from 'src/components/Icons';
|
||||
|
||||
|
|
@ -45,11 +45,9 @@ const FaveStar = ({
|
|||
saveFaveStar,
|
||||
fetchFaveStar,
|
||||
}: FaveStarProps) => {
|
||||
useComponentDidMount(() => {
|
||||
if (fetchFaveStar) {
|
||||
fetchFaveStar(itemId);
|
||||
}
|
||||
});
|
||||
useEffect(() => {
|
||||
fetchFaveStar?.(itemId);
|
||||
}, [fetchFaveStar, itemId]);
|
||||
|
||||
const onClick = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
/* eslint camelcase: 0 */
|
||||
import { ActionCreators as UndoActionCreators } from 'redux-undo';
|
||||
import rison from 'rison';
|
||||
import {
|
||||
ensureIsArray,
|
||||
t,
|
||||
|
|
@ -82,7 +83,6 @@ export function removeSlice(sliceId) {
|
|||
return { type: REMOVE_SLICE, sliceId };
|
||||
}
|
||||
|
||||
const FAVESTAR_BASE_URL = '/superset/favstar/Dashboard';
|
||||
export const TOGGLE_FAVE_STAR = 'TOGGLE_FAVE_STAR';
|
||||
export function toggleFaveStar(isStarred) {
|
||||
return { type: TOGGLE_FAVE_STAR, isStarred };
|
||||
|
|
@ -92,10 +92,10 @@ export const FETCH_FAVE_STAR = 'FETCH_FAVE_STAR';
|
|||
export function fetchFaveStar(id) {
|
||||
return function fetchFaveStarThunk(dispatch) {
|
||||
return SupersetClient.get({
|
||||
endpoint: `${FAVESTAR_BASE_URL}/${id}/count/`,
|
||||
endpoint: `/api/v1/dashboard/favorite_status/?q=${rison.encode([id])}`,
|
||||
})
|
||||
.then(({ json }) => {
|
||||
if (json.count > 0) dispatch(toggleFaveStar(true));
|
||||
dispatch(toggleFaveStar(!!json?.result?.[0]?.value));
|
||||
})
|
||||
.catch(() =>
|
||||
dispatch(
|
||||
|
|
@ -112,10 +112,14 @@ export function fetchFaveStar(id) {
|
|||
export const SAVE_FAVE_STAR = 'SAVE_FAVE_STAR';
|
||||
export function saveFaveStar(id, isStarred) {
|
||||
return function saveFaveStarThunk(dispatch) {
|
||||
const urlSuffix = isStarred ? 'unselect' : 'select';
|
||||
return SupersetClient.get({
|
||||
endpoint: `${FAVESTAR_BASE_URL}/${id}/${urlSuffix}/`,
|
||||
})
|
||||
const endpoint = `/api/v1/dashboard/${id}/favorites/`;
|
||||
const apiCall = isStarred
|
||||
? SupersetClient.delete({
|
||||
endpoint,
|
||||
})
|
||||
: SupersetClient.post({ endpoint });
|
||||
|
||||
return apiCall
|
||||
.then(() => {
|
||||
dispatch(toggleFaveStar(!isStarred));
|
||||
})
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ export const DashboardPage: FC<PageProps> = ({ idOrSlug }: PageProps) => {
|
|||
}, [addDangerToast, datasets, datasetsApiError, dispatch]);
|
||||
|
||||
if (error) throw error; // caught in error boundary
|
||||
if (!readyToRender) return <Loading />;
|
||||
if (!readyToRender || !isDashboardHydrated.current) return <Loading />;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
/* eslint camelcase: 0 */
|
||||
import rison from 'rison';
|
||||
import { Dataset } from '@superset-ui/chart-controls';
|
||||
import { t, SupersetClient, QueryFormData } from '@superset-ui/core';
|
||||
import { Dispatch } from 'redux';
|
||||
|
|
@ -27,8 +28,6 @@ import {
|
|||
import { Slice } from 'src/types/Chart';
|
||||
import { SaveActionType } from 'src/explore/types';
|
||||
|
||||
const FAVESTAR_BASE_URL = '/superset/favstar/slice';
|
||||
|
||||
export const UPDATE_FORM_DATA_BY_DATASOURCE = 'UPDATE_FORM_DATA_BY_DATASOURCE';
|
||||
export function updateFormDataByDatasource(
|
||||
prevDatasource: Dataset,
|
||||
|
|
@ -66,11 +65,9 @@ export const FETCH_FAVE_STAR = 'FETCH_FAVE_STAR';
|
|||
export function fetchFaveStar(sliceId: string) {
|
||||
return function (dispatch: Dispatch) {
|
||||
SupersetClient.get({
|
||||
endpoint: `${FAVESTAR_BASE_URL}/${sliceId}/count/`,
|
||||
endpoint: `/api/v1/chart/favorite_status/?q=${rison.encode([sliceId])}`,
|
||||
}).then(({ json }) => {
|
||||
if (json.count > 0) {
|
||||
dispatch(toggleFaveStar(true));
|
||||
}
|
||||
dispatch(toggleFaveStar(!!json?.result?.[0]?.value));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
@ -78,10 +75,14 @@ export function fetchFaveStar(sliceId: string) {
|
|||
export const SAVE_FAVE_STAR = 'SAVE_FAVE_STAR';
|
||||
export function saveFaveStar(sliceId: string, isStarred: boolean) {
|
||||
return function (dispatch: Dispatch) {
|
||||
const urlSuffix = isStarred ? 'unselect' : 'select';
|
||||
SupersetClient.get({
|
||||
endpoint: `${FAVESTAR_BASE_URL}/${sliceId}/${urlSuffix}/`,
|
||||
})
|
||||
const endpoint = `/api/v1/chart/${sliceId}/favorites/`;
|
||||
const apiCall = isStarred
|
||||
? SupersetClient.delete({
|
||||
endpoint,
|
||||
})
|
||||
: SupersetClient.post({ endpoint });
|
||||
|
||||
apiCall
|
||||
.then(() => dispatch(toggleFaveStar(!isStarred)))
|
||||
.catch(() => {
|
||||
dispatch(
|
||||
|
|
|
|||
|
|
@ -91,7 +91,9 @@ jest.mock('lodash/debounce', () => ({
|
|||
fetchMock.post('glob:*/api/v1/explore/form_data*', { key: KEY });
|
||||
fetchMock.put('glob:*/api/v1/explore/form_data*', { key: KEY });
|
||||
fetchMock.get('glob:*/api/v1/explore/form_data*', {});
|
||||
fetchMock.get('glob:*/favstar/slice*', { count: 0 });
|
||||
fetchMock.get('glob:*/api/v1/chart/favorite_status*', {
|
||||
result: [{ value: true }],
|
||||
});
|
||||
|
||||
const defaultPath = '/explore/';
|
||||
const renderWithRouter = ({
|
||||
|
|
|
|||
|
|
@ -542,11 +542,6 @@ export function useImportResource(
|
|||
return { state, importResource };
|
||||
}
|
||||
|
||||
enum FavStarClassName {
|
||||
CHART = 'slice',
|
||||
DASHBOARD = 'Dashboard',
|
||||
}
|
||||
|
||||
type FavoriteStatusResponse = {
|
||||
result: Array<{
|
||||
id: string;
|
||||
|
|
@ -599,15 +594,17 @@ export function useFavoriteStatus(
|
|||
|
||||
const saveFaveStar = useCallback(
|
||||
(id: number, isStarred: boolean) => {
|
||||
const urlSuffix = isStarred ? 'unselect' : 'select';
|
||||
SupersetClient.get({
|
||||
endpoint: `/superset/favstar/${
|
||||
type === 'chart' ? FavStarClassName.CHART : FavStarClassName.DASHBOARD
|
||||
}/${id}/${urlSuffix}/`,
|
||||
}).then(
|
||||
({ json }) => {
|
||||
const endpoint = `/api/v1/${type}/${id}/favorites/`;
|
||||
const apiCall = isStarred
|
||||
? SupersetClient.delete({
|
||||
endpoint,
|
||||
})
|
||||
: SupersetClient.post({ endpoint });
|
||||
|
||||
apiCall.then(
|
||||
() => {
|
||||
updateFavoriteStatus({
|
||||
[id]: (json as { count: number })?.count > 0,
|
||||
[id]: !isStarred,
|
||||
});
|
||||
},
|
||||
createErrorHandler(errMsg =>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# pylint: disable=too-many-lines
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
|
@ -111,6 +112,8 @@ class ChartRestApi(BaseSupersetModelRestApi):
|
|||
"bulk_delete", # not using RouteMethod since locally defined
|
||||
"viz_types",
|
||||
"favorite_status",
|
||||
"add_favorite",
|
||||
"remove_favorite",
|
||||
"thumbnail",
|
||||
"screenshot",
|
||||
"cache_screenshot",
|
||||
|
|
@ -848,6 +851,94 @@ class ChartRestApi(BaseSupersetModelRestApi):
|
|||
]
|
||||
return self.response(200, result=res)
|
||||
|
||||
@expose("/<pk>/favorites/", methods=["POST"])
|
||||
@protect()
|
||||
@safe
|
||||
@statsd_metrics
|
||||
@event_logger.log_this_with_context(
|
||||
action=lambda self, *args, **kwargs: f"{self.__class__.__name__}"
|
||||
f".add_favorite",
|
||||
log_to_statsd=False,
|
||||
)
|
||||
def add_favorite(self, pk: int) -> Response:
|
||||
"""Marks the chart as favorite
|
||||
---
|
||||
post:
|
||||
description: >-
|
||||
Marks the chart as favorite for the current user
|
||||
parameters:
|
||||
- in: path
|
||||
schema:
|
||||
type: integer
|
||||
name: pk
|
||||
responses:
|
||||
200:
|
||||
description: Chart added to favorites
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
result:
|
||||
type: object
|
||||
401:
|
||||
$ref: '#/components/responses/401'
|
||||
404:
|
||||
$ref: '#/components/responses/404'
|
||||
500:
|
||||
$ref: '#/components/responses/500'
|
||||
"""
|
||||
chart = ChartDAO.find_by_id(pk)
|
||||
if not chart:
|
||||
return self.response_404()
|
||||
|
||||
ChartDAO.add_favorite(chart)
|
||||
return self.response(200, result="OK")
|
||||
|
||||
@expose("/<pk>/favorites/", methods=["DELETE"])
|
||||
@protect()
|
||||
@safe
|
||||
@statsd_metrics
|
||||
@event_logger.log_this_with_context(
|
||||
action=lambda self, *args, **kwargs: f"{self.__class__.__name__}"
|
||||
f".remove_favorite",
|
||||
log_to_statsd=False,
|
||||
)
|
||||
def remove_favorite(self, pk: int) -> Response:
|
||||
"""Remove the chart from the user favorite list
|
||||
---
|
||||
delete:
|
||||
description: >-
|
||||
Remove the chart from the user favorite list
|
||||
parameters:
|
||||
- in: path
|
||||
schema:
|
||||
type: integer
|
||||
name: pk
|
||||
responses:
|
||||
200:
|
||||
description: Chart removed from favorites
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
result:
|
||||
type: object
|
||||
401:
|
||||
$ref: '#/components/responses/401'
|
||||
404:
|
||||
$ref: '#/components/responses/404'
|
||||
500:
|
||||
$ref: '#/components/responses/500'
|
||||
"""
|
||||
chart = ChartDAO.find_by_id(pk)
|
||||
if not chart:
|
||||
return self.response_404()
|
||||
|
||||
ChartDAO.remove_favorite(chart)
|
||||
return self.response(200, result="OK")
|
||||
|
||||
@expose("/import/", methods=["POST"])
|
||||
@protect()
|
||||
@statsd_metrics
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
# under the License.
|
||||
# pylint: disable=arguments-renamed
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import List, Optional, TYPE_CHECKING
|
||||
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
|
@ -82,3 +83,32 @@ class ChartDAO(BaseDAO):
|
|||
)
|
||||
.all()
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def add_favorite(chart: Slice) -> None:
|
||||
ids = ChartDAO.favorited_ids([chart])
|
||||
if chart.id not in ids:
|
||||
db.session.add(
|
||||
FavStar(
|
||||
class_name=FavStarClassName.CHART,
|
||||
obj_id=chart.id,
|
||||
user_id=get_user_id(),
|
||||
dttm=datetime.now(),
|
||||
)
|
||||
)
|
||||
db.session.commit()
|
||||
|
||||
@staticmethod
|
||||
def remove_favorite(chart: Slice) -> None:
|
||||
fav = (
|
||||
db.session.query(FavStar)
|
||||
.filter(
|
||||
FavStar.class_name == FavStarClassName.CHART,
|
||||
FavStar.obj_id == chart.id,
|
||||
FavStar.user_id == get_user_id(),
|
||||
)
|
||||
.one_or_none()
|
||||
)
|
||||
if fav:
|
||||
db.session.delete(fav)
|
||||
db.session.commit()
|
||||
|
|
|
|||
|
|
@ -128,6 +128,8 @@ MODEL_API_RW_METHOD_PERMISSION_MAP = {
|
|||
"test_connection": "read",
|
||||
"validate_parameters": "read",
|
||||
"favorite_status": "read",
|
||||
"add_favorite": "read",
|
||||
"remove_favorite": "read",
|
||||
"thumbnail": "read",
|
||||
"import_": "write",
|
||||
"refresh": "write",
|
||||
|
|
|
|||
|
|
@ -141,6 +141,8 @@ class DashboardRestApi(BaseSupersetModelRestApi):
|
|||
RouteMethod.RELATED,
|
||||
"bulk_delete", # not using RouteMethod since locally defined
|
||||
"favorite_status",
|
||||
"add_favorite",
|
||||
"remove_favorite",
|
||||
"get_charts",
|
||||
"get_datasets",
|
||||
"get_embedded",
|
||||
|
|
@ -1001,6 +1003,94 @@ class DashboardRestApi(BaseSupersetModelRestApi):
|
|||
]
|
||||
return self.response(200, result=res)
|
||||
|
||||
@expose("/<pk>/favorites/", methods=["POST"])
|
||||
@protect()
|
||||
@safe
|
||||
@statsd_metrics
|
||||
@event_logger.log_this_with_context(
|
||||
action=lambda self, *args, **kwargs: f"{self.__class__.__name__}"
|
||||
f".add_favorite",
|
||||
log_to_statsd=False,
|
||||
)
|
||||
def add_favorite(self, pk: int) -> Response:
|
||||
"""Marks the dashboard as favorite
|
||||
---
|
||||
post:
|
||||
description: >-
|
||||
Marks the dashboard as favorite for the current user
|
||||
parameters:
|
||||
- in: path
|
||||
schema:
|
||||
type: integer
|
||||
name: pk
|
||||
responses:
|
||||
200:
|
||||
description: Dashboard added to favorites
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
result:
|
||||
type: object
|
||||
401:
|
||||
$ref: '#/components/responses/401'
|
||||
404:
|
||||
$ref: '#/components/responses/404'
|
||||
500:
|
||||
$ref: '#/components/responses/500'
|
||||
"""
|
||||
dashboard = DashboardDAO.find_by_id(pk)
|
||||
if not dashboard:
|
||||
return self.response_404()
|
||||
|
||||
DashboardDAO.add_favorite(dashboard)
|
||||
return self.response(200, result="OK")
|
||||
|
||||
@expose("/<pk>/favorites/", methods=["DELETE"])
|
||||
@protect()
|
||||
@safe
|
||||
@statsd_metrics
|
||||
@event_logger.log_this_with_context(
|
||||
action=lambda self, *args, **kwargs: f"{self.__class__.__name__}"
|
||||
f".remove_favorite",
|
||||
log_to_statsd=False,
|
||||
)
|
||||
def remove_favorite(self, pk: int) -> Response:
|
||||
"""Remove the dashboard from the user favorite list
|
||||
---
|
||||
delete:
|
||||
description: >-
|
||||
Remove the dashboard from the user favorite list
|
||||
parameters:
|
||||
- in: path
|
||||
schema:
|
||||
type: integer
|
||||
name: pk
|
||||
responses:
|
||||
200:
|
||||
description: Dashboard removed from favorites
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
result:
|
||||
type: object
|
||||
401:
|
||||
$ref: '#/components/responses/401'
|
||||
404:
|
||||
$ref: '#/components/responses/404'
|
||||
500:
|
||||
$ref: '#/components/responses/500'
|
||||
"""
|
||||
dashboard = DashboardDAO.find_by_id(pk)
|
||||
if not dashboard:
|
||||
return self.response_404()
|
||||
|
||||
DashboardDAO.remove_favorite(dashboard)
|
||||
return self.response(200, result="OK")
|
||||
|
||||
@expose("/import/", methods=["POST"])
|
||||
@protect()
|
||||
@statsd_metrics
|
||||
|
|
|
|||
|
|
@ -307,3 +307,32 @@ class DashboardDAO(BaseDAO):
|
|||
)
|
||||
.all()
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def add_favorite(dashboard: Dashboard) -> None:
|
||||
ids = DashboardDAO.favorited_ids([dashboard])
|
||||
if dashboard.id not in ids:
|
||||
db.session.add(
|
||||
FavStar(
|
||||
class_name=FavStarClassName.DASHBOARD,
|
||||
obj_id=dashboard.id,
|
||||
user_id=get_user_id(),
|
||||
dttm=datetime.now(),
|
||||
)
|
||||
)
|
||||
db.session.commit()
|
||||
|
||||
@staticmethod
|
||||
def remove_favorite(dashboard: Dashboard) -> None:
|
||||
fav = (
|
||||
db.session.query(FavStar)
|
||||
.filter(
|
||||
FavStar.class_name == FavStarClassName.DASHBOARD,
|
||||
FavStar.obj_id == dashboard.id,
|
||||
FavStar.user_id == get_user_id(),
|
||||
)
|
||||
.one_or_none()
|
||||
)
|
||||
if fav:
|
||||
db.session.delete(fav)
|
||||
db.session.commit()
|
||||
|
|
|
|||
|
|
@ -1787,6 +1787,7 @@ class Superset(BaseSupersetView): # pylint: disable=too-many-public-methods
|
|||
@has_access_api
|
||||
@event_logger.log_this
|
||||
@expose("/favstar/<class_name>/<int:obj_id>/<action>/")
|
||||
@deprecated()
|
||||
def favstar( # pylint: disable=no-self-use
|
||||
self, class_name: str, obj_id: int, action: str
|
||||
) -> FlaskResponse:
|
||||
|
|
|
|||
|
|
@ -1252,6 +1252,75 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixin):
|
|||
if res["id"] in users_favorite_ids:
|
||||
assert res["value"]
|
||||
|
||||
def test_add_favorite(self):
|
||||
"""
|
||||
Dataset API: Test add chart to favorites
|
||||
"""
|
||||
chart = Slice(
|
||||
id=100,
|
||||
datasource_id=1,
|
||||
datasource_type="table",
|
||||
datasource_name="tmp_perm_table",
|
||||
slice_name="slice_name",
|
||||
)
|
||||
db.session.add(chart)
|
||||
db.session.commit()
|
||||
|
||||
self.login(username="admin")
|
||||
uri = f"api/v1/chart/favorite_status/?q={prison.dumps([chart.id])}"
|
||||
rv = self.client.get(uri)
|
||||
data = json.loads(rv.data.decode("utf-8"))
|
||||
for res in data["result"]:
|
||||
assert res["value"] is False
|
||||
|
||||
uri = f"api/v1/chart/{chart.id}/favorites/"
|
||||
self.client.post(uri)
|
||||
|
||||
uri = f"api/v1/chart/favorite_status/?q={prison.dumps([chart.id])}"
|
||||
rv = self.client.get(uri)
|
||||
data = json.loads(rv.data.decode("utf-8"))
|
||||
for res in data["result"]:
|
||||
assert res["value"] is True
|
||||
|
||||
db.session.delete(chart)
|
||||
db.session.commit()
|
||||
|
||||
def test_remove_favorite(self):
|
||||
"""
|
||||
Dataset API: Test remove chart from favorites
|
||||
"""
|
||||
chart = Slice(
|
||||
id=100,
|
||||
datasource_id=1,
|
||||
datasource_type="table",
|
||||
datasource_name="tmp_perm_table",
|
||||
slice_name="slice_name",
|
||||
)
|
||||
db.session.add(chart)
|
||||
db.session.commit()
|
||||
|
||||
self.login(username="admin")
|
||||
uri = f"api/v1/chart/{chart.id}/favorites/"
|
||||
self.client.post(uri)
|
||||
|
||||
uri = f"api/v1/chart/favorite_status/?q={prison.dumps([chart.id])}"
|
||||
rv = self.client.get(uri)
|
||||
data = json.loads(rv.data.decode("utf-8"))
|
||||
for res in data["result"]:
|
||||
assert res["value"] is True
|
||||
|
||||
uri = f"api/v1/chart/{chart.id}/favorites/"
|
||||
self.client.delete(uri)
|
||||
|
||||
uri = f"api/v1/chart/favorite_status/?q={prison.dumps([chart.id])}"
|
||||
rv = self.client.get(uri)
|
||||
data = json.loads(rv.data.decode("utf-8"))
|
||||
for res in data["result"]:
|
||||
assert res["value"] is False
|
||||
|
||||
db.session.delete(chart)
|
||||
db.session.commit()
|
||||
|
||||
def test_get_time_range(self):
|
||||
"""
|
||||
Chart API: Test get actually time range from human readable string
|
||||
|
|
|
|||
|
|
@ -720,6 +720,75 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixi
|
|||
if res["id"] in users_favorite_ids:
|
||||
assert res["value"]
|
||||
|
||||
def test_add_favorite(self):
|
||||
"""
|
||||
Dataset API: Test add dashboard to favorites
|
||||
"""
|
||||
dashboard = Dashboard(
|
||||
id=100,
|
||||
dashboard_title="test_dashboard",
|
||||
slug="test_slug",
|
||||
slices=[],
|
||||
published=True,
|
||||
)
|
||||
db.session.add(dashboard)
|
||||
db.session.commit()
|
||||
|
||||
self.login(username="admin")
|
||||
uri = f"api/v1/dashboard/favorite_status/?q={prison.dumps([dashboard.id])}"
|
||||
rv = self.client.get(uri)
|
||||
data = json.loads(rv.data.decode("utf-8"))
|
||||
for res in data["result"]:
|
||||
assert res["value"] is False
|
||||
|
||||
uri = f"api/v1/dashboard/{dashboard.id}/favorites/"
|
||||
self.client.post(uri)
|
||||
|
||||
uri = f"api/v1/dashboard/favorite_status/?q={prison.dumps([dashboard.id])}"
|
||||
rv = self.client.get(uri)
|
||||
data = json.loads(rv.data.decode("utf-8"))
|
||||
for res in data["result"]:
|
||||
assert res["value"] is True
|
||||
|
||||
db.session.delete(dashboard)
|
||||
db.session.commit()
|
||||
|
||||
def test_remove_favorite(self):
|
||||
"""
|
||||
Dataset API: Test remove dashboard from favorites
|
||||
"""
|
||||
dashboard = Dashboard(
|
||||
id=100,
|
||||
dashboard_title="test_dashboard",
|
||||
slug="test_slug",
|
||||
slices=[],
|
||||
published=True,
|
||||
)
|
||||
db.session.add(dashboard)
|
||||
db.session.commit()
|
||||
|
||||
self.login(username="admin")
|
||||
uri = f"api/v1/dashboard/{dashboard.id}/favorites/"
|
||||
self.client.post(uri)
|
||||
|
||||
uri = f"api/v1/dashboard/favorite_status/?q={prison.dumps([dashboard.id])}"
|
||||
rv = self.client.get(uri)
|
||||
data = json.loads(rv.data.decode("utf-8"))
|
||||
for res in data["result"]:
|
||||
assert res["value"] is True
|
||||
|
||||
uri = f"api/v1/dashboard/{dashboard.id}/favorites/"
|
||||
self.client.delete(uri)
|
||||
|
||||
uri = f"api/v1/dashboard/favorite_status/?q={prison.dumps([dashboard.id])}"
|
||||
rv = self.client.get(uri)
|
||||
data = json.loads(rv.data.decode("utf-8"))
|
||||
for res in data["result"]:
|
||||
assert res["value"] is False
|
||||
|
||||
db.session.delete(dashboard)
|
||||
db.session.commit()
|
||||
|
||||
@pytest.mark.usefixtures("create_dashboards")
|
||||
def test_get_dashboards_not_favorite_filter(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -65,3 +65,36 @@ def test_datasource_find_by_id_skip_base_filter_not_found(
|
|||
125326326, session=session_with_data, skip_base_filter=True
|
||||
)
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_add_favorite(session_with_data: Session) -> None:
|
||||
from superset.charts.dao import ChartDAO
|
||||
|
||||
chart = ChartDAO.find_by_id(1, session=session_with_data, skip_base_filter=True)
|
||||
if not chart:
|
||||
return
|
||||
assert len(ChartDAO.favorited_ids([chart])) == 0
|
||||
|
||||
ChartDAO.add_favorite(chart)
|
||||
assert len(ChartDAO.favorited_ids([chart])) == 1
|
||||
|
||||
ChartDAO.add_favorite(chart)
|
||||
assert len(ChartDAO.favorited_ids([chart])) == 1
|
||||
|
||||
|
||||
def test_remove_favorite(session_with_data: Session) -> None:
|
||||
from superset.charts.dao import ChartDAO
|
||||
|
||||
chart = ChartDAO.find_by_id(1, session=session_with_data, skip_base_filter=True)
|
||||
if not chart:
|
||||
return
|
||||
assert len(ChartDAO.favorited_ids([chart])) == 0
|
||||
|
||||
ChartDAO.add_favorite(chart)
|
||||
assert len(ChartDAO.favorited_ids([chart])) == 1
|
||||
|
||||
ChartDAO.remove_favorite(chart)
|
||||
assert len(ChartDAO.favorited_ids([chart])) == 0
|
||||
|
||||
ChartDAO.remove_favorite(chart)
|
||||
assert len(ChartDAO.favorited_ids([chart])) == 0
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from typing import Iterator
|
||||
|
||||
import pytest
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def session_with_data(session: Session) -> Iterator[Session]:
|
||||
from superset.models.dashboard import Dashboard
|
||||
|
||||
engine = session.get_bind()
|
||||
Dashboard.metadata.create_all(engine) # pylint: disable=no-member
|
||||
|
||||
dashboard_obj = Dashboard(
|
||||
id=100,
|
||||
dashboard_title="test_dashboard",
|
||||
slug="test_slug",
|
||||
slices=[],
|
||||
published=True,
|
||||
)
|
||||
|
||||
session.add(dashboard_obj)
|
||||
session.commit()
|
||||
yield session
|
||||
session.rollback()
|
||||
|
||||
|
||||
def test_add_favorite(session_with_data: Session) -> None:
|
||||
from superset.dashboards.dao import DashboardDAO
|
||||
|
||||
dashboard = DashboardDAO.find_by_id(
|
||||
100, session=session_with_data, skip_base_filter=True
|
||||
)
|
||||
if not dashboard:
|
||||
return
|
||||
assert len(DashboardDAO.favorited_ids([dashboard])) == 0
|
||||
|
||||
DashboardDAO.add_favorite(dashboard)
|
||||
assert len(DashboardDAO.favorited_ids([dashboard])) == 1
|
||||
|
||||
DashboardDAO.add_favorite(dashboard)
|
||||
assert len(DashboardDAO.favorited_ids([dashboard])) == 1
|
||||
|
||||
|
||||
def test_remove_favorite(session_with_data: Session) -> None:
|
||||
from superset.dashboards.dao import DashboardDAO
|
||||
|
||||
dashboard = DashboardDAO.find_by_id(
|
||||
100, session=session_with_data, skip_base_filter=True
|
||||
)
|
||||
if not dashboard:
|
||||
return
|
||||
assert len(DashboardDAO.favorited_ids([dashboard])) == 0
|
||||
|
||||
DashboardDAO.add_favorite(dashboard)
|
||||
assert len(DashboardDAO.favorited_ids([dashboard])) == 1
|
||||
|
||||
DashboardDAO.remove_favorite(dashboard)
|
||||
assert len(DashboardDAO.favorited_ids([dashboard])) == 0
|
||||
|
||||
DashboardDAO.remove_favorite(dashboard)
|
||||
assert len(DashboardDAO.favorited_ids([dashboard])) == 0
|
||||
Loading…
Reference in New Issue