diff --git a/spring-vault-repository/src/main/java/org/springframework/vault/repository/configuration/EnableVaultRepositories.java b/spring-vault-repository/src/main/java/org/springframework/vault/repository/configuration/EnableVaultRepositories.java index f6884d0a..6e3343a2 100644 --- a/spring-vault-repository/src/main/java/org/springframework/vault/repository/configuration/EnableVaultRepositories.java +++ b/spring-vault-repository/src/main/java/org/springframework/vault/repository/configuration/EnableVaultRepositories.java @@ -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 { /** diff --git a/spring-vault-repository/src/main/java/org/springframework/vault/repository/configuration/VaultRepositoryConfigurationExtension.java b/spring-vault-repository/src/main/java/org/springframework/vault/repository/configuration/VaultRepositoryConfigurationExtension.java index 4e55c028..44df9028 100644 --- a/spring-vault-repository/src/main/java/org/springframework/vault/repository/configuration/VaultRepositoryConfigurationExtension.java +++ b/spring-vault-repository/src/main/java/org/springframework/vault/repository/configuration/VaultRepositoryConfigurationExtension.java @@ -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 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 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> getIdentifyingAnnotations() { return Collections.> singleton(Secret.class); } + + @Override + protected String getMappingContextBeanRef() { + return VAULT_MAPPING_CONTEXT_BEAN_NAME; + } } diff --git a/spring-vault-repository/src/main/java/org/springframework/vault/repository/configuration/package-info.java b/spring-vault-repository/src/main/java/org/springframework/vault/repository/configuration/package-info.java new file mode 100644 index 00000000..902e58f6 --- /dev/null +++ b/spring-vault-repository/src/main/java/org/springframework/vault/repository/configuration/package-info.java @@ -0,0 +1,5 @@ +/** + * Support infrastructure for the configuration of Vault specific repositories. + */ +@org.springframework.lang.NonNullApi +package org.springframework.vault.repository.configuration; \ No newline at end of file diff --git a/spring-vault-repository/src/main/java/org/springframework/vault/repository/convert/VaultCustomConversions.java b/spring-vault-repository/src/main/java/org/springframework/vault/repository/convert/VaultCustomConversions.java index 8b762c2a..c195745a 100644 --- a/spring-vault-repository/src/main/java/org/springframework/vault/repository/convert/VaultCustomConversions.java +++ b/spring-vault-repository/src/main/java/org/springframework/vault/repository/convert/VaultCustomConversions.java @@ -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 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; } } } diff --git a/spring-vault-repository/src/main/java/org/springframework/vault/repository/convert/package-info.java b/spring-vault-repository/src/main/java/org/springframework/vault/repository/convert/package-info.java index d40313aa..2def3b3d 100644 --- a/spring-vault-repository/src/main/java/org/springframework/vault/repository/convert/package-info.java +++ b/spring-vault-repository/src/main/java/org/springframework/vault/repository/convert/package-info.java @@ -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; - diff --git a/spring-vault-repository/src/main/java/org/springframework/vault/repository/core/VaultKeyValueAdapter.java b/spring-vault-repository/src/main/java/org/springframework/vault/repository/core/VaultKeyValueAdapter.java index c3ffd331..09a098c1 100644 --- a/spring-vault-repository/src/main/java/org/springframework/vault/repository/core/VaultKeyValueAdapter.java +++ b/spring-vault-repository/src/main/java/org/springframework/vault/repository/core/VaultKeyValueAdapter.java @@ -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"); diff --git a/spring-vault-repository/src/main/java/org/springframework/vault/repository/core/VaultQueryEngine.java b/spring-vault-repository/src/main/java/org/springframework/vault/repository/core/VaultQueryEngine.java index ce044dc2..127e428e 100644 --- a/spring-vault-repository/src/main/java/org/springframework/vault/repository/core/VaultQueryEngine.java +++ b/spring-vault-repository/src/main/java/org/springframework/vault/repository/core/VaultQueryEngine.java @@ -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> { - 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 Collection execute(VaultQuery vaultQuery, Comparator comparator, - long offset, int rows, String keyspace, Class type) { + public Collection execute(@Nullable VaultQuery vaultQuery, + @Nullable Comparator comparator, long offset, int rows, String keyspace, + Class type) { - validatePropertyPaths(vaultQuery); - - Stream stream = getAdapter().doList(keyspace).stream(); + Stream stream = getRequiredAdapter().doList(keyspace).stream(); if (vaultQuery != null) { stream = stream.filter(vaultQuery::test); @@ -87,7 +79,7 @@ class VaultQueryEngine extends } } - Stream typed = stream.map(it -> getAdapter().get(it, keyspace, type)); + Stream 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 stream = getAdapter().doList(keyspace).stream(); + Stream 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 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 { INSTANCE; @@ -148,53 +118,4 @@ class VaultQueryEngine extends } } - /** - * {@link SortAccessor} implementation capable of creating - * {@link SpelPropertyComparator}. - */ - enum SpelSortAccessor implements SortAccessor> { - 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 = Optional.empty(); - for (Order order : query.getSort()) { - - SpelPropertyComparator 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 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")); - } - } } diff --git a/spring-vault-repository/src/main/java/org/springframework/vault/repository/core/package-info.java b/spring-vault-repository/src/main/java/org/springframework/vault/repository/core/package-info.java new file mode 100644 index 00000000..586add58 --- /dev/null +++ b/spring-vault-repository/src/main/java/org/springframework/vault/repository/core/package-info.java @@ -0,0 +1,6 @@ +/** + * Spring Vault core repository implementation. + */ +@org.springframework.lang.NonNullApi +package org.springframework.vault.repository.core; + diff --git a/spring-vault-repository/src/main/java/org/springframework/vault/repository/mapping/package-info.java b/spring-vault-repository/src/main/java/org/springframework/vault/repository/mapping/package-info.java new file mode 100644 index 00000000..9329c895 --- /dev/null +++ b/spring-vault-repository/src/main/java/org/springframework/vault/repository/mapping/package-info.java @@ -0,0 +1,6 @@ +/** + * Infrastructure for the Vault JSON-to-object mapping subsystem. + */ +@org.springframework.lang.NonNullApi +package org.springframework.vault.repository.mapping; + diff --git a/spring-vault-repository/src/main/java/org/springframework/vault/repository/query/VaultPartTreeQuery.java b/spring-vault-repository/src/main/java/org/springframework/vault/repository/query/VaultPartTreeQuery.java new file mode 100644 index 00000000..665f1199 --- /dev/null +++ b/spring-vault-repository/src/main/java/org/springframework/vault/repository/query/VaultPartTreeQuery.java @@ -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> queryCreator) { + + super(queryMethod, evaluationContextProvider, keyValueOperations, + new VaultQueryCreatorFactory( + (MappingContext) keyValueOperations.getMappingContext())); + } + + @RequiredArgsConstructor + static class VaultQueryCreatorFactory implements + QueryCreatorFactory { + + private final MappingContext, VaultPersistentProperty> mappingContext; + + @Override + public VaultQueryCreator queryCreatorFor(PartTree partTree, + ParameterAccessor accessor) { + return new VaultQueryCreator(partTree, accessor, mappingContext); + } + } +} diff --git a/spring-vault-repository/src/main/java/org/springframework/vault/repository/query/VaultQuery.java b/spring-vault-repository/src/main/java/org/springframework/vault/repository/query/VaultQuery.java index ff9b66ca..9a914a38 100644 --- a/spring-vault-repository/src/main/java/org/springframework/vault/repository/query/VaultQuery.java +++ b/spring-vault-repository/src/main/java/org/springframework/vault/repository/query/VaultQuery.java @@ -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 predicate; - private final Set 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 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 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 predicate, Set 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 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 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 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 predicate, PropertyPath propertyPath) { - - Set 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 getPredicate() { return predicate; } - - /** - * @return constrained {@link PropertyPath}s for this {@link VaultQuery}. - */ - public Set getPropertyPaths() { - return Collections.unmodifiableSet(propertyPaths); - } } diff --git a/spring-vault-repository/src/main/java/org/springframework/vault/repository/query/VaultQueryCreator.java b/spring-vault-repository/src/main/java/org/springframework/vault/repository/query/VaultQueryCreator.java index 0a039b1d..4302a535 100644 --- a/spring-vault-repository/src/main/java/org/springframework/vault/repository/query/VaultQueryCreator.java +++ b/spring-vault-repository/src/main/java/org/springframework/vault/repository/query/VaultQueryCreator.java @@ -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, 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, 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, VaultPersistentProperty> mappingContext) { + super(tree, parameters); + this.mappingContext = mappingContext; } @Override protected VaultQuery create(Part part, Iterator parameters) { - return new VaultQuery(createPredicate(part, parameters), part.getProperty()); + return new VaultQuery(createPredicate(part, parameters)); } @Override protected VaultQuery and(Part part, VaultQuery base, Iterator 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 createPredicate(Part part, - Iterator parameters) { + private Predicate createPredicate(Part part, Iterator parameters) { + + PersistentPropertyPath 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 query = new KeyValueQuery<>(vaultQuery); - if (sort != null) { + if (sort.isSorted()) { query.orderBy(sort); } diff --git a/spring-vault-repository/src/main/java/org/springframework/vault/repository/query/package-info.java b/spring-vault-repository/src/main/java/org/springframework/vault/repository/query/package-info.java new file mode 100644 index 00000000..8ede8366 --- /dev/null +++ b/spring-vault-repository/src/main/java/org/springframework/vault/repository/query/package-info.java @@ -0,0 +1,6 @@ +/** + * Query derivation mechanism for Vault specific repositories. + */ +@org.springframework.lang.NonNullApi +package org.springframework.vault.repository.query; + diff --git a/spring-vault-repository/src/main/java/org/springframework/vault/repository/support/package-info.java b/spring-vault-repository/src/main/java/org/springframework/vault/repository/support/package-info.java new file mode 100644 index 00000000..382e6ca1 --- /dev/null +++ b/spring-vault-repository/src/main/java/org/springframework/vault/repository/support/package-info.java @@ -0,0 +1,6 @@ +/** + * Support infrastructure for query derivation of Vault specific repositories. + */ +@org.springframework.lang.NonNullApi +package org.springframework.vault.repository.support; + diff --git a/spring-vault-repository/src/test/java/org/springframework/vault/repository/MultipleSpringDataModulesIntegrationTests.java b/spring-vault-repository/src/test/java/org/springframework/vault/repository/MultipleSpringDataModulesIntegrationTests.java new file mode 100644 index 00000000..e4a543ac --- /dev/null +++ b/spring-vault-repository/src/test/java/org/springframework/vault/repository/MultipleSpringDataModulesIntegrationTests.java @@ -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 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 all = mapRepository.findAll(); + + assertThat(all).contains(person); + assertThat(mapRepository.findByNameStartsWith("bar")).contains(person); + assertThat(vaultRepository.findById("foo-key")).isEmpty(); + } + + interface VaultRepository extends CrudRepository { + + List findByIdStartsWith(String prefix); + } + + interface MapRepository extends CrudRepository { + + List findByNameStartsWith(String prefix); + } + + @Data + static class Person { + + @Id + String id; + String name; + } +} diff --git a/spring-vault-repository/src/test/java/org/springframework/vault/repository/query/VaultQueryCreatorUnitTests.java b/spring-vault-repository/src/test/java/org/springframework/vault/repository/query/VaultQueryCreatorUnitTests.java index 5208a171..a228f6c6 100644 --- a/spring-vault-repository/src/test/java/org/springframework/vault/repository/query/VaultQueryCreatorUnitTests.java +++ b/spring-vault-repository/src/test/java/org/springframework/vault/repository/query/VaultQueryCreatorUnitTests.java @@ -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; } } \ No newline at end of file