Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2021 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openmetadata.service.jdbi3;
import static org.openmetadata.common.utils.CommonUtil.listOrEmpty;
import static org.openmetadata.common.utils.CommonUtil.listOrEmptyMutable;
import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty;
import static org.openmetadata.csv.CsvUtil.addEntityReferences;
import static org.openmetadata.csv.CsvUtil.addField;
import static org.openmetadata.csv.CsvUtil.addOwners;
import static org.openmetadata.schema.api.teams.CreateTeam.TeamType.BUSINESS_UNIT;
import static org.openmetadata.schema.api.teams.CreateTeam.TeamType.DEPARTMENT;
import static org.openmetadata.schema.api.teams.CreateTeam.TeamType.DIVISION;
import static org.openmetadata.schema.api.teams.CreateTeam.TeamType.GROUP;
import static org.openmetadata.schema.api.teams.CreateTeam.TeamType.ORGANIZATION;
import static org.openmetadata.schema.type.Include.ALL;
import static org.openmetadata.schema.type.Include.NON_DELETED;
import static org.openmetadata.service.Entity.ADMIN_USER_NAME;
import static org.openmetadata.service.Entity.FIELD_DOMAINS;
import static org.openmetadata.service.Entity.ORGANIZATION_NAME;
import static org.openmetadata.service.Entity.POLICY;
import static org.openmetadata.service.Entity.ROLE;
import static org.openmetadata.service.Entity.TEAM;
import static org.openmetadata.service.exception.CatalogExceptionMessage.CREATE_GROUP;
import static org.openmetadata.service.exception.CatalogExceptionMessage.DELETE_ORGANIZATION;
import static org.openmetadata.service.exception.CatalogExceptionMessage.INVALID_GROUP_TEAM_CHILDREN_UPDATE;
import static org.openmetadata.service.exception.CatalogExceptionMessage.INVALID_GROUP_TEAM_UPDATE;
import static org.openmetadata.service.exception.CatalogExceptionMessage.TEAM_HIERARCHY;
import static org.openmetadata.service.exception.CatalogExceptionMessage.UNEXPECTED_PARENT;
import static org.openmetadata.service.exception.CatalogExceptionMessage.invalidChild;
import static org.openmetadata.service.exception.CatalogExceptionMessage.invalidParent;
import static org.openmetadata.service.exception.CatalogExceptionMessage.invalidParentCount;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.csv.CSVRecord;
import org.jdbi.v3.sqlobject.transaction.Transaction;
import org.openmetadata.common.utils.CommonUtil;
import org.openmetadata.csv.EntityCsv;
import org.openmetadata.schema.api.teams.CreateTeam.TeamType;
import org.openmetadata.schema.entity.teams.Team;
import org.openmetadata.schema.entity.teams.TeamHierarchy;
import org.openmetadata.schema.type.EntityReference;
import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.Relationship;
import org.openmetadata.schema.type.api.BulkAssets;
import org.openmetadata.schema.type.api.BulkOperationResult;
import org.openmetadata.schema.type.csv.CsvDocumentation;
import org.openmetadata.schema.type.csv.CsvErrorType;
import org.openmetadata.schema.type.csv.CsvFile;
import org.openmetadata.schema.type.csv.CsvHeader;
import org.openmetadata.schema.type.csv.CsvImportResult;
import org.openmetadata.service.Entity;
import org.openmetadata.service.exception.EntityNotFoundException;
import org.openmetadata.service.jdbi3.CollectionDAO.EntityRelationshipRecord;
import org.openmetadata.service.resources.teams.TeamResource;
import org.openmetadata.service.security.policyevaluator.SubjectContext;
import org.openmetadata.service.util.EntityUtil;
import org.openmetadata.service.util.EntityUtil.Fields;
import org.openmetadata.service.util.ResultList;
@Slf4j
public class TeamRepository extends EntityRepository {
static final String PARENTS_FIELD = "parents";
static final String TEAM_UPDATE_FIELDS =
"profile,users,defaultRoles,parents,children,policies,teamType,email,domains";
static final String TEAM_PATCH_FIELDS =
"profile,users,defaultRoles,parents,children,policies,teamType,email,domains";
private static final String DEFAULT_ROLES = "defaultRoles";
private Team organization = null;
public TeamRepository() {
super(
TeamResource.COLLECTION_PATH,
TEAM,
Team.class,
Entity.getCollectionDAO().teamDAO(),
TEAM_PATCH_FIELDS,
TEAM_UPDATE_FIELDS);
this.quoteFqn = true;
supportsSearch = true;
}
@Override
public void setFields(Team team, Fields fields) {
team.setUsers(fields.contains("users") ? getUsers(team) : team.getUsers());
team.setOwns(fields.contains("owns") ? getOwns(team) : team.getOwns());
team.setDefaultRoles(
fields.contains(DEFAULT_ROLES) ? getDefaultRoles(team) : team.getDefaultRoles());
team.setInheritedRoles(
fields.contains(DEFAULT_ROLES) ? getInheritedRoles(team) : team.getInheritedRoles());
team.setParents(fields.contains(PARENTS_FIELD) ? getParents(team) : team.getParents());
team.setPolicies(fields.contains("policies") ? getPolicies(team) : team.getPolicies());
team.setChildrenCount(
fields.contains("childrenCount") ? getChildrenCount(team) : team.getChildrenCount());
team.setUserCount(
fields.contains("userCount") ? getUserCount(team.getId()) : team.getUserCount());
team.setDomains(fields.contains(FIELD_DOMAINS) ? getDomains(team.getId()) : team.getDomains());
}
@Override
public void clearFields(Team team, Fields fields) {
team.setProfile(fields.contains("profile") ? team.getProfile() : null);
team.setUsers(fields.contains("users") ? team.getUsers() : null);
team.setOwns(fields.contains("owns") ? team.getOwns() : null);
team.setDefaultRoles(fields.contains(DEFAULT_ROLES) ? team.getDefaultRoles() : null);
team.setInheritedRoles(fields.contains(DEFAULT_ROLES) ? team.getInheritedRoles() : null);
team.setParents(fields.contains(PARENTS_FIELD) ? team.getParents() : null);
team.setPolicies(fields.contains("policies") ? team.getPolicies() : null);
team.setChildrenCount(fields.contains("childrenCount") ? team.getChildrenCount() : null);
team.withUserCount(fields.contains("userCount") ? team.getUserCount() : null);
}
private List getDomains(UUID teamId) {
// Team does not have domain. 'domains' is the field for user as team can belong to multiple
// domains
return findFrom(teamId, TEAM, Relationship.HAS, Entity.DOMAIN);
}
@Override
protected void storeDomain(Team entity, EntityReference exclude) {
for (EntityReference domainRef : listOrEmpty(entity.getDomains())) {
// Add relationship domain --- has ---> entity
LOG.info(
"Adding domain {} for user {}:{}",
domainRef.getFullyQualifiedName(),
entityType,
entity.getId());
addRelationship(
domainRef.getId(), entity.getId(), Entity.DOMAIN, entityType, Relationship.HAS);
}
}
@Override
public void restorePatchAttributes(Team original, Team updated) {
// Patch can't make changes to following fields. Ignore the changes
super.restorePatchAttributes(original, updated);
updated.withInheritedRoles(original.getInheritedRoles());
}
@Override
public void prepare(Team team, boolean update) {
populateParents(team); // Validate parents
populateChildren(team); // Validate children
validateUsers(team.getUsers());
validateRoles(team.getDefaultRoles());
validatePolicies(team.getPolicies());
}
public BulkOperationResult bulkAddAssets(String domainName, BulkAssets request) {
Team team = getByName(null, domainName, getFields("id"));
// Validate all to be users
validateAllRefUsers(request.getAssets());
for (EntityReference asset : request.getAssets()) {
if (!Objects.equals(asset.getType(), Entity.USER)) {
throw new IllegalArgumentException("Only users can be added to a Team");
}
}
return bulkAssetsOperation(team.getId(), TEAM, Relationship.HAS, request, true);
}
public BulkOperationResult bulkRemoveAssets(String domainName, BulkAssets request) {
Team team = getByName(null, domainName, getFields("id"));
// Validate all to be users
validateAllRefUsers(request.getAssets());
return bulkAssetsOperation(team.getId(), TEAM, Relationship.HAS, request, false);
}
private void validateAllRefUsers(List refs) {
for (EntityReference asset : refs) {
if (!Objects.equals(asset.getType(), Entity.USER)) {
throw new IllegalArgumentException("Only users can be added to a Team");
}
}
}
@Override
public void storeEntity(Team team, boolean update) {
// Relationships and fields such as href are derived and not stored as part of json
List users = team.getUsers();
List defaultRoles = team.getDefaultRoles();
List parents = team.getParents();
List policies = team.getPolicies();
// Don't store users, defaultRoles, href as JSON. Build it on the fly based on relationships
team.withUsers(null)
.withDefaultRoles(null)
.withParents(null)
.withPolicies(null)
.withInheritedRoles(null);
store(team, update);
// Restore the relationships
team.withUsers(users)
.withDefaultRoles(defaultRoles)
.withParents(parents)
.withPolicies(policies);
}
@Override
public void storeRelationships(Team team) {
for (EntityReference user : listOrEmpty(team.getUsers())) {
addRelationship(team.getId(), user.getId(), TEAM, Entity.USER, Relationship.HAS);
}
for (EntityReference defaultRole : listOrEmpty(team.getDefaultRoles())) {
addRelationship(team.getId(), defaultRole.getId(), TEAM, Entity.ROLE, Relationship.HAS);
}
for (EntityReference parent : listOrEmpty(team.getParents())) {
if (parent.getId().equals(organization.getId())) {
continue; // When the parent is the default parent - organization, don't store the
// relationship
}
addRelationship(parent.getId(), team.getId(), TEAM, TEAM, Relationship.PARENT_OF);
}
for (EntityReference child : listOrEmpty(team.getChildren())) {
addRelationship(team.getId(), child.getId(), TEAM, TEAM, Relationship.PARENT_OF);
}
for (EntityReference policy : listOrEmpty(team.getPolicies())) {
addRelationship(team.getId(), policy.getId(), TEAM, POLICY, Relationship.HAS);
}
}
@Override
public void setInheritedFields(Team team, Fields fields) {
// If user does not have domain, then inherit it from parent Team
// TODO have default team when a user belongs to multiple teams
if (fields.contains(FIELD_DOMAINS)) {
Set combinedParent = new TreeSet<>(EntityUtil.compareEntityReferenceById);
List parents =
!fields.contains(PARENTS_FIELD) ? getParents(team) : team.getParents();
if (!nullOrEmpty(parents)) {
for (EntityReference parentRef : parents) {
Team parentTeam = Entity.getEntity(TEAM, parentRef.getId(), "domains", ALL);
combinedParent.addAll(parentTeam.getDomains());
}
}
team.setDomains(
EntityUtil.mergedInheritedEntityRefs(
team.getDomains(), combinedParent.stream().toList()));
}
}
@Override
public TeamUpdater getUpdater(Team original, Team updated, Operation operation) {
return new TeamUpdater(original, updated, operation);
}
@Override
protected void preDelete(Team entity, String deletedBy) {
if (entity.getId().equals(organization.getId())) {
throw new IllegalArgumentException(DELETE_ORGANIZATION);
}
}
@Override
protected void cleanup(Team team) {
// When a team is deleted, if the children team don't have another parent, set Organization as
// the parent
for (EntityReference child : listOrEmpty(team.getChildren())) {
Team childTeam = find(child.getId(), NON_DELETED);
getParents(childTeam);
if (childTeam.getParents().size()
== 1) { // Only parent is being deleted, move the parent to Organization
addRelationship(
organization.getId(), childTeam.getId(), TEAM, TEAM, Relationship.PARENT_OF);
LOG.info("Moving parent of team " + childTeam.getId() + " to organization");
}
}
super.cleanup(team);
}
@Override
public String exportToCsv(String parentTeam, String user) throws IOException {
Team team = getByName(null, parentTeam, Fields.EMPTY_FIELDS); // Validate team name
return new TeamCsv(team, user).exportCsv();
}
@Override
public CsvImportResult importFromCsv(String name, String csv, boolean dryRun, String user)
throws IOException {
Team team = getByName(null, name, Fields.EMPTY_FIELDS); // Validate team name
TeamCsv teamCsv = new TeamCsv(team, user);
return teamCsv.importCsv(csv, dryRun);
}
private List getInheritedRoles(Team team) {
return SubjectContext.getRolesForTeams(getParentsForInheritedRoles(team));
}
private TeamHierarchy getTeamHierarchy(Team team) {
return new TeamHierarchy()
.withId(team.getId())
.withTeamType(team.getTeamType())
.withName(team.getName())
.withDisplayName(team.getDisplayName())
.withHref(team.getHref())
.withFullyQualifiedName(team.getFullyQualifiedName())
.withIsJoinable(team.getIsJoinable())
.withChildren(null)
.withDescription(team.getDescription());
}
private TeamHierarchy deepCopy(TeamHierarchy team) {
TeamHierarchy newTeam =
new TeamHierarchy()
.withId(team.getId())
.withTeamType(team.getTeamType())
.withName(team.getName())
.withDisplayName(team.getDisplayName())
.withHref(team.getHref())
.withFullyQualifiedName(team.getFullyQualifiedName())
.withIsJoinable(team.getIsJoinable());
if (team.getChildren() != null) {
List children = new ArrayList<>();
for (TeamHierarchy n : team.getChildren()) {
children.add(deepCopy(n));
}
newTeam.withChildren(children);
}
return newTeam;
}
private TeamHierarchy mergeTrees(TeamHierarchy team1, TeamHierarchy team2) {
List team1Children = team1.getChildren();
List team2Children = team2.getChildren();
if (team1Children != null && team2Children != null) {
List toMerge = new ArrayList<>(team1Children);
toMerge.retainAll(team2Children);
for (TeamHierarchy n : toMerge) mergeTrees(n, team2Children.get(team2Children.indexOf(n)));
}
if (team2Children != null) {
List toAdd = new ArrayList<>(team2Children);
if (team1Children != null) {
toAdd.removeAll(team1Children);
} else {
team1.setChildren(new ArrayList<>());
}
for (TeamHierarchy n : toAdd) team1.getChildren().add(deepCopy(n));
}
return team1;
}
public List listHierarchy(ListFilter filter, int limit, Boolean isJoinable) {
Fields fields = getFields(PARENTS_FIELD);
Map map = new HashMap<>();
ResultList resultList = listAfter(null, fields, filter, limit, null);
List allTeams = resultList.getData();
List joinableTeams =
allTeams.stream()
.filter(Boolean.TRUE.equals(isJoinable) ? Team::getIsJoinable : t -> true)
.filter(t -> !t.getName().equals(ORGANIZATION_NAME))
.toList();
// build hierarchy of joinable teams
joinableTeams.forEach(
team -> {
Team currentTeam = team;
TeamHierarchy currentHierarchy = getTeamHierarchy(team);
while (currentTeam != null
&& !currentTeam.getParents().isEmpty()
&& !currentTeam.getParents().get(0).getName().equals(ORGANIZATION_NAME)) {
EntityReference parentRef = currentTeam.getParents().get(0);
Team parent =
allTeams.stream()
.filter(t -> t.getId().equals(parentRef.getId()))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(TEAM_HIERARCHY));
currentHierarchy =
getTeamHierarchy(parent).withChildren(new ArrayList<>(List.of(currentHierarchy)));
if (map.containsKey(parent.getId())) {
TeamHierarchy parentTeam = map.get(parent.getId());
currentHierarchy = mergeTrees(parentTeam, currentHierarchy);
currentTeam =
allTeams.stream()
.filter(t -> t.getId().equals(parent.getId()))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(TEAM_HIERARCHY));
} else {
currentTeam = parent;
}
}
UUID currentId = currentHierarchy.getId();
if (!map.containsKey(currentId)) {
map.put(currentId, currentHierarchy);
} else {
map.put(currentId, mergeTrees(map.get(currentId), currentHierarchy));
}
});
return new ArrayList<>(map.values());
}
private List getUsers(Team team) {
return findTo(team.getId(), TEAM, Relationship.HAS, Entity.USER);
}
private List getUsersRelationshipRecords(UUID teamId) {
List userRecord =
findToRecords(teamId, TEAM, Relationship.HAS, Entity.USER);
List children = getChildren(teamId);
for (EntityReference child : children) {
userRecord.addAll(getUsersRelationshipRecords(child.getId()));
}
return userRecord;
}
private Integer getUserCount(UUID teamId) {
List userIds = new ArrayList<>();
List userRecordList = getUsersRelationshipRecords(teamId);
for (EntityRelationshipRecord userRecord : userRecordList) {
userIds.add(userRecord.getId().toString());
}
Set userIdsSet = new HashSet<>(userIds);
userIds.clear();
userIds.addAll(userIdsSet);
return userIds.size();
}
private List getOwns(Team team) {
// Compile entities owned by the team
return findTo(team.getId(), TEAM, Relationship.OWNS, null);
}
private List getDefaultRoles(Team team) {
return findTo(team.getId(), TEAM, Relationship.HAS, Entity.ROLE);
}
private List getParents(Team team) {
List parents = findFrom(team.getId(), TEAM, Relationship.PARENT_OF, TEAM);
if (organization != null
&& listOrEmpty(parents).isEmpty()
&& !team.getId().equals(organization.getId())) {
return new ArrayList<>(List.of(organization.getEntityReference()));
}
return parents;
}
private List getParentsForInheritedRoles(Team team) {
// filter out any deleted teams
List parents =
findFrom(team.getId(), TEAM, Relationship.PARENT_OF, TEAM).stream()
.filter(e -> !e.getDeleted())
.collect(Collectors.toList());
if (organization != null
&& listOrEmpty(parents).isEmpty()
&& !team.getId().equals(organization.getId())) {
return new ArrayList<>(List.of(organization.getEntityReference()));
}
return parents;
}
@Override
protected List getChildren(Team team) {
return getChildren(team.getId());
}
protected List getChildren(UUID teamId) {
if (teamId.equals(
organization.getId())) { // For organization all the parentless teams are children
List children = daoCollection.teamDAO().listTeamsUnderOrganization(teamId);
return EntityUtil.populateEntityReferencesById(EntityUtil.strToIds(children), Entity.TEAM);
}
return findTo(teamId, TEAM, Relationship.PARENT_OF, TEAM);
}
private Integer getChildrenCount(Team team) {
return !nullOrEmpty(team.getChildren()) ? team.getChildren().size() : getChildren(team).size();
}
private List getPolicies(Team team) {
return findTo(team.getId(), TEAM, Relationship.HAS, POLICY);
}
private void populateChildren(Team team) {
List childrenRefs = team.getChildren();
if (childrenRefs == null) {
return;
}
List children = getTeams(childrenRefs);
switch (team.getTeamType()) {
case GROUP:
if (!children.isEmpty()) {
throw new IllegalArgumentException(CREATE_GROUP);
}
break;
case DEPARTMENT:
validateChildren(team, children, DEPARTMENT, GROUP);
break;
case DIVISION:
validateChildren(team, children, DEPARTMENT, DIVISION, GROUP);
break;
case BUSINESS_UNIT, ORGANIZATION:
validateChildren(team, children, BUSINESS_UNIT, DIVISION, DEPARTMENT, GROUP);
break;
}
populateTeamRefs(childrenRefs, children);
}
private void populateParents(Team team) {
// Teams created without parents has the top Organization as the default parent
List parentRefs = listOrEmpty(team.getParents());
// When there are no parents for a team, add organization as the default parent
if (parentRefs.isEmpty() && !team.getName().equals(ORGANIZATION_NAME)) {
team.setParents(new ArrayList<>());
team.getParents().add(organization.getEntityReference());
return;
}
List parents = getTeams(parentRefs);
switch (team.getTeamType()) {
case GROUP, DEPARTMENT:
validateParents(team, parents, DEPARTMENT, DIVISION, BUSINESS_UNIT, ORGANIZATION);
break;
case DIVISION:
validateSingleParent(team, parentRefs);
validateParents(team, parents, DIVISION, BUSINESS_UNIT, ORGANIZATION);
break;
case BUSINESS_UNIT:
validateSingleParent(team, parentRefs);
validateParents(team, parents, BUSINESS_UNIT, ORGANIZATION);
break;
case ORGANIZATION:
if (!parentRefs.isEmpty()) {
throw new IllegalArgumentException(UNEXPECTED_PARENT);
}
}
populateTeamRefs(parentRefs, parents);
}
// Populate team refs from team entity list
private void populateTeamRefs(List teamRefs, List teams) {
for (int i = 0; i < teams.size(); i++) {
EntityUtil.copy(teams.get(i).getEntityReference(), teamRefs.get(i));
}
}
private List getTeams(List teamRefs) {
List teams = new ArrayList<>();
for (EntityReference teamRef : teamRefs) {
try {
Team team = find(teamRef.getId(), NON_DELETED);
teams.add(team);
} catch (EntityNotFoundException ex) {
// Team was soft-deleted
LOG.debug("Failed to populate team since it might be soft deleted.", ex);
// Ensure that the team was soft-deleted otherwise throw an exception
find(teamRef.getId(), Include.DELETED);
}
}
return teams;
}
// Validate if the team can have given type of parents
private void validateParents(Team team, List relatedTeams, TeamType... allowedTeamTypes) {
List allowed = Arrays.asList(allowedTeamTypes);
for (Team relatedTeam : relatedTeams) {
if (!allowed.contains(relatedTeam.getTeamType())) {
throw new IllegalArgumentException(
invalidParent(relatedTeam, team.getName(), team.getTeamType()));
}
}
}
// Validate if the team can given type of children
private void validateChildren(Team team, List children, TeamType... allowedTeamTypes) {
List allowed = Arrays.asList(allowedTeamTypes);
for (Team child : children) {
if (!allowed.contains(child.getTeamType())) {
throw new IllegalArgumentException(invalidChild(team.getName(), team.getTeamType(), child));
}
}
}
private void validateSingleParent(Team team, List parentRefs) {
if (listOrEmpty(parentRefs).size() != 1) {
throw new IllegalArgumentException(invalidParentCount(1, team.getTeamType()));
}
}
public void initOrganization() {
organization = findByNameOrNull(ORGANIZATION_NAME, ALL);
if (organization == null) {
LOG.debug("Organization {} is not initialized", ORGANIZATION_NAME);
// Teams
try {
EntityReference organizationPolicy =
Entity.getEntityReferenceByName(POLICY, "OrganizationPolicy", Include.ALL);
EntityReference dataConsumerRole =
Entity.getEntityReferenceByName(ROLE, "DataConsumer", Include.ALL);
Team team =
new Team()
.withId(UUID.randomUUID())
.withName(ORGANIZATION_NAME)
.withDisplayName(ORGANIZATION_NAME)
.withDescription("Organization under which all the other team hierarchy is created")
.withTeamType(ORGANIZATION)
.withUpdatedBy(ADMIN_USER_NAME)
.withUpdatedAt(System.currentTimeMillis())
.withPolicies(new ArrayList<>(List.of(organizationPolicy)))
.withDefaultRoles(new ArrayList<>(List.of(dataConsumerRole)));
organization = create(null, team);
LOG.info(
"Organization {}:{} is successfully initialized",
ORGANIZATION_NAME,
organization.getId());
} catch (Exception e) {
LOG.error("Failed to initialize organization", e);
throw e;
}
} else {
LOG.info("Organization is already initialized");
}
}
public static class TeamCsv extends EntityCsv {
public static final CsvDocumentation DOCUMENTATION = getCsvDocumentation(TEAM);
public static final List HEADERS = DOCUMENTATION.getHeaders();
private final Team team;
TeamCsv(Team team, String updatedBy) {
super(Entity.TEAM, HEADERS, updatedBy);
this.team = team;
}
@Override
protected void createEntity(CSVPrinter printer, List csvRecords) throws IOException {
CSVRecord csvRecord = getNextRecord(printer, csvRecords);
// Field 1, 2, 3, 4, 7 - name, displayName, description, teamType, isJoinable
Team team =
new Team()
.withName(csvRecord.get(0))
.withDisplayName(csvRecord.get(1))
.withDescription(csvRecord.get(2))
.withTeamType(TeamType.fromValue(csvRecord.get(3)))
.withOwners(getOwners(printer, csvRecord, 5))
.withIsJoinable(getBoolean(printer, csvRecord, 6))
.withDefaultRoles(getEntityReferences(printer, csvRecord, 7, ROLE))
.withPolicies(getEntityReferences(printer, csvRecord, 8, POLICY));
// Field 5 - parent teams
getParents(printer, csvRecord, team);
if (processRecord) {
createEntity(printer, csvRecord, team);
}
}
@Override
protected void addRecord(CsvFile csvFile, Team entity) {
List recordList = new ArrayList<>();
addField(recordList, entity.getName());
addField(recordList, entity.getDisplayName());
addField(recordList, entity.getDescription());
addField(recordList, entity.getTeamType().value());
addEntityReferences(recordList, entity.getParents());
addOwners(recordList, entity.getOwners());
addField(recordList, entity.getIsJoinable());
addEntityReferences(recordList, entity.getDefaultRoles());
addEntityReferences(recordList, entity.getPolicies());
addRecord(csvFile, recordList);
}
private void getParents(CSVPrinter printer, CSVRecord csvRecord, Team importedTeam)
throws IOException {
if (!processRecord) {
return;
}
List parentRefs = getEntityReferences(printer, csvRecord, 4, Entity.TEAM);
// Validate team being created is under the hierarchy of the team for which CSV is being
// imported to
for (EntityReference parentRef : listOrEmpty(parentRefs)) {
if (parentRef.getName().equals(team.getName())) {
continue; // Parent is same as the team to which CSV is being imported, then it is in the
// same hierarchy
}
if (dryRunCreatedEntities.get(parentRef.getName()) != null) {
continue; // Parent is being created by CSV import
}
// Else the parent should already exist
if (!SubjectContext.isInTeam(team.getName(), parentRef)) {
importFailure(
printer,
invalidTeam(4, team.getName(), importedTeam.getName(), parentRef.getName()),
csvRecord);
processRecord = false;
}
}
importedTeam.setParents(parentRefs);
}
public static String invalidTeam(
int field, String team, String importedTeam, String parentName) {
String error =
String.format(
"Parent %s of imported team %s is not under %s team hierarchy",
parentName, importedTeam, team);
return String.format(
"#%s: Field %d error - %s", CsvErrorType.INVALID_FIELD, field + 1, error);
}
private List listTeams(
TeamRepository repository, String parentTeam, List teams, Fields fields) {
// Export the entire hierarchy of teams
final ListFilter filter =
new ListFilter(Include.NON_DELETED).addQueryParam("parentTeam", parentTeam);
List list = repository.listAll(fields, filter);
if (nullOrEmpty(list)) {
return teams;
}
teams.addAll(list);
for (Team teamEntry : list) {
listTeams(repository, teamEntry.getFullyQualifiedName(), teams, fields);
}
return teams;
}
public String exportCsv() throws IOException {
TeamRepository repository = (TeamRepository) Entity.getEntityRepository(TEAM);
final Fields fields = repository.getFields("owners,defaultRoles,parents,policies");
return exportCsv(
listTeams(repository, team.getFullyQualifiedName(), new ArrayList<>(), fields));
}
}
/** Handles entity updated from PUT and POST operation. */
public class TeamUpdater extends EntityUpdater {
public TeamUpdater(Team original, Team updated, Operation operation) {
super(original, updated, operation);
}
@Transaction
@Override
public void entitySpecificUpdate() {
if (original.getTeamType() != updated.getTeamType()) {
// A team of type 'Group' cannot be updated
if (GROUP.equals(original.getTeamType())) {
throw new IllegalArgumentException(INVALID_GROUP_TEAM_UPDATE);
}
// A team containing children cannot be updated to Group
if (!original.getChildren().isEmpty() && GROUP.equals(updated.getTeamType())) {
throw new IllegalArgumentException(INVALID_GROUP_TEAM_CHILDREN_UPDATE);
}
}
recordChange("profile", original.getProfile(), updated.getProfile(), true);
recordChange("isJoinable", original.getIsJoinable(), updated.getIsJoinable());
recordChange("teamType", original.getTeamType(), updated.getTeamType());
// If the team is empty then email should be null, not be empty
if (CommonUtil.nullOrEmpty(updated.getEmail())) {
updated.setEmail(null);
}
recordChange("email", original.getEmail(), updated.getEmail());
updateUsers(original, updated);
updateDefaultRoles(original, updated);
updateParents(original, updated);
updateChildren(original, updated);
updatePolicies(original, updated);
}
private void updateUsers(Team origTeam, Team updatedTeam) {
List origUsers = listOrEmpty(origTeam.getUsers());
List updatedUsers = listOrEmpty(updatedTeam.getUsers());
updateToRelationships(
"users",
TEAM,
origTeam.getId(),
Relationship.HAS,
Entity.USER,
origUsers,
updatedUsers,
false);
}
private void updateDefaultRoles(Team origTeam, Team updatedTeam) {
List origDefaultRoles = listOrEmpty(origTeam.getDefaultRoles());
List updatedDefaultRoles = listOrEmpty(updatedTeam.getDefaultRoles());
updateToRelationships(
DEFAULT_ROLES,
TEAM,
origTeam.getId(),
Relationship.HAS,
Entity.ROLE,
origDefaultRoles,
updatedDefaultRoles,
false);
}
@Override
protected void updateDomain() {
if (operation.isPut() && !nullOrEmpty(original.getDomains()) && updatedByBot()) {
// Revert change to non-empty domain if it is being updated by a bot
// This is to prevent bots from overwriting the domain. Domain need to be
// updated with a PATCH request
updated.setDomains(original.getDomains());
return;
}
List origDomains =
EntityUtil.populateEntityReferences(listOrEmptyMutable(original.getDomains()));
List updatedDomains =
EntityUtil.populateEntityReferences(listOrEmptyMutable(updated.getDomains()));
// Remove Domains for the user
deleteTo(original.getId(), TEAM, Relationship.HAS, Entity.DOMAIN);
for (EntityReference domain : updatedDomains) {
addRelationship(domain.getId(), original.getId(), Entity.DOMAIN, TEAM, Relationship.HAS);
}
origDomains.sort(EntityUtil.compareEntityReference);
updatedDomains.sort(EntityUtil.compareEntityReference);
List added = new ArrayList<>();
List deleted = new ArrayList<>();
recordListChange(
FIELD_DOMAINS,
origDomains,
updatedDomains,
added,
deleted,
EntityUtil.entityReferenceMatch);
}
private void updateParents(Team original, Team updated) {
List origParents = listOrEmpty(original.getParents());
List updatedParents = listOrEmpty(updated.getParents());
updateFromRelationships(
PARENTS_FIELD,
TEAM,
origParents,
updatedParents,
Relationship.PARENT_OF,
TEAM,
original.getId());
}
private void updateChildren(Team original, Team updated) {
List origParents = listOrEmpty(original.getChildren());
List updatedParents = listOrEmpty(updated.getChildren());
updateToRelationships(
"children",
TEAM,
original.getId(),
Relationship.PARENT_OF,
TEAM,
origParents,
updatedParents,
false);
}
private void updatePolicies(Team original, Team updated) {
List origPolicies = listOrEmpty(original.getPolicies());
List updatedPolicies = listOrEmpty(updated.getPolicies());
updateToRelationships(
"policies",
TEAM,
original.getId(),
Relationship.HAS,
POLICY,
origPolicies,
updatedPolicies,
false);
}
}
}