From 021a4da13dcce32eec4c742bd511b3ea99c99bd8 Mon Sep 17 00:00:00 2001 From: SirWayne Date: Fri, 22 Jan 2016 10:55:05 +0100 Subject: [PATCH] Create new filter options for maps - Add new filter property for target - Add new filter property for software module - Add new filter property for distribution set Signed-off-by: SirWayne --- .../repository/DistributionSetFields.java | 36 ++- .../hawkbit/repository/FieldNameProvider.java | 71 +++- .../repository/SoftwareModuleFields.java | 24 +- .../hawkbit/repository/TargetFields.java | 52 ++- .../hawkbit/repository/TargetManagement.java | 67 ++-- .../hawkbit/repository/rsql/RSQLUtility.java | 302 +++++++++--------- .../resource/DistributionSetResource.java | 10 +- .../resource/DistributionSetTagResource.java | 4 +- .../resource/DistributionSetTypeResource.java | 5 +- .../rest/resource/SoftwareModuleResource.java | 31 +- .../resource/SoftwareModuleTypeResource.java | 2 +- .../hawkbit/rest/resource/TargetResource.java | 4 +- .../rest/resource/TargetTagResource.java | 2 +- .../rest/resource/RSQLUtilityTest.java | 140 ++++---- 14 files changed, 466 insertions(+), 284 deletions(-) diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/DistributionSetFields.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/DistributionSetFields.java index 6935ed051..b6120157a 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/DistributionSetFields.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/DistributionSetFields.java @@ -36,16 +36,50 @@ public enum DistributionSetFields implements FieldNameProvider { /** * 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 String keyFieldName; + private String valueFieldName; private DistributionSetFields(final String fieldName) { + this(fieldName, null, null); + } + + private DistributionSetFields(final String fieldName, final String keyFieldName, final String valueFieldName) { this.fieldName = fieldName; + this.keyFieldName = keyFieldName; + this.valueFieldName = valueFieldName; + } + + @Override + public String getValueFieldName() { + return valueFieldName; + } + + @Override + public String getKeyFieldName() { + return keyFieldName; } @Override public String getFieldName() { return fieldName; } + } diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/FieldNameProvider.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/FieldNameProvider.java index f3f799c85..eef36f552 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/FieldNameProvider.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/FieldNameProvider.java @@ -8,18 +8,87 @@ */ 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 * which is used as string representation of the field, e.g. for sorting the * fields over REST. * */ -@FunctionalInterface public interface FieldNameProvider { + /** + * Seperator for the sub attributes + */ + public static final String SUB_ATTRIBUTE_SEPERATOR = "."; /** * @return the string representation of the underlying persistence field * name e.g. in case of sorting. Never {@code null}. */ String getFieldName(); + + default boolean containsSubEntityAttribute(final String propertyField) { + return FieldNameProvider.containsSubEntityAttribute(propertyField, getSubEntityAttributes()); + }; + + default List 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 property exists not exists + */ + static boolean containsSubEntityAttribute(final String propertyField, final List 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; + } + } diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleFields.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleFields.java index 0425f8c03..33fa638a2 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleFields.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleFields.java @@ -36,16 +36,38 @@ public enum SoftwareModuleFields implements FieldNameProvider { /** * The id field. */ - ID("id"); + ID("id"), + /** + * The metadata. + */ + METADATA("metadata", "key", "value"); private final String fieldName; + private String keyFieldName; + private String valueFieldName; private SoftwareModuleFields(final String fieldName) { + this(fieldName, null, null); + } + + private SoftwareModuleFields(final String fieldName, final String keyFieldName, final String valueFieldName) { this.fieldName = fieldName; + this.keyFieldName = keyFieldName; + this.valueFieldName = valueFieldName; } @Override public String getFieldName() { return fieldName; } + + @Override + public String getKeyFieldName() { + return keyFieldName; + } + + @Override + public String getValueFieldName() { + return valueFieldName; + } } diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/TargetFields.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/TargetFields.java index 1ffc04fc3..1b4daaf66 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/TargetFields.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/TargetFields.java @@ -8,15 +8,22 @@ */ 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 * e.g. for sorting etc. * - * - * - * */ public enum TargetFields implements FieldNameProvider { + + /** + * The controllerId field. + */ + ID("controllerId"), + /** * The name field. */ @@ -37,12 +44,49 @@ public enum TargetFields implements FieldNameProvider { /** * 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 List subEntityAttribues; + private boolean mapField; 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 subEntityAttribues) { this.fieldName = fieldName; + this.mapField = mapField; + this.subEntityAttribues = subEntityAttribues; + } + + @Override + public List getSubEntityAttributes() { + return subEntityAttribues; + } + + @Override + public boolean isMap() { + return mapField; } @Override diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java index f0047f6dd..fcc644d18 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java @@ -205,8 +205,7 @@ public class TargetManagement { @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) public Slice findTargetsAll(@NotNull final TargetFilterQuery targetFilterQuery, @NotNull final Pageable pageable) { - return findTargetsAll(RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class, entityManager), - pageable); + return findTargetsAll(RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class), pageable); } /** @@ -220,7 +219,7 @@ public class TargetManagement { */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) public Slice 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) public Page findTargetByAssignedDistributionSet(@NotNull final Long distributionSetID, final Specification spec, @NotNull final Pageable pageReq) { - return targetRepository.findAll((Specification) (root, query, cb) -> cb.and( - TargetSpecifications.hasAssignedDistributionSet(distributionSetID).toPredicate(root, query, cb), - spec.toPredicate(root, query, cb)), pageReq); + return targetRepository.findAll((Specification) (root, query, cb) -> cb.and(TargetSpecifications + .hasAssignedDistributionSet(distributionSetID).toPredicate(root, query, cb), spec.toPredicate(root, + query, cb)), pageReq); } /** @@ -390,9 +389,9 @@ public class TargetManagement { @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET) public Page findTargetByInstalledDistributionSet(final Long distributionSetId, final Specification spec, final Pageable pageable) { - return targetRepository.findAll((Specification) (root, query, cb) -> cb.and( - TargetSpecifications.hasInstalledDistributionSet(distributionSetId).toPredicate(root, query, cb), - spec.toPredicate(root, query, cb)), pageable); + return targetRepository.findAll((Specification) (root, query, cb) -> cb.and(TargetSpecifications + .hasInstalledDistributionSet(distributionSetId).toPredicate(root, query, cb), spec.toPredicate(root, + query, cb)), pageable); } /** @@ -487,8 +486,8 @@ public class TargetManagement { specList.add(TargetSpecifications.hasTargetUpdateStatus(status, fetch)); } if (installedOrAssignedDistributionSetId != null) { - specList.add( - TargetSpecifications.hasInstalledOrAssignedDistributionSet(installedOrAssignedDistributionSetId)); + specList.add(TargetSpecifications + .hasInstalledOrAssignedDistributionSet(installedOrAssignedDistributionSetId)); } if (!Strings.isNullOrEmpty(searchText)) { specList.add(TargetSpecifications.likeNameOrDescriptionOrIp(searchText)); @@ -560,8 +559,8 @@ public class TargetManagement { @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) public TargetTagAssigmentResult toggleTagAssignment(@NotEmpty final List targets, @NotNull final TargetTag tag) { - return toggleTagAssignment( - targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList()), tag.getName()); + return toggleTagAssignment(targets.stream().map(target -> target.getControllerId()) + .collect(Collectors.toList()), tag.getName()); } /** @@ -584,8 +583,8 @@ public class TargetManagement { @NotNull final String tagName) { final TargetTag tag = targetTagRepository.findByNameEquals(tagName); final List alreadyAssignedTargets = targetRepository.findByTagNameAndControllerIdIn(tagName, targetIds); - final List allTargets = targetRepository - .findAll(TargetSpecifications.byControllerIdWithStatusAndTagsInJoin(targetIds)); + final List allTargets = targetRepository.findAll(TargetSpecifications + .byControllerIdWithStatusAndTagsInJoin(targetIds)); // all are already assigned -> unassign 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 // able to order based on // this column, installed first,... - final Expression selectCase = cb.selectCase() + final Expression selectCase = cb + .selectCase() .when(cb.equal(targetInfo.get(TargetInfo_.installedDistributionSet).get(DistributionSet_.id), orderByDistributionId), 1) .when(cb.equal(targetRoot.get(Target_.assignedDistributionSet).get(DistributionSet_.id), - orderByDistributionId), 2) - .otherwise(100); + orderByDistributionId), 2).otherwise(100); // multiselect statement order by the select case and controllerId query.distinct(true); // build the specifications and then to predicates necessary by the // given filters final Predicate[] specificationsForMultiSelect = specificationsToPredicate( buildSpecificationList(filterByStatus, filterBySearchText, filterByDistributionId, - selectTargetWithNoTag, true, filterByTagNames), - targetRoot, query, cb); + selectTargetWithNoTag, true, filterByTagNames), targetRoot, query, cb); // if we have some predicates then add it to the where clause of the // multiselect @@ -828,8 +826,9 @@ public class TargetManagement { final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); final CriteriaQuery query = cb.createQuery(TargetIdName.class); final Root targetRoot = query.from(Target.class); - return entityManager.createQuery(query.multiselect(targetRoot.get(Target_.id), - targetRoot.get(Target_.controllerId), targetRoot.get(Target_.name))).getResultList(); + return entityManager.createQuery( + 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( buildSpecificationList(filterByStatus, filterBySearchText, filterByDistributionId, - selectTargetWithNoTag, false, filterByTagNames), - targetRoot, multiselect, cb); + selectTargetWithNoTag, false, filterByTagNames), targetRoot, multiselect, cb); // if we have some predicates then add it to the where clause of the // multiselect @@ -905,8 +903,7 @@ public class TargetManagement { targetRoot.get(Target_.controllerId), targetRoot.get(Target_.name), targetRoot.get(pageRequest.getSort().iterator().next().getProperty())); - final Specification spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class, - entityManager); + final Specification spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class); final List> specList = new ArrayList<>(); specList.add(spec); @@ -1003,17 +1000,16 @@ public class TargetManagement { * to be created. * @return the created {@link Target}s * - * @throws {@link - * EntityAlreadyExistsException} of one of the given targets - * already exist. + * @throws {@link EntityAlreadyExistsException} of one of the given targets + * already exist. */ @Modifying @Transactional @NotNull @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET) public List createTargets(@NotNull final List targets) { - if (targetRepository.countByControllerIdIn( - targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList())) > 0) { + if (targetRepository.countByControllerIdIn(targets.stream().map(target -> target.getControllerId()) + .collect(Collectors.toList())) > 0) { throw new EntityAlreadyExistsException(); } final List savedTargets = new ArrayList<>(); @@ -1045,8 +1041,8 @@ public class TargetManagement { @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET) public List createTargets(@NotNull final Collection targets, @NotNull final TargetUpdateStatus status, final long lastTargetQuery, final URI address) { - if (targetRepository.countByControllerIdIn( - targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList())) > 0) { + if (targetRepository.countByControllerIdIn(targets.stream().map(target -> target.getControllerId()) + .collect(Collectors.toList())) > 0) { throw new EntityAlreadyExistsException(); } final List savedTargets = new ArrayList<>(); @@ -1080,8 +1076,7 @@ public class TargetManagement { */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) public Long countTargetByTargetFilterQuery(@NotNull final TargetFilterQuery targetFilterQuery) { - final Specification specs = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class, - entityManager); + final Specification specs = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class); return targetRepository.count(specs); } @@ -1094,7 +1089,7 @@ public class TargetManagement { */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) public Long countTargetByTargetFilterQuery(@NotNull final String targetFilterQuery) { - final Specification specs = RSQLUtility.parse(targetFilterQuery, TargetFields.class, entityManager); + final Specification specs = RSQLUtility.parse(targetFilterQuery, TargetFields.class); return targetRepository.count(specs); } diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/rsql/RSQLUtility.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/rsql/RSQLUtility.java index da569a85e..0ef42fbf7 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/rsql/RSQLUtility.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/rsql/RSQLUtility.java @@ -10,7 +10,6 @@ package org.eclipse.hawkbit.repository.rsql; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; @@ -19,18 +18,13 @@ import java.util.stream.Collectors; import javax.persistence.EntityManager; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.MapJoin; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; 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.FieldValueConverter; -import org.eclipse.hawkbit.repository.model.DistributionSet; -import org.eclipse.hawkbit.repository.model.Target; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.jpa.domain.Specification; @@ -104,20 +98,18 @@ public final class RSQLUtility { * if the RSQL syntax is wrong */ public static & FieldNameProvider, T> Specification parse(final String rsql, - final Class fieldNameProvider, final EntityManager entityManager) { - return new RSQLSpecification<>(rsql, fieldNameProvider, entityManager); + final Class fieldNameProvider) { + return new RSQLSpecification<>(rsql, fieldNameProvider); } private static final class RSQLSpecification & FieldNameProvider, T> implements Specification { private final String rsql; private final Class enumType; - private final EntityManager entityManager; - private RSQLSpecification(final String rsql, final Class enumType, final EntityManager entityManager) { + private RSQLSpecification(final String rsql, final Class enumType) { this.rsql = rsql; this.enumType = enumType; - this.entityManager = entityManager; } /* @@ -141,8 +133,7 @@ public final class RSQLUtility { throw new RSQLParameterSyntaxException(e); } - final JpqQueryRSQLVisitor jpqQueryRSQLVisitor = new JpqQueryRSQLVisitor<>(root, cb, enumType, - entityManager); + final JpqQueryRSQLVisitor jpqQueryRSQLVisitor = new JpqQueryRSQLVisitor<>(root, cb, enumType); final List accept = rootNode., String> accept(jpqQueryRSQLVisitor); if (accept != null && !accept.isEmpty()) { @@ -169,36 +160,14 @@ public final class RSQLUtility { RSQLVisitor, String> { 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 root; private final CriteriaBuilder cb; private final Class enumType; - private final EntityManager entityManager; - private JpqQueryRSQLVisitor(final Root root, final CriteriaBuilder cb, final Class enumType, - final EntityManager entityManager) { + private JpqQueryRSQLVisitor(final Root root, final CriteriaBuilder cb, final Class enumType) { this.root = root; this.cb = cb; this.enumType = enumType; - this.entityManager = entityManager; } @Override @@ -219,134 +188,146 @@ public final class RSQLUtility { return toSingleList(cb.conjunction()); } - private static boolean isItAssociationType(final String property, final ManagedType classMetadata) { - return classMetadata.getAttribute(property).isAssociation(); + private List toSingleList(final Predicate predicate) { + return Collections.singletonList(predicate); } - private static Class getPropertyType(final String property, final ManagedType classMetadata) { - Class propertyType; - if (classMetadata.getAttribute(property).isCollection()) { - propertyType = ((PluralAttribute) classMetadata.getAttribute(property)).getBindableJavaType(); - } else { - propertyType = classMetadata.getAttribute(property).getJavaType(); - } - return propertyType; - } + private String getAndValidatePropertyFieldName(final A propertyEnum, final ComparisonNode node) { + String finalProperty = propertyEnum.getFieldName(); + final String[] graph = node.getSelector().split("\\" + FieldNameProvider.SUB_ATTRIBUTE_SEPERATOR); - private static ManagedType getClassMetaData(final String property, final ManagedType classMetadata, - final Metamodel metaModel) { - if (isItAssociationType(property, classMetadata)) { - final Class associationType = getPropertyType(property, classMetadata); - return metaModel.managedType(associationType); - } else { - if (isItEmbeddedType(property, classMetadata)) { - final Class embeddedType = getPropertyType(property, classMetadata); - return metaModel.managedType(embeddedType); + validateMapParamter(propertyEnum, node, graph); + + // sub entity need minium 1 dot + if (!propertyEnum.getSubEntityAttributes().isEmpty()) { + if (graph.length < 2) { + throw createRSQLParameterUnsupportedException(node); } } - return classMetadata; - } - private static boolean isItEmbeddedType(final String property, final ManagedType classMetadata) { - return classMetadata.getAttribute(property).getPersistentAttributeType() == PersistentAttributeType.EMBEDDED; - } + for (int i = 1; i < graph.length; i++) { + final String propertyField = graph[i]; + finalProperty += FieldNameProvider.SUB_ATTRIBUTE_SEPERATOR + propertyField; - 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()); - } + // the key of map is not in the graph + if (propertyEnum.isMap() && graph.length == (i + 1)) { + continue; + } + + if (!propertyEnum.containsSubEntityAttribute(propertyField)) { + throw createRSQLParameterUnsupportedException(node); } - classMetadata = getClassMetaData(property, classMetadata, metaModel); } + return finalProperty; } - private Path 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 getFieldPath(final A enumField, final String finalProperty) { Path fieldPath = null; - final String[] split = finalProperty.split("\\."); + final String[] split = finalProperty.split("\\" + FieldNameProvider.SUB_ATTRIBUTE_SEPERATOR); if (split.length == 0) { - fieldPath = root.get(split[0]); - } else { - for (final String fieldNameSplit : split) { - // hibernate workaround because cannot get attribute of an - // PluralAttributePath, needs - // an implicit join. - // https://hibernate.atlassian.net/browse/HHH-7892 - if (fieldPath == null && root.get(fieldNameSplit) != null - && Collection.class.isAssignableFrom(root.get(fieldNameSplit).getJavaType())) { - fieldPath = root.join(fieldNameSplit); - } else { - fieldPath = (fieldPath != null) ? fieldPath.get(fieldNameSplit) : root.get(fieldNameSplit); - } + return root.get(split[0]); + } + + for (int i = 0; i < split.length; i++) { + final boolean isMapKeyField = enumField.isMap() && i == (split.length - 1); + if (isMapKeyField) { + return fieldPath; } + + final String fieldNameSplit = split[i]; + fieldPath = (fieldPath != null) ? fieldPath.get(fieldNameSplit) : root.get(fieldNameSplit); } return fieldPath; } @Override public List 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; try { - /** - * 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; + fieldName = getFieldEnumByName(node); } catch (final IllegalArgumentException e) { - if (PropertyMapper.getAllowedcolmns().containsKey(classMetadata.getJavaType())) { - propertyFieldName = node.getSelector(); - } else { - throw new RSQLParameterUnsupportedFieldException("The given search parameter field {" - + node.getSelector() - + "} does not exist, must be one of the following fields {" - + Arrays.stream(enumType.getEnumConstants()).map(v -> v.name().toLowerCase()) - .collect(Collectors.toList()) + "}", e); - } + throw new RSQLParameterUnsupportedFieldException("The given search parameter field {" + + node.getSelector() + + "} does not exist, must be one of the following fields {" + + Arrays.stream(enumType.getEnumConstants()).map(v -> v.name().toLowerCase()) + .collect(Collectors.toList()) + "}", e); + } - final String finalProperty = validatePropertyFieldName(propertyFieldName, isDefinedInEnum, classMetadata, - metaModel, node); + final String finalProperty = getAndValidatePropertyFieldName(fieldName, node); final List values = node.getArguments(); final List transformedValue = new ArrayList<>(); - final Path fieldPath = getFieldPath(finalProperty); + final Path fieldPath = getFieldPath(fieldName, finalProperty); + for (final String value : values) { transformedValue.add(convertValueIfNecessary(node, fieldName, value, fieldPath)); } - return mapToPredicate(node, fieldPath, node.getArguments(), transformedValue); + return mapToPredicate(node, fieldPath, node.getArguments(), transformedValue, fieldName); } private List getExpectedFieldList() { final List expectedFieldList = Arrays.stream(enumType.getEnumConstants()) - .map(v -> v.name().toLowerCase()).collect(Collectors.toList()); - expectedFieldList.add("assignedds.name"); - expectedFieldList.add("assignedds.version"); + .filter(enumField -> enumField.getSubEntityAttributes().isEmpty()).map(enumField -> { + final String enumFieldName = enumField.name().toLowerCase(); + + if (enumField.isMap()) { + return enumFieldName + FieldNameProvider.SUB_ATTRIBUTE_SEPERATOR + "keyName"; + } + + return enumFieldName; + }).collect(Collectors.toList()); + + final List expectedSubFieldList = Arrays + .stream(enumType.getEnumConstants()) + .filter(enumField -> !enumField.getSubEntityAttributes().isEmpty()) + .flatMap( + enumField -> { + final List 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; } - private A getFieldIdentifierByName(final ComparisonNode node) { - final String enumName = node.getSelector().toUpperCase(); + private A getFieldEnumByName(final ComparisonNode node) { + 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); - return Enum.valueOf(enumType, enumName); + return Enum.valueOf(enumType, enumName.toUpperCase()); } @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -400,58 +381,86 @@ public final class RSQLUtility { } } - private List mapToPredicate(final ComparisonNode node, final Path fieldPath, - final List values, final List transformedValues) { + private List mapToPredicate(final ComparisonNode node, Path fieldPath, + final List values, final List transformedValues, final A enumField) { // only 'equal' and 'notEqual' can handle transformed value like // enums. The JPA API // cannot handle object types for greaterThan etc methods. final Object transformedValue = transformedValues.get(0); final String value = values.get(0); - final List singleList; + final List singleList = new ArrayList<>(); + + final Predicate mapPredicate = mapToMapPredicate(node, fieldPath, enumField); + if (mapPredicate != null) { + singleList.add(mapPredicate); + } + + fieldPath = getMapValueFieldPath(enumField, fieldPath); + switch (node.getOperator().getSymbol()) { case "=li=": - singleList = toSingleList(cb.like(cb.upper(pathOfString(fieldPath)), transformedValue.toString() - .toUpperCase())); + singleList.add(cb.like(cb.upper(pathOfString(fieldPath)), transformedValue.toString().toUpperCase())); break; case "==": - singleList = getEqualToPredicate(transformedValue, fieldPath); + singleList.add(getEqualToPredicate(transformedValue, fieldPath)); break; case "!=": - singleList = toSingleList(cb.notEqual(fieldPath, transformedValue)); + singleList.add(cb.notEqual(fieldPath, transformedValue)); break; case "=gt=": - singleList = toSingleList(cb.greaterThan(pathOfString(fieldPath), value)); + singleList.add(cb.greaterThan(pathOfString(fieldPath), value)); break; case "=ge=": - singleList = toSingleList(cb.greaterThanOrEqualTo(pathOfString(fieldPath), value)); + singleList.add(cb.greaterThanOrEqualTo(pathOfString(fieldPath), value)); break; case "=lt=": - singleList = toSingleList(cb.lessThan(pathOfString(fieldPath), value)); + singleList.add(cb.lessThan(pathOfString(fieldPath), value)); break; case "=le=": - singleList = toSingleList(cb.lessThanOrEqualTo(pathOfString(fieldPath), value)); + singleList.add(cb.lessThanOrEqualTo(pathOfString(fieldPath), value)); break; case "=in=": - singleList = toSingleList(fieldPath.in(transformedValues)); + singleList.add(fieldPath.in(transformedValues)); break; case "=out=": - singleList = toSingleList(cb.not(fieldPath.in(transformedValues))); + singleList.add(cb.not(fieldPath.in(transformedValues))); break; default: LOGGER.info("operator symbol {} is either not supported or not implemented"); - singleList = Collections.emptyList(); } - return singleList; + return Collections.unmodifiableList(singleList); } - private List getEqualToPredicate(final Object transformedValue, final Path fieldPath) { + /** + * @param enumField + * @return + */ + private Path getMapValueFieldPath(final A enumField, final Path fieldPath) { + if (!enumField.isMap() || enumField.getValueFieldName() == null) { + return fieldPath; + } + return fieldPath.get(enumField.getValueFieldName()); + } + + private Predicate mapToMapPredicate(final ComparisonNode node, final Path 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 fieldPath) { if (transformedValue instanceof String) { final String preFormattedValue = ((String) transformedValue).replace(LIKE_WILDCARD, '%'); - return toSingleList(cb.like(cb.upper(pathOfString(fieldPath)), preFormattedValue.toString() - .toUpperCase())); - } else { - return toSingleList(cb.equal(fieldPath, transformedValue)); + return cb.like(cb.upper(pathOfString(fieldPath)), preFormattedValue.toString().toUpperCase()); } + return cb.equal(fieldPath, transformedValue); } @SuppressWarnings("unchecked") @@ -473,8 +482,5 @@ public final class RSQLUtility { return childs; } - private static List toSingleList(final Predicate p) { - return Collections.singletonList(p); - } } } diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetResource.java index 500f0a786..4f1365747 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetResource.java @@ -92,8 +92,6 @@ public class DistributionSetResource { @Autowired private DistributionSetManagement distributionSetManagement; - @Autowired - private EntityManager entityManager; /** * Handles the GET request of retrieving all {@link DistributionSet}s within @@ -130,7 +128,7 @@ public class DistributionSetResource { final Page findDsPage; if (rsqlParam != null) { findDsPage = distributionSetManagement.findDistributionSetsAll( - RSQLUtility.parse(rsqlParam, DistributionSetFields.class, entityManager), pageable, false); + RSQLUtility.parse(rsqlParam, DistributionSetFields.class), pageable, false); } else { findDsPage = distributionSetManagement.findDistributionSetsAll(pageable, false, null); } @@ -281,7 +279,7 @@ public class DistributionSetResource { final Page targetsAssignedDS; if (rsqlParam != null) { targetsAssignedDS = targetManagement.findTargetByAssignedDistributionSet(distributionSetId, - RSQLUtility.parse(rsqlParam, TargetFields.class, entityManager), pageable); + RSQLUtility.parse(rsqlParam, TargetFields.class), pageable); } else { targetsAssignedDS = targetManagement.findTargetByAssignedDistributionSet(distributionSetId, pageable); } @@ -331,7 +329,7 @@ public class DistributionSetResource { final Page targetsInstalledDS; if (rsqlParam != null) { targetsInstalledDS = targetManagement.findTargetByInstalledDistributionSet(distributionSetId, - RSQLUtility.parse(rsqlParam, TargetFields.class, entityManager), pageable); + RSQLUtility.parse(rsqlParam, TargetFields.class), pageable); } else { targetsInstalledDS = targetManagement.findTargetByInstalledDistributionSet(distributionSetId, pageable); } @@ -410,7 +408,7 @@ public class DistributionSetResource { if (rsqlParam != null) { metaDataPage = distributionSetManagement.findDistributionSetMetadataByDistributionSetId(distributionSetId, - RSQLUtility.parse(rsqlParam, DistributionSetMetadataFields.class, entityManager), pageable); + RSQLUtility.parse(rsqlParam, DistributionSetMetadataFields.class), pageable); } else { metaDataPage = distributionSetManagement.findDistributionSetMetadataByDistributionSetId(distributionSetId, pageable); diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTagResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTagResource.java index 43830e803..18de23dd9 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTagResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTagResource.java @@ -60,8 +60,6 @@ public class DistributionSetTagResource { @Autowired private DistributionSetManagement distributionSetManagement; - @Autowired - private EntityManager entityManager; /** * Handles the GET request of retrieving all DistributionSet tags. @@ -103,7 +101,7 @@ public class DistributionSetTagResource { } else { final Page findTargetPage = tagManagement.findAllDistributionSetTags( - RSQLUtility.parse(rsqlParam, TagFields.class, entityManager), pageable); + RSQLUtility.parse(rsqlParam, TagFields.class), pageable); countTargetsAll = findTargetPage.getTotalElements(); findTargetsAll = findTargetPage; diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTypeResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTypeResource.java index 21c7bb956..6242b6acd 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTypeResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTypeResource.java @@ -62,9 +62,6 @@ public class DistributionSetTypeResource { @Autowired private DistributionSetManagement distributionSetManagement; - @Autowired - private EntityManager entityManager; - /** * Handles the GET request of retrieving all {@link DistributionSetType}s * within SP. @@ -105,7 +102,7 @@ public class DistributionSetTypeResource { Long countModulesAll; if (rsqlParam != null) { findModuleTypessAll = distributionSetManagement.findDistributionSetTypesByPredicate( - RSQLUtility.parse(rsqlParam, DistributionSetTypeFields.class, entityManager), pageable); + RSQLUtility.parse(rsqlParam, DistributionSetTypeFields.class), pageable); countModulesAll = ((Page) findModuleTypessAll).getTotalElements(); } else { findModuleTypessAll = distributionSetManagement.findDistributionSetTypesAll(pageable); diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleResource.java index fa95c0f5c..43228d365 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleResource.java @@ -11,7 +11,6 @@ package org.eclipse.hawkbit.rest.resource; import java.io.IOException; import java.util.List; -import javax.persistence.EntityManager; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -75,9 +74,6 @@ public class SoftwareModuleResource { @Autowired private SoftwareManagement softwareManagement; - @Autowired - private EntityManager entityManager; - /** * Handles POST request for artifact upload. * @@ -266,7 +262,7 @@ public class SoftwareModuleResource { Long countModulesAll; if (rsqlParam != null) { findModulesAll = softwareManagement.findSoftwareModulesByPredicate( - RSQLUtility.parse(rsqlParam, SoftwareModuleFields.class, entityManager), pageable); + RSQLUtility.parse(rsqlParam, SoftwareModuleFields.class), pageable); countModulesAll = ((Page) findModulesAll).getTotalElements(); } else { findModulesAll = softwareManagement.findSoftwareModulesAll(pageable); @@ -307,8 +303,8 @@ public class SoftwareModuleResource { * failure the JsonResponseExceptionHandler is handling the * response. */ - @RequestMapping(method = RequestMethod.POST, consumes = { "application/hal+json", - MediaType.APPLICATION_JSON_VALUE }, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) + @RequestMapping(method = RequestMethod.POST, consumes = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }, produces = { + "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) public ResponseEntity createSoftwareModules( @RequestBody final List softwareModules) { LOG.debug("creating {} softwareModules", softwareModules.size()); @@ -387,7 +383,8 @@ public class SoftwareModuleResource { */ @RequestMapping(method = RequestMethod.GET, value = "/{softwareModuleId}/metadata", produces = { MediaType.APPLICATION_JSON_VALUE, "application/hal+json" }) - public ResponseEntity getMetadata(@PathVariable final Long softwareModuleId, + public ResponseEntity 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_LIMIT, defaultValue = RestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam, @RequestParam(value = RestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam, @@ -405,15 +402,13 @@ public class SoftwareModuleResource { if (rsqlParam != null) { metaDataPage = softwareManagement.findSoftwareModuleMetadataBySoftwareModuleId(softwareModuleId, - RSQLUtility.parse(rsqlParam, SoftwareModuleMetadataFields.class, entityManager), pageable); + RSQLUtility.parse(rsqlParam, SoftwareModuleMetadataFields.class), pageable); } else { metaDataPage = softwareManagement.findSoftwareModuleMetadataBySoftwareModuleId(softwareModuleId, pageable); } - return new ResponseEntity<>( - new MetadataRestPageList(SoftwareModuleMapper.toResponseSwMetadata(metaDataPage.getContent()), - metaDataPage.getTotalElements()), - HttpStatus.OK); + return new ResponseEntity<>(new MetadataRestPageList(SoftwareModuleMapper.toResponseSwMetadata(metaDataPage + .getContent()), 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 * data */ - @RequestMapping(method = RequestMethod.GET, value = "/{softwareModuleId}/metadata/{metadataKey}", produces = { - MediaType.APPLICATION_JSON_VALUE }) + @RequestMapping(method = RequestMethod.GET, value = "/{softwareModuleId}/metadata/{metadataKey}", produces = { MediaType.APPLICATION_JSON_VALUE }) public ResponseEntity getMetadataValue(@PathVariable final Long softwareModuleId, @PathVariable final String metadataKey) { // check if distribution set exists otherwise throw exception @@ -487,8 +481,8 @@ public class SoftwareModuleResource { * the created meta data */ @RequestMapping(method = RequestMethod.POST, value = "/{softwareModuleId}/metadata", consumes = { - MediaType.APPLICATION_JSON_VALUE, - "application/hal+json" }, produces = { MediaType.APPLICATION_JSON_VALUE, "application/hal+json" }) + MediaType.APPLICATION_JSON_VALUE, "application/hal+json" }, produces = { MediaType.APPLICATION_JSON_VALUE, + "application/hal+json" }) public ResponseEntity> createMetadata(@PathVariable final Long softwareModuleId, @RequestBody final List metadataRest) { // check if software module exists otherwise throw exception immediately @@ -501,8 +495,7 @@ public class SoftwareModuleResource { } - private SoftwareModule findSoftwareModuleWithExceptionIfNotFound(final Long softwareModuleId, - final Long artifactId) { + private SoftwareModule findSoftwareModuleWithExceptionIfNotFound(final Long softwareModuleId, final Long artifactId) { final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId); if (module == null) { throw new EntityNotFoundException("SoftwareModule with Id {" + softwareModuleId + "} does not exist"); diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleTypeResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleTypeResource.java index bef4b5ee4..621efc690 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleTypeResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleTypeResource.java @@ -94,7 +94,7 @@ public class SoftwareModuleTypeResource { Long countModulesAll; if (rsqlParam != null) { findModuleTypessAll = softwareManagement.findSoftwareModuleTypesByPredicate( - RSQLUtility.parse(rsqlParam, SoftwareModuleTypeFields.class, entityManager), pageable); + RSQLUtility.parse(rsqlParam, SoftwareModuleTypeFields.class), pageable); countModulesAll = ((Page) findModuleTypessAll).getTotalElements(); } else { findModuleTypessAll = softwareManagement.findSoftwareModuleTypesAll(pageable); diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetResource.java index 4c94ce36b..b15845d3a 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetResource.java @@ -135,7 +135,7 @@ public class TargetResource { final Long countTargetsAll; if (rsqlParam != null) { final Page findTargetPage = targetManagement.findTargetsAll( - RSQLUtility.parse(rsqlParam, TargetFields.class, entityManager), pageable); + RSQLUtility.parse(rsqlParam, TargetFields.class), pageable); countTargetsAll = findTargetPage.getTotalElements(); findTargetsAll = findTargetPage; } else { @@ -284,7 +284,7 @@ public class TargetResource { final Slice activeActions; final Long totalActionCount; if (rsqlParam != null) { - final Specification parse = RSQLUtility.parse(rsqlParam, ActionFields.class, entityManager); + final Specification parse = RSQLUtility.parse(rsqlParam, ActionFields.class); activeActions = deploymentManagement.findActionsByTarget(parse, foundTarget, pageable); totalActionCount = deploymentManagement.countActionsByTarget(parse, foundTarget); } else { diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetTagResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetTagResource.java index 2e5406d1e..c0d5c7c32 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetTagResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetTagResource.java @@ -103,7 +103,7 @@ public class TargetTagResource { } else { final Page findTargetPage = tagManagement.findAllTargetTags( - RSQLUtility.parse(rsqlParam, TagFields.class, entityManager), pageable); + RSQLUtility.parse(rsqlParam, TagFields.class), pageable); countTargetsAll = findTargetPage.getTotalElements(); findTargetsAll = findTargetPage; diff --git a/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/RSQLUtilityTest.java b/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/RSQLUtilityTest.java index a413f77e7..076c03c80 100644 --- a/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/RSQLUtilityTest.java +++ b/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/RSQLUtilityTest.java @@ -12,14 +12,12 @@ import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import javax.persistence.EntityManager; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Expression; @@ -27,11 +25,11 @@ import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; 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.SoftwareModuleFields; +import org.eclipse.hawkbit.repository.TargetFields; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.rsql.RSQLParameterSyntaxException; 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.runner.RunWith; import org.mockito.Mock; -import org.mockito.invocation.InvocationOnMock; import org.mockito.runners.MockitoJUnitRunner; -import org.mockito.stubbing.Answer; import ru.yandex.qatools.allure.annotations.Features; import ru.yandex.qatools.allure.annotations.Stories; @@ -60,33 +56,87 @@ public class RSQLUtilityTest { private CriteriaQuery criteriaQueryMock; @Mock private CriteriaBuilder criteriaBuilderMock; - @Mock - private EntityManager entityManager; - - @Mock - private Metamodel metamodel; - - @Mock - private ManagedType managedType; @Mock private Attribute attribute; - @Test(expected = RSQLParameterSyntaxException.class) + @Test public void wrongRsqlSyntaxThrowSyntaxException() { final String wrongRSQL = "name==abc;d"; - when(entityManager.getMetamodel()).thenReturn(metamodel); - RSQLUtility.parse(wrongRSQL, SoftwareModuleFields.class, entityManager).toPredicate(baseSoftwareModuleRootMock, - criteriaQueryMock, criteriaBuilderMock); + try { + RSQLUtility.parse(wrongRSQL, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock, + criteriaQueryMock, criteriaBuilderMock); + fail(); + } catch (final RSQLParameterSyntaxException e) { + } } - @Test(expected = RSQLParameterUnsupportedFieldException.class) + @Test public void wrongFieldThrowUnsupportedFieldException() { final String wrongRSQL = "unknownField==abc"; when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class); - doEntitySetup(SoftwareModule.class); - RSQLUtility.parse(wrongRSQL, SoftwareModuleFields.class, entityManager).toPredicate(baseSoftwareModuleRootMock, - criteriaQueryMock, criteriaBuilderMock); + try { + RSQLUtility.parse(wrongRSQL, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock, + 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 @@ -100,11 +150,9 @@ public class RSQLUtilityTest { when(criteriaBuilderMock. greaterThanOrEqualTo(any(Expression.class), any(String.class))).thenReturn( mock(Predicate.class)); - doEntitySetup(SoftwareModule.class); - // test - RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, entityManager).toPredicate( - baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock); + RSQLUtility.parse(correctRsql, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock, + criteriaQueryMock, criteriaBuilderMock); // verfication 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. greaterThanOrEqualTo(any(Expression.class), any(String.class))).thenReturn( mock(Predicate.class)); - doEntitySetup(SoftwareModule.class); // test - RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, entityManager).toPredicate( - baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock); + RSQLUtility.parse(correctRsql, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock, + criteriaQueryMock, criteriaBuilderMock); // verfication 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. greaterThanOrEqualTo(any(Expression.class), any(String.class))).thenReturn( mock(Predicate.class)); - doEntitySetup(SoftwareModule.class); // test - RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, entityManager).toPredicate( - baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock); + RSQLUtility.parse(correctRsql, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock, + criteriaQueryMock, criteriaBuilderMock); // verfication verify(criteriaBuilderMock, times(1)).and(any(Predicate.class)); @@ -159,10 +205,9 @@ public class RSQLUtilityTest { mock(Predicate.class)); when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock)))).thenReturn( pathOfString(baseSoftwareModuleRootMock)); - doEntitySetup(SoftwareModule.class); // test - RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, entityManager).toPredicate( - baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock); + RSQLUtility.parse(correctRsql, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock, + criteriaQueryMock, criteriaBuilderMock); // verfication verify(criteriaBuilderMock, times(1)).and(any(Predicate.class)); @@ -178,10 +223,9 @@ public class RSQLUtilityTest { when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) TestValueEnum.class); when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class)); - doEntitySetup(TestValueEnum.class); // test - RSQLUtility.parse(correctRsql, TestFieldEnum.class, entityManager).toPredicate(baseSoftwareModuleRootMock, - criteriaQueryMock, criteriaBuilderMock); + RSQLUtility.parse(correctRsql, TestFieldEnum.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock, + criteriaBuilderMock); // verfication verify(criteriaBuilderMock, times(1)).and(any(Predicate.class)); @@ -196,11 +240,9 @@ public class RSQLUtilityTest { when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) TestValueEnum.class); when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class)); - doEntitySetup(TestValueEnum.class); - try { // test - RSQLUtility.parse(correctRsql, TestFieldEnum.class, entityManager).toPredicate(baseSoftwareModuleRootMock, + RSQLUtility.parse(correctRsql, TestFieldEnum.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock); fail("missing RSQLParameterUnsupportedFieldException for wrong enum value"); } 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() { - @Override - public Attribute answer(final InvocationOnMock invocation) throws Throwable { - return attribute; - } - }).when(managedType).getAttribute(anyString()); - - when(attribute.isAssociation()).thenReturn(false); - - } - @SuppressWarnings("unchecked") private Path pathOfString(final Path path) { return (Path) path;