-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split off implementation that allows supplying arbitrary QueryDSL pre…
…dicates This is a focussed, reusable piece of code that is not tightly bound to the Thunx ABAC system, and that is reusable for other purposes without having to use all of thunx.
- Loading branch information
1 parent
e2735b2
commit e4b0179
Showing
28 changed files
with
812 additions
and
184 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
plugins { | ||
id 'java-library' | ||
id 'maven-publish' | ||
} | ||
|
||
dependencies { | ||
internalPlatform platform(project(':thunx-dependencies')) | ||
|
||
compileOnly 'org.projectlombok:lombok' | ||
annotationProcessor 'org.projectlombok:lombok' | ||
|
||
api 'com.querydsl:querydsl-core' | ||
api 'org.springframework:spring-core' | ||
|
||
implementation 'org.springframework.data:spring-data-rest-webmvc' | ||
|
||
compileOnly("org.springframework.boot:spring-boot-autoconfigure") { | ||
because 'used for autoconfiguration annotations' | ||
} | ||
|
||
testCompileOnly 'org.projectlombok:lombok' | ||
testAnnotationProcessor 'org.projectlombok:lombok' | ||
|
||
testAnnotationProcessor 'jakarta.persistence:jakarta.persistence-api' | ||
testAnnotationProcessor 'com.querydsl:querydsl-apt::jakarta' | ||
|
||
testImplementation 'org.springframework.boot:spring-boot-starter-test' | ||
testImplementation 'org.springframework.boot:spring-boot-testcontainers' | ||
testImplementation 'org.springframework.boot:spring-boot-starter-data-rest' | ||
testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa' | ||
testImplementation 'com.querydsl:querydsl-jpa::jakarta' | ||
|
||
testImplementation "org.assertj:assertj-core" | ||
testImplementation "org.junit.jupiter:junit-jupiter-api" | ||
|
||
testImplementation 'org.testcontainers:testcontainers' | ||
testImplementation 'org.testcontainers:junit-jupiter' | ||
testImplementation 'org.testcontainers:postgresql' | ||
testRuntimeOnly 'org.postgresql:postgresql' | ||
} | ||
|
||
test { | ||
useJUnitPlatform() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
description=Inject additional QueryDSL predicates in Spring Data REST repositories |
22 changes: 22 additions & 0 deletions
22
...x/spring/data/querydsl/predicate/injector/repository/RepositoryInvokerAdapterFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.contentgrid.thunx.spring.data.querydsl.predicate.injector.repository; | ||
|
||
import com.contentgrid.thunx.spring.data.querydsl.predicate.injector.resolver.OperationPredicates; | ||
import com.querydsl.core.types.Predicate; | ||
import org.springframework.data.repository.support.RepositoryInvoker; | ||
|
||
/** | ||
* Adapts a {@link RepositoryInvoker} with one applies the {@link Predicate} to results | ||
*/ | ||
@FunctionalInterface | ||
public interface RepositoryInvokerAdapterFactory { | ||
|
||
/** | ||
* Adapt a repository invoker to apply the supplied QueryDSL predicate | ||
* | ||
* @param repositoryInvoker Original invoker | ||
* @param domainType Domain class for which the invoker is requested | ||
* @param predicate Predicate to apply to the invoker | ||
* @return An invoker that applies the predicate | ||
*/ | ||
RepositoryInvoker adaptRepositoryInvoker(RepositoryInvoker repositoryInvoker, Class<?> domainType, OperationPredicates predicate); | ||
} |
54 changes: 54 additions & 0 deletions
54
...data/querydsl/predicate/injector/resolver/CollectionFilteringOnlyOperationPredicates.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package com.contentgrid.thunx.spring.data.querydsl.predicate.injector.resolver; | ||
|
||
import com.contentgrid.thunx.spring.data.querydsl.predicate.injector.repository.RepositoryInvokerAdapterFactory; | ||
import com.querydsl.core.types.ExpressionUtils; | ||
import com.querydsl.core.types.Predicate; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
/** | ||
* Only applies predicates on the collection of entities | ||
* <p> | ||
* This implementation of {@link OperationPredicates} is the only implementation that can be used with the out-of-the | ||
* box {@link RepositoryInvokerAdapterFactory} | ||
*/ | ||
@RequiredArgsConstructor | ||
public class CollectionFilteringOnlyOperationPredicates implements OperationPredicates { | ||
|
||
private final Predicate predicate; | ||
|
||
@Override | ||
public OperationPredicates and(OperationPredicates predicate) { | ||
if (predicate instanceof CollectionFilteringOnlyOperationPredicates) { | ||
return new CollectionFilteringOnlyOperationPredicates(ExpressionUtils.and( | ||
this.readPredicate(), | ||
predicate.readPredicate() | ||
)); | ||
} | ||
return OperationPredicates.super.and(predicate); | ||
} | ||
|
||
@Override | ||
public Predicate readPredicate() { | ||
return predicate; | ||
} | ||
|
||
@Override | ||
public Predicate afterCreatePredicate() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public Predicate beforeUpdatePredicate() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public Predicate afterUpdatePredicate() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public Predicate beforeDeletePredicate() { | ||
return null; | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
.../thunx/spring/data/querydsl/predicate/injector/resolver/CompositeOperationPredicates.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package com.contentgrid.thunx.spring.data.querydsl.predicate.injector.resolver; | ||
|
||
import com.querydsl.core.types.ExpressionUtils; | ||
import com.querydsl.core.types.Predicate; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.function.Function; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.lang.Nullable; | ||
|
||
@RequiredArgsConstructor | ||
class CompositeOperationPredicates implements OperationPredicates { | ||
private final List<OperationPredicates> predicates; | ||
|
||
@Override | ||
public OperationPredicates and(OperationPredicates predicate) { | ||
var copy = new ArrayList<>(predicates); | ||
copy.add(predicate); | ||
return new CompositeOperationPredicates(copy); | ||
} | ||
|
||
@Nullable | ||
private Predicate combine(Function<OperationPredicates, Predicate> extractor) { | ||
return predicates.stream() | ||
.map(extractor) | ||
.reduce(ExpressionUtils::and) | ||
.orElse(null); | ||
} | ||
|
||
@Override | ||
public Predicate readPredicate() { | ||
return combine(OperationPredicates::readPredicate); | ||
} | ||
|
||
@Override | ||
public Predicate afterCreatePredicate() { | ||
return combine(OperationPredicates::afterCreatePredicate); | ||
} | ||
|
||
@Override | ||
public Predicate beforeUpdatePredicate() { | ||
return combine(OperationPredicates::beforeUpdatePredicate); | ||
} | ||
|
||
@Override | ||
public Predicate afterUpdatePredicate() { | ||
return combine(OperationPredicates::afterUpdatePredicate); | ||
} | ||
|
||
@Override | ||
public Predicate beforeDeletePredicate() { | ||
return combine(OperationPredicates::beforeDeletePredicate); | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
...ntentgrid/thunx/spring/data/querydsl/predicate/injector/resolver/OperationPredicates.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package com.contentgrid.thunx.spring.data.querydsl.predicate.injector.resolver; | ||
|
||
import com.querydsl.core.types.Predicate; | ||
import java.util.List; | ||
import org.springframework.lang.Nullable; | ||
|
||
/** | ||
* A set of QueryDSL predicates that apply to different operations that can be executed on an entity | ||
*/ | ||
public interface OperationPredicates { | ||
|
||
/** | ||
* Combines this predicate with a different predicate | ||
* | ||
* @param predicate The predicate to AND together with | ||
* @return The combined predicate | ||
*/ | ||
default OperationPredicates and(OperationPredicates predicate) { | ||
return new CompositeOperationPredicates(List.of(this, predicate)); | ||
} | ||
|
||
/** | ||
* @return Predicate used for reading an entity | ||
*/ | ||
@Nullable | ||
Predicate readPredicate(); | ||
|
||
/** | ||
* @return Predicate used to check permissions for creating an entity with certain values | ||
*/ | ||
@Nullable | ||
Predicate afterCreatePredicate(); | ||
|
||
/** | ||
* @return Predicate used to check permission before updating an entity | ||
*/ | ||
@Nullable | ||
Predicate beforeUpdatePredicate(); | ||
|
||
/** | ||
* @return Predicate used to check permissions for updating an entity with certain values | ||
*/ | ||
@Nullable | ||
Predicate afterUpdatePredicate(); | ||
|
||
/** | ||
* @return Predicate used for deleting an entity | ||
*/ | ||
@Nullable | ||
Predicate beforeDeletePredicate(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
...r/rest/webmvc/PredicateInjectingRootResourceInformationHandlerMethodArgumentResolver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package com.contentgrid.thunx.spring.data.querydsl.predicate.injector.rest.webmvc; | ||
|
||
import com.contentgrid.thunx.spring.data.querydsl.predicate.injector.repository.RepositoryInvokerAdapterFactory; | ||
import com.contentgrid.thunx.spring.data.querydsl.predicate.injector.resolver.QuerydslPredicateResolver; | ||
import com.contentgrid.thunx.spring.data.querydsl.predicate.injector.resolver.OperationPredicates; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import lombok.NonNull; | ||
import org.springframework.beans.factory.ObjectProvider; | ||
import org.springframework.core.MethodParameter; | ||
import org.springframework.data.repository.support.Repositories; | ||
import org.springframework.data.repository.support.RepositoryInvoker; | ||
import org.springframework.data.repository.support.RepositoryInvokerFactory; | ||
import org.springframework.data.rest.webmvc.RootResourceInformation; | ||
import org.springframework.data.rest.webmvc.config.ResourceMetadataHandlerMethodArgumentResolver; | ||
import org.springframework.data.rest.webmvc.config.RootResourceInformationHandlerMethodArgumentResolver; | ||
import org.springframework.web.method.support.HandlerMethodArgumentResolver; | ||
|
||
/** | ||
* {@link HandlerMethodArgumentResolver} to create {@link RootResourceInformation} for injection into Spring MVC | ||
* controller methods. | ||
* <p> | ||
* This variant injects a custom {@link RepositoryInvoker} that filters its output based on QueryDSL predicates resolved | ||
* by {@link QuerydslPredicateResolver} | ||
*/ | ||
class PredicateInjectingRootResourceInformationHandlerMethodArgumentResolver extends | ||
RootResourceInformationHandlerMethodArgumentResolver { | ||
|
||
private final RepositoryInvokerAdapterFactory repositoryInvokerAdapterFactory; | ||
private final ObjectProvider<QuerydslPredicateResolver> predicateResolvers; | ||
|
||
public PredicateInjectingRootResourceInformationHandlerMethodArgumentResolver( | ||
Repositories repositories, | ||
RepositoryInvokerFactory invokerFactory, | ||
ResourceMetadataHandlerMethodArgumentResolver resourceMetadataResolver, | ||
@NonNull RepositoryInvokerAdapterFactory repositoryInvokerAdapterFactory, | ||
@NonNull ObjectProvider<QuerydslPredicateResolver> predicateResolvers | ||
) { | ||
super(repositories, invokerFactory, resourceMetadataResolver); | ||
|
||
this.repositoryInvokerAdapterFactory = repositoryInvokerAdapterFactory; | ||
this.predicateResolvers = predicateResolvers; | ||
} | ||
|
||
@Override | ||
protected RepositoryInvoker postProcess(MethodParameter parameter, RepositoryInvoker invoker, Class<?> domainType, Map<String, String[]> parameters) { | ||
return getPredicate(parameter, domainType, parameters) | ||
.map(predicate -> repositoryInvokerAdapterFactory.adaptRepositoryInvoker(invoker, domainType, predicate)) | ||
.orElse(invoker); | ||
} | ||
|
||
private Optional<OperationPredicates> getPredicate(MethodParameter parameter, Class<?> domainType, Map<String, String[]> parameters) { | ||
return predicateResolvers.stream() | ||
.map(resolver -> resolver.resolve(parameter, domainType, parameters)) | ||
.flatMap(Optional::stream) | ||
.reduce(OperationPredicates::and); | ||
} | ||
} |
Oops, something went wrong.