mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-07-23 17:30:35 +00:00
This commit is contained in:
parent
45370c773f
commit
c5e7a63fbb
@ -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<String, EntityRepository<? extends EntityInterface>> ENTITY_REPOSITORY_MAP = new HashMap<>();
|
||||
|
||||
@Getter @Setter private static FeedRepository feedRepository;
|
||||
|
||||
// List of all the entities
|
||||
private static final List<String> 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() {}
|
||||
|
@ -42,6 +42,7 @@ public class CatalogGenericExceptionMapper implements ExceptionMapper<Throwable>
|
||||
@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) {
|
||||
|
@ -850,6 +850,9 @@ public abstract class EntityRepository<T extends EntityInterface> {
|
||||
// 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);
|
||||
}
|
||||
|
@ -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<Thread> 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<String> threadIds = listOrEmpty(dao.feedDAO().findByEntityId(entityId.toString()));
|
||||
for (String threadId : threadIds) {
|
||||
try {
|
||||
deleteThreadInternal(threadId);
|
||||
} catch (Exception ex) {
|
||||
// Continue deletion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Transaction
|
||||
|
@ -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 = "<E#/{entityType}/{entityFQN}/{fieldName}>"))
|
||||
@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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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<T extends EntityInterface, K extends Cr
|
||||
public abstract K createRequest(String name);
|
||||
|
||||
// Get container entity used in createRequest that has CONTAINS relationship to the entity created with this
|
||||
// request has . For table, it is database. For database, it is databaseService. See Relationship.CONTAINS for
|
||||
// request has. For table, it is database. For database, it is databaseService. See Relationship.CONTAINS for
|
||||
// details.
|
||||
public EntityReference getContainer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get container entity based on create request that has CONTAINS relationship to the entity created with this
|
||||
// request has . For table, it is database. For database, it is databaseService. See Relationship.CONTAINS for
|
||||
// request has. For table, it is database. For database, it is databaseService. See Relationship.CONTAINS for
|
||||
// details.
|
||||
public EntityReference getContainer(T e) {
|
||||
return null;
|
||||
@ -1837,6 +1843,48 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr
|
||||
return response;
|
||||
}
|
||||
|
||||
@Test
|
||||
@Execution(ExecutionMode.CONCURRENT)
|
||||
void test_cleanupConversations(TestInfo test) throws HttpResponseException {
|
||||
if (!Entity.supportsFeed(entityType)) {
|
||||
return;
|
||||
}
|
||||
K request = createRequest(getEntityName(test), "", "", null);
|
||||
T entity = createEntity(request, ADMIN_AUTH_HEADERS);
|
||||
|
||||
// Add a conversation thread for the entity
|
||||
FeedResourceTest feedTest = new FeedResourceTest();
|
||||
String about = String.format("<#E::%s::%s>", 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<T extends EntityInterface, K extends Cr
|
||||
return entity;
|
||||
}
|
||||
|
||||
public T assertDomainInheritanceOverride(T entity, K updateRequest, EntityReference newDomain)
|
||||
public void assertDomainInheritanceOverride(T entity, K updateRequest, EntityReference newDomain)
|
||||
throws JsonProcessingException, HttpResponseException {
|
||||
// When an entity has domain set, it does not inherit domain from the parent
|
||||
String json = JsonUtils.pojoToJson(entity);
|
||||
@ -2721,6 +2769,5 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr
|
||||
assertReference(newDomain, entity.getDomain()); // Domain remains the same
|
||||
entity = getEntity(entity.getId(), "domain", ADMIN_AUTH_HEADERS);
|
||||
assertReference(newDomain, entity.getDomain()); // Domain remains the same
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
@ -394,50 +394,23 @@ public class FeedResourceTest extends OpenMetadataApplicationTest {
|
||||
|
||||
// create two announcements with start time in the future
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
AnnouncementDetails announcementDetails =
|
||||
new AnnouncementDetails()
|
||||
.withDescription("First announcement")
|
||||
.withStartTime(now.plusDays(10L).toEpochSecond(ZoneOffset.UTC))
|
||||
.withEndTime(now.plusDays(11L).toEpochSecond(ZoneOffset.UTC));
|
||||
CreateThread create =
|
||||
create()
|
||||
.withMessage("Announcement One")
|
||||
.withType(ThreadType.Announcement)
|
||||
.withAnnouncementDetails(announcementDetails);
|
||||
createAndCheck(create, USER_AUTH_HEADERS);
|
||||
String about = String.format("<#E::%s::%s>", 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<String, String> 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));
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user