From 3af7269dbb698d0cc57ae407bb13a37e41a6ba2b Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 13 May 2020 09:48:02 +0200 Subject: [PATCH] DATAMONGO-2545 - Fix regression in String query SpEL parameter binding. We reenabled parameter binding within SpEL using query parameter placeholders ?0, ?1,... instead of their array index [0],[1],... Original pull request: #864. --- .../util/json/ParameterBindingJsonReader.java | 58 ++++++++++++------- .../ParameterBindingJsonReaderUnitTests.java | 51 ++++++++++++++++ 2 files changed, 88 insertions(+), 21 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java index 2cf11d8d1..86e311920 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java @@ -368,26 +368,33 @@ public class ParameterBindingJsonReader extends AbstractBsonReader { if (token.getType().equals(JsonTokenType.UNQUOTED_STRING)) { - if (matcher.find()) { - - int index = computeParameterIndex(matcher.group()); - bindableValue.setValue(getBindableValueForIndex(index)); - bindableValue.setType(bsonTypeForValue(getBindableValueForIndex(index))); - return bindableValue; - } - Matcher regexMatcher = EXPRESSION_BINDING_PATTERN.matcher(tokenValue); if (regexMatcher.find()) { String binding = regexMatcher.group(); String expression = binding.substring(3, binding.length() - 1); + Matcher inSpelMatcher = PARAMETER_BINDING_PATTERN.matcher(expression); + while (inSpelMatcher.find()) { + + int index = computeParameterIndex(inSpelMatcher.group()); + expression = expression.replace(inSpelMatcher.group(), getBindableValueForIndex(index).toString()); + } + Object value = evaluateExpression(expression); bindableValue.setValue(value); bindableValue.setType(bsonTypeForValue(value)); return bindableValue; } + if (matcher.find()) { + + int index = computeParameterIndex(matcher.group()); + bindableValue.setValue(getBindableValueForIndex(index)); + bindableValue.setType(bsonTypeForValue(getBindableValueForIndex(index))); + return bindableValue; + } + bindableValue.setValue(tokenValue); bindableValue.setType(BsonType.STRING); return bindableValue; @@ -396,26 +403,35 @@ public class ParameterBindingJsonReader extends AbstractBsonReader { String computedValue = tokenValue; - boolean matched = false; - while (matcher.find()) { - matched = true; - String group = matcher.group(); - int index = computeParameterIndex(group); - computedValue = computedValue.replace(group, nullSafeToString(getBindableValueForIndex(index))); - } - if (!matched) { + Matcher regexMatcher = EXPRESSION_BINDING_PATTERN.matcher(computedValue); - Matcher regexMatcher = EXPRESSION_BINDING_PATTERN.matcher(tokenValue); + while (regexMatcher.find()) { - while (regexMatcher.find()) { + String binding = regexMatcher.group(); + String expression = binding.substring(3, binding.length() - 1); - String binding = regexMatcher.group(); - String expression = binding.substring(3, binding.length() - 1); + Matcher inSpelMatcher = PARAMETER_BINDING_PATTERN.matcher(expression); + while (inSpelMatcher.find()) { - computedValue = computedValue.replace(binding, nullSafeToString(evaluateExpression(expression))); + int index = computeParameterIndex(inSpelMatcher.group()); + expression = expression.replace(inSpelMatcher.group(), getBindableValueForIndex(index).toString()); } + + computedValue = computedValue.replace(binding, nullSafeToString(evaluateExpression(expression))); + + bindableValue.setValue(computedValue); + bindableValue.setType(BsonType.STRING); + + return bindableValue; + } + + while (matcher.find()) { + + String group = matcher.group(); + int index = computeParameterIndex(group); + computedValue = computedValue.replace(group, nullSafeToString(getBindableValueForIndex(index))); } bindableValue.setValue(computedValue); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java index c3257b5f7..6e6e14dbd 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java @@ -26,6 +26,7 @@ import java.util.List; import org.bson.Document; import org.bson.codecs.DecoderContext; import org.junit.jupiter.api.Test; +import org.springframework.data.spel.EvaluationContextProvider; import org.springframework.expression.EvaluationContext; import org.springframework.expression.TypedValue; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -264,10 +265,60 @@ class ParameterBindingJsonReaderUnitTests { assertThat(target).isEqualTo(Document.parse("{\"$and\": [{\"v1\": {\"$in\": [1]}}]}")); } + @Test // DATAMONGO-2545 + void shouldABindArgumentsViaIndexInSpelExpressions() { + + Object[] args = new Object[] { "yess", "nooo" }; + StandardEvaluationContext evaluationContext = (StandardEvaluationContext) EvaluationContextProvider.DEFAULT + .getEvaluationContext(args); + + ParameterBindingJsonReader reader = new ParameterBindingJsonReader( + "{ 'isBatman' : ?#{ T(" + this.getClass().getName() + ").isBatman() ? [0] : [1] }}", + new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext)); + Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build()); + + assertThat(target).isEqualTo(new Document("isBatman", "nooo")); + } + + @Test // DATAMONGO-2545 + void shouldAllowMethodArgumentPlaceholdersInSpelExpressions/*becuase this worked before*/() { + + Object[] args = new Object[] { "yess", "nooo" }; + StandardEvaluationContext evaluationContext = (StandardEvaluationContext) EvaluationContextProvider.DEFAULT + .getEvaluationContext(args); + + ParameterBindingJsonReader reader = new ParameterBindingJsonReader( + "{ 'isBatman' : ?#{ T(" + this.getClass().getName() + ").isBatman() ? '?0' : '?1' }}", + new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext)); + Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build()); + + assertThat(target).isEqualTo(new Document("isBatman", "nooo")); + } + + @Test // DATAMONGO-2545 + void shouldAllowMethodArgumentPlaceholdersInQuotedSpelExpressions/*becuase this worked before*/() { + + Object[] args = new Object[] { "yess", "nooo" }; + StandardEvaluationContext evaluationContext = (StandardEvaluationContext) EvaluationContextProvider.DEFAULT + .getEvaluationContext(args); + + ParameterBindingJsonReader reader = new ParameterBindingJsonReader( + "{ 'isBatman' : \"?#{ T(" + this.getClass().getName() + ").isBatman() ? '?0' : '?1' }\" }", + new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext)); + Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build()); + + assertThat(target).isEqualTo(new Document("isBatman", "nooo")); + } + private static Document parse(String json, Object... args) { ParameterBindingJsonReader reader = new ParameterBindingJsonReader(json, args); return new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build()); } + // DATAMONGO-2545 + public static boolean isBatman() { + return false; + } + }