Skip to content

Commit

Permalink
chore(tags): Handle tagging as part of asset update call (#28570)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vitor-Avila authored May 28, 2024
1 parent 4753642 commit 0fdb4b7
Show file tree
Hide file tree
Showing 17 changed files with 1,075 additions and 167 deletions.
6 changes: 3 additions & 3 deletions superset-frontend/src/components/Tags/utils.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ import { tagToSelectOption } from 'src/components/Tags/utils';
describe('tagToSelectOption', () => {
test('converts a Tag object with table_name to a SelectTagsValue', () => {
const tag = {
id: '1',
id: 1,
name: 'TagName',
table_name: 'Table1',
};

const expectedSelectTagsValue = {
value: 'TagName',
value: 1,
label: 'TagName',
key: '1',
key: 1,
};

expect(tagToSelectOption(tag)).toEqual(expectedSelectTagsValue);
Expand Down
14 changes: 7 additions & 7 deletions superset-frontend/src/components/Tags/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,17 @@ const cachedSupersetGet = cacheWrapper(
);

type SelectTagsValue = {
value: string | number | undefined;
label: string;
key: string | number | undefined;
value: number | undefined;
label: string | undefined;
key: number | undefined;
};

export const tagToSelectOption = (
item: Tag & { table_name: string },
tag: Tag & { table_name: string },
): SelectTagsValue => ({
value: item.name,
label: item.name,
key: item.id,
value: tag.id,
label: tag.name,
key: tag.id,
});

export const loadTags = async (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,7 @@ import ColorSchemeControlWrapper from 'src/dashboard/components/ColorSchemeContr
import FilterScopeModal from 'src/dashboard/components/filterscope/FilterScopeModal';
import withToasts from 'src/components/MessageToasts/withToasts';
import TagType from 'src/types/TagType';
import {
addTag,
deleteTaggedObjects,
fetchTags,
OBJECT_TYPES,
} from 'src/features/tags/tags';
import { fetchTags, OBJECT_TYPES } from 'src/features/tags/tags';
import { loadTags } from 'src/components/Tags/utils';

const StyledFormItem = styled(FormItem)`
Expand Down Expand Up @@ -115,10 +110,9 @@ const PropertiesModal = ({
const categoricalSchemeRegistry = getCategoricalSchemeRegistry();

const tagsAsSelectValues = useMemo(() => {
const selectTags = tags.map(tag => ({
value: tag.name,
const selectTags = tags.map((tag: { id: number; name: string }) => ({
value: tag.id,
label: tag.name,
key: tag.name,
}));
return selectTags;
}, [tags.length]);
Expand Down Expand Up @@ -309,41 +303,6 @@ const PropertiesModal = ({
setColorScheme(colorScheme);
};

const updateTags = (oldTags: TagType[], newTags: TagType[]) => {
// update the tags for this object
// add tags that are in new tags, but not in old tags
// eslint-disable-next-line array-callback-return
newTags.map((tag: TagType) => {
if (!oldTags.some(t => t.name === tag.name)) {
addTag(
{
objectType: OBJECT_TYPES.DASHBOARD,
objectId: dashboardId,
includeTypes: false,
},
tag.name,
() => {},
() => {},
);
}
});
// delete tags that are in old tags, but not in new tags
// eslint-disable-next-line array-callback-return
oldTags.map((tag: TagType) => {
if (!newTags.some(t => t.name === tag.name)) {
deleteTaggedObjects(
{
objectType: OBJECT_TYPES.DASHBOARD,
objectId: dashboardId,
},
tag,
() => {},
() => {},
);
}
});
};

const onFinish = () => {
const { title, slug, certifiedBy, certificationDetails } =
form.getFieldsValue();
Expand Down Expand Up @@ -401,31 +360,16 @@ const PropertiesModal = ({
updateMetadata: false,
});

if (isFeatureEnabled(FeatureFlag.TaggingSystem)) {
// update tags
try {
fetchTags(
{
objectType: OBJECT_TYPES.DASHBOARD,
objectId: dashboardId,
includeTypes: false,
},
(currentTags: TagType[]) => updateTags(currentTags, tags),
error => {
handleErrorResponse(error);
},
);
} catch (error) {
handleErrorResponse(error);
}
}

const moreOnSubmitProps: { roles?: Roles } = {};
const morePutProps: { roles?: number[] } = {};
const morePutProps: { roles?: number[]; tags?: (number | undefined)[] } =
{};
if (isFeatureEnabled(FeatureFlag.DashboardRbac)) {
moreOnSubmitProps.roles = roles;
morePutProps.roles = (roles || []).map(r => r.id);
}
if (isFeatureEnabled(FeatureFlag.TaggingSystem)) {
morePutProps.tags = tags.map(tag => tag.id);
}
const onSubmitProps = {
id: dashboardId,
title,
Expand Down Expand Up @@ -621,12 +565,12 @@ const PropertiesModal = ({
}
}, [dashboardId]);

const handleChangeTags = (values: { label: string; value: number }[]) => {
// triggered whenever a new tag is selected or a tag was deselected
// on new tag selected, add the tag

const uniqueTags = [...new Set(values.map(v => v.label))];
setTags([...uniqueTags.map(t => ({ name: t }))]);
const handleChangeTags = (tags: { label: string; value: number }[]) => {
const parsedTags: TagType[] = ensureIsArray(tags).map(r => ({
id: r.value,
name: r.label,
}));
setTags(parsedTags);
};

return (
Expand Down
77 changes: 11 additions & 66 deletions superset-frontend/src/explore/components/PropertiesModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,12 @@ import {
isFeatureEnabled,
FeatureFlag,
getClientErrorObject,
ensureIsArray,
} from '@superset-ui/core';
import Chart, { Slice } from 'src/types/Chart';
import withToasts from 'src/components/MessageToasts/withToasts';
import { loadTags } from 'src/components/Tags/utils';
import {
addTag,
deleteTaggedObjects,
fetchTags,
OBJECT_TYPES,
} from 'src/features/tags/tags';
import { fetchTags, OBJECT_TYPES } from 'src/features/tags/tags';
import TagType from 'src/types/TagType';

export type PropertiesModalProps = {
Expand Down Expand Up @@ -80,10 +76,9 @@ function PropertiesModal({
const [tags, setTags] = useState<TagType[]>([]);

const tagsAsSelectValues = useMemo(() => {
const selectTags = tags.map(tag => ({
value: tag.name,
const selectTags = tags.map((tag: { id: number; name: string }) => ({
value: tag.id,
label: tag.name,
key: tag.name,
}));
return selectTags;
}, [tags.length]);
Expand Down Expand Up @@ -144,41 +139,6 @@ function PropertiesModal({
[],
);

const updateTags = (oldTags: TagType[], newTags: TagType[]) => {
// update the tags for this object
// add tags that are in new tags, but not in old tags
// eslint-disable-next-line array-callback-return
newTags.map((tag: TagType) => {
if (!oldTags.some(t => t.name === tag.name)) {
addTag(
{
objectType: OBJECT_TYPES.CHART,
objectId: slice.slice_id,
includeTypes: false,
},
tag.name,
() => {},
() => {},
);
}
});
// delete tags that are in old tags, but not in new tags
// eslint-disable-next-line array-callback-return
oldTags.map((tag: TagType) => {
if (!newTags.some(t => t.name === tag.name)) {
deleteTaggedObjects(
{
objectType: OBJECT_TYPES.CHART,
objectId: slice.slice_id,
},
tag,
() => {},
() => {},
);
}
});
};

const onSubmit = async (values: {
certified_by?: string;
certification_details?: string;
Expand Down Expand Up @@ -209,22 +169,7 @@ function PropertiesModal({
).map(o => o.value);
}
if (isFeatureEnabled(FeatureFlag.TaggingSystem)) {
// update tags
try {
fetchTags(
{
objectType: OBJECT_TYPES.CHART,
objectId: slice.slice_id,
includeTypes: false,
},
(currentTags: TagType[]) => updateTags(currentTags, tags),
error => {
showError(error);
},
);
} catch (error) {
showError(error);
}
payload.tags = tags.map(tag => tag.id);
}

try {
Expand Down Expand Up @@ -282,12 +227,12 @@ function PropertiesModal({
}
}, [slice.slice_id]);

const handleChangeTags = (values: { label: string; value: number }[]) => {
// triggered whenever a new tag is selected or a tag was deselected
// on new tag selected, add the tag

const uniqueTags = [...new Set(values.map(v => v.label))];
setTags([...uniqueTags.map(t => ({ name: t }))]);
const handleChangeTags = (tags: { label: string; value: number }[]) => {
const parsedTags: TagType[] = ensureIsArray(tags).map(r => ({
id: r.value,
name: r.label,
}));
setTags(parsedTags);
};

const handleClearTags = () => {
Expand Down
2 changes: 1 addition & 1 deletion superset-frontend/src/types/TagType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import { MouseEventHandler } from 'react';

export interface TagType {
id?: string | number;
id?: number;
type?: string | number;
editable?: boolean;
onDelete?: (index: number) => void;
Expand Down
4 changes: 3 additions & 1 deletion superset/charts/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
from superset.commands.chart.importers.dispatcher import ImportChartsCommand
from superset.commands.chart.update import UpdateChartCommand
from superset.commands.chart.warm_up_cache import ChartWarmUpCacheCommand
from superset.commands.exceptions import CommandException
from superset.commands.exceptions import CommandException, TagForbiddenError
from superset.commands.importers.exceptions import (
IncorrectFormatError,
NoValidFilesFoundError,
Expand Down Expand Up @@ -404,6 +404,8 @@ def put(self, pk: int) -> Response:
response = self.response_404()
except ChartForbiddenError:
response = self.response_403()
except TagForbiddenError as ex:
response = self.response(403, message=str(ex))
except ChartInvalidError as ex:
response = self.response_422(message=ex.normalized_messages())
except ChartUpdateFailedError as ex:
Expand Down
10 changes: 2 additions & 8 deletions superset/charts/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
from superset import app
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 @@ -122,6 +121,7 @@
owners_name_description = "Name of an owner of the chart."
certified_by_description = "Person or group that has certified this chart"
certification_details_description = "Details of the certification"
tags_description = "Tags to be associated with the chart"

openapi_spec_methods_override = {
"get": {"get": {"summary": "Get a chart detail information"}},
Expand All @@ -143,12 +143,6 @@
}


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


class ChartEntityResponseSchema(Schema):
"""
Schema for a chart object
Expand Down Expand Up @@ -284,7 +278,7 @@ class ChartPutSchema(Schema):
)
is_managed_externally = fields.Boolean(allow_none=True, dump_default=False)
external_url = fields.String(allow_none=True)
tags = fields.Nested(TagSchema, many=True)
tags = fields.List(fields.Integer(metadata={"description": tags_description}))


class ChartGetDatasourceObjectDataResponseSchema(Schema):
Expand Down
Loading

0 comments on commit 0fdb4b7

Please sign in to comment.