Skip to content

Commit

Permalink
Merge pull request #37 from xenit-eu/remove-dep-spring-content
Browse files Browse the repository at this point in the history
Removed dependency on spring-content-rest
  • Loading branch information
tgeens authored Apr 13, 2022
2 parents b6afb35 + 111312f commit ea06467
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 7 deletions.
4 changes: 3 additions & 1 deletion thunx-spring/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ dependencies {
compileOnly 'org.springframework.data:spring-data-jpa'
compileOnly 'org.springframework.data:spring-data-rest-core'
compileOnly 'org.springframework.data:spring-data-rest-webmvc'
compileOnly 'com.github.paulcwarren:spring-content-rest:2.1.0'
compileOnly 'jakarta.persistence:jakarta.persistence-api'
compileOnly 'javax.servlet:javax.servlet-api'
compileOnly 'com.querydsl:querydsl-core'
Expand All @@ -37,11 +36,14 @@ dependencies {

testRuntimeOnly 'org.springframework.cloud:spring-cloud-gateway-server'
testRuntimeOnly "org.springframework.security:spring-security-web"
testImplementation "org.springframework.data:spring-data-commons"

testImplementation 'jakarta.persistence:jakarta.persistence-api'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
testImplementation 'net.javacrumbs.json-unit:json-unit-assertj:2.33.0'


testImplementation 'org.springframework.security:spring-security-oauth2-core'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,9 @@
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.PathBuilder;
import eu.xenit.contentcloud.thunx.spring.data.context.AbacContext;
import java.lang.reflect.Field;
import java.util.Optional;
import javax.persistence.Id;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.content.commons.utils.BeanUtils;
import org.springframework.content.commons.utils.DomainObjectUtils;
import org.springframework.beans.BeansException;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.querydsl.QuerydslRepositoryInvokerAdapter;
Expand All @@ -23,6 +19,12 @@
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Optional;
import java.util.stream.Stream;

public class AbacRepositoryInvokerAdapter extends QuerydslRepositoryInvokerAdapter {

Expand Down Expand Up @@ -139,8 +141,68 @@ private String toAlias(Class<?> subjectType) {
}

private BooleanExpression idExpr(Object id, PathBuilder entityPath) {
Field idField = BeanUtils.findFieldWithAnnotation(domainType, Id.class);
Field idField = DomainObjectUtils.getIdField(domainType);
PathBuilder idPath = entityPath.get(idField.getName(), id.getClass());
return idPath.eq(Expressions.constant(id));
}


static class DomainObjectUtils {

private static final boolean JAVAX_PERSISTENCE_PRESENT = ClassUtils.isPresent(
"javax.persistence.Id", DomainObjectUtils.class.getClassLoader());

static final Field getIdField(Class<?> domainClass) {

// Looking for @javax.persistence.Id
if (JAVAX_PERSISTENCE_PRESENT) {
var jpaIdField = DomainObjectUtils.findFieldWithAnnotation(domainClass, javax.persistence.Id.class);
if (jpaIdField.isPresent()) {
return jpaIdField.get();
}
}

// Looking for @org.springframework.data.annotation.Id
var springDataId = DomainObjectUtils.findFieldWithAnnotation(domainClass, org.springframework.data.annotation.Id.class);
if (springDataId.isPresent()) {
return springDataId.get();
}

// None found
return null;
}

private static Optional<Field> findFieldWithAnnotation(Class<?> domainObjClass,
Class<? extends Annotation> annotationClass)
throws SecurityException, BeansException {

// First look for the annotation on the accessor methods
BeanWrapper wrapper = new BeanWrapperImpl(domainObjClass);
return Stream.of(wrapper.getPropertyDescriptors())
.map(descriptor -> findFieldByName(domainObjClass, descriptor.getName()))
.filter(Optional::isPresent)
.map(Optional::get)
.filter(field -> field.isAnnotationPresent(annotationClass))
.findFirst()

// Otherwise look for the annotation on the fields directly
.or(() -> allFields(domainObjClass)
.filter(field -> field.isAnnotationPresent(annotationClass))
.findFirst());


}

private static Optional<Field> findFieldByName(Class<?> type, String fieldName) {
return allFields(type)
.filter(field -> field.getName().equals(fieldName))
.findFirst();
}

private static Stream<Field> allFields(Class<?> type) {
return Stream.concat(
Stream.of(type.getDeclaredFields()),
type.getSuperclass() != null ? allFields(type.getSuperclass()) : Stream.empty());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package eu.xenit.contentcloud.thunx.spring.data.rest;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

class AbacRepositoryInvokerAdapterTest {

static class DomainObjectUtilsTest {

@Test
void long_jpaAnnotated_idField() {
var idField = AbacRepositoryInvokerAdapter.DomainObjectUtils.getIdField(JpaEntity.class);
assertThat(idField).isNotNull();
assertThat(idField.getName()).isEqualTo("myId");
}

@Test
@Disabled("@Id on getter method not supported (ported as-is from spring-content)")
void long_jpaAnnotated_idGetter() {
var idField = AbacRepositoryInvokerAdapter.DomainObjectUtils.getIdField(AccessorJpaEntity.class);
assertThat(idField).isNotNull();
assertThat(idField.getName()).isEqualTo("myId");
}

@Test
void jpa_customIdType() {
var idField = AbacRepositoryInvokerAdapter.DomainObjectUtils.getIdField(JpaEntityWithValueObjectId.class);
assertThat(idField).isNotNull();
assertThat(idField.getType()).isEqualTo(CustomIdClass.class);
}

@Test
void long_springAnnotated_idField() {
var idField = AbacRepositoryInvokerAdapter.DomainObjectUtils.getIdField(SpringDataEntity.class);
assertThat(idField).isNotNull();
assertThat(idField.getName()).isEqualTo("otherId");
}

@Test
void id_from_subclass() {
var idField = AbacRepositoryInvokerAdapter.DomainObjectUtils.getIdField(JpaSubClass.class);
assertThat(idField).isNotNull();
assertThat(idField.getName()).isEqualTo("myId");
}


static class JpaEntity {
@javax.persistence.Id
private Long myId;
}

static class JpaSubClass extends JpaEntity {

}

static class CustomIdClass {
private String value;
}

static class JpaEntityWithValueObjectId {
@javax.persistence.Id
private CustomIdClass id;
}

static class AccessorJpaEntity {

private Long myId;

@javax.persistence.Id
public Long getMyId() {
return this.myId;
}

}

static class SpringDataEntity {
@org.springframework.data.annotation.Id
private Long otherId;
}

}

}

0 comments on commit ea06467

Please sign in to comment.