Fix #2199: Follow-up of PR: Support for getting deleted entities in GET and LIST operations (#2206)

* Improve tests for deleted entities, fix User->Teams->OwnsEntities

* Improve doc for recursive deletion and improve UsageRepository

* Remove listAfter and listBerfore without Include. Add Include.NON_DELETED to the resources without deletion like Bots, Metrics, and Reports. Improve UserRepository.

* Add method to add all relationships for the get deleted entity test. Adapt the test for the entity User.

* Add soft deletion to Webhook. Implement Include for Webhook.

* Improve existing test to include a check for owner.

* Fix populateService of Table

* Fix Team setFields.
This commit is contained in:
Alberto Miorin 2022-01-14 21:19:19 +01:00 committed by GitHub
parent 9a55d4efb5
commit 4fedb5fd9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 184 additions and 117 deletions

View File

@ -21,6 +21,7 @@ import java.io.IOException;
import org.jdbi.v3.core.Jdbi; import org.jdbi.v3.core.Jdbi;
import org.openmetadata.catalog.jdbi3.CollectionDAO; import org.openmetadata.catalog.jdbi3.CollectionDAO;
import org.openmetadata.catalog.jdbi3.UserRepository; import org.openmetadata.catalog.jdbi3.UserRepository;
import org.openmetadata.catalog.type.Include;
import org.openmetadata.catalog.util.EntityUtil; import org.openmetadata.catalog.util.EntityUtil;
public class CatalogHealthCheck extends HealthCheck { public class CatalogHealthCheck extends HealthCheck {
@ -36,7 +37,7 @@ public class CatalogHealthCheck extends HealthCheck {
@Override @Override
protected Result check() throws Exception { protected Result check() throws Exception {
try { try {
userRepository.listAfter(null, fields, null, 1, null); userRepository.listAfter(null, fields, null, 1, null, Include.NON_DELETED);
return Result.healthy(); return Result.healthy();
} catch (IOException e) { } catch (IOException e) {
LOG.error("Health check error {}", e.getMessage()); LOG.error("Health check error {}", e.getMessage());

View File

@ -147,6 +147,17 @@ public final class Entity {
return entityRepository.getEntityInterface(entity); return entityRepository.getEntityInterface(entity);
} }
public static <T> EntityInterface<T> getEntityInterface(EntityReference entityReference)
throws IOException, ParseException {
if (entityReference == null) {
return null;
}
String entityName = entityReference.getType();
EntityRepository<T> entityRepository = getEntityRepository(entityName);
T entity = entityRepository.get(null, entityReference.getId().toString(), EntityUtil.Fields.EMPTY_FIELDS);
return entityRepository.getEntityInterface(entity);
}
/** /**
* Retrieve the entity using id from given entity reference and fields * Retrieve the entity using id from given entity reference and fields
* *

View File

@ -13,6 +13,8 @@
package org.openmetadata.catalog.jdbi3; package org.openmetadata.catalog.jdbi3;
import static org.openmetadata.catalog.util.EntityUtil.toBoolean;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays; import java.util.Arrays;
@ -1133,17 +1135,19 @@ public interface CollectionDAO {
String findByEmail(@Bind("email") String email); String findByEmail(@Bind("email") String email);
default int listCount(String team, Include include) { default int listCount(String team, Include include) {
return listCount(getTableName(), getNameColumn(), team, Relationship.HAS.ordinal()); return listCount(getTableName(), getNameColumn(), team, Relationship.HAS.ordinal(), toBoolean(include));
} }
@Override @Override
default List<String> listBefore(String team, int limit, String before, Include include) { default List<String> listBefore(String team, int limit, String before, Include include) {
return listBefore(getTableName(), getNameColumn(), team, limit, before, Relationship.HAS.ordinal()); return listBefore(
getTableName(), getNameColumn(), team, limit, before, Relationship.HAS.ordinal(), toBoolean(include));
} }
@Override @Override
default List<String> listAfter(String team, int limit, String after, Include include) { default List<String> listAfter(String team, int limit, String after, Include include) {
return listAfter(getTableName(), getNameColumn(), team, limit, after, Relationship.HAS.ordinal()); return listAfter(
getTableName(), getNameColumn(), team, limit, after, Relationship.HAS.ordinal(), toBoolean(include));
} }
@SqlQuery( @SqlQuery(
@ -1152,13 +1156,16 @@ public interface CollectionDAO {
+ "FROM user_entity ue " + "FROM user_entity ue "
+ "LEFT JOIN entity_relationship er on ue.id = er.toId " + "LEFT JOIN entity_relationship er on ue.id = er.toId "
+ "LEFT JOIN team_entity te on te.id = er.fromId and er.relation = :relation " + "LEFT JOIN team_entity te on te.id = er.fromId and er.relation = :relation "
+ "WHERE (ue.deleted = false AND (te.name = :team OR :team IS NULL)) " + "WHERE (te.name = :team OR :team IS NULL) "
+ "AND (ue.deleted = :deleted OR :deleted IS NULL) "
+ "AND (er.deleted = :deleted OR :deleted IS NULL OR (:team IS NULL AND er.deleted IS NULL)) "
+ "GROUP BY ue.id) subquery") + "GROUP BY ue.id) subquery")
int listCount( int listCount(
@Define("table") String table, @Define("table") String table,
@Define("nameColumn") String nameColumn, @Define("nameColumn") String nameColumn,
@Bind("team") String team, @Bind("team") String team,
@Bind("relation") int relation); @Bind("relation") int relation,
@Bind("deleted") Boolean deleted);
@SqlQuery( @SqlQuery(
"SELECT json FROM (" "SELECT json FROM ("
@ -1166,8 +1173,10 @@ public interface CollectionDAO {
+ "FROM user_entity ue " + "FROM user_entity ue "
+ "LEFT JOIN entity_relationship er on ue.id = er.toId " + "LEFT JOIN entity_relationship er on ue.id = er.toId "
+ "LEFT JOIN team_entity te on te.id = er.fromId and er.relation = :relation " + "LEFT JOIN team_entity te on te.id = er.fromId and er.relation = :relation "
+ "WHERE (ue.deleted = false AND (te.name = :team OR :team IS NULL)) AND " + "WHERE (te.name = :team OR :team IS NULL) "
+ "ue.<nameColumn> < :before " + "AND (ue.deleted = :deleted OR :deleted IS NULL) "
+ "AND (er.deleted = :deleted OR :deleted IS NULL OR (:team IS NULL AND er.deleted IS NULL)) "
+ "AND ue.<nameColumn> < :before "
+ "GROUP BY ue.<nameColumn>, ue.json " + "GROUP BY ue.<nameColumn>, ue.json "
+ "ORDER BY ue.<nameColumn> DESC " + "ORDER BY ue.<nameColumn> DESC "
+ "LIMIT :limit" + "LIMIT :limit"
@ -1178,15 +1187,18 @@ public interface CollectionDAO {
@Bind("team") String team, @Bind("team") String team,
@Bind("limit") int limit, @Bind("limit") int limit,
@Bind("before") String before, @Bind("before") String before,
@Bind("relation") int relation); @Bind("relation") int relation,
@Bind("deleted") Boolean deleted);
@SqlQuery( @SqlQuery(
"SELECT ue.json " "SELECT ue.json "
+ "FROM user_entity ue " + "FROM user_entity ue "
+ "LEFT JOIN entity_relationship er on ue.id = er.toId " + "LEFT JOIN entity_relationship er on ue.id = er.toId "
+ "LEFT JOIN team_entity te on te.id = er.fromId and er.relation = :relation " + "LEFT JOIN team_entity te on te.id = er.fromId and er.relation = :relation "
+ "WHERE (ue.deleted = false AND (te.name = :team OR :team IS NULL)) AND " + "WHERE (te.name = :team OR :team IS NULL) "
+ "ue.<nameColumn> > :after " + "AND (ue.deleted = :deleted OR :deleted IS NULL) "
+ "AND (er.deleted = :deleted OR :deleted IS NULL OR (:team IS NULL AND er.deleted IS NULL)) "
+ "AND ue.<nameColumn> > :after "
+ "GROUP BY ue.json " + "GROUP BY ue.json "
+ "ORDER BY ue.<nameColumn> " + "ORDER BY ue.<nameColumn> "
+ "LIMIT :limit") + "LIMIT :limit")
@ -1196,7 +1208,8 @@ public interface CollectionDAO {
@Bind("team") String team, @Bind("team") String team,
@Bind("limit") int limit, @Bind("limit") int limit,
@Bind("after") String after, @Bind("after") String after,
@Bind("relation") int relation); @Bind("relation") int relation,
@Bind("deleted") Boolean deleted);
} }
interface ChangeEventDAO { interface ChangeEventDAO {

View File

@ -220,12 +220,6 @@ public abstract class EntityRepository<T> {
return withHref(uriInfo, setFields(dao.findEntityByName(fqn, include), fields)); return withHref(uriInfo, setFields(dao.findEntityByName(fqn, include), fields));
} }
@Transaction
public final ResultList<T> listAfter(UriInfo uriInfo, Fields fields, String fqnPrefix, int limitParam, String after)
throws GeneralSecurityException, IOException, ParseException {
return listAfter(uriInfo, fields, fqnPrefix, limitParam, after, Include.NON_DELETED);
}
@Transaction @Transaction
public final ResultList<T> listAfter( public final ResultList<T> listAfter(
UriInfo uriInfo, Fields fields, String fqnPrefix, int limitParam, String after, Include include) UriInfo uriInfo, Fields fields, String fqnPrefix, int limitParam, String after, Include include)
@ -251,12 +245,6 @@ public abstract class EntityRepository<T> {
return getResultList(entities, beforeCursor, afterCursor, total); return getResultList(entities, beforeCursor, afterCursor, total);
} }
@Transaction
public final ResultList<T> listBefore(UriInfo uriInfo, Fields fields, String fqnPrefix, int limitParam, String before)
throws IOException, GeneralSecurityException, ParseException {
return listBefore(uriInfo, fields, fqnPrefix, limitParam, before, Include.NON_DELETED);
}
@Transaction @Transaction
public final ResultList<T> listBefore( public final ResultList<T> listBefore(
UriInfo uriInfo, Fields fields, String fqnPrefix, int limitParam, String before, Include include) UriInfo uriInfo, Fields fields, String fqnPrefix, int limitParam, String before, Include include)
@ -406,7 +394,7 @@ public abstract class EntityRepository<T> {
@Transaction @Transaction
public final void delete(UUID id, boolean recursive) throws IOException { public final void delete(UUID id, boolean recursive) throws IOException {
// If an entity being deleted contains other children entities, it can't be deleted // If an entity being deleted contains other **non-deleted** children entities, it can't be deleted
List<EntityReference> contains = List<EntityReference> contains =
daoCollection daoCollection
.relationshipDAO() .relationshipDAO()
@ -496,7 +484,7 @@ public abstract class EntityRepository<T> {
EntityInterface entityInterface = getEntityInterface(entity); EntityInterface entityInterface = getEntityInterface(entity);
return supportsOwner && entity != null return supportsOwner && entity != null
? EntityUtil.populateOwner( ? EntityUtil.populateOwner(
entityInterface.getId(), entityInterface,
entityName, entityName,
daoCollection.relationshipDAO(), daoCollection.relationshipDAO(),
daoCollection.userDAO(), daoCollection.userDAO(),

View File

@ -16,6 +16,7 @@ package org.openmetadata.catalog.jdbi3;
import static org.openmetadata.catalog.util.EntityUtil.toBoolean; import static org.openmetadata.catalog.util.EntityUtil.toBoolean;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -31,6 +32,7 @@ import org.openmetadata.catalog.resources.feeds.MessageParser.EntityLink.LinkTyp
import org.openmetadata.catalog.type.EntityReference; import org.openmetadata.catalog.type.EntityReference;
import org.openmetadata.catalog.type.Include; import org.openmetadata.catalog.type.Include;
import org.openmetadata.catalog.type.Post; import org.openmetadata.catalog.type.Post;
import org.openmetadata.catalog.util.EntityInterface;
import org.openmetadata.catalog.util.EntityUtil; import org.openmetadata.catalog.util.EntityUtil;
import org.openmetadata.catalog.util.JsonUtils; import org.openmetadata.catalog.util.JsonUtils;
@ -42,7 +44,7 @@ public class FeedRepository {
} }
@Transaction @Transaction
public Thread create(Thread thread) throws IOException { public Thread create(Thread thread) throws IOException, ParseException {
// Validate user creating thread // Validate user creating thread
UUID fromUser = thread.getPosts().get(0).getFrom(); UUID fromUser = thread.getPosts().get(0).getFrom();
dao.userDAO().findEntityById(fromUser); dao.userDAO().findEntityById(fromUser);
@ -50,11 +52,12 @@ public class FeedRepository {
// Validate about data entity is valid // Validate about data entity is valid
EntityLink about = EntityLink.parse(thread.getAbout()); EntityLink about = EntityLink.parse(thread.getAbout());
EntityReference aboutRef = EntityUtil.validateEntityLink(about); EntityReference aboutRef = EntityUtil.validateEntityLink(about);
EntityInterface aboutEntityInterface = Entity.getEntityInterface(aboutRef);
// Get owner for the addressed to Entity // Get owner for the addressed to Entity
EntityReference owner = EntityReference owner =
EntityUtil.populateOwner( EntityUtil.populateOwner(
aboutRef.getId(), aboutRef.getType(), dao.relationshipDAO(), dao.userDAO(), dao.teamDAO()); aboutEntityInterface, aboutRef.getType(), dao.relationshipDAO(), dao.userDAO(), dao.teamDAO());
// Insert a new thread // Insert a new thread
dao.feedDAO().insert(JsonUtils.pojoToJson(thread)); dao.feedDAO().insert(JsonUtils.pojoToJson(thread));

View File

@ -351,16 +351,18 @@ public class TableRepository extends EntityRepository<Table> {
} }
private void populateService(Table table) throws IOException { private void populateService(Table table) throws IOException {
Database database = daoCollection.databaseDAO().findEntityById(table.getDatabase().getId(), Include.ALL);
Include include = database.getDeleted() ? Include.DELETED : Include.NON_DELETED;
// Find database service from the database that table is contained in // Find database service from the database that table is contained in
String serviceId = String serviceId =
daoCollection daoCollection
.relationshipDAO() .relationshipDAO()
.findFrom( .findFrom(
table.getDatabase().getId().toString(), database.getId().toString(),
Entity.DATABASE, Entity.DATABASE,
Relationship.CONTAINS.ordinal(), Relationship.CONTAINS.ordinal(),
Entity.DATABASE_SERVICE, Entity.DATABASE_SERVICE,
toBoolean(Include.NON_DELETED)) toBoolean(include))
.get(0); .get(0);
DatabaseService service = daoCollection.dbServiceDAO().findEntityById(UUID.fromString(serviceId)); DatabaseService service = daoCollection.dbServiceDAO().findEntityById(UUID.fromString(serviceId));
table.setService(new DatabaseServiceEntityInterface(service).getEntityReference()); table.setService(new DatabaseServiceEntityInterface(service).getEntityReference());

View File

@ -31,7 +31,6 @@ import org.openmetadata.catalog.exception.CatalogExceptionMessage;
import org.openmetadata.catalog.resources.teams.TeamResource; import org.openmetadata.catalog.resources.teams.TeamResource;
import org.openmetadata.catalog.type.ChangeDescription; import org.openmetadata.catalog.type.ChangeDescription;
import org.openmetadata.catalog.type.EntityReference; import org.openmetadata.catalog.type.EntityReference;
import org.openmetadata.catalog.type.Include;
import org.openmetadata.catalog.util.EntityInterface; import org.openmetadata.catalog.util.EntityInterface;
import org.openmetadata.catalog.util.EntityUtil; import org.openmetadata.catalog.util.EntityUtil;
import org.openmetadata.catalog.util.EntityUtil.Fields; import org.openmetadata.catalog.util.EntityUtil.Fields;
@ -79,8 +78,8 @@ public class TeamRepository extends EntityRepository<Team> {
if (!fields.contains("profile")) { if (!fields.contains("profile")) {
team.setProfile(null); team.setProfile(null);
} }
team.setUsers(fields.contains("users") ? getUsers(team.getId().toString()) : null); team.setUsers(fields.contains("users") ? getUsers(team) : null);
team.setOwns(fields.contains("owns") ? getOwns(team.getId().toString()) : null); team.setOwns(fields.contains("owns") ? getOwns(team) : null);
return team; return team;
} }
@ -128,11 +127,12 @@ public class TeamRepository extends EntityRepository<Team> {
return new TeamUpdater(original, updated, patchOperation); return new TeamUpdater(original, updated, patchOperation);
} }
private List<EntityReference> getUsers(String id) throws IOException { private List<EntityReference> getUsers(Team team) throws IOException {
List<String> userIds = List<String> userIds =
daoCollection daoCollection
.relationshipDAO() .relationshipDAO()
.findTo(id, Entity.TEAM, Relationship.HAS.ordinal(), "user", toBoolean(Include.NON_DELETED)); .findTo(
team.getId().toString(), Entity.TEAM, Relationship.HAS.ordinal(), "user", toBoolean(toInclude(team)));
List<EntityReference> users = new ArrayList<>(); List<EntityReference> users = new ArrayList<>();
for (String userId : userIds) { for (String userId : userIds) {
users.add(daoCollection.userDAO().findEntityReferenceById(UUID.fromString(userId))); users.add(daoCollection.userDAO().findEntityReferenceById(UUID.fromString(userId)));
@ -140,10 +140,12 @@ public class TeamRepository extends EntityRepository<Team> {
return users; return users;
} }
private List<EntityReference> getOwns(String teamId) throws IOException { private List<EntityReference> getOwns(Team team) throws IOException {
// Compile entities owned by the team // Compile entities owned by the team
return EntityUtil.populateEntityReferences( return EntityUtil.populateEntityReferences(
daoCollection.relationshipDAO().findTo(teamId, Entity.TEAM, OWNS.ordinal(), toBoolean(Include.NON_DELETED))); daoCollection
.relationshipDAO()
.findTo(team.getId().toString(), Entity.TEAM, OWNS.ordinal(), toBoolean(toInclude(team))));
} }
public static class TeamEntityInterface implements EntityInterface<Team> { public static class TeamEntityInterface implements EntityInterface<Team> {

View File

@ -24,6 +24,7 @@ import org.jdbi.v3.core.mapper.RowMapper;
import org.jdbi.v3.core.statement.StatementContext; import org.jdbi.v3.core.statement.StatementContext;
import org.jdbi.v3.sqlobject.transaction.Transaction; import org.jdbi.v3.sqlobject.transaction.Transaction;
import org.openmetadata.catalog.Entity; import org.openmetadata.catalog.Entity;
import org.openmetadata.catalog.entity.data.Table;
import org.openmetadata.catalog.type.DailyCount; import org.openmetadata.catalog.type.DailyCount;
import org.openmetadata.catalog.type.EntityReference; import org.openmetadata.catalog.type.EntityReference;
import org.openmetadata.catalog.type.EntityUsage; import org.openmetadata.catalog.type.EntityUsage;
@ -74,20 +75,18 @@ public class UsageRepository {
dao.usageDAO().computePercentile(entityType, date); dao.usageDAO().computePercentile(entityType, date);
} }
private void addUsage(String entityType, String entityId, DailyCount usage) { private void addUsage(String entityType, String entityId, DailyCount usage) throws IOException {
// Insert usage record // Insert usage record
dao.usageDAO().insert(usage.getDate(), entityId, entityType, usage.getCount()); dao.usageDAO().insert(usage.getDate(), entityId, entityType, usage.getCount());
// If table usage was reported, add the usage count to database // If table usage was reported, add the usage count to database
if (entityType.equalsIgnoreCase(Entity.TABLE)) { if (entityType.equalsIgnoreCase(Entity.TABLE)) {
// we accept usage for deleted entities
Table table = dao.tableDAO().findEntityById(UUID.fromString(entityId), Include.ALL);
Include include = table.getDeleted() ? Include.DELETED : Include.NON_DELETED;
List<String> databaseIds = List<String> databaseIds =
dao.relationshipDAO() dao.relationshipDAO()
.findFrom( .findFrom(entityId, entityType, Relationship.CONTAINS.ordinal(), Entity.DATABASE, toBoolean(include));
entityId,
entityType,
Relationship.CONTAINS.ordinal(),
Entity.DATABASE,
toBoolean(Include.NON_DELETED));
dao.usageDAO().insertOrUpdateCount(usage.getDate(), databaseIds.get(0), Entity.DATABASE, usage.getCount()); dao.usageDAO().insertOrUpdateCount(usage.getDate(), databaseIds.get(0), Entity.DATABASE, usage.getCount());
} }
} }

View File

@ -16,11 +16,14 @@ package org.openmetadata.catalog.jdbi3;
import static org.openmetadata.catalog.jdbi3.Relationship.FOLLOWS; import static org.openmetadata.catalog.jdbi3.Relationship.FOLLOWS;
import static org.openmetadata.catalog.jdbi3.Relationship.HAS; import static org.openmetadata.catalog.jdbi3.Relationship.HAS;
import static org.openmetadata.catalog.jdbi3.Relationship.OWNS; import static org.openmetadata.catalog.jdbi3.Relationship.OWNS;
import static org.openmetadata.catalog.type.Include.DELETED;
import static org.openmetadata.catalog.type.Include.NON_DELETED;
import static org.openmetadata.catalog.util.EntityUtil.toBoolean; import static org.openmetadata.catalog.util.EntityUtil.toBoolean;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -94,13 +97,13 @@ public class UserRepository extends EntityRepository<User> {
} }
@Transaction @Transaction
public User getByEmail(String email, Fields fields) throws IOException { public User getByEmail(String email, Fields fields) throws IOException, ParseException {
User user = EntityUtil.validate(email, daoCollection.userDAO().findByEmail(email), User.class); User user = EntityUtil.validate(email, daoCollection.userDAO().findByEmail(email), User.class);
return setFields(user, fields); return setFields(user, fields);
} }
@Override @Override
public User setFields(User user, Fields fields) throws IOException { public User setFields(User user, Fields fields) throws IOException, ParseException {
user.setProfile(fields.contains("profile") ? user.getProfile() : null); user.setProfile(fields.contains("profile") ? user.getProfile() : null);
user.setTeams(fields.contains("teams") ? getTeams(user) : null); user.setTeams(fields.contains("teams") ? getTeams(user) : null);
user.setRoles(fields.contains("roles") ? getRoles(user) : null); user.setRoles(fields.contains("roles") ? getRoles(user) : null);
@ -112,7 +115,7 @@ public class UserRepository extends EntityRepository<User> {
@Override @Override
public void restorePatchAttributes(User original, User updated) {} public void restorePatchAttributes(User original, User updated) {}
private List<EntityReference> getOwns(User user) throws IOException { private List<EntityReference> getOwns(User user) throws IOException, ParseException {
// Compile entities owned by the user // Compile entities owned by the user
List<EntityReference> ownedEntities = List<EntityReference> ownedEntities =
daoCollection daoCollection
@ -122,10 +125,11 @@ public class UserRepository extends EntityRepository<User> {
// Compile entities owned by the team the user belongs to // Compile entities owned by the team the user belongs to
List<EntityReference> teams = user.getTeams() == null ? getTeams(user) : user.getTeams(); List<EntityReference> teams = user.getTeams() == null ? getTeams(user) : user.getTeams();
for (EntityReference team : teams) { for (EntityReference team : teams) {
Include include = Entity.getEntityInterface(team).isDeleted() ? DELETED : NON_DELETED;
ownedEntities.addAll( ownedEntities.addAll(
daoCollection daoCollection
.relationshipDAO() .relationshipDAO()
.findTo(team.getId().toString(), Entity.TEAM, OWNS.ordinal(), toBoolean(Include.NON_DELETED))); .findTo(team.getId().toString(), Entity.TEAM, OWNS.ordinal(), toBoolean(include)));
} }
// Populate details in entity reference // Populate details in entity reference
return EntityUtil.populateEntityReferences(ownedEntities); return EntityUtil.populateEntityReferences(ownedEntities);

View File

@ -45,6 +45,7 @@ import org.openmetadata.catalog.jdbi3.CollectionDAO;
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;
import org.openmetadata.catalog.type.Include;
import org.openmetadata.catalog.util.EntityUtil.Fields; import org.openmetadata.catalog.util.EntityUtil.Fields;
import org.openmetadata.catalog.util.RestUtil; import org.openmetadata.catalog.util.RestUtil;
import org.openmetadata.catalog.util.ResultList; import org.openmetadata.catalog.util.ResultList;
@ -97,9 +98,9 @@ public class BotsResource {
ResultList<Bots> list; ResultList<Bots> list;
if (before != null) { // Reverse paging if (before != null) { // Reverse paging
list = dao.listBefore(uriInfo, null, name, limitParam, before); list = dao.listBefore(uriInfo, null, name, limitParam, before, Include.NON_DELETED);
} else { // Forward paging or first page } else { // Forward paging or first page
list = dao.listAfter(uriInfo, null, name, limitParam, after); list = dao.listAfter(uriInfo, null, name, limitParam, after, Include.NON_DELETED);
} }
return list; return list;
} }

View File

@ -52,6 +52,7 @@ import org.openmetadata.catalog.security.Authorizer;
import org.openmetadata.catalog.security.SecurityUtil; import org.openmetadata.catalog.security.SecurityUtil;
import org.openmetadata.catalog.type.ChangeEvent; import org.openmetadata.catalog.type.ChangeEvent;
import org.openmetadata.catalog.type.EntityHistory; import org.openmetadata.catalog.type.EntityHistory;
import org.openmetadata.catalog.type.Include;
import org.openmetadata.catalog.type.Webhook; import org.openmetadata.catalog.type.Webhook;
import org.openmetadata.catalog.type.Webhook.Status; import org.openmetadata.catalog.type.Webhook.Status;
import org.openmetadata.catalog.util.EntityUtil.Fields; import org.openmetadata.catalog.util.EntityUtil.Fields;
@ -111,14 +112,20 @@ public class WebhookResource {
String before, String before,
@Parameter(description = "Returns list of webhooks after this cursor", schema = @Schema(type = "string")) @Parameter(description = "Returns list of webhooks after this cursor", schema = @Schema(type = "string"))
@QueryParam("after") @QueryParam("after")
String after) String after,
@Parameter(
description = "Include all, deleted, or non-deleted entities.",
schema = @Schema(implementation = Include.class))
@QueryParam("include")
@DefaultValue("non-deleted")
Include include)
throws IOException, ParseException, GeneralSecurityException { throws IOException, ParseException, GeneralSecurityException {
RestUtil.validateCursors(before, after); RestUtil.validateCursors(before, after);
ResultList<Webhook> webhooks; ResultList<Webhook> webhooks;
if (before != null) { // Reverse paging if (before != null) { // Reverse paging
webhooks = dao.listBefore(uriInfo, Fields.EMPTY_FIELDS, null, limitParam, before); webhooks = dao.listBefore(uriInfo, Fields.EMPTY_FIELDS, null, limitParam, before, include);
} else { // Forward paging or first page } else { // Forward paging or first page
webhooks = dao.listAfter(uriInfo, Fields.EMPTY_FIELDS, null, limitParam, after); webhooks = dao.listAfter(uriInfo, Fields.EMPTY_FIELDS, null, limitParam, after, include);
} }
webhooks.getData().forEach(t -> dao.withHref(uriInfo, t)); webhooks.getData().forEach(t -> dao.withHref(uriInfo, t));
return webhooks; return webhooks;
@ -138,11 +145,17 @@ public class WebhookResource {
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ChangeEvent.class))), content = @Content(mediaType = "application/json", schema = @Schema(implementation = ChangeEvent.class))),
@ApiResponse(responseCode = "404", description = "Entity for instance {id} is not found") @ApiResponse(responseCode = "404", description = "Entity for instance {id} is not found")
}) })
public Webhook getWebhook( public Webhook get(
@Context UriInfo uriInfo, @Context UriInfo uriInfo,
@Parameter(description = "webhook Id", schema = @Schema(type = "string")) @PathParam("id") String id) @Parameter(description = "webhook Id", schema = @Schema(type = "string")) @PathParam("id") String id,
@Parameter(
description = "Include all, deleted, or non-deleted entities.",
schema = @Schema(implementation = Include.class))
@QueryParam("include")
@DefaultValue("non-deleted")
Include include)
throws IOException, GeneralSecurityException, ParseException { throws IOException, GeneralSecurityException, ParseException {
return dao.get(uriInfo, id, Fields.EMPTY_FIELDS); return dao.get(uriInfo, id, Fields.EMPTY_FIELDS, include);
} }
@GET @GET
@ -161,9 +174,15 @@ public class WebhookResource {
public Webhook getByName( public Webhook getByName(
@Context UriInfo uriInfo, @Context UriInfo uriInfo,
@Context SecurityContext securityContext, @Context SecurityContext securityContext,
@Parameter(description = "Name of the webhook", schema = @Schema(type = "string")) @PathParam("name") String fqn) @Parameter(description = "Name of the webhook", schema = @Schema(type = "string")) @PathParam("name") String fqn,
@Parameter(
description = "Include all, deleted, or non-deleted entities.",
schema = @Schema(implementation = Include.class))
@QueryParam("include")
@DefaultValue("non-deleted")
Include include)
throws IOException, ParseException { throws IOException, ParseException {
return dao.getByName(uriInfo, fqn, Fields.EMPTY_FIELDS); return dao.getByName(uriInfo, fqn, Fields.EMPTY_FIELDS, include);
} }
@GET @GET
@ -280,7 +299,7 @@ public class WebhookResource {
@Context UriInfo uriInfo, @Context UriInfo uriInfo,
@Parameter(description = "webhook Id", schema = @Schema(type = "string")) @PathParam("id") String id) @Parameter(description = "webhook Id", schema = @Schema(type = "string")) @PathParam("id") String id)
throws IOException, GeneralSecurityException, ParseException, InterruptedException { throws IOException, GeneralSecurityException, ParseException, InterruptedException {
dao.delete(id); dao.delete(UUID.fromString(id), false);
dao.deleteWebhookPublisher(UUID.fromString(id)); dao.deleteWebhookPublisher(UUID.fromString(id));
return Response.ok().build(); return Response.ok().build();
} }

View File

@ -20,6 +20,7 @@ import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
@ -130,7 +131,7 @@ public class FeedResource {
content = @Content(mediaType = "application/json", schema = @Schema(implementation = CreateThread.class))), content = @Content(mediaType = "application/json", schema = @Schema(implementation = CreateThread.class))),
@ApiResponse(responseCode = "400", description = "Bad request") @ApiResponse(responseCode = "400", description = "Bad request")
}) })
public Response create(@Context UriInfo uriInfo, @Valid CreateThread cr) throws IOException { public Response create(@Context UriInfo uriInfo, @Valid CreateThread cr) throws IOException, ParseException {
Thread thread = Thread thread =
new Thread().withId(UUID.randomUUID()).withThreadTs(System.currentTimeMillis()).withAbout(cr.getAbout()); new Thread().withId(UUID.randomUUID()).withThreadTs(System.currentTimeMillis()).withAbout(cr.getAbout());
// For now redundantly storing everything in json (that includes fromEntity, addressedTo entity) // For now redundantly storing everything in json (that includes fromEntity, addressedTo entity)

View File

@ -48,6 +48,7 @@ import org.openmetadata.catalog.jdbi3.CollectionDAO;
import org.openmetadata.catalog.jdbi3.MetricsRepository; import org.openmetadata.catalog.jdbi3.MetricsRepository;
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.type.Include;
import org.openmetadata.catalog.util.EntityUtil.Fields; import org.openmetadata.catalog.util.EntityUtil.Fields;
import org.openmetadata.catalog.util.RestUtil; import org.openmetadata.catalog.util.RestUtil;
import org.openmetadata.catalog.util.RestUtil.PutResponse; import org.openmetadata.catalog.util.RestUtil.PutResponse;
@ -106,10 +107,10 @@ public class MetricsResource {
Fields fields = new Fields(FIELD_LIST, fieldsParam); Fields fields = new Fields(FIELD_LIST, fieldsParam);
if (before != null) { // Reverse paging if (before != null) { // Reverse paging
return dao.listBefore(uriInfo, fields, null, limitParam, before); return dao.listBefore(uriInfo, fields, null, limitParam, before, Include.NON_DELETED);
} }
// Forward paging or first page // Forward paging or first page
return dao.listAfter(uriInfo, fields, null, limitParam, after); return dao.listAfter(uriInfo, fields, null, limitParam, after, Include.NON_DELETED);
} }
@GET @GET

View File

@ -45,6 +45,7 @@ import org.openmetadata.catalog.jdbi3.CollectionDAO;
import org.openmetadata.catalog.jdbi3.ReportRepository; import org.openmetadata.catalog.jdbi3.ReportRepository;
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.type.Include;
import org.openmetadata.catalog.util.EntityUtil.Fields; import org.openmetadata.catalog.util.EntityUtil.Fields;
import org.openmetadata.catalog.util.RestUtil.PutResponse; import org.openmetadata.catalog.util.RestUtil.PutResponse;
import org.openmetadata.catalog.util.ResultList; import org.openmetadata.catalog.util.ResultList;
@ -92,7 +93,7 @@ public class ReportResource {
String fieldsParam) String fieldsParam)
throws IOException, GeneralSecurityException, ParseException { throws IOException, GeneralSecurityException, ParseException {
Fields fields = new Fields(FIELD_LIST, fieldsParam); Fields fields = new Fields(FIELD_LIST, fieldsParam);
return dao.listAfter(uriInfo, fields, null, 10000, null); return dao.listAfter(uriInfo, fields, null, 10000, null, Include.NON_DELETED);
} }
@GET @GET

View File

@ -64,6 +64,7 @@ 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;
import org.openmetadata.catalog.type.EntityHistory; import org.openmetadata.catalog.type.EntityHistory;
import org.openmetadata.catalog.type.Include;
import org.openmetadata.catalog.util.EntityUtil.Fields; import org.openmetadata.catalog.util.EntityUtil.Fields;
import org.openmetadata.catalog.util.RestUtil; import org.openmetadata.catalog.util.RestUtil;
import org.openmetadata.catalog.util.RestUtil.PatchResponse; import org.openmetadata.catalog.util.RestUtil.PatchResponse;
@ -146,16 +147,22 @@ public class UserResource {
String before, String before,
@Parameter(description = "Returns list of users after this cursor", schema = @Schema(type = "string")) @Parameter(description = "Returns list of users after this cursor", schema = @Schema(type = "string"))
@QueryParam("after") @QueryParam("after")
String after) String after,
@Parameter(
description = "Include all, deleted, or non-deleted entities.",
schema = @Schema(implementation = Include.class))
@QueryParam("include")
@DefaultValue("non-deleted")
Include include)
throws IOException, GeneralSecurityException, ParseException { throws IOException, GeneralSecurityException, ParseException {
RestUtil.validateCursors(before, after); RestUtil.validateCursors(before, after);
Fields fields = new Fields(FIELD_LIST, fieldsParam); Fields fields = new Fields(FIELD_LIST, fieldsParam);
ResultList<User> users; ResultList<User> users;
if (before != null) { // Reverse paging if (before != null) { // Reverse paging
users = dao.listBefore(uriInfo, fields, teamParam, limitParam, before); users = dao.listBefore(uriInfo, fields, teamParam, limitParam, before, include);
} else { // Forward paging or first page } else { // Forward paging or first page
users = dao.listAfter(uriInfo, fields, teamParam, limitParam, after); users = dao.listAfter(uriInfo, fields, teamParam, limitParam, after, include);
} }
Optional.ofNullable(users.getData()).orElse(Collections.emptyList()).forEach(u -> addHref(uriInfo, u)); Optional.ofNullable(users.getData()).orElse(Collections.emptyList()).forEach(u -> addHref(uriInfo, u));
return users; return users;
@ -203,10 +210,16 @@ public class UserResource {
description = "Fields requested in the returned resource", description = "Fields requested in the returned resource",
schema = @Schema(type = "string", example = FIELDS)) schema = @Schema(type = "string", example = FIELDS))
@QueryParam("fields") @QueryParam("fields")
String fieldsParam) String fieldsParam,
@Parameter(
description = "Include all, deleted, or non-deleted entities.",
schema = @Schema(implementation = Include.class))
@QueryParam("include")
@DefaultValue("non-deleted")
Include include)
throws IOException, ParseException { throws IOException, ParseException {
Fields fields = new Fields(FIELD_LIST, fieldsParam); Fields fields = new Fields(FIELD_LIST, fieldsParam);
User user = dao.get(uriInfo, id, fields); User user = dao.get(uriInfo, id, fields, include);
return addHref(uriInfo, user); return addHref(uriInfo, user);
} }
@ -232,10 +245,16 @@ public class UserResource {
description = "Fields requested in the returned resource", description = "Fields requested in the returned resource",
schema = @Schema(type = "string", example = FIELDS)) schema = @Schema(type = "string", example = FIELDS))
@QueryParam("fields") @QueryParam("fields")
String fieldsParam) String fieldsParam,
@Parameter(
description = "Include all, deleted, or non-deleted entities.",
schema = @Schema(implementation = Include.class))
@QueryParam("include")
@DefaultValue("non-deleted")
Include include)
throws IOException, ParseException { throws IOException, ParseException {
Fields fields = new Fields(FIELD_LIST, fieldsParam); Fields fields = new Fields(FIELD_LIST, fieldsParam);
User user = dao.getByName(uriInfo, name, fields); User user = dao.getByName(uriInfo, name, fields, include);
return addHref(uriInfo, user); return addHref(uriInfo, user);
} }

View File

@ -15,6 +15,7 @@ package org.openmetadata.catalog.util;
import static org.openmetadata.catalog.type.Include.ALL; import static org.openmetadata.catalog.type.Include.ALL;
import static org.openmetadata.catalog.type.Include.DELETED; import static org.openmetadata.catalog.type.Include.DELETED;
import static org.openmetadata.catalog.type.Include.NON_DELETED;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -209,13 +210,18 @@ public final class EntityUtil {
// Get owner for a given entity // Get owner for a given entity
public static EntityReference populateOwner( public static EntityReference populateOwner(
UUID id, String entityType, EntityRelationshipDAO entityRelationshipDAO, UserDAO userDAO, TeamDAO teamDAO) EntityInterface entityInterface,
String entityType,
EntityRelationshipDAO entityRelationshipDAO,
UserDAO userDAO,
TeamDAO teamDAO)
throws IOException { throws IOException {
Include include = entityInterface.isDeleted() ? DELETED : NON_DELETED;
List<EntityReference> ids = List<EntityReference> ids =
entityRelationshipDAO.findFrom( entityRelationshipDAO.findFrom(
id.toString(), entityType, Relationship.OWNS.ordinal(), toBoolean(Include.NON_DELETED)); entityInterface.getId().toString(), entityType, Relationship.OWNS.ordinal(), toBoolean(include));
if (ids.size() > 1) { if (ids.size() > 1) {
LOG.warn("Possible database issues - multiple owners {} found for entity {}", ids, id); LOG.warn("Possible database issues - multiple owners {} found for entity {}", ids, entityInterface.getId());
} }
return ids.isEmpty() ? null : populateOwner(userDAO, teamDAO, ids.get(0)); return ids.isEmpty() ? null : populateOwner(userDAO, teamDAO, ids.get(0));
} }

View File

@ -287,6 +287,11 @@ public abstract class EntityResourceTest<T> extends CatalogApplicationTest {
public abstract Object createRequest(String name, String description, String displayName, EntityReference owner) public abstract Object createRequest(String name, String description, String displayName, EntityReference owner)
throws URISyntaxException; throws URISyntaxException;
// Add all possible relationship to check if the entity is missing any of them after deletion
public Object addAllRelationships(TestInfo test, Object create) throws HttpResponseException {
return create;
}
// Get container entity based on create request that has CONTAINS relationship to the entity created with this // 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. // details.
@ -322,8 +327,11 @@ public abstract class EntityResourceTest<T> extends CatalogApplicationTest {
Random rand = new Random(); Random rand = new Random();
int maxEntities = rand.nextInt(16) + 5; int maxEntities = rand.nextInt(16) + 5;
List<UUID> createdUUIDs = new ArrayList<>();
for (int i = 0; i < maxEntities; i++) { for (int i = 0; i < maxEntities; i++) {
createEntity(createRequest(getEntityName(test, i), null, null, null), adminAuthHeaders()); createdUUIDs.add(
getEntityInterface(createEntity(createRequest(getEntityName(test, i), null, null, null), adminAuthHeaders()))
.getId());
} }
T entity = createEntity(createRequest(getEntityName(test, -1), null, null, null), adminAuthHeaders()); T entity = createEntity(createRequest(getEntityName(test, -1), null, null, null), adminAuthHeaders());
@ -409,12 +417,11 @@ public abstract class EntityResourceTest<T> extends CatalogApplicationTest {
} }
} }
// before running "deleted" delete all entries // before running "deleted" delete all created entries otherwise the test doesn't work with just one element.
if ("all".equals(include)) { if ("all".equals(include)) {
// delete all entries
for (T e : allEntities.getData()) { for (T e : allEntities.getData()) {
EntityInterface<T> toBeDeleted = getEntityInterface(e); EntityInterface<T> toBeDeleted = getEntityInterface(e);
if (!toBeDeleted.isDeleted()) { if (createdUUIDs.contains(toBeDeleted.getId()) && !toBeDeleted.isDeleted()) {
deleteEntity(toBeDeleted.getId(), adminAuthHeaders()); deleteEntity(toBeDeleted.getId(), adminAuthHeaders());
} }
} }
@ -480,8 +487,8 @@ public abstract class EntityResourceTest<T> extends CatalogApplicationTest {
@Test @Test
void get_entityIncludeDeleted_200(TestInfo test) throws HttpResponseException, URISyntaxException { void get_entityIncludeDeleted_200(TestInfo test) throws HttpResponseException, URISyntaxException {
Object create = createRequest(getEntityName(test), "", "", null); Object create =
EntityReference container = getContainer(create); addAllRelationships(test, createRequest(getEntityName(test), "description", "displayName", USER_OWNER1));
// Create first time using POST // Create first time using POST
T entity = createEntity(create, adminAuthHeaders()); T entity = createEntity(create, adminAuthHeaders());
EntityInterface<T> entityInterface = getEntityInterface(entity); EntityInterface<T> entityInterface = getEntityInterface(entity);
@ -497,20 +504,16 @@ public abstract class EntityResourceTest<T> extends CatalogApplicationTest {
NOT_FOUND, NOT_FOUND,
entityNotFound(entityName, entityInterface.getFullyQualifiedName())); entityNotFound(entityName, entityInterface.getFullyQualifiedName()));
Map<String, String> queryParams = Map<String, String> queryParams = new HashMap<>();
new HashMap<>() { for (String include : List.of("deleted", "all")) {
{ queryParams.put("include", include);
put("include", "deleted"); validateCreatedEntity(
getEntity(entityInterface.getId(), queryParams, allFields, adminAuthHeaders()), create, adminAuthHeaders());
validateCreatedEntity(
getEntityByName(entityInterface.getFullyQualifiedName(), queryParams, allFields, adminAuthHeaders()),
create,
adminAuthHeaders());
} }
};
checkContainer(container, getEntity(entityInterface.getId(), queryParams, null, adminAuthHeaders()));
checkContainer(
container, getEntityByName(entityInterface.getFullyQualifiedName(), queryParams, null, adminAuthHeaders()));
queryParams.put("include", "all");
checkContainer(container, getEntity(entityInterface.getId(), queryParams, null, adminAuthHeaders()));
checkContainer(
container, getEntityByName(entityInterface.getFullyQualifiedName(), queryParams, null, adminAuthHeaders()));
queryParams.put("include", "non-deleted"); queryParams.put("include", "non-deleted");
assertResponse( assertResponse(
@ -523,14 +526,6 @@ public abstract class EntityResourceTest<T> extends CatalogApplicationTest {
entityNotFound(entityName, entityInterface.getFullyQualifiedName())); entityNotFound(entityName, entityInterface.getFullyQualifiedName()));
} }
protected void checkContainer(EntityReference container, T entity) {
EntityInterface<T> entityInterface = getEntityInterface(entity);
if (container != null) {
assertNotNull(entityInterface.getContainer(), "Container is null");
assertEquals(container.getId(), entityInterface.getContainer().getId(), "Containers are different");
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Common entity tests for POST operations // Common entity tests for POST operations
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -294,6 +294,7 @@ public class DashboardResourceTest extends EntityResourceTest<Dashboard> {
List<UUID> expectedChartReferences = List<UUID> expectedChartReferences =
expectedCharts.stream().map(EntityReference::getId).collect(Collectors.toList()); expectedCharts.stream().map(EntityReference::getId).collect(Collectors.toList());
List<UUID> actualChartReferences = new ArrayList<>(); List<UUID> actualChartReferences = new ArrayList<>();
assertNotNull(dashboard.getCharts(), "dashboard should have charts");
dashboard dashboard
.getCharts() .getCharts()
.forEach( .forEach(

View File

@ -64,14 +64,6 @@ public class WebhookResourceTest extends EntityResourceTest<Webhook> {
supportsPatch = false; supportsPatch = false;
} }
// FIXME: This test is added to be able to merge the PR. We will open a new PR, fix the bug, and remove this test
@Test
void get_entityIncludeDeleted_200(TestInfo test) throws HttpResponseException, URISyntaxException {}
// FIXME: This test is added to be able to merge the PR. We will open a new PR, fix the bug, and remove this test
@Test
void get_entityListWithPagination_200(TestInfo test) throws HttpResponseException, URISyntaxException {}
@Test @Test
void post_webhookEnabledStateChange(TestInfo test) throws URISyntaxException, IOException, InterruptedException { void post_webhookEnabledStateChange(TestInfo test) throws URISyntaxException, IOException, InterruptedException {
// //

View File

@ -340,6 +340,11 @@ public class TeamResourceTest extends EntityResourceTest<Team> {
return create(name).withDescription(description).withDisplayName(displayName).withProfile(PROFILE); return create(name).withDescription(description).withDisplayName(displayName).withProfile(PROFILE);
} }
@Override
public Object addAllRelationships(TestInfo test, Object create) throws HttpResponseException {
return ((CreateTeam) create).withUsers(List.of(USER1.getId()));
}
@Override @Override
public EntityReference getContainer(Object createRequest) throws URISyntaxException { public EntityReference getContainer(Object createRequest) throws URISyntaxException {
return null; // No container entity return null; // No container entity

View File

@ -85,14 +85,6 @@ public class UserResourceTest extends EntityResourceTest<User> {
super(Entity.USER, User.class, UserList.class, "users", UserResource.FIELDS, false, false, false); super(Entity.USER, User.class, UserList.class, "users", UserResource.FIELDS, false, false, false);
} }
// FIXME: This test is added to be able to merge the PR. We will open a new PR, fix the bug, and remove this test
@Test
void get_entityIncludeDeleted_200(TestInfo test) throws HttpResponseException, URISyntaxException {}
// FIXME: This test is added to be able to merge the PR. We will open a new PR, fix the bug, and remove this test
@Test
void get_entityListWithPagination_200(TestInfo test) throws HttpResponseException, URISyntaxException {}
@Test @Test
void post_userWithoutEmail_400_badRequest(TestInfo test) { void post_userWithoutEmail_400_badRequest(TestInfo test) {
// Create user with mandatory email field null // Create user with mandatory email field null
@ -548,6 +540,17 @@ public class UserResourceTest extends EntityResourceTest<User> {
return create(name).withDescription(description).withDisplayName(displayName).withProfile(PROFILE); return create(name).withDescription(description).withDisplayName(displayName).withProfile(PROFILE);
} }
@Override
public Object addAllRelationships(TestInfo test, Object create) throws HttpResponseException {
TeamResourceTest teamResourceTest = new TeamResourceTest();
Team team1 = createTeam(teamResourceTest.create(test), adminAuthHeaders());
((CreateUser) create).setTeams(List.of(team1.getId()));
RoleResourceTest roleResourceTest = new RoleResourceTest();
Role role1 = createRole(roleResourceTest.create(test), adminAuthHeaders());
((CreateUser) create).setRoles(List.of(role1.getId()));
return create;
}
@Override @Override
public EntityReference getContainer(Object createRequest) throws URISyntaxException { public EntityReference getContainer(Object createRequest) throws URISyntaxException {
return null; // No container entity return null; // No container entity