Skip to content

Commit

Permalink
Addressing PR feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
Vitor-Avila committed May 28, 2024
1 parent 26603e2 commit 31aeaae
Show file tree
Hide file tree
Showing 13 changed files with 315 additions and 189 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 @@ -110,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 @@ -362,13 +361,14 @@ const PropertiesModal = ({
});

const moreOnSubmitProps: { roles?: Roles } = {};
const morePutProps: { roles?: number[]; tags?: string[] } = {};
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(r => r.name);
morePutProps.tags = tags.map(tag => tag.id);

Check warning on line 371 in superset-frontend/src/dashboard/components/PropertiesModal/index.tsx

View check run for this annotation

Codecov / codecov/patch

superset-frontend/src/dashboard/components/PropertiesModal/index.tsx#L371

Added line #L371 was not covered by tests
}
const onSubmitProps = {
id: dashboardId,
Expand Down Expand Up @@ -565,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 => ({

Check warning on line 569 in superset-frontend/src/dashboard/components/PropertiesModal/index.tsx

View check run for this annotation

Codecov / codecov/patch

superset-frontend/src/dashboard/components/PropertiesModal/index.tsx#L569

Added line #L569 was not covered by tests
id: r.value,
name: r.label,
}));
setTags(parsedTags);

Check warning on line 573 in superset-frontend/src/dashboard/components/PropertiesModal/index.tsx

View check run for this annotation

Codecov / codecov/patch

superset-frontend/src/dashboard/components/PropertiesModal/index.tsx#L573

Added line #L573 was not covered by tests
};

return (
Expand Down
20 changes: 10 additions & 10 deletions superset-frontend/src/explore/components/PropertiesModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
isFeatureEnabled,
FeatureFlag,
getClientErrorObject,
ensureIsArray,
} from '@superset-ui/core';
import Chart, { Slice } from 'src/types/Chart';
import withToasts from 'src/components/MessageToasts/withToasts';
Expand Down Expand Up @@ -75,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 @@ -169,7 +169,7 @@ function PropertiesModal({
).map(o => o.value);
}
if (isFeatureEnabled(FeatureFlag.TaggingSystem)) {
payload.tags = tags.map(tag => tag.name);
payload.tags = tags.map(tag => tag.id);

Check warning on line 172 in superset-frontend/src/explore/components/PropertiesModal/index.tsx

View check run for this annotation

Codecov / codecov/patch

superset-frontend/src/explore/components/PropertiesModal/index.tsx#L172

Added line #L172 was not covered by tests
}

try {
Expand Down Expand Up @@ -227,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 => ({

Check warning on line 231 in superset-frontend/src/explore/components/PropertiesModal/index.tsx

View check run for this annotation

Codecov / codecov/patch

superset-frontend/src/explore/components/PropertiesModal/index.tsx#L231

Added line #L231 was not covered by tests
id: r.value,
name: r.label,
}));
setTags(parsedTags);

Check warning on line 235 in superset-frontend/src/explore/components/PropertiesModal/index.tsx

View check run for this annotation

Codecov / codecov/patch

superset-frontend/src/explore/components/PropertiesModal/index.tsx#L235

Added line #L235 was not covered by tests
};

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
11 changes: 1 addition & 10 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 @@ -144,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 @@ -285,9 +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.List(
fields.String(metadata={"description": tags_description}, allow_none=True)
)
tags = fields.List(fields.Integer(metadata={"description": tags_description}))


class ChartGetDatasourceObjectDataResponseSchema(Schema):
Expand Down
4 changes: 2 additions & 2 deletions superset/commands/chart/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def validate(self) -> None:
exceptions: list[ValidationError] = []
dashboard_ids = self._properties.get("dashboards")
owner_ids: Optional[list[int]] = self._properties.get("owners")
tag_names: Optional[list[str]] = self._properties.get("tags")
tag_ids: Optional[list[int]] = self._properties.get("tags")

# Validate if datasource_id is provided datasource_type is required
datasource_id = self._properties.get("datasource_id")
Expand Down Expand Up @@ -109,7 +109,7 @@ def validate(self) -> None:

# validate tags
try:
validate_tags(self._model.__class__.__name__, self._model.tags, tag_names)
validate_tags(ObjectType.chart, self._model.tags, tag_ids)
except ValidationError as ex:
exceptions.append(ex)

Check warning on line 114 in superset/commands/chart/update.py

View check run for this annotation

Codecov / codecov/patch

superset/commands/chart/update.py#L114

Added line #L114 was not covered by tests

Expand Down
4 changes: 2 additions & 2 deletions superset/commands/dashboard/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def validate(self) -> None:
owner_ids: Optional[list[int]] = self._properties.get("owners")
roles_ids: Optional[list[int]] = self._properties.get("roles")
slug: Optional[str] = self._properties.get("slug")
tag_names: Optional[list[str]] = self._properties.get("tags")
tag_ids: Optional[list[int]] = self._properties.get("tags")

