mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-19 22:49:00 +00:00
parent
8f30c7b23f
commit
a0b50d328a
@ -145,8 +145,21 @@ CREATE TABLE IF NOT EXISTS database_schema_entity (
|
|||||||
INDEX (updatedAt)
|
INDEX (updatedAt)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Drop indexes for deleted boolean column
|
||||||
|
-- Drop unused indexes for updatedAt and updatedBy
|
||||||
|
--
|
||||||
|
RENAME TABLE airflow_pipeline_entity to ingestion_pipeline_entity;
|
||||||
|
|
||||||
ALTER TABLE tag_category
|
ALTER TABLE tag_category
|
||||||
ADD COLUMN id VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.id') STORED NOT NULL FIRST;
|
ADD COLUMN id VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.id') STORED NOT NULL FIRST;
|
||||||
|
|
||||||
|
UPDATE tag_category
|
||||||
|
SET json = JSON_SET(json, '$.id', UUID());
|
||||||
|
|
||||||
ALTER TABLE tag
|
ALTER TABLE tag
|
||||||
ADD COLUMN id VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.id') STORED NOT NULL FIRST;
|
ADD COLUMN id VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.id') STORED NOT NULL FIRST;
|
||||||
|
|
||||||
|
UPDATE tag
|
||||||
|
SET json = JSON_SET(json, '$.id', UUID());
|
||||||
|
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
--
|
|
||||||
-- Drop indexes for deleted boolean column
|
|
||||||
-- Drop unused indexes for updatedAt and updatedBy
|
|
||||||
--
|
|
||||||
RENAME TABLE airflow_pipeline_entity to ingestion_pipeline_entity;
|
|
@ -1252,6 +1252,9 @@ public interface CollectionDAO {
|
|||||||
default EntityReference getEntityReference(Tag entity) {
|
default EntityReference getEntityReference(Tag entity) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SqlUpdate("DELETE FROM tag where fullyQualifiedName LIKE CONCAT(:fqnPrefix, '.%')")
|
||||||
|
void deleteTagsByPrefix(@Bind("fqnPrefix") String fqnPrefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RegisterRowMapper(TagLabelMapper.class)
|
@RegisterRowMapper(TagLabelMapper.class)
|
||||||
@ -1276,10 +1279,13 @@ public interface CollectionDAO {
|
|||||||
int getTagCount(@Bind("source") int source, @Bind("fqnPrefix") String fqnPrefix);
|
int getTagCount(@Bind("source") int source, @Bind("fqnPrefix") String fqnPrefix);
|
||||||
|
|
||||||
@SqlUpdate("DELETE FROM tag_usage where targetFQN = :targetFQN")
|
@SqlUpdate("DELETE FROM tag_usage where targetFQN = :targetFQN")
|
||||||
void deleteTags(@Bind("targetFQN") String targetFQN);
|
void deleteTagsByTarget(@Bind("targetFQN") String targetFQN);
|
||||||
|
|
||||||
@SqlUpdate("DELETE FROM tag_usage where targetFQN LIKE CONCAT(:fqnPrefix, '%')")
|
@SqlUpdate("DELETE FROM tag_usage where tagFQN = :tagFQN AND source = :source")
|
||||||
void deleteTagsByPrefix(@Bind("fqnPrefix") String fqnPrefix);
|
void deleteTagLabels(@Bind("source") int source, @Bind("tagFQN") String tagFQN);
|
||||||
|
|
||||||
|
@SqlUpdate("DELETE FROM tag_usage where tagFQN LIKE CONCAT(:tagFQN, '.%') AND source = :source")
|
||||||
|
void deleteTagLabelsByPrefix(@Bind("source") int source, @Bind("tagFQN") String tagFQN);
|
||||||
|
|
||||||
class TagLabelMapper implements RowMapper<TagLabel> {
|
class TagLabelMapper implements RowMapper<TagLabel> {
|
||||||
@Override
|
@Override
|
||||||
|
@ -993,7 +993,7 @@ public abstract class EntityRepository<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove current entity tags in the database. It will be added back later from the merged tag list.
|
// Remove current entity tags in the database. It will be added back later from the merged tag list.
|
||||||
daoCollection.tagUsageDAO().deleteTags(fqn);
|
daoCollection.tagUsageDAO().deleteTagsByTarget(fqn);
|
||||||
|
|
||||||
if (operation.isPut()) {
|
if (operation.isPut()) {
|
||||||
// PUT operation merges tags in the request with what already exists
|
// PUT operation merges tags in the request with what already exists
|
||||||
|
@ -1079,7 +1079,8 @@ public class TableRepository extends EntityRepository<Table> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete tags related to deleted columns
|
// Delete tags related to deleted columns
|
||||||
deletedColumns.forEach(deleted -> daoCollection.tagUsageDAO().deleteTags(deleted.getFullyQualifiedName()));
|
deletedColumns.forEach(
|
||||||
|
deleted -> daoCollection.tagUsageDAO().deleteTagsByTarget(deleted.getFullyQualifiedName()));
|
||||||
|
|
||||||
// Add tags related to newly added columns
|
// Add tags related to newly added columns
|
||||||
for (Column added : addedColumns) {
|
for (Column added : addedColumns) {
|
||||||
|
@ -20,6 +20,7 @@ import java.sql.SQLException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.jdbi.v3.core.mapper.RowMapper;
|
import org.jdbi.v3.core.mapper.RowMapper;
|
||||||
import org.jdbi.v3.sqlobject.transaction.Transaction;
|
import org.jdbi.v3.sqlobject.transaction.Transaction;
|
||||||
@ -117,6 +118,16 @@ public class TagCategoryRepository extends EntityRepository<TagCategory> {
|
|||||||
return daoCollection.tagUsageDAO().getTagCount(Source.TAG.ordinal(), category.getName());
|
return daoCollection.tagUsageDAO().getTagCount(Source.TAG.ordinal(), category.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
public TagCategory delete(UriInfo uriInfo, String id) throws IOException {
|
||||||
|
TagCategory category = get(uriInfo, id, Fields.EMPTY_FIELDS, Include.NON_DELETED);
|
||||||
|
dao.delete(id);
|
||||||
|
daoCollection.tagDAO().deleteTagsByPrefix(category.getName());
|
||||||
|
daoCollection.tagUsageDAO().deleteTagLabels(Source.TAG.ordinal(), category.getName());
|
||||||
|
daoCollection.tagUsageDAO().deleteTagLabelsByPrefix(Source.TAG.ordinal(), category.getName());
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
public static class TagLabelMapper implements RowMapper<TagLabel> {
|
public static class TagLabelMapper implements RowMapper<TagLabel> {
|
||||||
@Override
|
@Override
|
||||||
public TagLabel map(ResultSet r, org.jdbi.v3.core.statement.StatementContext ctx) throws SQLException {
|
public TagLabel map(ResultSet r, org.jdbi.v3.core.statement.StatementContext ctx) throws SQLException {
|
||||||
|
@ -20,7 +20,9 @@ import java.net.URI;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jdbi.v3.sqlobject.transaction.Transaction;
|
||||||
import org.openmetadata.catalog.Entity;
|
import org.openmetadata.catalog.Entity;
|
||||||
import org.openmetadata.catalog.resources.tags.TagResource;
|
import org.openmetadata.catalog.resources.tags.TagResource;
|
||||||
import org.openmetadata.catalog.type.ChangeDescription;
|
import org.openmetadata.catalog.type.ChangeDescription;
|
||||||
@ -124,6 +126,16 @@ public class TagRepository extends EntityRepository<Tag> {
|
|||||||
return daoCollection.tagUsageDAO().getTagCount(Source.TAG.ordinal(), tag.getFullyQualifiedName());
|
return daoCollection.tagUsageDAO().getTagCount(Source.TAG.ordinal(), tag.getFullyQualifiedName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
public Tag delete(UriInfo uriInfo, String id) throws IOException {
|
||||||
|
Tag tag = get(uriInfo, id, Fields.EMPTY_FIELDS, Include.NON_DELETED);
|
||||||
|
dao.delete(id);
|
||||||
|
daoCollection.tagDAO().deleteTagsByPrefix(tag.getFullyQualifiedName());
|
||||||
|
daoCollection.tagUsageDAO().deleteTagLabels(Source.TAG.ordinal(), tag.getFullyQualifiedName());
|
||||||
|
daoCollection.tagUsageDAO().deleteTagLabelsByPrefix(Source.TAG.ordinal(), tag.getFullyQualifiedName());
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
public static class TagEntityInterface extends EntityInterface<Tag> {
|
public static class TagEntityInterface extends EntityInterface<Tag> {
|
||||||
public TagEntityInterface(Tag entity) {
|
public TagEntityInterface(Tag entity) {
|
||||||
super(Entity.TAG, entity);
|
super(Entity.TAG, entity);
|
||||||
|
@ -31,6 +31,7 @@ import java.util.Objects;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.POST;
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.PUT;
|
import javax.ws.rs.PUT;
|
||||||
@ -465,6 +466,37 @@ public class TagResource {
|
|||||||
return Response.ok(tag).build();
|
return Response.ok(tag).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("/{id}")
|
||||||
|
@Operation(
|
||||||
|
summary = "Delete tag category",
|
||||||
|
tags = "tags",
|
||||||
|
description = "Delete a tag category and all the tags under it.")
|
||||||
|
public TagCategory deleteCategory(
|
||||||
|
@Context UriInfo uriInfo,
|
||||||
|
@Context SecurityContext securityContext,
|
||||||
|
@Parameter(description = "Tag category id", schema = @Schema(type = "string")) @PathParam("id") String id)
|
||||||
|
throws IOException {
|
||||||
|
SecurityUtil.authorizeAdmin(authorizer, securityContext, ADMIN | BOT);
|
||||||
|
TagCategory tagCategory = daoCategory.delete(uriInfo, id);
|
||||||
|
return addHref(uriInfo, tagCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("/{category}/{id}")
|
||||||
|
@Operation(summary = "Delete tag", tags = "tags", description = "Delete a tag and all the tags under it.")
|
||||||
|
public Tag deleteTags(
|
||||||
|
@Context UriInfo uriInfo,
|
||||||
|
@Context SecurityContext securityContext,
|
||||||
|
@Parameter(description = "Tag id", schema = @Schema(type = "string")) @PathParam("category") String category,
|
||||||
|
@Parameter(description = "Tag id", schema = @Schema(type = "string")) @PathParam("id") String id)
|
||||||
|
throws IOException {
|
||||||
|
SecurityUtil.authorizeAdmin(authorizer, securityContext, ADMIN | BOT);
|
||||||
|
Tag tag = dao.delete(uriInfo, id);
|
||||||
|
URI categoryHref = RestUtil.getHref(uriInfo, TAG_COLLECTION_PATH, category);
|
||||||
|
return addHref(categoryHref, tag);
|
||||||
|
}
|
||||||
|
|
||||||
private TagCategory addHref(UriInfo uriInfo, TagCategory category) {
|
private TagCategory addHref(UriInfo uriInfo, TagCategory category) {
|
||||||
category.setHref(RestUtil.getHref(uriInfo, TAG_COLLECTION_PATH, category.getName()));
|
category.setHref(RestUtil.getHref(uriInfo, TAG_COLLECTION_PATH, category.getName()));
|
||||||
addHref(category.getHref(), category.getChildren());
|
addHref(category.getHref(), category.getChildren());
|
||||||
|
@ -128,16 +128,91 @@ public class TagResourceTest extends CatalogApplicationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void post_validTagCategory_as_admin_201(TestInfo test) throws HttpResponseException {
|
void post_delete_validTagCategory_as_admin_201(TestInfo test) throws HttpResponseException, JsonProcessingException {
|
||||||
// POST .../tags/{newCategory} returns 201
|
// POST .../tags/{newCategory} returns 201
|
||||||
String categoryName = test.getDisplayName().substring(0, 10); // Form a unique category name based on the test name
|
String categoryName = test.getDisplayName().substring(0, 20); // Form a unique category name based on the test name
|
||||||
CreateTagCategory create =
|
CreateTagCategory create =
|
||||||
new CreateTagCategory()
|
new CreateTagCategory()
|
||||||
.withName(categoryName)
|
.withName(categoryName)
|
||||||
.withDescription("description")
|
.withDescription("description")
|
||||||
.withCategoryType(TagCategoryType.Descriptive);
|
.withCategoryType(TagCategoryType.Descriptive);
|
||||||
TagCategory newCategory = createAndCheckCategory(create, ADMIN_AUTH_HEADERS);
|
TagCategory category = createAndCheckCategory(create, ADMIN_AUTH_HEADERS);
|
||||||
assertEquals(0, newCategory.getChildren().size());
|
assertEquals(0, category.getChildren().size());
|
||||||
|
|
||||||
|
CreateTag createTag = new CreateTag().withName("PrimaryTag").withDescription("description");
|
||||||
|
Tag primaryTag = createPrimaryTag(category.getName(), createTag, ADMIN_AUTH_HEADERS);
|
||||||
|
|
||||||
|
// POST .../tags/{category}/{primaryTag}/{secondaryTag} to create secondary tag
|
||||||
|
createTag = new CreateTag().withName("SecondaryTag").withDescription("description");
|
||||||
|
Tag secondaryTag = createSecondaryTag(category.getName(), "PrimaryTag", createTag, ADMIN_AUTH_HEADERS);
|
||||||
|
|
||||||
|
// Now delete Tag category and ensure the tag category and tags under it are all deleted
|
||||||
|
deleteCategory(category, ADMIN_AUTH_HEADERS);
|
||||||
|
assertResponse(
|
||||||
|
() -> getTag(primaryTag.getFullyQualifiedName(), ADMIN_AUTH_HEADERS),
|
||||||
|
NOT_FOUND,
|
||||||
|
entityNotFound(Entity.TAG, primaryTag.getFullyQualifiedName()));
|
||||||
|
assertResponse(
|
||||||
|
() -> getTag(secondaryTag.getFullyQualifiedName(), ADMIN_AUTH_HEADERS),
|
||||||
|
NOT_FOUND,
|
||||||
|
entityNotFound(Entity.TAG, secondaryTag.getFullyQualifiedName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void post_delete_validTags_as_admin_201(TestInfo test) throws HttpResponseException, JsonProcessingException {
|
||||||
|
// Create tag category
|
||||||
|
String categoryName = test.getDisplayName().substring(0, 20);
|
||||||
|
CreateTagCategory create =
|
||||||
|
new CreateTagCategory()
|
||||||
|
.withName(categoryName)
|
||||||
|
.withDescription("description")
|
||||||
|
.withCategoryType(TagCategoryType.Descriptive);
|
||||||
|
TagCategory category = createAndCheckCategory(create, ADMIN_AUTH_HEADERS);
|
||||||
|
assertEquals(0, category.getChildren().size());
|
||||||
|
|
||||||
|
// Create primaryTag
|
||||||
|
CreateTag createTag = new CreateTag().withName("PrimaryTag").withDescription("description");
|
||||||
|
Tag primaryTag = createPrimaryTag(category.getName(), createTag, ADMIN_AUTH_HEADERS);
|
||||||
|
|
||||||
|
// Create secondaryTag1
|
||||||
|
createTag = new CreateTag().withName("SecondaryTag1").withDescription("description");
|
||||||
|
Tag secondaryTag1 = createSecondaryTag(category.getName(), "PrimaryTag", createTag, ADMIN_AUTH_HEADERS);
|
||||||
|
|
||||||
|
// Create secondaryTag2
|
||||||
|
createTag = new CreateTag().withName("SecondaryTag2").withDescription("description");
|
||||||
|
Tag secondaryTag2 = createSecondaryTag(category.getName(), "PrimaryTag", createTag, ADMIN_AUTH_HEADERS);
|
||||||
|
|
||||||
|
// Delete and verify secondaryTag2 is deleted
|
||||||
|
deleteTag(category.getName(), secondaryTag2, ADMIN_AUTH_HEADERS);
|
||||||
|
|
||||||
|
// Delete primaryTag and ensure secondaryTag1 under it is deleted
|
||||||
|
deleteTag(category.getName(), primaryTag, ADMIN_AUTH_HEADERS);
|
||||||
|
assertResponse(
|
||||||
|
() -> getTag(secondaryTag1.getFullyQualifiedName(), ADMIN_AUTH_HEADERS),
|
||||||
|
NOT_FOUND,
|
||||||
|
entityNotFound(Entity.TAG, secondaryTag1.getFullyQualifiedName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public final TagCategory deleteCategory(TagCategory category, Map<String, String> authHeaders)
|
||||||
|
throws HttpResponseException {
|
||||||
|
WebTarget target = getResource("tags/" + category.getId());
|
||||||
|
TagCategory deletedCategory = TestUtils.delete(target, TagCategory.class, authHeaders);
|
||||||
|
assertResponse(
|
||||||
|
() -> getCategory(category.getName(), ADMIN_AUTH_HEADERS),
|
||||||
|
NOT_FOUND,
|
||||||
|
entityNotFound(Entity.TAG_CATEGORY, category.getName()));
|
||||||
|
return deletedCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Tag deleteTag(String categoryName, Tag tag, Map<String, String> authHeaders)
|
||||||
|
throws HttpResponseException {
|
||||||
|
WebTarget target = getResource("tags/" + categoryName + "/" + tag.getId());
|
||||||
|
Tag deletedTag = TestUtils.delete(target, Tag.class, authHeaders);
|
||||||
|
assertResponse(
|
||||||
|
() -> getTag(tag.getFullyQualifiedName(), ADMIN_AUTH_HEADERS),
|
||||||
|
NOT_FOUND,
|
||||||
|
entityNotFound(Entity.TAG, tag.getFullyQualifiedName()));
|
||||||
|
return deletedTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -323,7 +398,7 @@ public class TagResourceTest extends CatalogApplicationTest {
|
|||||||
return category;
|
return category;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createPrimaryTag(String category, CreateTag create, Map<String, String> authHeaders)
|
private Tag createPrimaryTag(String category, CreateTag create, Map<String, String> authHeaders)
|
||||||
throws HttpResponseException, JsonProcessingException {
|
throws HttpResponseException, JsonProcessingException {
|
||||||
String updatedBy = TestUtils.getPrincipal(authHeaders);
|
String updatedBy = TestUtils.getPrincipal(authHeaders);
|
||||||
WebTarget target = getResource("tags/" + category);
|
WebTarget target = getResource("tags/" + category);
|
||||||
@ -347,9 +422,10 @@ public class TagResourceTest extends CatalogApplicationTest {
|
|||||||
create.getDescription(),
|
create.getDescription(),
|
||||||
create.getAssociatedTags(),
|
create.getAssociatedTags(),
|
||||||
updatedBy);
|
updatedBy);
|
||||||
|
return returnedTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createSecondaryTag(String category, String primaryTag, CreateTag create, Map<String, String> authHeaders)
|
private Tag createSecondaryTag(String category, String primaryTag, CreateTag create, Map<String, String> authHeaders)
|
||||||
throws HttpResponseException, JsonProcessingException {
|
throws HttpResponseException, JsonProcessingException {
|
||||||
String updatedBy = TestUtils.getPrincipal(authHeaders);
|
String updatedBy = TestUtils.getPrincipal(authHeaders);
|
||||||
WebTarget target = getResource("tags/" + category + "/" + primaryTag);
|
WebTarget target = getResource("tags/" + category + "/" + primaryTag);
|
||||||
@ -373,6 +449,7 @@ public class TagResourceTest extends CatalogApplicationTest {
|
|||||||
create.getDescription(),
|
create.getDescription(),
|
||||||
create.getAssociatedTags(),
|
create.getAssociatedTags(),
|
||||||
updatedBy);
|
updatedBy);
|
||||||
|
return returnedTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
|
Loading…
x
Reference in New Issue
Block a user