2525import java .util .Collections ;
2626import java .util .Date ;
2727import java .util .List ;
28+ import java .util .UUID ;
2829
29- import org .bson .BsonBinary ;
3030import org .bson .Document ;
3131import org .bson .codecs .DecoderContext ;
3232import org .junit .jupiter .api .Test ;
3333import org .springframework .data .spel .EvaluationContextProvider ;
3434import org .springframework .data .spel .ExpressionDependencies ;
3535import org .springframework .expression .EvaluationContext ;
36+ import org .springframework .expression .ParseException ;
3637import org .springframework .expression .TypedValue ;
3738import org .springframework .expression .spel .standard .SpelExpressionParser ;
3839import org .springframework .expression .spel .support .StandardEvaluationContext ;
@@ -369,11 +370,11 @@ public void capturingExpressionDependenciesShouldNotThrowParseErrorForSpelOnlyJs
369370 new SpelExpressionParser ());
370371 }
371372
372- @ Test // GH-3871
373- public void bindEntireQueryUsingSpelExpressionWhenEvaluationResultIsJsonString () {
373+ @ Test // GH-3871, GH-4089
374+ public void bindEntireQueryUsingSpelExpressionWhenEvaluationResultIsDocument () {
374375
375376 Object [] args = new Object [] { "expected" , "unexpected" };
376- String json = "?#{ true ? \" { 'name': ?0 }\" : \" { 'name' : ?1 }\" }" ;
377+ String json = "?#{ true ? { 'name': ?0 } : { 'name' : ?1 } }" ;
377378 StandardEvaluationContext evaluationContext = (StandardEvaluationContext ) EvaluationContextProvider .DEFAULT
378379 .getEvaluationContext (args );
379380
@@ -384,25 +385,27 @@ public void bindEntireQueryUsingSpelExpressionWhenEvaluationResultIsJsonString()
384385 assertThat (target ).isEqualTo (new Document ("name" , "expected" ));
385386 }
386387
387- @ Test // GH-3871
388- public void throwsExceptionWhenbindEntireQueryUsingSpelExpressionResultsInInvalidJsonString () {
388+ @ Test // GH-3871, GH-4089
389+ public void throwsExceptionWhenBindEntireQueryUsingSpelExpressionIsMalFormatted () {
389390
390391 Object [] args = new Object [] { "expected" , "unexpected" };
391- String json = "?#{ true ? \" { 'name': ?0 { }\" : \" { 'name' : ?1 }\" }" ;
392+ String json = "?#{ true ? { 'name': ?0 { } } : { 'name' : ?1 } }" ;
392393 StandardEvaluationContext evaluationContext = (StandardEvaluationContext ) EvaluationContextProvider .DEFAULT
393394 .getEvaluationContext (args );
394395
395- ParameterBindingJsonReader reader = new ParameterBindingJsonReader (json ,
396- new ParameterBindingContext ((index ) -> args [index ], new SpelExpressionParser (), evaluationContext ));
396+ assertThatExceptionOfType (ParseException .class ).isThrownBy (() -> {
397+ ParameterBindingJsonReader reader = new ParameterBindingJsonReader (json ,
398+ new ParameterBindingContext ((index ) -> args [index ], new SpelExpressionParser (), evaluationContext ));
397399
398- assertThatExceptionOfType (IllegalArgumentException .class ).isThrownBy (() -> new ParameterBindingDocumentCodec ().decode (reader , DecoderContext .builder ().build ()));
400+ new ParameterBindingDocumentCodec ().decode (reader , DecoderContext .builder ().build ());
401+ });
399402 }
400403
401- @ Test // GH-3871
404+ @ Test // GH-3871, GH-4089
402405 public void bindEntireQueryUsingSpelExpressionWhenEvaluationResultIsJsonStringContainingUUID () {
403406
404- Object [] args = new Object [] { " UUID(' cfbca728-4e39-4613-96bc-f920b5c37e16')" , "unexpected" };
405- String json = "?#{ true ? \" { 'name': ?0 }\" : \" { 'name' : ?1 }\" }" ;
407+ Object [] args = new Object [] { UUID . fromString ( " cfbca728-4e39-4613-96bc-f920b5c37e16" ) , "unexpected" };
408+ String json = "?#{ true ? { 'name': ?0 } : { 'name' : ?1 } }" ;
406409 StandardEvaluationContext evaluationContext = (StandardEvaluationContext ) EvaluationContextProvider .DEFAULT
407410 .getEvaluationContext (args );
408411
@@ -411,7 +414,7 @@ public void bindEntireQueryUsingSpelExpressionWhenEvaluationResultIsJsonStringCo
411414
412415 Document target = new ParameterBindingDocumentCodec ().decode (reader , DecoderContext .builder ().build ());
413416
414- assertThat (target .get ("name" )).isInstanceOf (BsonBinary .class );
417+ assertThat (target .get ("name" )).isInstanceOf (UUID .class );
415418 }
416419
417420 @ Test // GH-3871
@@ -481,6 +484,69 @@ void parsesNullValue() {
481484 assertThat (target ).isEqualTo (new Document ("parent" , null ));
482485 }
483486
487+
488+ @ Test // GH-4089
489+ void retainsSpelArgumentTypeViaArgumentIndex () {
490+
491+ String source = "new java.lang.Object()" ;
492+ Document target = parse ("{ arg0 : ?#{[0]} }" , source );
493+ assertThat (target .get ("arg0" )).isEqualTo (source );
494+ }
495+
496+ @ Test // GH-4089
497+ void retainsSpelArgumentTypeViaParameterPlaceholder () {
498+
499+ String source = "new java.lang.Object()" ;
500+ Document target = parse ("{ arg0 : :#{?0} }" , source );
501+ assertThat (target .get ("arg0" )).isEqualTo (source );
502+ }
503+
504+ @ Test // GH-4089
505+ void errorsOnNonDocument () {
506+
507+ String source = "new java.lang.Object()" ;
508+ assertThatExceptionOfType (IllegalArgumentException .class ).isThrownBy (() -> parse (":#{?0}" , source ));
509+ }
510+
511+ @ Test // GH-4089
512+ void bindsFullDocument () {
513+
514+ Document source = new Document ();
515+ assertThat (parse (":#{?0}" , source )).isSameAs (source );
516+ }
517+
518+ @ Test // GH-4089
519+ void enforcesStringSpelArgumentTypeViaParameterPlaceholderWhenQuoted () {
520+
521+ Integer source = 10 ;
522+ Document target = parse ("{ arg0 : :#{'?0'} }" , source );
523+ assertThat (target .get ("arg0" )).isEqualTo ("10" );
524+ }
525+
526+ @ Test // GH-4089
527+ void enforcesSpelArgumentTypeViaParameterPlaceholderWhenQuoted () {
528+
529+ String source = "new java.lang.Object()" ;
530+ Document target = parse ("{ arg0 : :#{'?0'} }" , source );
531+ assertThat (target .get ("arg0" )).isEqualTo (source );
532+ }
533+
534+ @ Test // GH-4089
535+ void retainsSpelArgumentTypeViaParameterPlaceholderWhenValueContainsSingleQuotes () {
536+
537+ String source = "' + new java.lang.Object() + '" ;
538+ Document target = parse ("{ arg0 : :#{?0} }" , source );
539+ assertThat (target .get ("arg0" )).isEqualTo (source );
540+ }
541+
542+ @ Test // GH-4089
543+ void retainsSpelArgumentTypeViaParameterPlaceholderWhenValueContainsDoubleQuotes () {
544+
545+ String source = "\\ \" + new java.lang.Object() + \\ \" " ;
546+ Document target = parse ("{ arg0 : :#{?0} }" , source );
547+ assertThat (target .get ("arg0" )).isEqualTo (source );
548+ }
549+
484550 private static Document parse (String json , Object ... args ) {
485551
486552 ParameterBindingJsonReader reader = new ParameterBindingJsonReader (json , args );
0 commit comments