Merge pull request #9 from bsinno/MECS-1438_CREATE_FILTER_OPTIONS_FOR_MAP

ok
This commit is contained in:
Kai Zimmermann
2016-01-26 11:35:53 +01:00
14 changed files with 466 additions and 284 deletions

View File

@@ -36,16 +36,50 @@ public enum DistributionSetFields implements FieldNameProvider {
/** /**
* The id field. * The id field.
*/ */
ID("id"); ID("id"),
/**
* The tags field.
*/
TAG("tags.name"),
/**
* The sw type key field.
*/
TYPE("type.key"),
/**
* The metadata.
*/
METADATA("metadata", "key", "value");
private final String fieldName; private final String fieldName;
private String keyFieldName;
private String valueFieldName;
private DistributionSetFields(final String fieldName) { private DistributionSetFields(final String fieldName) {
this(fieldName, null, null);
}
private DistributionSetFields(final String fieldName, final String keyFieldName, final String valueFieldName) {
this.fieldName = fieldName; this.fieldName = fieldName;
this.keyFieldName = keyFieldName;
this.valueFieldName = valueFieldName;
}
@Override
public String getValueFieldName() {
return valueFieldName;
}
@Override
public String getKeyFieldName() {
return keyFieldName;
} }
@Override @Override
public String getFieldName() { public String getFieldName() {
return fieldName; return fieldName;
} }
} }

View File

@@ -8,18 +8,87 @@
*/ */
package org.eclipse.hawkbit.repository; package org.eclipse.hawkbit.repository;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/** /**
* An interface for declaring the name of the field described in the database * An interface for declaring the name of the field described in the database
* which is used as string representation of the field, e.g. for sorting the * which is used as string representation of the field, e.g. for sorting the
* fields over REST. * fields over REST.
* *
*/ */
@FunctionalInterface
public interface FieldNameProvider { public interface FieldNameProvider {
/**
* Seperator for the sub attributes
*/
public static final String SUB_ATTRIBUTE_SEPERATOR = ".";
/** /**
* @return the string representation of the underlying persistence field * @return the string representation of the underlying persistence field
* name e.g. in case of sorting. Never {@code null}. * name e.g. in case of sorting. Never {@code null}.
*/ */
String getFieldName(); String getFieldName();
default boolean containsSubEntityAttribute(final String propertyField) {
return FieldNameProvider.containsSubEntityAttribute(propertyField, getSubEntityAttributes());
};
default List<String> getSubEntityAttributes() {
return Collections.emptyList();
};
/**
* the database column for the key
*
* @return key fieldname
*/
default String getKeyFieldName() {
return null;
}
/**
* the database column for the value
*
* @return key fieldname
*/
default String getValueFieldName() {
return null;
}
/**
* Is the entity field a {@link Map}.
*
* @return
*/
default boolean isMap() {
return getKeyFieldName() != null;
};
/**
* Check if a sub attribute exists.
*
* @param propertyField
* the sub property field.
* @param subEntityAttribues
* the list of available properties
* @return <true> property exists <false> not exists
*/
static boolean containsSubEntityAttribute(final String propertyField, final List<String> subEntityAttribues) {
if (subEntityAttribues.contains(propertyField)) {
return true;
}
for (final String attribute : subEntityAttribues) {
final String[] graph = attribute.split("\\" + SUB_ATTRIBUTE_SEPERATOR);
for (final String subAttribute : graph) {
if (subAttribute.equalsIgnoreCase(propertyField)) {
return true;
}
}
}
return false;
}
} }

View File

@@ -36,16 +36,38 @@ public enum SoftwareModuleFields implements FieldNameProvider {
/** /**
* The id field. * The id field.
*/ */
ID("id"); ID("id"),
/**
* The metadata.
*/
METADATA("metadata", "key", "value");
private final String fieldName; private final String fieldName;
private String keyFieldName;
private String valueFieldName;
private SoftwareModuleFields(final String fieldName) { private SoftwareModuleFields(final String fieldName) {
this(fieldName, null, null);
}
private SoftwareModuleFields(final String fieldName, final String keyFieldName, final String valueFieldName) {
this.fieldName = fieldName; this.fieldName = fieldName;
this.keyFieldName = keyFieldName;
this.valueFieldName = valueFieldName;
} }
@Override @Override
public String getFieldName() { public String getFieldName() {
return fieldName; return fieldName;
} }
@Override
public String getKeyFieldName() {
return keyFieldName;
}
@Override
public String getValueFieldName() {
return valueFieldName;
}
} }

View File

