package net.techreadiness.service;
import java.util.AbstractMap.SimpleEntry;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.inject.Inject;
import net.techreadiness.annotation.CoreDataModificationStatus;
import net.techreadiness.annotation.CoreDataModificationStatus.ModificationType;
import net.techreadiness.persistence.AbstractAuditedBaseEntityWithExt;
import net.techreadiness.persistence.dao.EntityDAO.EntityTypeCode;
import net.techreadiness.persistence.dao.ExtDAO;
import net.techreadiness.persistence.dao.OrgDAO;
import net.techreadiness.persistence.dao.OrgPartDAO;
import net.techreadiness.persistence.dao.OrgTypeDAO;
import net.techreadiness.persistence.dao.ScopeDAO;
import net.techreadiness.persistence.domain.OrgDO;
import net.techreadiness.persistence.domain.OrgPartDO;
import net.techreadiness.persistence.domain.OrgTypeDO;
import net.techreadiness.persistence.domain.ScopeDO;
import net.techreadiness.service.common.ValidationError;
import net.techreadiness.service.exception.AuthorizationException;
import net.techreadiness.service.exception.FaultInfo;
import net.techreadiness.service.exception.InvalidServiceContextException;
import net.techreadiness.service.exception.ValidationServiceException;
import net.techreadiness.service.object.Org;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.google.common.collect.Lists;
@Service
@Transactional
public class OrganizationServiceImpl extends BaseServiceWithValidationAndExt<OrgDO, AbstractAuditedBaseEntityWithExt<OrgDO>>
implements OrganizationService {
@Inject
private OrgDAO orgDao;
@Inject
@Qualifier("orgExtDAOImpl")
private ExtDAO<OrgDO, AbstractAuditedBaseEntityWithExt<OrgDO>> orgExtDao;
@Inject
private OrgPartDAO orgPartDao;
@Inject
private OrgTypeDAO orgTypeDao;
@Inject
private ScopeDAO scopeDao;
@Inject
private ConfigService configService;
@Inject
private OrgPartService orgPartService;
@Inject
private UserService userService;
@Override
public List<Org> findOrgsParticipatingInScope(ServiceContext context) {
return getMappingService().mapFromDOList(orgPartDao.findOrgsPartForScope(context.getScopeId(), context.getOrgId()));
}
@Override
public Org getById(ServiceContext context, Long orgId) {
OrgDO orgDO = orgDao.getById(orgId);
if (orgDO == null) {
return null;
}
Map<String, String> extAttributes = orgDO.getExtAttributes();
if (extAttributes == null) {
orgDO.setExtAttributes(orgDO.getAsMap());
} else {
orgDO.getExtAttributes().putAll(orgDO.getAsMap());
}
return getMappingService().map(orgDO);
}
@Override
public Org getByCode(ServiceContext context, String orgCode) {
OrgDO orgDO = orgDao.getOrg(orgCode, context.getScopeId());
if (orgDO == null) {
return null;
}
Map<String, String> extAttributes = orgDO.getExtAttributes();
if (extAttributes == null) {
orgDO.setExtAttributes(orgDO.getAsMap());
} else {
orgDO.getExtAttributes().putAll(orgDO.getAsMap());
}
return getMappingService().map(orgDO);
}
@Override
public List<Org> findOrgsByScope(ServiceContext context, int maxResults) {
ScopeDO scopeDO = scopeDao.getScopeForOrgs(context.getScopeId());
List<OrgDO> orgs = orgDao.findByScopeId(scopeDO.getScopeId(), context.getOrgId(), maxResults);
return getMappingService().mapFromDOList(orgs);
}
@Override
public Org getMatch(ServiceContext context, Org org) {
OrgDO orgDO = null;
ScopeDO scopeDO = scopeDao.getScopeForOrgs(context.getScopeId());
if (scopeDO == null) {
throw new InvalidServiceContextException("The scope is invalid for org creation.");
}
if (org.getOrgId() != null) {
orgDO = orgDao.getById(org.getOrgId());
}
if (orgDO == null && StringUtils.isNotBlank(org.getCode())) {
orgDO = orgDao.getOrg(org.getCode(), scopeDO.getScopeId());
}
return getMappingService().map(orgDO);
}
@Override
@CoreDataModificationStatus(modificationType = ModificationType.UPDATE, entityClass = OrgDO.class)
public Org addOrUpdate(ServiceContext context, Org org) {
ScopeDO scopeDO = scopeDao.getScopeForOrgs(context.getScopeId());
if (scopeDO == null) {
throw new InvalidServiceContextException("The scope is invalid for org creation.");
}
// Attempt to find the organization using the org code if the ID isn't provided.
if (org.getOrgId() == null && StringUtils.isNotBlank(org.getCode())) {
OrgDO orgDO = orgDao.getOrg(org.getCode(), scopeDO.getScopeId());
if (orgDO != null) {
org.setOrgId(orgDO.getOrgId());
}
}
// Attempt to find the organization using the org code if the ID isn't provided.
if (org.getParentOrgId() == null && StringUtils.isNotBlank(org.getParentOrgCode())) {
OrgDO parentOrgDO = orgDao.getOrg(org.getParentOrgCode(), scopeDO.getScopeId());
if (parentOrgDO != null) {
org.setParentOrgId(parentOrgDO.getOrgId());
org.setParentOrgName(parentOrgDO.getName());
}
}
if (org.getOrgTypeId() == null && StringUtils.isNotBlank(org.getOrgTypeCode())) {
OrgTypeDO orgTypeDO = orgTypeDao.getByCode(org.getOrgTypeCode(), scopeDO.getScopeId());
if (orgTypeDO != null) {
org.setOrgTypeId(orgTypeDO.getOrgTypeId());
org.setOrgTypeName(orgTypeDO.getName());
}
}
OrgDO orgDO = null;
FaultInfo faultInfo = new FaultInfo();
if (StringUtils.isNotBlank(org.getParentOrgCode()) && org.getParentOrgId() == null) {
String message = getMessage("validation.org.parent.not.found", org.getParentOrgCode());
ValidationError error = new ValidationError("parentOrgCode", "Parent Organization", message);
faultInfo.getAttributeErrors().add(error);
}
try {
orgDO = updateOrg(context, org, scopeDO);
boolean newEntity = orgDO.getOrgId() == null;
hasAccessToOrg(context, orgDO, newEntity);
orgDao.persist(orgDO);
storeExtFields(context, orgDO, orgExtDao, EntityTypeCode.ORG, orgDO.getScope().getScopeId());
addParticipations(context, org, scopeDO, orgDO);
} catch (ValidationServiceException e) {
faultInfo.getAttributeErrors().addAll(e.getFaultInfo().getAttributeErrors());
}
if (!faultInfo.getAttributeErrors().isEmpty()) {
throw new ValidationServiceException(faultInfo);
}
return getMappingService().map(orgDO);
}
protected void hasAccessToOrg(ServiceContext context, OrgDO org, boolean checkParent) {
if (org.getOrgId() != null) {
if (!userService.hasAccessToOrg(context, context.getUserId(), org.getOrgId())) {
throw new AuthorizationException(getMessage("validation.org.access.denied", context.getUserName(),
org.getName()));
}
}
if (checkParent && org.getParentOrg() != null) {
if (!userService.hasAccessToOrg(context, context.getUserId(), org.getParentOrg().getOrgId())) {
throw new AuthorizationException(getMessage("validation.org.access.denied", context.getUserName(), org
.getParentOrg().getName()));
}
}
}
private OrgDO updateOrg(ServiceContext context, Org organization, ScopeDO scopeDO) {
FaultInfo faultInfo = new FaultInfo();
if (scopeDO == null) {
String errorMessage = messageSource.getMessage("validation.scope.required", null, Locale.getDefault());
ValidationError error = new ValidationError("scopeId", "Scope", errorMessage, "validation.scope.required",
errorMessage);
faultInfo.getAttributeErrors().add(error);
throw new ValidationServiceException(faultInfo);
} else if (!scopeDO.getScopeType().isAllowOrg()) {
String errorMessage = messageSource.getMessage("validation.scope.orgsNotAllowed", null, Locale.getDefault());
ValidationError error = new ValidationError("scopeId", "Scope", errorMessage, "validation.scope.orgsNotAllowed",
errorMessage);
faultInfo.getAttributeErrors().add(error);
throw new ValidationServiceException(faultInfo);
}
List<ValidationError> errors = performValidation(organization.getAsMap(), scopeDO.getScopeId(), EntityTypeCode.ORG);
if (errors == null) {
errors = performCrossFieldValidation(context, organization.getAsMap(), scopeDO.getScopeId(), EntityTypeCode.ORG);
} else if (errors.size() == 0) { // if the list has an entry, data may be too invalid to do cross field checking.
errors.addAll(performCrossFieldValidation(context, organization.getAsMap(), scopeDO.getScopeId(),
EntityTypeCode.ORG));
}
faultInfo.getAttributeErrors().addAll(errors);
OrgDO org;
if (organization.getOrgId() != null) {
org = orgDao.getById(organization.getOrgId());
} else {
org = new OrgDO();
}
OrgDO existingOrg = orgDao.getOrg(organization.getCode(), scopeDO.getScopeId());
if (existingOrg != null && !existingOrg.getOrgId().equals(org.getOrgId())) {
String errorMessage = messageSource.getMessage("validation.org.alreadyExists",
new Object[] { existingOrg.getName(), existingOrg.getCode() }, Locale.getDefault());
ValidationError error = new ValidationError("code", "Code", errorMessage, "validation.org.alreadyExists",
errorMessage);
faultInfo.getAttributeErrors().add(error);
}
OrgTypeDO orgType = orgTypeDao.getById(organization.getOrgTypeId());
if (orgType == null) {
String errorMessage = messageSource.getMessage("validation.org.type.required", null, Locale.getDefault());
ValidationError error = new ValidationError("orgTypeId", "Organization Type", errorMessage,
"validation.org.type.required", errorMessage);
faultInfo.getAttributeErrors().add(error);
} else if (orgType.getParentOrgType() == null) {
List<OrgDO> orgs = orgDao.findOrgsByType(organization.getOrgTypeId(), scopeDO.getScopeId());
if (!orgs.isEmpty()) {
String msg = messageSource.getMessage("validation.org.rootOrgExists", new Object[] { orgType.getName() },
Locale.getDefault());
ValidationError error = new ValidationError("orgTypeId", "Organization Type", msg);
faultInfo.getAttributeErrors().add(error);
}
} else if (!scopeDO.equals(orgType.getScope())) {
String errorMessage = messageSource.getMessage("validation.org.type.wrongScope", null, Locale.getDefault());
ValidationError error = new ValidationError("orgTypeId", "Organization Type", errorMessage,
"validation.org.type.wrongScope", errorMessage);
faultInfo.getAttributeErrors().add(error);
}
OrgDO parentOrg = null;
if (organization.getParentOrgId() != null) {
parentOrg = orgDao.getById(organization.getParentOrgId());
}
if (parentOrg != null) {
if (!parentOrg.getScope().getScopeId().equals(scopeDO.getScopeId())) {
String errorMessage = messageSource.getMessage("validation.org.parent.scope", null, Locale.getDefault());
ValidationError error = new ValidationError("scopeId", "Scope", errorMessage, "validation.org.parent.scope",
errorMessage);
faultInfo.getAttributeErrors().add(error);
}
if (orgType != null) {
List<Long> orgTypeIds = orgTypeDao.findDisallowedChildOrgTypeIdsForOrg(parentOrg.getOrgId());
if (orgTypeIds.contains(orgType.getOrgTypeId())) {
String errorMessage = messageSource.getMessage("validation.org.parent.childOrgType", null,
Locale.getDefault());
ValidationError error = new ValidationError("orgTypeId", "Organization Type", errorMessage,
"validation.org.parent.childOrgType", errorMessage);
faultInfo.getAttributeErrors().add(error);
}
if (!parentOrg.getOrgType().equals(orgType.getParentOrgType())) {
String errorMessage = messageSource.getMessage("validation.org.orgType.wrongType", new Object[] {
orgType.getName(), orgType.getParentOrgType().getName(), parentOrg.getOrgType().getName() },
Locale.getDefault());
ValidationError error = new ValidationError("orgTypeId", "Organization Type", errorMessage,
"validation.org.orgType.wrongType", errorMessage);
faultInfo.getAttributeErrors().add(error);
}
}
if (parentOrg.equals(org)) {
String errorMessage = messageSource
.getMessage("validation.org.parentSameAsChild", null, Locale.getDefault());
ValidationError error = new ValidationError("parentOrgId", "Parent Organization", errorMessage,
"validation.org.parentSameAsChild", errorMessage);
faultInfo.getAttributeErrors().add(error);
}
} else if (orgType != null && orgType.getParentOrgType() != null) {
// Require a parent org if the org type has a parent org type
String errorMessage = messageSource.getMessage("validation.org.orgType.requires.parent",
new Object[] { orgType.getName() }, Locale.getDefault());
ValidationError error = new ValidationError("orgTypeId", "Organization Type", errorMessage,
"validation.org.orgType.requires.parent", errorMessage);
faultInfo.getAttributeErrors().add(error);
}
if (!faultInfo.getAttributeErrors().isEmpty()) {
throw new ValidationServiceException(faultInfo);
}
getMappingService().getMapper().map(organization, org);
org.setExtAttributes(organization.getExtendedAttributes());
org.setScope(scopeDO);
org.setOrgType(orgType);
org.setParentOrg(parentOrg);
return org;
}
@Override
@CoreDataModificationStatus(modificationType = ModificationType.DELETE, entityClass = OrgDO.class)
public void delete(ServiceContext context, Long orgId) {
OrgDO orgDO = orgDao.getById(orgId);
hasAccessToOrg(context, orgDO, true);
if (orgDao.hasUsers(orgId)) {
throw new ValidationServiceException(getMessage("validation.org.delete.hasUser", orgDO.getName()));
}
try {
if (orgDO.getOrgs().get(0).getOrgId() != null) {
throw new ValidationServiceException(getMessage("validation.org.delete.hasChildren", orgDO.getName()));
}
} catch (IndexOutOfBoundsException ibe) {
// this is to be expected at times.
// we need to look at the first child org above (if it exists) for the lazy loading.
// if there simply is no child org, we'll be getting this index out of bounds.
}
orgPartService.deleteOrgPartsByOrg(context, orgId);
orgDao.delete(orgDO);
}
@Override
@CoreDataModificationStatus(modificationType = ModificationType.UPDATE, entityClass = OrgDO.class)
public Org create(ServiceContext context, Org org) {
ScopeDO scopeDO = scopeDao.getScopeForOrgs(context.getScopeId());
if (scopeDO == null) {
throw new InvalidServiceContextException(messageSource.getMessage("validation.scope.orgsNotAllowed", null,
Locale.getDefault()));
}
OrgDO orgDO = updateOrg(context, org, scopeDO);
hasAccessToOrg(context, orgDO, true);
orgDao.persist(orgDO);
storeExtFields(context, orgDO, orgExtDao, EntityTypeCode.ORG, orgDO.getScope().getScopeId());
addParticipations(context, org, scopeDO, orgDO);
return getMappingService().map(orgDO);
}
private void addParticipations(ServiceContext context, Org org, ScopeDO scopeDO, OrgDO orgDO) {
if (orgDO.getParentOrg() != null) {
// is the parent org participating?
List<OrgPartDO> orgParts = orgPartDao.findOrgPartsForOrg(org.getParentOrgId());
if (orgParts != null) {
for (OrgPartDO orgPartDO : orgParts) {
if (configService.isBooleanActive(context, orgPartDO.getScope().getScopeId(),
ConfigService.ORG_PART_DESCENDANT_CASCADE_ADD)) {
orgPartService.createIfNotExistsAlternateScope(context, orgPartDO.getScope().getScopeId(),
orgDO.getOrgId(), new HashMap<String, String>());
}
}
}
}
}
@Override
public List<Org> findOrgsThatCanHaveChildren(ServiceContext context, int maxResults) {
List<OrgDO> orgs = orgDao.findOrgsThatCanHaveChildren(context.getScopeId(), context.getOrgId(), maxResults);
return getMappingService().mapFromDOList(orgs);
}
@Override
public List<SimpleEntry<Long, String>> findChildOrgTypesByParentOrgType(ServiceContext context, Long parentOrgTypeId) {
List<OrgTypeDO> orgTypeDOs = orgTypeDao.findChildOrgTypes(parentOrgTypeId, context.getScopeId());
List<SimpleEntry<Long, String>> list = Lists.newArrayList();
if (orgTypeDOs != null) {
for (OrgTypeDO o : orgTypeDOs) {
list.add(new SimpleEntry<>(o.getOrgTypeId(), o.getName()));
}
}
return list;
}
@Override
public List<Org> findOrgsBySearchTerm(ServiceContext context, String term, int maxResults) {
ScopeDO scope = scopeDao.getScopeForOrgs(context.getScopeId());
if (scope == null) {
return Collections.emptyList();
}
ScopeDO scopeDO = scopeDao.getScopeForOrgs(context.getScopeId());
return getMappingService().mapFromDOList(
orgDao.findOrgsBySearchTerm(scopeDO.getScopeId(), context.getOrgId(), term, maxResults));
}
@Override
public List<Org> findOrgsParticipatingInScope(ServiceContext context, String term) {
return getMappingService().mapFromDOList(
orgPartDao.findOrgPartsForScope(context.getScopeId(), context.getOrgId(), term));
}
@Override
public List<Org> findByIds(ServiceContext context, Collection<Long> orgIds) {
return getMappingService().mapFromDOList(orgDao.findById(orgIds));
}
@Override
public List<Org> findOrgsParticipatingInScopeThatAllowEnrollments(ServiceContext context) {
return getMappingService().mapFromDOList(
orgDao.findOrgsParticipatingInScopeThatAllowEnrollments(context.getScopeId(), context.getOrgId()));
}
@Override
public List<SimpleEntry<Long, String>> findOrgTypes(ServiceContext context) {
List<SimpleEntry<Long, String>> list = Lists.newArrayList();
ScopeDO scopeDO = scopeDao.getScopeForOrgs(context.getScopeId());
List<OrgTypeDO> orgTypes = orgTypeDao.findOrgTypesForScope(scopeDO.getScopeId());
if (orgTypes != null) {
for (OrgTypeDO orgTypeDO : orgTypes) {
list.add(new SimpleEntry<>(orgTypeDO.getOrgTypeId(), orgTypeDO.getName()));
}
}
return list;
}
@Override
public List<SimpleEntry<Long, String>> findOrgTypesByIds(Collection<Long> orgTypeIds) {
List<SimpleEntry<Long, String>> list = Lists.newArrayList();
List<OrgTypeDO> orgTypes = orgTypeDao.findOrgTypesByIds(orgTypeIds);
if (orgTypes != null) {
for (OrgTypeDO orgTypeDO : orgTypes) {
list.add(new SimpleEntry<>(orgTypeDO.getOrgTypeId(), orgTypeDO.getName()));
}
}
return list;
}
@Override
public List<Org> findOrgsParticipatingInScopeThatAllowGroups(ServiceContext context) {
return getMappingService().mapFromDOList(
orgDao.findOrgsParticipatingInScopeThatAllowGroups(context.getScopeId(), context.getOrgId()));
}
@Override
public List<Org> findOrgsParticipatingInScopeThatAllowGroups(ServiceContext context, String term) {
return getMappingService().mapFromDOList(
orgDao.findOrgsParticipatingInScopeThatAllowGroups(context.getScopeId(), context.getOrgId(), term));
}
@Override
public List<Org> findOrgsParticipatingInScopeThatAllowEnrollments(ServiceContext context, String term) {
return getMappingService().mapFromDOList(
orgDao.findOrgsParticipatingInScopeThatAllowEnrollments(context.getScopeId(), context.getOrgId(), term));
}
@Override
public Org getOrgForUser(ServiceContext context) {
ScopeDO scopeDO = scopeDao.getScopeForOrgs(context.getScopeId());
if (scopeDO == null || context.getUser() == null) {
return null;
}
return getMappingService().map(orgDao.getHighestOrgForUser(scopeDO.getScopeId(), context.getUserId()));
}
@Override
public List<Org> findOrgsForUser(ServiceContext context) {
ScopeDO scopeDO = scopeDao.getScopeForOrgs(context.getScopeId());
if (scopeDO == null || context.getUser() == null) {
return null;
}
return getMappingService().mapFromDOList(orgDao.findOrgsForUser(scopeDO.getScopeId(), context.getUserId()));
}
@Override
public List<Org> findChildOrgsThatAllowDevices(ServiceContext context, String term, int limit) {
return getMappingService().mapFromDOList(orgDao.findChildOrgsThatAllowDevices(context.getOrgId(), term, limit));
}
@Override
public List<Org> findOrgsThatCanHaveChildrenBySearchTerm(ServiceContext context, String term, int maxResults) {
List<OrgDO> orgs = orgDao.findOrgsThatCanHaveChildrenBySearchTerm(context.getScopeId(), context.getOrgId(), term,
maxResults);
return getMappingService().mapFromDOList(orgs);
}
@Override
public List<Org> findOrgsThatCanHaveChildrenBySearchTermByType(ServiceContext context, String term, Long orgId) {
Org org = getById(context, orgId);
Org parentOrg = getById(context, org.getParentOrgId());
if (null == parentOrg) {
return Collections.emptyList();
}
List<OrgDO> orgs = orgDao.findOrgsThatCanHaveChildrenBySearchTermByType(context.getScopeId(), context.getOrgId(),
term, parentOrg.getOrgTypeId());
return getMappingService().mapFromDOList(orgs);
}
@Override
public List<Org> findOrgsThatCanHaveChildrenByType(ServiceContext context, Long orgId) {
List<OrgDO> orgs = orgDao.findOrgsThatCanHaveChildrenByType(context.getScopeId(), context.getOrgId(), orgId);
return getMappingService().mapFromDOList(orgs);
}
@Override
public List<Org> findDescendantOrgsParticipatingInScope(ServiceContext context, Long orgId, boolean allDescendants) {
return getMappingService().mapFromDOList(
orgDao.findDescendantOrgsParticipatingInScope(context.getScopeId(), orgId, allDescendants));
}
@Override
public Org getParentOrgOfType(ServiceContext context, Long orgId, String typeCode) {
OrgDO org = orgDao.getOrgOfType(orgId, typeCode);
return getMappingService().map(org);
}
@Override
public Collection<Org> findDescendantOrgs(ServiceContext context, Long orgId) {
return getMappingService().getMapper().mapAsSet(orgDao.findDescendantOrgs(orgId), Org.class);
}
}