From b1ed8941bbebf868bb081eedec2e5670f1805ee5 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Sat, 18 Apr 2020 10:57:42 +0200 Subject: [PATCH] DATACOUCH-504 - Make sure reactive repositories can be used This changeset fixes an issue where while blocking repositories would work properly, the reactive repositories won't. This has been an oversight in the transition before RC1. --- .../AbstractCouchbaseConfiguration.java | 22 +++- .../repository/CouchbaseRepository.java | 5 - .../ReactiveCouchbaseRepository.java | 7 +- .../EnableReactiveCouchbaseRepositories.java | 6 +- ...hbaseRepositoryConfigurationExtension.java | 2 +- .../ReactiveCouchbaseRepositoryQuery.java | 27 +++++ .../ReactiveN1qlRepositoryQueryExecutor.java | 35 ++++++ .../ReactiveCouchbaseRepositoryFactory.java | 54 +++++---- ...eactiveCouchbaseRepositoryFactoryBean.java | 4 +- .../support/SimpleCouchbaseRepository.java | 5 - .../SimpleReactiveCouchbaseRepository.java | 35 ++++-- ...hbaseTemplateKeyValueIntegrationTests.java | 2 + .../core/CustomTypeKeyIntegrationTests.java | 3 + .../domain/ReactiveAirportRepository.java} | 28 +++-- .../domain/ReactiveUserRepository.java | 30 +++++ ...aseRepositoryKeyValueIntegrationTests.java | 3 + ...aseRepositoryKeyValueIntegrationTests.java | 84 ++++++++++++++ ...chbaseRepositoryQueryIntegrationTests.java | 106 ++++++++++++++++++ 18 files changed, 385 insertions(+), 73 deletions(-) create mode 100644 src/main/java/org/springframework/data/couchbase/repository/query/ReactiveCouchbaseRepositoryQuery.java create mode 100644 src/main/java/org/springframework/data/couchbase/repository/query/ReactiveN1qlRepositoryQueryExecutor.java rename src/{main/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseSortingRepository.java => test/java/org/springframework/data/couchbase/domain/ReactiveAirportRepository.java} (52%) create mode 100644 src/test/java/org/springframework/data/couchbase/domain/ReactiveUserRepository.java create mode 100644 src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryKeyValueIntegrationTests.java create mode 100644 src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java diff --git a/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java b/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java index 983af273..6af72f6f 100644 --- a/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java +++ b/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java @@ -38,6 +38,7 @@ import org.springframework.data.couchbase.core.convert.translation.JacksonTransl import org.springframework.data.couchbase.core.convert.translation.TranslationService; import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext; import org.springframework.data.couchbase.core.mapping.Document; +import org.springframework.data.couchbase.repository.config.ReactiveRepositoryOperationsMapping; import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping; import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy; import org.springframework.data.mapping.model.FieldNamingStrategy; @@ -116,7 +117,7 @@ public abstract class AbstractCouchbaseConfiguration { return new ReactiveCouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter); } - @Bean + @Bean(name = BeanNames.COUCHBASE_OPERATIONS_MAPPING) public RepositoryOperationsMapping couchbaseRepositoryOperationsMapping(CouchbaseTemplate couchbaseTemplate) { // create a base mapping that associates all repositories to the default template RepositoryOperationsMapping baseMapping = new RepositoryOperationsMapping(couchbaseTemplate); @@ -135,6 +136,25 @@ public abstract class AbstractCouchbaseConfiguration { // NO_OP } + @Bean(name = BeanNames.REACTIVE_COUCHBASE_OPERATIONS_MAPPING) + public ReactiveRepositoryOperationsMapping reactiveCouchbaseRepositoryOperationsMapping(ReactiveCouchbaseTemplate reactiveCouchbaseTemplate) { + // create a base mapping that associates all repositories to the default template + ReactiveRepositoryOperationsMapping baseMapping = new ReactiveRepositoryOperationsMapping(reactiveCouchbaseTemplate); + // let the user tune it + configureReactiveRepositoryOperationsMapping(baseMapping); + return baseMapping; + } + + /** + * In order to customize the mapping between repositories/entity types to couchbase templates, use the provided + * mapping's api (eg. in order to have different buckets backing different repositories). + * + * @param mapping the default mapping (will associate all repositories to the default template). + */ + protected void configureReactiveRepositoryOperationsMapping(ReactiveRepositoryOperationsMapping mapping) { + // NO_OP + } + /** * Scans the mapping base package for classes annotated with {@link Document}. * diff --git a/src/main/java/org/springframework/data/couchbase/repository/CouchbaseRepository.java b/src/main/java/org/springframework/data/couchbase/repository/CouchbaseRepository.java index 10e795b9..19f0ed05 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/CouchbaseRepository.java +++ b/src/main/java/org/springframework/data/couchbase/repository/CouchbaseRepository.java @@ -31,11 +31,6 @@ import org.springframework.data.repository.PagingAndSortingRepository; @NoRepositoryBean public interface CouchbaseRepository extends PagingAndSortingRepository { - /** - * @return a reference to the underlying {@link CouchbaseOperations operation template}. - */ - CouchbaseOperations getCouchbaseOperations(); - @Override List findAll(Sort sort); diff --git a/src/main/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepository.java b/src/main/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepository.java index 4bba8ff1..a2afdbe0 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepository.java +++ b/src/main/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepository.java @@ -15,8 +15,6 @@ */ package org.springframework.data.couchbase.repository; -import org.springframework.data.couchbase.core.CouchbaseOperations; -import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations; import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.reactive.ReactiveSortingRepository; @@ -26,8 +24,5 @@ import org.springframework.data.repository.reactive.ReactiveSortingRepository; */ @NoRepositoryBean public interface ReactiveCouchbaseRepository extends ReactiveSortingRepository { - /** - * @return a reference to the underlying {@link CouchbaseOperations operation template}. - */ - ReactiveCouchbaseOperations getReactiveCouchbaseOperations(); + } diff --git a/src/main/java/org/springframework/data/couchbase/repository/config/EnableReactiveCouchbaseRepositories.java b/src/main/java/org/springframework/data/couchbase/repository/config/EnableReactiveCouchbaseRepositories.java index aeaab0fd..76a1e8cc 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/config/EnableReactiveCouchbaseRepositories.java +++ b/src/main/java/org/springframework/data/couchbase/repository/config/EnableReactiveCouchbaseRepositories.java @@ -47,8 +47,8 @@ public @interface EnableReactiveCouchbaseRepositories { /** * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation declarations e.g.: - * {@code @EnableCouchbaseRepositories("org.my.pkg")} instead of - * {@code @EnableCouchbaseRepositories(basePackages="org.my.pkg")}. + * {@code @EnableReactiveCouchbaseRepositories("org.my.pkg")} instead of + * {@code @EnableReactiveCouchbaseRepositories(basePackages="org.my.pkg")}. */ String[] value() default {}; @@ -119,6 +119,6 @@ public @interface EnableReactiveCouchbaseRepositories { * * @return */ - String couchbaseTemplateRef() default BeanNames.COUCHBASE_TEMPLATE; + String couchbaseTemplateRef() default BeanNames.REACTIVE_COUCHBASE_TEMPLATE; } diff --git a/src/main/java/org/springframework/data/couchbase/repository/config/ReactiveCouchbaseRepositoryConfigurationExtension.java b/src/main/java/org/springframework/data/couchbase/repository/config/ReactiveCouchbaseRepositoryConfigurationExtension.java index 42ececcc..14940e90 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/config/ReactiveCouchbaseRepositoryConfigurationExtension.java +++ b/src/main/java/org/springframework/data/couchbase/repository/config/ReactiveCouchbaseRepositoryConfigurationExtension.java @@ -109,7 +109,7 @@ public class ReactiveCouchbaseRepositoryConfigurationExtension extends Repositor public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource config) { builder.addDependsOn(BeanNames.REACTIVE_COUCHBASE_OPERATIONS_MAPPING); - builder.addPropertyReference("couchbaseOperationsMapping", BeanNames.REACTIVE_COUCHBASE_OPERATIONS_MAPPING); + builder.addPropertyReference("reactiveCouchbaseOperationsMapping", BeanNames.REACTIVE_COUCHBASE_OPERATIONS_MAPPING); } /* diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveCouchbaseRepositoryQuery.java b/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveCouchbaseRepositoryQuery.java new file mode 100644 index 00000000..39a44306 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveCouchbaseRepositoryQuery.java @@ -0,0 +1,27 @@ +package org.springframework.data.couchbase.repository.query; + +import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations; +import org.springframework.data.repository.query.QueryMethod; +import org.springframework.data.repository.query.RepositoryQuery; + +public class ReactiveCouchbaseRepositoryQuery implements RepositoryQuery { + + private final ReactiveCouchbaseOperations operations; + private final QueryMethod queryMethod; + + public ReactiveCouchbaseRepositoryQuery(final ReactiveCouchbaseOperations operations, final QueryMethod queryMethod) { + this.operations = operations; + this.queryMethod = queryMethod; + } + + @Override + public Object execute(final Object[] parameters) { + return new ReactiveN1qlRepositoryQueryExecutor(operations, queryMethod).execute(parameters); + } + + @Override + public QueryMethod getQueryMethod() { + return queryMethod; + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveN1qlRepositoryQueryExecutor.java b/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveN1qlRepositoryQueryExecutor.java new file mode 100644 index 00000000..5f7ab6f7 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveN1qlRepositoryQueryExecutor.java @@ -0,0 +1,35 @@ +package org.springframework.data.couchbase.repository.query; + +import java.util.List; + +import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations; +import org.springframework.data.couchbase.core.query.Query; +import org.springframework.data.repository.query.ParameterAccessor; +import org.springframework.data.repository.query.ParametersParameterAccessor; +import org.springframework.data.repository.query.QueryMethod; +import org.springframework.data.repository.query.parser.PartTree; +import reactor.core.publisher.Flux; + +public class ReactiveN1qlRepositoryQueryExecutor { + + private final ReactiveCouchbaseOperations operations; + private final QueryMethod queryMethod; + + public ReactiveN1qlRepositoryQueryExecutor(final ReactiveCouchbaseOperations operations, final QueryMethod queryMethod) { + this.operations = operations; + this.queryMethod = queryMethod; + } + + public Object execute(final Object[] parameters) { + final Class domainClass = queryMethod.getResultProcessor().getReturnedType().getDomainType(); + final ParameterAccessor accessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters); + + final PartTree tree = new PartTree(queryMethod.getName(), domainClass); + Query query = new N1qlQueryCreator(tree, accessor, operations.getConverter().getMappingContext()).createQuery(); + + Flux all = operations.findByQuery(domainClass).matching(query).all(); + return all; + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/ReactiveCouchbaseRepositoryFactory.java b/src/main/java/org/springframework/data/couchbase/repository/support/ReactiveCouchbaseRepositoryFactory.java index c0df9138..ef13c520 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/ReactiveCouchbaseRepositoryFactory.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/ReactiveCouchbaseRepositoryFactory.java @@ -19,12 +19,15 @@ import java.io.Serializable; import java.lang.reflect.Method; import java.util.Optional; +import org.springframework.data.couchbase.core.CouchbaseOperations; import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty; import org.springframework.data.couchbase.repository.config.ReactiveRepositoryOperationsMapping; import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation; import org.springframework.data.couchbase.repository.query.CouchbaseQueryMethod; +import org.springframework.data.couchbase.repository.query.CouchbaseRepositoryQuery; +import org.springframework.data.couchbase.repository.query.ReactiveCouchbaseRepositoryQuery; import org.springframework.data.couchbase.repository.query.ReactivePartTreeN1qlBasedQuery; import org.springframework.data.couchbase.repository.query.ReactiveStringN1qlBasedQuery; import org.springframework.data.mapping.context.MappingContext; @@ -34,6 +37,7 @@ import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.ReactiveRepositoryFactorySupport; import org.springframework.data.repository.query.QueryLookupStrategy; +import org.springframework.data.repository.query.QueryMethod; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -57,6 +61,8 @@ public class ReactiveCouchbaseRepositoryFactory extends ReactiveRepositoryFactor */ private final MappingContext, CouchbasePersistentProperty> mappingContext; + private final CrudMethodMetadataPostProcessor crudMethodMetadataPostProcessor; + /** * Create a new factory. * @@ -66,8 +72,16 @@ public class ReactiveCouchbaseRepositoryFactory extends ReactiveRepositoryFactor Assert.notNull(couchbaseOperationsMapping); this.couchbaseOperationsMapping = couchbaseOperationsMapping; + this.crudMethodMetadataPostProcessor = new CrudMethodMetadataPostProcessor(); mappingContext = this.couchbaseOperationsMapping.getMappingContext(); + addRepositoryProxyPostProcessor(crudMethodMetadataPostProcessor); + } + + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + super.setBeanClassLoader(classLoader); + this.crudMethodMetadataPostProcessor.setBeanClassLoader(classLoader); } /** @@ -80,9 +94,9 @@ public class ReactiveCouchbaseRepositoryFactory extends ReactiveRepositoryFactor */ @Override public CouchbaseEntityInformation getEntityInformation(Class domainClass) { - CouchbasePersistentEntity entity = mappingContext.getRequiredPersistentEntity(domainClass); - - return new MappingCouchbaseEntityInformation<>((CouchbasePersistentEntity) entity); + CouchbasePersistentEntity entity = (CouchbasePersistentEntity) mappingContext + .getRequiredPersistentEntity(domainClass); + return new MappingCouchbaseEntityInformation<>(entity); } /** @@ -97,15 +111,12 @@ public class ReactiveCouchbaseRepositoryFactory extends ReactiveRepositoryFactor @Override protected final Object getTargetRepository(final RepositoryInformation metadata) { ReactiveCouchbaseOperations couchbaseOperations = couchbaseOperationsMapping.resolve(metadata.getRepositoryInterface(), - metadata.getDomainType()); - // boolean isN1qlAvailable = - // couchbaseOperations.getCouchbaseClusterConfig().clusterCapabilities().containsKey(ServiceType.QUERY); - + metadata.getDomainType()); CouchbaseEntityInformation entityInformation = getEntityInformation(metadata.getDomainType()); - SimpleReactiveCouchbaseRepository repo = getTargetRepositoryViaReflection(metadata, entityInformation, - couchbaseOperations); - // repo.setViewMetadataProvider(viewPostProcessor.getViewMetadataProvider()); - return repo; + SimpleReactiveCouchbaseRepository repository = getTargetRepositoryViaReflection(metadata, entityInformation, + couchbaseOperations); + repository.setRepositoryMethodMetadata(crudMethodMetadataPostProcessor.getCrudMethodMetadata()); + return repository; } /** @@ -119,8 +130,6 @@ public class ReactiveCouchbaseRepositoryFactory extends ReactiveRepositoryFactor */ @Override protected final Class getRepositoryBaseClass(final RepositoryMetadata repositoryMetadata) { - // Since we always need n1ql (we eliminated use of views for findAll, etc...), lets just - // always return the n1ql repo return SimpleReactiveCouchbaseRepository.class; } @@ -144,23 +153,10 @@ public class ReactiveCouchbaseRepositoryFactory extends ReactiveRepositoryFactor @Override public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory, NamedQueries namedQueries) { - ReactiveCouchbaseOperations couchbaseOperations = couchbaseOperationsMapping.resolve(metadata.getRepositoryInterface(), - metadata.getDomainType()); + final ReactiveCouchbaseOperations couchbaseOperations = couchbaseOperationsMapping + .resolve(metadata.getRepositoryInterface(), metadata.getDomainType()); - CouchbaseQueryMethod queryMethod = new CouchbaseQueryMethod(method, metadata, factory, mappingContext); - String namedQueryName = queryMethod.getNamedQueryName(); - - if (queryMethod.hasN1qlAnnotation()) { - if (queryMethod.hasInlineN1qlQuery()) { - return new ReactiveStringN1qlBasedQuery(queryMethod.getInlineN1qlQuery(), queryMethod, couchbaseOperations, - SPEL_PARSER, evaluationContextProvider); - } else if (namedQueries.hasQuery(namedQueryName)) { - String namedQuery = namedQueries.getQuery(namedQueryName); - return new ReactiveStringN1qlBasedQuery(namedQuery, queryMethod, couchbaseOperations, SPEL_PARSER, - evaluationContextProvider); - } // otherwise will do default, queryDerivation - } - return new ReactivePartTreeN1qlBasedQuery(queryMethod, couchbaseOperations); + return new ReactiveCouchbaseRepositoryQuery(couchbaseOperations, new QueryMethod(method, metadata, factory)); } } diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/ReactiveCouchbaseRepositoryFactoryBean.java b/src/main/java/org/springframework/data/couchbase/repository/support/ReactiveCouchbaseRepositoryFactoryBean.java index 20a849c7..bafa69ca 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/ReactiveCouchbaseRepositoryFactoryBean.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/ReactiveCouchbaseRepositoryFactoryBean.java @@ -51,10 +51,10 @@ public class ReactiveCouchbaseRepositoryFactoryBean, * @param reactiveCouchbaseOperations the reference to the operations template. */ public void setCouchbaseOperations(final ReactiveCouchbaseOperations reactiveCouchbaseOperations) { - setCouchbaseOperationsMapping(new ReactiveRepositoryOperationsMapping(reactiveCouchbaseOperations)); + setReactiveCouchbaseOperationsMapping(new ReactiveRepositoryOperationsMapping(reactiveCouchbaseOperations)); } - public void setCouchbaseOperationsMapping(final ReactiveRepositoryOperationsMapping couchbaseOperationsMapping) { + public void setReactiveCouchbaseOperationsMapping(final ReactiveRepositoryOperationsMapping couchbaseOperationsMapping) { this.couchbaseOperationsMapping = couchbaseOperationsMapping; setMappingContext(couchbaseOperationsMapping.getMappingContext()); } diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java index 6d15ee07..f6b7ee7c 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java @@ -153,11 +153,6 @@ public class SimpleCouchbaseRepository implements CouchbaseRepository(results, pageable, count()); } - @Override - public CouchbaseOperations getCouchbaseOperations() { - return couchbaseOperations; - } - /** * Returns the information for the underlying template. * diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleReactiveCouchbaseRepository.java b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleReactiveCouchbaseRepository.java index 52a9cab5..fcf7e379 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleReactiveCouchbaseRepository.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleReactiveCouchbaseRepository.java @@ -16,6 +16,7 @@ package org.springframework.data.couchbase.repository.support; +import com.couchbase.client.java.query.QueryScanConsistency; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -55,18 +56,20 @@ public class SimpleReactiveCouchbaseRepository implements ReactiveCouchba */ private final CouchbaseEntityInformation entityInformation; + private CrudMethodMetadata crudMethodMetadata; + /** * Create a new Repository. * - * @param metadata the Metadata for the entity. + * @param entityInformation the Metadata for the entity. * @param operations the reference to the reactive template used. */ - public SimpleReactiveCouchbaseRepository(final CouchbaseEntityInformation metadata, + public SimpleReactiveCouchbaseRepository(final CouchbaseEntityInformation entityInformation, final ReactiveCouchbaseOperations operations) { - Assert.notNull(operations, "RxJavaCouchbaseOperations must not be null!"); - Assert.notNull(metadata, "CouchbaseEntityInformation must not be null!"); + Assert.notNull(operations, "ReactiveCouchbaseOperations must not be null!"); + Assert.notNull(entityInformation, "CouchbaseEntityInformation must not be null!"); - this.entityInformation = metadata; + this.entityInformation = entityInformation; this.operations = operations; } @@ -195,13 +198,25 @@ public class SimpleReactiveCouchbaseRepository implements ReactiveCouchba return entityInformation; } - @Override - public ReactiveCouchbaseOperations getReactiveCouchbaseOperations() { - return operations; + private Flux findAll(final Query query) { + return operations.findByQuery(entityInformation.getJavaType()).consistentWith(buildQueryScanConsistency()).matching(query).all(); } - private Flux findAll(final Query query) { - return operations.findByQuery(entityInformation.getJavaType()).matching(query).all(); + private QueryScanConsistency buildQueryScanConsistency() { + QueryScanConsistency scanConsistency = QueryScanConsistency.NOT_BOUNDED; + if (crudMethodMetadata.getScanConsistency() != null) { + scanConsistency = crudMethodMetadata.getScanConsistency().query(); + } + return scanConsistency; + } + + /** + * Setter for the repository metadata, contains annotations on the overidden methods. + * + * @param crudMethodMetadata the injected repository metadata. + */ + void setRepositoryMethodMetadata(final CrudMethodMetadata crudMethodMetadata) { + this.crudMethodMetadata = crudMethodMetadata; } } diff --git a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java index 4f30cab5..9d4116cd 100644 --- a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java @@ -19,6 +19,7 @@ package org.springframework.data.couchbase.core; import static org.junit.jupiter.api.Assertions.*; import java.io.IOException; +import java.time.Duration; import java.util.UUID; import org.junit.jupiter.api.AfterAll; @@ -44,6 +45,7 @@ class CouchbaseTemplateKeyValueIntegrationTests extends ClusterAwareIntegrationT @BeforeAll static void beforeAll() { couchbaseClientFactory = new SimpleCouchbaseClientFactory(connectionString(), authenticator(), bucketName()); + couchbaseClientFactory.getBucket().waitUntilReady(Duration.ofSeconds(10)); } @AfterAll diff --git a/src/test/java/org/springframework/data/couchbase/core/CustomTypeKeyIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/core/CustomTypeKeyIntegrationTests.java index 20890543..0f4037b0 100644 --- a/src/test/java/org/springframework/data/couchbase/core/CustomTypeKeyIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/CustomTypeKeyIntegrationTests.java @@ -18,6 +18,7 @@ package org.springframework.data.couchbase.core; import static org.junit.jupiter.api.Assertions.*; +import java.time.Duration; import java.util.UUID; import org.junit.jupiter.api.Test; @@ -44,6 +45,8 @@ public class CustomTypeKeyIntegrationTests extends ClusterAwareIntegrationTests @Test void saveSimpleEntityCorrectlyWithDifferentTypeKey() { + clientFactory.getBucket().waitUntilReady(Duration.ofSeconds(5)); + User user = new User(UUID.randomUUID().toString(), "firstname", "lastname"); User modified = operations.upsertById(User.class).one(user); assertEquals(user, modified); diff --git a/src/main/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseSortingRepository.java b/src/test/java/org/springframework/data/couchbase/domain/ReactiveAirportRepository.java similarity index 52% rename from src/main/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseSortingRepository.java rename to src/test/java/org/springframework/data/couchbase/domain/ReactiveAirportRepository.java index 4ae22f39..624d0a1e 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseSortingRepository.java +++ b/src/test/java/org/springframework/data/couchbase/domain/ReactiveAirportRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 the original author or authors. + * Copyright 2017-2019 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. @@ -13,17 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.couchbase.repository; -import java.io.Serializable; +package org.springframework.data.couchbase.domain; + +import org.springframework.data.couchbase.repository.ScanConsistency; import org.springframework.data.repository.reactive.ReactiveSortingRepository; +import org.springframework.stereotype.Repository; + +import com.couchbase.client.java.query.QueryScanConsistency; +import reactor.core.publisher.Flux; + +@Repository +public interface ReactiveAirportRepository extends ReactiveSortingRepository { + + @Override + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + Flux findAll(); + + Flux findAllByIata(String iata); -/** - * Couchbase specific {@link org.springframework.data.repository.Repository} interface that is - * a {@link ReactiveSortingRepository}. - * - * @author Subhashni Balakrishnan - */ -public interface ReactiveCouchbaseSortingRepository - extends ReactiveCouchbaseRepository, ReactiveSortingRepository { } diff --git a/src/test/java/org/springframework/data/couchbase/domain/ReactiveUserRepository.java b/src/test/java/org/springframework/data/couchbase/domain/ReactiveUserRepository.java new file mode 100644 index 00000000..0b378cb0 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/domain/ReactiveUserRepository.java @@ -0,0 +1,30 @@ +/* + * Copyright 2012-2020 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 + * + * https://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.data.couchbase.domain; + +import org.springframework.data.repository.reactive.ReactiveSortingRepository; +import org.springframework.stereotype.Repository; +import reactor.core.publisher.Flux; + +@Repository +public interface ReactiveUserRepository extends ReactiveSortingRepository { + + Flux findByFirstname(String firstname); + + Flux findByFirstnameAndLastname(String firstname, String lastname); + +} diff --git a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryKeyValueIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryKeyValueIntegrationTests.java index 01b9cb50..003cc0ca 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryKeyValueIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryKeyValueIntegrationTests.java @@ -29,9 +29,12 @@ import org.springframework.data.couchbase.domain.User; import org.springframework.data.couchbase.domain.UserRepository; import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories; import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests; +import org.springframework.data.couchbase.util.ClusterType; +import org.springframework.data.couchbase.util.IgnoreWhen; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; @SpringJUnitConfig(CouchbaseRepositoryKeyValueIntegrationTests.Config.class) +@IgnoreWhen(clusterTypes = ClusterType.MOCKED) public class CouchbaseRepositoryKeyValueIntegrationTests extends ClusterAwareIntegrationTests { @Autowired UserRepository userRepository; diff --git a/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryKeyValueIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryKeyValueIntegrationTests.java new file mode 100644 index 00000000..0f6e7317 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryKeyValueIntegrationTests.java @@ -0,0 +1,84 @@ +/* + * Copyright 2012-2020 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 + * + * https://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.data.couchbase.repository; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Optional; +import java.util.UUID; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration; +import org.springframework.data.couchbase.domain.ReactiveUserRepository; +import org.springframework.data.couchbase.domain.User; +import org.springframework.data.couchbase.repository.config.EnableReactiveCouchbaseRepositories; +import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests; +import org.springframework.data.couchbase.util.ClusterType; +import org.springframework.data.couchbase.util.IgnoreWhen; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +@SpringJUnitConfig(ReactiveCouchbaseRepositoryKeyValueIntegrationTests.Config.class) +@IgnoreWhen(clusterTypes = ClusterType.MOCKED) +public class ReactiveCouchbaseRepositoryKeyValueIntegrationTests extends ClusterAwareIntegrationTests { + + @Autowired + ReactiveUserRepository userRepository; + + @Test + void saveAndFindById() { + User user = new User(UUID.randomUUID().toString(), "f", "l"); + + assertFalse(userRepository.existsById(user.getId()).block()); + + userRepository.save(user).block(); + + Optional found = userRepository.findById(user.getId()).blockOptional(); + assertTrue(found.isPresent()); + found.ifPresent(u -> assertEquals(user, u)); + + assertTrue(userRepository.existsById(user.getId()).block()); + } + + @Configuration + @EnableReactiveCouchbaseRepositories("org.springframework.data.couchbase") + static class Config extends AbstractCouchbaseConfiguration { + + @Override + public String getConnectionString() { + return connectionString(); + } + + @Override + public String getUserName() { + return config().adminUsername(); + } + + @Override + public String getPassword() { + return config().adminPassword(); + } + + @Override + public String getBucketName() { + return bucketName(); + } + + } + +} diff --git a/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java new file mode 100644 index 00000000..e96187e3 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java @@ -0,0 +1,106 @@ +/* + * Copyright 2017-2019 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 + * + * https://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.data.couchbase.repository; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.couchbase.CouchbaseClientFactory; +import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration; +import org.springframework.data.couchbase.domain.Airport; +import org.springframework.data.couchbase.domain.AirportRepository; +import org.springframework.data.couchbase.domain.ReactiveAirportRepository; +import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories; +import org.springframework.data.couchbase.repository.config.EnableReactiveCouchbaseRepositories; +import org.springframework.data.couchbase.util.Capabilities; +import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests; +import org.springframework.data.couchbase.util.ClusterType; +import org.springframework.data.couchbase.util.IgnoreWhen; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import com.couchbase.client.core.error.IndexExistsException; + +@SpringJUnitConfig(ReactiveCouchbaseRepositoryQueryIntegrationTests.Config.class) +@IgnoreWhen(missesCapabilities = Capabilities.QUERY, clusterTypes = ClusterType.MOCKED) +public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegrationTests { + + @Autowired CouchbaseClientFactory clientFactory; + + @Autowired ReactiveAirportRepository airportRepository; + + @BeforeEach + void beforeEach() { + try { + clientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName()); + } catch (IndexExistsException ex) { + // ignore, all good. + } + } + + @Test + void shouldSaveAndFindAll() { + Airport vie = new Airport("airports::vie", "vie", "loww"); + airportRepository.save(vie).block(); + + List all = airportRepository.findAll().toStream() + .collect(Collectors.toList()); + + assertFalse(all.isEmpty()); + assertTrue(all.stream().anyMatch(a -> a.getId().equals("airports::vie"))); + } + + @Test + void findBySimpleProperty() { + List airports = airportRepository.findAllByIata("vie").collectList().block(); + // TODO + System.err.println(airports); + } + + @Configuration + @EnableReactiveCouchbaseRepositories("org.springframework.data.couchbase") + static class Config extends AbstractCouchbaseConfiguration { + + @Override + public String getConnectionString() { + return connectionString(); + } + + @Override + public String getUserName() { + return config().adminUsername(); + } + + @Override + public String getPassword() { + return config().adminPassword(); + } + + @Override + public String getBucketName() { + return bucketName(); + } + + } + +}