mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2026-01-06 12:36:56 +00:00
This commit is contained in:
parent
1a42428e42
commit
3dd8929567
@ -13,6 +13,7 @@
|
||||
|
||||
package org.openmetadata.catalog.jdbi3;
|
||||
|
||||
import static org.openmetadata.catalog.Entity.ORGANIZATION_NAME;
|
||||
import static org.openmetadata.catalog.jdbi3.locator.ConnectionType.MYSQL;
|
||||
import static org.openmetadata.catalog.jdbi3.locator.ConnectionType.POSTGRES;
|
||||
|
||||
@ -80,6 +81,7 @@ import org.openmetadata.catalog.type.UsageStats;
|
||||
import org.openmetadata.catalog.type.Webhook;
|
||||
import org.openmetadata.catalog.util.EntitiesCount;
|
||||
import org.openmetadata.catalog.util.EntityUtil;
|
||||
import org.openmetadata.catalog.util.FullyQualifiedName;
|
||||
import org.openmetadata.catalog.util.ServicesCount;
|
||||
import org.openmetadata.common.utils.CommonUtil;
|
||||
|
||||
@ -1818,49 +1820,133 @@ public interface CollectionDAO {
|
||||
return "name";
|
||||
}
|
||||
|
||||
default List<String> listUsersWithoutParents() {
|
||||
return listUsersWithoutParents(Relationship.HAS.ordinal());
|
||||
@Override
|
||||
default int listCount(ListFilter filter) {
|
||||
String parentTeam = filter.getQueryParam("parentTeam");
|
||||
String condition = filter.getCondition();
|
||||
if (parentTeam != null) {
|
||||
// validate parent team
|
||||
Team team = findEntityByName(parentTeam);
|
||||
if (ORGANIZATION_NAME.equals(team.getName())) {
|
||||
// All the parentless teams should come under "organization" team
|
||||
condition =
|
||||
String.format(
|
||||
"%s AND id NOT IN ( (SELECT '%s') UNION (SELECT toId FROM entity_relationship WHERE fromId!='%s' AND fromEntity='team' AND toEntity='team' AND relation=%d) )",
|
||||
condition, team.getId(), team.getId(), Relationship.PARENT_OF.ordinal());
|
||||
} else {
|
||||
condition =
|
||||
String.format(
|
||||
"%s AND id IN (SELECT toId FROM entity_relationship WHERE fromId='%s' AND fromEntity='team' AND toEntity='team' AND relation=%d)",
|
||||
condition, team.getId(), Relationship.PARENT_OF.ordinal());
|
||||
}
|
||||
}
|
||||
|
||||
return listCount(getTableName(), getNameColumn(), condition);
|
||||
}
|
||||
|
||||
default List<String> listTeamsWithoutParents() {
|
||||
return listTeamsWithoutParents(Relationship.PARENT_OF.ordinal());
|
||||
@Override
|
||||
default List<String> listBefore(ListFilter filter, int limit, String before) {
|
||||
String parentTeam = filter.getQueryParam("parentTeam");
|
||||
String condition = filter.getCondition();
|
||||
if (parentTeam != null) {
|
||||
// validate parent team
|
||||
Team team = findEntityByName(parentTeam);
|
||||
if (ORGANIZATION_NAME.equals(team.getName())) {
|
||||
// All the parentless teams should come under "organization" team
|
||||
condition =
|
||||
String.format(
|
||||
"%s AND id NOT IN ( (SELECT '%s') UNION (SELECT toId FROM entity_relationship WHERE fromId!='%s' AND fromEntity='team' AND toEntity='team' AND relation=%d) )",
|
||||
condition, team.getId(), team.getId(), Relationship.PARENT_OF.ordinal());
|
||||
} else {
|
||||
condition =
|
||||
String.format(
|
||||
"%s AND id IN (SELECT toId FROM entity_relationship WHERE fromId='%s' AND fromEntity='team' AND toEntity='team' AND relation=%d)",
|
||||
condition, team.getId(), Relationship.PARENT_OF.ordinal());
|
||||
}
|
||||
}
|
||||
|
||||
// Quoted name is stored in fullyQualifiedName column and not in the name column
|
||||
before = getNameColumn().equals("name") ? FullyQualifiedName.unquoteName(before) : before;
|
||||
return listBefore(getTableName(), getNameColumn(), condition, limit, before);
|
||||
}
|
||||
|
||||
@ConnectionAwareSqlQuery(
|
||||
value =
|
||||
"SELECT ue.id "
|
||||
+ "FROM user_entity ue "
|
||||
+ "WHERE ue.id NOT IN "
|
||||
+ "(SELECT toId FROM entity_relationship "
|
||||
+ "WHERE fromEntity = `team` AND relation = :relation AND toEntity = `user`)",
|
||||
connectionType = MYSQL)
|
||||
@ConnectionAwareSqlQuery(
|
||||
value =
|
||||
"SELECT ue.id "
|
||||
+ "FROM user_entity ue "
|
||||
+ "WHERE ue.id NOT IN "
|
||||
+ "(SELECT toId FROM entity_relationship "
|
||||
+ "WHERE fromEntity = `team` AND relation = :relation AND toEntity = `user`)",
|
||||
connectionType = POSTGRES)
|
||||
List<String> listUsersWithoutParents(@Bind("relation") int relation);
|
||||
@Override
|
||||
default List<String> listAfter(ListFilter filter, int limit, String after) {
|
||||
String parentTeam = filter.getQueryParam("parentTeam");
|
||||
String condition = filter.getCondition();
|
||||
if (parentTeam != null) {
|
||||
// validate parent team
|
||||
Team team = findEntityByName(parentTeam);
|
||||
if (ORGANIZATION_NAME.equals(team.getName())) {
|
||||
// All the parentless teams should come under "organization" team
|
||||
condition =
|
||||
String.format(
|
||||
"%s AND id NOT IN ( (SELECT '%s') UNION (SELECT toId FROM entity_relationship WHERE fromId!='%s' AND fromEntity='team' AND toEntity='team' AND relation=%d) )",
|
||||
condition, team.getId(), team.getId(), Relationship.PARENT_OF.ordinal());
|
||||
} else {
|
||||
condition =
|
||||
String.format(
|
||||
"%s AND id IN (SELECT toId FROM entity_relationship WHERE fromId='%s' AND fromEntity='team' AND toEntity='team' AND relation=%d)",
|
||||
condition, team.getId(), Relationship.PARENT_OF.ordinal());
|
||||
}
|
||||
}
|
||||
|
||||
@ConnectionAwareSqlQuery(
|
||||
value =
|
||||
"SELECT te.id "
|
||||
+ "FROM team_entity te "
|
||||
+ "WHERE te.id NOT IN "
|
||||
+ "(SELECT toId FROM entity_relationship "
|
||||
+ "WHERE fromEntity = 'team' AND relation = :relation AND toEntity = 'user')",
|
||||
connectionType = MYSQL)
|
||||
@ConnectionAwareSqlQuery(
|
||||
value =
|
||||
"SELECT te.id "
|
||||
+ "FROM team_entity te "
|
||||
+ "WHERE te.id NOT IN "
|
||||
+ "(SELECT toId FROM entity_relationship "
|
||||
+ "WHERE fromEntity = 'team' AND relation = :relation AND toEntity = 'user')",
|
||||
connectionType = POSTGRES)
|
||||
List<String> listTeamsWithoutParents(@Bind("relation") int relation);
|
||||
// Quoted name is stored in fullyQualifiedName column and not in the name column
|
||||
after = getNameColumn().equals("name") ? FullyQualifiedName.unquoteName(after) : after;
|
||||
return listAfter(getTableName(), getNameColumn(), condition, limit, after);
|
||||
}
|
||||
|
||||
@SqlQuery("SELECT count(*) FROM <table> <cond>")
|
||||
int listCount(@Define("table") String table, @Define("nameColumn") String nameColumn, @Define("cond") String cond);
|
||||
|
||||
@SqlQuery(
|
||||
"SELECT json FROM ("
|
||||
+ "SELECT <nameColumn>, json FROM <table> <cond> AND "
|
||||
+ "<nameColumn> < :before "
|
||||
+ // Pagination by entity fullyQualifiedName or name (when entity does not have fqn)
|
||||
"ORDER BY <nameColumn> DESC "
|
||||
+ // Pagination ordering by entity fullyQualifiedName or name (when entity does not have fqn)
|
||||
"LIMIT :limit"
|
||||
+ ") last_rows_subquery ORDER BY <nameColumn>")
|
||||
List<String> listBefore(
|
||||
@Define("table") String table,
|
||||
@Define("nameColumn") String nameColumn,
|
||||
@Define("cond") String cond,
|
||||
@Bind("limit") int limit,
|
||||
@Bind("before") String before);
|
||||
|
||||
@SqlQuery(
|
||||
"SELECT json FROM <table> <cond> AND " + "<nameColumn> > :after " + "ORDER BY <nameColumn> " + "LIMIT :limit")
|
||||
List<String> listAfter(
|
||||
@Define("table") String table,
|
||||
@Define("nameColumn") String nameColumn,
|
||||
@Define("cond") String cond,
|
||||
@Bind("limit") int limit,
|
||||
@Bind("after") String after);
|
||||
|
||||
default List<String> listUsersUnderOrganization(String teamId) {
|
||||
return listUsersUnderOrganization(teamId, Relationship.HAS.ordinal());
|
||||
}
|
||||
|
||||
default List<String> listTeamsUnderOrganization(String teamId) {
|
||||
return listTeamsUnderOrganization(teamId, Relationship.PARENT_OF.ordinal());
|
||||
}
|
||||
|
||||
@SqlQuery(
|
||||
"SELECT ue.id "
|
||||
+ "FROM user_entity ue "
|
||||
+ "WHERE ue.id NOT IN (SELECT :teamId) UNION "
|
||||
+ "(SELECT toId FROM entity_relationship "
|
||||
+ "WHERE fromId != :teamId AND fromEntity = `team` AND relation = :relation AND toEntity = `user`)")
|
||||
List<String> listUsersUnderOrganization(@Bind("teamId") String teamId, @Bind("relation") int relation);
|
||||
|
||||
@SqlQuery(
|
||||
"SELECT te.id "
|
||||
+ "FROM team_entity te "
|
||||
+ "WHERE te.id NOT IN (SELECT :teamId) UNION "
|
||||
+ "(SELECT toId FROM entity_relationship "
|
||||
+ "WHERE fromId != :teamId AND fromEntity = 'team' AND relation = :relation AND toEntity = 'team')")
|
||||
List<String> listTeamsUnderOrganization(@Bind("teamId") String teamId, @Bind("relation") int relation);
|
||||
}
|
||||
|
||||
interface TopicDAO extends EntityDAO<Topic> {
|
||||
|
||||
@ -66,8 +66,10 @@ public class TeamRepository extends EntityRepository<Team> {
|
||||
team.setDefaultRoles(fields.contains("defaultRoles") ? getDefaultRoles(team) : null);
|
||||
team.setOwner(fields.contains(FIELD_OWNER) ? getOwner(team) : null);
|
||||
team.setParents(fields.contains("parents") ? getParents(team) : null);
|
||||
team.setChildren(fields.contains("children") ? getChildren(team) : null);
|
||||
team.setChildren(fields.contains("children") ? getChildren(team.getId()) : null);
|
||||
team.setPolicies(fields.contains("policies") ? getPolicies(team) : null);
|
||||
team.setChildrenCount(fields.contains("childrenCount") ? getChildrenCount(team) : null);
|
||||
team.setUserCount(fields.contains("userCount") ? getUserCount(team.getId()) : null);
|
||||
return team;
|
||||
}
|
||||
|
||||
@ -168,6 +170,16 @@ public class TeamRepository extends EntityRepository<Team> {
|
||||
return EntityUtil.populateEntityReferences(userIds, Entity.USER);
|
||||
}
|
||||
|
||||
private Integer getUserCount(UUID teamId) throws IOException {
|
||||
List<EntityRelationshipRecord> userIds = findTo(teamId, TEAM, Relationship.HAS, Entity.USER);
|
||||
int userCount = userIds.size();
|
||||
List<EntityReference> children = getChildren(teamId);
|
||||
for (EntityReference child : children) {
|
||||
userCount += getUserCount(child.getId());
|
||||
}
|
||||
return userCount;
|
||||
}
|
||||
|
||||
private List<EntityReference> getOwns(Team team) throws IOException {
|
||||
// Compile entities owned by the team
|
||||
return EntityUtil.getEntityReferences(
|
||||
@ -187,15 +199,19 @@ public class TeamRepository extends EntityRepository<Team> {
|
||||
return EntityUtil.populateEntityReferences(parents, TEAM);
|
||||
}
|
||||
|
||||
private List<EntityReference> getChildren(Team team) throws IOException {
|
||||
if (team.getId().equals(organization.getId())) { // For organization all the parentless teams are children
|
||||
List<String> children = daoCollection.teamDAO().listTeamsWithoutParents();
|
||||
private List<EntityReference> getChildren(UUID teamId) throws IOException {
|
||||
if (teamId.equals(organization.getId())) { // For organization all the parentless teams are children
|
||||
List<String> children = daoCollection.teamDAO().listTeamsUnderOrganization(teamId.toString());
|
||||
return EntityUtil.populateEntityReferencesById(children, Entity.TEAM);
|
||||
}
|
||||
List<EntityRelationshipRecord> children = findTo(team.getId(), TEAM, Relationship.PARENT_OF, TEAM);
|
||||
List<EntityRelationshipRecord> children = findTo(teamId, TEAM, Relationship.PARENT_OF, TEAM);
|
||||
return EntityUtil.populateEntityReferences(children, TEAM);
|
||||
}
|
||||
|
||||
private Integer getChildrenCount(Team team) throws IOException {
|
||||
return getChildren(team.getId()).size();
|
||||
}
|
||||
|
||||
private List<EntityReference> getPolicies(Team team) throws IOException {
|
||||
List<EntityRelationshipRecord> policies = findTo(team.getId(), TEAM, Relationship.HAS, POLICY);
|
||||
return EntityUtil.populateEntityReferences(policies, POLICY);
|
||||
|
||||
@ -101,7 +101,8 @@ public class TeamResource extends EntityResource<Team, TeamRepository> {
|
||||
}
|
||||
}
|
||||
|
||||
static final String FIELDS = "owner,profile,users,owns,defaultRoles,parents,children,policies";
|
||||
static final String FIELDS =
|
||||
"owner,profile,users,owns,defaultRoles,parents,children,policies,userCount,childrenCount";
|
||||
|
||||
@GET
|
||||
@Valid
|
||||
@ -139,6 +140,9 @@ public class TeamResource extends EntityResource<Team, TeamRepository> {
|
||||
@Parameter(description = "Returns list of teams after this cursor", schema = @Schema(type = "string"))
|
||||
@QueryParam("after")
|
||||
String after,
|
||||
@Parameter(description = "Filter the results by parent team name", schema = @Schema(type = "string"))
|
||||
@QueryParam("parentTeam")
|
||||
String parentTeam,
|
||||
@Parameter(
|
||||
description = "Include all, deleted, or non-deleted entities.",
|
||||
schema = @Schema(implementation = Include.class))
|
||||
@ -146,7 +150,7 @@ public class TeamResource extends EntityResource<Team, TeamRepository> {
|
||||
@DefaultValue("non-deleted")
|
||||
Include include)
|
||||
throws IOException {
|
||||
ListFilter filter = new ListFilter(include);
|
||||
ListFilter filter = new ListFilter(include).addQueryParam("parentTeam", parentTeam);
|
||||
return super.listInternal(uriInfo, securityContext, fieldsParam, filter, limitParam, before, after);
|
||||
}
|
||||
|
||||
|
||||
@ -76,6 +76,14 @@
|
||||
"$ref": "../../type/entityReference.json#/definitions/entityReferenceList",
|
||||
"default": null
|
||||
},
|
||||
"childrenCount": {
|
||||
"description" : "Total count of Children teams.",
|
||||
"type": "integer"
|
||||
},
|
||||
"userCount": {
|
||||
"description": "Total count of users that are part of the team.",
|
||||
"type": "integer"
|
||||
},
|
||||
"owns": {
|
||||
"description": "List of entities owned by the team.",
|
||||
"$ref": "../../type/entityReference.json#/definitions/entityReferenceList"
|
||||
|
||||
@ -19,6 +19,8 @@ import static javax.ws.rs.core.Response.Status.OK;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.openmetadata.catalog.Entity.ORGANIZATION_NAME;
|
||||
import static org.openmetadata.catalog.api.teams.CreateTeam.TeamType.BUSINESS_UNIT;
|
||||
import static org.openmetadata.catalog.api.teams.CreateTeam.TeamType.DEPARTMENT;
|
||||
import static org.openmetadata.catalog.api.teams.CreateTeam.TeamType.DIVISION;
|
||||
@ -41,6 +43,7 @@ import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
@ -77,6 +80,7 @@ import org.openmetadata.catalog.type.PolicyType;
|
||||
import org.openmetadata.catalog.type.Profile;
|
||||
import org.openmetadata.catalog.util.EntityUtil;
|
||||
import org.openmetadata.catalog.util.JsonUtils;
|
||||
import org.openmetadata.catalog.util.ResultList;
|
||||
import org.openmetadata.catalog.util.TestUtils;
|
||||
import org.openmetadata.catalog.util.TestUtils.UpdateType;
|
||||
|
||||
@ -94,13 +98,13 @@ public class TeamResourceTest extends EntityResourceTest<Team, CreateTeam> {
|
||||
TEAM1 = teamResourceTest.createEntity(teamResourceTest.createRequest(test), ADMIN_AUTH_HEADERS);
|
||||
TEAM_OWNER1 = TEAM1.getEntityReference();
|
||||
|
||||
ORG_TEAM = teamResourceTest.getEntityByName(Entity.ORGANIZATION_NAME, "", ADMIN_AUTH_HEADERS);
|
||||
ORG_TEAM = teamResourceTest.getEntityByName(ORGANIZATION_NAME, "", ADMIN_AUTH_HEADERS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_organization() throws HttpResponseException {
|
||||
// Ensure getting organization from team hierarchy is successful
|
||||
Team org = getEntityByName(Entity.ORGANIZATION_NAME, "", ADMIN_AUTH_HEADERS);
|
||||
Team org = getEntityByName(ORGANIZATION_NAME, "", ADMIN_AUTH_HEADERS);
|
||||
|
||||
// Organization can't be deleted
|
||||
assertResponse(
|
||||
@ -356,6 +360,23 @@ public class TeamResourceTest extends EntityResourceTest<Team, CreateTeam> {
|
||||
"t1", BUSINESS_UNIT, bu11.getEntityReference(), div12.getEntityReference(), dep13.getEntityReference());
|
||||
assertEntityReferencesContain(t1.getParents(), ORG_TEAM.getEntityReference());
|
||||
|
||||
// assert children count for the newly created bu team
|
||||
Map<String, String> queryParams = new HashMap<>();
|
||||
queryParams.put("parentTeam", "t1");
|
||||
queryParams.put("fields", "childrenCount,userCount");
|
||||
ResultList<Team> teams = listEntities(queryParams, ADMIN_AUTH_HEADERS);
|
||||
assertEquals(3, teams.getData().size());
|
||||
assertEquals(3, teams.getPaging().getTotal());
|
||||
assertEquals(0, teams.getData().get(0).getChildrenCount());
|
||||
assertEquals(0, teams.getData().get(0).getUserCount());
|
||||
|
||||
queryParams.put("parentTeam", ORGANIZATION_NAME);
|
||||
teams = listEntities(queryParams, ADMIN_AUTH_HEADERS);
|
||||
assertTrue(teams.getData().stream().anyMatch(t -> t.getName().equals("t1")));
|
||||
t1 = teams.getData().stream().filter(t -> t.getName().equals("t1")).collect(Collectors.toList()).get(0);
|
||||
assertEquals(3, t1.getChildrenCount());
|
||||
assertEquals(0, t1.getUserCount());
|
||||
|
||||
//
|
||||
// Creating a parent with invalid children type is not allowed
|
||||
// Department can't have Business unit as a child
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user