feat: Update Tags CRUD API (#24839)

This commit is contained in:
Hugh A. Miles II
2023-08-25 21:16:35 +02:00
committed by GitHub
parent 5168475826
commit 69fb309ec3
16 changed files with 1109 additions and 22 deletions

View File

@@ -53,6 +53,7 @@ TAGS_LIST_COLUMNS = [
"id",
"name",
"type",
"description",
"changed_by.first_name",
"changed_by.last_name",
"changed_on_delta_humanized",
@@ -457,3 +458,46 @@ class TestTagApi(SupersetTestCase):
rv = self.client.delete(uri, follow_redirects=True)
self.assertEqual(rv.status_code, 422)
@pytest.mark.usefixtures("load_world_bank_dashboard_with_slices")
def test_post_tag(self):
self.login(username="admin")
uri = f"api/v1/tag/"
dashboard = (
db.session.query(Dashboard)
.filter(Dashboard.dashboard_title == "World Bank's Data")
.first()
)
rv = self.client.post(
uri,
json={"name": "my_tag", "objects_to_tag": [["dashboard", dashboard.id]]},
)
self.assertEqual(rv.status_code, 201)
user_id = self.get_user(username="admin").get_id()
tag = (
db.session.query(Tag)
.filter(Tag.name == "my_tag", Tag.type == TagTypes.custom)
.one_or_none()
)
assert tag is not None
@pytest.mark.usefixtures("load_world_bank_dashboard_with_slices")
@pytest.mark.usefixtures("create_tags")
def test_put_tag(self):
self.login(username="admin")
tag_to_update = db.session.query(Tag).first()
uri = f"api/v1/tag/{tag_to_update.id}"
rv = self.client.put(
uri, json={"name": "new_name", "description": "new description"}
)
self.assertEqual(rv.status_code, 200)
tag = (
db.session.query(Tag)
.filter(Tag.name == "new_name", Tag.description == "new description")
.one_or_none()
)
assert tag is not None

View File

@@ -144,3 +144,31 @@ def test_user_favorite_tag_exc_raise(mocker):
mock_session.commit.side_effect = Exception("DB Error")
with pytest.raises(Exception):
TagDAO.remove_user_favorite_tag(1)
def test_create_tag_relationship(mocker):
from superset.daos.tag import TagDAO
from superset.tags.models import ( # Assuming these are defined in the same module
ObjectTypes,
TaggedObject,
)
mock_session = mocker.patch("superset.daos.tag.db.session")
# Define a list of objects to tag
objects_to_tag = [
(ObjectTypes.query, 1),
(ObjectTypes.chart, 2),
(ObjectTypes.dashboard, 3),
]
# Call the function
tag = TagDAO.get_by_name("test_tag")
TagDAO.create_tag_relationship(objects_to_tag, tag)
# Verify that the correct number of TaggedObjects are added to the session
assert mock_session.add_all.call_count == 1
assert len(mock_session.add_all.call_args[0][0]) == len(objects_to_tag)
# Verify that commit is called
mock_session.commit.assert_called_once()

View File

View File

@@ -0,0 +1,110 @@
import pytest
from sqlalchemy.orm.session import Session
from superset.utils.core import DatasourceType
@pytest.fixture
def session_with_data(session: Session):
from superset.connectors.sqla.models import SqlaTable, TableColumn
from superset.models.core import Database
from superset.models.dashboard import Dashboard
from superset.models.slice import Slice
from superset.models.sql_lab import Query, SavedQuery
engine = session.get_bind()
SqlaTable.metadata.create_all(engine) # pylint: disable=no-member
slice_obj = Slice(
id=1,
datasource_id=1,
datasource_type=DatasourceType.TABLE,
datasource_name="tmp_perm_table",
slice_name="slice_name",
)
db = Database(database_name="my_database", sqlalchemy_uri="postgresql://")
columns = [
TableColumn(column_name="a", type="INTEGER"),
]
saved_query = SavedQuery(label="test_query", database=db, sql="select * from foo")
dashboard_obj = Dashboard(
id=100,
dashboard_title="test_dashboard",
slug="test_slug",
slices=[],
published=True,
)
session.add(slice_obj)
session.add(db)
session.add(saved_query)
session.add(dashboard_obj)
session.commit()
yield session
def test_create_command_success(session_with_data: Session):
from superset.connectors.sqla.models import SqlaTable
from superset.daos.tag import TagDAO
from superset.models.dashboard import Dashboard
from superset.models.slice import Slice
from superset.models.sql_lab import Query, SavedQuery
from superset.tags.commands.create import CreateCustomTagWithRelationshipsCommand
from superset.tags.models import ObjectTypes, TaggedObject
# Define a list of objects to tag
query = session_with_data.query(SavedQuery).first()
chart = session_with_data.query(Slice).first()
dashboard = session_with_data.query(Dashboard).first()
objects_to_tag = [
(ObjectTypes.query, query.id),
(ObjectTypes.chart, chart.id),
(ObjectTypes.dashboard, dashboard.id),
]
CreateCustomTagWithRelationshipsCommand(
data={"name": "test_tag", "objects_to_tag": objects_to_tag}
).run()
assert len(session_with_data.query(TaggedObject).all()) == len(objects_to_tag)
for object_type, object_id in objects_to_tag:
assert (
session_with_data.query(TaggedObject)
.filter(
TaggedObject.object_type == object_type,
TaggedObject.object_id == object_id,
)
.one_or_none()
is not None
)
def test_create_command_failed_validate(session_with_data: Session):
from superset.connectors.sqla.models import SqlaTable
from superset.daos.tag import TagDAO
from superset.models.dashboard import Dashboard
from superset.models.slice import Slice
from superset.models.sql_lab import Query, SavedQuery
from superset.tags.commands.create import CreateCustomTagWithRelationshipsCommand
from superset.tags.commands.exceptions import TagInvalidError
from superset.tags.models import ObjectTypes, TaggedObject
query = session_with_data.query(SavedQuery).first()
chart = session_with_data.query(Slice).first()
dashboard = session_with_data.query(Dashboard).first()
objects_to_tag = [
(ObjectTypes.query, query.id),
(ObjectTypes.chart, chart.id),
(ObjectTypes.dashboard, 0),
]
with pytest.raises(TagInvalidError):
CreateCustomTagWithRelationshipsCommand(
data={"name": "test_tag", "objects_to_tag": objects_to_tag}
).run()

View File

@@ -0,0 +1,160 @@
import pytest
from sqlalchemy.orm.session import Session
from superset.utils.core import DatasourceType
@pytest.fixture
def session_with_data(session: Session):
from superset.connectors.sqla.models import SqlaTable, TableColumn
from superset.models.core import Database
from superset.models.dashboard import Dashboard
from superset.models.slice import Slice
from superset.models.sql_lab import Query, SavedQuery
from superset.tags.models import Tag
engine = session.get_bind()
Tag.metadata.create_all(engine) # pylint: disable=no-member
slice_obj = Slice(
id=1,
datasource_id=1,
datasource_type=DatasourceType.TABLE,
datasource_name="tmp_perm_table",
slice_name="slice_name",
)
db = Database(database_name="my_database", sqlalchemy_uri="postgresql://")
columns = [
TableColumn(column_name="a", type="INTEGER"),
]
sqla_table = SqlaTable(
table_name="my_sqla_table",
columns=columns,
metrics=[],
database=db,
)
dashboard_obj = Dashboard(
id=100,
dashboard_title="test_dashboard",
slug="test_slug",
slices=[],
published=True,
)
saved_query = SavedQuery(label="test_query", database=db, sql="select * from foo")
tag = Tag(name="test_name", description="test_description")
session.add(slice_obj)
session.add(dashboard_obj)
session.add(tag)
session.commit()
yield session
def test_update_command_success(session_with_data: Session):
from superset.daos.tag import TagDAO
from superset.models.dashboard import Dashboard
from superset.tags.commands.update import UpdateTagCommand
from superset.tags.models import ObjectTypes, TaggedObject
dashboard = session_with_data.query(Dashboard).first()
objects_to_tag = [
(ObjectTypes.dashboard, dashboard.id),
]
tag_to_update = TagDAO.find_by_name("test_name")
changed_model = UpdateTagCommand(
tag_to_update.id,
{
"name": "new_name",
"description": "new_description",
"objects_to_tag": objects_to_tag,
},
).run()
updated_tag = TagDAO.find_by_name("new_name")
assert updated_tag is not None
assert updated_tag.description == "new_description"
assert len(session_with_data.query(TaggedObject).all()) == len(objects_to_tag)
def test_update_command_success_duplicates(session_with_data: Session):
from superset.daos.tag import TagDAO
from superset.models.dashboard import Dashboard
from superset.models.slice import Slice
from superset.tags.commands.create import CreateCustomTagWithRelationshipsCommand
from superset.tags.commands.update import UpdateTagCommand
from superset.tags.models import ObjectTypes, TaggedObject
dashboard = session_with_data.query(Dashboard).first()
chart = session_with_data.query(Slice).first()
objects_to_tag = [
(ObjectTypes.dashboard, dashboard.id),
]
CreateCustomTagWithRelationshipsCommand(
data={"name": "test_tag", "objects_to_tag": objects_to_tag}
).run()
tag_to_update = TagDAO.find_by_name("test_tag")
objects_to_tag = [
(ObjectTypes.chart, chart.id),
]
changed_model = UpdateTagCommand(
tag_to_update.id,
{
"name": "new_name",
"description": "new_description",
"objects_to_tag": objects_to_tag,
},
).run()
updated_tag = TagDAO.find_by_name("new_name")
assert updated_tag is not None
assert updated_tag.description == "new_description"
assert len(session_with_data.query(TaggedObject).all()) == len(objects_to_tag)
assert changed_model.objects[0].object_id == chart.id
def test_update_command_failed_validation(session_with_data: Session):
from superset.daos.tag import TagDAO
from superset.models.dashboard import Dashboard
from superset.models.slice import Slice
from superset.tags.commands.create import CreateCustomTagWithRelationshipsCommand
from superset.tags.commands.exceptions import TagInvalidError
from superset.tags.commands.update import UpdateTagCommand
from superset.tags.models import ObjectTypes, TaggedObject
dashboard = session_with_data.query(Dashboard).first()
chart = session_with_data.query(Slice).first()
objects_to_tag = [
(ObjectTypes.chart, chart.id),
]
CreateCustomTagWithRelationshipsCommand(
data={"name": "test_tag", "objects_to_tag": objects_to_tag}
).run()
tag_to_update = TagDAO.find_by_name("test_tag")
objects_to_tag = [
(0, dashboard.id), # type: ignore
]
with pytest.raises(TagInvalidError):
UpdateTagCommand(
tag_to_update.id,
{
"name": "new_name",
"description": "new_description",
"objects_to_tag": objects_to_tag,
},
).run()