mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-28 18:35:07 +00:00
parent
90c1fecc44
commit
8cc00ff0b7
@ -478,11 +478,105 @@ public interface CollectionDAO {
|
|||||||
@SqlQuery("SELECT json FROM thread_entity ORDER BY updatedAt DESC")
|
@SqlQuery("SELECT json FROM thread_entity ORDER BY updatedAt DESC")
|
||||||
List<String> list();
|
List<String> list();
|
||||||
|
|
||||||
|
@SqlQuery("SELECT count(id) FROM thread_entity WHERE resolved = :resolved")
|
||||||
|
int listCount(@Bind("resolved") boolean resolved);
|
||||||
|
|
||||||
|
@SqlQuery(
|
||||||
|
"SELECT json FROM thread_entity "
|
||||||
|
+ "WHERE updatedAt > :before AND resolved = :resolved "
|
||||||
|
+ "ORDER BY updatedAt DESC "
|
||||||
|
+ "LIMIT :limit")
|
||||||
|
List<String> listBefore(@Bind("limit") int limit, @Bind("before") long before, @Bind("resolved") boolean resolved);
|
||||||
|
|
||||||
|
@SqlQuery(
|
||||||
|
"SELECT json FROM thread_entity "
|
||||||
|
+ "WHERE updatedAt < :after AND resolved = :resolved "
|
||||||
|
+ "ORDER BY updatedAt DESC "
|
||||||
|
+ "LIMIT :limit")
|
||||||
|
List<String> listAfter(@Bind("limit") int limit, @Bind("after") long after, @Bind("resolved") boolean resolved);
|
||||||
|
|
||||||
|
@SqlQuery(
|
||||||
|
"SELECT json FROM thread_entity WHERE updatedAt > :before AND resolved = :resolved AND "
|
||||||
|
+ "(entityId in (SELECT toId FROM entity_relationship WHERE "
|
||||||
|
+ "((fromEntity='user' AND fromId= :userId) OR "
|
||||||
|
+ "(fromEntity='team' AND fromId IN (<teamIds>))) AND relation=8) OR "
|
||||||
|
+ "id in (SELECT toId FROM entity_relationship WHERE (fromEntity='user' AND fromId= :userId AND toEntity='THREAD' AND relation IN (1,2)))) "
|
||||||
|
+ "ORDER BY updatedAt DESC "
|
||||||
|
+ "LIMIT :limit")
|
||||||
|
List<String> listThreadsByOwnerBefore(
|
||||||
|
@Bind("userId") String userId,
|
||||||
|
@BindList("teamIds") List<String> teamIds,
|
||||||
|
@Bind("limit") int limit,
|
||||||
|
@Bind("before") long before,
|
||||||
|
@Bind("resolved") boolean resolved);
|
||||||
|
|
||||||
|
@SqlQuery(
|
||||||
|
"SELECT json FROM thread_entity WHERE updatedAt < :after AND resolved = :resolved AND "
|
||||||
|
+ "(entityId in (SELECT toId FROM entity_relationship WHERE "
|
||||||
|
+ "((fromEntity='user' AND fromId= :userId) OR "
|
||||||
|
+ "(fromEntity='team' AND fromId IN (<teamIds>))) AND relation=8) OR "
|
||||||
|
+ "id in (SELECT toId FROM entity_relationship WHERE (fromEntity='user' AND fromId= :userId AND toEntity='THREAD' AND relation IN (1,2)))) "
|
||||||
|
+ "ORDER BY updatedAt DESC "
|
||||||
|
+ "LIMIT :limit")
|
||||||
|
List<String> listThreadsByOwnerAfter(
|
||||||
|
@Bind("userId") String userId,
|
||||||
|
@BindList("teamIds") List<String> teamIds,
|
||||||
|
@Bind("limit") int limit,
|
||||||
|
@Bind("after") long after,
|
||||||
|
@Bind("resolved") boolean resolved);
|
||||||
|
|
||||||
|
@SqlQuery(
|
||||||
|
"SELECT count(id) FROM thread_entity WHERE resolved = :resolved AND "
|
||||||
|
+ "(entityId in (SELECT toId FROM entity_relationship WHERE "
|
||||||
|
+ "((fromEntity='user' AND fromId= :userId) OR "
|
||||||
|
+ "(fromEntity='team' AND fromId IN (<teamIds>))) AND relation=8) OR "
|
||||||
|
+ "id in (SELECT toId FROM entity_relationship WHERE (fromEntity='user' AND fromId= :userId AND toEntity='THREAD' AND relation IN (1,2)))) ")
|
||||||
|
int listCountThreadsByOwner(
|
||||||
|
@Bind("userId") String userId, @BindList("teamIds") List<String> teamIds, @Bind("resolved") boolean resolved);
|
||||||
|
|
||||||
|
@SqlQuery(
|
||||||
|
"SELECT json FROM thread_entity WHERE updatedAt > :before AND resolved = :resolved AND "
|
||||||
|
+ "id in (SELECT fromFQN FROM field_relationship WHERE "
|
||||||
|
+ "toFQN LIKE CONCAT(:fqnPrefix, '%') AND fromType='THREAD' AND toType LIKE CONCAT(:toType, '%') AND relation= :relation) "
|
||||||
|
+ "ORDER BY updatedAt DESC "
|
||||||
|
+ "LIMIT :limit")
|
||||||
|
List<String> listThreadsByEntityLinkBefore(
|
||||||
|
@Bind("fqnPrefix") String fqnPrefix,
|
||||||
|
@Bind("toType") String toType,
|
||||||
|
@Bind("limit") int limit,
|
||||||
|
@Bind("before") long before,
|
||||||
|
@Bind("resolved") boolean resolved,
|
||||||
|
@Bind("relation") int relation);
|
||||||
|
|
||||||
|
@SqlQuery(
|
||||||
|
"SELECT json FROM thread_entity WHERE updatedAt < :after AND resolved = :resolved AND "
|
||||||
|
+ "id in (SELECT fromFQN FROM field_relationship WHERE "
|
||||||
|
+ "toFQN LIKE CONCAT(:fqnPrefix, '%') AND fromType='THREAD' AND toType LIKE CONCAT(:toType, '%') AND relation= :relation) "
|
||||||
|
+ "ORDER BY updatedAt DESC "
|
||||||
|
+ "LIMIT :limit")
|
||||||
|
List<String> listThreadsByEntityLinkAfter(
|
||||||
|
@Bind("fqnPrefix") String fqnPrefix,
|
||||||
|
@Bind("toType") String toType,
|
||||||
|
@Bind("limit") int limit,
|
||||||
|
@Bind("after") long after,
|
||||||
|
@Bind("resolved") boolean resolved,
|
||||||
|
@Bind("relation") int relation);
|
||||||
|
|
||||||
|
@SqlQuery(
|
||||||
|
"SELECT count(id) FROM thread_entity WHERE resolved = :resolved AND "
|
||||||
|
+ "id in (SELECT fromFQN FROM field_relationship WHERE "
|
||||||
|
+ "toFQN LIKE CONCAT(:fqnPrefix, '%') AND fromType='THREAD' AND toType LIKE CONCAT(:toType, '%') AND relation= :relation)")
|
||||||
|
int listCountThreadsByEntityLink(
|
||||||
|
@Bind("fqnPrefix") String fqnPrefix,
|
||||||
|
@Bind("toType") String toType,
|
||||||
|
@Bind("resolved") boolean resolved,
|
||||||
|
@Bind("relation") int relation);
|
||||||
|
|
||||||
@SqlUpdate("UPDATE thread_entity SET json = :json where id = :id")
|
@SqlUpdate("UPDATE thread_entity SET json = :json where id = :id")
|
||||||
void update(@Bind("id") String id, @Bind("json") String json);
|
void update(@Bind("id") String id, @Bind("json") String json);
|
||||||
|
|
||||||
@SqlQuery(
|
@SqlQuery(
|
||||||
"SELECT entityLink, COUNT(*) count FROM field_relationship fr INNER JOIN thread_entity te ON fr.fromFQN=te.id "
|
"SELECT entityLink, COUNT(id) count FROM field_relationship fr INNER JOIN thread_entity te ON fr.fromFQN=te.id "
|
||||||
+ "WHERE fr.toFQN LIKE CONCAT(:fqnPrefix, '%') AND fr.toType like concat(:toType, '%') AND fr.fromType = :fromType "
|
+ "WHERE fr.toFQN LIKE CONCAT(:fqnPrefix, '%') AND fr.toType like concat(:toType, '%') AND fr.fromType = :fromType "
|
||||||
+ "AND fr.relation = :relation AND te.resolved= :isResolved "
|
+ "AND fr.relation = :relation AND te.resolved= :isResolved "
|
||||||
+ "GROUP BY entityLink")
|
+ "GROUP BY entityLink")
|
||||||
@ -495,29 +589,98 @@ public interface CollectionDAO {
|
|||||||
@Bind("isResolved") boolean isResolved);
|
@Bind("isResolved") boolean isResolved);
|
||||||
|
|
||||||
@SqlQuery(
|
@SqlQuery(
|
||||||
"SELECT entityLink, COUNT(*) count FROM thread_entity WHERE (id IN (<threadIds>)) "
|
"SELECT entityLink, COUNT(id) count FROM thread_entity WHERE resolved = :resolved AND "
|
||||||
|
+ "id in (SELECT toId FROM entity_relationship WHERE "
|
||||||
|
+ "(((fromEntity='user' AND fromId= :userId) OR "
|
||||||
|
+ "(fromEntity='team' AND fromId IN (<teamIds>))) AND relation=8) OR "
|
||||||
|
+ "(fromEntity='user' AND fromId= :userId AND toEntity='THREAD' AND relation IN (1,2))) "
|
||||||
|
+ "GROUP BY entityLink")
|
||||||
|
@RegisterRowMapper(CountFieldMapper.class)
|
||||||
|
List<List<String>> listCountByOwner(
|
||||||
|
@Bind("userId") String userId, @BindList("teamIds") List<String> teamIds, @Bind("resolved") boolean resolved);
|
||||||
|
|
||||||
|
@SqlQuery(
|
||||||
|
"SELECT entityLink, COUNT(id) count FROM thread_entity WHERE (id IN (<threadIds>)) "
|
||||||
+ "AND resolved= :isResolved GROUP BY entityLink")
|
+ "AND resolved= :isResolved GROUP BY entityLink")
|
||||||
@RegisterRowMapper(CountFieldMapper.class)
|
@RegisterRowMapper(CountFieldMapper.class)
|
||||||
List<List<String>> listCountByThreads(
|
List<List<String>> listCountByThreads(
|
||||||
@BindList("threadIds") List<String> threadIds, @Bind("isResolved") boolean isResolved);
|
@BindList("threadIds") List<String> threadIds, @Bind("isResolved") boolean isResolved);
|
||||||
|
|
||||||
@SqlQuery(
|
@SqlQuery(
|
||||||
"SELECT id FROM thread_entity WHERE entityId in ("
|
"SELECT json FROM thread_entity WHERE updatedAt > :before AND resolved = :resolved AND entityId in ("
|
||||||
|
+ "SELECT toId FROM entity_relationship WHERE "
|
||||||
|
+ "((fromEntity='user' AND fromId= :userId) OR "
|
||||||
|
+ "(fromEntity='team' AND fromId IN (<teamIds>))) AND relation= :relation) "
|
||||||
|
+ "ORDER BY updatedAt DESC "
|
||||||
|
+ "LIMIT :limit")
|
||||||
|
List<String> listThreadsByFollowsBefore(
|
||||||
|
@Bind("userId") String userId,
|
||||||
|
@Bind("limit") int limit,
|
||||||
|
@Bind("before") long before,
|
||||||
|
@Bind("resolved") boolean resolved,
|
||||||
|
@Bind("relation") int relation);
|
||||||
|
|
||||||
|
@SqlQuery(
|
||||||
|
"SELECT json FROM thread_entity WHERE updatedAt < :after AND resolved = :resolved AND entityId in ("
|
||||||
|
+ "SELECT toId FROM entity_relationship WHERE "
|
||||||
|
+ "((fromEntity='user' AND fromId= :userId) OR "
|
||||||
|
+ "(fromEntity='team' AND fromId IN (<teamIds>))) AND relation= :relation) "
|
||||||
|
+ "ORDER BY updatedAt DESC "
|
||||||
|
+ "LIMIT :limit")
|
||||||
|
List<String> listThreadsByFollowsAfter(
|
||||||
|
@Bind("userId") String userId,
|
||||||
|
@Bind("limit") int limit,
|
||||||
|
@Bind("after") long after,
|
||||||
|
@Bind("resolved") boolean resolved,
|
||||||
|
@Bind("relation") int relation);
|
||||||
|
|
||||||
|
@SqlQuery(
|
||||||
|
"SELECT count(id) FROM thread_entity WHERE resolved = :resolved AND entityId in ("
|
||||||
+ "SELECT toId FROM entity_relationship WHERE "
|
+ "SELECT toId FROM entity_relationship WHERE "
|
||||||
+ "((fromEntity='user' AND fromId= :userId) OR "
|
+ "((fromEntity='user' AND fromId= :userId) OR "
|
||||||
+ "(fromEntity='team' AND fromId IN (<teamIds>))) AND relation= :relation)")
|
+ "(fromEntity='team' AND fromId IN (<teamIds>))) AND relation= :relation)")
|
||||||
List<String> listUserThreadsFromER(
|
int listCountThreadsByFollows(
|
||||||
@Bind("userId") String userId, @BindList("teamIds") List<String> teamIds, @Bind("relation") int relation);
|
@Bind("userId") String userId, @Bind("resolved") boolean resolved, @Bind("relation") int relation);
|
||||||
|
|
||||||
@SqlQuery(
|
@SqlQuery(
|
||||||
"SELECT id FROM thread_entity WHERE id in ("
|
"SELECT json FROM thread_entity WHERE updatedAt > :before AND resolved = :resolved AND id in ("
|
||||||
+ "SELECT toFQN FROM field_relationship WHERE "
|
+ "SELECT toFQN FROM field_relationship WHERE "
|
||||||
+ "((fromType='user' AND fromFQN= :userName) OR "
|
+ "((fromType='user' AND fromFQN= :userName) OR "
|
||||||
+ "(fromType='team' AND fromFQN IN (<teamNames>))) AND toType = :toType AND relation = :relation)")
|
+ "(fromType='team' AND fromFQN IN (<teamNames>))) AND toType='THREAD' AND relation= :relation) "
|
||||||
List<String> listUserThreadsFromFR(
|
+ "ORDER BY updatedAt DESC "
|
||||||
|
+ "LIMIT :limit")
|
||||||
|
List<String> listThreadsByMentionsBefore(
|
||||||
@Bind("userName") String userName,
|
@Bind("userName") String userName,
|
||||||
@BindList("teamNames") List<String> teamNames,
|
@BindList("teamNames") List<String> teamNames,
|
||||||
@Bind("toType") String toType,
|
@Bind("limit") int limit,
|
||||||
|
@Bind("before") long before,
|
||||||
|
@Bind("resolved") boolean resolved,
|
||||||
|
@Bind("relation") int relation);
|
||||||
|
|
||||||
|
@SqlQuery(
|
||||||
|
"SELECT json FROM thread_entity WHERE updatedAt < :after AND resolved = :resolved AND id in ("
|
||||||
|
+ "SELECT toFQN FROM field_relationship WHERE "
|
||||||
|
+ "((fromType='user' AND fromFQN= :userName) OR "
|
||||||
|
+ "(fromType='team' AND fromFQN IN (<teamNames>))) AND toType='THREAD' AND relation= :relation) "
|
||||||
|
+ "ORDER BY updatedAt DESC "
|
||||||
|
+ "LIMIT :limit")
|
||||||
|
List<String> listThreadsByMentionsAfter(
|
||||||
|
@Bind("userName") String userName,
|
||||||
|
@BindList("teamNames") List<String> teamNames,
|
||||||
|
@Bind("limit") int limit,
|
||||||
|
@Bind("after") long after,
|
||||||
|
@Bind("resolved") boolean resolved,
|
||||||
|
@Bind("relation") int relation);
|
||||||
|
|
||||||
|
@SqlQuery(
|
||||||
|
"SELECT count(id) FROM thread_entity WHERE resolved = :resolved AND id in ("
|
||||||
|
+ "SELECT toFQN FROM field_relationship WHERE "
|
||||||
|
+ "((fromType='user' AND fromFQN= :userName) OR "
|
||||||
|
+ "(fromType='team' AND fromFQN IN (<teamNames>))) AND toType='THREAD' AND relation= :relation) ")
|
||||||
|
int listCountThreadsByMentions(
|
||||||
|
@Bind("userName") String userName,
|
||||||
|
@BindList("teamNames") List<String> teamNames,
|
||||||
|
@Bind("resolved") boolean resolved,
|
||||||
@Bind("relation") int relation);
|
@Bind("relation") int relation);
|
||||||
|
|
||||||
class CountFieldMapper implements RowMapper<List<String>> {
|
class CountFieldMapper implements RowMapper<List<String>> {
|
||||||
|
@ -18,10 +18,8 @@ import java.io.IOException;
|
|||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -50,6 +48,7 @@ import org.openmetadata.catalog.util.JsonUtils;
|
|||||||
import org.openmetadata.catalog.util.RestUtil;
|
import org.openmetadata.catalog.util.RestUtil;
|
||||||
import org.openmetadata.catalog.util.RestUtil.DeleteResponse;
|
import org.openmetadata.catalog.util.RestUtil.DeleteResponse;
|
||||||
import org.openmetadata.catalog.util.RestUtil.PatchResponse;
|
import org.openmetadata.catalog.util.RestUtil.PatchResponse;
|
||||||
|
import org.openmetadata.catalog.util.ResultList;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class FeedRepository {
|
public class FeedRepository {
|
||||||
@ -65,6 +64,11 @@ public class FeedRepository {
|
|||||||
FOLLOWS
|
FOLLOWS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum PaginationType {
|
||||||
|
BEFORE,
|
||||||
|
AFTER
|
||||||
|
}
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
public Thread create(Thread thread) throws IOException, ParseException {
|
public Thread create(Thread thread) throws IOException, ParseException {
|
||||||
String createdBy = thread.getCreatedBy();
|
String createdBy = thread.getCreatedBy();
|
||||||
@ -226,9 +230,15 @@ public class FeedRepository {
|
|||||||
} else {
|
} else {
|
||||||
EntityLink entityLink = EntityLink.parse(link);
|
EntityLink entityLink = EntityLink.parse(link);
|
||||||
EntityReference reference = EntityUtil.validateEntityLink(entityLink);
|
EntityReference reference = EntityUtil.validateEntityLink(entityLink);
|
||||||
if (reference.getType().equals(Entity.USER)) {
|
if (reference.getType().equals(Entity.USER) || reference.getType().equals(Entity.TEAM)) {
|
||||||
List<String> threadIds = getThreadIdsByOwner(reference.getId().toString());
|
if (reference.getType().equals(Entity.USER)) {
|
||||||
result = dao.feedDAO().listCountByThreads(threadIds, isResolved);
|
String userId = reference.getId().toString();
|
||||||
|
List<String> teamIds = getTeamIds(userId);
|
||||||
|
result = dao.feedDAO().listCountByOwner(userId, teamIds, isResolved);
|
||||||
|
} else {
|
||||||
|
// team is not supported
|
||||||
|
result = new ArrayList<>();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
result =
|
result =
|
||||||
dao.feedDAO()
|
dao.feedDAO()
|
||||||
@ -256,72 +266,133 @@ public class FeedRepository {
|
|||||||
return thread.getPosts();
|
return thread.getPosts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List threads based on the filters and limits in the order of the updated timestamp.
|
||||||
|
*
|
||||||
|
* @param link entity link filter
|
||||||
|
* @param limitPosts the number of posts to limit per thread
|
||||||
|
* @param userId UUID of the user. Enables UserId filter
|
||||||
|
* @param filterType Type of the filter to be applied with userId filter
|
||||||
|
* @param limit the number of threads to limit in the response
|
||||||
|
* @param pageMarker the before/after updatedTime to be used as pagination marker for queries
|
||||||
|
* @param isResolved whether the thread is resolved or open
|
||||||
|
* @param paginationType before or after
|
||||||
|
* @return a list of threads as ResultList
|
||||||
|
* @throws IOException on error
|
||||||
|
* @throws ParseException on error
|
||||||
|
*/
|
||||||
@Transaction
|
@Transaction
|
||||||
public List<Thread> listThreads(String link, int limitPosts, String userId, FilterType filterType)
|
public final ResultList<Thread> list(
|
||||||
throws IOException {
|
String link,
|
||||||
List<Thread> threads = new ArrayList<>();
|
int limitPosts,
|
||||||
|
String userId,
|
||||||
|
FilterType filterType,
|
||||||
|
int limit,
|
||||||
|
String pageMarker,
|
||||||
|
boolean isResolved,
|
||||||
|
PaginationType paginationType)
|
||||||
|
throws IOException, ParseException {
|
||||||
|
List<Thread> threads;
|
||||||
|
int total;
|
||||||
|
// Here updatedAt time is used for page marker since threads are sorted by last update time
|
||||||
|
long time = Long.MAX_VALUE;
|
||||||
|
// if paginationType is "before", it must have a pageMarker time.
|
||||||
|
// "after" could be null to get the first page. In this case we set time to MAX_VALUE
|
||||||
|
// to get any entry with updatedTime < MAX_VALUE
|
||||||
|
if (pageMarker != null) {
|
||||||
|
time = Long.parseLong(RestUtil.decodeCursor(pageMarker));
|
||||||
|
}
|
||||||
|
|
||||||
|
// No filters are enabled. Listing all the threads
|
||||||
if (link == null && userId == null) {
|
if (link == null && userId == null) {
|
||||||
// No filters are enabled. Listing all the threads
|
// Get one extra result used for computing before cursor
|
||||||
threads = JsonUtils.readObjects(dao.feedDAO().list(), Thread.class);
|
List<String> jsons;
|
||||||
|
if (paginationType == PaginationType.BEFORE) {
|
||||||
|
jsons = dao.feedDAO().listBefore(limit + 1, time, isResolved);
|
||||||
|
} else {
|
||||||
|
jsons = dao.feedDAO().listAfter(limit + 1, time, isResolved);
|
||||||
|
}
|
||||||
|
threads = JsonUtils.readObjects(jsons, Thread.class);
|
||||||
|
total = dao.feedDAO().listCount(isResolved);
|
||||||
} else {
|
} else {
|
||||||
// Either one or both the filters are enabled
|
// Either one or both the filters are enabled
|
||||||
List<String> threadIds = new ArrayList<>();
|
// we don't support both the filters together. If both are not null, entity link takes precedence
|
||||||
|
|
||||||
if (link != null) {
|
if (link != null) {
|
||||||
EntityLink entityLink = EntityLink.parse(link);
|
EntityLink entityLink = EntityLink.parse(link);
|
||||||
EntityReference reference = EntityUtil.validateEntityLink(entityLink);
|
EntityReference reference = EntityUtil.validateEntityLink(entityLink);
|
||||||
List<List<String>> result;
|
|
||||||
|
|
||||||
// For a user entityLink get created or replied relationships to the thread
|
// For a user entityLink get created or replied relationships to the thread
|
||||||
if (reference.getType().equals(Entity.USER)) {
|
if (reference.getType().equals(Entity.USER)) {
|
||||||
threadIds.addAll(getThreadIdsByOwner(reference.getId().toString()));
|
FilteredThreads filteredThreads =
|
||||||
|
getThreadsByOwner(reference.getId().toString(), limit + 1, time, isResolved, paginationType);
|
||||||
|
threads = filteredThreads.getThreads();
|
||||||
|
total = filteredThreads.getTotalCount();
|
||||||
} else {
|
} else {
|
||||||
// Only data assets are added as about
|
// Only data assets are added as about
|
||||||
result =
|
List<String> jsons;
|
||||||
dao.fieldRelationshipDAO()
|
if (paginationType == PaginationType.BEFORE) {
|
||||||
.listFromByAllPrefix(
|
jsons =
|
||||||
entityLink.getFullyQualifiedFieldValue(),
|
dao.feedDAO()
|
||||||
Entity.THREAD,
|
.listThreadsByEntityLinkBefore(
|
||||||
entityLink.getFullyQualifiedFieldType(),
|
entityLink.getFullyQualifiedFieldValue(),
|
||||||
Relationship.IS_ABOUT.ordinal());
|
entityLink.getFullyQualifiedFieldType(),
|
||||||
result.forEach(l -> threadIds.add(l.get(1)));
|
limit + 1,
|
||||||
}
|
time,
|
||||||
}
|
isResolved,
|
||||||
|
Relationship.IS_ABOUT.ordinal());
|
||||||
if (userId != null) {
|
} else {
|
||||||
List<String> userThreadIds;
|
jsons =
|
||||||
if (filterType == FilterType.FOLLOWS) {
|
dao.feedDAO()
|
||||||
userThreadIds = getThreadIdsByFollows(userId);
|
.listThreadsByEntityLinkAfter(
|
||||||
} else if (filterType == FilterType.MENTIONS) {
|
entityLink.getFullyQualifiedFieldValue(),
|
||||||
userThreadIds = getThreadIdsByMentions(userId);
|
entityLink.getFullyQualifiedFieldType(),
|
||||||
} else {
|
limit + 1,
|
||||||
userThreadIds = getThreadIdsByOwner(userId);
|
time,
|
||||||
}
|
isResolved,
|
||||||
|
Relationship.IS_ABOUT.ordinal());
|
||||||
// if both link and user filters are enabled, the filters should be applied as "AND"
|
|
||||||
if (!threadIds.isEmpty()) {
|
|
||||||
// apply user filter on top of the link filter
|
|
||||||
if (!userThreadIds.isEmpty()) {
|
|
||||||
userThreadIds = userThreadIds.stream().filter(threadIds::contains).collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
threads = JsonUtils.readObjects(jsons, Thread.class);
|
||||||
|
total =
|
||||||
|
dao.feedDAO()
|
||||||
|
.listCountThreadsByEntityLink(
|
||||||
|
entityLink.getFullyQualifiedFieldValue(),
|
||||||
|
entityLink.getFullyQualifiedFieldType(),
|
||||||
|
isResolved,
|
||||||
|
Relationship.IS_ABOUT.ordinal());
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
threadIds.addAll(userThreadIds);
|
FilteredThreads filteredThreads;
|
||||||
}
|
if (filterType == FilterType.FOLLOWS) {
|
||||||
|
filteredThreads = getThreadsByFollows(userId, limit + 1, time, isResolved, paginationType);
|
||||||
Set<String> uniqueValues = new HashSet<>();
|
} else if (filterType == FilterType.MENTIONS) {
|
||||||
for (String t : threadIds) {
|
filteredThreads = getThreadsByMentions(userId, limit + 1, time, isResolved, paginationType);
|
||||||
// If an entity has multiple relationships (created, mentioned, repliedTo etc.) to the same thread
|
} else {
|
||||||
// Don't send duplicated copies of the thread in response
|
filteredThreads = getThreadsByOwner(userId, limit + 1, time, isResolved, paginationType);
|
||||||
if (uniqueValues.add(t)) {
|
|
||||||
threads.add(EntityUtil.validate(t, dao.feedDAO().findById(t), Thread.class));
|
|
||||||
}
|
}
|
||||||
|
threads = filteredThreads.getThreads();
|
||||||
|
total = filteredThreads.getTotalCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort the list by thread updated timestamp before returning
|
|
||||||
threads.sort(Comparator.comparing(Thread::getUpdatedAt, Comparator.reverseOrder()));
|
|
||||||
}
|
}
|
||||||
return limitPostsInThreads(threads, limitPosts);
|
|
||||||
|
limitPostsInThreads(threads, limitPosts);
|
||||||
|
|
||||||
|
String beforeCursor = null;
|
||||||
|
String afterCursor = null;
|
||||||
|
if (paginationType == PaginationType.BEFORE) {
|
||||||
|
if (threads.size() > limit) { // If extra result exists, then previous page exists - return before cursor
|
||||||
|
threads.remove(0);
|
||||||
|
beforeCursor = threads.get(0).getUpdatedAt().toString();
|
||||||
|
}
|
||||||
|
afterCursor = threads.get(threads.size() - 1).getUpdatedAt().toString();
|
||||||
|
} else {
|
||||||
|
beforeCursor = pageMarker == null ? null : threads.get(0).getUpdatedAt().toString();
|
||||||
|
if (threads.size() > limit) { // If extra result exists, then next page exists - return after cursor
|
||||||
|
threads.remove(limit);
|
||||||
|
afterCursor = threads.get(limit - 1).getUpdatedAt().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ResultList<>(threads, beforeCursor, afterCursor, total);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@ -365,7 +436,13 @@ public class FeedRepository {
|
|||||||
return original.getResolved() != updated.getResolved() || !original.getMessage().equals(updated.getMessage());
|
return original.getResolved() != updated.getResolved() || !original.getMessage().equals(updated.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Thread> limitPostsInThreads(List<Thread> threads, int limitPosts) {
|
/**
|
||||||
|
* Limit the number of posts within each thread.
|
||||||
|
*
|
||||||
|
* @param threads list of threads
|
||||||
|
* @param limitPosts the number of posts to limit per thread
|
||||||
|
*/
|
||||||
|
private void limitPostsInThreads(List<Thread> threads, int limitPosts) {
|
||||||
for (Thread t : threads) {
|
for (Thread t : threads) {
|
||||||
List<Post> posts = t.getPosts();
|
List<Post> posts = t.getPosts();
|
||||||
if (posts.size() > limitPosts) {
|
if (posts.size() > limitPosts) {
|
||||||
@ -375,26 +452,62 @@ public class FeedRepository {
|
|||||||
t.withPosts(posts);
|
t.withPosts(posts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return threads;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getThreadIdsByOwner(String userId) {
|
/**
|
||||||
List<String> threadIds = new ArrayList<>();
|
* Return the threads associated with user/team owned entities and the threads that were created by or replied to by
|
||||||
|
* the user.
|
||||||
|
*
|
||||||
|
* @param userId UUID of the user
|
||||||
|
* @param limit number of threads to limit
|
||||||
|
* @param time updatedTime before/after which the results should be filtered for pagination
|
||||||
|
* @param isResolved whether the thread is resolved or open
|
||||||
|
* @param paginationType before or after
|
||||||
|
* @return a list of threads and the total count of threads
|
||||||
|
* @throws IOException on error
|
||||||
|
*/
|
||||||
|
private FilteredThreads getThreadsByOwner(
|
||||||
|
String userId, int limit, long time, boolean isResolved, PaginationType paginationType) throws IOException {
|
||||||
// add threads on user or team owned entities
|
// add threads on user or team owned entities
|
||||||
|
// and threads created by or replied to by the user
|
||||||
|
List<String> teamIds = getTeamIds(userId);
|
||||||
|
List<String> jsons;
|
||||||
|
if (paginationType == PaginationType.BEFORE) {
|
||||||
|
jsons = dao.feedDAO().listThreadsByOwnerBefore(userId, teamIds, limit, time, isResolved);
|
||||||
|
} else {
|
||||||
|
jsons = dao.feedDAO().listThreadsByOwnerAfter(userId, teamIds, limit, time, isResolved);
|
||||||
|
}
|
||||||
|
List<Thread> threads = JsonUtils.readObjects(jsons, Thread.class);
|
||||||
|
int totalCount = dao.feedDAO().listCountThreadsByOwner(userId, teamIds, isResolved);
|
||||||
|
return new FilteredThreads(threads, totalCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of team ids that the given user is a part of.
|
||||||
|
*
|
||||||
|
* @param userId UUID of the user.
|
||||||
|
* @return list of team ids.
|
||||||
|
*/
|
||||||
|
private List<String> getTeamIds(String userId) {
|
||||||
List<String> teamIds = dao.relationshipDAO().findFrom(userId, Entity.USER, Relationship.HAS.ordinal(), Entity.TEAM);
|
List<String> teamIds = dao.relationshipDAO().findFrom(userId, Entity.USER, Relationship.HAS.ordinal(), Entity.TEAM);
|
||||||
if (teamIds.isEmpty()) {
|
if (teamIds.isEmpty()) {
|
||||||
teamIds = List.of(StringUtils.EMPTY);
|
teamIds = List.of(StringUtils.EMPTY);
|
||||||
}
|
}
|
||||||
threadIds.addAll(dao.feedDAO().listUserThreadsFromER(userId, teamIds, Relationship.OWNS.ordinal()));
|
return teamIds;
|
||||||
|
|
||||||
// add threads created by or replied to by the user
|
|
||||||
threadIds.addAll(dao.relationshipDAO().findTo(userId, Entity.USER, Relationship.CREATED.ordinal(), Entity.THREAD));
|
|
||||||
threadIds.addAll(
|
|
||||||
dao.relationshipDAO().findTo(userId, Entity.USER, Relationship.REPLIED_TO.ordinal(), Entity.THREAD));
|
|
||||||
return threadIds;
|
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
private List<String> getThreadIdsByMentions(String userId) throws IOException {
|
* Returns the threads where the user or the team they belong to were mentioned by other users with @mention.
|
||||||
|
*
|
||||||
|
* @param userId UUID of the user
|
||||||
|
* @param limit number of threads to limit
|
||||||
|
* @param time updatedTime before/after which the results should be filtered for pagination
|
||||||
|
* @param isResolved whether the thread is resolved or open
|
||||||
|
* @param paginationType before or after
|
||||||
|
* @return a list of threads and the total count of threads
|
||||||
|
* @throws IOException on error
|
||||||
|
*/
|
||||||
|
private FilteredThreads getThreadsByMentions(
|
||||||
|
String userId, int limit, long time, boolean isResolved, PaginationType paginationType) throws IOException {
|
||||||
List<EntityReference> teams =
|
List<EntityReference> teams =
|
||||||
EntityUtil.populateEntityReferences(
|
EntityUtil.populateEntityReferences(
|
||||||
dao.relationshipDAO().findFromEntity(userId, Entity.USER, Relationship.HAS.ordinal(), Entity.TEAM));
|
dao.relationshipDAO().findFromEntity(userId, Entity.USER, Relationship.HAS.ordinal(), Entity.TEAM));
|
||||||
@ -404,13 +517,65 @@ public class FeedRepository {
|
|||||||
}
|
}
|
||||||
User user = dao.userDAO().findEntityById(UUID.fromString(userId));
|
User user = dao.userDAO().findEntityById(UUID.fromString(userId));
|
||||||
|
|
||||||
// Return all the thread ids where the user or team was mentioned
|
// Return the threads where the user or team was mentioned
|
||||||
return new ArrayList<>(
|
List<String> jsons;
|
||||||
|
if (paginationType == PaginationType.BEFORE) {
|
||||||
|
jsons =
|
||||||
|
dao.feedDAO()
|
||||||
|
.listThreadsByMentionsBefore(
|
||||||
|
user.getName(), teamNames, limit, time, isResolved, Relationship.MENTIONED_IN.ordinal());
|
||||||
|
} else {
|
||||||
|
jsons =
|
||||||
|
dao.feedDAO()
|
||||||
|
.listThreadsByMentionsAfter(
|
||||||
|
user.getName(), teamNames, limit, time, isResolved, Relationship.MENTIONED_IN.ordinal());
|
||||||
|
}
|
||||||
|
List<Thread> threads = JsonUtils.readObjects(jsons, Thread.class);
|
||||||
|
int totalCount =
|
||||||
dao.feedDAO()
|
dao.feedDAO()
|
||||||
.listUserThreadsFromFR(user.getName(), teamNames, Entity.THREAD, Relationship.MENTIONED_IN.ordinal()));
|
.listCountThreadsByMentions(user.getName(), teamNames, isResolved, Relationship.MENTIONED_IN.ordinal());
|
||||||
|
return new FilteredThreads(threads, totalCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getThreadIdsByFollows(String userId) {
|
/**
|
||||||
return dao.feedDAO().listUserThreadsFromER(userId, List.of(StringUtils.EMPTY), Relationship.FOLLOWS.ordinal());
|
* Returns the threads that are associated with the entities followed by the user.
|
||||||
|
*
|
||||||
|
* @param userId UUID of the user
|
||||||
|
* @param limit number of threads to limit
|
||||||
|
* @param time updatedTime before/after which the results should be filtered for pagination
|
||||||
|
* @param isResolved whether the thread is resolved or open
|
||||||
|
* @param paginationType before or after
|
||||||
|
* @return a list of threads and the total count of threads
|
||||||
|
* @throws IOException on error
|
||||||
|
*/
|
||||||
|
private FilteredThreads getThreadsByFollows(
|
||||||
|
String userId, int limit, long time, boolean isResolved, PaginationType paginationType) throws IOException {
|
||||||
|
List<String> jsons;
|
||||||
|
if (paginationType == PaginationType.BEFORE) {
|
||||||
|
jsons = dao.feedDAO().listThreadsByFollowsBefore(userId, limit, time, isResolved, Relationship.FOLLOWS.ordinal());
|
||||||
|
} else {
|
||||||
|
jsons = dao.feedDAO().listThreadsByFollowsAfter(userId, limit, time, isResolved, Relationship.FOLLOWS.ordinal());
|
||||||
|
}
|
||||||
|
List<Thread> threads = JsonUtils.readObjects(jsons, Thread.class);
|
||||||
|
int totalCount = dao.feedDAO().listCountThreadsByFollows(userId, isResolved, Relationship.FOLLOWS.ordinal());
|
||||||
|
return new FilteredThreads(threads, totalCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FilteredThreads {
|
||||||
|
List<Thread> threads;
|
||||||
|
int totalCount;
|
||||||
|
|
||||||
|
public FilteredThreads(List<Thread> threads, int totalCount) {
|
||||||
|
this.threads = threads;
|
||||||
|
this.totalCount = totalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Thread> getThreads() {
|
||||||
|
return threads;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalCount() {
|
||||||
|
return totalCount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,7 @@ import org.openmetadata.catalog.entity.feed.Thread;
|
|||||||
import org.openmetadata.catalog.jdbi3.CollectionDAO;
|
import org.openmetadata.catalog.jdbi3.CollectionDAO;
|
||||||
import org.openmetadata.catalog.jdbi3.FeedRepository;
|
import org.openmetadata.catalog.jdbi3.FeedRepository;
|
||||||
import org.openmetadata.catalog.jdbi3.FeedRepository.FilterType;
|
import org.openmetadata.catalog.jdbi3.FeedRepository.FilterType;
|
||||||
|
import org.openmetadata.catalog.jdbi3.FeedRepository.PaginationType;
|
||||||
import org.openmetadata.catalog.resources.Collection;
|
import org.openmetadata.catalog.resources.Collection;
|
||||||
import org.openmetadata.catalog.security.Authorizer;
|
import org.openmetadata.catalog.security.Authorizer;
|
||||||
import org.openmetadata.catalog.security.SecurityUtil;
|
import org.openmetadata.catalog.security.SecurityUtil;
|
||||||
@ -130,7 +131,7 @@ public class FeedResource {
|
|||||||
description = "List of threads",
|
description = "List of threads",
|
||||||
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ThreadList.class)))
|
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ThreadList.class)))
|
||||||
})
|
})
|
||||||
public ThreadList list(
|
public ResultList<Thread> list(
|
||||||
@Context UriInfo uriInfo,
|
@Context UriInfo uriInfo,
|
||||||
@Parameter(
|
@Parameter(
|
||||||
description = "Limit the number of posts sorted by chronological order (1 to 1000000, default = 3)",
|
description = "Limit the number of posts sorted by chronological order (1 to 1000000, default = 3)",
|
||||||
@ -140,6 +141,18 @@ public class FeedResource {
|
|||||||
@DefaultValue("3")
|
@DefaultValue("3")
|
||||||
@QueryParam("limitPosts")
|
@QueryParam("limitPosts")
|
||||||
int limitPosts,
|
int limitPosts,
|
||||||
|
@Parameter(description = "Limit the number of threads returned. (1 to 1000000, default = 10)")
|
||||||
|
@DefaultValue("10")
|
||||||
|
@Min(1)
|
||||||
|
@Max(1000000)
|
||||||
|
@QueryParam("limit")
|
||||||
|
int limitParam,
|
||||||
|
@Parameter(description = "Returns list of threads before this cursor", schema = @Schema(type = "string"))
|
||||||
|
@QueryParam("before")
|
||||||
|
String before,
|
||||||
|
@Parameter(description = "Returns list of threads after this cursor", schema = @Schema(type = "string"))
|
||||||
|
@QueryParam("after")
|
||||||
|
String after,
|
||||||
@Parameter(
|
@Parameter(
|
||||||
description = "Filter threads by entity link",
|
description = "Filter threads by entity link",
|
||||||
schema = @Schema(type = "string", example = "<E#/{entityType}/{entityFQN}/{fieldName}>"))
|
schema = @Schema(type = "string", example = "<E#/{entityType}/{entityFQN}/{fieldName}>"))
|
||||||
@ -147,7 +160,7 @@ public class FeedResource {
|
|||||||
String entityLink,
|
String entityLink,
|
||||||
@Parameter(
|
@Parameter(
|
||||||
description =
|
description =
|
||||||
"Filter threads by user id. This filter requires a 'filterType' query param. The default filter type is 'OWNER'",
|
"Filter threads by user id. This filter requires a 'filterType' query param. The default filter type is 'OWNER'. This filter cannot be combined with the entityLink filter.",
|
||||||
schema = @Schema(type = "string"))
|
schema = @Schema(type = "string"))
|
||||||
@QueryParam("userId")
|
@QueryParam("userId")
|
||||||
String userId,
|
String userId,
|
||||||
@ -156,9 +169,24 @@ public class FeedResource {
|
|||||||
"Filter type definition for the user filter. It can take one of 'OWNER', 'FOLLOWS', 'MENTIONS'. This must be used with the 'user' query param",
|
"Filter type definition for the user filter. It can take one of 'OWNER', 'FOLLOWS', 'MENTIONS'. This must be used with the 'user' query param",
|
||||||
schema = @Schema(implementation = FilterType.class))
|
schema = @Schema(implementation = FilterType.class))
|
||||||
@QueryParam("filterType")
|
@QueryParam("filterType")
|
||||||
FilterType filterType)
|
FilterType filterType,
|
||||||
throws IOException {
|
@Parameter(description = "Filter threads by whether they are resolved or not. By default resolved is false")
|
||||||
return new ThreadList(addHref(uriInfo, dao.listThreads(entityLink, limitPosts, userId, filterType)));
|
@DefaultValue("false")
|
||||||
|
@QueryParam("resolved")
|
||||||
|
boolean resolved)
|
||||||
|
throws IOException, ParseException {
|
||||||
|
RestUtil.validateCursors(before, after);
|
||||||
|
|
||||||
|
ResultList<Thread> threads;
|
||||||
|
if (before != null) { // Reverse paging
|
||||||
|
threads =
|
||||||
|
dao.list(entityLink, limitPosts, userId, filterType, limitParam, before, resolved, PaginationType.BEFORE);
|
||||||
|
} else { // Forward paging or first page
|
||||||
|
threads = dao.list(entityLink, limitPosts, userId, filterType, limitParam, after, resolved, PaginationType.AFTER);
|
||||||
|
}
|
||||||
|
threads.getData().forEach(thread -> addHref(uriInfo, thread));
|
||||||
|
|
||||||
|
return threads;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
@ -121,16 +121,16 @@ public class TeamResource extends EntityResource<Team, TeamRepository> {
|
|||||||
schema = @Schema(type = "string", example = FIELDS))
|
schema = @Schema(type = "string", example = FIELDS))
|
||||||
@QueryParam("fields")
|
@QueryParam("fields")
|
||||||
String fieldsParam,
|
String fieldsParam,
|
||||||
@Parameter(description = "Limit the number tables returned. (1 to 1000000, default = 10)")
|
@Parameter(description = "Limit the number of teams returned. (1 to 1000000, default = 10)")
|
||||||
@DefaultValue("10")
|
@DefaultValue("10")
|
||||||
@Min(0)
|
@Min(0)
|
||||||
@Max(1000000)
|
@Max(1000000)
|
||||||
@QueryParam("limit")
|
@QueryParam("limit")
|
||||||
int limitParam,
|
int limitParam,
|
||||||
@Parameter(description = "Returns list of tables before this cursor", schema = @Schema(type = "string"))
|
@Parameter(description = "Returns list of teams before this cursor", schema = @Schema(type = "string"))
|
||||||
@QueryParam("before")
|
@QueryParam("before")
|
||||||
String before,
|
String before,
|
||||||
@Parameter(description = "Returns list of tables after this cursor", schema = @Schema(type = "string"))
|
@Parameter(description = "Returns list of teams after this cursor", schema = @Schema(type = "string"))
|
||||||
@QueryParam("after")
|
@QueryParam("after")
|
||||||
String after,
|
String after,
|
||||||
@Parameter(
|
@Parameter(
|
||||||
|
@ -19,6 +19,7 @@ import static javax.ws.rs.core.Response.Status.NOT_FOUND;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
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.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.openmetadata.catalog.exception.CatalogExceptionMessage.entityNotFound;
|
import static org.openmetadata.catalog.exception.CatalogExceptionMessage.entityNotFound;
|
||||||
import static org.openmetadata.catalog.exception.CatalogExceptionMessage.noPermission;
|
import static org.openmetadata.catalog.exception.CatalogExceptionMessage.noPermission;
|
||||||
@ -39,6 +40,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import javax.json.JsonPatch;
|
import javax.json.JsonPatch;
|
||||||
import javax.ws.rs.client.WebTarget;
|
import javax.ws.rs.client.WebTarget;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -49,6 +51,10 @@ import org.junit.jupiter.api.MethodOrderer;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.TestInfo;
|
import org.junit.jupiter.api.TestInfo;
|
||||||
import org.junit.jupiter.api.TestMethodOrder;
|
import org.junit.jupiter.api.TestMethodOrder;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.junit.jupiter.params.provider.NullSource;
|
||||||
import org.openmetadata.catalog.CatalogApplicationTest;
|
import org.openmetadata.catalog.CatalogApplicationTest;
|
||||||
import org.openmetadata.catalog.Entity;
|
import org.openmetadata.catalog.Entity;
|
||||||
import org.openmetadata.catalog.api.data.CreateTable;
|
import org.openmetadata.catalog.api.data.CreateTable;
|
||||||
@ -194,12 +200,13 @@ public class FeedResourceTest extends CatalogApplicationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void post_validThreadAndList_200(TestInfo test) throws IOException {
|
void post_validThreadAndList_200(TestInfo test) throws IOException {
|
||||||
int totalThreadCount = listThreads(null, null, ADMIN_AUTH_HEADERS).getData().size();
|
int totalThreadCount = listThreads(null, null, ADMIN_AUTH_HEADERS).getPaging().getTotal();
|
||||||
int userThreadCount = listThreads(USER_LINK, null, ADMIN_AUTH_HEADERS).getData().size();
|
int userThreadCount = listThreads(USER_LINK, null, ADMIN_AUTH_HEADERS).getPaging().getTotal();
|
||||||
int teamThreadCount = listThreads(TEAM_LINK, null, ADMIN_AUTH_HEADERS).getData().size();
|
int tableThreadCount = listThreads(TABLE_LINK, null, ADMIN_AUTH_HEADERS).getPaging().getTotal();
|
||||||
int tableThreadCount = listThreads(TABLE_LINK, null, ADMIN_AUTH_HEADERS).getData().size();
|
int tableDescriptionThreadCount =
|
||||||
int tableDescriptionThreadCount = listThreads(TABLE_DESCRIPTION_LINK, null, ADMIN_AUTH_HEADERS).getData().size();
|
listThreads(TABLE_DESCRIPTION_LINK, null, ADMIN_AUTH_HEADERS).getPaging().getTotal();
|
||||||
int tableColumnDescriptionThreadCount = listThreads(TABLE_COLUMN_LINK, null, ADMIN_AUTH_HEADERS).getData().size();
|
int tableColumnDescriptionThreadCount =
|
||||||
|
listThreads(TABLE_COLUMN_LINK, null, ADMIN_AUTH_HEADERS).getPaging().getTotal();
|
||||||
|
|
||||||
CreateThread create =
|
CreateThread create =
|
||||||
create()
|
create()
|
||||||
@ -217,45 +224,51 @@ public class FeedResourceTest extends CatalogApplicationTest {
|
|||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
createAndCheck(create, userAuthHeaders);
|
createAndCheck(create, userAuthHeaders);
|
||||||
// List all the threads and make sure the number of threads increased by 1
|
// List all the threads and make sure the number of threads increased by 1
|
||||||
assertEquals(++userThreadCount, listThreads(USER_LINK, null, userAuthHeaders).getData().size()); // Mentioned user
|
assertEquals(
|
||||||
// TODO: There is no support for team mentions yet.
|
++userThreadCount, listThreads(USER_LINK, null, userAuthHeaders).getPaging().getTotal()); // Mentioned user
|
||||||
// assertEquals(++teamThreadCount, listThreads(TEAM_LINK, null, userAuthHeaders).getData().size()); // Mentioned
|
assertEquals(
|
||||||
// team
|
++tableThreadCount, listThreads(TABLE_LINK, null, userAuthHeaders).getPaging().getTotal()); // About TABLE
|
||||||
assertEquals(++tableThreadCount, listThreads(TABLE_LINK, null, userAuthHeaders).getData().size()); // About TABLE
|
assertEquals(
|
||||||
assertEquals(++totalThreadCount, listThreads(null, null, userAuthHeaders).getData().size()); // Overall threads
|
++totalThreadCount, listThreads(null, null, userAuthHeaders).getPaging().getTotal()); // Overall threads
|
||||||
}
|
}
|
||||||
|
|
||||||
// List threads should not include mentioned entities
|
// List threads should not include mentioned entities
|
||||||
// It should only include threads which are about the entity link
|
// It should only include threads which are about the entity link
|
||||||
assertEquals(
|
assertEquals(
|
||||||
tableDescriptionThreadCount,
|
tableDescriptionThreadCount,
|
||||||
listThreads(TABLE_DESCRIPTION_LINK, null, userAuthHeaders).getData().size()); // About TABLE Description
|
listThreads(TABLE_DESCRIPTION_LINK, null, userAuthHeaders).getPaging().getTotal()); // About TABLE Description
|
||||||
assertEquals(
|
assertEquals(
|
||||||
tableColumnDescriptionThreadCount,
|
tableColumnDescriptionThreadCount,
|
||||||
listThreads(TABLE_COLUMN_LINK, null, userAuthHeaders).getData().size()); // About TABLE Column Description
|
listThreads(TABLE_COLUMN_LINK, null, userAuthHeaders).getPaging().getTotal()); // About TABLE Column Description
|
||||||
|
|
||||||
create.withAbout(TABLE_DESCRIPTION_LINK);
|
create.withAbout(TABLE_DESCRIPTION_LINK);
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
createAndCheck(create, userAuthHeaders);
|
createAndCheck(create, userAuthHeaders);
|
||||||
// List all the threads and make sure the number of threads increased by 1
|
// List all the threads and make sure the number of threads increased by 1
|
||||||
assertEquals(++userThreadCount, listThreads(USER_LINK, null, userAuthHeaders).getData().size()); // Mentioned user
|
assertEquals(
|
||||||
assertEquals(++tableThreadCount, listThreads(TABLE_LINK, null, userAuthHeaders).getData().size()); // About TABLE
|
++userThreadCount, listThreads(USER_LINK, null, userAuthHeaders).getPaging().getTotal()); // Mentioned user
|
||||||
|
assertEquals(
|
||||||
|
++tableThreadCount, listThreads(TABLE_LINK, null, userAuthHeaders).getPaging().getTotal()); // About TABLE
|
||||||
assertEquals(
|
assertEquals(
|
||||||
++tableDescriptionThreadCount,
|
++tableDescriptionThreadCount,
|
||||||
listThreads(TABLE_DESCRIPTION_LINK, null, userAuthHeaders).getData().size()); // About TABLE Description
|
listThreads(TABLE_DESCRIPTION_LINK, null, userAuthHeaders).getPaging().getTotal()); // About TABLE Description
|
||||||
assertEquals(++totalThreadCount, listThreads(null, null, userAuthHeaders).getData().size()); // Overall threads
|
assertEquals(
|
||||||
|
++totalThreadCount, listThreads(null, null, userAuthHeaders).getPaging().getTotal()); // Overall threads
|
||||||
}
|
}
|
||||||
|
|
||||||
create.withAbout(TABLE_COLUMN_LINK);
|
create.withAbout(TABLE_COLUMN_LINK);
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
createAndCheck(create, userAuthHeaders);
|
createAndCheck(create, userAuthHeaders);
|
||||||
// List all the threads and make sure the number of threads increased by 1
|
// List all the threads and make sure the number of threads increased by 1
|
||||||
assertEquals(++userThreadCount, listThreads(USER_LINK, null, userAuthHeaders).getData().size()); // Mentioned user
|
assertEquals(
|
||||||
assertEquals(++tableThreadCount, listThreads(TABLE_LINK, null, userAuthHeaders).getData().size()); // About TABLE
|
++userThreadCount, listThreads(USER_LINK, null, userAuthHeaders).getPaging().getTotal()); // Mentioned user
|
||||||
|
assertEquals(
|
||||||
|
++tableThreadCount, listThreads(TABLE_LINK, null, userAuthHeaders).getPaging().getTotal()); // About TABLE
|
||||||
assertEquals(
|
assertEquals(
|
||||||
++tableColumnDescriptionThreadCount,
|
++tableColumnDescriptionThreadCount,
|
||||||
listThreads(TABLE_COLUMN_LINK, null, userAuthHeaders).getData().size()); // About TABLE Description
|
listThreads(TABLE_COLUMN_LINK, null, userAuthHeaders).getPaging().getTotal()); // About TABLE Description
|
||||||
assertEquals(++totalThreadCount, listThreads(null, null, userAuthHeaders).getData().size()); // Overall threads
|
assertEquals(
|
||||||
|
++totalThreadCount, listThreads(null, null, userAuthHeaders).getPaging().getTotal()); // Overall threads
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test the /api/v1/feed/count API
|
// Test the /api/v1/feed/count API
|
||||||
@ -264,6 +277,72 @@ public class FeedResourceTest extends CatalogApplicationTest {
|
|||||||
assertEquals(tableColumnDescriptionThreadCount, getThreadCount(TABLE_COLUMN_LINK, userAuthHeaders));
|
assertEquals(tableColumnDescriptionThreadCount, getThreadCount(TABLE_COLUMN_LINK, userAuthHeaders));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Stream<Arguments> provideStringsForListThreads() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of(String.format("<#E/%s/%s>", Entity.USER, USER.getName())),
|
||||||
|
Arguments.of(String.format("<#E/%s/%s>", Entity.TABLE, TABLE.getFullyQualifiedName())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@NullSource
|
||||||
|
@MethodSource("provideStringsForListThreads")
|
||||||
|
void get_listThreadsWithPagination(String entityLink) throws HttpResponseException {
|
||||||
|
// Create 10 threads
|
||||||
|
int totalThreadCount = listThreads(entityLink, null, ADMIN_AUTH_HEADERS).getPaging().getTotal();
|
||||||
|
Map<String, String> userAuthHeaders = authHeaders(USER.getEmail());
|
||||||
|
for (int i = 1; i <= 10; i++) {
|
||||||
|
CreateThread create = create().withMessage("Thread " + i);
|
||||||
|
createAndCheck(create, userAuthHeaders);
|
||||||
|
// List all the threads and make sure the number of threads increased by 1
|
||||||
|
assertEquals(++totalThreadCount, listThreads(entityLink, null, userAuthHeaders).getPaging().getTotal());
|
||||||
|
}
|
||||||
|
// Now test if there are n number of pages with limit set to 5. (n = totalThreadCount / 5)
|
||||||
|
int limit = 5;
|
||||||
|
int totalPages = totalThreadCount / limit;
|
||||||
|
int lastPageCount;
|
||||||
|
if (totalThreadCount % limit != 0) {
|
||||||
|
totalPages++;
|
||||||
|
lastPageCount = totalThreadCount % limit;
|
||||||
|
} else {
|
||||||
|
lastPageCount = limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the first page
|
||||||
|
ThreadList threads = listThreads(entityLink, null, userAuthHeaders, limit, null, null);
|
||||||
|
assertEquals(limit, threads.getData().size());
|
||||||
|
assertEquals(totalThreadCount, threads.getPaging().getTotal());
|
||||||
|
assertNotNull(threads.getPaging().getAfter());
|
||||||
|
assertNull(threads.getPaging().getBefore());
|
||||||
|
String afterCursor = threads.getPaging().getAfter();
|
||||||
|
String beforeCursor = null;
|
||||||
|
int pageCount = 1;
|
||||||
|
|
||||||
|
// From the second page till last page, after and before cursors should not be null
|
||||||
|
while (afterCursor != null && pageCount < totalPages - 1) {
|
||||||
|
threads = listThreads(entityLink, null, userAuthHeaders, limit, null, afterCursor);
|
||||||
|
assertNotNull(threads.getPaging().getAfter());
|
||||||
|
assertNotNull(threads.getPaging().getBefore());
|
||||||
|
pageCount++;
|
||||||
|
afterCursor = threads.getPaging().getAfter();
|
||||||
|
if (pageCount == 2) {
|
||||||
|
beforeCursor = threads.getPaging().getBefore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals(totalPages - 1, pageCount);
|
||||||
|
|
||||||
|
// Get the last page
|
||||||
|
threads = listThreads(entityLink, null, userAuthHeaders, limit, null, afterCursor);
|
||||||
|
assertEquals(lastPageCount, threads.getData().size());
|
||||||
|
assertNull(threads.getPaging().getAfter());
|
||||||
|
|
||||||
|
// beforeCursor should point to the first page
|
||||||
|
threads = listThreads(entityLink, null, userAuthHeaders, limit, beforeCursor, null);
|
||||||
|
assertEquals(limit, threads.getData().size());
|
||||||
|
// since threads are always returned to the order of updated timestamp
|
||||||
|
// the first message should read "Thread 10"
|
||||||
|
assertEquals("Thread 10", threads.getData().get(0).getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void post_addPostWithoutMessage_4xx() {
|
void post_addPostWithoutMessage_4xx() {
|
||||||
// Add post to a thread without message field
|
// Add post to a thread without message field
|
||||||
@ -381,9 +460,11 @@ public class FeedResourceTest extends CatalogApplicationTest {
|
|||||||
@Test
|
@Test
|
||||||
void list_threadsWithOwnerFilter() throws HttpResponseException {
|
void list_threadsWithOwnerFilter() throws HttpResponseException {
|
||||||
// THREAD is created with TABLE entity in BeforeAll
|
// THREAD is created with TABLE entity in BeforeAll
|
||||||
int totalThreadCount = listThreads(null, null, ADMIN_AUTH_HEADERS).getData().size();
|
int totalThreadCount = listThreads(null, null, ADMIN_AUTH_HEADERS).getPaging().getTotal();
|
||||||
int user2ThreadCount =
|
int user2ThreadCount =
|
||||||
listThreadsWithFilter(USER2.getId().toString(), FilterType.OWNER.toString(), AUTH_HEADERS).getData().size();
|
listThreadsWithFilter(USER2.getId().toString(), FilterType.OWNER.toString(), AUTH_HEADERS)
|
||||||
|
.getPaging()
|
||||||
|
.getTotal();
|
||||||
String ownerId = TABLE.getOwner().getId().toString();
|
String ownerId = TABLE.getOwner().getId().toString();
|
||||||
|
|
||||||
// create another thread on an entity with a different owner
|
// create another thread on an entity with a different owner
|
||||||
@ -397,20 +478,21 @@ public class FeedResourceTest extends CatalogApplicationTest {
|
|||||||
assertNotEquals(ownerId, ownerId2);
|
assertNotEquals(ownerId, ownerId2);
|
||||||
|
|
||||||
ThreadList threads = listThreadsWithFilter(ownerId, FilterType.OWNER.toString(), AUTH_HEADERS);
|
ThreadList threads = listThreadsWithFilter(ownerId, FilterType.OWNER.toString(), AUTH_HEADERS);
|
||||||
assertEquals(totalThreadCount, threads.getData().size());
|
assertEquals(totalThreadCount, threads.getPaging().getTotal());
|
||||||
|
|
||||||
// This should return 0 since the table is owned by a team
|
// This should return 0 since the table is owned by a team
|
||||||
// and for the filter we are passing team id instead of user id
|
// and for the filter we are passing team id instead of user id
|
||||||
threads = listThreadsWithFilter(ownerId2, FilterType.OWNER.toString(), AUTH_HEADERS);
|
threads = listThreadsWithFilter(ownerId2, FilterType.OWNER.toString(), AUTH_HEADERS);
|
||||||
|
assertEquals(0, threads.getPaging().getTotal());
|
||||||
assertEquals(0, threads.getData().size());
|
assertEquals(0, threads.getData().size());
|
||||||
|
|
||||||
// Now, test the filter with user who is part of the team
|
// Now, test the filter with user who is part of the team
|
||||||
threads = listThreadsWithFilter(USER2.getId().toString(), FilterType.OWNER.toString(), AUTH_HEADERS);
|
threads = listThreadsWithFilter(USER2.getId().toString(), FilterType.OWNER.toString(), AUTH_HEADERS);
|
||||||
assertEquals(user2ThreadCount + 1, threads.getData().size());
|
assertEquals(user2ThreadCount + 1, threads.getPaging().getTotal());
|
||||||
|
|
||||||
// Test if no user id filter returns all threads
|
// Test if no user id filter returns all threads
|
||||||
threads = listThreadsWithFilter(null, FilterType.OWNER.toString(), AUTH_HEADERS);
|
threads = listThreadsWithFilter(null, FilterType.OWNER.toString(), AUTH_HEADERS);
|
||||||
assertEquals(totalThreadCount + 1, threads.getData().size());
|
assertEquals(totalThreadCount + 1, threads.getPaging().getTotal());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -433,7 +515,7 @@ public class FeedResourceTest extends CatalogApplicationTest {
|
|||||||
addPostAndCheck(thread, createPost, ADMIN_AUTH_HEADERS);
|
addPostAndCheck(thread, createPost, ADMIN_AUTH_HEADERS);
|
||||||
|
|
||||||
ThreadList threads = listThreadsWithFilter(USER.getId().toString(), FilterType.MENTIONS.toString(), AUTH_HEADERS);
|
ThreadList threads = listThreadsWithFilter(USER.getId().toString(), FilterType.MENTIONS.toString(), AUTH_HEADERS);
|
||||||
assertEquals(2, threads.getData().size());
|
assertEquals(2, threads.getPaging().getTotal());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -577,9 +659,23 @@ public class FeedResourceTest extends CatalogApplicationTest {
|
|||||||
|
|
||||||
public static ThreadList listThreads(String entityLink, Integer limitPosts, Map<String, String> authHeaders)
|
public static ThreadList listThreads(String entityLink, Integer limitPosts, Map<String, String> authHeaders)
|
||||||
throws HttpResponseException {
|
throws HttpResponseException {
|
||||||
|
return listThreads(entityLink, limitPosts, authHeaders, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ThreadList listThreads(
|
||||||
|
String entityLink,
|
||||||
|
Integer limitPosts,
|
||||||
|
Map<String, String> authHeaders,
|
||||||
|
Integer limitParam,
|
||||||
|
String before,
|
||||||
|
String after)
|
||||||
|
throws HttpResponseException {
|
||||||
WebTarget target = getResource("feed");
|
WebTarget target = getResource("feed");
|
||||||
target = entityLink != null ? target.queryParam("entityLink", entityLink) : target;
|
target = entityLink != null ? target.queryParam("entityLink", entityLink) : target;
|
||||||
target = limitPosts != null ? target.queryParam("limitPosts", limitPosts) : target;
|
target = limitPosts != null ? target.queryParam("limitPosts", limitPosts) : target;
|
||||||
|
target = limitParam != null ? target.queryParam("limit", limitParam) : target;
|
||||||
|
target = before != null ? target.queryParam("before", before) : target;
|
||||||
|
target = after != null ? target.queryParam("after", after) : target;
|
||||||
return TestUtils.get(target, ThreadList.class, authHeaders);
|
return TestUtils.get(target, ThreadList.class, authHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user