Remove own code in favor of Spring Data KeyValue's extension points.
Remove code that would be duplicated by Spring Data KeyValue otherwise. Hook into extension points. Add package-info and nullability annotations. Closes gh-151.
This commit is contained in:
@@ -30,6 +30,7 @@ import org.springframework.data.keyvalue.repository.config.QueryCreatorType;
|
||||
import org.springframework.data.repository.config.DefaultRepositoryBaseClass;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
||||
import org.springframework.vault.repository.query.VaultPartTreeQuery;
|
||||
import org.springframework.vault.repository.query.VaultQueryCreator;
|
||||
import org.springframework.vault.repository.support.VaultRepositoryFactoryBean;
|
||||
|
||||
@@ -46,7 +47,7 @@ import org.springframework.vault.repository.support.VaultRepositoryFactoryBean;
|
||||
@Documented
|
||||
@Inherited
|
||||
@Import(VaultRepositoriesRegistrar.class)
|
||||
@QueryCreatorType(VaultQueryCreator.class)
|
||||
@QueryCreatorType(value = VaultQueryCreator.class, repositoryQueryType = VaultPartTreeQuery.class)
|
||||
public @interface EnableVaultRepositories {
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,22 +18,14 @@ package org.springframework.vault.repository.configuration;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.keyvalue.repository.config.KeyValueRepositoryConfigurationExtension;
|
||||
import org.springframework.data.keyvalue.repository.config.QueryCreatorType;
|
||||
import org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery;
|
||||
import org.springframework.data.keyvalue.repository.query.SpelQueryCreator;
|
||||
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationSource;
|
||||
import org.springframework.vault.repository.core.VaultKeyValueAdapter;
|
||||
@@ -69,70 +61,6 @@ public class VaultRepositoryConfigurationExtension extends
|
||||
return "vaultKeyValueTemplate";
|
||||
}
|
||||
|
||||
// TODO: Remove this as soon as KeyValue provides overrides for the mapping context
|
||||
// name.
|
||||
@Override
|
||||
public void postProcess(BeanDefinitionBuilder builder,
|
||||
AnnotationRepositoryConfigurationSource config) {
|
||||
|
||||
AnnotationAttributes attributes = config.getAttributes();
|
||||
|
||||
builder.addPropertyReference("keyValueOperations",
|
||||
attributes.getString(KEY_VALUE_TEMPLATE_BEAN_REF_ATTRIBUTE));
|
||||
builder.addPropertyValue("queryCreator", getQueryCreatorType(config));
|
||||
builder.addPropertyValue("queryType", getQueryType(config));
|
||||
builder.addPropertyReference("mappingContext", VAULT_MAPPING_CONTEXT_BEAN_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the query creator type to be used for the factory to set. Will lookup a
|
||||
* {@link QueryCreatorType} annotation on the {@code @Enable}-annotation or use
|
||||
* {@link SpelQueryCreator} if not found.
|
||||
*
|
||||
* @param config
|
||||
* @return
|
||||
*/
|
||||
private static Class<?> getQueryCreatorType(
|
||||
AnnotationRepositoryConfigurationSource config) {
|
||||
|
||||
AnnotationMetadata metadata = config.getEnableAnnotationMetadata();
|
||||
|
||||
Map<String, Object> queryCreatorAnnotationAttributes = metadata
|
||||
.getAnnotationAttributes(QueryCreatorType.class.getName());
|
||||
|
||||
if (queryCreatorAnnotationAttributes == null) {
|
||||
return SpelQueryCreator.class;
|
||||
}
|
||||
|
||||
AnnotationAttributes queryCreatorAttributes = new AnnotationAttributes(
|
||||
queryCreatorAnnotationAttributes);
|
||||
return queryCreatorAttributes.getClass("value");
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the query creator type to be used for the factory to set. Will lookup a
|
||||
* {@link QueryCreatorType} annotation on the {@code @Enable}-annotation or use
|
||||
* {@link SpelQueryCreator} if not found.
|
||||
*
|
||||
* @param config
|
||||
* @return
|
||||
*/
|
||||
private static Class<?> getQueryType(AnnotationRepositoryConfigurationSource config) {
|
||||
|
||||
AnnotationMetadata metadata = config.getEnableAnnotationMetadata();
|
||||
|
||||
Map<String, Object> queryCreatorAnnotationAttributes = metadata
|
||||
.getAnnotationAttributes(QueryCreatorType.class.getName());
|
||||
|
||||
if (queryCreatorAnnotationAttributes == null) {
|
||||
return KeyValuePartTreeQuery.class;
|
||||
}
|
||||
|
||||
AnnotationAttributes queryCreatorAttributes = new AnnotationAttributes(
|
||||
queryCreatorAnnotationAttributes);
|
||||
return queryCreatorAttributes.getClass("repositoryQueryType");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerBeansForRoot(BeanDefinitionRegistry registry,
|
||||
RepositoryConfigurationSource configurationSource) {
|
||||
@@ -171,11 +99,11 @@ public class VaultRepositoryConfigurationExtension extends
|
||||
|
||||
AbstractBeanDefinition beanDefinition = getDefaultKeyValueTemplateBeanDefinition(configurationSource);
|
||||
|
||||
if (beanDefinition != null) {
|
||||
registerIfNotAlreadyRegistered(beanDefinition, registry,
|
||||
keyValueTemplateName.get(), configurationSource.getSource());
|
||||
}
|
||||
registerIfNotAlreadyRegistered(beanDefinition, registry,
|
||||
keyValueTemplateName.get(), configurationSource.getSource());
|
||||
}
|
||||
|
||||
super.registerBeansForRoot(registry, configurationSource);
|
||||
}
|
||||
|
||||
private RootBeanDefinition createVaultMappingContext(
|
||||
@@ -186,6 +114,7 @@ public class VaultRepositoryConfigurationExtension extends
|
||||
RootBeanDefinition mappingContextBeanDef = new RootBeanDefinition(
|
||||
VaultMappingContext.class);
|
||||
mappingContextBeanDef.setConstructorArgumentValues(mappingContextArgs);
|
||||
mappingContextBeanDef.setSource(configurationSource.getSource());
|
||||
|
||||
return mappingContextBeanDef;
|
||||
}
|
||||
@@ -214,4 +143,9 @@ public class VaultRepositoryConfigurationExtension extends
|
||||
protected Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
|
||||
return Collections.<Class<? extends Annotation>> singleton(Secret.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMappingContextBeanRef() {
|
||||
return VAULT_MAPPING_CONTEXT_BEAN_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Support infrastructure for the configuration of Vault specific repositories.
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.vault.repository.configuration;
|
||||
@@ -27,6 +27,7 @@ import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.GenericConverter;
|
||||
import org.springframework.data.convert.JodaTimeConverters;
|
||||
import org.springframework.data.convert.WritingConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.vault.repository.mapping.VaultSimpleTypes;
|
||||
|
||||
/**
|
||||
@@ -43,6 +44,7 @@ public class VaultCustomConversions extends
|
||||
org.springframework.data.convert.CustomConversions {
|
||||
|
||||
private static final StoreConversions STORE_CONVERSIONS;
|
||||
|
||||
private static final List<Object> STORE_CONVERTERS;
|
||||
|
||||
static {
|
||||
@@ -89,9 +91,10 @@ public class VaultCustomConversions extends
|
||||
return new HashSet<>(Arrays.asList(localeToString, booleanToString));
|
||||
}
|
||||
|
||||
public Object convert(Object source, TypeDescriptor sourceType,
|
||||
@Nullable
|
||||
public Object convert(@Nullable Object source, TypeDescriptor sourceType,
|
||||
TypeDescriptor targetType) {
|
||||
return source.toString();
|
||||
return source != null ? source.toString() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/**
|
||||
* Spring Vault specific converter infrastructure.
|
||||
*/
|
||||
@NonNullApi
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.vault.repository.convert;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ public class VaultKeyValueAdapter extends AbstractKeyValueAdapter {
|
||||
public VaultKeyValueAdapter(VaultOperations vaultOperations,
|
||||
VaultConverter vaultConverter) {
|
||||
|
||||
super(VaultQueryEngine.INSTANCE);
|
||||
super(new VaultQueryEngine());
|
||||
|
||||
Assert.notNull(vaultOperations, "VaultOperations must not be null");
|
||||
Assert.notNull(vaultConverter, "VaultConverter must not be null");
|
||||
|
||||
@@ -18,22 +18,15 @@ package org.springframework.vault.repository.core;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.domain.Sort.NullHandling;
|
||||
import org.springframework.data.domain.Sort.Order;
|
||||
import org.springframework.data.keyvalue.core.CriteriaAccessor;
|
||||
import org.springframework.data.keyvalue.core.QueryEngine;
|
||||
import org.springframework.data.keyvalue.core.SortAccessor;
|
||||
import org.springframework.data.keyvalue.core.SpelPropertyComparator;
|
||||
import org.springframework.data.keyvalue.core.SpelSortAccessor;
|
||||
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.mapping.context.PersistentPropertyPath;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.vault.repository.query.VaultQuery;
|
||||
|
||||
/**
|
||||
@@ -50,27 +43,26 @@ import org.springframework.vault.repository.query.VaultQuery;
|
||||
class VaultQueryEngine extends
|
||||
QueryEngine<VaultKeyValueAdapter, VaultQuery, Comparator<?>> {
|
||||
|
||||
static final VaultQueryEngine INSTANCE = new VaultQueryEngine();
|
||||
private static final SpelExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
private VaultQueryEngine() {
|
||||
super(VaultCriteriaAccessor.INSTANCE, SpelSortAccessor.INSTANCE);
|
||||
VaultQueryEngine() {
|
||||
super(VaultCriteriaAccessor.INSTANCE, new SpelSortAccessor(parser));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Collection<?> execute(VaultQuery vaultQuery, Comparator<?> comparator,
|
||||
long offset, int rows, String keyspace) {
|
||||
public Collection<?> execute(@Nullable VaultQuery vaultQuery,
|
||||
@Nullable Comparator<?> comparator, long offset, int rows, String keyspace) {
|
||||
return execute(vaultQuery, comparator, offset, rows, keyspace, Object.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Collection<T> execute(VaultQuery vaultQuery, Comparator<?> comparator,
|
||||
long offset, int rows, String keyspace, Class<T> type) {
|
||||
public <T> Collection<T> execute(@Nullable VaultQuery vaultQuery,
|
||||
@Nullable Comparator<?> comparator, long offset, int rows, String keyspace,
|
||||
Class<T> type) {
|
||||
|
||||
validatePropertyPaths(vaultQuery);
|
||||
|
||||
Stream<String> stream = getAdapter().doList(keyspace).stream();
|
||||
Stream<String> stream = getRequiredAdapter().doList(keyspace).stream();
|
||||
|
||||
if (vaultQuery != null) {
|
||||
stream = stream.filter(vaultQuery::test);
|
||||
@@ -87,7 +79,7 @@ class VaultQueryEngine extends
|
||||
}
|
||||
}
|
||||
|
||||
Stream<T> typed = stream.map(it -> getAdapter().get(it, keyspace, type));
|
||||
Stream<T> typed = stream.map(it -> getRequiredAdapter().get(it, keyspace, type));
|
||||
|
||||
if (comparator != null) {
|
||||
|
||||
@@ -106,11 +98,9 @@ class VaultQueryEngine extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(VaultQuery vaultQuery, String keyspace) {
|
||||
public long count(@Nullable VaultQuery vaultQuery, String keyspace) {
|
||||
|
||||
validatePropertyPaths(vaultQuery);
|
||||
|
||||
Stream<String> stream = getAdapter().doList(keyspace).stream();
|
||||
Stream<String> stream = getRequiredAdapter().doList(keyspace).stream();
|
||||
|
||||
if (vaultQuery != null) {
|
||||
stream = stream.filter(vaultQuery::test);
|
||||
@@ -119,26 +109,6 @@ class VaultQueryEngine extends
|
||||
return stream.count();
|
||||
}
|
||||
|
||||
private void validatePropertyPaths(VaultQuery vaultQuery) {
|
||||
|
||||
if (vaultQuery == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Stream<PropertyPath> stream = vaultQuery.getPropertyPaths().stream();
|
||||
|
||||
stream.map(it -> getAdapter().getMappingContext().getPersistentPropertyPath(it))
|
||||
.filter(it -> it.getLeafProperty() != null)
|
||||
.map(PersistentPropertyPath::getLeafProperty)
|
||||
.filter(it -> !it.isIdProperty())
|
||||
.forEach(
|
||||
property -> {
|
||||
throw new InvalidDataAccessApiUsageException(String.format(
|
||||
"Cannot create criteria for non-@Id property %s",
|
||||
property));
|
||||
});
|
||||
}
|
||||
|
||||
enum VaultCriteriaAccessor implements CriteriaAccessor<VaultQuery> {
|
||||
|
||||
INSTANCE;
|
||||
@@ -148,53 +118,4 @@ class VaultQueryEngine extends
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link SortAccessor} implementation capable of creating
|
||||
* {@link SpelPropertyComparator}.
|
||||
*/
|
||||
enum SpelSortAccessor implements SortAccessor<Comparator<?>> {
|
||||
INSTANCE;
|
||||
|
||||
private final SpelExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Override
|
||||
public Comparator<?> resolve(KeyValueQuery<?> query) {
|
||||
|
||||
if (query == null || query.getSort() == null || query.getSort().isUnsorted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Optional<Comparator<?>> comparator = Optional.empty();
|
||||
for (Order order : query.getSort()) {
|
||||
|
||||
SpelPropertyComparator<Object> spelSort = new SpelPropertyComparator<>(
|
||||
order.getProperty(), parser);
|
||||
|
||||
if (Direction.DESC.equals(order.getDirection())) {
|
||||
|
||||
spelSort.desc();
|
||||
|
||||
if (!NullHandling.NATIVE.equals(order.getNullHandling())) {
|
||||
spelSort = NullHandling.NULLS_FIRST.equals(order
|
||||
.getNullHandling()) ? spelSort.nullsFirst() : spelSort
|
||||
.nullsLast();
|
||||
}
|
||||
}
|
||||
|
||||
if (!comparator.isPresent()) {
|
||||
comparator = Optional.of(spelSort);
|
||||
}
|
||||
else {
|
||||
|
||||
SpelPropertyComparator<Object> spelSortToUse = spelSort;
|
||||
comparator = comparator.map(it -> it.thenComparing(spelSortToUse));
|
||||
}
|
||||
}
|
||||
|
||||
return comparator
|
||||
.orElseThrow(() -> new IllegalStateException(
|
||||
"No sort definitions have been added to this CompoundComparator to compare"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Spring Vault core repository implementation.
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.vault.repository.core;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Infrastructure for the Vault JSON-to-object mapping subsystem.
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.vault.repository.mapping;
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.vault.repository.query;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.data.keyvalue.core.KeyValueOperations;
|
||||
import org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.repository.query.EvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
import org.springframework.vault.repository.mapping.VaultPersistentEntity;
|
||||
import org.springframework.vault.repository.mapping.VaultPersistentProperty;
|
||||
|
||||
/**
|
||||
* Vault-specific {@link KeyValuePartTreeQuery}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class VaultPartTreeQuery extends KeyValuePartTreeQuery {
|
||||
|
||||
/**
|
||||
* Creates a new {@link VaultPartTreeQuery} for the given {@link QueryMethod},
|
||||
* {@link EvaluationContextProvider}, {@link KeyValueOperations} and query creator
|
||||
* type.
|
||||
*
|
||||
* @param queryMethod must not be {@literal null}.
|
||||
* @param evaluationContextProvider must not be {@literal null}.
|
||||
* @param keyValueOperations must not be {@literal null}.
|
||||
* @param queryCreator must not be {@literal null}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public VaultPartTreeQuery(QueryMethod queryMethod,
|
||||
EvaluationContextProvider evaluationContextProvider,
|
||||
KeyValueOperations keyValueOperations,
|
||||
Class<? extends AbstractQueryCreator<?, ?>> queryCreator) {
|
||||
|
||||
super(queryMethod, evaluationContextProvider, keyValueOperations,
|
||||
new VaultQueryCreatorFactory(
|
||||
(MappingContext) keyValueOperations.getMappingContext()));
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
static class VaultQueryCreatorFactory implements
|
||||
QueryCreatorFactory<VaultQueryCreator> {
|
||||
|
||||
private final MappingContext<VaultPersistentEntity<?>, VaultPersistentProperty> mappingContext;
|
||||
|
||||
@Override
|
||||
public VaultQueryCreator queryCreatorFor(PartTree partTree,
|
||||
ParameterAccessor accessor) {
|
||||
return new VaultQueryCreator(partTree, accessor, mappingContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,12 +15,8 @@
|
||||
*/
|
||||
package org.springframework.vault.repository.query;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.vault.repository.convert.SecretDocument;
|
||||
|
||||
@@ -39,8 +35,6 @@ public class VaultQuery {
|
||||
|
||||
private final Predicate<String> predicate;
|
||||
|
||||
private final Set<PropertyPath> propertyPaths;
|
||||
|
||||
/**
|
||||
* Create a new {@link VaultQuery} that evaluates unconditionally to {@literal true}.
|
||||
*/
|
||||
@@ -54,30 +48,10 @@ public class VaultQuery {
|
||||
* @param predicate must not be {@literal null}.
|
||||
*/
|
||||
public VaultQuery(Predicate<String> predicate) {
|
||||
this(predicate, Collections.emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link VaultQuery} given {@link Predicate} and {@link PropertyPath}.
|
||||
*
|
||||
* @param predicate must not be {@literal null}.
|
||||
*/
|
||||
public VaultQuery(Predicate<String> predicate, PropertyPath propertyPath) {
|
||||
|
||||
Assert.notNull(predicate, "Predicate must not be null");
|
||||
Assert.notNull(propertyPath, "PropertyPath must not be null");
|
||||
|
||||
this.predicate = predicate;
|
||||
this.propertyPaths = Collections.singleton(propertyPath);
|
||||
}
|
||||
|
||||
private VaultQuery(Predicate<String> predicate, Set<PropertyPath> propertyPaths) {
|
||||
|
||||
Assert.notNull(propertyPaths, "PropertyPaths must not be null");
|
||||
Assert.notNull(predicate, "Predicate must not be null");
|
||||
|
||||
this.predicate = predicate;
|
||||
this.propertyPaths = propertyPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,13 +89,19 @@ public class VaultQuery {
|
||||
* @see Predicate#and(Predicate)
|
||||
*/
|
||||
public VaultQuery and(VaultQuery other) {
|
||||
return new VaultQuery(this.predicate.and(other.predicate));
|
||||
}
|
||||
|
||||
Set<PropertyPath> propertyPaths = new HashSet<>(this.propertyPaths.size()
|
||||
+ other.propertyPaths.size(), 1);
|
||||
propertyPaths.addAll(this.propertyPaths);
|
||||
propertyPaths.addAll(other.propertyPaths);
|
||||
|
||||
return new VaultQuery(this.predicate.and(other.predicate), propertyPaths);
|
||||
/**
|
||||
* Compose a new {@link VaultQuery} using predicates of {@literal this} and the
|
||||
* {@code other} query using logical {@code AND}.
|
||||
*
|
||||
* @param other must not be {@literal null}.
|
||||
* @return a new composed {@link VaultQuery}.
|
||||
* @see Predicate#and(Predicate)
|
||||
*/
|
||||
public VaultQuery and(Predicate<String> predicate) {
|
||||
return new VaultQuery(this.predicate.and(predicate));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,30 +113,7 @@ public class VaultQuery {
|
||||
* @see Predicate#and(Predicate)
|
||||
*/
|
||||
public VaultQuery or(VaultQuery other) {
|
||||
|
||||
Set<PropertyPath> propertyPaths = new HashSet<>(this.propertyPaths.size()
|
||||
+ other.propertyPaths.size(), 1);
|
||||
propertyPaths.addAll(this.propertyPaths);
|
||||
propertyPaths.addAll(other.propertyPaths);
|
||||
|
||||
return new VaultQuery(this.predicate.or(other.predicate), propertyPaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose a new {@link VaultQuery} using predicates of {@literal this} query and the
|
||||
* {@code other} {@link Predicate} using logical {@code AND}.
|
||||
*
|
||||
* @param other must not be {@literal null}.
|
||||
* @return a new composed {@link VaultQuery}.
|
||||
* @see Predicate#and(Predicate)
|
||||
*/
|
||||
public VaultQuery and(Predicate<String> predicate, PropertyPath propertyPath) {
|
||||
|
||||
Set<PropertyPath> propertyPaths = new HashSet<>(this.propertyPaths.size() + 1, 1);
|
||||
propertyPaths.addAll(this.propertyPaths);
|
||||
propertyPaths.add(propertyPath);
|
||||
|
||||
return new VaultQuery(this.predicate.and(predicate), propertyPaths);
|
||||
return new VaultQuery(this.predicate.or(other.predicate));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,11 +122,4 @@ public class VaultQuery {
|
||||
public Predicate<String> getPredicate() {
|
||||
return predicate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return constrained {@link PropertyPath}s for this {@link VaultQuery}.
|
||||
*/
|
||||
public Set<PropertyPath> getPropertyPaths() {
|
||||
return Collections.unmodifiableSet(propertyPaths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,14 +24,19 @@ import java.util.regex.Pattern;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.context.PersistentPropertyPath;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
import org.springframework.data.repository.query.parser.Part.IgnoreCaseType;
|
||||
import org.springframework.data.repository.query.parser.Part.Type;
|
||||
import org.springframework.vault.repository.mapping.VaultPersistentEntity;
|
||||
import org.springframework.vault.repository.mapping.VaultPersistentProperty;
|
||||
|
||||
/**
|
||||
* Query creator for Vault queries. Vault queries are limited to criterias constraining
|
||||
@@ -44,14 +49,7 @@ import org.springframework.data.repository.query.parser.Part.Type;
|
||||
public class VaultQueryCreator extends
|
||||
AbstractQueryCreator<KeyValueQuery<VaultQuery>, VaultQuery> {
|
||||
|
||||
/**
|
||||
* Create a new {@link VaultQueryCreator} given {@link PartTree}.
|
||||
*
|
||||
* @param tree must not be {@literal null}.
|
||||
*/
|
||||
public VaultQueryCreator(PartTree tree) {
|
||||
super(tree);
|
||||
}
|
||||
private final MappingContext<VaultPersistentEntity<?>, VaultPersistentProperty> mappingContext;
|
||||
|
||||
/**
|
||||
* Create a new {@link VaultQueryCreator} given {@link PartTree} and
|
||||
@@ -59,27 +57,38 @@ public class VaultQueryCreator extends
|
||||
*
|
||||
* @param tree must not be {@literal null}.
|
||||
* @param parameters must not be {@literal null}.
|
||||
* @param mappingContext must not be {@literal null}.
|
||||
*/
|
||||
public VaultQueryCreator(PartTree tree, ParameterAccessor parameters) {
|
||||
public VaultQueryCreator(
|
||||
PartTree tree,
|
||||
ParameterAccessor parameters,
|
||||
MappingContext<VaultPersistentEntity<?>, VaultPersistentProperty> mappingContext) {
|
||||
|
||||
super(tree, parameters);
|
||||
this.mappingContext = mappingContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VaultQuery create(Part part, Iterator<Object> parameters) {
|
||||
return new VaultQuery(createPredicate(part, parameters), part.getProperty());
|
||||
return new VaultQuery(createPredicate(part, parameters));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VaultQuery and(Part part, VaultQuery base, Iterator<Object> parameters) {
|
||||
|
||||
if (base == null) {
|
||||
return create(part, parameters);
|
||||
}
|
||||
return base.and(createPredicate(part, parameters), part.getProperty());
|
||||
return base.and(createPredicate(part, parameters));
|
||||
}
|
||||
|
||||
private static Predicate<String> createPredicate(Part part,
|
||||
Iterator<Object> parameters) {
|
||||
private Predicate<String> createPredicate(Part part, Iterator<Object> parameters) {
|
||||
|
||||
PersistentPropertyPath<VaultPersistentProperty> propertyPath = mappingContext
|
||||
.getPersistentPropertyPath(part.getProperty());
|
||||
|
||||
if (propertyPath.getLeafProperty() != null
|
||||
&& !propertyPath.getLeafProperty().isIdProperty()) {
|
||||
throw new InvalidDataAccessApiUsageException(String.format(
|
||||
"Cannot create criteria for non-@Id property %s",
|
||||
propertyPath.getLeafProperty()));
|
||||
}
|
||||
|
||||
VariableAccessor accessor = getVariableAccessor(part);
|
||||
|
||||
@@ -167,7 +176,7 @@ public class VaultQueryCreator extends
|
||||
|
||||
KeyValueQuery<VaultQuery> query = new KeyValueQuery<>(vaultQuery);
|
||||
|
||||
if (sort != null) {
|
||||
if (sort.isSorted()) {
|
||||
query.orderBy(sort);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Query derivation mechanism for Vault specific repositories.
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.vault.repository.query;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Support infrastructure for query derivation of Vault specific repositories.
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.vault.repository.support;
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.vault.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Data;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.map.repository.config.EnableMapRepositories;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.vault.core.VaultTemplate;
|
||||
import org.springframework.vault.repository.MultipleSpringDataModulesIntegrationTests.MultipleModulesActiveTestConfiguration;
|
||||
import org.springframework.vault.repository.configuration.EnableVaultRepositories;
|
||||
import org.springframework.vault.util.IntegrationTestSupport;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = MultipleModulesActiveTestConfiguration.class)
|
||||
public class MultipleSpringDataModulesIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
@Configuration
|
||||
@EnableMapRepositories(considerNestedRepositories = true, //
|
||||
excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, value = VaultRepository.class))
|
||||
@EnableVaultRepositories(considerNestedRepositories = true, //
|
||||
excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, value = MapRepository.class))
|
||||
static class MultipleModulesActiveTestConfiguration extends
|
||||
VaultIntegrationTestConfiguration {
|
||||
}
|
||||
|
||||
@Autowired
|
||||
VaultRepository vaultRepository;
|
||||
|
||||
@Autowired
|
||||
MapRepository mapRepository;
|
||||
|
||||
@Autowired
|
||||
VaultTemplate vaultTemplate;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
vaultRepository.deleteAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadAndSave() {
|
||||
|
||||
Person person = new Person();
|
||||
person.setId("foo-key");
|
||||
person.setName("bar");
|
||||
|
||||
vaultRepository.save(person);
|
||||
|
||||
Iterable<Person> all = vaultRepository.findAll();
|
||||
|
||||
assertThat(all).contains(person);
|
||||
assertThat(vaultRepository.findByIdStartsWith("foo-key")).contains(person);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadAndSaveMapRepository() {
|
||||
|
||||
vaultRepository.findAll().forEach(vaultRepository::delete);
|
||||
|
||||
Person person = new Person();
|
||||
person.setId("foo-key");
|
||||
person.setName("bar");
|
||||
|
||||
mapRepository.save(person);
|
||||
|
||||
Iterable<Person> all = mapRepository.findAll();
|
||||
|
||||
assertThat(all).contains(person);
|
||||
assertThat(mapRepository.findByNameStartsWith("bar")).contains(person);
|
||||
assertThat(vaultRepository.findById("foo-key")).isEmpty();
|
||||
}
|
||||
|
||||
interface VaultRepository extends CrudRepository<Person, String> {
|
||||
|
||||
List<Person> findByIdStartsWith(String prefix);
|
||||
}
|
||||
|
||||
interface MapRepository extends CrudRepository<Person, String> {
|
||||
|
||||
List<Person> findByNameStartsWith(String prefix);
|
||||
}
|
||||
|
||||
@Data
|
||||
static class Person {
|
||||
|
||||
@Id
|
||||
String id;
|
||||
String name;
|
||||
}
|
||||
}
|
||||
@@ -20,10 +20,12 @@ import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.repository.query.DefaultParameters;
|
||||
import org.springframework.data.repository.query.ParametersParameterAccessor;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.vault.repository.mapping.VaultMappingContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -34,6 +36,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
public class VaultQueryCreatorUnitTests {
|
||||
|
||||
private VaultMappingContext mappingContext = new VaultMappingContext();
|
||||
|
||||
@Test
|
||||
public void greaterThan() {
|
||||
|
||||
@@ -178,6 +182,11 @@ public class VaultQueryCreatorUnitTests {
|
||||
assertThat(query.getPredicate()).accepts("3", "4").rejects("2", "5", "6");
|
||||
}
|
||||
|
||||
@Test(expected = InvalidDataAccessApiUsageException.class)
|
||||
public void failsForNonIdProperties() {
|
||||
createQuery("findByName", "");
|
||||
}
|
||||
|
||||
VaultQuery createQuery(String methodName, String value) {
|
||||
|
||||
DefaultParameters defaultParameters = new DefaultParameters(
|
||||
@@ -187,7 +196,8 @@ public class VaultQueryCreatorUnitTests {
|
||||
PartTree partTree = new PartTree(methodName, Credentials.class);
|
||||
VaultQueryCreator queryCreator = new VaultQueryCreator(
|
||||
partTree,
|
||||
new ParametersParameterAccessor(defaultParameters, new Object[] { value }));
|
||||
new ParametersParameterAccessor(defaultParameters, new Object[] { value }),
|
||||
mappingContext);
|
||||
|
||||
return queryCreator.createQuery().getCriteria();
|
||||
}
|
||||
@@ -201,7 +211,8 @@ public class VaultQueryCreatorUnitTests {
|
||||
PartTree partTree = new PartTree(methodName, Credentials.class);
|
||||
VaultQueryCreator queryCreator = new VaultQueryCreator(
|
||||
partTree,
|
||||
new ParametersParameterAccessor(defaultParameters, new Object[] { value }));
|
||||
new ParametersParameterAccessor(defaultParameters, new Object[] { value }),
|
||||
mappingContext);
|
||||
|
||||
return queryCreator.createQuery().getCriteria();
|
||||
}
|
||||
@@ -215,7 +226,7 @@ public class VaultQueryCreatorUnitTests {
|
||||
PartTree partTree = new PartTree(methodName, Credentials.class);
|
||||
VaultQueryCreator queryCreator = new VaultQueryCreator(partTree,
|
||||
new ParametersParameterAccessor(defaultParameters, new Object[] { value,
|
||||
anotherValue }));
|
||||
anotherValue }), mappingContext);
|
||||
|
||||
return queryCreator.createQuery().getCriteria();
|
||||
}
|
||||
@@ -231,6 +242,6 @@ public class VaultQueryCreatorUnitTests {
|
||||
|
||||
static class Credentials {
|
||||
|
||||
String id;
|
||||
String id, name;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user