@@ -8,15 +8,22 @@
*/ */
package org.eclipse.hawkbit.repository; package org.eclipse.hawkbit.repository;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/** /**
* Describing the fields of the Target model which can be used in the REST API * Describing the fields of the Target model which can be used in the REST API
* e.g. for sorting etc. * e.g. for sorting etc.
* *
*
*
*
*/ */
public enum TargetFields implements FieldNameProvider { public enum TargetFields implements FieldNameProvider {
/**
* The controllerId field.
*/
ID("controllerId"),
/** /**
* The name field. * The name field.
*/ */
@@ -37,12 +44,49 @@ public enum TargetFields implements FieldNameProvider {
/** /**
* The ip-address field. * The ip-address field.
*/ */
IPADDRESS("targetInfo.ipAddress"); IPADDRESS("targetInfo.address"),
/**
* The attribute map of target info.
*/
ATTRIBUTE("targetInfo.controllerAttributes", true),
ASSIGNEDDS("assignedDistributionSet", "name", "version"),
/**
* The tags field.
*/
TAG("tags.name");
private final String fieldName; private final String fieldName;
private List<String> subEntityAttribues;
private boolean mapField;
private TargetFields(final String fieldName) { private TargetFields(final String fieldName) {
this(fieldName, false, Collections.emptyList());
}
private TargetFields(final String fieldName, final boolean isMapField) {
this(fieldName, isMapField, Collections.emptyList());
}
private TargetFields(final String fieldName, final String... subEntityAttribues) {
this(fieldName, false, Arrays.asList(subEntityAttribues));
}
private TargetFields(final String fieldName, final boolean mapField, final List<String> subEntityAttribues) {
this.fieldName = fieldName; this.fieldName = fieldName;
this.mapField = mapField;
this.subEntityAttribues = subEntityAttribues;
}
@Override
public List<String> getSubEntityAttributes() {
return subEntityAttribues;
}
@Override
public boolean isMap() {
return mapField;
} }
@Override @Override

View File

@@ -205,8 +205,7 @@ public class TargetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Slice<Target> findTargetsAll(@NotNull final TargetFilterQuery targetFilterQuery, public Slice<Target> findTargetsAll(@NotNull final TargetFilterQuery targetFilterQuery,
@NotNull final Pageable pageable) { @NotNull final Pageable pageable) {
return findTargetsAll(RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class, entityManager), return findTargetsAll(RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class), pageable);
pageable);
} }
/** /**
@@ -220,7 +219,7 @@ public class TargetManagement {
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Slice<Target> findTargetsAll(@NotNull final String targetFilterQuery, @NotNull final Pageable pageable) { public Slice<Target> findTargetsAll(@NotNull final String targetFilterQuery, @NotNull final Pageable pageable) {
return findTargetsAll(RSQLUtility.parse(targetFilterQuery, TargetFields.class, entityManager), pageable); return findTargetsAll(RSQLUtility.parse(targetFilterQuery, TargetFields.class), pageable);
} }
/** /**
@@ -351,9 +350,9 @@ public class TargetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET)
public Page<Target> findTargetByAssignedDistributionSet(@NotNull final Long distributionSetID, public Page<Target> findTargetByAssignedDistributionSet(@NotNull final Long distributionSetID,
final Specification<Target> spec, @NotNull final Pageable pageReq) { final Specification<Target> spec, @NotNull final Pageable pageReq) {
return targetRepository.findAll((Specification<Target>) (root, query, cb) -> cb.and( return targetRepository.findAll((Specification<Target>) (root, query, cb) -> cb.and(TargetSpecifications
TargetSpecifications.hasAssignedDistributionSet(distributionSetID).toPredicate(root, query, cb), .hasAssignedDistributionSet(distributionSetID).toPredicate(root, query, cb), spec.toPredicate(root,
spec.toPredicate(root, query, cb)), pageReq); query, cb)), pageReq);
} }
/** /**
@@ -390,9 +389,9 @@ public class TargetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET)
public Page<Target> findTargetByInstalledDistributionSet(final Long distributionSetId, public Page<Target> findTargetByInstalledDistributionSet(final Long distributionSetId,
final Specification<Target> spec, final Pageable pageable) { final Specification<Target> spec, final Pageable pageable) {
return targetRepository.findAll((Specification<Target>) (root, query, cb) -> cb.and( return targetRepository.findAll((Specification<Target>) (root, query, cb) -> cb.and(TargetSpecifications
TargetSpecifications.hasInstalledDistributionSet(distributionSetId).toPredicate(root, query, cb), .hasInstalledDistributionSet(distributionSetId).toPredicate(root, query, cb), spec.toPredicate(root,
spec.toPredicate(root, query, cb)), pageable); query, cb)), pageable);
} }
/** /**
@@ -487,8 +486,8 @@ public class TargetManagement {
specList.add(TargetSpecifications.hasTargetUpdateStatus(status, fetch)); specList.add(TargetSpecifications.hasTargetUpdateStatus(status, fetch));
} }
if (installedOrAssignedDistributionSetId != null) { if (installedOrAssignedDistributionSetId != null) {
specList.add( specList.add(TargetSpecifications
TargetSpecifications.hasInstalledOrAssignedDistributionSet(installedOrAssignedDistributionSetId)); .hasInstalledOrAssignedDistributionSet(installedOrAssignedDistributionSetId));
} }
if (!Strings.isNullOrEmpty(searchText)) { if (!Strings.isNullOrEmpty(searchText)) {
specList.add(TargetSpecifications.likeNameOrDescriptionOrIp(searchText)); specList.add(TargetSpecifications.likeNameOrDescriptionOrIp(searchText));
@@ -560,8 +559,8 @@ public class TargetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public TargetTagAssigmentResult toggleTagAssignment(@NotEmpty final List<Target> targets, public TargetTagAssigmentResult toggleTagAssignment(@NotEmpty final List<Target> targets,
@NotNull final TargetTag tag) { @NotNull final TargetTag tag) {
return toggleTagAssignment( return toggleTagAssignment(targets.stream().map(target -> target.getControllerId())
targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList()), tag.getName()); .collect(Collectors.toList()), tag.getName());
} }
/** /**
@@ -584,8 +583,8 @@ public class TargetManagement {
@NotNull final String tagName) { @NotNull final String tagName) {
final TargetTag tag = targetTagRepository.findByNameEquals(tagName); final TargetTag tag = targetTagRepository.findByNameEquals(tagName);
final List<Target> alreadyAssignedTargets = targetRepository.findByTagNameAndControllerIdIn(tagName, targetIds); final List<Target> alreadyAssignedTargets = targetRepository.findByTagNameAndControllerIdIn(tagName, targetIds);
final List<Target> allTargets = targetRepository final List<Target> allTargets = targetRepository.findAll(TargetSpecifications
.findAll(TargetSpecifications.byControllerIdWithStatusAndTagsInJoin(targetIds)); .byControllerIdWithStatusAndTagsInJoin(targetIds));
// all are already assigned -> unassign // all are already assigned -> unassign
if (alreadyAssignedTargets.size() == allTargets.size()) { if (alreadyAssignedTargets.size() == allTargets.size()) {
@@ -741,20 +740,19 @@ public class TargetManagement {
// select case expression to retrieve the case value as a column to be // select case expression to retrieve the case value as a column to be
// able to order based on // able to order based on
// this column, installed first,... // this column, installed first,...
final Expression<Object> selectCase = cb.selectCase() final Expression<Object> selectCase = cb
.selectCase()
.when(cb.equal(targetInfo.get(TargetInfo_.installedDistributionSet).get(DistributionSet_.id), .when(cb.equal(targetInfo.get(TargetInfo_.installedDistributionSet).get(DistributionSet_.id),
orderByDistributionId), 1) orderByDistributionId), 1)
.when(cb.equal(targetRoot.get(Target_.assignedDistributionSet).get(DistributionSet_.id), .when(cb.equal(targetRoot.get(Target_.assignedDistributionSet).get(DistributionSet_.id),
orderByDistributionId), 2) orderByDistributionId), 2).otherwise(100);
.otherwise(100);
// multiselect statement order by the select case and controllerId // multiselect statement order by the select case and controllerId
query.distinct(true); query.distinct(true);
// build the specifications and then to predicates necessary by the // build the specifications and then to predicates necessary by the
// given filters // given filters
final Predicate[] specificationsForMultiSelect = specificationsToPredicate( final Predicate[] specificationsForMultiSelect = specificationsToPredicate(
buildSpecificationList(filterByStatus, filterBySearchText, filterByDistributionId, buildSpecificationList(filterByStatus, filterBySearchText, filterByDistributionId,
selectTargetWithNoTag, true, filterByTagNames), selectTargetWithNoTag, true, filterByTagNames), targetRoot, query, cb);
targetRoot, query, cb);
// if we have some predicates then add it to the where clause of the // if we have some predicates then add it to the where clause of the
// multiselect // multiselect
@@ -828,8 +826,9 @@ public class TargetManagement {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<TargetIdName> query = cb.createQuery(TargetIdName.class); final CriteriaQuery<TargetIdName> query = cb.createQuery(TargetIdName.class);
final Root<Target> targetRoot = query.from(Target.class); final Root<Target> targetRoot = query.from(Target.class);
return entityManager.createQuery(query.multiselect(targetRoot.get(Target_.id), return entityManager.createQuery(
targetRoot.get(Target_.controllerId), targetRoot.get(Target_.name))).getResultList(); query.multiselect(targetRoot.get(Target_.id), targetRoot.get(Target_.controllerId),
targetRoot.get(Target_.name))).getResultList();
} }
@@ -871,8 +870,7 @@ public class TargetManagement {
final Predicate[] specificationsForMultiSelect = specificationsToPredicate( final Predicate[] specificationsForMultiSelect = specificationsToPredicate(
buildSpecificationList(filterByStatus, filterBySearchText, filterByDistributionId, buildSpecificationList(filterByStatus, filterBySearchText, filterByDistributionId,
selectTargetWithNoTag, false, filterByTagNames), selectTargetWithNoTag, false, filterByTagNames), targetRoot, multiselect, cb);
targetRoot, multiselect, cb);
// if we have some predicates then add it to the where clause of the // if we have some predicates then add it to the where clause of the
// multiselect // multiselect
@@ -905,8 +903,7 @@ public class TargetManagement {
targetRoot.get(Target_.controllerId), targetRoot.get(Target_.name), targetRoot.get(Target_.controllerId), targetRoot.get(Target_.name),
targetRoot.get(pageRequest.getSort().iterator().next().getProperty())); targetRoot.get(pageRequest.getSort().iterator().next().getProperty()));
final Specification<Target> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class, final Specification<Target> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class);
entityManager);
final List<Specification<Target>> specList = new ArrayList<>(); final List<Specification<Target>> specList = new ArrayList<>();
specList.add(spec); specList.add(spec);
@@ -1003,8 +1000,7 @@ public class TargetManagement {
* to be created. * to be created.
* @return the created {@link Target}s * @return the created {@link Target}s
* *
* @throws {@link * @throws {@link EntityAlreadyExistsException} of one of the given targets
* EntityAlreadyExistsException} of one of the given targets
* already exist. * already exist.
*/ */
@Modifying @Modifying
@@ -1012,8 +1008,8 @@ public class TargetManagement {
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
public List<Target> createTargets(@NotNull final List<Target> targets) { public List<Target> createTargets(@NotNull final List<Target> targets) {
if (targetRepository.countByControllerIdIn( if (targetRepository.countByControllerIdIn(targets.stream().map(target -> target.getControllerId())
targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList())) > 0) { .collect(Collectors.toList())) > 0) {
throw new EntityAlreadyExistsException(); throw new EntityAlreadyExistsException();
} }
final List<Target> savedTargets = new ArrayList<>(); final List<Target> savedTargets = new ArrayList<>();
@@ -1045,8 +1041,8 @@ public class TargetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
public List<Target> createTargets(@NotNull final Collection<Target> targets, public List<Target> createTargets(@NotNull final Collection<Target> targets,
@NotNull final TargetUpdateStatus status, final long lastTargetQuery, final URI address) { @NotNull final TargetUpdateStatus status, final long lastTargetQuery, final URI address) {
if (targetRepository.countByControllerIdIn( if (targetRepository.countByControllerIdIn(targets.stream().map(target -> target.getControllerId())
targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList())) > 0) { .collect(Collectors.toList())) > 0) {
throw new EntityAlreadyExistsException(); throw new EntityAlreadyExistsException();
} }
final List<Target> savedTargets = new ArrayList<>(); final List<Target> savedTargets = new ArrayList<>();
@@ -1080,8 +1076,7 @@ public class TargetManagement {
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Long countTargetByTargetFilterQuery(@NotNull final TargetFilterQuery targetFilterQuery) { public Long countTargetByTargetFilterQuery(@NotNull final TargetFilterQuery targetFilterQuery) {
final Specification<Target> specs = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class, final Specification<Target> specs = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class);
entityManager);
return targetRepository.count(specs); return targetRepository.count(specs);
} }
@@ -1094,7 +1089,7 @@ public class TargetManagement {
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Long countTargetByTargetFilterQuery(@NotNull final String targetFilterQuery) { public Long countTargetByTargetFilterQuery(@NotNull final String targetFilterQuery) {
final Specification<Target> specs = RSQLUtility.parse(targetFilterQuery, TargetFields.class, entityManager); final Specification<Target> specs = RSQLUtility.parse(targetFilterQuery, TargetFields.class);
return targetRepository.count(specs); return targetRepository.count(specs);
} }

View File

@@ -10,7 +10,6 @@ package org.eclipse.hawkbit.repository.rsql;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@@ -19,18 +18,13 @@ import java.util.stream.Collectors;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.MapJoin;
import javax.persistence.criteria.Path; import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute.PersistentAttributeType;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Metamodel;
import javax.persistence.metamodel.PluralAttribute;
import org.eclipse.hawkbit.repository.FieldNameProvider; import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.FieldValueConverter; import org.eclipse.hawkbit.repository.FieldValueConverter;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
@@ -104,20 +98,18 @@ public final class RSQLUtility {
* if the RSQL syntax is wrong * if the RSQL syntax is wrong
*/ */
public static <A extends Enum<A> & FieldNameProvider, T> Specification<T> parse(final String rsql, public static <A extends Enum<A> & FieldNameProvider, T> Specification<T> parse(final String rsql,
final Class<A> fieldNameProvider, final EntityManager entityManager) { final Class<A> fieldNameProvider) {
return new RSQLSpecification<>(rsql, fieldNameProvider, entityManager); return new RSQLSpecification<>(rsql, fieldNameProvider);
} }
private static final class RSQLSpecification<A extends Enum<A> & FieldNameProvider, T> implements Specification<T> { private static final class RSQLSpecification<A extends Enum<A> & FieldNameProvider, T> implements Specification<T> {
private final String rsql; private final String rsql;
private final Class<A> enumType; private final Class<A> enumType;
private final EntityManager entityManager;
private RSQLSpecification(final String rsql, final Class<A> enumType, final EntityManager entityManager) { private RSQLSpecification(final String rsql, final Class<A> enumType) {
this.rsql = rsql; this.rsql = rsql;
this.enumType = enumType; this.enumType = enumType;
this.entityManager = entityManager;
} }
/* /*
@@ -141,8 +133,7 @@ public final class RSQLUtility {
throw new RSQLParameterSyntaxException(e); throw new RSQLParameterSyntaxException(e);
} }
final JpqQueryRSQLVisitor<A, T> jpqQueryRSQLVisitor = new JpqQueryRSQLVisitor<>(root, cb, enumType, final JpqQueryRSQLVisitor<A, T> jpqQueryRSQLVisitor = new JpqQueryRSQLVisitor<>(root, cb, enumType);
entityManager);
final List<Predicate> accept = rootNode.<List<Predicate>, String> accept(jpqQueryRSQLVisitor); final List<Predicate> accept = rootNode.<List<Predicate>, String> accept(jpqQueryRSQLVisitor);
if (accept != null && !accept.isEmpty()) { if (accept != null && !accept.isEmpty()) {
@@ -169,36 +160,14 @@ public final class RSQLUtility {
RSQLVisitor<List<Predicate>, String> { RSQLVisitor<List<Predicate>, String> {
public static final Character LIKE_WILDCARD = '*'; public static final Character LIKE_WILDCARD = '*';
static {
/**
* Property mapping are done in FieldNameProvider like
* TargetFields,SoftwareModuleFields etc.
*
* In addition to this mapping in PropertyMapper are done if we want
* to drill down on entity .
*
* For example : Drill down on distribution set of target entity are
* done by adding the mappings in PropertyMapper as below.
*
* User can now use assignedds.name and assignedds.version
*
*/
PropertyMapper.addNewMapping(Target.class, "assignedds", "assignedDistributionSet");
PropertyMapper.addNewMapping(DistributionSet.class, "name", "name");
PropertyMapper.addNewMapping(DistributionSet.class, "version", "version");
}
private final Root<T> root; private final Root<T> root;
private final CriteriaBuilder cb; private final CriteriaBuilder cb;
private final Class<A> enumType; private final Class<A> enumType;
private final EntityManager entityManager;
private JpqQueryRSQLVisitor(final Root<T> root, final CriteriaBuilder cb, final Class<A> enumType, private JpqQueryRSQLVisitor(final Root<T> root, final CriteriaBuilder cb, final Class<A> enumType) {
final EntityManager entityManager) {
this.root = root; this.root = root;
this.cb = cb; this.cb = cb;
this.enumType = enumType; this.enumType = enumType;
this.entityManager = entityManager;
} }
@Override @Override
@@ -219,134 +188,146 @@ public final class RSQLUtility {
return toSingleList(cb.conjunction()); return toSingleList(cb.conjunction());
} }
private static <T> boolean isItAssociationType(final String property, final ManagedType<T> classMetadata) { private List<Predicate> toSingleList(final Predicate predicate) {
return classMetadata.getAttribute(property).isAssociation(); return Collections.singletonList(predicate);
} }
private static <T> Class<?> getPropertyType(final String property, final ManagedType<T> classMetadata) { private String getAndValidatePropertyFieldName(final A propertyEnum, final ComparisonNode node) {
Class<?> propertyType; String finalProperty = propertyEnum.getFieldName();
if (classMetadata.getAttribute(property).isCollection()) { final String[] graph = node.getSelector().split("\\" + FieldNameProvider.SUB_ATTRIBUTE_SEPERATOR);
propertyType = ((PluralAttribute) classMetadata.getAttribute(property)).getBindableJavaType();
} else { validateMapParamter(propertyEnum, node, graph);
propertyType = classMetadata.getAttribute(property).getJavaType();
// sub entity need minium 1 dot
if (!propertyEnum.getSubEntityAttributes().isEmpty()) {
if (graph.length < 2) {
throw createRSQLParameterUnsupportedException(node);
} }
return propertyType;
} }
private static ManagedType<?> getClassMetaData(final String property, final ManagedType classMetadata, for (int i = 1; i < graph.length; i++) {
final Metamodel metaModel) { final String propertyField = graph[i];
if (isItAssociationType(property, classMetadata)) { finalProperty += FieldNameProvider.SUB_ATTRIBUTE_SEPERATOR + propertyField;
final Class<?> associationType = getPropertyType(property, classMetadata);
return metaModel.managedType(associationType); // the key of map is not in the graph
} else { if (propertyEnum.isMap() && graph.length == (i + 1)) {
if (isItEmbeddedType(property, classMetadata)) { continue;
final Class<?> embeddedType = getPropertyType(property, classMetadata);
return metaModel.managedType(embeddedType);
}
}
return classMetadata;
} }
private static <T> boolean isItEmbeddedType(final String property, final ManagedType<T> classMetadata) { if (!propertyEnum.containsSubEntityAttribute(propertyField)) {
return classMetadata.getAttribute(property).getPersistentAttributeType() == PersistentAttributeType.EMBEDDED; throw createRSQLParameterUnsupportedException(node);
}
} }
private String validatePropertyFieldName(final String propertyFieldName, final boolean isDefinedInEnum,
ManagedType classMetadata, final Metamodel metaModel, final ComparisonNode node) {
String finalProperty = propertyFieldName;
final String[] graph = propertyFieldName.split("\\.");
for (String property : graph) {
if (!isDefinedInEnum && PropertyMapper.getAllowedcolmns().containsKey(classMetadata.getJavaType())) {
if (PropertyMapper.getAllowedcolmns().get(classMetadata.getJavaType()).get(property) != null) {
final String mappedValue = PropertyMapper.getAllowedcolmns().get(classMetadata.getJavaType())
.get(property);
finalProperty = finalProperty.replace(property, mappedValue);
property = mappedValue;
} else {
throw new RSQLParameterUnsupportedFieldException("The given search parameter field {"
+ node.getSelector() + "} does not exist, must be one of the following fields {"
+ getExpectedFieldList() + "}", new Exception());
}
}
classMetadata = getClassMetaData(property, classMetadata, metaModel);
}
return finalProperty; return finalProperty;
} }
private Path<Object> getFieldPath(final String finalProperty) { private void validateMapParamter(final A propertyEnum, final ComparisonNode node, final String[] graph) {
if (!propertyEnum.isMap()) {
return;
}
if (!propertyEnum.getSubEntityAttributes().isEmpty()) {
throw new UnsupportedOperationException("Currently subentity attributes for maps are not supported");
}
// enum.key
final int minAttributeForMap = 2;
if (graph.length != minAttributeForMap) {
throw new RSQLParameterUnsupportedFieldException("The syntax of the given map search parameter field {"
+ node.getSelector() + "} is wrong. Syntax is: fieldname.keyname", new Exception());
}
}
private RSQLParameterUnsupportedFieldException createRSQLParameterUnsupportedException(final ComparisonNode node) {
return new RSQLParameterUnsupportedFieldException("The given search parameter field {" + node.getSelector()
+ "} does not exist, must be one of the following fields {" + getExpectedFieldList() + "}",
new Exception());
}
private Path<Object> getFieldPath(final A enumField, final String finalProperty) {
Path<Object> fieldPath = null; Path<Object> fieldPath = null;
final String[] split = finalProperty.split("\\."); final String[] split = finalProperty.split("\\" + FieldNameProvider.SUB_ATTRIBUTE_SEPERATOR);
if (split.length == 0) { if (split.length == 0) {
fieldPath = root.get(split[0]); return root.get(split[0]);
} else { }
for (final String fieldNameSplit : split) {
// hibernate workaround because cannot get attribute of an for (int i = 0; i < split.length; i++) {
// PluralAttributePath, needs final boolean isMapKeyField = enumField.isMap() && i == (split.length - 1);
// an implicit join. if (isMapKeyField) {
// https://hibernate.atlassian.net/browse/HHH-7892 return fieldPath;
if (fieldPath == null && root.get(fieldNameSplit) != null }
&& Collection.class.isAssignableFrom(root.get(fieldNameSplit).getJavaType())) {
fieldPath = root.join(fieldNameSplit); final String fieldNameSplit = split[i];
} else {
fieldPath = (fieldPath != null) ? fieldPath.get(fieldNameSplit) : root.get(fieldNameSplit); fieldPath = (fieldPath != null) ? fieldPath.get(fieldNameSplit) : root.get(fieldNameSplit);
} }
}
}
return fieldPath; return fieldPath;
} }
@Override @Override
public List<Predicate> visit(final ComparisonNode node, final String param) { public List<Predicate> visit(final ComparisonNode node, final String param) {
final Metamodel metaModel = entityManager.getMetamodel();
final ManagedType<?> classMetadata = metaModel.managedType(root.getJavaType());
String propertyFieldName = null;
Boolean isDefinedInEnum = Boolean.FALSE;
A fieldName = null; A fieldName = null;
try { try {
/** fieldName = getFieldEnumByName(node);
* Get the property mapping from FieldNameProvider .If not
* available check in PropertyMapping.If not found throw
* RSQLParameterUnsupportedFieldException.
*/
fieldName = getFieldIdentifierByName(node);
propertyFieldName = fieldName.getFieldName();
isDefinedInEnum = Boolean.TRUE;
} catch (final IllegalArgumentException e) { } catch (final IllegalArgumentException e) {
if (PropertyMapper.getAllowedcolmns().containsKey(classMetadata.getJavaType())) {
propertyFieldName = node.getSelector();
} else {
throw new RSQLParameterUnsupportedFieldException("The given search parameter field {" throw new RSQLParameterUnsupportedFieldException("The given search parameter field {"
+ node.getSelector() + node.getSelector()
+ "} does not exist, must be one of the following fields {" + "} does not exist, must be one of the following fields {"
+ Arrays.stream(enumType.getEnumConstants()).map(v -> v.name().toLowerCase()) + Arrays.stream(enumType.getEnumConstants()).map(v -> v.name().toLowerCase())
.collect(Collectors.toList()) + "}", e); .collect(Collectors.toList()) + "}", e);
} }
} final String finalProperty = getAndValidatePropertyFieldName(fieldName, node);
final String finalProperty = validatePropertyFieldName(propertyFieldName, isDefinedInEnum, classMetadata,
metaModel, node);
final List<String> values = node.getArguments(); final List<String> values = node.getArguments();
final List<Object> transformedValue = new ArrayList<>(); final List<Object> transformedValue = new ArrayList<>();
final Path<Object> fieldPath = getFieldPath(finalProperty); final Path<Object> fieldPath = getFieldPath(fieldName, finalProperty);
for (final String value : values) { for (final String value : values) {
transformedValue.add(convertValueIfNecessary(node, fieldName, value, fieldPath)); transformedValue.add(convertValueIfNecessary(node, fieldName, value, fieldPath));
} }
return mapToPredicate(node, fieldPath, node.getArguments(), transformedValue); return mapToPredicate(node, fieldPath, node.getArguments(), transformedValue, fieldName);
} }
private List<String> getExpectedFieldList() { private List<String> getExpectedFieldList() {
final List<String> expectedFieldList = Arrays.stream(enumType.getEnumConstants()) final List<String> expectedFieldList = Arrays.stream(enumType.getEnumConstants())
.map(v -> v.name().toLowerCase()).collect(Collectors.toList()); .filter(enumField -> enumField.getSubEntityAttributes().isEmpty()).map(enumField -> {
expectedFieldList.add("assignedds.name"); final String enumFieldName = enumField.name().toLowerCase();
expectedFieldList.add("assignedds.version");
if (enumField.isMap()) {
return enumFieldName + FieldNameProvider.SUB_ATTRIBUTE_SEPERATOR + "keyName";
}
return enumFieldName;
}).collect(Collectors.toList());
final List<String> expectedSubFieldList = Arrays
.stream(enumType.getEnumConstants())
.filter(enumField -> !enumField.getSubEntityAttributes().isEmpty())
.flatMap(
enumField -> {
final List<String> subEntity = enumField
.getSubEntityAttributes()
.stream()
.map(fieldName -> enumField.name().toLowerCase()
+ FieldNameProvider.SUB_ATTRIBUTE_SEPERATOR + fieldName)
.collect(Collectors.toList());
return subEntity.stream();
}).collect(Collectors.toList());
expectedFieldList.addAll(expectedSubFieldList);
return expectedFieldList; return expectedFieldList;
} }
private A getFieldIdentifierByName(final ComparisonNode node) { private A getFieldEnumByName(final ComparisonNode node) {
final String enumName = node.getSelector().toUpperCase(); String enumName = node.getSelector();
final String[] graph = enumName.split("\\" + FieldNameProvider.SUB_ATTRIBUTE_SEPERATOR);
if (graph.length != 0) {
enumName = graph[0];
}
LOGGER.debug("get fieldidentifier by name {} of enum type {}", enumName, enumType); LOGGER.debug("get fieldidentifier by name {} of enum type {}", enumName, enumType);
return Enum.valueOf(enumType, enumName); return Enum.valueOf(enumType, enumName.toUpperCase());
} }
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
@@ -400,58 +381,86 @@ public final class RSQLUtility {
} }
} }
private List<Predicate> mapToPredicate(final ComparisonNode node, final Path<Object> fieldPath, private List<Predicate> mapToPredicate(final ComparisonNode node, Path<Object> fieldPath,
final List<String> values, final List<Object> transformedValues) { final List<String> values, final List<Object> transformedValues, final A enumField) {
// only 'equal' and 'notEqual' can handle transformed value like // only 'equal' and 'notEqual' can handle transformed value like
// enums. The JPA API // enums. The JPA API
// cannot handle object types for greaterThan etc methods. // cannot handle object types for greaterThan etc methods.
final Object transformedValue = transformedValues.get(0); final Object transformedValue = transformedValues.get(0);
final String value = values.get(0); final String value = values.get(0);
final List<Predicate> singleList; final List<Predicate> singleList = new ArrayList<>();
final Predicate mapPredicate = mapToMapPredicate(node, fieldPath, enumField);
if (mapPredicate != null) {
singleList.add(mapPredicate);
}
fieldPath = getMapValueFieldPath(enumField, fieldPath);
switch (node.getOperator().getSymbol()) { switch (node.getOperator().getSymbol()) {
case "=li=": case "=li=":
singleList = toSingleList(cb.like(cb.upper(pathOfString(fieldPath)), transformedValue.toString() singleList.add(cb.like(cb.upper(pathOfString(fieldPath)), transformedValue.toString().toUpperCase()));
.toUpperCase()));
break; break;
case "==": case "==":
singleList = getEqualToPredicate(transformedValue, fieldPath); singleList.add(getEqualToPredicate(transformedValue, fieldPath));
break; break;
case "!=": case "!=":
singleList = toSingleList(cb.notEqual(fieldPath, transformedValue)); singleList.add(cb.notEqual(fieldPath, transformedValue));
break; break;
case "=gt=": case "=gt=":
singleList = toSingleList(cb.greaterThan(pathOfString(fieldPath), value)); singleList.add(cb.greaterThan(pathOfString(fieldPath), value));
break; break;
case "=ge=": case "=ge=":
singleList = toSingleList(cb.greaterThanOrEqualTo(pathOfString(fieldPath), value)); singleList.add(cb.greaterThanOrEqualTo(pathOfString(fieldPath), value));
break; break;
case "=lt=": case "=lt=":
singleList = toSingleList(cb.lessThan(pathOfString(fieldPath), value)); singleList.add(cb.lessThan(pathOfString(fieldPath), value));
break; break;
case "=le=": case "=le=":
singleList = toSingleList(cb.lessThanOrEqualTo(pathOfString(fieldPath), value)); singleList.add(cb.lessThanOrEqualTo(pathOfString(fieldPath), value));
break; break;
case "=in=": case "=in=":
singleList = toSingleList(fieldPath.in(transformedValues)); singleList.add(fieldPath.in(transformedValues));
break; break;
case "=out=": case "=out=":
singleList = toSingleList(cb.not(fieldPath.in(transformedValues))); singleList.add(cb.not(fieldPath.in(transformedValues)));
break; break;
default: default:
LOGGER.info("operator symbol {} is either not supported or not implemented"); LOGGER.info("operator symbol {} is either not supported or not implemented");
singleList = Collections.emptyList();
} }
return singleList; return Collections.unmodifiableList(singleList);
} }
private List<Predicate> getEqualToPredicate(final Object transformedValue, final Path<Object> fieldPath) { /**
* @param enumField
* @return
*/
private Path<Object> getMapValueFieldPath(final A enumField, final Path<Object> fieldPath) {
if (!enumField.isMap() || enumField.getValueFieldName() == null) {
return fieldPath;
}
return fieldPath.get(enumField.getValueFieldName());
}
private Predicate mapToMapPredicate(final ComparisonNode node, final Path<Object> fieldPath, final A enumField) {
if (!enumField.isMap()) {
return null;
}
final String[] graph = node.getSelector().split("\\" + FieldNameProvider.SUB_ATTRIBUTE_SEPERATOR);
final String keyValue = graph[graph.length - 1];
if (fieldPath instanceof MapJoin) {
return cb.equal(((MapJoin) fieldPath).key(), keyValue);
}
return cb.equal(fieldPath.get(enumField.getKeyFieldName()), keyValue);
}
private Predicate getEqualToPredicate(final Object transformedValue, final Path<Object> fieldPath) {
if (transformedValue instanceof String) { if (transformedValue instanceof String) {
final String preFormattedValue = ((String) transformedValue).replace(LIKE_WILDCARD, '%'); final String preFormattedValue = ((String) transformedValue).replace(LIKE_WILDCARD, '%');
return toSingleList(cb.like(cb.upper(pathOfString(fieldPath)), preFormattedValue.toString() return cb.like(cb.upper(pathOfString(fieldPath)), preFormattedValue.toString().toUpperCase());
.toUpperCase()));
} else {
return toSingleList(cb.equal(fieldPath, transformedValue));
} }
return cb.equal(fieldPath, transformedValue);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -473,8 +482,5 @@ public final class RSQLUtility {
return childs; return childs;
} }
private static List<Predicate> toSingleList(final Predicate p) {
return Collections.singletonList(p);
}
} }
} }

