Skip to content

Commit

Permalink
Merge pull request #66 from sipios/chore/bump-java-version
Browse files Browse the repository at this point in the history
bump spring, java, and kotlin versions and refactor
  • Loading branch information
phmz authored Jan 10, 2024
2 parents 08e5ca6 + abf051a commit 26e9b6c
Show file tree
Hide file tree
Showing 22 changed files with 172 additions and 115 deletions.
7 changes: 6 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ version: 2.1
orbs:
codecov: codecov/[email protected]

parameters:
jdk-image:
type: string
default: cimg/openjdk:17.0

jobs:
test:
environment:
MAVEN_OPTS: -Xmx3200m
docker:
- image: circleci/openjdk:11-jdk
- image: << pipeline.parameters.jdk-image >>
working_directory: ~/project
steps:
- checkout
Expand Down
1 change: 1 addition & 0 deletions .java-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
17.0
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ Please note that providing such a feature on your API does not come without risk
<!-- GETTING STARTED -->
## Getting Started

**Requirements** : JDK 8 or more.
**Requirements** : JDK 17 or more.
To get a local copy up and running follow these simple steps.

### Installation
Expand All @@ -91,7 +91,7 @@ Add the repo to your project inside your `pom.xml` file
<dependency>
<groupId>com.sipios</groupId>
<artifactId>spring-search</artifactId>
<version>0.2.0</version>
<version>0.3.0</version>
</dependency>
```

Expand Down Expand Up @@ -175,6 +175,40 @@ Request : `/cars?search=options.transmission:Auto`
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)

<!-- TROUBLESHOOTING -->
## Troubleshooting

If you get the following error ⬇️

> No primary or default constructor found for interface org.springframework.data.jpa.domain.Specification
You are free to opt for either of the two following solutions :
1. Add a `@Configuration` class to add our argument resolver to your project
```kotlin
// Kotlin
@Configuration
class SpringSearchResolverConf : WebMvcConfigurer {
override fun addArgumentResolvers(argumentResolvers: MutableList<HandlerMethodArgumentResolver>) {
argumentResolvers.add(SearchSpecificationResolver())
}
}
```
```java
// Java
@Configuration
public class SpringSearchResolverConf implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new SearchSpecificationResolver());
}
}
```

2. Add a `@ComponentScan` annotation to your project configuration class
```java
@ComponentScan(basePackages = {"com.your-application", "com.sipios.springsearch"})
```

<!-- ROADMAP -->
## Roadmap

Expand Down
61 changes: 39 additions & 22 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,26 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.1</version>
<version>3.1.7</version>
<relativePath /> <!-- lookup parent from repository --> <!-- lookup parent from repository -->
</parent>
<groupId>com.sipios</groupId>
<artifactId>spring-search</artifactId>
<version>0.2.5</version>
<version>0.2.6</version>
<name>spring-search</name>
<description>API for generating spring boot database queries</description>

<properties>
<java.version>1.8</java.version>
<kotlin.version>1.6.20</kotlin.version>
<antl4.version>4.9</antl4.version>
<java.version>17</java.version>
<kotlin.version>1.9.22</kotlin.version>
<jacoco-maven-plugin.version>0.8.4</jacoco-maven-plugin.version>
<h2.version>2.2.220</h2.version>
<jackson-databind.version>2.16.0</jackson-databind.version>
<snakeyaml.version>2.0</snakeyaml.version>
<dependency-check-maven.version>9.0.7</dependency-check-maven.version>
<antl4.version>4.13.1</antl4.version>
<grammar.package>com.sipios.springsearch.grammar</grammar.package>
<grammar.directory>com/sipios/springsearch/grammar</grammar.directory>
<jacoco-maven-plugin.version>0.8.4</jacoco-maven-plugin.version>
<project.testresult.directory>${project.build.directory}/test-results</project.testresult.directory>
</properties>

Expand All @@ -32,6 +36,7 @@
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2.version}</version>
<scope>test</scope>
</dependency>
<dependency>
Expand All @@ -44,6 +49,18 @@
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-databind.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
Expand All @@ -52,10 +69,6 @@
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
Expand All @@ -79,11 +92,6 @@
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>

<build>
Expand Down Expand Up @@ -112,18 +120,14 @@
<outputDirectory>${project.build.directory}/generated-sources/antlr4/${grammar.directory}</outputDirectory>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
</goals>
Expand Down Expand Up @@ -161,7 +165,7 @@
<sourceDir>src/main/kotlin</sourceDir>
<sourceDir>target/generated-sources/antlr4</sourceDir>
</sourceDirs>
<jvmTarget>1.8</jvmTarget>
<jvmTarget>17</jvmTarget>
</configuration>
<dependencies>
<dependency>
Expand Down Expand Up @@ -291,6 +295,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<executions>
<execution>
<id>compile</id>
Expand Down Expand Up @@ -351,7 +356,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.5</version>
<version>3.1.0</version>
<executions>
<execution>
<id>sign-artifacts</id>
Expand All @@ -368,6 +373,18 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>${dependency-check-maven.version}</version>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<scm>
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/com/sipios/springsearch/QueryVisitorImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import com.sipios.springsearch.grammar.QueryParser
import org.springframework.data.jpa.domain.Specification

class QueryVisitorImpl<T>(private val searchSpecAnnotation: SearchSpec) : QueryBaseVisitor<Specification<T>>() {
private val ValueRegExp = Regex(pattern = "^(\\*?)(.+?)(\\*?)$")
private val valueRegExp = Regex(pattern = "^(\\*?)(.+?)(\\*?)$")
override fun visitOpQuery(ctx: QueryParser.OpQueryContext): Specification<T> {
val left = visit(ctx.left)
val right = visit(ctx.right)
Expand Down Expand Up @@ -43,7 +43,7 @@ class QueryVisitorImpl<T>(private val searchSpecAnnotation: SearchSpec) : QueryB
.replace("\\'", "'")
}

val matchResult = this.ValueRegExp.find(value!!)
val matchResult = this.valueRegExp.find(value!!)
val criteria = SearchCriteria(
key,
op,
Expand Down
23 changes: 11 additions & 12 deletions src/main/kotlin/com/sipios/springsearch/SearchCriteria.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,19 @@ class SearchCriteria // Change EQUALS into ENDS_WITH, CONTAINS, STARTS_WITH base
// Change EQUALS into ENDS_WITH, CONTAINS, STARTS_WITH based on the presence of * in the value
val startsWithAsterisk = prefix != null && prefix.contains(SearchOperation.ZERO_OR_MORE_REGEX)
val endsWithAsterisk = suffix != null && suffix.contains(SearchOperation.ZERO_OR_MORE_REGEX)
if (op === SearchOperation.EQUALS && startsWithAsterisk && endsWithAsterisk) {
op = SearchOperation.CONTAINS
} else if (op === SearchOperation.EQUALS && startsWithAsterisk) {
op = SearchOperation.ENDS_WITH
} else if (op === SearchOperation.EQUALS && endsWithAsterisk) {
op = SearchOperation.STARTS_WITH
op = when {
op === SearchOperation.EQUALS && startsWithAsterisk && endsWithAsterisk -> SearchOperation.CONTAINS
op === SearchOperation.EQUALS && startsWithAsterisk -> SearchOperation.ENDS_WITH
op === SearchOperation.EQUALS && endsWithAsterisk -> SearchOperation.STARTS_WITH
else -> op
}

// Change NOT_EQUALS into DOESNT_END_WITH, DOESNT_CONTAIN, DOESNT_START_WITH based on the presence of * in the value
if (op === SearchOperation.NOT_EQUALS && startsWithAsterisk && endsWithAsterisk) {
op = SearchOperation.DOESNT_CONTAIN
} else if (op === SearchOperation.NOT_EQUALS && startsWithAsterisk) {
op = SearchOperation.DOESNT_END_WITH
} else if (op === SearchOperation.NOT_EQUALS && endsWithAsterisk) {
op = SearchOperation.DOESNT_START_WITH
op = when {
op === SearchOperation.NOT_EQUALS && startsWithAsterisk && endsWithAsterisk -> SearchOperation.DOESNT_CONTAIN
op === SearchOperation.NOT_EQUALS && startsWithAsterisk -> SearchOperation.DOESNT_END_WITH
op === SearchOperation.NOT_EQUALS && endsWithAsterisk -> SearchOperation.DOESNT_START_WITH
else -> op
}
}
this.operation = op
Expand Down
10 changes: 5 additions & 5 deletions src/main/kotlin/com/sipios/springsearch/SpecificationImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package com.sipios.springsearch

import com.sipios.springsearch.anotation.SearchSpec
import com.sipios.springsearch.strategies.ParsingStrategy
import jakarta.persistence.criteria.CriteriaBuilder
import jakarta.persistence.criteria.CriteriaQuery
import jakarta.persistence.criteria.Path
import jakarta.persistence.criteria.Predicate
import jakarta.persistence.criteria.Root
import java.util.ArrayList
import javax.persistence.criteria.CriteriaBuilder
import javax.persistence.criteria.CriteriaQuery
import javax.persistence.criteria.Path
import javax.persistence.criteria.Predicate
import javax.persistence.criteria.Root
import org.springframework.data.jpa.domain.Specification
import org.springframework.http.HttpStatus
import org.springframework.web.server.ResponseStatusException
Expand Down
10 changes: 5 additions & 5 deletions src/main/kotlin/com/sipios/springsearch/SpecificationsBuilder.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.sipios.springsearch

import com.sipios.springsearch.anotation.SearchSpec
import javax.persistence.criteria.CriteriaBuilder
import javax.persistence.criteria.CriteriaQuery
import javax.persistence.criteria.Predicate
import javax.persistence.criteria.Root
import jakarta.persistence.criteria.CriteriaBuilder
import jakarta.persistence.criteria.CriteriaQuery
import jakarta.persistence.criteria.Predicate
import jakarta.persistence.criteria.Root
import org.springframework.data.jpa.domain.Specification

class SpecificationsBuilder<U>(searchSpecAnnotation: SearchSpec) {
Expand All @@ -20,7 +20,7 @@ class SpecificationsBuilder<U>(searchSpecAnnotation: SearchSpec) {

/**
* This function expect a search string to have been provided.
* The search string has been transformed into a Expression Queue with the format: [OR, value>100, AND, value<1000, label:*MONO*]
* The search string has been transformed into an Expression Queue with the format: [OR, value>100, AND, value<1000, label:*MONO*]
*
* @return A list of specification used to filter the underlying object using JPA specifications
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ annotation class SearchSpec(
val searchParam: String = "search",

/**
* A flag to indicate if the search needs to be case sensitive or not
* A flag to indicate if the search needs to be case-sensitive or not
*/
val caseSensitiveFlag: Boolean = true
)
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class SearchSpecificationResolver : HandlerMethodArgumentResolver {
private fun <T> buildSpecification(specClass: Class<T>, search: String?, searchSpecAnnotation: SearchSpec): Specification<T>? {
logger.debug("Building specification for class {}", specClass)
logger.debug("Search value found is {}", search)
if (search == null || search.isEmpty()) {
if (search.isNullOrEmpty()) {
return null
}
val specBuilder = SpecificationsBuilder<T>(searchSpecAnnotation)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package com.sipios.springsearch.strategies

import com.fasterxml.jackson.databind.util.StdDateFormat
import com.sipios.springsearch.SearchOperation
import jakarta.persistence.criteria.CriteriaBuilder
import jakarta.persistence.criteria.Path
import jakarta.persistence.criteria.Predicate
import java.text.DateFormat
import java.util.Date
import javax.persistence.criteria.CriteriaBuilder
import javax.persistence.criteria.Path
import javax.persistence.criteria.Predicate
import kotlin.reflect.KClass

class DateStrategy : ParsingStrategy {
Expand All @@ -20,8 +20,8 @@ class DateStrategy : ParsingStrategy {
value: Any?
): Predicate? {
return when (ops) {
SearchOperation.GREATER_THAN -> builder.greaterThan(path.get(fieldName), value as Date)
SearchOperation.LESS_THAN -> builder.lessThan(path.get(fieldName), value as Date)
SearchOperation.GREATER_THAN -> builder.greaterThan(path[fieldName], value as Date)
SearchOperation.LESS_THAN -> builder.lessThan(path[fieldName], value as Date)
else -> super.buildPredicate(builder, path, fieldName, ops, value)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.sipios.springsearch.strategies

import com.sipios.springsearch.SearchOperation
import javax.persistence.criteria.CriteriaBuilder
import javax.persistence.criteria.Path
import javax.persistence.criteria.Predicate
import jakarta.persistence.criteria.CriteriaBuilder
import jakarta.persistence.criteria.Path
import jakarta.persistence.criteria.Predicate
import kotlin.reflect.KClass

class DoubleStrategy : ParsingStrategy {
Expand All @@ -15,8 +15,8 @@ class DoubleStrategy : ParsingStrategy {
value: Any?
): Predicate? {
return when (ops) {
SearchOperation.GREATER_THAN -> builder.greaterThan(path.get(fieldName), value as Double)
SearchOperation.LESS_THAN -> builder.lessThan(path.get(fieldName), value as Double)
SearchOperation.GREATER_THAN -> builder.greaterThan(path[fieldName], value as Double)
SearchOperation.LESS_THAN -> builder.lessThan(path[fieldName], value as Double)
else -> super.buildPredicate(builder, path, fieldName, ops, value)
}
}
Expand Down
Loading

0 comments on commit 26e9b6c

Please sign in to comment.