diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/Entity.java b/openmetadata-service/src/main/java/org/openmetadata/service/Entity.java index c3d66d96c6c..e9f538a3cf0 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/Entity.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/Entity.java @@ -13,6 +13,7 @@ package org.openmetadata.service; +import static org.openmetadata.common.utils.CommonUtil.listOf; import static org.openmetadata.common.utils.CommonUtil.listOrEmpty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; @@ -30,7 +31,9 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import javax.ws.rs.core.UriInfo; +import lombok.Getter; import lombok.NonNull; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.openmetadata.schema.EntityInterface; import org.openmetadata.schema.entity.services.ServiceType; @@ -41,6 +44,7 @@ import org.openmetadata.schema.type.TagLabel; import org.openmetadata.service.exception.CatalogExceptionMessage; import org.openmetadata.service.exception.EntityNotFoundException; import org.openmetadata.service.jdbi3.EntityRepository; +import org.openmetadata.service.jdbi3.FeedRepository; import org.openmetadata.service.resources.feeds.MessageParser.EntityLink; import org.openmetadata.service.util.EntityUtil.Fields; @@ -52,6 +56,8 @@ public final class Entity { // Canonical entity name to corresponding EntityRepository map private static final Map> ENTITY_REPOSITORY_MAP = new HashMap<>(); + @Getter @Setter private static FeedRepository feedRepository; + // List of all the entities private static final List ENTITY_LIST = new ArrayList<>(); @@ -89,15 +95,13 @@ public final class Entity { public static final String DATABASE_SCHEMA = "databaseSchema"; public static final String METRICS = "metrics"; public static final String DASHBOARD = "dashboard"; + public static final String DASHBOARD_DATA_MODEL = "dashboardDataModel"; public static final String PIPELINE = "pipeline"; public static final String CHART = "chart"; public static final String REPORT = "report"; public static final String TOPIC = "topic"; public static final String MLMODEL = "mlmodel"; public static final String CONTAINER = "container"; - public static final String BOT = "bot"; - public static final String EVENT_SUBSCRIPTION = "eventsubscription"; - public static final String THREAD = "THREAD"; public static final String QUERY = "query"; public static final String GLOSSARY = "glossary"; @@ -107,15 +111,12 @@ public final class Entity { public static final String TYPE = "type"; public static final String TEST_DEFINITION = "testDefinition"; public static final String TEST_CONNECTION_DEFINITION = "testConnectionDefinition"; - public static final String WORKFLOW = "workflow"; public static final String TEST_SUITE = "testSuite"; public static final String KPI = "kpi"; public static final String TEST_CASE = "testCase"; public static final String WEB_ANALYTIC_EVENT = "webAnalyticEvent"; public static final String DATA_INSIGHT_CHART = "dataInsightChart"; - public static final String DASHBOARD_DATA_MODEL = "dashboardDataModel"; - // // Policy entity // @@ -128,6 +129,7 @@ public final class Entity { public static final String ROLE = "role"; public static final String USER = "user"; public static final String TEAM = "team"; + public static final String BOT = "bot"; // // Operation related entities @@ -140,6 +142,12 @@ public final class Entity { public static final String DOMAIN = "domain"; public static final String DATA_PRODUCT = "dataProduct"; + // + // Other entities + public static final String EVENT_SUBSCRIPTION = "eventsubscription"; + public static final String THREAD = "THREAD"; + public static final String WORKFLOW = "workflow"; + // // Reserved names in OpenMetadata // @@ -357,6 +365,29 @@ public final class Entity { return new HashSet<>(Arrays.asList(propertyOrder.value())); } + /** Returns true if the entity supports activity feeds, announcement, and tasks */ + public static boolean supportsFeed(String entityType) { + return listOf( + TABLE, + DATABASE, + DATABASE_SCHEMA, + METRICS, + DASHBOARD, + DASHBOARD_DATA_MODEL, + PIPELINE, + CHART, + REPORT, + TOPIC, + MLMODEL, + CONTAINER, + QUERY, + GLOSSARY, + GLOSSARY_TERM, + TAG, + CLASSIFICATION) + .contains(entityType); + } + /** Class for getting validated entity list from a queryParam with list of entities. */ public static class EntityList { private EntityList() {} diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/exception/CatalogGenericExceptionMapper.java b/openmetadata-service/src/main/java/org/openmetadata/service/exception/CatalogGenericExceptionMapper.java index a4c7ae6c536..a63b922e27d 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/exception/CatalogGenericExceptionMapper.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/exception/CatalogGenericExceptionMapper.java @@ -42,6 +42,7 @@ public class CatalogGenericExceptionMapper implements ExceptionMapper @Override public Response toResponse(Throwable ex) { LOG.debug(ex.getMessage()); + ex.printStackTrace(); if (ex instanceof ProcessingException || ex instanceof IllegalArgumentException || ex instanceof javax.ws.rs.BadRequestException) { diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java index d1ce15f6b36..2ebc2a55f35 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java @@ -850,6 +850,9 @@ public abstract class EntityRepository { // Delete the extension data storing custom properties removeExtension(entityInterface); + // Delete all the threads that are about this entity + Entity.getFeedRepository().deleteByAbout(entityInterface.getId()); + // Finally, delete the entity dao.delete(id); } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/FeedRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/FeedRepository.java index e9be56bd5cc..f521eeca174 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/FeedRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/FeedRepository.java @@ -92,6 +92,15 @@ import org.openmetadata.service.util.RestUtil.DeleteResponse; import org.openmetadata.service.util.RestUtil.PatchResponse; import org.openmetadata.service.util.ResultList; +/* + * Feed relationships: + * - 'user' --- createdBy ---> 'thread' in entity_relationship + * - 'user' --- repliedTo ---> 'thread' in entity_relationship + * - 'user' --- mentionedIn ---> 'thread' in entity_relationship + * - 'user' --- reactedTo ---> 'thread' in entity_relationship + * - 'thread' --- addressedTo ---> 'user' in field_relationship + * - 'thread' --- isAbout ---> 'entity' in entity_relationship + */ @Slf4j public class FeedRepository { private final CollectionDAO dao; @@ -369,19 +378,32 @@ public class FeedRepository { @Transaction public DeleteResponse deleteThread(Thread thread, String deletedByUser) { - String id = thread.getId().toString(); + deleteThreadInternal(thread.getId().toString()); + LOG.info("{} deleted thread with id {}", deletedByUser, thread.getId()); + return new DeleteResponse<>(thread, RestUtil.ENTITY_DELETED); + } + public void deleteThreadInternal(String id) { // Delete all the relationships to other entities dao.relationshipDAO().deleteAll(id, Entity.THREAD); // Delete all the field relationships to other entities dao.fieldRelationshipDAO().deleteAllByPrefix(id); - // Finally, delete the entity + // Finally, delete the thread dao.feedDAO().delete(id); + } - LOG.info("{} deleted thread with id {}", deletedByUser, thread.getId()); - return new DeleteResponse<>(thread, RestUtil.ENTITY_DELETED); + @Transaction + public void deleteByAbout(UUID entityId) { + List threadIds = listOrEmpty(dao.feedDAO().findByEntityId(entityId.toString())); + for (String threadId : threadIds) { + try { + deleteThreadInternal(threadId); + } catch (Exception ex) { + // Continue deletion + } + } } @Transaction diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/feeds/FeedResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/feeds/FeedResource.java index 0348016cee5..ed43a8a36d7 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/feeds/FeedResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/feeds/FeedResource.java @@ -100,6 +100,7 @@ public class FeedResource { public FeedResource(CollectionDAO dao, Authorizer authorizer) { Objects.requireNonNull(dao, "FeedRepository must not be null"); this.dao = new FeedRepository(dao); + Entity.setFeedRepository(this.dao); this.authorizer = authorizer; } @@ -145,7 +146,7 @@ public class FeedResource { @QueryParam("after") String after, @Parameter( - description = "Filter threads by entity link", + description = "Filter threads by entity link of entity about which this thread is created", schema = @Schema(type = "string", example = "")) @QueryParam("entityLink") String entityLink, @@ -354,8 +355,7 @@ public class FeedResource { @Parameter(description = "Filter threads by whether it is active or resolved", schema = @Schema(type = "boolean")) @DefaultValue("false") @QueryParam("isResolved") - Boolean isResolved) - throws IOException { + Boolean isResolved) { FeedFilter filter = FeedFilter.builder().threadType(threadType).taskStatus(taskStatus).resolved(isResolved).build(); return dao.getThreadsCount(filter, entityLink); } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/feeds/MessageParser.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/feeds/MessageParser.java index 429418f8a1b..f2f3554abbd 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/feeds/MessageParser.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/feeds/MessageParser.java @@ -31,12 +31,12 @@ public final class MessageParser { private static final String ENTITY_LINK_SEPARATOR = "::"; // Pattern to match the following markdown entity links: - // <#E::{entityType}::{entityFQN}> -- <#E::table::bigquery_gcp.shopify.raw_product_catalog> - // <#E::{entityType}::{entityFQN}::{fieldName}> -- <#E::table::bigquery_gcp.shopify.raw_product_catalog::description> + // <#E::{entityType}::{entityFQN}> -- <#E::table::bigquery_gcp.shopify.product> + // <#E::{entityType}::{entityFQN}::{fieldName}> -- <#E::table::bigquery_gcp.shopify.product::description> // <#E::{entityType}::{entityFQN}::{fieldName}::{arrayFieldName}> - // -- <#E::table::bigquery_gcp.shopify.raw_product_catalog::columns::comment> + // -- <#E::table::bigquery_gcp.shopify.product::columns::product_id> // <#E::{entityType}::{entityFQN}::{fieldName}::{arrayFieldName}::{arrayFieldValue}> - // -- <#E::table::bigquery_gcp.shopify.raw_product_catalog::columns::comment::description> + // -- <#E::table::bigquery_gcp.shopify.product::columns::product_id::description> private static final Pattern ENTITY_LINK_PATTERN = Pattern.compile( "<#E" @@ -77,6 +77,10 @@ public final class MessageParser { ENTITY_ARRAY_FIELD } + public EntityLink(String entityType, String entityFqn) { + this(entityType, entityFqn, null, null, null); + } + public EntityLink( String entityType, String entityFqn, String fieldName, String arrayFieldName, String arrayFieldValue) { if (entityType == null || entityFqn == null) { @@ -92,18 +96,30 @@ public final class MessageParser { if (arrayFieldName == null) { throw new IllegalArgumentException(INVALID_ENTITY_LINK); } + // Entity link example: <#E::table::bigquery_gcp.shopify.product::columns::product_id::description> + // FullyQualifiedFieldType: table.columns.member + // FullyQualifiedFieldValue: bigQuery_gcp.shopify.product.product_id.description this.linkType = LinkType.ENTITY_ARRAY_FIELD; this.fullyQualifiedFieldType = String.format("%s.%s.member", entityType, fieldName); this.fullyQualifiedFieldValue = String.format("%s.%s.%s", entityFqn, arrayFieldName, arrayFieldValue); } else if (arrayFieldName != null) { + // Entity link example: <#E::table::bigquery_gcp.shopify.product::columns::product_id> + // FullyQualifiedFieldType: table.columns.member + // FullyQualifiedFieldValue: bigQuery_gcp.shopify.product.product_id this.linkType = LinkType.ENTITY_ARRAY_FIELD; this.fullyQualifiedFieldType = String.format("%s.%s.member", entityType, fieldName); this.fullyQualifiedFieldValue = String.format("%s.%s", entityFqn, arrayFieldName); } else if (fieldName != null) { - this.fullyQualifiedFieldType = String.format("%s.%s", entityType, fieldName); + // Entity link example: <#E::table::bigquery_gcp.shopify.product::description> + // FullyQualifiedFieldType: table.description + // FullyQualifiedFieldValue: bigQuery_gcp.shopify.product.description this.linkType = LinkType.ENTITY_REGULAR_FIELD; + this.fullyQualifiedFieldType = String.format("%s.%s", entityType, fieldName); this.fullyQualifiedFieldValue = String.format("%s.%s", entityFqn, fieldName); } else { + // Entity link example: <#E::table::bigquery_gcp.shopify.product> + // FullyQualifiedFieldType: table + // FullyQualifiedFieldValue: bigQuery_gcp.shopify.product this.linkType = LinkType.ENTITY; this.fullyQualifiedFieldType = entityType; this.fullyQualifiedFieldValue = entityFqn; @@ -111,13 +127,8 @@ public final class MessageParser { } public String getLinkString() { - StringBuilder builder = new StringBuilder(); - builder - .append("<#E") - .append(ENTITY_LINK_SEPARATOR) - .append(entityType) - .append(ENTITY_LINK_SEPARATOR) - .append(entityFQN); + StringBuilder builder = new StringBuilder("<#E"); + builder.append(ENTITY_LINK_SEPARATOR).append(entityType).append(ENTITY_LINK_SEPARATOR).append(entityFQN); if (linkType == LinkType.ENTITY_REGULAR_FIELD || linkType == LinkType.ENTITY_ARRAY_FIELD) { builder.append(ENTITY_LINK_SEPARATOR).append(fieldName); } diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/EntityResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/EntityResourceTest.java index f4687b1d9ab..1c3410be4b3 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/EntityResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/EntityResourceTest.java @@ -26,11 +26,13 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.openmetadata.common.utils.CommonUtil.listOf; import static org.openmetadata.common.utils.CommonUtil.listOrEmpty; import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty; import static org.openmetadata.csv.EntityCsvTest.assertSummary; import static org.openmetadata.schema.type.MetadataOperation.EDIT_ALL; import static org.openmetadata.schema.type.MetadataOperation.EDIT_TESTS; +import static org.openmetadata.schema.type.TaskType.RequestDescription; import static org.openmetadata.service.Entity.ADMIN_USER_NAME; import static org.openmetadata.service.Entity.FIELD_DELETED; import static org.openmetadata.service.Entity.FIELD_EXTENSION; @@ -129,6 +131,7 @@ import org.openmetadata.schema.CreateEntity; import org.openmetadata.schema.EntityInterface; import org.openmetadata.schema.api.data.RestoreEntity; import org.openmetadata.schema.api.data.TermReference; +import org.openmetadata.schema.api.feed.CreateThread; import org.openmetadata.schema.api.teams.CreateTeam; import org.openmetadata.schema.api.teams.CreateTeam.TeamType; import org.openmetadata.schema.api.tests.CreateTestSuite; @@ -144,6 +147,7 @@ import org.openmetadata.schema.entity.data.GlossaryTerm; import org.openmetadata.schema.entity.data.Table; import org.openmetadata.schema.entity.domains.DataProduct; import org.openmetadata.schema.entity.domains.Domain; +import org.openmetadata.schema.entity.feed.Thread; import org.openmetadata.schema.entity.policies.Policy; import org.openmetadata.schema.entity.policies.accessControl.Rule; import org.openmetadata.schema.entity.services.connections.TestConnectionResult; @@ -156,6 +160,7 @@ import org.openmetadata.schema.entity.type.Category; import org.openmetadata.schema.entity.type.CustomProperty; import org.openmetadata.schema.tests.TestDefinition; import org.openmetadata.schema.tests.TestSuite; +import org.openmetadata.schema.type.AnnouncementDetails; import org.openmetadata.schema.type.ChangeDescription; import org.openmetadata.schema.type.ChangeEvent; import org.openmetadata.schema.type.Column; @@ -182,6 +187,7 @@ import org.openmetadata.service.resources.dqtests.TestDefinitionResourceTest; import org.openmetadata.service.resources.dqtests.TestSuiteResourceTest; import org.openmetadata.service.resources.events.EventResource.EventList; import org.openmetadata.service.resources.events.EventSubscriptionResourceTest; +import org.openmetadata.service.resources.feeds.FeedResourceTest; import org.openmetadata.service.resources.glossary.GlossaryResourceTest; import org.openmetadata.service.resources.kpi.KpiResourceTest; import org.openmetadata.service.resources.metadata.TypeResourceTest; @@ -465,14 +471,14 @@ public abstract class EntityResourceTest", entityType, entity.getFullyQualifiedName()); + CreateThread createThread = new CreateThread().withFrom(USER1.getName()).withMessage("message").withAbout(about); + Thread thread = feedTest.createAndCheck(createThread, ADMIN_AUTH_HEADERS); + + // Add task thread for the entity from user1 to user2 + Thread taskThread = + feedTest.createTaskThread( + USER1.getName(), + about, + USER2.getEntityReference(), + "old", + "new", + RequestDescription, + authHeaders(USER1.getName())); + + // Add announcement thread for the entity from user1 to user2 + AnnouncementDetails announcementDetails = feedTest.getAnnouncementDetails("Announcement", 10, 11); + Thread announcementThread = + feedTest.createAnnouncement( + USER1.getName(), about, "message", announcementDetails, authHeaders(USER1.getName())); + + // When the entity is deleted, all the threads also should be deleted + deleteEntity(entity.getId(), true, true, ADMIN_AUTH_HEADERS); + for (UUID id : listOf(thread.getId(), taskThread.getId(), announcementThread.getId())) { + assertResponseContains( + () -> feedTest.getThread(id, ADMIN_AUTH_HEADERS), + NOT_FOUND, + CatalogExceptionMessage.entityNotFound("Thread", id)); + } + } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Common entity functionality for tests /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -2710,7 +2758,7 @@ public abstract class EntityResourceTest", Entity.TABLE, TABLE.getFullyQualifiedName()); - announcementDetails - .withStartTime(now.plusDays(12L).toEpochSecond(ZoneOffset.UTC)) - .withEndTime(now.plusDays(13L).toEpochSecond(ZoneOffset.UTC)); - create = - create() - .withMessage("Announcement Two") - .withType(ThreadType.Announcement) - .withAnnouncementDetails(announcementDetails); - createAndCheck(create, USER_AUTH_HEADERS); + // Create announcement 1 + AnnouncementDetails announcementDetails = getAnnouncementDetails("First announcement", 10, 11); + createAnnouncement(USER.getName(), about, "Announcement One", announcementDetails, USER_AUTH_HEADERS); - // create one expired announcement - announcementDetails - .withStartTime(now.minusDays(30L).toEpochSecond(ZoneOffset.UTC)) - .withEndTime(now.minusDays(20L).toEpochSecond(ZoneOffset.UTC)); - create = - create() - .withMessage("Announcement Three") - .withType(ThreadType.Announcement) - .withAnnouncementDetails(announcementDetails); - createAndCheck(create, USER_AUTH_HEADERS); + // Create announcement 2 + announcementDetails = getAnnouncementDetails("Second announcement", 12, 13); + createAnnouncement(USER.getName(), about, "Announcement Two", announcementDetails, USER_AUTH_HEADERS); + + // create an expired announcement + announcementDetails = getAnnouncementDetails("Expired", -30, -20); + createAnnouncement(USER.getName(), about, "Announcement Three", announcementDetails, USER_AUTH_HEADERS); // create one active announcement - announcementDetails - .withDescription("Active Announcement") - .withStartTime(now.minusDays(1L).toEpochSecond(ZoneOffset.UTC)) - .withEndTime(now.plusDays(1L).toEpochSecond(ZoneOffset.UTC)); - create = - create() - .withMessage("Announcement Four") - .withType(ThreadType.Announcement) - .withAnnouncementDetails(announcementDetails); - createAndCheck(create, USER_AUTH_HEADERS); + announcementDetails = getAnnouncementDetails("Active", -1, 1); + createAnnouncement(USER.getName(), about, "Announcement Four", announcementDetails, USER_AUTH_HEADERS); ThreadList announcements = listAnnouncements(null, null, null, ADMIN_AUTH_HEADERS); int announcementCount = announcements.getPaging().getTotal(); @@ -445,7 +418,7 @@ public class FeedResourceTest extends OpenMetadataApplicationTest { assertEquals(totalAnnouncementCount + 4, announcementCount); assertEquals(totalAnnouncementCount + 4, announcements.getData().size()); - announcements = listAnnouncements(create.getAbout(), null, null, ADMIN_AUTH_HEADERS); + announcements = listAnnouncements(about, null, null, ADMIN_AUTH_HEADERS); assertEquals(announcementCount, announcements.getPaging().getTotal()); assertEquals(announcementCount, announcements.getData().size()); @@ -454,9 +427,9 @@ public class FeedResourceTest extends OpenMetadataApplicationTest { assertEquals(1, activeAnnouncementCount); assertEquals(1, announcements.getData().size()); - assertEquals("Active Announcement", announcements.getData().get(0).getAnnouncement().getDescription()); + assertEquals("Active", announcements.getData().get(0).getAnnouncement().getDescription()); - announcements = listAnnouncements(create.getAbout(), null, true, ADMIN_AUTH_HEADERS); + announcements = listAnnouncements(about, null, true, ADMIN_AUTH_HEADERS); assertEquals(activeAnnouncementCount, announcements.getPaging().getTotal()); assertEquals(activeAnnouncementCount, announcements.getData().size()); @@ -465,7 +438,7 @@ public class FeedResourceTest extends OpenMetadataApplicationTest { assertEquals(totalAnnouncementCount + 3, announcements.getPaging().getTotal()); assertEquals(totalAnnouncementCount + 3, announcements.getData().size()); - announcements = listAnnouncements(create.getAbout(), null, false, ADMIN_AUTH_HEADERS); + announcements = listAnnouncements(about, null, false, ADMIN_AUTH_HEADERS); assertEquals(totalAnnouncementCount + 3, announcements.getPaging().getTotal()); assertEquals(totalAnnouncementCount + 3, announcements.getData().size()); } @@ -474,58 +447,31 @@ public class FeedResourceTest extends OpenMetadataApplicationTest { void post_invalidAnnouncement_400() throws IOException { // create two announcements with same start time in the future LocalDateTime now = LocalDateTime.now(); - AnnouncementDetails announcementDetails = - new AnnouncementDetails() - .withDescription("First announcement") - .withStartTime(now.plusDays(3L).toEpochSecond(ZoneOffset.UTC)) - .withEndTime(now.plusDays(5L).toEpochSecond(ZoneOffset.UTC)); - CreateThread create = - create() - .withMessage("Announcement One") - .withType(ThreadType.Announcement) - .withAnnouncementDetails(announcementDetails); - createAndCheck(create, USER_AUTH_HEADERS); - - CreateThread create2 = - create() - .withMessage("Announcement Two") - .withType(ThreadType.Announcement) - .withAnnouncementDetails(announcementDetails); + String about = String.format("<#E::%s::%s>", Entity.TABLE, TABLE.getFullyQualifiedName()); + AnnouncementDetails announcementDetails = getAnnouncementDetails("1", 3, 5); + createAnnouncement(USER.getName(), about, "Announcement One", announcementDetails, USER_AUTH_HEADERS); // create announcement with same start and end time - assertResponse(() -> createThread(create2, USER_AUTH_HEADERS), BAD_REQUEST, ANNOUNCEMENT_OVERLAP); + assertResponse( + () -> createAnnouncement(USER.getName(), about, "Announcement Two", announcementDetails, USER_AUTH_HEADERS), + BAD_REQUEST, + ANNOUNCEMENT_OVERLAP); // create announcement with start time > end time - announcementDetails - .withStartTime(now.plusDays(3L).toEpochSecond(ZoneOffset.UTC)) - .withEndTime(now.plusDays(2L).toEpochSecond(ZoneOffset.UTC)); - CreateThread create3 = create2.withAnnouncementDetails(announcementDetails); - assertResponse(() -> createThread(create3, USER_AUTH_HEADERS), BAD_REQUEST, ANNOUNCEMENT_INVALID_START_TIME); + assertResponse( + () -> + createAnnouncement( + USER.getName(), about, "Announcement Three", getAnnouncementDetails("2", 3, 2), USER_AUTH_HEADERS), + BAD_REQUEST, + ANNOUNCEMENT_INVALID_START_TIME); - // create announcement with overlaps - announcementDetails - .withStartTime(now.plusDays(2L).toEpochSecond(ZoneOffset.UTC)) - .withEndTime(now.plusDays(6L).toEpochSecond(ZoneOffset.UTC)); - CreateThread create4 = create2.withAnnouncementDetails(announcementDetails); - assertResponse(() -> createThread(create4, USER_AUTH_HEADERS), BAD_REQUEST, ANNOUNCEMENT_OVERLAP); - - announcementDetails - .withStartTime(now.plusDays(3L).plusHours(2L).toEpochSecond(ZoneOffset.UTC)) - .withEndTime(now.plusDays(4L).toEpochSecond(ZoneOffset.UTC)); - CreateThread create5 = create2.withAnnouncementDetails(announcementDetails); - assertResponse(() -> createThread(create5, USER_AUTH_HEADERS), BAD_REQUEST, ANNOUNCEMENT_OVERLAP); - - announcementDetails - .withStartTime(now.plusDays(2L).plusHours(12L).toEpochSecond(ZoneOffset.UTC)) - .withEndTime(now.plusDays(4L).toEpochSecond(ZoneOffset.UTC)); - CreateThread create6 = create2.withAnnouncementDetails(announcementDetails); - assertResponse(() -> createThread(create6, USER_AUTH_HEADERS), BAD_REQUEST, ANNOUNCEMENT_OVERLAP); - - announcementDetails - .withStartTime(now.plusDays(4L).plusHours(12L).toEpochSecond(ZoneOffset.UTC)) - .withEndTime(now.plusDays(6L).toEpochSecond(ZoneOffset.UTC)); - CreateThread create7 = create2.withAnnouncementDetails(announcementDetails); - assertResponse(() -> createThread(create7, USER_AUTH_HEADERS), BAD_REQUEST, ANNOUNCEMENT_OVERLAP); + // create announcement with overlap + assertResponse( + () -> + createAnnouncement( + USER.getName(), about, "Announcement Four", getAnnouncementDetails("3", 2, 6), USER_AUTH_HEADERS), + BAD_REQUEST, + ANNOUNCEMENT_OVERLAP); } @Test @@ -856,18 +802,10 @@ public class FeedResourceTest extends OpenMetadataApplicationTest { @Test void patch_announcement_200() throws IOException { LocalDateTime now = LocalDateTime.now(); - AnnouncementDetails announcementDetails = - new AnnouncementDetails() - .withDescription("First announcement") - .withStartTime(now.plusDays(5L).toEpochSecond(ZoneOffset.UTC)) - .withEndTime(now.plusDays(6L).toEpochSecond(ZoneOffset.UTC)); - CreateThread create = - create() - .withMessage("Announcement One") - .withType(ThreadType.Announcement) - .withAnnouncementDetails(announcementDetails); - Thread thread = createAndCheck(create, USER_AUTH_HEADERS); - + AnnouncementDetails announcementDetails = getAnnouncementDetails("First announcement", 5, 6); + String about = String.format("<#E::%s::%s>", Entity.TABLE, TABLE.getFullyQualifiedName()); + Thread thread = + createAnnouncement(USER.getName(), about, "Announcement One", announcementDetails, USER_AUTH_HEADERS); String originalJson = JsonUtils.pojoToJson(thread); long startTs = now.plusDays(6L).toEpochSecond(ZoneOffset.UTC); @@ -900,27 +838,14 @@ public class FeedResourceTest extends OpenMetadataApplicationTest { @Test void patch_invalidAnnouncement_400() throws IOException { LocalDateTime now = LocalDateTime.now(); - AnnouncementDetails announcementDetails = - new AnnouncementDetails() - .withDescription("First announcement") - .withStartTime(now.plusDays(53L).toEpochSecond(ZoneOffset.UTC)) - .withEndTime(now.plusDays(55L).toEpochSecond(ZoneOffset.UTC)); - CreateThread create = - create() - .withMessage("Announcement One") - .withType(ThreadType.Announcement) - .withAnnouncementDetails(announcementDetails); - Thread thread1 = createAndCheck(create, USER_AUTH_HEADERS); + String about = String.format("<#E::%s::%s>", Entity.TABLE, TABLE.getFullyQualifiedName()); + AnnouncementDetails announcementDetails = getAnnouncementDetails("First announcement", 53, 55); + Thread thread1 = + createAnnouncement(USER.getName(), about, "Announcement One", announcementDetails, USER_AUTH_HEADERS); - announcementDetails - .withStartTime(now.plusDays(57L).toEpochSecond(ZoneOffset.UTC)) - .withEndTime(now.plusDays(59L).toEpochSecond(ZoneOffset.UTC)); - CreateThread create2 = - create() - .withMessage("Announcement Two") - .withType(ThreadType.Announcement) - .withAnnouncementDetails(announcementDetails); - Thread thread2 = createAndCheck(create2, USER_AUTH_HEADERS); + announcementDetails = getAnnouncementDetails("Second announcement", 57, 59); + Thread thread2 = + createAnnouncement(USER.getName(), about, "Announcement Two", announcementDetails, USER_AUTH_HEADERS); String originalJson = JsonUtils.pojoToJson(thread2); @@ -1573,7 +1498,7 @@ public class FeedResourceTest extends OpenMetadataApplicationTest { return true; } - private Thread createTaskThread( + public Thread createTaskThread( String fromUser, String about, EntityReference assignee, @@ -1598,6 +1523,23 @@ public class FeedResourceTest extends OpenMetadataApplicationTest { return createAndCheck(create, authHeaders); } + public Thread createAnnouncement( + String fromUser, + String about, + String message, + AnnouncementDetails announcementDetails, + Map authHeaders) + throws HttpResponseException { + CreateThread create = + new CreateThread() + .withFrom(fromUser) + .withMessage(message) + .withAbout(about) + .withType(ThreadType.Announcement) + .withAnnouncementDetails(announcementDetails); + return createAndCheck(create, authHeaders); + } + public void validateTaskList( UUID expectedAssignee, String expectedSuggestion, @@ -1623,4 +1565,12 @@ public class FeedResourceTest extends OpenMetadataApplicationTest { assertEquals(expected.getSuggestion(), actual.getSuggestion()); assertEquals(expected.getStatus(), actual.getStatus()); } + + public AnnouncementDetails getAnnouncementDetails(String description, long start, long end) { + LocalDateTime now = LocalDateTime.now(); + return new AnnouncementDetails() + .withDescription(description) + .withStartTime(now.plusDays(start).toEpochSecond(ZoneOffset.UTC)) + .withEndTime(now.plusDays(end).toEpochSecond(ZoneOffset.UTC)); + } } diff --git a/openmetadata-service/src/test/resources/openmetadata-secure-test.yaml b/openmetadata-service/src/test/resources/openmetadata-secure-test.yaml index eaf987797bd..b8c4812b602 100644 --- a/openmetadata-service/src/test/resources/openmetadata-secure-test.yaml +++ b/openmetadata-service/src/test/resources/openmetadata-secure-test.yaml @@ -89,8 +89,10 @@ server: # Logging settings. logging: - level: OFF - appenders: [] + level: INFO + appenders: + - type: console + logFormat: "%level %logger{5} - %msg%n" database: # the name of the JDBC driver, h2 is used for testing