View File

@@ -92,8 +92,6 @@ public class DistributionSetResource {
@Autowired @Autowired
private DistributionSetManagement distributionSetManagement; private DistributionSetManagement distributionSetManagement;
@Autowired
private EntityManager entityManager;
/** /**
* Handles the GET request of retrieving all {@link DistributionSet}s within * Handles the GET request of retrieving all {@link DistributionSet}s within
@@ -130,7 +128,7 @@ public class DistributionSetResource {
final Page<DistributionSet> findDsPage; final Page<DistributionSet> findDsPage;
if (rsqlParam != null) { if (rsqlParam != null) {
findDsPage = distributionSetManagement.findDistributionSetsAll( findDsPage = distributionSetManagement.findDistributionSetsAll(
RSQLUtility.parse(rsqlParam, DistributionSetFields.class, entityManager), pageable, false); RSQLUtility.parse(rsqlParam, DistributionSetFields.class), pageable, false);
} else { } else {
findDsPage = distributionSetManagement.findDistributionSetsAll(pageable, false, null); findDsPage = distributionSetManagement.findDistributionSetsAll(pageable, false, null);
} }
@@ -281,7 +279,7 @@ public class DistributionSetResource {
final Page<Target> targetsAssignedDS; final Page<Target> targetsAssignedDS;
if (rsqlParam != null) { if (rsqlParam != null) {
targetsAssignedDS = targetManagement.findTargetByAssignedDistributionSet(distributionSetId, targetsAssignedDS = targetManagement.findTargetByAssignedDistributionSet(distributionSetId,
RSQLUtility.parse(rsqlParam, TargetFields.class, entityManager), pageable); RSQLUtility.parse(rsqlParam, TargetFields.class), pageable);
} else { } else {
targetsAssignedDS = targetManagement.findTargetByAssignedDistributionSet(distributionSetId, pageable); targetsAssignedDS = targetManagement.findTargetByAssignedDistributionSet(distributionSetId, pageable);
} }
@@ -331,7 +329,7 @@ public class DistributionSetResource {
final Page<Target> targetsInstalledDS; final Page<Target> targetsInstalledDS;
if (rsqlParam != null) { if (rsqlParam != null) {
targetsInstalledDS = targetManagement.findTargetByInstalledDistributionSet(distributionSetId, targetsInstalledDS = targetManagement.findTargetByInstalledDistributionSet(distributionSetId,
RSQLUtility.parse(rsqlParam, TargetFields.class, entityManager), pageable); RSQLUtility.parse(rsqlParam, TargetFields.class), pageable);
} else { } else {
targetsInstalledDS = targetManagement.findTargetByInstalledDistributionSet(distributionSetId, pageable); targetsInstalledDS = targetManagement.findTargetByInstalledDistributionSet(distributionSetId, pageable);
} }
@@ -410,7 +408,7 @@ public class DistributionSetResource {
if (rsqlParam != null) { if (rsqlParam != null) {
metaDataPage = distributionSetManagement.findDistributionSetMetadataByDistributionSetId(distributionSetId, metaDataPage = distributionSetManagement.findDistributionSetMetadataByDistributionSetId(distributionSetId,
RSQLUtility.parse(rsqlParam, DistributionSetMetadataFields.class, entityManager), pageable); RSQLUtility.parse(rsqlParam, DistributionSetMetadataFields.class), pageable);
} else { } else {
metaDataPage = distributionSetManagement.findDistributionSetMetadataByDistributionSetId(distributionSetId, metaDataPage = distributionSetManagement.findDistributionSetMetadataByDistributionSetId(distributionSetId,
pageable); pageable);

View File

@@ -60,8 +60,6 @@ public class DistributionSetTagResource {
@Autowired @Autowired
private DistributionSetManagement distributionSetManagement; private DistributionSetManagement distributionSetManagement;
@Autowired
private EntityManager entityManager;
/** /**
* Handles the GET request of retrieving all DistributionSet tags. * Handles the GET request of retrieving all DistributionSet tags.
@@ -103,7 +101,7 @@ public class DistributionSetTagResource {
} else { } else {
final Page<DistributionSetTag> findTargetPage = tagManagement.findAllDistributionSetTags( final Page<DistributionSetTag> findTargetPage = tagManagement.findAllDistributionSetTags(
RSQLUtility.parse(rsqlParam, TagFields.class, entityManager), pageable); RSQLUtility.parse(rsqlParam, TagFields.class), pageable);
countTargetsAll = findTargetPage.getTotalElements(); countTargetsAll = findTargetPage.getTotalElements();
findTargetsAll = findTargetPage; findTargetsAll = findTargetPage;

View File

@@ -62,9 +62,6 @@ public class DistributionSetTypeResource {
@Autowired @Autowired
private DistributionSetManagement distributionSetManagement; private DistributionSetManagement distributionSetManagement;
@Autowired
private EntityManager entityManager;
/** /**
* Handles the GET request of retrieving all {@link DistributionSetType}s * Handles the GET request of retrieving all {@link DistributionSetType}s
* within SP. * within SP.
@@ -105,7 +102,7 @@ public class DistributionSetTypeResource {
Long countModulesAll; Long countModulesAll;
if (rsqlParam != null) { if (rsqlParam != null) {
findModuleTypessAll = distributionSetManagement.findDistributionSetTypesByPredicate( findModuleTypessAll = distributionSetManagement.findDistributionSetTypesByPredicate(
RSQLUtility.parse(rsqlParam, DistributionSetTypeFields.class, entityManager), pageable); RSQLUtility.parse(rsqlParam, DistributionSetTypeFields.class), pageable);
countModulesAll = ((Page<DistributionSetType>) findModuleTypessAll).getTotalElements(); countModulesAll = ((Page<DistributionSetType>) findModuleTypessAll).getTotalElements();
} else { } else {
findModuleTypessAll = distributionSetManagement.findDistributionSetTypesAll(pageable); findModuleTypessAll = distributionSetManagement.findDistributionSetTypesAll(pageable);

View File

@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.rest.resource;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import javax.persistence.EntityManager;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@@ -75,9 +74,6 @@ public class SoftwareModuleResource {
@Autowired @Autowired
private SoftwareManagement softwareManagement; private SoftwareManagement softwareManagement;
@Autowired
private EntityManager entityManager;
/** /**
* Handles POST request for artifact upload. * Handles POST request for artifact upload.
* *
@@ -266,7 +262,7 @@ public class SoftwareModuleResource {
Long countModulesAll; Long countModulesAll;
if (rsqlParam != null) { if (rsqlParam != null) {
findModulesAll = softwareManagement.findSoftwareModulesByPredicate( findModulesAll = softwareManagement.findSoftwareModulesByPredicate(
RSQLUtility.parse(rsqlParam, SoftwareModuleFields.class, entityManager), pageable); RSQLUtility.parse(rsqlParam, SoftwareModuleFields.class), pageable);
countModulesAll = ((Page<SoftwareModule>) findModulesAll).getTotalElements(); countModulesAll = ((Page<SoftwareModule>) findModulesAll).getTotalElements();
} else { } else {
findModulesAll = softwareManagement.findSoftwareModulesAll(pageable); findModulesAll = softwareManagement.findSoftwareModulesAll(pageable);
@@ -307,8 +303,8 @@ public class SoftwareModuleResource {
* failure the JsonResponseExceptionHandler is handling the * failure the JsonResponseExceptionHandler is handling the
* response. * response.
*/ */
@RequestMapping(method = RequestMethod.POST, consumes = { "application/hal+json", @RequestMapping(method = RequestMethod.POST, consumes = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }, produces = {
MediaType.APPLICATION_JSON_VALUE }, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) "application/hal+json", MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<SoftwareModulesRest> createSoftwareModules( public ResponseEntity<SoftwareModulesRest> createSoftwareModules(
@RequestBody final List<SoftwareModuleRequestBodyPost> softwareModules) { @RequestBody final List<SoftwareModuleRequestBodyPost> softwareModules) {
LOG.debug("creating {} softwareModules", softwareModules.size()); LOG.debug("creating {} softwareModules", softwareModules.size());
@@ -387,7 +383,8 @@ public class SoftwareModuleResource {
*/ */
@RequestMapping(method = RequestMethod.GET, value = "/{softwareModuleId}/metadata", produces = { @RequestMapping(method = RequestMethod.GET, value = "/{softwareModuleId}/metadata", produces = {
MediaType.APPLICATION_JSON_VALUE, "application/hal+json" }) MediaType.APPLICATION_JSON_VALUE, "application/hal+json" })
public ResponseEntity<MetadataRestPageList> getMetadata(@PathVariable final Long softwareModuleId, public ResponseEntity<MetadataRestPageList> getMetadata(
@PathVariable final Long softwareModuleId,
@RequestParam(value = RestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = RestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam, @RequestParam(value = RestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = RestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam,
@RequestParam(value = RestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = RestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam, @RequestParam(value = RestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = RestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam,
@RequestParam(value = RestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam, @RequestParam(value = RestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam,
@@ -405,15 +402,13 @@ public class SoftwareModuleResource {
if (rsqlParam != null) { if (rsqlParam != null) {
metaDataPage = softwareManagement.findSoftwareModuleMetadataBySoftwareModuleId(softwareModuleId, metaDataPage = softwareManagement.findSoftwareModuleMetadataBySoftwareModuleId(softwareModuleId,
RSQLUtility.parse(rsqlParam, SoftwareModuleMetadataFields.class, entityManager), pageable); RSQLUtility.parse(rsqlParam, SoftwareModuleMetadataFields.class), pageable);
} else { } else {
metaDataPage = softwareManagement.findSoftwareModuleMetadataBySoftwareModuleId(softwareModuleId, pageable); metaDataPage = softwareManagement.findSoftwareModuleMetadataBySoftwareModuleId(softwareModuleId, pageable);
} }
return new ResponseEntity<>( return new ResponseEntity<>(new MetadataRestPageList(SoftwareModuleMapper.toResponseSwMetadata(metaDataPage
new MetadataRestPageList(SoftwareModuleMapper.toResponseSwMetadata(metaDataPage.getContent()), .getContent()), metaDataPage.getTotalElements()), HttpStatus.OK);
metaDataPage.getTotalElements()),
HttpStatus.OK);
} }
/** /**
@@ -426,8 +421,7 @@ public class SoftwareModuleResource {
* @return status OK if get request is successful with the value of the meta * @return status OK if get request is successful with the value of the meta
* data * data
*/ */
@RequestMapping(method = RequestMethod.GET, value = "/{softwareModuleId}/metadata/{metadataKey}", produces = { @RequestMapping(method = RequestMethod.GET, value = "/{softwareModuleId}/metadata/{metadataKey}", produces = { MediaType.APPLICATION_JSON_VALUE })
MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<MetadataRest> getMetadataValue(@PathVariable final Long softwareModuleId, public ResponseEntity<MetadataRest> getMetadataValue(@PathVariable final Long softwareModuleId,
@PathVariable final String metadataKey) { @PathVariable final String metadataKey) {
// check if distribution set exists otherwise throw exception // check if distribution set exists otherwise throw exception
@@ -487,8 +481,8 @@ public class SoftwareModuleResource {
* the created meta data * the created meta data
*/ */
@RequestMapping(method = RequestMethod.POST, value = "/{softwareModuleId}/metadata", consumes = { @RequestMapping(method = RequestMethod.POST, value = "/{softwareModuleId}/metadata", consumes = {
MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE, "application/hal+json" }, produces = { MediaType.APPLICATION_JSON_VALUE,
"application/hal+json" }, produces = { MediaType.APPLICATION_JSON_VALUE, "application/hal+json" }) "application/hal+json" })
public ResponseEntity<List<MetadataRest>> createMetadata(@PathVariable final Long softwareModuleId, public ResponseEntity<List<MetadataRest>> createMetadata(@PathVariable final Long softwareModuleId,
@RequestBody final List<MetadataRest> metadataRest) { @RequestBody final List<MetadataRest> metadataRest) {
// check if software module exists otherwise throw exception immediately // check if software module exists otherwise throw exception immediately
@@ -501,8 +495,7 @@ public class SoftwareModuleResource {
} }
private SoftwareModule findSoftwareModuleWithExceptionIfNotFound(final Long softwareModuleId, private SoftwareModule findSoftwareModuleWithExceptionIfNotFound(final Long softwareModuleId, final Long artifactId) {
final Long artifactId) {
final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId); final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId);
if (module == null) { if (module == null) {
throw new EntityNotFoundException("SoftwareModule with Id {" + softwareModuleId + "} does not exist"); throw new EntityNotFoundException("SoftwareModule with Id {" + softwareModuleId + "} does not exist");

View File

@@ -94,7 +94,7 @@ public class SoftwareModuleTypeResource {
Long countModulesAll; Long countModulesAll;
if (rsqlParam != null) { if (rsqlParam != null) {
findModuleTypessAll = softwareManagement.findSoftwareModuleTypesByPredicate( findModuleTypessAll = softwareManagement.findSoftwareModuleTypesByPredicate(
RSQLUtility.parse(rsqlParam, SoftwareModuleTypeFields.class, entityManager), pageable); RSQLUtility.parse(rsqlParam, SoftwareModuleTypeFields.class), pageable);
countModulesAll = ((Page<SoftwareModuleType>) findModuleTypessAll).getTotalElements(); countModulesAll = ((Page<SoftwareModuleType>) findModuleTypessAll).getTotalElements();
} else { } else {
findModuleTypessAll = softwareManagement.findSoftwareModuleTypesAll(pageable); findModuleTypessAll = softwareManagement.findSoftwareModuleTypesAll(pageable);

View File

@@ -135,7 +135,7 @@ public class TargetResource {
final Long countTargetsAll; final Long countTargetsAll;
if (rsqlParam != null) { if (rsqlParam != null) {
final Page<Target> findTargetPage = targetManagement.findTargetsAll( final Page<Target> findTargetPage = targetManagement.findTargetsAll(
RSQLUtility.parse(rsqlParam, TargetFields.class, entityManager), pageable); RSQLUtility.parse(rsqlParam, TargetFields.class), pageable);
countTargetsAll = findTargetPage.getTotalElements(); countTargetsAll = findTargetPage.getTotalElements();
findTargetsAll = findTargetPage; findTargetsAll = findTargetPage;
} else { } else {
@@ -284,7 +284,7 @@ public class TargetResource {
final Slice<Action> activeActions; final Slice<Action> activeActions;
final Long totalActionCount; final Long totalActionCount;
if (rsqlParam != null) { if (rsqlParam != null) {
final Specification<Action> parse = RSQLUtility.parse(rsqlParam, ActionFields.class, entityManager); final Specification<Action> parse = RSQLUtility.parse(rsqlParam, ActionFields.class);
activeActions = deploymentManagement.findActionsByTarget(parse, foundTarget, pageable); activeActions = deploymentManagement.findActionsByTarget(parse, foundTarget, pageable);
totalActionCount = deploymentManagement.countActionsByTarget(parse, foundTarget); totalActionCount = deploymentManagement.countActionsByTarget(parse, foundTarget);
} else { } else {

View File

@@ -103,7 +103,7 @@ public class TargetTagResource {
} else { } else {
final Page<TargetTag> findTargetPage = tagManagement.findAllTargetTags( final Page<TargetTag> findTargetPage = tagManagement.findAllTargetTags(
RSQLUtility.parse(rsqlParam, TagFields.class, entityManager), pageable); RSQLUtility.parse(rsqlParam, TagFields.class), pageable);
countTargetsAll = findTargetPage.getTotalElements(); countTargetsAll = findTargetPage.getTotalElements();
findTargetsAll = findTargetPage; findTargetsAll = findTargetPage;

View File

@@ -12,14 +12,12 @@ import static org.junit.Assert.fail;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset; import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression; import javax.persistence.criteria.Expression;
@@ -27,11 +25,11 @@ import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute; import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Metamodel;
import org.eclipse.hawkbit.repository.DistributionSetFields;
import org.eclipse.hawkbit.repository.FieldNameProvider; import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.SoftwareModuleFields; import org.eclipse.hawkbit.repository.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.rsql.RSQLParameterSyntaxException; import org.eclipse.hawkbit.repository.rsql.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.rsql.RSQLParameterUnsupportedFieldException; import org.eclipse.hawkbit.repository.rsql.RSQLParameterUnsupportedFieldException;
@@ -39,9 +37,7 @@ import org.eclipse.hawkbit.repository.rsql.RSQLUtility;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import ru.yandex.qatools.allure.annotations.Features; import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories; import ru.yandex.qatools.allure.annotations.Stories;
@@ -60,33 +56,87 @@ public class RSQLUtilityTest {
private CriteriaQuery<SoftwareModule> criteriaQueryMock; private CriteriaQuery<SoftwareModule> criteriaQueryMock;
@Mock @Mock
private CriteriaBuilder criteriaBuilderMock; private CriteriaBuilder criteriaBuilderMock;
@Mock
private EntityManager entityManager;
@Mock
private Metamodel metamodel;
@Mock
private ManagedType managedType;
@Mock @Mock
private Attribute attribute; private Attribute attribute;
@Test(expected = RSQLParameterSyntaxException.class) @Test
public void wrongRsqlSyntaxThrowSyntaxException() { public void wrongRsqlSyntaxThrowSyntaxException() {
final String wrongRSQL = "name==abc;d"; final String wrongRSQL = "name==abc;d";
when(entityManager.getMetamodel()).thenReturn(metamodel); try {
RSQLUtility.parse(wrongRSQL, SoftwareModuleFields.class, entityManager).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(wrongRSQL, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock); criteriaQueryMock, criteriaBuilderMock);
fail();
} catch (final RSQLParameterSyntaxException e) {
}
} }
@Test(expected = RSQLParameterUnsupportedFieldException.class) @Test
public void wrongFieldThrowUnsupportedFieldException() { public void wrongFieldThrowUnsupportedFieldException() {
final String wrongRSQL = "unknownField==abc"; final String wrongRSQL = "unknownField==abc";
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class); when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
doEntitySetup(SoftwareModule.class); try {
RSQLUtility.parse(wrongRSQL, SoftwareModuleFields.class, entityManager).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(wrongRSQL, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock); criteriaQueryMock, criteriaBuilderMock);
fail();
} catch (final RSQLParameterUnsupportedFieldException e) {
}
}
@Test
public void wrongRsqlMapSyntaxThrowSyntaxException() {
String wrongRSQL = TargetFields.ATTRIBUTE + "==abc";
try {
RSQLUtility.parse(wrongRSQL, TargetFields.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock,
criteriaBuilderMock);
fail();
} catch (final RSQLParameterUnsupportedFieldException e) {
}
wrongRSQL = TargetFields.ATTRIBUTE + ".unkwon.wrong==abc";
try {
RSQLUtility.parse(wrongRSQL, TargetFields.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock,
criteriaBuilderMock);
fail();
} catch (final RSQLParameterUnsupportedFieldException e) {
}
wrongRSQL = DistributionSetFields.METADATA + "==abc";
try {
RSQLUtility.parse(wrongRSQL, DistributionSetFields.class).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock);
fail();
} catch (final RSQLParameterUnsupportedFieldException e) {
}
}
@Test
public void wrongRsqlSubEntitySyntaxThrowSyntaxException() {
String wrongRSQL = TargetFields.ASSIGNEDDS + "==abc";
try {
RSQLUtility.parse(wrongRSQL, TargetFields.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock,
criteriaBuilderMock);
fail();
} catch (final RSQLParameterUnsupportedFieldException e) {
}
wrongRSQL = TargetFields.ASSIGNEDDS + ".unknownField==abc";
try {
RSQLUtility.parse(wrongRSQL, TargetFields.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock,
criteriaBuilderMock);
fail();
} catch (final RSQLParameterUnsupportedFieldException e) {
}
wrongRSQL = TargetFields.ASSIGNEDDS + ".unknownField.ToMuch==abc";
try {
RSQLUtility.parse(wrongRSQL, TargetFields.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock,
criteriaBuilderMock);
fail();
} catch (final RSQLParameterUnsupportedFieldException e) {
}
} }
@Test @Test
@@ -100,11 +150,9 @@ public class RSQLUtilityTest {
when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class))).thenReturn( when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class))).thenReturn(
mock(Predicate.class)); mock(Predicate.class));
doEntitySetup(SoftwareModule.class);
// test // test
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, entityManager).toPredicate( RSQLUtility.parse(correctRsql, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock,
baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock); criteriaQueryMock, criteriaBuilderMock);
// verfication // verfication
verify(criteriaBuilderMock, times(1)).and(any(Predicate.class)); verify(criteriaBuilderMock, times(1)).and(any(Predicate.class));
@@ -119,10 +167,9 @@ public class RSQLUtilityTest {
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class)); when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class))).thenReturn( when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class))).thenReturn(
mock(Predicate.class)); mock(Predicate.class));
doEntitySetup(SoftwareModule.class);
// test // test
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, entityManager).toPredicate( RSQLUtility.parse(correctRsql, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock,
baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock); criteriaQueryMock, criteriaBuilderMock);
// verfication // verfication
verify(criteriaBuilderMock, times(1)).and(any(Predicate.class)); verify(criteriaBuilderMock, times(1)).and(any(Predicate.class));
@@ -138,10 +185,9 @@ public class RSQLUtilityTest {
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class)); when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class))).thenReturn( when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class))).thenReturn(
mock(Predicate.class)); mock(Predicate.class));
doEntitySetup(SoftwareModule.class);
// test // test
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, entityManager).toPredicate( RSQLUtility.parse(correctRsql, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock,
baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock); criteriaQueryMock, criteriaBuilderMock);
// verfication // verfication
verify(criteriaBuilderMock, times(1)).and(any(Predicate.class)); verify(criteriaBuilderMock, times(1)).and(any(Predicate.class));
@@ -159,10 +205,9 @@ public class RSQLUtilityTest {
mock(Predicate.class)); mock(Predicate.class));
when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock)))).thenReturn( when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock)))).thenReturn(
pathOfString(baseSoftwareModuleRootMock)); pathOfString(baseSoftwareModuleRootMock));
doEntitySetup(SoftwareModule.class);
// test // test
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, entityManager).toPredicate( RSQLUtility.parse(correctRsql, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock,
baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock); criteriaQueryMock, criteriaBuilderMock);
// verfication // verfication
verify(criteriaBuilderMock, times(1)).and(any(Predicate.class)); verify(criteriaBuilderMock, times(1)).and(any(Predicate.class));
@@ -178,10 +223,9 @@ public class RSQLUtilityTest {
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) TestValueEnum.class); when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) TestValueEnum.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class)); when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
doEntitySetup(TestValueEnum.class);
// test // test
RSQLUtility.parse(correctRsql, TestFieldEnum.class, entityManager).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(correctRsql, TestFieldEnum.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock,
criteriaQueryMock, criteriaBuilderMock); criteriaBuilderMock);
// verfication // verfication
verify(criteriaBuilderMock, times(1)).and(any(Predicate.class)); verify(criteriaBuilderMock, times(1)).and(any(Predicate.class));
@@ -196,11 +240,9 @@ public class RSQLUtilityTest {
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) TestValueEnum.class); when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) TestValueEnum.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class)); when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
doEntitySetup(TestValueEnum.class);
try { try {
// test // test
RSQLUtility.parse(correctRsql, TestFieldEnum.class, entityManager).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(correctRsql, TestFieldEnum.class).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock); criteriaQueryMock, criteriaBuilderMock);
fail("missing RSQLParameterUnsupportedFieldException for wrong enum value"); fail("missing RSQLParameterUnsupportedFieldException for wrong enum value");
} catch (final RSQLParameterUnsupportedFieldException e) { } catch (final RSQLParameterUnsupportedFieldException e) {
@@ -208,22 +250,6 @@ public class RSQLUtilityTest {
} }
} }
private void doEntitySetup(final Class clasName) {
when(entityManager.getMetamodel()).thenReturn(metamodel);
when(metamodel.managedType(clasName)).thenReturn(managedType);
when(managedType.getJavaType()).thenReturn(clasName);
doAnswer(new Answer<Attribute>() {
@Override
public Attribute answer(final InvocationOnMock invocation) throws Throwable {
return attribute;
}
}).when(managedType).getAttribute(anyString());
when(attribute.isAssociation()).thenReturn(false);
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private <Y> Path<Y> pathOfString(final Path<?> path) { private <Y> Path<Y> pathOfString(final Path<?> path) {
return (Path<Y>) path; return (Path<Y>) path;