ItEr46S13CUImportacionRecursosProductivosItEr45S11: Added support for updating when importing criterion types.

Support for updating criterion types when importing criterion types has been added.

To update a criterion type, it is only necessary to send the modifications, and not all the state. In consequence, it is only necessary to send modified propert
ies in the criterions and the criterion type itself, and only new or modified cr
iterions need to be sent. Furthermore, any legal change in the criterion hierarchy of the criterion type is allowed.

Finally, a test case has been added to test the updating funcionality.
This commit is contained in:
Fernando Bellas Permuy 2010-02-03 15:49:17 +01:00 committed by Javier Moran Rua
parent f0bfc9e385
commit ed2bc04448
23 changed files with 1032 additions and 190 deletions

View file

@ -33,12 +33,28 @@ import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
public interface IIntegrationEntityDAO<E extends IntegrationEntity>
extends IGenericDAO<E, Long> {
/**
* If <code>code</code> is blank (whitespace, empty ("") or
* <code>null</code>), it returns <code>false</code>.
*/
public boolean existsByCode(String code);
/**
* If <code>code</code> is blank (whitespace, empty ("") or
* <code>null</code>), it returns <code>false</code>.
*/
public boolean existsByCodeAnotherTransaction(String code);
/**
* If <code>code</code> is blank (whitespace, empty ("") or
* <code>null</code>), it throws <code>InstanceNotFoundException</code>.
*/
public E findByCode(String code) throws InstanceNotFoundException;
/**
* If <code>code</code> is blank (whitespace, empty ("") or
* <code>null</code>), it throws <code>InstanceNotFoundException</code>.
*/
public E findByCodeAnotherTransaction(String code)
throws InstanceNotFoundException;

View file

@ -20,6 +20,7 @@
package org.navalplanner.business.common.daos;
import org.apache.commons.lang.StringUtils;
import org.hibernate.criterion.Restrictions;
import org.navalplanner.business.common.IntegrationEntity;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
@ -57,6 +58,11 @@ public class IntegrationEntityDAO<E extends IntegrationEntity>
@Override
public E findByCode(String code) throws InstanceNotFoundException {
if (StringUtils.isBlank(code)) {
throw new InstanceNotFoundException(null,
getEntityClass().getName());
}
E entity = (E) getSession().createCriteria(getEntityClass()).add(
Restrictions.eq("code", code).ignoreCase()).uniqueResult();

View file

@ -28,6 +28,7 @@ import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import org.navalplanner.business.common.daos.IntegrationEntityDAO;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.CriterionType;
import org.navalplanner.business.resources.entities.ResourceEnum;
import org.springframework.stereotype.Component;
@ -44,6 +45,22 @@ import org.springframework.transaction.annotation.Transactional;
public class CriterionTypeDAO extends IntegrationEntityDAO<CriterionType>
implements ICriterionTypeDAO {
@Override
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
public CriterionType findByCodeAnotherTransactionInitialized(
String code) throws InstanceNotFoundException {
CriterionType criterionType = findByCode(code);
for (Criterion c : criterionType.getCriterions()) {
c.getChildren().size();
c.getType().getName();
}
return criterionType;
}
@Override
public List<CriterionType> findByName(CriterionType criterionType) {
Criteria c = getSession().createCriteria(CriterionType.class);

View file

@ -36,6 +36,9 @@ import org.navalplanner.business.resources.entities.ResourceEnum;
public interface ICriterionTypeDAO
extends IIntegrationEntityDAO<CriterionType> {
CriterionType findByCodeAnotherTransactionInitialized(String code)
throws InstanceNotFoundException;
CriterionType findUniqueByName(String name)
throws InstanceNotFoundException;

View file

@ -45,14 +45,17 @@ import org.navalplanner.business.resources.daos.ICriterionDAO;
public class Criterion extends IntegrationEntity implements ICriterion {
public static Criterion createUnvalidated(String code, String name,
CriterionType type, Criterion parent, boolean active) {
CriterionType type, Criterion parent, Boolean active) {
Criterion criterion = create(new Criterion(), code);
criterion.name = name;
criterion.type = type;
criterion.parent = parent;
criterion.active = active;
if (active != null) {
criterion.active = active;
}
return criterion;
@ -169,6 +172,29 @@ public class Criterion extends IntegrationEntity implements ICriterion {
this.children = children;
}
public void moveTo(Criterion newParent) {
if (parent == null) {
if (newParent != null) {
parent = newParent;
parent.getChildren().add(this);
}
} else { // parent != null
if (!parent.equals(newParent)) {
parent.getChildren().remove(this);
parent = newParent;
if (parent != null) {
parent.getChildren().add(this);
}
}
}
}
public boolean isEquivalent(ICriterion criterion) {
if (criterion instanceof Criterion) {
Criterion other = (Criterion) criterion;

View file

@ -58,11 +58,23 @@ public class CriterionType extends IntegrationEntity implements
criterionType.name = name;
criterionType.description = description;
criterionType.allowHierarchy = allowHierarchy;
criterionType.allowSimultaneousCriterionsPerResource =
allowSimultaneousCriterionsPerResource;
criterionType.enabled = enabled;
criterionType.resource = resource;
if (allowHierarchy != null) {
criterionType.allowHierarchy = allowHierarchy;
}
if (allowSimultaneousCriterionsPerResource != null) {
criterionType.allowSimultaneousCriterionsPerResource =
allowSimultaneousCriterionsPerResource;
}
if (enabled != null) {
criterionType.enabled = enabled;
}
if (resource != null) {
criterionType.resource = resource;
}
return criterionType;
@ -289,11 +301,51 @@ public class CriterionType extends IntegrationEntity implements
}
}
throw new InstanceNotFoundException(name + "::" + criterionName,
throw new InstanceNotFoundException(criterionName,
Criterion.class.getName());
}
public Criterion getCriterionByCode(String code)
throws InstanceNotFoundException {
if (StringUtils.isBlank(code)) {
throw new InstanceNotFoundException(code,
Criterion.class.getName());
}
for (Criterion c : criterions) {
if (c.getCode().equalsIgnoreCase(StringUtils.trim(code))) {
return c;
}
}
throw new InstanceNotFoundException(code,
Criterion.class.getName());
}
public Criterion getExistingCriterionByCode(String code) {
try {
return getCriterionByCode(code);
} catch (InstanceNotFoundException e) {
throw new RuntimeException(e);
}
}
public boolean existsCriterionByCode(String code) {
try {
getCriterionByCode(code);
return true;
} catch (InstanceNotFoundException e) {
return false;
}
}
@AssertTrue(message="criterion names must be unique inside a criterion " +
"type")
public boolean checkConstraintNonRepeatedCriterionNames() {

View file

@ -0,0 +1,47 @@
/*
* This file is part of ###PROJECT_NAME###
*
* Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.navalplanner.ws.common.api;
/**
* An exception for notifying that an instance is already being imported in
* a bulk import because it has a code used by another instance of the same
* type.
*
* @author Fernando Bellas Permuy <fbellas@udc.es>
*/
@SuppressWarnings("serial")
public class DuplicateCodeBeingImportedException
extends DuplicateInstanceBeingImportedException {
private String code;
public DuplicateCodeBeingImportedException(String instanceType,
String code) {
super(instanceType);
this.code = code;
}
public String getCode() {
return code;
}
}

View file

@ -0,0 +1,42 @@
/*
* This file is part of ###PROJECT_NAME###
*
* Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.navalplanner.ws.common.api;
/**
* An exception for notifying that an instance is already being imported in
* a bulk import.
*
* @author Fernando Bellas Permuy <fbellas@udc.es>
*/
@SuppressWarnings("serial")
public class DuplicateInstanceBeingImportedException extends Exception {
private String entityType;
public DuplicateInstanceBeingImportedException(String entityType) {
this.entityType = entityType;
}
public String getEntityType() {
return entityType;
}
}

View file

@ -0,0 +1,48 @@
/*
* This file is part of ###PROJECT_NAME###
*
* Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.navalplanner.ws.common.api;
/**
* An exception for notifying that an instance is already being imported in
* a bulk import because it has a natural key used by another instance of the
* same type.
*
* @author Fernando Bellas Permuy <fbellas@udc.es>
*/
@SuppressWarnings("serial")
public class DuplicateNaturalKeyBeingImportedException
extends DuplicateInstanceBeingImportedException {
private String[] naturalKeyValues;
public DuplicateNaturalKeyBeingImportedException(String instanceType,
String[] naturalKeyValues) {
super(instanceType);
this.naturalKeyValues = naturalKeyValues;
}
public String[] getNaturalKeyValues() {
return naturalKeyValues;
}
}

View file

@ -17,15 +17,31 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.navalplanner.ws.common.api;
import java.util.Set;
import java.util.UUID;
import javax.xml.bind.annotation.XmlAttribute;
import org.apache.commons.lang.StringUtils;
/**
* DTO for <code>IntegrationEntity</code>. All DTOs corresponding to entities
* to be used in application integration must extend from this DTO.
* <br/>
* <br/>
* All entities must redefine <code>getEntityType()</code>. Additionally,
* entities that can be bulk imported may need to redefine the following
* methods (ordered by probability of being redefined):
* <ul>
* <li><code>getNaturalKeyValues()</code>.</li>
* <li><code>checkDuplicateCode()</code>.</li>
* <li><code>getUniversalCodePrefix()</code>.</li>
* <li><code>getUniversalNaturalKeyPrefix()</code>.</li>
* <li><code>checkDuplicateNaturalKey()</code>.</li>
* </ul>
*
* @author Fernando Bellas Permuy <fbellas@udc.es>
*/
@ -42,13 +58,99 @@ public abstract class IntegrationEntityDTO {
this.code = code;
}
/**
* It returns the String to use in
* <code>InstanceConstraintViolationsDTOId.entityType</code>.
*/
public abstract String getEntityType();
/**
* It returns the prefix specified in <code>getUniversalCode()</code>. The
* default implementation returns the same value as
* <code>getEntityType()</code>. Entities that can be bulk imported, and
* that have derived entities, must redefine this method to return the
* same String for all child entities if they share the code space.
*/
protected String getUniversalCodePrefix() {
return getEntityType();
}
/**
* It returns the prefix specified in <code>getUniversalNaturalKey()</code>.
* The default implementation returns the same value as
* <code>getEntityType()</code>. Entities that can be bulk imported, and
* that have derived entities, must redefine this method to return the
* same String for all child entities if they share the natural key space.
*/
protected String getUniversalNaturalKeyPrefix() {
return getEntityType();
}
/**
* It returns the values (as String) of the fields that represent the
* natural key. If the entity has no natural key fields, it must return
* <code>null</code> or an empty array. The default implementation
* returns <code>null</code>. Entities, with natural key, that can be bulk
* imported must redefine this method.
*/
protected String[] getNaturalKeyValues() {
return null;
}
/**
* It checks if this entity or one its contained entities has a code
* (the entity code is obtained by calling on
* <code>getUniversalCode()</code>) contained in the set of keys passed as
* a parameter. If the code is not contained, it is added to the set of
* keys. No other class should manipulate this set. Comparison is case
* insensitive and leading and trailing whitespace is discarded. If the
* code is whitespace, empty ("") or <code>null</code>, the check is
* considered OK (and the code is not added to the set of existing keys).
* <br/>
* <br/>
* The default implementation only provides this semantics for the
* container (this) entity. Entities, with contained entities, that can be
* bulk imported must redefine this method to apply it recursively to their
* contained entities.
*/
public void checkDuplicateCode(Set<String> existingKeys)
throws DuplicateCodeBeingImportedException {
if (containsKeyAndAdd(existingKeys, getUniversalCode())) {
throw new DuplicateCodeBeingImportedException(getEntityType(),
code);
}
}
/**
* It checks if this entity or one its contained entities has a natural key
* (the entity natural key is obtained by calling on
* <code>getUniversalNaturalKey()</code>) contained in the set of keys
* passed as a parameter. If the natural key is not contained, it is added
* to the set of keys. No other class should manipulate this set.
* Comparison is case insensitive and leading and trailing whitespace is
* discarded. If some value of the natural key is whitespace, empty ("") or
* <code>null</code>, the check is considered OK (and the natural key is
* not added to the set of existing keys).
* <br/>
* <br/>
* The default implementation only provides this semantics for the
* container entity. Entities that can be bulk imported could be interested
* in redefining this method. However, the default implementation is
* probably good enough for them, since probably only the container
* entities will have natural keys.
*/
public void checkDuplicateNaturalKey(Set<String> existingKeys)
throws DuplicateNaturalKeyBeingImportedException {
if (containsKeyAndAdd(existingKeys, getUniversalNaturalKey())) {
throw new DuplicateNaturalKeyBeingImportedException(getEntityType(),
getNaturalKeyValues());
}
}
/**
* This method is useful to implement constructors (in subclasses) that
* automatically generate a unique code. Such constructors are useful for
@ -59,4 +161,76 @@ public abstract class IntegrationEntityDTO {
return UUID.randomUUID().toString();
}
/**
* It returns a String with the format
* <code>getUniversalCodePrefix().code.<<code-value>></code>, or
* <code>null</code> if <code>code</code> is whitespace, empty ("") or
* <code>null</code>.
*/
private String getUniversalCode() {
if (!StringUtils.isBlank(code)) {
return getUniversalCodePrefix() + ".code." + StringUtils.trim(code);
} else {
return null;
}
}
/**
* It returns a String with the format
* <code>getUniversalNaturalKeyPrefix().naturalkey.<<getNaturalKeyValues()>></code>,
* or <code>null</code> if some natural key value is whitespace, empty ("")
* or <code>null</code>.
*/
protected final String getUniversalNaturalKey() {
String[] naturalKeyValues = getNaturalKeyValues();
if (naturalKeyValues == null || naturalKeyValues.length == 0) {
return null;
}
String universalNaturalKey = getUniversalNaturalKeyPrefix() +
".natural-key.";
for (String k : naturalKeyValues) {
if (StringUtils.isBlank(k)) {
return null;
}
universalNaturalKey += StringUtils.trim(k);
}
return universalNaturalKey;
}
/**
* It checks if a key is contained in a set of existing keys. If not
* contained, the key is added to the set of existing keys. Comparison is
* case insensitive and leading and trailing whitespace is discarded. If
* the key is whitespace, empty ("") or <code>null</code>, it
* returns <code>false</code> (and the key is not added to the set of
* existing keys).
*/
protected boolean containsKeyAndAdd(Set<String> existingKeys, String newKey) {
if (StringUtils.isBlank(newKey)) {
return false;
}
String key = StringUtils.trim(newKey).toLowerCase();
if (!existingKeys.contains(key)) {
existingKeys.add(key);
return false;
}
return true;
}
}

View file

@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.List;
import org.hibernate.validator.InvalidValue;
import org.navalplanner.business.common.exceptions.ValidationException;
import org.navalplanner.ws.common.api.ConstraintViolationDTO;
import org.navalplanner.ws.common.api.InstanceConstraintViolationsDTO;
import org.navalplanner.ws.common.api.InstanceConstraintViolationsDTOId;
@ -75,6 +76,7 @@ public class ConstraintViolationConverter {
}
@Deprecated
public final static InstanceConstraintViolationsDTO toDTO(
InstanceConstraintViolationsDTOId instanceId,
InvalidValue[] invalidValues) {
@ -91,4 +93,25 @@ public class ConstraintViolationConverter {
}
public final static InstanceConstraintViolationsDTO toDTO(
InstanceConstraintViolationsDTOId instanceId,
ValidationException validationException) {
List<ConstraintViolationDTO> constraintViolationDTOs =
new ArrayList<ConstraintViolationDTO>();
if (validationException.getInvalidValues().length == 0) {
constraintViolationDTOs.add(new ConstraintViolationDTO(null,
validationException.getMessage()));
} else {
for (InvalidValue i : validationException.getInvalidValues()) {
constraintViolationDTOs.add(toDTO(i));
}
}
return new InstanceConstraintViolationsDTO(instanceId,
constraintViolationDTOs);
}
}

View file

@ -66,7 +66,15 @@ public class ResourceEnumConverter {
}
}
/**
* It returns <code>null</code> if the parameter is <code>null</code>.
*/
public final static ResourceEnum fromDTO(ResourceEnumDTO resource) {
if (resource == null) {
return null;
}
ResourceEnum value = resourceEnumFromDTO.get(resource);
if (value == null) {
@ -75,6 +83,7 @@ public class ResourceEnumConverter {
} else {
return value;
}
}
}

View file

@ -22,11 +22,13 @@ package org.navalplanner.ws.resources.criterion.api;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import org.navalplanner.ws.common.api.DuplicateCodeBeingImportedException;
import org.navalplanner.ws.common.api.IntegrationEntityDTO;
/**
@ -42,7 +44,7 @@ public class CriterionDTO extends IntegrationEntityDTO {
public String name;
@XmlAttribute
public boolean active = true;
public Boolean active;
@XmlElementWrapper(name="children")
@XmlElement(name="criterion")
@ -50,7 +52,7 @@ public class CriterionDTO extends IntegrationEntityDTO {
public CriterionDTO() {}
public CriterionDTO(String code, String name, boolean active,
public CriterionDTO(String code, String name, Boolean active,
List<CriterionDTO> children) {
super(code);
@ -65,7 +67,7 @@ public class CriterionDTO extends IntegrationEntityDTO {
* to facilitate the implementation of test cases that add new instances
* (such instances will have a unique code).
*/
public CriterionDTO(String name, boolean active,
public CriterionDTO(String name, Boolean active,
List<CriterionDTO> children) {
this(generateCode(), name, active, children);
@ -77,4 +79,16 @@ public class CriterionDTO extends IntegrationEntityDTO {
return ENTITY_TYPE;
}
@Override
public void checkDuplicateCode(Set<String> existingKeys)
throws DuplicateCodeBeingImportedException {
super.checkDuplicateCode(existingKeys);
for (CriterionDTO c : children) {
c.checkDuplicateCode(existingKeys);
}
}
}

View file

@ -22,11 +22,13 @@ package org.navalplanner.ws.resources.criterion.api;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import org.navalplanner.ws.common.api.DuplicateCodeBeingImportedException;
import org.navalplanner.ws.common.api.IntegrationEntityDTO;
import org.navalplanner.ws.common.api.ResourceEnumDTO;
@ -46,16 +48,16 @@ public class CriterionTypeDTO extends IntegrationEntityDTO {
public String description;
@XmlAttribute(name="allow-hierarchy")
public boolean allowHierarchy = true;
public Boolean allowHierarchy;
@XmlAttribute(name="allow-simultaneous-criterions-per-resource")
public boolean allowSimultaneousCriterionsPerResource = true;
public Boolean allowSimultaneousCriterionsPerResource;
@XmlAttribute
public boolean enabled = true;
public Boolean enabled;
@XmlAttribute
public ResourceEnumDTO resource = ResourceEnumDTO.RESOURCE;
public ResourceEnumDTO resource;
@XmlElementWrapper(name="criterion-list")
@XmlElement(name="criterion")
@ -64,8 +66,8 @@ public class CriterionTypeDTO extends IntegrationEntityDTO {
public CriterionTypeDTO() {}
public CriterionTypeDTO(String code, String name, String description,
boolean allowHierarchy, boolean allowSimultaneousCriterionsPerResource,
boolean enabled, ResourceEnumDTO resource,
Boolean allowHierarchy, Boolean allowSimultaneousCriterionsPerResource,
Boolean enabled, ResourceEnumDTO resource,
List<CriterionDTO> criterions) {
super(code);
@ -86,8 +88,8 @@ public class CriterionTypeDTO extends IntegrationEntityDTO {
* (such instances will have a unique code).
*/
public CriterionTypeDTO(String name, String description,
boolean allowHierarchy, boolean allowSimultaneousCriterionsPerResource,
boolean enabled, ResourceEnumDTO resource,
Boolean allowHierarchy, Boolean allowSimultaneousCriterionsPerResource,
Boolean enabled, ResourceEnumDTO resource,
List<CriterionDTO> criterions) {
this(generateCode(), name, description, allowHierarchy,
@ -101,4 +103,21 @@ public class CriterionTypeDTO extends IntegrationEntityDTO {
return ENTITY_TYPE;
}
@Override
public String[] getNaturalKeyValues() {
return new String[] {name};
}
@Override
public void checkDuplicateCode(Set<String> existingKeys)
throws DuplicateCodeBeingImportedException {
super.checkDuplicateCode(existingKeys);
for (CriterionDTO c : criterions) {
c.checkDuplicateCode(existingKeys);
}
}
}

View file

@ -20,11 +20,17 @@
package org.navalplanner.ws.resources.criterion.impl;
import static org.navalplanner.web.I18nHelper._;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
import org.navalplanner.business.common.exceptions.ValidationException;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.CriterionType;
import org.navalplanner.ws.common.impl.ResourceEnumConverter;
@ -109,8 +115,7 @@ public final class CriterionConverter {
criterionTypeDTO.allowHierarchy,
criterionTypeDTO.allowSimultaneousCriterionsPerResource,
criterionTypeDTO.enabled,
ResourceEnumConverter
.fromDTO(criterionTypeDTO.resource));
ResourceEnumConverter.fromDTO(criterionTypeDTO.resource));
for (CriterionDTO criterionDTO : criterionTypeDTO.criterions) {
addCriterion(criterionType, criterionDTO, null);
@ -120,6 +125,58 @@ public final class CriterionConverter {
}
public final static void updateCriterionType(CriterionType criterionType,
CriterionTypeDTO criterionTypeDTO) throws ValidationException {
/* 1: Get criterion wrappers with parent code. */
Set<CriterionDTOWithParentCode> criterionWrappers =
getCriterionWrappers(criterionTypeDTO.criterions, null);
/*
* 2: Update basic properties in existing criteria and add new
* criteria.
*/
for (CriterionDTOWithParentCode criterionWrapper : criterionWrappers) {
/* Step 3 requires each criterion DTO to have a code. */
if (StringUtils.isBlank(criterionWrapper.dto.code)) {
throw new ValidationException(_("missing code in a criterion"));
}
try {
Criterion criterion = criterionType.getCriterionByCode(
criterionWrapper.dto.code);
updateCriterionBasicProperties(criterion, criterionWrapper.dto);
} catch (InstanceNotFoundException e) {
criterionType.getCriterions().add(toEntityWithoutChildren(
criterionWrapper.dto, criterionType, null));
}
}
/* 3: Update relationships. */
for (CriterionDTOWithParentCode criterionWrapper : criterionWrappers) {
Criterion criterion = criterionType.getExistingCriterionByCode(
criterionWrapper.dto.code);
Criterion newCriterionParent = null;
if (criterionWrapper.parentCode != null) {
newCriterionParent = criterionType.getExistingCriterionByCode(
criterionWrapper.parentCode);
}
criterion.moveTo(newCriterionParent);
}
/* 4: Update criterion type basic properties. */
updateCriterionTypeBasicProperties(criterionType, criterionTypeDTO);
}
private static Criterion addCriterion(CriterionType criterionType,
CriterionDTO criterionDTO, Criterion criterionParent) {
@ -149,4 +206,66 @@ public final class CriterionConverter {
}
private static Set<CriterionDTOWithParentCode> getCriterionWrappers(
Collection<CriterionDTO> criterionTypeDTOs, String parentCode) {
Set<CriterionDTOWithParentCode> wrappers =
new HashSet<CriterionDTOWithParentCode>();
for (CriterionDTO criterionDTO : criterionTypeDTOs) {
wrappers.add(new CriterionDTOWithParentCode(criterionDTO,
parentCode));
wrappers.addAll(getCriterionWrappers(criterionDTO.children,
criterionDTO.code));
}
return wrappers;
}
private static void updateCriterionTypeBasicProperties(
CriterionType criterionType, CriterionTypeDTO criterionTypeDTO) {
if (!StringUtils.isBlank(criterionTypeDTO.name)) {
criterionType.setName(StringUtils.trim(criterionTypeDTO.name));
}
if (!StringUtils.isBlank(criterionTypeDTO.description)) {
criterionType.setDescription(
StringUtils.trim(criterionTypeDTO.description));
}
if (criterionTypeDTO.allowHierarchy != null) {
criterionType.setAllowHierarchy(criterionTypeDTO.allowHierarchy);
}
if (criterionTypeDTO.allowSimultaneousCriterionsPerResource != null) {
criterionType.setAllowSimultaneousCriterionsPerResource(
criterionTypeDTO.allowSimultaneousCriterionsPerResource);
}
if (criterionTypeDTO.enabled != null) {
criterionType.setEnabled(criterionTypeDTO.enabled);
}
if (criterionTypeDTO.resource != null) {
criterionType.setResource(
ResourceEnumConverter.fromDTO(criterionTypeDTO.resource));
}
}
private static void updateCriterionBasicProperties(Criterion criterion,
CriterionDTO criterionDTO) {
if (!StringUtils.isBlank(criterionDTO.name)) {
criterion.setName(StringUtils.trim(criterionDTO.name));
}
if (criterionDTO.active != null) {
criterion.setActive(criterionDTO.active);
}
}
}

View file

@ -0,0 +1,43 @@
/*
* This file is part of ###PROJECT_NAME###
*
* Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.navalplanner.ws.resources.criterion.impl;
import org.navalplanner.ws.resources.criterion.api.CriterionDTO;
/**
* It aggregates a <code>CriterionDTO</code> and its parent criterion code.
*
* @author Fernando Bellas Permuy <fbellas@udc.es>
*/
public class CriterionDTOWithParentCode {
public CriterionDTO dto;
public String parentCode;
public CriterionDTOWithParentCode(CriterionDTO dto, String parentCode) {
this.dto = dto;
this.parentCode = parentCode;
}
}

View file

@ -23,6 +23,7 @@ package org.navalplanner.ws.resources.criterion.impl;
import static org.navalplanner.web.I18nHelper._;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -33,9 +34,12 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
import org.navalplanner.business.common.exceptions.ValidationException;
import org.navalplanner.business.resources.daos.ICriterionTypeDAO;
import org.navalplanner.business.resources.entities.CriterionType;
import org.navalplanner.ws.common.api.DuplicateCodeBeingImportedException;
import org.navalplanner.ws.common.api.DuplicateNaturalKeyBeingImportedException;
import org.navalplanner.ws.common.api.InstanceConstraintViolationsDTO;
import org.navalplanner.ws.common.api.InstanceConstraintViolationsListDTO;
import org.navalplanner.ws.common.impl.ConstraintViolationConverter;
@ -77,75 +81,94 @@ public class CriterionServiceREST implements ICriterionService {
List<InstanceConstraintViolationsDTO> instanceConstraintViolationsList =
new ArrayList<InstanceConstraintViolationsDTO>();
Long numItem = new Long(1);
Set<String> criterionTypeNames = new HashSet<String>();
Set<String> existingKeys = new HashSet<String>();
/* Process criterion types. */
for (CriterionTypeDTO criterionTypeDTO :
criterionTypes.criterionTypes) {
/* Convert DTO to entity. */
InstanceConstraintViolationsDTO instanceConstraintViolationsDTO =
null;
CriterionType criterionType =
CriterionConverter.toEntity(criterionTypeDTO);
CriterionType criterionType = null;
/*
* Check if the criterion type name is used by another criterion
* type being imported.
*/
if (criterionType.getName() != null && criterionTypeNames.contains(
criterionType.getName().toLowerCase())) {
try {
/*
* We must detect if there exists another instance being
* imported with the same code or natural key, since
* "IntegrationEntity::checkConstraintUniqueCode" and
* the natural key unique constraint rule
* (@AssertTrue/@AssertFalse method) in the concrete entity only
* can check this condition with respect to the entities already
* existing in database (such methods use DAO
* "xxxAnotherTransaction" methods to avoid Hibernate to launch
* INSERT statements for new objects when launching queries in
* conversational use cases).
*/
criterionTypeDTO.checkDuplicateCode(existingKeys);
criterionTypeDTO.checkDuplicateNaturalKey(existingKeys);
/*
* Convert DTO to entity. Note that the entity, if exists in
* the database, must be retrieved in another transaction.
* Otherwise (if retrieved as part of the current
* transaction), if the implementation of "updateEntity" makes
* modifications to the entity passed as a parameter and
* then throws an exception (because something make impossible
* to continue updating), the entity would be considered as
* dirty because it would be contained in the underlying ORM
* session, and in consequence, the ORM would try to update it
* when committing the transaction. Furthermore, the entity
* must be initialized so that "updateEntity" can access
* related entities.
*/
try {
criterionType =
findByCodeAnotherTransactionInitialized(
criterionTypeDTO.code);
udpateEntity(criterionType, criterionTypeDTO);
} catch (InstanceNotFoundException e) {
criterionType = toEntity(criterionTypeDTO);
}
/*
* Save the entity (insert or update).
*
* "validate" is executed before "save", since "save" first
* adds the object to the underlying ORM session and then
* validates. So, if "validate" method is not called explicitly
* before "save", an invalid entity would be added to the
* underlying ORM session, causing the invalid entity to be
* added to the database when the ORM commits the transaction.
* As a side effect, validations are executed twice.
*/
criterionType.validate();
criterionTypeDAO.save(criterionType);
} catch (DuplicateCodeBeingImportedException e) {
instanceConstraintViolationsDTO =
InstanceConstraintViolationsDTO.create(
Util.generateInstanceConstraintViolationsDTOId(numItem,
criterionTypeDTO),
_("criterion type name is used by another criterion " +
"type being imported"));
} else {
/* Validate criterion type. */
try {
/*
* "validate" is executed before "save", since "save" first
* adds the object to the underlying ORM session and then
* validates. So, if "validate" method is not called
* explicitly before "save", an invalid criterion type
* would be added to the underlying ORM session, causing
* the invalid criterion type to be added to the database
* when the ORM commits the transaction. As a side effect,
* validations are executed twice. Note also, that
* "CriterionType::checkConstraintUniqueCriterionTypeName"
* only checks if a criterion type with the same name
* already exists in the *database*, and that the criterion
* types being imported are inserted in the database when
* the transaction is committed. In consequence, we can only
* call "save" if the criterion type is valid according to
* "validate" method and its name is not used by another
* previously *imported* (not in the database yet) criterion
* type.
*/
criterionType.validate();
criterionTypeDAO.save(criterionType);
if (criterionType.getName() != null) {
criterionTypeNames.add(criterionType.getName().
toLowerCase());
}
} catch (ValidationException e) {
instanceConstraintViolationsDTO =
ConstraintViolationConverter.toDTO(
Util.generateInstanceConstraintViolationsDTOId(
numItem, criterionTypeDTO),
e.getInvalidValues());
}
_("code: {0} is used by another instance of type {1} " +
"being imported", e.getCode(), e.getEntityType()));
} catch (DuplicateNaturalKeyBeingImportedException e) {
instanceConstraintViolationsDTO =
InstanceConstraintViolationsDTO.create(
Util.generateInstanceConstraintViolationsDTOId(numItem,
criterionTypeDTO),
_("values: {0} are used by another instance of type " +
"{1} being imported",
Arrays.toString(e.getNaturalKeyValues()),
e.getEntityType()));
} catch (ValidationException e) {
instanceConstraintViolationsDTO =
ConstraintViolationConverter.toDTO(
Util.generateInstanceConstraintViolationsDTOId(
numItem, criterionTypeDTO), e);
}
/* Add constraint violations (if any). */
if (instanceConstraintViolationsDTO != null) {
instanceConstraintViolationsList.add(
instanceConstraintViolationsDTO);
@ -160,4 +183,23 @@ public class CriterionServiceREST implements ICriterionService {
}
private CriterionType findByCodeAnotherTransactionInitialized(
String code) throws InstanceNotFoundException {
return criterionTypeDAO.findByCodeAnotherTransactionInitialized(
code);
}
private CriterionType toEntity(CriterionTypeDTO criterionTypeDTO) {
return CriterionConverter.toEntity(criterionTypeDTO);
}
private void udpateEntity(CriterionType criterionType,
CriterionTypeDTO criterionTypeDTO) throws ValidationException {
CriterionConverter.updateCriterionType(criterionType, criterionTypeDTO);
}
}

View file

@ -20,14 +20,22 @@
package org.navalplanner.web.test.ws.common;
import static org.junit.Assert.assertTrue;
import java.util.List;
import java.util.UUID;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.navalplanner.ws.common.api.InstanceConstraintViolationsDTO;
import org.navalplanner.ws.common.api.InstanceConstraintViolationsListDTO;
/**
* Utilities class related with web service tests.
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
* @author Fernando Bellas Permuy <fbellas@udc.es>
*/
public class Util {
@ -50,4 +58,60 @@ public class Util {
};
}
public static String getUniqueName() {
return UUID.randomUUID().toString();
}
public static void assertNoConstraintViolations(
InstanceConstraintViolationsListDTO
instanceConstraintViolationsListDTO) {
assertTrue(
instanceConstraintViolationsListDTO.
instanceConstraintViolationsList.toString(),
instanceConstraintViolationsListDTO.
instanceConstraintViolationsList.size() == 0);
}
public static void assertOneConstraintViolation(
InstanceConstraintViolationsListDTO
instanceConstraintViolationsListDTO) {
List<InstanceConstraintViolationsDTO> instanceConstraintViolationsList =
instanceConstraintViolationsListDTO.
instanceConstraintViolationsList;
assertTrue(
instanceConstraintViolationsList.toString(),
instanceConstraintViolationsList.size() == 1);
assertTrue(
instanceConstraintViolationsList.get(0).
constraintViolations.toString(),
instanceConstraintViolationsList.get(0).
constraintViolations.size() == 1);
}
public static void assertOneConstraintViolationPerInstance(
InstanceConstraintViolationsListDTO
instanceConstraintViolationsListDTO, int numberOfInstances) {
List<InstanceConstraintViolationsDTO> instanceConstraintViolationsList =
instanceConstraintViolationsListDTO.
instanceConstraintViolationsList;
assertTrue(
instanceConstraintViolationsList.toString(),
instanceConstraintViolationsList.size() == numberOfInstances);
for (InstanceConstraintViolationsDTO i :
instanceConstraintViolationsList) {
assertTrue(
i.constraintViolations.toString(),
i.constraintViolations.size() == 1);
}
}
}

View file

@ -28,10 +28,13 @@ import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONF
import static org.navalplanner.web.WebappGlobalNames.WEBAPP_SPRING_CONFIG_FILE;
import static org.navalplanner.web.WebappGlobalNames.WEBAPP_SPRING_SECURITY_CONFIG_FILE;
import static org.navalplanner.web.test.WebappGlobalNames.WEBAPP_SPRING_CONFIG_TEST_FILE;
import static org.navalplanner.web.test.ws.common.Util.assertNoConstraintViolations;
import static org.navalplanner.web.test.ws.common.Util.assertOneConstraintViolation;
import static org.navalplanner.web.test.ws.common.Util.assertOneConstraintViolationPerInstance;
import static org.navalplanner.web.test.ws.common.Util.getUniqueName;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeConstants;
@ -62,7 +65,6 @@ import org.navalplanner.business.resources.entities.Resource;
import org.navalplanner.business.resources.entities.ResourceEnum;
import org.navalplanner.business.resources.entities.Worker;
import org.navalplanner.ws.common.api.InstanceConstraintViolationsDTO;
import org.navalplanner.ws.common.api.InstanceConstraintViolationsListDTO;
import org.navalplanner.ws.resources.api.CriterionSatisfactionDTO;
import org.navalplanner.ws.resources.api.IResourceService;
import org.navalplanner.ws.resources.api.MachineDTO;
@ -900,58 +902,6 @@ public class ResourceServiceTest {
}
private void assertNoConstraintViolations(
InstanceConstraintViolationsListDTO
instanceConstraintViolationsListDTO) {
assertTrue(
instanceConstraintViolationsListDTO.
instanceConstraintViolationsList.toString(),
instanceConstraintViolationsListDTO.
instanceConstraintViolationsList.size() == 0);
}
private void assertOneConstraintViolation(
InstanceConstraintViolationsListDTO
instanceConstraintViolationsListDTO) {
List<InstanceConstraintViolationsDTO> instanceConstraintViolationsList =
instanceConstraintViolationsListDTO.
instanceConstraintViolationsList;
assertTrue(
instanceConstraintViolationsList.toString(),
instanceConstraintViolationsList.size() == 1);
assertTrue(
instanceConstraintViolationsList.get(0).
constraintViolations.toString(),
instanceConstraintViolationsList.get(0).
constraintViolations.size() == 1);
}
private void assertOneConstraintViolationPerInstance(
InstanceConstraintViolationsListDTO
instanceConstraintViolationsListDTO, int numberOfInstances) {
List<InstanceConstraintViolationsDTO> instanceConstraintViolationsList =
instanceConstraintViolationsListDTO.
instanceConstraintViolationsList;
assertTrue(
instanceConstraintViolationsList.toString(),
instanceConstraintViolationsList.size() == numberOfInstances);
for (InstanceConstraintViolationsDTO i :
instanceConstraintViolationsList) {
assertTrue(
i.constraintViolations.toString(),
i.constraintViolations.size() == 1);
}
}
private XMLGregorianCalendar getDate(int year, int month, int day) {
try {
@ -963,8 +913,4 @@ public class ResourceServiceTest {
}
private String getUniqueName() {
return UUID.randomUUID().toString();
}
}

View file

@ -20,24 +20,29 @@
package org.navalplanner.web.test.ws.resources.criterion.api;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE;
import static org.navalplanner.web.WebappGlobalNames.WEBAPP_SPRING_CONFIG_FILE;
import static org.navalplanner.web.test.WebappGlobalNames.WEBAPP_SPRING_CONFIG_TEST_FILE;
import static org.navalplanner.web.test.ws.common.Util.assertNoConstraintViolations;
import static org.navalplanner.web.test.ws.common.Util.assertOneConstraintViolation;
import static org.navalplanner.web.test.ws.common.Util.getUniqueName;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.navalplanner.business.common.IAdHocTransactionService;
import org.navalplanner.business.common.IOnTransaction;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
import org.navalplanner.business.resources.daos.ICriterionTypeDAO;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.CriterionType;
import org.navalplanner.ws.common.api.InstanceConstraintViolationsDTO;
import org.navalplanner.ws.common.api.InstanceConstraintViolationsListDTO;
import org.navalplanner.ws.common.api.ResourceEnumDTO;
import org.navalplanner.ws.common.impl.ResourceEnumConverter;
import org.navalplanner.ws.resources.criterion.api.CriterionDTO;
import org.navalplanner.ws.resources.criterion.api.CriterionTypeDTO;
import org.navalplanner.ws.resources.criterion.api.CriterionTypeListDTO;
@ -59,14 +64,14 @@ import org.springframework.transaction.annotation.Transactional;
@Transactional
public class CriterionServiceTest {
@Autowired
private IAdHocTransactionService transactionService;
@Autowired
private ICriterionService criterionService;
@Autowired
private ICriterionTypeDAO criterionTypeDAO;
@Test
public void testAddCriterionTypes() {
public void testAddAndGetCriterionTypes() {
/* Build criterion type "ct1" (4 constraint violations). */
CriterionDTO ct1c1 = new CriterionDTO(null, true, // Missing criterion
@ -137,17 +142,12 @@ public class CriterionServiceTest {
ResourceEnumDTO.RESOURCE, new ArrayList<CriterionDTO>());
/* Criterion type list. */
List<CriterionTypeDTO> criterionTypes =
new ArrayList<CriterionTypeDTO>();
criterionTypes.add(ct1);
criterionTypes.add(ct2);
criterionTypes.add(ct3);
criterionTypes.add(ct4);
CriterionTypeListDTO criterionTypes =
createCriterionTypeListDTO(ct1, ct2, ct3, ct4);
List<InstanceConstraintViolationsDTO> instanceConstraintViolationsList =
criterionService.addCriterionTypes(
new CriterionTypeListDTO(criterionTypes)).
instanceConstraintViolationsList;
criterionService.addCriterionTypes(criterionTypes).
instanceConstraintViolationsList;
assertTrue(
instanceConstraintViolationsList.toString(),
@ -180,52 +180,113 @@ public class CriterionServiceTest {
@Test
@NotTransactional
public void testAddCriterionTypeThatAlreadyExistsInDB()
public void testAddRepeatedCriterionTypeThatAlreadyExistsInDB()
throws InstanceNotFoundException {
final String criterionTypeName = getUniqueName();
CriterionTypeDTO criterionType = new CriterionTypeDTO(
getUniqueName(), "desc", true, true, true,
ResourceEnumDTO.RESOURCE, new ArrayList<CriterionDTO>());
IOnTransaction<InstanceConstraintViolationsListDTO>
createCriterionType =
new IOnTransaction<InstanceConstraintViolationsListDTO>() {
assertNoConstraintViolations(criterionService.addCriterionTypes(
createCriterionTypeListDTO(criterionType)));
@Override
public InstanceConstraintViolationsListDTO execute() {
CriterionTypeDTO criterionType = new CriterionTypeDTO(
criterionTypeName, "desc", true, true, true,
ResourceEnumDTO.RESOURCE, new ArrayList<CriterionDTO>());
List<CriterionTypeDTO> criterionTypes =
new ArrayList<CriterionTypeDTO>();
criterionTypes.add(criterionType);
return criterionService.addCriterionTypes(
new CriterionTypeListDTO(criterionTypes));
}
};
List<InstanceConstraintViolationsDTO> instanceConstraintViolationsList =
transactionService.runOnTransaction(
createCriterionType).
instanceConstraintViolationsList;
assertTrue(
instanceConstraintViolationsList.toString(),
instanceConstraintViolationsList.size() == 0);
instanceConstraintViolationsList =
transactionService.runOnTransaction(
createCriterionType).
instanceConstraintViolationsList;
assertTrue(
instanceConstraintViolationsList.toString(),
instanceConstraintViolationsList.size() == 1);
criterionType.code = getUniqueName(); // Another criterion with the
// same data.
assertOneConstraintViolation(criterionService.addCriterionTypes(
createCriterionTypeListDTO(criterionType))); // Repeated criterion
// type name.
}
private String getUniqueName() {
return UUID.randomUUID().toString();
@Test
@NotTransactional
public void testUpdateCriterionType() throws InstanceNotFoundException {
/* Build criterion type with criteria: c1, c2->c2-1. */
CriterionDTO c1 = new CriterionDTO("c1", true,
new ArrayList<CriterionDTO>());
CriterionDTO c2c1 = new CriterionDTO("c2-1",
true, new ArrayList<CriterionDTO>());
List<CriterionDTO> c2Criterions = new ArrayList<CriterionDTO>();
c2Criterions.add(c2c1);
CriterionDTO c2 = new CriterionDTO("c2", true, c2Criterions);
List<CriterionDTO> rootCriterions = new ArrayList<CriterionDTO>();
rootCriterions.add(c1);
rootCriterions.add(c2);
CriterionTypeDTO ct = new CriterionTypeDTO(getUniqueName(),
"desc", true, true, true, ResourceEnumDTO.WORKER, rootCriterions);
/* Add criterion type. */
assertNoConstraintViolations(criterionService.addCriterionTypes(
createCriterionTypeListDTO(ct)));
/*
* Build a DTO for making the following update: add new root criterion
* ("c3"), move "c2" to "c1" and modify c2's name, and update
* criterion type description.
*/
CriterionDTO c3 = new CriterionDTO("c3", true,
new ArrayList<CriterionDTO>());
CriterionDTO c2Updated = new CriterionDTO(c2.code, c2.name + "UPDATED",
null, new ArrayList<CriterionDTO>());
List<CriterionDTO> c1CriterionsUpdated = new ArrayList<CriterionDTO>();
c1CriterionsUpdated.add(c2Updated);
CriterionDTO c1Updated = new CriterionDTO(c1.code, null, null,
c1CriterionsUpdated);
List<CriterionDTO> rootCriterionsUpdated =
new ArrayList<CriterionDTO>();
rootCriterionsUpdated.add(c3);
rootCriterionsUpdated.add(c1Updated);
CriterionTypeDTO ctUpdated = new CriterionTypeDTO(ct.code, null,
"desc" + "UPDATED", null, null, null, null, rootCriterionsUpdated);
/* Update criterion type and test. */
assertNoConstraintViolations(criterionService.addCriterionTypes(
createCriterionTypeListDTO(ctUpdated)));
CriterionType ctEntity =
criterionTypeDAO.findByCodeAnotherTransactionInitialized(ct.code);
assertTrue(ctEntity.getCriterions().size() == 4);
/* Test criterion hierarchy. */
Criterion c1Entity = ctEntity.getCriterion(c1.name);
Criterion c2Entity = ctEntity.getCriterion(c2Updated.name);
Criterion c2c1Entity = ctEntity.getCriterion(c2c1.name);
Criterion c3Entity = ctEntity.getCriterion(c3.name);
assertNull(c1Entity.getParent());
assertTrue(c1Entity.getChildren().size() == 1);
assertTrue(c1Entity.getChildren().contains(c2Entity));
assertTrue(c2Entity.getChildren().size() == 1);
assertTrue(c2Entity.getChildren().contains(c2c1Entity));
assertTrue(c2c1Entity.getChildren().size() == 0);
assertNull(c3Entity.getParent());
assertTrue(c3Entity.getChildren().size() == 0);
/*
* Basic properties in criteria "c1" and "c2", which are contained in
* "ctUpdated", must not be modified, except c2's name property.
*/
assertEquals(c1.name, c1Entity.getName());
assertEquals(c1.active, c1Entity.isActive());
assertEquals(c2Updated.name, c2Entity.getName());
assertEquals(c2.active, c2Entity.isActive());
/*
* Basic properties values, except description, must be not be
* modified.
*/
assertEquals(ct.name, ctEntity.getName());
assertEquals(ctUpdated.description, ctEntity.getDescription());
assertEquals(ct.allowHierarchy, ctEntity.allowHierarchy());
assertEquals(ct.allowSimultaneousCriterionsPerResource,
ctEntity.isAllowSimultaneousCriterionsPerResource());
assertEquals(ct.enabled, ctEntity.isEnabled());
assertEquals(ResourceEnumConverter.fromDTO(ct.resource),
ctEntity.getResource());
}
private boolean containsCriterionType(
@ -241,4 +302,18 @@ public class CriterionServiceTest {
}
private CriterionTypeListDTO createCriterionTypeListDTO(
CriterionTypeDTO... criterionTypes) {
List<CriterionTypeDTO> criterionTypeList =
new ArrayList<CriterionTypeDTO>();
for (CriterionTypeDTO c : criterionTypes) {
criterionTypeList.add(c);
}
return new CriterionTypeListDTO(criterionTypeList);
}
}

View file

@ -23,6 +23,9 @@
- Check the returned errors are consistent with the comments in
criterion-types-sample.xml.
- Repeat with criterion-types-update-sample.xml (for updating some
criterion types).
* Export criterion types:
- export-criterion-types.sh (authenticate with wsreader/wsreader)

View file

@ -108,4 +108,38 @@
</criterion-list>
</criterion-type>
<!-- Repeated criterion type code ("ct-6") -->
<criterion-type code="ct-6" name="ct-7" description="ct-7 desc"
allow-hierarchy="false"
allow-simultaneous-criterions-per-resource="false" enabled="false"
resource="WORKER"/>
<!-- Repeated criterion code (ct-8-c1) -->
<criterion-type code="ct-8" name="ct-8" description="ct-8 desc"
allow-hierarchy="true" allow-simultaneous-criterions-per-resource="true"
enabled="true" resource="RESOURCE">
<criterion-list>
<criterion code="ct-8-c1" name="c8" active="true"/>
<criterion code="ct-8-c1" name="c8" active="true"/>
</criterion-list>
</criterion-type>
<!-- Repeated criterion code (ct-4-c2-1) -->
<criterion-type code="ct-9" name="ct-9" description="ct-9 desc"
allow-hierarchy="true" allow-simultaneous-criterions-per-resource="true"
enabled="true" resource="RESOURCE">
<criterion-list>
<criterion code="ct-9-c1" name="c9" active="true"/>
<criterion code="ct-9-c2" name="c9" active="true">
<children>
<criterion code="ct-9-c2-1" name="c9-1" active="true"/>
<criterion code="ct-4-c2-1" name="c9-2" active="true"/>
</children>
</criterion>
</criterion-list>
</criterion-type>
</criterion-type-list>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<criterion-type-list xmlns="http://rest.ws.navalplanner.org">
<!-- OK -->
<criterion-type code="ct-6" name="ct-6-XXX" description="ct-6 desc XXX"/>
<!-- OK -->
<criterion-type code="ct-4">
<criterion-list>
<criterion code="ct-4-c2">
<children>
<criterion code="ct-4-c2-1-1" active="true"/>
</children>
</criterion>
</criterion-list>
</criterion-type>
</criterion-type-list>