# Validate/populate model exists
self._model = DashboardDAO.find_by_id(self._model_id)
Expand Down Expand Up @@ -105,7 +105,7 @@ def validate(self) -> None:

# validate tags
try:
validate_tags(self._model.__class__.__name__, self._model.tags, tag_names)
validate_tags(ObjectType.dashboard, self._model.tags, tag_ids)
except ValidationError as ex:
exceptions.append(ex)

Check warning on line 110 in superset/commands/dashboard/update.py

View check run for this annotation

Codecov / codecov/patch

superset/commands/dashboard/update.py#L110

Added line #L110 was not covered by tests

Expand Down
60 changes: 31 additions & 29 deletions superset/commands/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ def get_datasource_by_id(datasource_id: int, datasource_type: str) -> BaseDataso


def validate_tags(
object_type: str,
object_type: ObjectType,
current_tags: list[Tag],
new_tags: Optional[list[str]],
new_tag_ids: Optional[list[int]],
) -> None:
"""
Helper function for update commands, to validate the tags list. Users
Expand All @@ -121,38 +121,33 @@ def validate_tags(
only allowed to manage existing existing tags' associations with the object.
:param current_tags: list of current tags
:param new_tags: list of tags specified in the update payload
:param new_tag_ids: list of tags specified in the update payload
"""

# Classname override
class_overrides = {"Slice": "Chart"}
object_type = class_overrides.get(object_type, object_type)

# `tags` not part of the update payload
if new_tags is None:
return

# User has perm to create new tags
if security_manager.can_access("can_write", "Tag"):
if new_tag_ids is None:
return

# No changes in the list
custom_tags = [tag.name for tag in current_tags if tag.type == TagType.custom]
if Counter(custom_tags) == Counter(new_tags):
current_custom_tags = [tag.id for tag in current_tags if tag.type == TagType.custom]
if Counter(current_custom_tags) == Counter(new_tag_ids):
return

# No perm to tags assets
if not security_manager.can_access("can_tag", object_type):
if not (
security_manager.can_access("can_write", "Tag")
or security_manager.can_access("can_tag", object_type.name.capitalize())
):
validation_error = (
f"You do not have permission to manage tags on {object_type.lower()}s"
f"You do not have permission to manage tags on {object_type.name}s"
)
raise TagForbiddenError(validation_error)

# Validate if new tags already exist
additional_tags = [tag for tag in new_tags if tag not in custom_tags]
for tag in additional_tags:
if not TagDAO.find_by_name(tag):
validation_error = f"Tag not found: {tag}"
additional_tags = [tag for tag in new_tag_ids if tag not in current_custom_tags]
for tag_id in additional_tags:
if not TagDAO.find_by_id(tag_id):
validation_error = f"Tag ID {tag_id} not found"
raise TagNotFoundValidationError(validation_error)

return
Expand All @@ -162,24 +157,31 @@ def update_tags(
object_type: ObjectType,
object_id: int,
current_tags: list[Tag],
new_tags: list[str],
new_tag_ids: list[int],
) -> None:
"""
Helper function for update commands, to update the tag relationship.
:param object_id: The object (dashboard, chart, etc) ID
:param object_type: The object type
:param current_tags: list of current tags
:param new_tags: list of tags specified in the update payload
:param new_tag_ids: list of tags specified in the update payload
"""

current_custom_tags = [
tag.name for tag in current_tags if tag.type == TagType.custom
current_custom_tags = [tag for tag in current_tags if tag.type == TagType.custom]
current_custom_tag_ids = [
tag.id for tag in current_tags if tag.type == TagType.custom
]
tags_to_delete = [tag for tag in current_custom_tags if tag not in new_tags]
tags_to_add = [tag for tag in new_tags if tag not in current_custom_tags]

tags_to_delete = [tag for tag in current_custom_tags if tag.id not in new_tag_ids]
for tag in tags_to_delete:
TagDAO.delete_tagged_object(object_type, object_id, tag)
if tags_to_add:
TagDAO.create_custom_tagged_objects(object_type, object_id, tags_to_add)
TagDAO.delete_tagged_object(object_type, object_id, tag.name)

tag_ids_to_add = [
tag_id for tag_id in new_tag_ids if tag_id not in current_custom_tag_ids
]
if tag_ids_to_add:
tags_to_add = TagDAO.find_by_ids(tag_ids_to_add)
TagDAO.create_custom_tagged_objects(
object_type, object_id, [tag.name for tag in tags_to_add]
)
2 changes: 1 addition & 1 deletion superset/dashboards/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ class DashboardPutSchema(BaseDashboardSchema):
is_managed_externally = fields.Boolean(allow_none=True, dump_default=False)
external_url = fields.String(allow_none=True)
tags = fields.List(
fields.String(metadata={"description": tags_description}, allow_none=True)
fields.Integer(metadata={"description": tags_description}, allow_none=True)
)


Expand Down
Loading

0 comments on commit 31aeaae

Please sign in to comment.