From bd97d3a87efaea4cd433fd713163726bbe4679f4 Mon Sep 17 00:00:00 2001 From: vincentescoffier Date: Fri, 12 Jan 2024 17:18:26 +0100 Subject: [PATCH 01/27] feat: is null/is not null --- src/main/antlr4/Query.g4 | 5 ++++ .../sipios/springsearch/QueryVisitorImpl.kt | 6 +++- .../springsearch/strategies/EnumStrategy.kt | 1 + .../strategies/ParsingStrategy.kt | 15 ++++++++++ .../SpringSearchApplicationTest.kt | 30 +++++++++++++++++++ 5 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/main/antlr4/Query.g4 b/src/main/antlr4/Query.g4 index 923390b..2478f01 100644 --- a/src/main/antlr4/Query.g4 +++ b/src/main/antlr4/Query.g4 @@ -24,6 +24,7 @@ criteria is_value : EMPTY + | NULL ; eq_array_value @@ -62,6 +63,10 @@ BOOL | 'false' ; +NULL + : 'null' + ; + STRING : '"' DoubleStringCharacter* '"' | '\'' SingleStringCharacter* '\'' diff --git a/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt b/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt index e6225ad..9d3e45c 100644 --- a/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt +++ b/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt @@ -37,7 +37,11 @@ class QueryVisitorImpl(private val searchSpecAnnotation: SearchSpec) : QueryB } else { SearchOperation.IS_NOT } - val value = ctx.is_value!!.text + val value = if (ctx.is_value.NULL() != null) { + null + } else { + ctx.is_value.text + } val criteria = SearchCriteria( key, op, diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/EnumStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/EnumStrategy.kt index 4e74ee5..55ac510 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/EnumStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/EnumStrategy.kt @@ -4,6 +4,7 @@ import kotlin.reflect.KClass class EnumStrategy : ParsingStrategy { override fun parse(value: String?, fieldClass: KClass): Any? { + if (value == null) return null return Class.forName(fieldClass.qualifiedName).getMethod("valueOf", String::class.java).invoke(null, value) } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/ParsingStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/ParsingStrategy.kt index 4045851..9ec7f6c 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/ParsingStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/ParsingStrategy.kt @@ -59,6 +59,21 @@ interface ParsingStrategy { builder.not(inClause) } + SearchOperation.IS -> { + if (value == null) { + builder.isNull(path.get(fieldName)) + } else { + builder.isEmpty(path[fieldName]) + } + } + SearchOperation.IS_NOT -> { + if (value == null) { + builder.isNotNull(path.get(fieldName)) + } else { + builder.isNotEmpty(path[fieldName]) + } + } + SearchOperation.EQUALS -> builder.equal(path.get(fieldName), value) SearchOperation.NOT_EQUALS -> builder.notEqual(path.get(fieldName), value) SearchOperation.STARTS_WITH -> builder.like(path[fieldName], "$value%") diff --git a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt index 1eeb12e..df36a72 100644 --- a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt +++ b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt @@ -1411,4 +1411,34 @@ class SpringSearchApplicationTest { val setNames = users.map { user -> user.userFirstName }.toSet() Assertions.assertEquals(setOf("joe", "jean"), setNames) } + + @Test + fun canGetUsersWithNullColumn() { + userRepository.save(Users(userFirstName = "john", type = null)) + userRepository.save(Users(userFirstName = "jane", type = UserType.ADMINISTRATOR)) + userRepository.save(Users(userFirstName = "joe", type = UserType.MANAGER)) + userRepository.save(Users(userFirstName = "jean", type = null)) + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("type IS null").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(2, users.size) + val setNames = users.map { user -> user.userFirstName }.toSet() + Assertions.assertEquals(setOf("john", "jean"), setNames) + } + + @Test + fun canGetUsersWithNotNullColumn() { + userRepository.save(Users(userFirstName = "john", type = null)) + userRepository.save(Users(userFirstName = "jane", type = UserType.ADMINISTRATOR)) + userRepository.save(Users(userFirstName = "joe", type = UserType.MANAGER)) + userRepository.save(Users(userFirstName = "jean", type = null)) + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("type IS NOT null").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(2, users.size) + val setNames = users.map { user -> user.userFirstName }.toSet() + Assertions.assertEquals(setOf("jane", "joe"), setNames) + } } From 6af7374a1236f22b2525445154370519ef23795b Mon Sep 17 00:00:00 2001 From: vincentescoffier Date: Wed, 17 Jan 2024 11:46:10 +0100 Subject: [PATCH 02/27] fix: search null for collections --- .../com/sipios/springsearch/SearchOperation.kt | 1 + .../strategies/CollectionStrategy.kt | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/com/sipios/springsearch/SearchOperation.kt b/src/main/kotlin/com/sipios/springsearch/SearchOperation.kt index e44c49f..b4f33fb 100644 --- a/src/main/kotlin/com/sipios/springsearch/SearchOperation.kt +++ b/src/main/kotlin/com/sipios/springsearch/SearchOperation.kt @@ -28,6 +28,7 @@ enum class SearchOperation { val AND_OPERATOR = "AND" val LEFT_PARANTHESIS = "(" val RIGHT_PARANTHESIS = ")" + val EMPTY = "EMPTY" /** * Parse a string into an operation. diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/CollectionStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/CollectionStrategy.kt index 468f84e..be05d11 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/CollectionStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/CollectionStrategy.kt @@ -6,7 +6,6 @@ import jakarta.persistence.criteria.Path import jakarta.persistence.criteria.Predicate class CollectionStrategy : ParsingStrategy { - override fun buildPredicate( builder: CriteriaBuilder, path: Path<*>, @@ -14,12 +13,22 @@ class CollectionStrategy : ParsingStrategy { ops: SearchOperation?, value: Any? ): Predicate? { - if (ops == SearchOperation.IS && value != null) { + if (ops == SearchOperation.IS && value == null) { + return builder.isNull(path.get(fieldName)) + } + if (ops == SearchOperation.IS && value == SearchOperation.EMPTY) { return builder.isEmpty(path[fieldName]) } - if (ops == SearchOperation.IS_NOT && value != null) { + if (ops == SearchOperation.IS_NOT && value == null) { + return builder.isNotNull(path.get(fieldName)) + } + if (ops == SearchOperation.IS_NOT && value == SearchOperation.EMPTY) { return builder.isNotEmpty(path[fieldName]) } - throw IllegalArgumentException("Unsupported operation $ops for collection field $fieldName, only IS and IS_NOT are supported") + + throw IllegalArgumentException( + "Unsupported operation $ops $value for collection field $fieldName, " + + "only IS null/EMPTY and IS NOT null/EMPTY are supported" + ) } } From 446fb051eba3aaaeb0a2a1061a8ef1a11f7b7f90 Mon Sep 17 00:00:00 2001 From: vincentescoffier Date: Wed, 17 Jan 2024 11:53:29 +0100 Subject: [PATCH 03/27] refactor: null not case sensitive --- src/main/antlr4/Query.g4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/antlr4/Query.g4 b/src/main/antlr4/Query.g4 index dcd2a33..69848bc 100644 --- a/src/main/antlr4/Query.g4 +++ b/src/main/antlr4/Query.g4 @@ -64,7 +64,7 @@ BOOL ; NULL - : 'null' + : 'null' | 'NULL' ; STRING From 8f45975260d98df37c4e4eff5f9cbf72c48ddc5b Mon Sep 17 00:00:00 2001 From: vincentescoffier Date: Wed, 17 Jan 2024 11:55:01 +0100 Subject: [PATCH 04/27] doc: update README.md --- README.md | 84 +++++++++++++++++++++---------------------------------- 1 file changed, 32 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 16236e9..19f747b 100644 --- a/README.md +++ b/README.md @@ -124,67 +124,47 @@ fun yourFunctionNameHere(@SearchSpec specs: Specification): ResponseE } ``` -1. Using the equal operator `:` -Request : `/cars?search=color:Red` -![equal operator example](./docs/images/equal-example.gif) - -2. Using the not equal operator `!` -Request : `/cars?search=color!Red` -![not equal operator example](./docs/images/not-equal-example.gif) - -3. Using the greater than operator `>` -Request : `/cars?search=creationyear>2017` -> Note: You can use the `>:` operator as well. - -![greater than operator example](./docs/images/greater-than-example.gif) - -4. Using the less than operator `<` -Request : `/cars?search=price<100000` -> Note: You can use the `<:` operator as well. - -![less than operator example](./docs/images/less-than-example.gif) - -5. Using the starts with operator `*` -*For the ends with operator, simply place `*` at the beginning of the word*. -*For the contains operator, simply place `*` at the beginning and the end of the word*. -Request : `/cars?search=brand:Aston*` -![starts with operator example](./docs/images/starts-with-example.gif) - -6. Using the `OR` operator -Request : `/cars?search=color:Red OR color:Blue` -![or operator example](./docs/images/or-example.gif) - -7. Using the `AND` operator -Request : `/cars?search=brand:Aston* AND price<300000` -![and operator example](./docs/images/and-example.gif) - -8. Checking if value is or not in a list -Request : `/cars?search=color IN ['Red', 'Blue']` -Request : `/cars?search=color NOT IN ['Red', 'Blue']` -*Note: Spaces inside the brackets are not necessary* -*Note: You will need to encode the value (e.g. encodeURI) as brackets are not valid url parts* - -9. Using the `IS EMPTY` and `IS NOT EMPTY` operators for collection fields -Request : `/users?search=cars IS EMPTY` - -10. Using parenthesis +## Operators + +| Operator | Description | Example | +|----------------|-------------------------------|----------------------------------------------------| +| `:` | Equal | `color:Red` | +| `!` | Not equal | `color!Red` | +| `>` | Greater than | `creationyear>2017` | +| `>:` | Greater than eq | `creationyear>2017` | +| `<` | Less than | `price<100000` | +| `<:` | Less than eq | `price<100000` | +| `*` | Starts with | `brand:*Martin` | +| `*` | Ends with | `brand:Aston*` | +| `*` | Contains | `brand:*Martin*` | +| `OR` | Logical OR | `color:Red OR color:Blue` | +| `AND` | Logical AND | `brand:Aston* AND price<300000` | +| `IN` | Value is in list | `color IN ['Red', 'Blue']` | +| `NOT IN` | Value is not in list | `color NOT IN ['Red', 'Blue']` | +| `IS EMPTY` | Collection field is empty | `cars IS EMPTY` | +| `IS NOT EMPTY` | Collection field is not empty | `cars IS NOT EMPTY` | +| `IS NULL` | Field is null | `brand IS NULL` | +| `IS NOT NULL` | Field is not null | `brand IS NOT NULL` | +| `()` | Parenthesis | `brand:Nissan OR (brand:Chevrolet AND color:Blue)` | +| `BETWEEN` | Value is between two values | `creationyear BETWEEN 2017 AND 2019` | + + +## Examples + +1. Using parenthesis Request : `/cars?search=( brand:Nissan OR brand:Chevrolet ) AND color:Blue` *Note: Spaces inside the parenthesis are not necessary* ![parenthesis example](./docs/images/parenthesis-example.gif) - -11. Using space in nouns +2. Using space in nouns Request : `/cars?search=model:'Spacetourer Business Lounge'` ![space example](./docs/images/space-example.gif) - -12. Using special characters +3. Using special characters Request: `/cars?search=model:中华V7` ![special characters example](./docs/images/special-characters-example.gif) - -13. Using deep fields +4. Using deep fields Request : `/cars?search=options.transmission:Auto` ![deep field example](./docs/images/deep-field-example.gif) - -14. Complex example +5. Complex example Request : `/cars?search=creationyear:2018 AND price<300000 AND (color:Yellow OR color:Blue) AND options.transmission:Auto` ![complex example](./docs/images/complex-example.gif) From df52d0d4f518d761237e99ae8304d353af31c910 Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Mon, 22 Jan 2024 10:14:42 +0100 Subject: [PATCH 05/27] refactor: throw if operand != IS | IS NOT --- src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt b/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt index 99c368d..ec0adc0 100644 --- a/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt +++ b/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt @@ -34,8 +34,10 @@ class QueryVisitorImpl(private val searchSpecAnnotation: SearchSpec) : QueryB val key = ctx.key()!!.text val op = if (ctx.IS() != null) { SearchOperation.IS - } else { + } else if (ctx.IS_NOT() != null) { SearchOperation.IS_NOT + } else { + throw IllegalArgumentException("Invalid operation") } val value = if (ctx.is_value.NULL() != null) { null From 7887789c5abf805218067ff15eace3fae177a54b Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Mon, 22 Jan 2024 10:27:20 +0100 Subject: [PATCH 06/27] refactor: throw if calling buildPredicate parent method with empty clause --- .../com/sipios/springsearch/strategies/ParsingStrategy.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/ParsingStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/ParsingStrategy.kt index 38f4454..e86d5f9 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/ParsingStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/ParsingStrategy.kt @@ -63,14 +63,16 @@ interface ParsingStrategy { if (value == null) { builder.isNull(path.get(fieldName)) } else { - builder.isEmpty(path[fieldName]) + // we should not call parent method for collection fields + throw IllegalArgumentException("Unsupported operation $ops $value for field $fieldName") } } SearchOperation.IS_NOT -> { if (value == null) { builder.isNotNull(path.get(fieldName)) } else { - builder.isNotEmpty(path[fieldName]) + // we should not call parent method for collection fields + throw IllegalArgumentException("Unsupported operation $ops $value for field $fieldName") } } From 00615a22d64f52b11f1c5445dac345d204ef3228 Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Mon, 22 Jan 2024 10:40:37 +0100 Subject: [PATCH 07/27] refactor: don't allow search for null collections --- .../sipios/springsearch/strategies/CollectionStrategy.kt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/CollectionStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/CollectionStrategy.kt index be05d11..a129b5b 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/CollectionStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/CollectionStrategy.kt @@ -13,22 +13,15 @@ class CollectionStrategy : ParsingStrategy { ops: SearchOperation?, value: Any? ): Predicate? { - if (ops == SearchOperation.IS && value == null) { - return builder.isNull(path.get(fieldName)) - } if (ops == SearchOperation.IS && value == SearchOperation.EMPTY) { return builder.isEmpty(path[fieldName]) } - if (ops == SearchOperation.IS_NOT && value == null) { - return builder.isNotNull(path.get(fieldName)) - } if (ops == SearchOperation.IS_NOT && value == SearchOperation.EMPTY) { return builder.isNotEmpty(path[fieldName]) } - throw IllegalArgumentException( "Unsupported operation $ops $value for collection field $fieldName, " + - "only IS null/EMPTY and IS NOT null/EMPTY are supported" + "only IS EMPTY and IS NOT EMPTY are supported" ) } } From 17b189f5a13d524da373d7326b25af080eaec22a Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Mon, 22 Jan 2024 10:40:37 +0100 Subject: [PATCH 08/27] refactor: don't allow search for null collections --- .../strategies/CollectionStrategy.kt | 4 +++- .../SpringSearchApplicationTest.kt | 22 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/CollectionStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/CollectionStrategy.kt index a129b5b..9004e93 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/CollectionStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/CollectionStrategy.kt @@ -4,6 +4,8 @@ import com.sipios.springsearch.SearchOperation import jakarta.persistence.criteria.CriteriaBuilder import jakarta.persistence.criteria.Path import jakarta.persistence.criteria.Predicate +import org.springframework.http.HttpStatus +import org.springframework.web.server.ResponseStatusException class CollectionStrategy : ParsingStrategy { override fun buildPredicate( @@ -19,7 +21,7 @@ class CollectionStrategy : ParsingStrategy { if (ops == SearchOperation.IS_NOT && value == SearchOperation.EMPTY) { return builder.isNotEmpty(path[fieldName]) } - throw IllegalArgumentException( + throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Unsupported operation $ops $value for collection field $fieldName, " + "only IS EMPTY and IS NOT EMPTY are supported" ) diff --git a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt index 6219920..95c2e8b 100644 --- a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt +++ b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt @@ -14,6 +14,7 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.transaction.annotation.Transactional import org.springframework.web.server.ResponseStatusException +import java.net.http.HttpResponse @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = [SpringSearchApplication::class]) @Transactional @@ -1352,6 +1353,27 @@ class SpringSearchApplicationTest { val users = authorRepository.findAll(specification) Assertions.assertTrue(users.size == 0) } + @Test + fun cantGetAuthorsWithBooksNull() { + val john = Author() + john.name = "john" + authorRepository.save(john) + val jane = Author() + jane.name = "jane" + authorRepository.save(jane) + val spec = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("books IS null").build() + Assertions.assertThrows( + ResponseStatusException::class.java + ) { authorRepository.findAll(spec) } + val specNotNull = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("books IS NOT null").build() + Assertions.assertThrows( + ResponseStatusException::class.java + ) { authorRepository.findAll(specNotNull) } + } @Test fun canGetUsersWithNumberOfChildrenBetween() { From 9f0107fe6572e31359e58a09928fe1f1b774bacc Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Mon, 22 Jan 2024 11:07:40 +0100 Subject: [PATCH 09/27] fix: lint --- .../com/sipios/springsearch/SpringSearchApplicationTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt index 95c2e8b..0372d2b 100644 --- a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt +++ b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt @@ -14,7 +14,6 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.transaction.annotation.Transactional import org.springframework.web.server.ResponseStatusException -import java.net.http.HttpResponse @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = [SpringSearchApplication::class]) @Transactional From 4bbb7a7560ac7a79d7595c146b2aaf68b5d4afe1 Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Mon, 22 Jan 2024 11:18:11 +0100 Subject: [PATCH 10/27] test: add test for searching for empty non-collection fields --- .../strategies/ParsingStrategy.kt | 11 +++++--- .../SpringSearchApplicationTest.kt | 25 +++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/ParsingStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/ParsingStrategy.kt index e86d5f9..5c6beb4 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/ParsingStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/ParsingStrategy.kt @@ -14,7 +14,8 @@ import java.util.Date import java.util.UUID import kotlin.reflect.KClass import kotlin.reflect.full.isSubclassOf - +import org.springframework.http.HttpStatus +import org.springframework.web.server.ResponseStatusException interface ParsingStrategy { /** * Method to parse the value specified to the corresponding strategy @@ -64,7 +65,9 @@ interface ParsingStrategy { builder.isNull(path.get(fieldName)) } else { // we should not call parent method for collection fields - throw IllegalArgumentException("Unsupported operation $ops $value for field $fieldName") + // so this makes no sense to search for EMPTY with a non-collection field + throw ResponseStatusException(HttpStatus.BAD_REQUEST, + "Unsupported operation $ops $value for field $fieldName") } } SearchOperation.IS_NOT -> { @@ -72,7 +75,9 @@ interface ParsingStrategy { builder.isNotNull(path.get(fieldName)) } else { // we should not call parent method for collection fields - throw IllegalArgumentException("Unsupported operation $ops $value for field $fieldName") + // so this makes no sense to search for NOT EMPTY with a non-collection field + throw ResponseStatusException(HttpStatus.BAD_REQUEST, + "Unsupported operation $ops $value for field $fieldName") } } diff --git a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt index 0372d2b..056c832 100644 --- a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt +++ b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt @@ -1283,6 +1283,31 @@ class SpringSearchApplicationTest { Assertions.assertTrue(users.isEmpty()) } + @Test + fun cantSearchForEmptyWithNonFieldProperties() { + val johnBook = Book() + val john = Author() + john.name = "john" + john.addBook(johnBook) + authorRepository.save(john) + val janeBook = Book() + val jane = Author() + jane.name = "jane" + jane.addBook(janeBook) + authorRepository.save(jane) + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("name IS EMPTY").build() + Assertions.assertThrows( + ResponseStatusException::class.java + ) { authorRepository.findAll(specification) } + val specification2 = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("name IS NOT EMPTY").build() + Assertions.assertThrows( + ResponseStatusException::class.java + ) { authorRepository.findAll(specification2) } + } @Test fun canGetAuthorsWithEmptyBookWithResult() { val johnBook = Book() From d03db3dd9fdd8530288ea6198a013ef181714e8c Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Tue, 23 Jan 2024 09:09:24 +0100 Subject: [PATCH 11/27] refactor: avoid passing null value, check literal value instead (SearchOperation.NULL) --- README.md | 43 ++++++++++--------- src/main/antlr4/Query.g4 | 2 +- .../sipios/springsearch/QueryVisitorImpl.kt | 9 +--- .../sipios/springsearch/SearchOperation.kt | 1 + .../springsearch/strategies/EnumStrategy.kt | 3 +- .../strategies/ParsingStrategy.kt | 4 +- .../SpringSearchApplicationTest.kt | 23 ++++++++-- 7 files changed, 49 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index a44131b..d3f0d18 100644 --- a/README.md +++ b/README.md @@ -126,27 +126,28 @@ fun yourFunctionNameHere(@SearchSpec specs: Specification): ResponseE ## Operators -| Operator | Description | Example | -|----------------|-------------------------------|----------------------------------------------------| -| `:` | Equal | `color:Red` | -| `!` | Not equal | `color!Red` | -| `>` | Greater than | `creationyear>2017` | -| `>:` | Greater than eq | `creationyear>2017` | -| `<` | Less than | `price<100000` | -| `<:` | Less than eq | `price<100000` | -| `*` | Starts with | `brand:*Martin` | -| `*` | Ends with | `brand:Aston*` | -| `*` | Contains | `brand:*Martin*` | -| `OR` | Logical OR | `color:Red OR color:Blue` | -| `AND` | Logical AND | `brand:Aston* AND price<300000` | -| `IN` | Value is in list | `color IN ['Red', 'Blue']` | -| `NOT IN` | Value is not in list | `color NOT IN ['Red', 'Blue']` | -| `IS EMPTY` | Collection field is empty | `cars IS EMPTY` | -| `IS NOT EMPTY` | Collection field is not empty | `cars IS NOT EMPTY` | -| `IS NULL` | Field is null | `brand IS NULL` | -| `IS NOT NULL` | Field is not null | `brand IS NOT NULL` | -| `()` | Parenthesis | `brand:Nissan OR (brand:Chevrolet AND color:Blue)` | -| `BETWEEN` | Value is between two values | `creationyear BETWEEN 2017 AND 2019` | +| Operator | Description | Example | +|----------------|---------------------------------|----------------------------------------------------| +| `:` | Equal | `color:Red` | +| `!` | Not equal | `color!Red` | +| `>` | Greater than | `creationyear>2017` | +| `>:` | Greater than eq | `creationyear>2017` | +| `<` | Less than | `price<100000` | +| `<:` | Less than eq | `price<100000` | +| `*` | Starts with | `brand:*Martin` | +| `*` | Ends with | `brand:Aston*` | +| `*` | Contains | `brand:*Martin*` | +| `OR` | Logical OR | `color:Red OR color:Blue` | +| `AND` | Logical AND | `brand:Aston* AND price<300000` | +| `IN` | Value is in list | `color IN ['Red', 'Blue']` | +| `NOT IN` | Value is not in list | `color NOT IN ['Red', 'Blue']` | +| `IS EMPTY` | Collection field is empty | `cars IS EMPTY` | +| `IS NOT EMPTY` | Collection field is not empty | `cars IS NOT EMPTY` | +| `IS NULL` | Field is null | `brand IS NULL` | +| `IS NOT NULL` | Field is not null | `brand IS NOT NULL` | +| `()` | Parenthesis | `brand:Nissan OR (brand:Chevrolet AND color:Blue)` | +| `BETWEEN` | Value is between two values | `creationyear BETWEEN 2017 AND 2019` | +| `NOT BETWEEN` | Value is not between two values | `creationyear NOT BETWEEN 2017 AND 2019` | ## Examples diff --git a/src/main/antlr4/Query.g4 b/src/main/antlr4/Query.g4 index ad4f4e6..4234fd6 100644 --- a/src/main/antlr4/Query.g4 +++ b/src/main/antlr4/Query.g4 @@ -59,7 +59,7 @@ BOOL ; NULL - : 'null' | 'NULL' + : 'NULL' ; STRING diff --git a/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt b/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt index ec0adc0..35b9fc8 100644 --- a/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt +++ b/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt @@ -31,7 +31,7 @@ class QueryVisitorImpl(private val searchSpecAnnotation: SearchSpec) : QueryB } override fun visitIsCriteria(ctx: QueryParser.IsCriteriaContext): Specification { - val key = ctx.key()!!.text + val key = ctx.key().text val op = if (ctx.IS() != null) { SearchOperation.IS } else if (ctx.IS_NOT() != null) { @@ -39,12 +39,7 @@ class QueryVisitorImpl(private val searchSpecAnnotation: SearchSpec) : QueryB } else { throw IllegalArgumentException("Invalid operation") } - val value = if (ctx.is_value.NULL() != null) { - null - } else { - ctx.is_value.text - } - return toSpec(key, op, value) + return toSpec(key, op, ctx.is_value().text) } override fun visitEqArrayCriteria(ctx: QueryParser.EqArrayCriteriaContext): Specification { diff --git a/src/main/kotlin/com/sipios/springsearch/SearchOperation.kt b/src/main/kotlin/com/sipios/springsearch/SearchOperation.kt index b4f33fb..aa941db 100644 --- a/src/main/kotlin/com/sipios/springsearch/SearchOperation.kt +++ b/src/main/kotlin/com/sipios/springsearch/SearchOperation.kt @@ -29,6 +29,7 @@ enum class SearchOperation { val LEFT_PARANTHESIS = "(" val RIGHT_PARANTHESIS = ")" val EMPTY = "EMPTY" + val NULL = "NULL" /** * Parse a string into an operation. diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/EnumStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/EnumStrategy.kt index 55ac510..451d31b 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/EnumStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/EnumStrategy.kt @@ -1,10 +1,11 @@ package com.sipios.springsearch.strategies +import com.sipios.springsearch.SearchOperation import kotlin.reflect.KClass class EnumStrategy : ParsingStrategy { override fun parse(value: String?, fieldClass: KClass): Any? { - if (value == null) return null + if (value == SearchOperation.NULL) return value return Class.forName(fieldClass.qualifiedName).getMethod("valueOf", String::class.java).invoke(null, value) } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/ParsingStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/ParsingStrategy.kt index 5c6beb4..b2670c3 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/ParsingStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/ParsingStrategy.kt @@ -61,7 +61,7 @@ interface ParsingStrategy { } SearchOperation.IS -> { - if (value == null) { + if (value == SearchOperation.NULL) { builder.isNull(path.get(fieldName)) } else { // we should not call parent method for collection fields @@ -71,7 +71,7 @@ interface ParsingStrategy { } } SearchOperation.IS_NOT -> { - if (value == null) { + if (value == SearchOperation.NULL) { builder.isNotNull(path.get(fieldName)) } else { // we should not call parent method for collection fields diff --git a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt index 056c832..9bcca12 100644 --- a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt +++ b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt @@ -1387,13 +1387,13 @@ class SpringSearchApplicationTest { authorRepository.save(jane) val spec = SpecificationsBuilder( SearchSpec::class.constructors.first().call("", false) - ).withSearch("books IS null").build() + ).withSearch("books IS NULL").build() Assertions.assertThrows( ResponseStatusException::class.java ) { authorRepository.findAll(spec) } val specNotNull = SpecificationsBuilder( SearchSpec::class.constructors.first().call("", false) - ).withSearch("books IS NOT null").build() + ).withSearch("books IS NOT NULL").build() Assertions.assertThrows( ResponseStatusException::class.java ) { authorRepository.findAll(specNotNull) } @@ -1574,7 +1574,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "jean", type = null)) val specification = SpecificationsBuilder( SearchSpec::class.constructors.first().call("", false) - ).withSearch("type IS null").build() + ).withSearch("type IS NULL").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) val setNames = users.map { user -> user.userFirstName }.toSet() @@ -1589,10 +1589,25 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "jean", type = null)) val specification = SpecificationsBuilder( SearchSpec::class.constructors.first().call("", false) - ).withSearch("type IS NOT null").build() + ).withSearch("type IS NOT NULL").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) val setNames = users.map { user -> user.userFirstName }.toSet() Assertions.assertEquals(setOf("jane", "joe"), setNames) } + + @Test + fun canGetUsersWithNotNullFirstName() { + userRepository.save(Users(userFirstName = "john", type = null)) + userRepository.save(Users(userFirstName = "jane", type = UserType.ADMINISTRATOR)) + userRepository.save(Users(userFirstName = "joe", type = UserType.MANAGER)) + userRepository.save(Users(userFirstName = "jean", type = null)) + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("userFirstName IS NOT NULL").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(4, users.size) + val setNames = users.map { user -> user.userFirstName }.toSet() + Assertions.assertEquals(setOf("john", "jane", "joe", "jean"), setNames) + } } From 3bc14fb45447ab952c8282e4677d8fe22485a68a Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Tue, 23 Jan 2024 09:54:00 +0100 Subject: [PATCH 12/27] feat: blacklist --- .../sipios/springsearch/QueryVisitorImpl.kt | 16 ++ .../springsearch/anotation/SearchSpec.kt | 7 +- .../SpringSearchApplicationTest.kt | 235 +++++++++--------- 3 files changed, 144 insertions(+), 114 deletions(-) diff --git a/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt b/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt index 35b9fc8..daed1ed 100644 --- a/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt +++ b/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt @@ -4,6 +4,8 @@ import com.sipios.springsearch.anotation.SearchSpec import com.sipios.springsearch.grammar.QueryBaseVisitor import com.sipios.springsearch.grammar.QueryParser import org.springframework.data.jpa.domain.Specification +import org.springframework.http.HttpStatus +import org.springframework.web.server.ResponseStatusException class QueryVisitorImpl(private val searchSpecAnnotation: SearchSpec) : QueryBaseVisitor>() { private val valueRegExp = Regex(pattern = "^(?\\*?)(?.+?)(?\\*?)$") @@ -32,6 +34,7 @@ class QueryVisitorImpl(private val searchSpecAnnotation: SearchSpec) : QueryB override fun visitIsCriteria(ctx: QueryParser.IsCriteriaContext): Specification { val key = ctx.key().text + verifyBlackList(key) val op = if (ctx.IS() != null) { SearchOperation.IS } else if (ctx.IS_NOT() != null) { @@ -44,6 +47,7 @@ class QueryVisitorImpl(private val searchSpecAnnotation: SearchSpec) : QueryB override fun visitEqArrayCriteria(ctx: QueryParser.EqArrayCriteriaContext): Specification { val key = ctx.key().text + verifyBlackList(key) val op = if (ctx.IN() != null) { SearchOperation.IN_ARRAY } else { @@ -60,6 +64,7 @@ class QueryVisitorImpl(private val searchSpecAnnotation: SearchSpec) : QueryB override fun visitBetweenCriteria(ctx: QueryParser.BetweenCriteriaContext): Specification { val key = ctx.key().text + verifyBlackList(key) val leftValue = if (ctx.left.STRING() != null) { clearString(ctx.left.text) } else { @@ -95,6 +100,7 @@ class QueryVisitorImpl(private val searchSpecAnnotation: SearchSpec) : QueryB } else { ctx.value().text } + verifyBlackList(key) val matchResult = this.valueRegExp.find(value) val op = SearchOperation.getSimpleOperation(ctx.op().text) ?: throw IllegalArgumentException("Invalid operation") val criteria = SearchCriteria( @@ -108,6 +114,16 @@ class QueryVisitorImpl(private val searchSpecAnnotation: SearchSpec) : QueryB return SpecificationImpl(criteria, searchSpecAnnotation) } + private fun verifyBlackList(key: String?) { + val blackList = this.searchSpecAnnotation.blackListedFields + if (blackList.contains(key)) { + throw ResponseStatusException( + HttpStatus.BAD_REQUEST, + "Field $key is blacklisted" + ) + } + } + private fun clearString(value: String) = value .removeSurrounding("'") .removeSurrounding("\"") diff --git a/src/main/kotlin/com/sipios/springsearch/anotation/SearchSpec.kt b/src/main/kotlin/com/sipios/springsearch/anotation/SearchSpec.kt index dd2a43d..2b73666 100644 --- a/src/main/kotlin/com/sipios/springsearch/anotation/SearchSpec.kt +++ b/src/main/kotlin/com/sipios/springsearch/anotation/SearchSpec.kt @@ -16,5 +16,10 @@ annotation class SearchSpec( /** * A flag to indicate if the search needs to be case-sensitive or not */ - val caseSensitiveFlag: Boolean = true + val caseSensitiveFlag: Boolean = true, + + /** + * A list of fields that should be excluded from the search + */ + val blackListedFields: Array = [] ) diff --git a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt index 9bcca12..b301d51 100644 --- a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt +++ b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt @@ -41,7 +41,7 @@ class SpringSearchApplicationTest { userRepository.save(Users()) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userId:$userId").build() Assertions.assertEquals(userId, userRepository.findAll(specification).get(0).userId) } @@ -52,7 +52,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "Bob")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName:Alice").build() Assertions.assertEquals(aliceId, userRepository.findAll(specification).get(0).userId) } @@ -65,7 +65,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "Bob", userLastName = "Two")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName:Alice AND userLastName:One").build() Assertions.assertEquals(aliceId, userRepository.findAll(specification).get(0).userId) } @@ -75,7 +75,7 @@ class SpringSearchApplicationTest { val edouardProstId = userRepository.save(Users(userFirstName = "Édouard", userLastName = "Pröst")).userId val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName:Édouard AND userLastName:Pröst").build() Assertions.assertEquals(edouardProstId, userRepository.findAll(specification).get(0).userId) } @@ -85,7 +85,7 @@ class SpringSearchApplicationTest { val sunDemingId = userRepository.save(Users(userFirstName = "孫德明")).userId val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName:孫德明").build() Assertions.assertEquals(sunDemingId, userRepository.findAll(specification).get(0).userId) } @@ -95,7 +95,7 @@ class SpringSearchApplicationTest { val sunDemingId = userRepository.save(Users(userFirstName = "孫德明")).userId val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName:孫德明").build() Assertions.assertEquals(sunDemingId, userRepository.findAll(specification).get(0).userId) } @@ -105,7 +105,7 @@ class SpringSearchApplicationTest { val hackermanId = userRepository.save(Users(userFirstName = "&@#*\"''^^^\$``%=+§__hack3rman__")).userId val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName:&@#*\"''^^^\$``%=+§__hack3rman__").build() Assertions.assertEquals(hackermanId, userRepository.findAll(specification).get(0).userId) } @@ -115,7 +115,7 @@ class SpringSearchApplicationTest { val robertJuniorId = userRepository.save(Users(userFirstName = "robert junior")).userId val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName:'robert junior'").build() Assertions.assertEquals(robertJuniorId, userRepository.findAll(specification).get(0).userId) } @@ -128,7 +128,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "röbert")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName:robe*").build() val robeUsers = userRepository.findAll(specification) Assertions.assertTrue(setOf(robertId, robertaId) == robeUsers.map { user -> user.userId }.toSet()) @@ -142,7 +142,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "röbęrt")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName:*ert").build() val robeUsers = userRepository.findAll(specification) Assertions.assertTrue(setOf(robertId, robertaId) == robeUsers.map { user -> user.userId }.toSet()) @@ -156,7 +156,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "röb*rt")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName:rob**").build() val robeUsers = userRepository.findAll(specification) Assertions.assertTrue(setOf(robertId, robertaId) == robeUsers.map { user -> user.userId }.toSet()) @@ -172,7 +172,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "Röbert")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName:*obe*").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue( @@ -195,7 +195,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "röb*rt")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName:*ob**").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue( @@ -218,7 +218,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "röb*rt")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName:'*ob**'").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue( @@ -241,7 +241,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "röb*rt")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName:\"*ob**\"").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue( @@ -264,7 +264,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robèrta")).userId val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName!*è*").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue( @@ -285,7 +285,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "Bob")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName!B*").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue(setOf(aliceId, aliceId2, bobId) == specificationUsers.map { user -> user.userId }.toSet()) @@ -299,7 +299,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "alice")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName!*e").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue(setOf(boBId, alicEId, bobId) == specificationUsers.map { user -> user.userId }.toSet()) @@ -316,7 +316,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userChildrenNumber = 2)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userChildrenNumber>4").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue( @@ -339,7 +339,7 @@ class SpringSearchApplicationTest { val user2With2ChildrenId = userRepository.save(Users(userChildrenNumber = 2)).userId val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userChildrenNumber<4").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue( @@ -364,7 +364,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userChildrenNumber = 5)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userChildrenNumber:4").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue( @@ -385,7 +385,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userChildrenNumber = 2)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userChildrenNumber!2").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue( @@ -406,7 +406,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userSalary = 2300.0F)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userSalary<2300").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue( @@ -425,7 +425,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userSalary = 1500.2F)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userSalary>4000.001").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue( @@ -444,7 +444,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userSalary = 5350.7F)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userSalary<4000.1 AND userSalary>1500.2").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue(setOf(medianUserId) == specificationUsers.map { user -> user.userId }.toSet()) @@ -457,7 +457,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userAgeInSeconds = 23222223.0)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userAgeInSeconds>23222223.2").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue(setOf(olderUserId) == specificationUsers.map { user -> user.userId }.toSet()) @@ -470,7 +470,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userAgeInSeconds = 23222223.3)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userAgeInSeconds<23222223.2").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue(setOf(youngerUserId) == specificationUsers.map { user -> user.userId }.toSet()) @@ -483,7 +483,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userAgeInSeconds = 23222223.0)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userAgeInSeconds:23222223.2").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue(setOf(middleUserId) == specificationUsers.map { user -> user.userId }.toSet()) @@ -499,7 +499,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userSalary = 1500.2F, userLastName = "Three")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userSalary>1500.1 AND ( userLastName:One OR userLastName:Two )").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue( @@ -520,7 +520,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userSalary = 1502F, userLastName = "One")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userSalary<1502 AND ( ( userLastName:One OR userLastName:one ) OR userLastName!*n* )").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue( @@ -541,7 +541,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userSalary = 1502F, userLastName = "One")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userSalary<1502 AND ((userLastName:One OR userLastName:one) OR userLastName!*n*)").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertTrue( @@ -558,7 +558,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(isAdmin = false)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("isAdmin:true").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertEquals(1, specificationUsers.size) @@ -571,7 +571,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(createdAt = sdf.parse("2019-01-03"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("createdAt<'2019-01-02'").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertEquals(1, specificationUsers.size) @@ -584,13 +584,13 @@ class SpringSearchApplicationTest { userRepository.save(Users(createdAt = sdf.parse("2019-01-03"))) var specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("createdAt>'2019-01-02'").build() var specificationUsers = userRepository.findAll(specification) Assertions.assertEquals(1, specificationUsers.size) specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("createdAt>'2019-01-04'").build() specificationUsers = userRepository.findAll(specification) Assertions.assertEquals(0, specificationUsers.size) @@ -603,13 +603,13 @@ class SpringSearchApplicationTest { userRepository.save(Users(createdAt = sdf.parse("2019-01-03"))) var specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("createdAt>:'2019-01-01'").build() var specificationUsers = userRepository.findAll(specification) Assertions.assertEquals(2, specificationUsers.size) specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("createdAt>:'2019-01-04'").build() specificationUsers = userRepository.findAll(specification) Assertions.assertEquals(0, specificationUsers.size) @@ -622,13 +622,13 @@ class SpringSearchApplicationTest { userRepository.save(Users(createdAt = sdf.parse("2019-01-03"))) var specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("createdAt<:'2019-01-01'").build() var specificationUsers = userRepository.findAll(specification) Assertions.assertEquals(1, specificationUsers.size) specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("createdAt<:'2019-01-03'").build() specificationUsers = userRepository.findAll(specification) Assertions.assertEquals(2, specificationUsers.size) @@ -641,7 +641,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(createdAt = date)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("createdAt:'${sdf.format(date)}'").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertEquals(1, specificationUsers.size) @@ -654,7 +654,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(createdAt = date)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("createdAt!'2019-01-02'").build() val specificationUsers = userRepository.findAll(specification) Assertions.assertEquals(1, specificationUsers.size) @@ -668,7 +668,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "röbęrt")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", false, emptyArray()) ).withSearch("userFirstName:robe*").build() val robeUsers = userRepository.findAll(specification) Assertions.assertTrue(setOf(robertId, robertaId) == robeUsers.map { user -> user.userId }.toSet()) @@ -682,7 +682,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "röbęrt")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", false, emptyArray()) ).withSearch("userFirstName:*ert").build() val robeUsers = userRepository.findAll(specification) Assertions.assertTrue(setOf(robertId, roubertId) == robeUsers.map { user -> user.userId }.toSet()) @@ -696,7 +696,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "röbęrt")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", false, emptyArray()) ).withSearch("userFirstName:*er*").build() val robeUsers = userRepository.findAll(specification) Assertions.assertTrue(setOf(robertId, roubertId) == robeUsers.map { user -> user.userId }.toSet()) @@ -709,7 +709,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", false, emptyArray()) ).withSearch("userFirstName!*rob*").build() val robeUsers = userRepository.findAll(specification) Assertions.assertTrue(setOf(roubertId) == robeUsers.map { user -> user.userId }.toSet()) @@ -722,7 +722,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", false, emptyArray()) ).withSearch("userFirstName!rob*").build() val robeUsers = userRepository.findAll(specification) Assertions.assertTrue(setOf(roubertId) == robeUsers.map { user -> user.userId }.toSet()) @@ -735,7 +735,7 @@ class SpringSearchApplicationTest { val robotId = userRepository.save(Users(userFirstName = "robot")).userId val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", false, emptyArray()) ).withSearch("userFirstName!*rt").build() val robotUsers = userRepository.findAll(specification) Assertions.assertTrue(setOf(robotId) == robotUsers.map { user -> user.userId }.toSet()) @@ -749,7 +749,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", type = UserType.ADMINISTRATOR)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("type:ADMINISTRATOR").build() val robeUsers = userRepository.findAll(specification) Assertions.assertEquals(1, robeUsers.size) @@ -763,7 +763,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", type = UserType.TEAM_MEMBER)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("type!TEAM_MEMBER").build() val irehUsers = userRepository.findAll(specification) Assertions.assertEquals(1, irehUsers.size) @@ -776,7 +776,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", updatedAt = LocalDateTime.parse("2020-01-11T10:20:30"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedAt>'2020-01-11T09:20:30'").build() val robotUsers = userRepository.findAll(specification) Assertions.assertEquals(1, robotUsers.size) @@ -794,7 +794,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", updatedInstantAt = Instant.parse("2020-01-11T10:20:30Z"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedInstantAt>'2020-01-11T09:20:30Z'").build() val robotUsers = userRepository.findAll(specification) Assertions.assertEquals(1, robotUsers.size) @@ -812,7 +812,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", updatedInstantAt = Instant.parse("2020-01-11T10:20:30Z"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedInstantAt>:'2020-01-11T09:20:30Z'").build() val robotUsers = userRepository.findAll(specification) Assertions.assertEquals(1, robotUsers.size) @@ -830,7 +830,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", updatedInstantAt = Instant.parse("2020-01-11T10:20:30Z"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedInstantAt<:'2020-01-11T09:20:30Z'").build() val robotUsers = userRepository.findAll(specification) Assertions.assertEquals(1, robotUsers.size) @@ -843,7 +843,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", updatedAt = LocalDateTime.parse("2020-01-11T10:20:30"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedAt<'2020-01-11T09:20:30'").build() val hamidrezaUsers = userRepository.findAll(specification) Assertions.assertEquals(1, hamidrezaUsers.size) @@ -856,7 +856,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", updatedAt = LocalDateTime.parse("2020-01-11T10:20:30"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedAt:'2020-01-10T10:15:30'").build() val hamidrezaUsers = userRepository.findAll(specification) Assertions.assertEquals(1, hamidrezaUsers.size) @@ -869,7 +869,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", updatedAt = LocalDateTime.parse("2020-01-11T10:20:30"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedAt!'2020-01-11T10:20:30'").build() val hamidrezaUsers = userRepository.findAll(specification) Assertions.assertEquals(1, hamidrezaUsers.size) @@ -882,7 +882,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", updatedAt = LocalDateTime.parse("2020-01-11T10:20:30"))) userRepository.save(Users(userFirstName = "robot2", updatedAt = LocalDateTime.parse("2020-01-12T10:20:30"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedAt>:'2020-01-11T10:20:30'").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) @@ -895,7 +895,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", updatedAt = LocalDateTime.parse("2020-01-11T10:20:30"))) userRepository.save(Users(userFirstName = "robot2", updatedAt = LocalDateTime.parse("2020-01-12T10:20:30"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedAt<:'2020-01-11T10:20:30'").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) @@ -908,7 +908,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", updatedDateAt = LocalDate.parse("2020-01-11"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedDateAt>'2020-01-10'").build() val robotUsers = userRepository.findAll(specification) Assertions.assertEquals(1, robotUsers.size) @@ -921,7 +921,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", updatedDateAt = LocalDate.parse("2020-01-11"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedDateAt<'2020-01-11'").build() val hamidrezaUsers = userRepository.findAll(specification) Assertions.assertEquals(1, hamidrezaUsers.size) @@ -934,7 +934,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", updatedDateAt = LocalDate.parse("2020-01-11"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedDateAt:'2020-01-10'").build() val hamidrezaUsers = userRepository.findAll(specification) Assertions.assertEquals(1, hamidrezaUsers.size) @@ -947,7 +947,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", updatedDateAt = LocalDate.parse("2020-01-11"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedDateAt!'2020-01-11'").build() val hamidrezaUsers = userRepository.findAll(specification) Assertions.assertEquals(1, hamidrezaUsers.size) @@ -961,7 +961,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot2", updatedDateAt = LocalDate.parse("2020-01-12"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedDateAt<:'2020-01-11'").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) @@ -975,7 +975,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot2", updatedDateAt = LocalDate.parse("2020-01-12"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedDateAt>:'2020-01-11'").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) @@ -988,7 +988,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", updatedTimeAt = LocalTime.parse("10:20:30"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedTimeAt>'10:15:30'").build() val robotUsers = userRepository.findAll(specification) Assertions.assertEquals(1, robotUsers.size) @@ -1001,7 +1001,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", updatedTimeAt = LocalTime.parse("10:20:30"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedTimeAt<'10:16:30'").build() val hamidrezaUsers = userRepository.findAll(specification) Assertions.assertEquals(1, hamidrezaUsers.size) @@ -1014,7 +1014,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", updatedTimeAt = LocalTime.parse("10:20:30"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedTimeAt:'10:15:30'").build() val hamidrezaUsers = userRepository.findAll(specification) Assertions.assertEquals(1, hamidrezaUsers.size) @@ -1028,7 +1028,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot2", updatedTimeAt = LocalTime.parse("10:25:30"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedTimeAt<:'10:20:30'").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) @@ -1042,7 +1042,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot2", updatedTimeAt = LocalTime.parse("10:25:30"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedTimeAt>:'10:20:30'").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) @@ -1055,7 +1055,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", updatedTimeAt = LocalTime.parse("10:20:30"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedTimeAt!'10:20:30'").build() val hamidrezaUsers = userRepository.findAll(specification) Assertions.assertEquals(1, hamidrezaUsers.size) @@ -1068,7 +1068,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", validityDuration = Duration.parse("PT15H"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("validityDuration>'PT10H'").build() val robotUsers = userRepository.findAll(specification) Assertions.assertEquals(1, robotUsers.size) @@ -1081,7 +1081,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", validityDuration = Duration.parse("PT15H"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("validityDuration<'PT11H'").build() val hamidrezaUsers = userRepository.findAll(specification) Assertions.assertEquals(1, hamidrezaUsers.size) @@ -1094,7 +1094,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", validityDuration = Duration.parse("PT15H"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("validityDuration:'PT10H'").build() val hamidrezaUsers = userRepository.findAll(specification) Assertions.assertEquals(1, hamidrezaUsers.size) @@ -1107,7 +1107,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "robot", validityDuration = Duration.parse("PT15H"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("validityDuration!'PT10H'").build() val robotUsers = userRepository.findAll(specification) Assertions.assertEquals(1, robotUsers.size) @@ -1119,7 +1119,7 @@ class SpringSearchApplicationTest { val userUUID = UUID.randomUUID() userRepository.save(Users(userFirstName = "Diego", uuid = userUUID)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("uuid:'$userUUID'").build() val robotUsers = userRepository.findAll(specification) Assertions.assertEquals(1, robotUsers.size) @@ -1133,7 +1133,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "Diego", uuid = userUUID)) userRepository.save(Users(userFirstName = "Diego two", uuid = user2UUID)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("uuid!'$userUUID'").build() val robotUsers = userRepository.findAll(specification) Assertions.assertEquals(1, robotUsers.size) @@ -1146,7 +1146,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "jane", userChildrenNumber = 3)) userRepository.save(Users(userFirstName = "joe", userChildrenNumber = 4)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userChildrenNumber<:2").build() val users = userRepository.findAll(specification) Assertions.assertEquals(1, users.size) @@ -1159,7 +1159,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "jane", userChildrenNumber = 3)) userRepository.save(Users(userFirstName = "joe", userChildrenNumber = 4)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userChildrenNumber>:3").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) @@ -1171,7 +1171,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "jane", userChildrenNumber = 3)) userRepository.save(Users(userFirstName = "joe", userChildrenNumber = 4)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userChildrenNumber<3").build() val users = userRepository.findAll(specification) Assertions.assertEquals(1, users.size) @@ -1184,7 +1184,7 @@ class SpringSearchApplicationTest { val janeId = userRepository.save(Users(userFirstName = "jane", userChildrenNumber = 3)).userId userRepository.save(Users(userFirstName = "joe", userChildrenNumber = 4)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName IN [\"john\", \"jane\"]").build() val users = userRepository.findAll(specification) Assertions.assertTrue(setOf(johnId, janeId) == users.map { user -> user.userId }.toSet()) @@ -1196,7 +1196,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "jane", userChildrenNumber = 3)) userRepository.save(Users(userFirstName = "joe", userChildrenNumber = 4)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName IN []").build() val users = userRepository.findAll(specification) Assertions.assertTrue(users.isEmpty()) @@ -1208,7 +1208,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "jane", userChildrenNumber = 3)) val joeId = userRepository.save(Users(userFirstName = "joe", userChildrenNumber = 4)).userId val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName NOT IN [\"john\", \"jane\"]").build() val users = userRepository.findAll(specification) Assertions.assertTrue(setOf(joeId) == users.map { user -> user.userId }.toSet()) @@ -1220,7 +1220,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "jane", userChildrenNumber = 3)) val joeId = userRepository.save(Users(userFirstName = "joe", userChildrenNumber = 4)).userId val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userChildrenNumber NOT IN [2, 3]").build() val users = userRepository.findAll(specification) Assertions.assertTrue(setOf(joeId) == users.map { user -> user.userId }.toSet()) @@ -1232,7 +1232,7 @@ class SpringSearchApplicationTest { val janeId = userRepository.save(Users(userFirstName = "jane", userChildrenNumber = 3)).userId userRepository.save(Users(userFirstName = "joe", userChildrenNumber = 4)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userChildrenNumber IN [2, 3]").build() val users = userRepository.findAll(specification) Assertions.assertTrue(setOf(janeId, johnId) == users.map { user -> user.userId }.toSet()) @@ -1244,7 +1244,7 @@ class SpringSearchApplicationTest { val janeId = userRepository.save(Users(userFirstName = "jane", type = UserType.ADMINISTRATOR)).userId userRepository.save(Users(userFirstName = "joe", type = UserType.MANAGER)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("type IN [ADMINISTRATOR, TEAM_MEMBER]").build() val users = userRepository.findAll(specification) Assertions.assertTrue(setOf(janeId, johnId) == users.map { user -> user.userId }.toSet()) @@ -1258,7 +1258,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "jane", updatedDateAt = LocalDate.parse("2020-01-15"))).userId userRepository.save(Users(userFirstName = "joe", updatedDateAt = LocalDate.parse("2021-01-10"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedDateAt IN ['2020-01-10', '2020-01-15']").build() val users = userRepository.findAll(specification) Assertions.assertTrue(setOf(janeId, johnId) == users.map { user -> user.userId }.toSet()) @@ -1277,7 +1277,7 @@ class SpringSearchApplicationTest { jane.addBook(janeBook) authorRepository.save(jane) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("books IS EMPTY").build() val users = authorRepository.findAll(specification) Assertions.assertTrue(users.isEmpty()) @@ -1296,13 +1296,13 @@ class SpringSearchApplicationTest { jane.addBook(janeBook) authorRepository.save(jane) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("name IS EMPTY").build() Assertions.assertThrows( ResponseStatusException::class.java ) { authorRepository.findAll(specification) } val specification2 = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("name IS NOT EMPTY").build() Assertions.assertThrows( ResponseStatusException::class.java @@ -1319,7 +1319,7 @@ class SpringSearchApplicationTest { jane.name = "jane" authorRepository.save(jane) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("books IS EMPTY").build() val users = authorRepository.findAll(specification) Assertions.assertTrue(users.size == 1) @@ -1337,7 +1337,7 @@ class SpringSearchApplicationTest { jane.name = "jane" authorRepository.save(jane) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("books IS NOT EMPTY").build() val users = authorRepository.findAll(specification) Assertions.assertTrue(users.size == 1) @@ -1357,7 +1357,7 @@ class SpringSearchApplicationTest { jane.addBook(janeBook) authorRepository.save(jane) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("books IS NOT EMPTY").build() val users = authorRepository.findAll(specification) Assertions.assertTrue(users.size == 2) @@ -1372,7 +1372,7 @@ class SpringSearchApplicationTest { jane.name = "jane" authorRepository.save(jane) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("books IS NOT EMPTY").build() val users = authorRepository.findAll(specification) Assertions.assertTrue(users.size == 0) @@ -1386,13 +1386,13 @@ class SpringSearchApplicationTest { jane.name = "jane" authorRepository.save(jane) val spec = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("books IS NULL").build() Assertions.assertThrows( ResponseStatusException::class.java ) { authorRepository.findAll(spec) } val specNotNull = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("books IS NOT NULL").build() Assertions.assertThrows( ResponseStatusException::class.java @@ -1406,7 +1406,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "joe", userChildrenNumber = 5)) userRepository.save(Users(userFirstName = "jean", userChildrenNumber = 10)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userChildrenNumber BETWEEN 4 AND 10").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) @@ -1421,7 +1421,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "joe", updatedDateAt = LocalDate.parse("2020-01-12"))) userRepository.save(Users(userFirstName = "jean", updatedDateAt = LocalDate.parse("2020-01-13"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedDateAt BETWEEN 2020-01-12 AND 2020-01-13").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) @@ -1436,7 +1436,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "joe", updatedDateAt = LocalDate.parse("2020-01-12"))) userRepository.save(Users(userFirstName = "jean", updatedDateAt = LocalDate.parse("2020-01-13"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedDateAt NOT BETWEEN 2020-01-12 AND 2020-01-13").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) @@ -1451,7 +1451,7 @@ class SpringSearchApplicationTest { val joeId = userRepository.save(Users(userFirstName = "joe", updatedDateAt = LocalDate.parse("2020-01-12"))).userId val jeanId = userRepository.save(Users(userFirstName = "jean", updatedDateAt = LocalDate.parse("2020-01-13"))).userId val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedDateAt BETWEEN 2020-01-11 AND 2020-01-13 AND userId IN [$joeId, $jeanId]").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) @@ -1466,7 +1466,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "connor")) userRepository.save(Users(userFirstName = "david")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName BETWEEN 'aaron' AND 'cyrano'").build() val users = userRepository.findAll(specification) Assertions.assertEquals(3, users.size) @@ -1480,7 +1480,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "connor")) userRepository.save(Users(userFirstName = "david")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName NOT BETWEEN 'aaron' AND 'cyrano'").build() val users = userRepository.findAll(specification) Assertions.assertEquals(1, users.size) @@ -1494,7 +1494,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "connor")) userRepository.save(Users(userFirstName = "david")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName > barry'").build() val users = userRepository.findAll(specification) Assertions.assertEquals(3, users.size) @@ -1509,7 +1509,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "connor")) userRepository.save(Users(userFirstName = "david")) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName < barry'").build() val users = userRepository.findAll(specification) Assertions.assertEquals(1, users.size) @@ -1525,7 +1525,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "david")) // create spec with case sensitive flag val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", true) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName : A*").build() val users = userRepository.findAll(specification) Assertions.assertEquals(1, users.size) @@ -1536,32 +1536,32 @@ class SpringSearchApplicationTest { fun badRequestWithWrongSearch() { Assertions.assertThrows(ResponseStatusException::class.java) { SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName : ").build() } Assertions.assertThrows(ResponseStatusException::class.java) { SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedDateAt BETWEEN AND 2020-01-11").build() } Assertions.assertThrows(ResponseStatusException::class.java) { SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("updatedDateAt BETWEEN 2020-01-11 AND").build() } Assertions.assertThrows(ResponseStatusException::class.java) { SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("books IS EMPT ").build() } Assertions.assertThrows(ResponseStatusException::class.java) { SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("books IS NOT EMPT ").build() } Assertions.assertThrows(ResponseStatusException::class.java) { SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userId IN [").build() } } @@ -1573,7 +1573,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "joe", type = UserType.MANAGER)) userRepository.save(Users(userFirstName = "jean", type = null)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("type IS NULL").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) @@ -1588,7 +1588,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "joe", type = UserType.MANAGER)) userRepository.save(Users(userFirstName = "jean", type = null)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("type IS NOT NULL").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) @@ -1603,11 +1603,20 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "joe", type = UserType.MANAGER)) userRepository.save(Users(userFirstName = "jean", type = null)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", true, emptyArray()) ).withSearch("userFirstName IS NOT NULL").build() val users = userRepository.findAll(specification) Assertions.assertEquals(4, users.size) val setNames = users.map { user -> user.userFirstName }.toSet() Assertions.assertEquals(setOf("john", "jane", "joe", "jean"), setNames) } + + @Test + fun canNotSearchABlackListedField() { + Assertions.assertThrows(ResponseStatusException::class.java) { + SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false, arrayOf("userFirstName")) + ).withSearch("userFirstName : A* AND userId : 3").build() + } + } } From a68662f0c0a677067ada5c10d1a01f4d4189ff3a Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:01:36 +0100 Subject: [PATCH 13/27] fix: add missing check for "NULL" value in strategies --- .../sipios/springsearch/strategies/BooleanStrategy.kt | 2 ++ .../sipios/springsearch/strategies/DateStrategy.kt | 1 + .../sipios/springsearch/strategies/DoubleStrategy.kt | 1 + .../springsearch/strategies/DurationStrategy.kt | 1 + .../sipios/springsearch/strategies/FloatStrategy.kt | 1 + .../sipios/springsearch/strategies/InstantStrategy.kt | 1 + .../com/sipios/springsearch/strategies/IntStrategy.kt | 1 + .../springsearch/strategies/LocalDateStrategy.kt | 1 + .../springsearch/strategies/LocalDateTimeStrategy.kt | 1 + .../springsearch/strategies/LocalTimeStrategy.kt | 1 + .../sipios/springsearch/strategies/UUIDStrategy.kt | 2 ++ .../springsearch/SpringSearchApplicationTest.kt | 11 +++++++++++ 12 files changed, 24 insertions(+) diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/BooleanStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/BooleanStrategy.kt index b87c182..2d854be 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/BooleanStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/BooleanStrategy.kt @@ -1,9 +1,11 @@ package com.sipios.springsearch.strategies +import com.sipios.springsearch.SearchOperation import kotlin.reflect.KClass class BooleanStrategy : ParsingStrategy { override fun parse(value: String?, fieldClass: KClass): Any? { + if (value == SearchOperation.NULL) return value return value?.toBoolean() } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/DateStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/DateStrategy.kt index 61f5c69..b4b5597 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/DateStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/DateStrategy.kt @@ -29,6 +29,7 @@ class DateStrategy : ParsingStrategy { } override fun parse(value: String?, fieldClass: KClass): Any? { + if (value == SearchOperation.NULL) return value return standardDateFormat.parse(value) } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/DoubleStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/DoubleStrategy.kt index 7b1ccc0..85cbbe8 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/DoubleStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/DoubleStrategy.kt @@ -24,6 +24,7 @@ class DoubleStrategy : ParsingStrategy { } override fun parse(value: String?, fieldClass: KClass): Any? { + if (value == SearchOperation.NULL) return value return value?.toDouble() } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/DurationStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/DurationStrategy.kt index a722fe2..dee1e03 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/DurationStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/DurationStrategy.kt @@ -25,6 +25,7 @@ class DurationStrategy : ParsingStrategy { } override fun parse(value: String?, fieldClass: KClass): Any? { + if (value == SearchOperation.NULL) return value return Duration.parse(value) } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/FloatStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/FloatStrategy.kt index ea1d1e6..85f292d 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/FloatStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/FloatStrategy.kt @@ -24,6 +24,7 @@ class FloatStrategy : ParsingStrategy { } override fun parse(value: String?, fieldClass: KClass): Any? { + if (value == SearchOperation.NULL) return value return value?.toFloat() } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/InstantStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/InstantStrategy.kt index 66294cd..65af72e 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/InstantStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/InstantStrategy.kt @@ -25,6 +25,7 @@ class InstantStrategy : ParsingStrategy { } override fun parse(value: String?, fieldClass: KClass): Any? { + if (value == SearchOperation.NULL) return value return Instant.parse(value) } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/IntStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/IntStrategy.kt index 21f3863..ba6ee55 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/IntStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/IntStrategy.kt @@ -24,6 +24,7 @@ class IntStrategy : ParsingStrategy { } override fun parse(value: String?, fieldClass: KClass): Any? { + if (value == SearchOperation.NULL) return value return value?.toInt() } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/LocalDateStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/LocalDateStrategy.kt index 2b435f6..c9c0b31 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/LocalDateStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/LocalDateStrategy.kt @@ -25,6 +25,7 @@ class LocalDateStrategy : ParsingStrategy { } override fun parse(value: String?, fieldClass: KClass): Any? { + if (value == SearchOperation.NULL) return value return LocalDate.parse(value) } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/LocalDateTimeStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/LocalDateTimeStrategy.kt index 763dae5..00ef88b 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/LocalDateTimeStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/LocalDateTimeStrategy.kt @@ -25,6 +25,7 @@ class LocalDateTimeStrategy : ParsingStrategy { } override fun parse(value: String?, fieldClass: KClass): Any? { + if (value == SearchOperation.NULL) return value return LocalDateTime.parse(value) } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/LocalTimeStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/LocalTimeStrategy.kt index 7682689..cc8b34b 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/LocalTimeStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/LocalTimeStrategy.kt @@ -25,6 +25,7 @@ class LocalTimeStrategy : ParsingStrategy { } override fun parse(value: String?, fieldClass: KClass): Any? { + if (value == SearchOperation.NULL) return value return LocalTime.parse(value) } } diff --git a/src/main/kotlin/com/sipios/springsearch/strategies/UUIDStrategy.kt b/src/main/kotlin/com/sipios/springsearch/strategies/UUIDStrategy.kt index 22542f2..013eff7 100644 --- a/src/main/kotlin/com/sipios/springsearch/strategies/UUIDStrategy.kt +++ b/src/main/kotlin/com/sipios/springsearch/strategies/UUIDStrategy.kt @@ -1,10 +1,12 @@ package com.sipios.springsearch.strategies +import com.sipios.springsearch.SearchOperation import java.util.UUID import kotlin.reflect.KClass class UUIDStrategy : ParsingStrategy { override fun parse(value: String?, fieldClass: KClass): Any? { + if (value == SearchOperation.NULL) return value return UUID.fromString(value) } } diff --git a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt index 9bcca12..cf3177c 100644 --- a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt +++ b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt @@ -1610,4 +1610,15 @@ class SpringSearchApplicationTest { val setNames = users.map { user -> user.userFirstName }.toSet() Assertions.assertEquals(setOf("john", "jane", "joe", "jean"), setNames) } + + @Test + fun canGetUserWithNullSalary() { + userRepository.save(Users(userFirstName = "john", userSalary = 100.0F)) + userRepository.save(Users(userFirstName = "jane", userSalary = 1000.0F)) + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("userSalary IS NULL").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(0, users.size) + } } From 78cd88dbe11853b3af81be080fcded3529e96ce1 Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:15:14 +0100 Subject: [PATCH 14/27] fix: missing arg in constructor --- .../com/sipios/springsearch/SpringSearchApplicationTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt index f1e844f..bdfb319 100644 --- a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt +++ b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt @@ -1616,7 +1616,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "john", userSalary = 100.0F)) userRepository.save(Users(userFirstName = "jane", userSalary = 1000.0F)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", false, emptyArray()) ).withSearch("userSalary IS NULL").build() val users = userRepository.findAll(specification) Assertions.assertEquals(0, users.size) From 8223e61487f84173cdfc5b2d50167b926a364f9a Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Mon, 6 May 2024 17:03:43 +0200 Subject: [PATCH 15/27] fix: update threshold --- codecov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codecov.yml b/codecov.yml index 3380cf9..3b11048 100644 --- a/codecov.yml +++ b/codecov.yml @@ -3,4 +3,4 @@ coverage: project: default: target: 80% - threshold: 1% + threshold: 2% From a9927b2249e1f8e5ca75ed25b7d9c2b847d49642 Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Mon, 6 May 2024 17:09:20 +0200 Subject: [PATCH 16/27] fix: remove unreachable code --- src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt b/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt index 8107e1c..d5fbcd7 100644 --- a/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt +++ b/src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt @@ -34,10 +34,8 @@ class QueryVisitorImpl(private val searchSpecAnnotation: SearchSpec) : QueryB val key = ctx.key().text val op = if (ctx.IS() != null) { SearchOperation.IS - } else if (ctx.IS_NOT() != null) { - SearchOperation.IS_NOT } else { - throw IllegalArgumentException("Invalid operation") + SearchOperation.IS_NOT } return toSpec(key, op, ctx.is_value().text) } From 079df6b099a43e1900ca8b9a015f9403dbc05a9e Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Mon, 13 May 2024 10:56:21 +0200 Subject: [PATCH 17/27] doc: add blacklist doc --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index d3f0d18..56dbaf2 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,14 @@ Request : `/cars?search=creationyear:2018 AND price<300000 AND (color:Yellow OR 15. Using the BETWEEN operator Request : `/cars?search=creationyear BETWEEN 2017 AND 2019` +## Blocking the search on a field +```java +@GetMapping +public List getUsers(@SearchSpec(blackListedFields = {"password"}) Specification specs) { + return userRepository.findAll(Specification.where(specs)); +} +``` + ## Troubleshooting From ae8e7bc4766f9a6f1092baeb0b1a673893eb5523 Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Wed, 15 May 2024 19:14:58 +0200 Subject: [PATCH 18/27] test: add test for UUID null --- .../springsearch/SpringSearchApplicationTest.kt | 13 +++++++++++++ src/test/kotlin/com/sipios/springsearch/Users.kt | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt index cf3177c..2c64818 100644 --- a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt +++ b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt @@ -1621,4 +1621,17 @@ class SpringSearchApplicationTest { val users = userRepository.findAll(specification) Assertions.assertEquals(0, users.size) } + + @Test + fun canGetUsersWithUUIDNull() { + val userUUID = UUID.randomUUID() + userRepository.save(Users(userFirstName = "Diego", uuid = userUUID)) + userRepository.save(Users(userFirstName = "Diego two", uuid = null)) + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("uuid IS NULL").build() + val robotUsers = userRepository.findAll(specification) + Assertions.assertEquals(1, robotUsers.size) + Assertions.assertEquals(null, robotUsers[0].uuid) + } } diff --git a/src/test/kotlin/com/sipios/springsearch/Users.kt b/src/test/kotlin/com/sipios/springsearch/Users.kt index 24a47cc..884de21 100644 --- a/src/test/kotlin/com/sipios/springsearch/Users.kt +++ b/src/test/kotlin/com/sipios/springsearch/Users.kt @@ -67,5 +67,5 @@ data class Users( var type: UserType? = UserType.TEAM_MEMBER, @Column - var uuid: UUID = UUID.randomUUID() + var uuid: UUID? = UUID.randomUUID() ) From e6e45c2edd8f82ff96c5f7f8caf0263a55ba9bf5 Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Wed, 15 May 2024 19:28:05 +0200 Subject: [PATCH 19/27] test: canGetUsersWithUpdatedDateAtNull --- .../springsearch/SpringSearchApplicationTest.kt | 17 +++++++++++++++++ .../kotlin/com/sipios/springsearch/Users.kt | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt index 2c64818..a12b1c4 100644 --- a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt +++ b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt @@ -1634,4 +1634,21 @@ class SpringSearchApplicationTest { Assertions.assertEquals(1, robotUsers.size) Assertions.assertEquals(null, robotUsers[0].uuid) } + + // Test for LocalDate is null + @Test + fun canGetUsersWithUpdatedDateAtNull() { + userRepository.save(Users(userFirstName = "john", updatedDateAt = LocalDate.parse("2020-01-10"))) + userRepository.save(Users(userFirstName = "jane", updatedDateAt = LocalDate.parse("2020-01-11"))) + userRepository.save(Users(userFirstName = "joe", updatedDateAt = null)) + userRepository.save(Users(userFirstName = "jean", updatedDateAt = null)) + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("updatedDateAt IS NULL").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(2, users.size) + val setNames = users.map { user -> user.userFirstName }.toSet() + Assertions.assertEquals(setOf("joe", "jean"), setNames) + } + } diff --git a/src/test/kotlin/com/sipios/springsearch/Users.kt b/src/test/kotlin/com/sipios/springsearch/Users.kt index 884de21..63c82b4 100644 --- a/src/test/kotlin/com/sipios/springsearch/Users.kt +++ b/src/test/kotlin/com/sipios/springsearch/Users.kt @@ -55,7 +55,7 @@ data class Users( var updatedTimeAt: LocalTime = LocalTime.now(), @Column - var updatedDateAt: LocalDate = LocalDate.now(), + var updatedDateAt: LocalDate? = LocalDate.now(), @Column var updatedInstantAt: Instant = Instant.now(), From 131c7abdeaeac4387b4a0b90635ad16d279fffc5 Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Thu, 16 May 2024 09:30:32 +0200 Subject: [PATCH 20/27] test: LocalDateTime is not null --- .../springsearch/SpringSearchApplicationTest.kt | 15 ++++++++++++++- src/test/kotlin/com/sipios/springsearch/Users.kt | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt index a12b1c4..0e20a92 100644 --- a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt +++ b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt @@ -1635,7 +1635,6 @@ class SpringSearchApplicationTest { Assertions.assertEquals(null, robotUsers[0].uuid) } - // Test for LocalDate is null @Test fun canGetUsersWithUpdatedDateAtNull() { userRepository.save(Users(userFirstName = "john", updatedDateAt = LocalDate.parse("2020-01-10"))) @@ -1651,4 +1650,18 @@ class SpringSearchApplicationTest { Assertions.assertEquals(setOf("joe", "jean"), setNames) } + @Test + fun canGetUsersWithUpdatedDateTimeAtNotNull() { + userRepository.save(Users(userFirstName = "john", updatedAt = LocalDateTime.parse("2020-01-10T10:15:30"))) + userRepository.save(Users(userFirstName = "jane", updatedAt = LocalDateTime.parse("2020-01-11T10:15:30"))) + userRepository.save(Users(userFirstName = "joe", updatedAt = null)) + userRepository.save(Users(userFirstName = "jean", updatedAt = LocalDateTime.parse("2020-01-13T10:15:30"))) + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("updatedAt IS NOT NULL").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(3, users.size) + val setNames = users.map { user -> user.userFirstName }.toSet() + Assertions.assertEquals(setOf("john", "jane", "jean"), setNames) + } } diff --git a/src/test/kotlin/com/sipios/springsearch/Users.kt b/src/test/kotlin/com/sipios/springsearch/Users.kt index 63c82b4..4f4eec7 100644 --- a/src/test/kotlin/com/sipios/springsearch/Users.kt +++ b/src/test/kotlin/com/sipios/springsearch/Users.kt @@ -49,7 +49,7 @@ data class Users( var createdAt: Date = Date(), @Column - var updatedAt: LocalDateTime = LocalDateTime.now(), + var updatedAt: LocalDateTime? = LocalDateTime.now(), @Column var updatedTimeAt: LocalTime = LocalTime.now(), From f145cd50d1300d5b36a224adfca4a63b5f086c56 Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Thu, 16 May 2024 09:41:00 +0200 Subject: [PATCH 21/27] test: Int Boolean and Date is null --- .../SpringSearchApplicationTest.kt | 44 ++++++++++++++++++- .../kotlin/com/sipios/springsearch/Users.kt | 9 ++-- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt index 0e20a92..ec90db9 100644 --- a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt +++ b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt @@ -7,13 +7,13 @@ import java.time.Instant import java.time.LocalDate import java.time.LocalDateTime import java.time.LocalTime -import java.util.UUID import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.transaction.annotation.Transactional import org.springframework.web.server.ResponseStatusException +import java.util.* @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = [SpringSearchApplication::class]) @Transactional @@ -1664,4 +1664,46 @@ class SpringSearchApplicationTest { val setNames = users.map { user -> user.userFirstName }.toSet() Assertions.assertEquals(setOf("john", "jane", "jean"), setNames) } + @Test + fun canGetUsersWithActiveNull() { + userRepository.save(Users(userFirstName = "john", active = true)) + userRepository.save(Users(userFirstName = "jane", active = false)) + userRepository.save(Users(userFirstName = "joe", active = null)) + userRepository.save(Users(userFirstName = "jean", active = null)) + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("active IS NULL").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(2, users.size) + val setNames = users.map { user -> user.userFirstName }.toSet() + Assertions.assertEquals(setOf("joe", "jean"), setNames) + } + @Test + fun canGetUsersWithUserChildrenNumberNull() { + userRepository.save(Users(userFirstName = "john", userChildrenNumber = 2)) + userRepository.save(Users(userFirstName = "jane", userChildrenNumber = 3)) + userRepository.save(Users(userFirstName = "joe", userChildrenNumber = null)) + userRepository.save(Users(userFirstName = "jean", userChildrenNumber = null)) + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("userChildrenNumber IS NULL").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(2, users.size) + val setNames = users.map { user -> user.userFirstName }.toSet() + Assertions.assertEquals(setOf("joe", "jean"), setNames) + } + @Test + fun canGetUsersWithCreatedAtNull() { + userRepository.save(Users(userFirstName = "john", createdAt = Date())) + userRepository.save(Users(userFirstName = "jane", createdAt = Date())) + userRepository.save(Users(userFirstName = "joe", createdAt = null)) + userRepository.save(Users(userFirstName = "jean", createdAt = null)) + val specification = SpecificationsBuilder( + SearchSpec::class.constructors.first().call("", false) + ).withSearch("createdAt IS NULL").build() + val users = userRepository.findAll(specification) + Assertions.assertEquals(2, users.size) + val setNames = users.map { user -> user.userFirstName }.toSet() + Assertions.assertEquals(setOf("joe", "jean"), setNames) + } } diff --git a/src/test/kotlin/com/sipios/springsearch/Users.kt b/src/test/kotlin/com/sipios/springsearch/Users.kt index 4f4eec7..8b91107 100644 --- a/src/test/kotlin/com/sipios/springsearch/Users.kt +++ b/src/test/kotlin/com/sipios/springsearch/Users.kt @@ -37,7 +37,7 @@ data class Users( var userAddress: String = "1 rue de l'angleterre", @Column(name = "NumberOfChildren") - var userChildrenNumber: Int = 3, + var userChildrenNumber: Int? = 3, @Column(name = "Salary") var userSalary: Float = 3000.0F, @@ -46,7 +46,7 @@ data class Users( var userAgeInSeconds: Double = 1261440000.0, @Column - var createdAt: Date = Date(), + var createdAt: Date? = Date(), @Column var updatedAt: LocalDateTime? = LocalDateTime.now(), @@ -67,5 +67,8 @@ data class Users( var type: UserType? = UserType.TEAM_MEMBER, @Column - var uuid: UUID? = UUID.randomUUID() + var uuid: UUID? = UUID.randomUUID(), + + @Column + var active: Boolean? = true ) From 6e8aba28e236813740e8c3e3c58843fca1241589 Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Thu, 16 May 2024 10:06:19 +0200 Subject: [PATCH 22/27] trigger-ci From e1aace94408b45a3e6b6cc268335eda82f876471 Mon Sep 17 00:00:00 2001 From: Vincent Escoffier <56202201+reifocS@users.noreply.github.com> Date: Thu, 16 May 2024 17:18:14 +0200 Subject: [PATCH 23/27] trigger-ci From 31c675efaadfab402eceef8fa23f546248bfd5cb Mon Sep 17 00:00:00 2001 From: Vincent Escoffier Date: Thu, 20 Jun 2024 15:30:32 +0200 Subject: [PATCH 24/27] fix: ktlint --- .../springsearch/SpringSearchApplicationTest.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt index ec90db9..11811f0 100644 --- a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt +++ b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt @@ -2,18 +2,20 @@ package com.sipios.springsearch import com.fasterxml.jackson.databind.util.StdDateFormat import com.sipios.springsearch.anotation.SearchSpec -import java.time.Duration -import java.time.Instant -import java.time.LocalDate -import java.time.LocalDateTime -import java.time.LocalTime import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.transaction.annotation.Transactional import org.springframework.web.server.ResponseStatusException -import java.util.* +import java.time.Duration +import java.time.Instant +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.LocalTime +import java.util.Date +import java.util.UUID + @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = [SpringSearchApplication::class]) @Transactional From dee880f437be1253a14f6a64b019b30c7902879a Mon Sep 17 00:00:00 2001 From: Vincent Escoffier Date: Thu, 20 Jun 2024 15:34:27 +0200 Subject: [PATCH 25/27] fix: ktlint --- .jpb/jpb-settings.xml | 6 ++++++ .../com/sipios/springsearch/SpringSearchApplicationTest.kt | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .jpb/jpb-settings.xml diff --git a/.jpb/jpb-settings.xml b/.jpb/jpb-settings.xml new file mode 100644 index 0000000..fb1f21c --- /dev/null +++ b/.jpb/jpb-settings.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt index 11811f0..67e7cdb 100644 --- a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt +++ b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt @@ -16,7 +16,6 @@ import java.time.LocalTime import java.util.Date import java.util.UUID - @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = [SpringSearchApplication::class]) @Transactional class SpringSearchApplicationTest { From 83555809a56e0cbdacd2e15d5bb3fb90c923c21d Mon Sep 17 00:00:00 2001 From: Vincent Escoffier Date: Thu, 20 Jun 2024 15:49:17 +0200 Subject: [PATCH 26/27] fix: ktlint --- .../springsearch/SpringSearchApplicationTest.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt index 67e7cdb..632e180 100644 --- a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt +++ b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt @@ -2,12 +2,6 @@ package com.sipios.springsearch import com.fasterxml.jackson.databind.util.StdDateFormat import com.sipios.springsearch.anotation.SearchSpec -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.transaction.annotation.Transactional -import org.springframework.web.server.ResponseStatusException import java.time.Duration import java.time.Instant import java.time.LocalDate @@ -15,6 +9,12 @@ import java.time.LocalDateTime import java.time.LocalTime import java.util.Date import java.util.UUID +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.transaction.annotation.Transactional +import org.springframework.web.server.ResponseStatusException @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = [SpringSearchApplication::class]) @Transactional From cdb879b5139c3a6a1e3b03f69beabcc12f4c1b7b Mon Sep 17 00:00:00 2001 From: Vincent Escoffier Date: Thu, 20 Jun 2024 15:58:54 +0200 Subject: [PATCH 27/27] fix: test missing arguments --- .../springsearch/SpringSearchApplicationTest.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt index a70737f..0171906 100644 --- a/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt +++ b/src/test/kotlin/com/sipios/springsearch/SpringSearchApplicationTest.kt @@ -1638,7 +1638,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "Diego", uuid = userUUID)) userRepository.save(Users(userFirstName = "Diego two", uuid = null)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", false, emptyArray()) ).withSearch("uuid IS NULL").build() val robotUsers = userRepository.findAll(specification) Assertions.assertEquals(1, robotUsers.size) @@ -1652,7 +1652,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "joe", updatedDateAt = null)) userRepository.save(Users(userFirstName = "jean", updatedDateAt = null)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", false, emptyArray()) ).withSearch("updatedDateAt IS NULL").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) @@ -1667,7 +1667,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "joe", updatedAt = null)) userRepository.save(Users(userFirstName = "jean", updatedAt = LocalDateTime.parse("2020-01-13T10:15:30"))) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", false, emptyArray()) ).withSearch("updatedAt IS NOT NULL").build() val users = userRepository.findAll(specification) Assertions.assertEquals(3, users.size) @@ -1681,7 +1681,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "joe", active = null)) userRepository.save(Users(userFirstName = "jean", active = null)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", false, emptyArray()) ).withSearch("active IS NULL").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) @@ -1695,7 +1695,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "joe", userChildrenNumber = null)) userRepository.save(Users(userFirstName = "jean", userChildrenNumber = null)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", false, emptyArray()) ).withSearch("userChildrenNumber IS NULL").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size) @@ -1709,7 +1709,7 @@ class SpringSearchApplicationTest { userRepository.save(Users(userFirstName = "joe", createdAt = null)) userRepository.save(Users(userFirstName = "jean", createdAt = null)) val specification = SpecificationsBuilder( - SearchSpec::class.constructors.first().call("", false) + SearchSpec::class.constructors.first().call("", false, emptyArray()) ).withSearch("createdAt IS NULL").build() val users = userRepository.findAll(specification) Assertions.assertEquals(2, users.size)