From feeb924ea9416c2486fcf30aca60c7e834c449cf Mon Sep 17 00:00:00 2001 From: Hicham Barhoumi <hicham.barhoumi@archiveco.fr> Date: Thu, 13 Aug 2020 18:11:17 +0200 Subject: [PATCH] [FENIX-66] add elemMatch support for criteria --- .../commons/api/utils/CriteriaUtils.java | 22 ++++++++++++---- .../commons/mongo/utils/MongoUtils.java | 26 +++++++++++++------ .../commons/mongo/utils/MongoUtilsTest.java | 24 +++++++++++++++++ 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/commons/commons-api/src/main/java/fr/gouv/vitamui/commons/api/utils/CriteriaUtils.java b/commons/commons-api/src/main/java/fr/gouv/vitamui/commons/api/utils/CriteriaUtils.java index ba76a41b..310fc23b 100644 --- a/commons/commons-api/src/main/java/fr/gouv/vitamui/commons/api/utils/CriteriaUtils.java +++ b/commons/commons-api/src/main/java/fr/gouv/vitamui/commons/api/utils/CriteriaUtils.java @@ -122,16 +122,28 @@ public final class CriteriaUtils { /** * Check if criteria contains only allowed keys - * @param criteriaDto + * @param queryDto * @param allowedKeys */ - public static void checkContainsAuthorizedKeys(final QueryDto criteriaDto, final Collection<String> allowedKeys) { - criteriaDto.getCriterionList().forEach(criterion -> { - if (!allowedKeys.contains(criterion.getKey())) { + public static void checkContainsAuthorizedKeys(final QueryDto queryDto, final Collection<String> allowedKeys) { + queryDto.getCriterionList().forEach(criterion -> { + // if we have a ElemMatch operator we have to check that current field is allowed and his child field also + // field.childField + if (criterion.getOperator().equals(CriterionOperator.ELEMMATCH) && + allowedKeys.stream().anyMatch(key -> key.startsWith(criterion.getKey() + "."))) { + // we recurse on children to check the allowed key + try { + QueryDto elemMatchQuery = QueryDto.fromJson(JsonUtils.toJson(criterion.getValue())); + checkContainsAuthorizedKeys(elemMatchQuery, allowedKeys); + } + catch (JsonProcessingException e) { + throw new InvalidFormatException(e.getMessage(), e); + } + } else if (!allowedKeys.contains(criterion.getKey())) { throw new ForbiddenException("Criterion with key : " + criterion.getKey() + " is not allowed"); } }); - criteriaDto.getSubQueries().forEach(queryDto -> checkContainsAuthorizedKeys(queryDto, allowedKeys)); + queryDto.getSubQueries().forEach(queryDtoItem -> checkContainsAuthorizedKeys(queryDtoItem, allowedKeys)); } public static QueryDto fromJson(final String criteriaJson) { diff --git a/commons/commons-mongo/src/main/java/fr/gouv/vitamui/commons/mongo/utils/MongoUtils.java b/commons/commons-mongo/src/main/java/fr/gouv/vitamui/commons/mongo/utils/MongoUtils.java index 740963ba..2186ecef 100644 --- a/commons/commons-mongo/src/main/java/fr/gouv/vitamui/commons/mongo/utils/MongoUtils.java +++ b/commons/commons-mongo/src/main/java/fr/gouv/vitamui/commons/mongo/utils/MongoUtils.java @@ -48,7 +48,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import fr.gouv.vitamui.commons.api.domain.QueryDto; -import fr.gouv.vitamui.commons.api.domain.QueryOperator; +import fr.gouv.vitamui.commons.api.exception.NotImplementedException; import fr.gouv.vitamui.commons.utils.JsonUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.data.mongodb.core.query.Criteria; @@ -290,7 +290,7 @@ public final class MongoUtils { criteria = buildAndOperator(startCriteria, endCriteria); break; case ELEMMATCH : - criteria = Criteria.where(key).elemMatch(queryDTOToCriterion((QueryDto)val)); + criteria = Criteria.where(key).elemMatch(queryDtoToCriterion((QueryDto)val)); break; default : throw new IllegalArgumentException("Operator " + operator + " is not supported"); @@ -298,7 +298,7 @@ public final class MongoUtils { return criteria; } - public static Criteria queryDTOToCriterion(QueryDto queryDto) { + public static Criteria queryDtoToCriterion(QueryDto queryDto) { Collection<CriteriaDefinition> criteria = new ArrayList<>(); queryDto.getCriterionList().forEach(criterion -> { criteria.add(MongoUtils.getCriteria(criterion.getKey(), criterion.getValue(), criterion.getOperator())); @@ -306,15 +306,25 @@ public final class MongoUtils { // if the criteria contains subQueries, a recursive call is made for each subQuery queryDto.getSubQueries().forEach(queryDtoItem -> { - criteria.add(queryDTOToCriterion(queryDtoItem)); + criteria.add(queryDtoToCriterion(queryDtoItem)); }); final Criteria commonCustomCriteria = new Criteria(); - if (queryDto.getQueryOperator() == QueryOperator.AND) { - commonCustomCriteria.andOperator(criteria.stream().map(c -> (Criteria) c).toArray(Criteria[]::new)); - } else if (queryDto.getQueryOperator() == QueryOperator.OR) { - commonCustomCriteria.orOperator(criteria.stream().map(c -> (Criteria) c).toArray(Criteria[]::new)); + Criteria[] criteriaList = criteria.stream().map(c -> (Criteria) c).toArray(Criteria[]::new); + switch (queryDto.getQueryOperator()){ + case AND: + commonCustomCriteria.andOperator(criteriaList); + break; + case OR: + commonCustomCriteria.orOperator(criteriaList); + break; + case NOR: + commonCustomCriteria.norOperator(criteriaList); + break; + default: + throw new NotImplementedException("Method is not implemented => " + queryDto.getQueryOperator().name()); } + return commonCustomCriteria; } diff --git a/commons/commons-mongo/src/test/java/fr/gouv/vitamui/commons/mongo/utils/MongoUtilsTest.java b/commons/commons-mongo/src/test/java/fr/gouv/vitamui/commons/mongo/utils/MongoUtilsTest.java index b314a111..b9a9f2d5 100644 --- a/commons/commons-mongo/src/test/java/fr/gouv/vitamui/commons/mongo/utils/MongoUtilsTest.java +++ b/commons/commons-mongo/src/test/java/fr/gouv/vitamui/commons/mongo/utils/MongoUtilsTest.java @@ -10,6 +10,10 @@ import java.util.List; import java.util.Optional; import java.util.regex.Pattern; +import com.mongodb.BasicDBList; +import fr.gouv.vitamui.commons.api.domain.QueryDto; +import org.bson.Document; + import org.junit.Test; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.CriteriaDefinition; @@ -153,4 +157,24 @@ public class MongoUtilsTest { } + + @Test + public void getCriteria_with_elemMatch() { + QueryDto queryDto = QueryDto.criteria("country", "France", CriterionOperator.EQUALS); + fr.gouv.vitamui.commons.api.domain.Criterion criterion = + new fr.gouv.vitamui.commons.api.domain.Criterion("addressList", queryDto, CriterionOperator.ELEMMATCH); + Criteria criteria = MongoUtils.getCriteriaDefinitionFromEntityClass(criterion, Person.class); + + assertThat(criteria.getKey()).isEqualTo("addressList"); + + Document addressCriteria = (Document)criteria.getCriteriaObject().get("addressList"); + assertThat(addressCriteria).isNotNull(); + + Document elemMatch = (Document)addressCriteria.get("$elemMatch"); + assertThat(elemMatch).isNotNull(); + + Document elemMatchInternalQuery = (Document)((BasicDBList)elemMatch.get("$and")).get(0); + assertThat(elemMatchInternalQuery.getString("country")).isEqualTo("France"); + } + } -- GitLab