DATACOUCH-504 - Migrate to Couchbase SDK 3

This commit is contained in:
David Kelly
2019-09-19 09:13:53 -06:00
committed by Michael Nitschinger
parent 690f064cac
commit c9a19c925e
399 changed files with 14616 additions and 27269 deletions

View File

@@ -1,20 +0,0 @@
package org.springframework.data.couchbase;
import org.junit.ClassRule;
import org.junit.runners.model.InitializationError;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* This runner initializes container for the container based testing.
*
* @author Subhashni Balakrishnan
*/
public class ContainerResourceRunner extends SpringJUnit4ClassRunner {
@ClassRule
public static final TestContainerResource resource = TestContainerResource.getResource();
public ContainerResourceRunner(Class<?> clazz) throws InitializationError {
super(clazz);
}
}

View File

@@ -1,46 +0,0 @@
package org.springframework.data.couchbase;
import java.util.concurrent.Callable;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
/**
* Helper to check if the Couchbase http endpoints are up.
*/
public class CouchbaseHttpPortListeningCheck implements Callable<Boolean> {
private final int port;
private final String path;
public CouchbaseHttpPortListeningCheck(int port, String path) {
this.port = port;
this.path = path;
}
private Boolean executeRequest(URIBuilder builder) throws Exception {
try {
HttpGet request = new HttpGet(builder.build());
HttpClient client = HttpClientBuilder.create().build();
HttpResponse response = client.execute(request);
int status = response.getStatusLine().getStatusCode();
if (status < 200 || status >= 300) {
return false;
}
return true;
} catch (Exception ex) {
Thread.sleep(1000);
throw ex;
}
}
@Override
public Boolean call() throws Exception {
URIBuilder builder = new URIBuilder();
builder.setScheme("http").setHost("localhost").setPort(this.port).setPath(this.path);
return executeRequest(builder);
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright 2019 Couchbase, Inc.
*
* 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;
import org.rnorth.ducttape.unreliables.Unreliables;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
public class CouchbaseTestHelper {
private CouchbaseTestHelper() {
throw new AssertionError("not instantiable");
}
/**
* Returns a repository instance for the given interface.
* <p>
* Retry because concurrent index creation throws exception.
* See https://issues.couchbase.com/browse/MB-32238
*/
public static <T> T getRepositoryWithRetry(RepositoryFactorySupport factory, Class<T> repositoryInterface) {
return retryUntilSuccess(() -> factory.getRepository(repositoryInterface));
}
private static <T> T retryUntilSuccess(final Callable<T> lambda) {
return Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, lambda);
}
}

View File

@@ -1,124 +0,0 @@
package org.springframework.data.couchbase;
import static java.time.temporal.ChronoUnit.SECONDS;
import java.time.Duration;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import com.couchbase.client.java.util.features.Version;
import org.rnorth.ducttape.unreliables.Unreliables;
import org.testcontainers.containers.Container;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.WaitStrategy;
import org.testcontainers.containers.wait.strategy.WaitStrategyTarget;
/**
* WaitStrategy for Couchbase containers which makes the Server node is
* initialized, RBAC user and default bucket is created.
*/
public class CouchbaseWaitStrategy implements WaitStrategy {
private Duration startupTimeout = Duration.of(60, SECONDS);
private final Boolean rbacEnabled;
private final Container container;
public CouchbaseWaitStrategy(String serverVersion, GenericContainer container) {
this.container = container;
Version version = Version.parseVersion(serverVersion);
rbacEnabled = version.major() >= 5;
}
private void checkResult(Container.ExecResult result, String command) throws Exception {
if (!result.getStdout().contains("SUCCESS")) {
throw new Exception(command + " command failed");
}
}
private void checkService(int port, String path) {
Callable<Boolean> externalCheck = new CouchbaseHttpPortListeningCheck(port, path);
Unreliables.retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () ->
externalCheck.call());
}
@Override
public void waitUntilReady(WaitStrategyTarget target) {
try {
checkService(8091, "/pools");
Container.ExecResult result;
if (rbacEnabled) {
result = container.execInContainer("/opt/couchbase/bin/couchbase-cli",
"cluster-init",
"--cluster=127.0.0.1:8091",
"--services=data,index,query",
"--cluster-name=localcontainer",
"--cluster-username=Administrator",
"--cluster-password=password",
"--cluster-ramsize=512",
"--cluster-index-ramsize=512",
"--index-storage-setting=default");
checkResult(result, "Cluster init");
result = container.execInContainer("/opt/couchbase/bin/couchbase-cli",
"user-manage",
"--cluster=127.0.0.1:8091",
"--username=Administrator",
"--password=password",
"--set",
"--rbac-username=protected",
"--rbac-password=password",
"--rbac-name=default",
"--roles=admin",
"--auth-domain=local");
checkResult(result, "User manage");
result = container.execInContainer("/opt/couchbase/bin/couchbase-cli",
"bucket-create",
"--cluster=127.0.0.1:8091",
"--username=Administrator",
"--password=password",
"--bucket=protected",
"--bucket-type=couchbase",
"--bucket-ramsize=200",
"--enable-flush=1",
"--wait");
} else {
result = container.execInContainer("/opt/couchbase/bin/couchbase-cli",
"cluster-init",
"--cluster=127.0.0.1:8091",
"--services=data,index,query",
"-u",
"Administrator",
"-p",
"password",
"--cluster-ramsize=512",
"--cluster-index-ramsize=512",
"--index-storage-setting=default");
checkResult(result, "Cluster init");
result = container.execInContainer("/opt/couchbase/bin/couchbase-cli",
"bucket-create",
"--cluster=127.0.0.1:8091",
"-u",
"Administrator",
"-p",
"password",
"--bucket=protected",
"--bucket-password=password",
"--bucket-type=couchbase",
"--bucket-ramsize=200",
"--enable-flush=1",
"--wait");
}
checkResult(result, "Bucket create");
checkService(8093, "/query/ping");
} catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
}
}
@Override
public WaitStrategy withStartupTimeout(Duration startupTimeout) {
this.startupTimeout = startupTimeout;
return this;
}
}

View File

@@ -1,81 +0,0 @@
package org.springframework.data.couchbase;
import java.util.Collections;
import java.util.List;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
import org.springframework.data.couchbase.config.CouchbaseConfigurer;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.WriteResultChecking;
import org.springframework.data.couchbase.core.query.Consistency;
import org.springframework.data.couchbase.repository.support.IndexManager;
@Configuration
public class IntegrationTestApplicationConfig extends AbstractCouchbaseConfiguration {
@Bean
public String couchbaseAdminUser() {
return "Administrator";
}
@Bean
public String couchbaseAdminPassword() {
return "password";
}
@Override
protected List<String> getBootstrapHosts() {
return Collections.singletonList("127.0.0.1");
}
@Override
protected String getBucketName() {
return "protected";
}
@Override
protected String getBucketPassword() {
return "password";
}
//TODO maybe create the bucket if doesn't exist
@Override
protected CouchbaseEnvironment getEnvironment() {
return DefaultCouchbaseEnvironment.builder()
.connectTimeout(10000)
.kvTimeout(10000)
.queryTimeout(20000)
.viewTimeout(20000)
.build();
}
@Override
public CouchbaseTemplate couchbaseTemplate() throws Exception {
CouchbaseTemplate template = super.couchbaseTemplate();
template.setWriteResultChecking(WriteResultChecking.LOG);
return template;
}
//this is for dev so it is ok to auto-create indexes
@Override
public IndexManager indexManager() {
return new IndexManager();
}
@Override
protected Consistency getDefaultConsistency() {
return Consistency.READ_YOUR_OWN_WRITES;
}
@Override
protected CouchbaseConfigurer couchbaseConfigurer() {
return this;
}
}

View File

@@ -1,7 +0,0 @@
package org.springframework.data.couchbase;
import org.springframework.context.annotation.Configuration;
@Configuration
public class IntegrationTestCustomKeySettings extends IntegrationTestApplicationConfig {
}

View File

@@ -1,13 +0,0 @@
package org.springframework.data.couchbase;
import org.springframework.context.annotation.Configuration;
@Configuration
public class IntegrationTestCustomTypeKeyConfig extends IntegrationTestApplicationConfig {
//change the name of the field that will hold type information
@Override
public String typeKey() {
return "javaClass";
}
}

View File

@@ -1,50 +0,0 @@
package org.springframework.data.couchbase;
import java.util.Collections;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
import org.springframework.data.couchbase.config.CouchbaseConfigurer;
/**
* Configuration for testing no shutdown
*
* @author Subhashni Balakrishnan
*/
public class IntegrationTestNoShutdownApplicationConfig extends AbstractCouchbaseConfiguration {
@Bean
public String couchbaseAdminUser() {
return "Administrator";
}
@Bean
public String couchbaseAdminPassword() {
return "password";
}
@Override
protected List<String> getBootstrapHosts() {
return Collections.singletonList("127.0.0.1");
}
@Override
protected String getBucketName() {
return "protected";
}
@Override
protected String getBucketPassword() {
return "password";
}
@Override
protected boolean isEnvironmentManagedBySpring() {
return false;
}
@Override
protected CouchbaseConfigurer couchbaseConfigurer() {
return this;
}
}

View File

@@ -1,72 +0,0 @@
package org.springframework.data.couchbase;
import java.util.Collections;
import java.util.List;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.config.AbstractReactiveCouchbaseConfiguration;
import org.springframework.data.couchbase.core.RxJavaCouchbaseTemplate;
import org.springframework.data.couchbase.core.WriteResultChecking;
import org.springframework.data.couchbase.core.query.Consistency;
import org.springframework.data.couchbase.repository.support.IndexManager;
@Configuration
public class ReactiveIntegrationTestApplicationConfig extends AbstractReactiveCouchbaseConfiguration {
@Bean
public String couchbaseAdminUser() {
return "Administrator";
}
@Bean
public String couchbaseAdminPassword() {
return "password";
}
@Override
protected List<String> getBootstrapHosts() {
return Collections.singletonList("127.0.0.1");
}
@Override
protected String getBucketName() {
return "protected";
}
@Override
protected String getBucketPassword() {
return "password";
}
@Override
protected CouchbaseEnvironment getEnvironment() {
return DefaultCouchbaseEnvironment.builder()
.connectTimeout(10000)
.kvTimeout(10000)
.queryTimeout(10000)
.viewTimeout(10000)
.build();
}
@Override
public RxJavaCouchbaseTemplate reactiveCouchbaseTemplate() throws Exception {
RxJavaCouchbaseTemplate template = super.reactiveCouchbaseTemplate();
template.setWriteResultChecking(WriteResultChecking.LOG);
return template;
}
//this is for dev so it is ok to auto-create indexes
@Override
public IndexManager indexManager() {
return new IndexManager();
}
@Override
protected Consistency getDefaultConsistency() {
return Consistency.READ_YOUR_OWN_WRITES;
}
}

View File

@@ -1,68 +0,0 @@
package org.springframework.data.couchbase;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.rules.ExternalResource;
import org.testcontainers.containers.FixedHostPortGenericContainer;
/**
* Testcontainers as external resource. It is recommended to use it as ClassRule.
* It also does the internal reference counting, in case if the getResource is called again.
*
*/
public class TestContainerResource extends ExternalResource {
private static FixedHostPortGenericContainer couchbaseContainer = null;
private static final AtomicInteger referenceCount = new AtomicInteger();
private static TestContainerResource currentInstance;
private static String serverVersion;
public static TestContainerResource getResource() {
if (currentInstance == null) {
currentInstance = new TestContainerResource();
try {
Properties properties = new Properties();
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("server.properties"));
serverVersion = properties.getProperty("server.version");
if(!"container".equals(properties.getProperty("server.resource"))) {
return null;
}
} catch (IOException ex) {
throw new RuntimeException(ex);
}
couchbaseContainer = new FixedHostPortGenericContainer("couchbase:" + serverVersion)
.withFixedExposedPort(8091, 8091)
.withFixedExposedPort(18091, 18091)
.withFixedExposedPort(8092, 8092)
.withFixedExposedPort(18092, 18092)
.withFixedExposedPort(8093, 8093)
.withFixedExposedPort(18093, 18093)
.withFixedExposedPort(8094, 8094)
.withFixedExposedPort(18094, 18094)
.withFixedExposedPort(11210, 11210)
.withFixedExposedPort(11211, 11211)
.withFixedExposedPort(11207, 11207);
couchbaseContainer.waitingFor(new CouchbaseWaitStrategy(serverVersion, couchbaseContainer));
couchbaseContainer.start();
}
return currentInstance;
}
@Override
protected void before() {
referenceCount.incrementAndGet();
}
@Override
protected void after() {
if (referenceCount.decrementAndGet() == 0 && couchbaseContainer != null) {
if(couchbaseContainer.isRunning()) {
couchbaseContainer.close();
}
currentInstance = null;
}
}
}

View File

@@ -1,89 +0,0 @@
package org.springframework.data.couchbase;
import static org.mockito.Mockito.*;
import java.util.Collections;
import java.util.List;
import org.mockito.Mockito;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.WriteResultChecking;
import org.springframework.data.couchbase.core.query.Consistency;
import org.springframework.data.couchbase.repository.support.IndexManager;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.CouchbaseBucket;
import com.couchbase.client.java.CouchbaseCluster;
import com.couchbase.client.java.cluster.ClusterInfo;
import com.couchbase.client.java.cluster.DefaultClusterInfo;
import com.couchbase.client.java.util.features.CouchbaseFeature;
import com.couchbase.client.java.util.features.Version;
@Configuration
public class UnitTestApplicationConfig extends AbstractCouchbaseConfiguration {
@Bean
public String couchbaseAdminUser() {
return "someLogin";
}
@Bean
public String couchbaseAdminPassword() {
return "somePassword";
}
@Override
protected List<String> getBootstrapHosts() {
return Collections.singletonList("192.1.2.3");
}
@Override
protected String getBucketName() {
return "someBucket";
}
@Override
protected String getBucketPassword() {
return "someBucketPassword";
}
@Override
public Cluster couchbaseCluster() throws Exception {
return Mockito.mock(CouchbaseCluster.class);
}
@Override
public ClusterInfo couchbaseClusterInfo() {
DefaultClusterInfo mock = Mockito.mock(DefaultClusterInfo.class);
when(mock.checkAvailable(CouchbaseFeature.N1QL)).thenReturn(true);
when(mock.getMinVersion()).thenReturn(new Version(4, 0, 0));
return mock;
}
@Override
public Bucket couchbaseClient() throws Exception {
return Mockito.mock(CouchbaseBucket.class);
}
@Override
public CouchbaseTemplate couchbaseTemplate() throws Exception {
CouchbaseTemplate template = super.couchbaseTemplate();
template.setWriteResultChecking(WriteResultChecking.LOG);
return template;
}
//this is for dev so it is ok to auto-create indexes
@Override
public IndexManager indexManager() {
return new IndexManager();
}
@Override
protected Consistency getDefaultConsistency() {
return Consistency.READ_YOUR_OWN_WRITES;
}
}

View File

@@ -1,164 +0,0 @@
package org.springframework.data.couchbase.config;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.CouchbaseCluster;
import com.couchbase.client.java.cluster.ClusterInfo;
import com.couchbase.client.java.document.JsonDocument;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.annotation.Id;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.repository.CouchbaseRepository;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
import org.springframework.stereotype.Repository;
import org.springframework.test.context.ContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* This test case demonstrates that the {@link AbstractCouchbaseDataConfiguration} can take its SDK beans
* from a sibling {@link Configuration}.
*
* Tests DATACOUCH-279
*
* @author Simon Baslé
*/
@SuppressWarnings("SpringJavaAutowiringInspection")
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration
public class AbstractCouchbaseDataConfigurationIntegrationTests {
@Autowired
ItemRepository repository;
@Autowired
Bucket client;
@Configuration
static class SdkConfig {
private static final String IP = "127.0.0.1";
private static final String BUCKET_NAME = "protected";
private static final String BUCKET_PASSWORD = "password";
public static Bucket bucket;
@Bean
public Cluster couchbaseCluster() {
return CouchbaseCluster.create(couchbaseEnv(), IP);
}
@Bean
public ClusterInfo couchbaseClusterInfo() {
return couchbaseCluster().clusterManager(BUCKET_NAME, BUCKET_PASSWORD).info();
}
@Bean
public Bucket couchbaseBucket() {
Bucket b = couchbaseCluster().openBucket(BUCKET_NAME, BUCKET_PASSWORD);
bucket = b;
return b;
}
@Bean
public CouchbaseEnvironment couchbaseEnv() {
return DefaultCouchbaseEnvironment.create();
}
}
@Configuration
@EnableCouchbaseRepositories(basePackageClasses = AbstractCouchbaseDataConfigurationIntegrationTests.class, considerNestedRepositories = true)
static class Config extends AbstractCouchbaseDataConfiguration {
@Autowired
Cluster c;
@Autowired
ClusterInfo ci;
@Autowired
Bucket b;
@Autowired
CouchbaseEnvironment e;
@Override
protected CouchbaseConfigurer couchbaseConfigurer() {
return new TestCouchbaseConfigurer(e, c, ci, b);
}
}
@Test
public void testInjectedBucketIsFromAdditionalConfig() {
assertThat(SdkConfig.bucket).isSameAs(client);
}
@Test
public void testTemplateIsUsable() {
String key = "simpleConfigTest";
assertThat(repository).isNotNull();
Item item = new Item();
item.id = key;
item.value = "Test if the SimpleCouchbaseConfiguration can correctly get Bucket/Cluster/etc... beans injected";
repository.save(item);
JsonDocument testDoc = client.get(key);
assertThat(testDoc).isNotNull();
assertThat(testDoc.content()).isNotNull();
assertThat(testDoc.content().getString("value")).isEqualTo(item.value);
}
private static class Item {
@Id
public String id;
public String value;
}
private static class TestCouchbaseConfigurer implements CouchbaseConfigurer {
private CouchbaseEnvironment env;
private Cluster cluster;
private ClusterInfo info;
private Bucket bucket;
public TestCouchbaseConfigurer(CouchbaseEnvironment env, Cluster cluster, ClusterInfo info, Bucket bucket) {
this.env = env;
this.cluster = cluster;
this.info = info;
this.bucket = bucket;
}
@Override
public CouchbaseEnvironment couchbaseEnvironment() throws Exception {
return this.env;
}
@Override
public Cluster couchbaseCluster() throws Exception {
return this.cluster;
}
@Override
public ClusterInfo couchbaseClusterInfo() throws Exception {
return this.info;
}
@Override
public Bucket couchbaseClient() throws Exception {
return this.bucket;
}
}
@Repository
interface ItemRepository extends CouchbaseRepository<Item, String> {}
}

View File

@@ -1,135 +0,0 @@
/*
* 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.config;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;
import static org.assertj.core.api.Assertions.assertThat;
public class CouchbaseBucketParserTest {
private static DefaultListableBeanFactory factory;
@BeforeClass
public static void setUp() {
factory = new DefaultListableBeanFactory();
BeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
int n = reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbaseBucket-bean.xml"));
System.out.println(n);
}
@AfterClass
public static void tearDown() {
}
@Test
public void testDefaultBucketNoCluster() {
BeanDefinition def = factory.getBeanDefinition("bucketDefaultNoCluster");
assertThat(def).isNotNull();
assertThat(def.getConstructorArgumentValues().getArgumentCount()).isEqualTo(1);
assertThat(def.getPropertyValues().size()).isEqualTo(0);
ConstructorArgumentValues.ValueHolder holder = def.getConstructorArgumentValues()
.getArgumentValue(0, Object.class);
assertThat(holder.getValue()).isInstanceOf(RuntimeBeanReference.class);
RuntimeBeanReference clusterRef = (RuntimeBeanReference) holder.getValue();
assertThat(clusterRef.getBeanName()).isEqualTo(BeanNames.COUCHBASE_CLUSTER);
}
@Test
public void testDefaultBucket() throws Exception {
BeanDefinition def = factory.getBeanDefinition("bucketDefault");
assertThat(def).isNotNull();
assertThat(def.getConstructorArgumentValues().getArgumentCount()).isEqualTo(1);
assertThat(def.getPropertyValues().size()).isEqualTo(0);
ConstructorArgumentValues.ValueHolder holder = def.getConstructorArgumentValues()
.getArgumentValue(0, Object.class);
assertThat(holder.getValue()).isInstanceOf(RuntimeBeanReference.class);
RuntimeBeanReference clusterRef = (RuntimeBeanReference) holder.getValue();
assertThat(clusterRef.getBeanName()).isEqualTo("clusterDefault");
}
@Test
public void testBucketWithName() throws Exception {
BeanDefinition def = factory.getBeanDefinition("bucketWithName");
assertThat(def).isNotNull();
assertThat(def.getConstructorArgumentValues().getArgumentCount()).isEqualTo(2);
assertThat(def.getPropertyValues().size()).isEqualTo(0);
ConstructorArgumentValues.ValueHolder holder = def.getConstructorArgumentValues()
.getArgumentValue(0, Object.class);
assertThat(holder.getValue()).isInstanceOf(RuntimeBeanReference.class);
RuntimeBeanReference clusterRef = (RuntimeBeanReference) holder.getValue();
assertThat(clusterRef.getBeanName()).isEqualTo("clusterDefault");
ConstructorArgumentValues.ValueHolder nameHolder = def.getConstructorArgumentValues()
.getArgumentValue(1, Object.class);
assertThat(nameHolder.getValue()).isInstanceOf(String.class);
assertThat(nameHolder.getValue()).hasToString("toto");
}
@Test
public void testBucketWithNameAndPassword() throws Exception {
BeanDefinition def = factory.getBeanDefinition("bucketWithNameAndPassword");
assertThat(def).isNotNull();
assertThat(def.getConstructorArgumentValues().getArgumentCount()).isEqualTo(4);
assertThat(def.getPropertyValues().size()).isEqualTo(0);
ConstructorArgumentValues.ValueHolder holder = def.getConstructorArgumentValues()
.getArgumentValue(0, Object.class);
assertThat(holder.getValue()).isInstanceOf(RuntimeBeanReference.class);
RuntimeBeanReference clusterRef = (RuntimeBeanReference) holder.getValue();
assertThat(clusterRef.getBeanName()).isEqualTo("clusterDefault");
ConstructorArgumentValues.ValueHolder nameHolder = def.getConstructorArgumentValues()
.getArgumentValue(1, Object.class);
assertThat(nameHolder.getValue()).isInstanceOf(String.class);
assertThat(nameHolder.getValue()).hasToString("test");
ConstructorArgumentValues.ValueHolder usernameHolder = def.getConstructorArgumentValues()
.getArgumentValue(2, Object.class);
assertThat(usernameHolder.getValue()).isInstanceOf(String.class);
assertThat(usernameHolder.getValue()).hasToString("testuser");
ConstructorArgumentValues.ValueHolder passwordHolder = def.getConstructorArgumentValues()
.getArgumentValue(3, Object.class);
assertThat(passwordHolder.getValue()).isInstanceOf(String.class);
assertThat(passwordHolder.getValue()).hasToString("123");
}
}

View File

@@ -1,156 +0,0 @@
/*
* 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.config;
import java.util.List;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
public class CouchbaseClusterParserTest {
private static DefaultListableBeanFactory factory;
@BeforeClass
public static void setUp() {
factory = new DefaultListableBeanFactory();
BeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
int n = reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbaseCluster-bean.xml"));
System.out.println(n);
}
@AfterClass
public static void tearDown() {
}
@Test
public void testClusterWithoutSpecificEnv() {
BeanDefinition def = factory.getBeanDefinition("clusterDefault");
assertThat(def).isNotNull();
assertThat(def.getConstructorArgumentValues().getArgumentCount()).isEqualTo(1);
assertThat(def.getPropertyValues().size()).isEqualTo(0);
assertThat(def.getFactoryMethodName()).isEqualTo("create");
ConstructorArgumentValues.ValueHolder holder = def.getConstructorArgumentValues()
.getArgumentValue(0, CouchbaseEnvironment.class);
assertThat(holder.getValue()).isInstanceOf(RuntimeBeanReference.class);
RuntimeBeanReference envRef = (RuntimeBeanReference) holder.getValue();
assertThat(envRef.getBeanName()).isEqualTo("couchbaseEnv");
}
@Test
public void testClusterWithNodes() {
BeanDefinition def = factory.getBeanDefinition("clusterWithNodes");
assertThat(def).isNotNull();
assertThat(def.getConstructorArgumentValues().getArgumentCount()).isEqualTo(2);
assertThat(def.getPropertyValues().size()).isEqualTo(0);
assertThat(def.getFactoryMethodName()).isEqualTo("create");
ConstructorArgumentValues.ValueHolder holder = def.getConstructorArgumentValues()
.getArgumentValue(1, List.class);
assertThat(holder.getValue()).isInstanceOf(List.class);
List nodes = (List<String>) holder.getValue();
assertThat(nodes.size()).isEqualTo(2);
assertThat((String) nodes.get(0)).isEqualTo("192.1.2.3");
assertThat((String) nodes.get(1)).isEqualTo("192.4.5.6");
}
@Test
public void testClusterWithEnvInline() {
BeanDefinition def = factory.getBeanDefinition("clusterWithEnvInline");
assertThat(def).isNotNull();
assertThat(def.getConstructorArgumentValues().getArgumentCount()).isEqualTo(1);
assertThat(def.getPropertyValues().size()).isEqualTo(0);
ConstructorArgumentValues.ValueHolder holder = def.getConstructorArgumentValues()
.getArgumentValue(0, CouchbaseEnvironment.class);
GenericBeanDefinition envDef = (GenericBeanDefinition) holder.getValue();
assertThat(envDef.getBeanClassName())
.isEqualTo(CouchbaseEnvironmentFactoryBean.class.getName());
assertThat(envDef.getPropertyValues().contains("managementTimeout"))
.as("unexpected attribute").isTrue();
}
@Test
public void testClusterWithEnvRef() {
BeanDefinition def = factory.getBeanDefinition("clusterWithEnvRef");
assertThat(def).isNotNull();
assertThat(def.getConstructorArgumentValues().getArgumentCount()).isEqualTo(1);
assertThat(def.getPropertyValues().size()).isEqualTo(0);
ConstructorArgumentValues.ValueHolder holder = def.getConstructorArgumentValues()
.getArgumentValue(0, CouchbaseEnvironment.class);
assertThat(holder.getValue()).isInstanceOf(RuntimeBeanReference.class);
RuntimeBeanReference envRef = (RuntimeBeanReference) holder.getValue();
assertThat(envRef.getBeanName()).isEqualTo("someEnv");
}
@Test
public void testClusterConfigurationPrecedence() {
BeanDefinition def = factory.getBeanDefinition("clusterWithAll");
assertThat(def).isNotNull();
assertThat(def.getConstructorArgumentValues().getArgumentCount()).isEqualTo(2);
assertThat(def.getPropertyValues().size()).isEqualTo(0);
assertThat(def.getFactoryMethodName()).isEqualTo("create");
assertThat(def.getConstructorArgumentValues().getIndexedArgumentValues().get(0)
.getValue()).isInstanceOf(GenericBeanDefinition.class);
assertThat(def.getConstructorArgumentValues().getIndexedArgumentValues().get(1)
.getValue()).isInstanceOf(List.class);
ConstructorArgumentValues.ValueHolder holderEnv = def.getConstructorArgumentValues()
.getArgumentValue(0, CouchbaseEnvironment.class);
GenericBeanDefinition envDef = (GenericBeanDefinition) holderEnv.getValue();
assertThat(envDef.getBeanClassName())
.isEqualTo(CouchbaseEnvironmentFactoryBean.class.getName());
assertThat(envDef.getPropertyValues().contains("autoreleaseAfter"))
.as("unexpected attribute").isTrue();
ConstructorArgumentValues.ValueHolder holderNodes = def.getConstructorArgumentValues()
.getArgumentValue(1, List.class);
List nodes = (List<String>) holderNodes.getValue();
assertThat(nodes.size()).isEqualTo(2);
assertThat((String) nodes.get(0)).isEqualTo("2.2.2.2");
assertThat((String) nodes.get(1)).isEqualTo("4.4.4.4");
}
}

View File

@@ -1,27 +0,0 @@
package org.springframework.data.couchbase.config;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.IntegrationTestNoShutdownApplicationConfig;
import org.springframework.test.context.ContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Simple test to make sure that environment is not shutdown if not life cycle managed by Spring.
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = IntegrationTestNoShutdownApplicationConfig.class)
public class CouchbaseEnvironmentNoShutdownProxyIntegrationTests {
@Autowired
public CouchbaseEnvironment environment;
@Test
public void testEnvironmentShutDown() {
assertThat(environment.shutdown()).as("Should return false").isEqualTo(false);
}
}

View File

@@ -1,122 +0,0 @@
/*
* 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.config;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.ClassPathResource;
import com.couchbase.client.core.retry.BestEffortRetryStrategy;
import com.couchbase.client.core.retry.FailFastRetryStrategy;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
public class CouchbaseEnvironmentParserTest {
private static GenericApplicationContext context;
@BeforeClass
public static void setUp() {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
BeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbaseEnv-bean.xml"));
context = new GenericApplicationContext(factory);
context.refresh();
}
@Test
public void testParsingRetryStrategyFailFast() throws Exception {
CouchbaseEnvironment env = context.getBean("envWithFailFast", CouchbaseEnvironment.class);
assertThat(env.retryStrategy()).isInstanceOf(FailFastRetryStrategy.class);
}
@Test
public void testParsingRetryStrategyBestEffort() throws Exception {
CouchbaseEnvironment env = context.getBean("envWithBestEffort", CouchbaseEnvironment.class);
assertThat(env.retryStrategy()).isInstanceOf(BestEffortRetryStrategy.class);
}
@Test
public void testAllDefaultsOverridden() {
CouchbaseEnvironment env = context.getBean("envWithNoDefault", CouchbaseEnvironment.class);
CouchbaseEnvironment defaultEnv = DefaultCouchbaseEnvironment.create();
assertThat(env).isInstanceOf(DefaultCouchbaseEnvironment.class);
assertThat(env.managementTimeout()).isEqualTo(1L);
assertThat(env.queryTimeout()).isEqualTo(2L);
assertThat(env.viewTimeout()).isEqualTo(3L);
assertThat(env.kvTimeout()).isEqualTo(4L);
assertThat(env.connectTimeout()).isEqualTo(5L);
assertThat(env.disconnectTimeout()).isEqualTo(6L);
assertThat(env.dnsSrvEnabled()).isTrue().isNotEqualTo(defaultEnv.dnsSrvEnabled());
assertThat(env.sslEnabled()).isTrue().isNotEqualTo(defaultEnv.sslEnabled());
assertThat(env.sslKeystoreFile()).isEqualTo("test");
assertThat(env.sslKeystorePassword()).isEqualTo("test");
assertThat(env.bootstrapHttpEnabled()).isFalse()
.isNotEqualTo(defaultEnv.bootstrapHttpEnabled());
assertThat(env.bootstrapCarrierEnabled()).isFalse()
.isNotEqualTo(defaultEnv.bootstrapCarrierEnabled());
assertThat(env.bootstrapHttpDirectPort()).isEqualTo(8);
assertThat(env.bootstrapHttpSslPort()).isEqualTo(9);
assertThat(env.bootstrapCarrierDirectPort()).isEqualTo(10);
assertThat(env.bootstrapCarrierSslPort()).isEqualTo(11);
assertThat(env.ioPoolSize()).isEqualTo(12);
assertThat(env.computationPoolSize()).isEqualTo(13);
assertThat(env.responseBufferSize()).isEqualTo(14);
assertThat(env.requestBufferSize()).isEqualTo(15);
assertThat(env.kvEndpoints()).isEqualTo(16);
assertThat(env.viewEndpoints()).isEqualTo(17);
assertThat(env.queryEndpoints()).isEqualTo(18);
assertThat(env.retryStrategy()).isInstanceOf(FailFastRetryStrategy.class);
assertThat(env.maxRequestLifetime()).isEqualTo(19L);
assertThat(env.keepAliveInterval()).isEqualTo(20L);
assertThat(env.autoreleaseAfter()).isEqualTo(21L);
assertThat(env.bufferPoolingEnabled()).isFalse()
.isNotEqualTo(defaultEnv.bufferPoolingEnabled());
assertThat(env.tcpNodelayEnabled()).isFalse()
.isNotEqualTo(defaultEnv.tcpNodelayEnabled());
assertThat(env.mutationTokensEnabled()).isTrue()
.isNotEqualTo(defaultEnv.mutationTokensEnabled());
assertThat(env.analyticsTimeout()).isEqualTo(30L);
assertThat(env.configPollInterval()).isEqualTo(50L);
assertThat(env.configPollFloorInterval()).isEqualTo(30L);
assertThat(env.operationTracingEnabled()).isFalse()
.isNotEqualTo(defaultEnv.operationTracingEnabled());
assertThat(env.operationTracingServerDurationEnabled()).isFalse()
.isNotEqualTo(defaultEnv.operationTracingServerDurationEnabled());
assertThat(env.orphanResponseReportingEnabled()).isFalse()
.isNotEqualTo(defaultEnv.orphanResponseReportingEnabled());
assertThat(env.compressionMinSize()).isEqualTo(100);
assertThat(env.compressionMinRatio()).isEqualTo(0.90);
}
@AfterClass
public static void tearDown() {
context.close();
}
}

View File

@@ -1,58 +0,0 @@
/*
* 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.config;
import org.junit.Test;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.ClassPathResource;
import com.couchbase.client.core.env.DefaultCoreEnvironment;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Simon Bland
*/
public class CouchbaseSingleEnvironmentParserTest {
/**
* See DATACOUCH-235
*/
@Test
public void testSingleCouchbaseEnvironment() throws Exception {
int instanceCounterBefore = DefaultCoreEnvironment.instanceCounter();
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
BeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbaseSingleEnv-bean.xml"));
GenericApplicationContext context = new GenericApplicationContext(factory);
context.refresh();
CouchbaseEnvironment env = context.getBean("singleEnv", CouchbaseEnvironment.class);
context.close();
int instanceCounterAfter = DefaultCoreEnvironment.instanceCounter();
assertThat(env).isInstanceOf(DefaultCouchbaseEnvironment.class);
assertThat(instanceCounterAfter).isEqualTo(instanceCounterBefore + 1);
}
}

View File

@@ -1,147 +0,0 @@
/*
* Copyright 2013-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.config;
import com.couchbase.client.java.document.JsonDocument;
import com.couchbase.client.java.document.json.JsonObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.core.query.Consistency;
import org.springframework.data.couchbase.repository.User;
import org.springframework.test.context.ContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Michael Nitschinger
* @author Simon Baslé
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = CouchbaseTemplateParserIntegrationTests.class)
public class CouchbaseTemplateParserIntegrationTests {
DefaultListableBeanFactory factory;
BeanDefinitionReader reader;
@Before
public void setUp() {
factory = new DefaultListableBeanFactory();
reader = new XmlBeanDefinitionReader(factory);
}
@Test
public void readsCouchbaseTemplateAttributesCorrectly() {
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-template-bean.xml"));
BeanDefinition definition = factory.getBeanDefinition(BeanNames.COUCHBASE_TEMPLATE);
assertThat(definition.getConstructorArgumentValues().getArgumentCount()).isEqualTo(2);
factory.getBean(BeanNames.COUCHBASE_TEMPLATE);
}
@Test
public void readsCouchbaseTemplateWithTranslationServiceAttributesCorrectly() {
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-template-with-translation-service-bean.xml"));
BeanDefinition definition = factory.getBeanDefinition(BeanNames.COUCHBASE_TEMPLATE);
assertThat(definition.getConstructorArgumentValues().getArgumentCount()).isEqualTo(3);
factory.getBean(BeanNames.COUCHBASE_TEMPLATE);
}
/**
* Test case for DATACOUCH-47.
*/
@Test
public void allowsMultipleBuckets() {
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-multi-bucket-bean.xml"));
factory.getBean("cb-template-first");
factory.getBean("cb-template-second");
}
/**
* Test case for DATACOUCH-134 in xml: field for storing type information can be renamed.
*/
@Test
public void testTypeFieldCanBeChosen() {
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-typekey.xml"));
CouchbaseTemplate template = factory.getBean(BeanNames.COUCHBASE_TEMPLATE, CouchbaseTemplate.class);
assertThat(template.getConverter() instanceof MappingCouchbaseConverter).isTrue();
MappingCouchbaseConverter converter = ((MappingCouchbaseConverter) template.getConverter());
assertThat(converter.getTypeKey()).isEqualTo("javaXmlClass");
User u = new User("specialSaveUser", "John Locke", 46);
template.save(u);
JsonDocument uJsonDoc = template.getCouchbaseBucket().get("specialSaveUser");
template.getCouchbaseBucket().remove("specialSaveUser");
assertThat(uJsonDoc).isNotNull();
JsonObject uJson = uJsonDoc.content();
assertThat(uJson.get(MappingCouchbaseConverter.TYPEKEY_DEFAULT)).isNull();
assertThat(uJson.getString("javaXmlClass"))
.isEqualTo("org.springframework.data.couchbase.repository.User");
assertThat(uJson.getString("username")).isEqualTo("John Locke");
}
/**
* Test case for DATACOUCH-148, choosing an alternative default for view/N1QL staleness.
*/
@Test
public void shouldParseCustomStaleness() {
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-consistency.xml"));
CouchbaseTemplate template = factory.getBean("template", CouchbaseTemplate.class);
assertThat(template.getDefaultConsistency())
.isEqualTo(Consistency.EVENTUALLY_CONSISTENT);
assertThat(template.getDefaultConsistency())
.isNotEqualTo(Consistency.DEFAULT_CONSISTENCY);
}
/**
* Test case for DATACOUCH-148, choosing an unknown value for view/N1QL staleness.
*/
@Test
public void shouldIgnoreBadCustomStaleness() {
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-consistency.xml"));
CouchbaseTemplate template = factory.getBean("templateBad", CouchbaseTemplate.class);
assertThat(template.getDefaultConsistency())
.isEqualTo(Consistency.DEFAULT_CONSISTENCY);
}
@Test
public void shouldHaveDefaultsForStaleness() {
//use another resource where staleness isn't customized
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-template-with-translation-service-bean.xml"));
CouchbaseTemplate template = factory.getBean(BeanNames.COUCHBASE_TEMPLATE, CouchbaseTemplate.class);
assertThat(template.getDefaultConsistency())
.isEqualTo(Consistency.DEFAULT_CONSISTENCY);
}
}

View File

@@ -1,55 +0,0 @@
package org.springframework.data.couchbase.core;
import rx.observers.TestSubscriber;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;
public class AsyncUtils {
public static void executeConcurrently(int numThreads, Callable<Void> task) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(numThreads);
Collection<Callable<Void>> tasks = Collections.nCopies(numThreads, task);
List<Future<Void>> futures = pool.invokeAll(tasks);
for (Future future : futures) {
future.get(numThreads, TimeUnit.SECONDS);
}
}
public static <T> void awaitCompleted(TestSubscriber<T> testSubscriber) {
testSubscriber.awaitTerminalEvent();
testSubscriber.assertNoErrors();
testSubscriber.assertNoValues();
testSubscriber.assertCompleted();
}
public static <T> void awaitCompletedWithAnyValue(TestSubscriber<T> testSubscriber) {
testSubscriber.awaitTerminalEvent();
testSubscriber.assertNoErrors();
testSubscriber.assertCompleted();
}
public static <T> void awaitCompletedWithValueCount(TestSubscriber<T> testSubscriber, int count) {
testSubscriber.awaitTerminalEvent();
testSubscriber.assertNoErrors();
testSubscriber.assertValueCount(count);
testSubscriber.assertCompleted();
}
public static <T> void awaitError(TestSubscriber<T> testSubscriber, Class<? extends Throwable> throwableClazz) {
testSubscriber.awaitTerminalEvent();
testSubscriber.assertError(throwableClazz);
testSubscriber.assertNoValues();
}
public static <T> void awaitValue(TestSubscriber<T> testSubscriber, T value) {
testSubscriber.awaitTerminalEvent();
testSubscriber.assertNoErrors();
testSubscriber.assertValue(value);
testSubscriber.assertCompleted();
}
}

View File

@@ -1,86 +0,0 @@
/*
* Copyright 2013-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.core;
import org.springframework.data.annotation.Id;
import com.couchbase.client.java.repository.annotation.Field;
/**
* Test class for persisting and loading from {@link CouchbaseTemplate}.
*
* @author Michael Nitschinger
* @author Subhashni Balakrishnan
*/
public class Beer {
@Id
private final String id;
private String name;
@Field("is_active")
private boolean active = true;
@Field("desc")
private String description;
public Beer(String id, String name, Boolean active, String description) {
this.id = id;
this.name = name;
this.active = active;
this.description = description;
}
@Override
public String toString() {
return "Beer [id=" + id + ", name=" + name + ", active=" + active + ", description=" + description + "]";
}
public Beer setName(String name) {
this.name = name;
return this;
}
public String getName() {
return name;
}
public Beer setActive(boolean active) {
this.active = active;
return this;
}
public boolean getActive() {
return active;
}
public Beer setDescription(String description) {
this.description = description;
return this;
}
public String getDescription() {
return description;
}
public String getId() {
return id;
}
}

View File

@@ -1,34 +0,0 @@
package org.springframework.data.couchbase.core;
import com.couchbase.client.java.repository.annotation.Field;
/**
* Test DTO for projecting from {@link CouchbaseTemplate}.
*
* @author Subhashni Balakrishnan
*/
public class BeerDTO{
private String name;
@Field("desc")
private String description;
public BeerDTO(String name, String description) {
this.name = name;
this.description = description;
}
public BeerDTO setName(String name) {
this.name = name;
return this;
}
public String getName() { return name; }
public BeerDTO setDescription(String description) {
this.description = description;
return this;
}
public String getDescription() { return description; }
}

View File

@@ -1,10 +0,0 @@
package org.springframework.data.couchbase.core;
/**
* Test interface for projecting data from {@link CouchbaseTemplate}.
*
* @author Subhashni Balakrishnan
*/
public interface BeerProjection {
String getDescription();
}

View File

@@ -1,152 +0,0 @@
package org.springframework.data.couchbase.core;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.data.couchbase.core.mapping.id.GenerationStrategy.*;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.error.DocumentDoesNotExistException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.couchbase.core.mapping.id.GeneratedValue;
import org.springframework.data.couchbase.core.mapping.id.IdAttribute;
import org.springframework.data.couchbase.core.mapping.id.IdPrefix;
import org.springframework.data.couchbase.core.mapping.id.IdSuffix;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Subhashni Balakrishnan
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
public class CouchbaseTemplateIdGenerationIntegrationTests {
@Rule
public TestName testName = new TestName();
@Autowired
private Bucket client;
@Autowired
private CouchbaseTemplate template;
private void removeIfExist(String key) {
try {
client.remove(key);
}
catch (DocumentDoesNotExistException e) {
//ignore
}
}
@Test
public void shouldGenerateIdUsingAtrributes() throws Exception {
SimpleClassWithGeneratedIdValueUsingAttributes simpleClass = new SimpleClassWithGeneratedIdValueUsingAttributes();
String generatedId = template.getGeneratedId(simpleClass);
removeIfExist(generatedId);
assertThat("prefix1::prefix2::0::1::2.0::3.0::4::Simple::Nested{value:simple}::suffix1::suffix2")
.as("Id generation should be correct").isEqualTo(generatedId);
template.insert(simpleClass);
assertThat(template.exists(generatedId)).as("Exists after insert")
.isEqualTo(true);
simpleClass.value = "modified";
template.save(simpleClass);
SimpleClassWithGeneratedIdValueUsingAttributes modifiedClass = template.findById(generatedId,
SimpleClassWithGeneratedIdValueUsingAttributes.class);
assertThat(modifiedClass.id).as("Get after save id should be correct")
.isEqualTo(generatedId);
template.update(simpleClass);
SimpleClassWithGeneratedIdValueUsingAttributes updatedClass = template.findById(generatedId,
SimpleClassWithGeneratedIdValueUsingAttributes.class);
assertThat(updatedClass.id).as("Get after update id should be correct")
.isEqualTo(generatedId);
template.remove(generatedId);
assertThat(template.exists(generatedId)).as("Exists after remove")
.isEqualTo(false);
}
@Test
public void shouldGenerateIdUsingUUID() throws Exception {
SimpleClassWithGeneratedIdValueUsingUUID simpleClass = new SimpleClassWithGeneratedIdValueUsingUUID();
String generatedId = template.getGeneratedId(simpleClass);
simpleClass.id = generatedId;
template.insert(simpleClass);
assertThat(simpleClass.id).as("Should not regenerate id").isEqualTo(generatedId);
template.remove(generatedId);
assertThat(template.exists(generatedId)).as("Exists after remove")
.isEqualTo(false);
}
@Document
static class SimpleClassWithGeneratedIdValueUsingAttributes {
@Id @GeneratedValue(strategy = USE_ATTRIBUTES, delimiter = "::")
public String id;
@IdAttribute(order = 6)
public Nested nested = new Nested("simple");
@IdAttribute(order = 5)
public String type = "Simple";
@IdAttribute(order = 4)
public int intNum = 4;
@IdAttribute(order = 2)
public float floatNum = 2F;
@IdAttribute(order = 3)
public double doubleNum = 3;
@IdAttribute
public long longNum = 0L;
@IdAttribute(order = 1)
public short shortNum = 1;
@IdPrefix(order = 1)
public String prefix2 = "prefix2";
@IdPrefix
public String prefix1 = "prefix1";
@IdSuffix(order = 1)
public String suffix2 = "suffix2";
@IdSuffix
public String suffix1 = "suffix1";
public String value = "new";
}
static class Nested {
private String value;
public Nested(String value) {
this.value = value;
}
@Override
public String toString() {
return "Nested{value:" + value + "}";
}
}
@Document
static class SimpleClassWithGeneratedIdValueUsingUUID {
@Id @GeneratedValue(strategy = UNIQUE)
public String id;
public String value = "new";
}
}

View File

@@ -1,812 +0,0 @@
/*
* Copyright 2013-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.core;
import static com.couchbase.client.java.query.Select.select;
import static com.couchbase.client.java.query.dsl.Expression.*;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicLong;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.document.RawJsonDocument;
import com.couchbase.client.java.error.DocumentDoesNotExistException;
import com.couchbase.client.java.query.N1qlParams;
import com.couchbase.client.java.query.N1qlQuery;
import com.couchbase.client.java.query.N1qlQueryResult;
import com.couchbase.client.java.query.consistency.ScanConsistency;
import com.couchbase.client.java.repository.annotation.Field;
import com.couchbase.client.java.view.Stale;
import com.couchbase.client.java.view.ViewQuery;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Version;
;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
/**
* @author Michael Nitschinger
* @author Simon Baslé
* @author Anastasiia Smirnova
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
@TestExecutionListeners(CouchbaseTemplateQueryListener.class)
public class CouchbaseTemplateIntegrationTests {
@Rule
public TestName testName = new TestName();
@Autowired
private Bucket client;
@Autowired
private CouchbaseTemplate template;
private static final ObjectMapper MAPPER = new ObjectMapper();
private void removeIfExist(String key) {
try {
template.remove(key);
}
catch (DocumentDoesNotExistException e) {
//ignore
}
}
@Test
public void saveSimpleEntityCorrectly() throws Exception {
String id = "beers:awesome-stout";
removeIfExist(id);
String name = "The Awesome Stout";
boolean active = false;
Beer beer = new Beer(id, name, active, "");
template.save(beer);
RawJsonDocument resultDoc = client.get(id, RawJsonDocument.class);
assertThat(resultDoc).isNotNull();
String result = resultDoc.content();
assertThat(result).isNotNull();
Map<String, Object> resultConv = MAPPER.readValue(result, new TypeReference<Map<String, Object>>() {});
assertThat(resultConv.get(MappingCouchbaseConverter.TYPEKEY_DEFAULT)).isNotNull();
assertThat(resultConv.get("javaClass")).isNull();
assertThat(resultConv.get(MappingCouchbaseConverter.TYPEKEY_DEFAULT))
.isEqualTo("org.springframework.data.couchbase.core.Beer");
assertThat(resultConv.get("is_active")).isEqualTo(false);
assertThat(resultConv.get("name")).isEqualTo("The Awesome Stout");
}
@Test
public void saveDocumentWithExpiry() throws Exception {
String id = "simple-doc-with-expiry";
DocumentWithExpiry doc = new DocumentWithExpiry(id);
template.save(doc);
assertThat(client.get(id)).isNotNull();
Thread.sleep(3000);
assertThat(client.get(id)).isNull();
}
@Test
public void insertDoesNotOverride() throws Exception {
String id = "double-insert-test";
removeIfExist(id);
SimplePerson doc = new SimplePerson(id, "Mr. A");
template.insert(doc);
RawJsonDocument resultDoc = client.get(id, RawJsonDocument.class);
assertThat(resultDoc).isNotNull();
String result = resultDoc.content();
Map<String, String> resultConv = MAPPER.readValue(result, new TypeReference<Map<String, String>>() {});
assertThat(resultConv.get("name")).isEqualTo("Mr. A");
doc = new SimplePerson(id, "Mr. B");
try {
template.insert(doc);
} catch (OptimisticLockingFailureException e) {
//ignore, since this insert should fail
}
resultDoc = client.get(id, RawJsonDocument.class);
assertThat(resultDoc).isNotNull();
result = resultDoc.content();
resultConv = MAPPER.readValue(result, new TypeReference<Map<String, String>>() {});
assertThat(resultConv.get("name")).isEqualTo("Mr. A");
}
@Test
public void updateDoesNotInsert() {
String id = "update-does-not-insert";
SimplePerson doc = new SimplePerson(id, "Nice Guy");
template.update(doc);
assertThat(client.get(id)).isNull();
}
@Test
public void removeDocument() {
String id = "beers:to-delete-stout";
Beer beer = new Beer(id, "", false, "");
template.save(beer);
Object result = client.get(id);
assertThat(result).isNotNull();
template.remove(beer);
result = client.get(id);
assertThat(result).isNull();
}
@Test
public void storeListsAndMaps() {
String id = "persons:lots-of-names";
List<String> names = new ArrayList<String>();
names.add("Michael");
names.add("Thomas");
names.add(null);
List<Integer> votes = new LinkedList<Integer>();
Map<String, Boolean> info1 = new HashMap<String, Boolean>();
info1.put("foo", true);
info1.put("bar", false);
info1.put("nullValue", null);
Map<String, Integer> info2 = new HashMap<String, Integer>();
ComplexPerson complex = new ComplexPerson(id, names, votes, info1, info2);
template.save(complex);
assertThat(client.get(id)).isNotNull();
ComplexPerson response = template.findById(id, ComplexPerson.class);
assertThat(response.getFirstnames()).isEqualTo(names);
assertThat(response.getVotes()).isEqualTo(votes);
assertThat(response.getId()).isEqualTo(id);
assertThat(response.getInfo1()).isEqualTo(info1);
assertThat(response.getInfo2()).isEqualTo(info2);
}
@Test
public void validFindById() {
String id = "beers:findme-stout";
String name = "The Findme Stout";
boolean active = true;
Beer beer = new Beer(id, name, active, "");
template.save(beer);
Beer found = template.findById(id, Beer.class);
assertThat(found).isNotNull();
assertThat(found.getId()).isEqualTo(id);
assertThat(found.getName()).isEqualTo(name);
assertThat(found.getActive()).isEqualTo(active);
}
@Test
public void shouldLoadAndMapViewDocs() {
ViewQuery query = ViewQuery.from("test_beers", "by_name");
query.stale(Stale.FALSE);
final List<Beer> beers = template.findByView(query, Beer.class);
assertThat(beers.size() > 0).isTrue();
for (Beer beer : beers) {
assertThat(beer.getId()).isNotNull();
assertThat(beer.getName()).isNotNull();
assertThat(beer.getActive()).isNotNull();
}
}
@Test
public void shouldQueryRaw() {
N1qlQuery query = N1qlQuery.simple(select("name").from(i(client.name()))
.where(x("name").isNotMissing()));
N1qlQueryResult queryResult = template.queryN1QL(query);
assertThat(queryResult).isNotNull();
assertThat(queryResult.finalSuccess()).as(queryResult.errors().toString())
.isTrue();
assertThat(queryResult.allRows().isEmpty()).isFalse();
}
@Test
public void shouldQueryWithMapping() {
FullFragment ff1 = new FullFragment("fullFragment1", 1, "fullFragment", "test1");
FullFragment ff2 = new FullFragment("fullFragment2", 2, "fullFragment", "test2");
template.save(Arrays.asList(ff1, ff2));
N1qlQuery query = N1qlQuery.simple(select(i("value")) //"value" is a n1ql keyword apparently
.from(i(client.name()))
.where(x("type").eq(s("fullFragment"))
.and(x("criteria").gt(1))),
N1qlParams.build().consistency(ScanConsistency.REQUEST_PLUS));
List<Fragment> fragments = template.findByN1QLProjection(query, Fragment.class);
assertThat(fragments).isNotNull();
assertThat(fragments.isEmpty()).isFalse();
assertThat(fragments.size()).isEqualTo(1);
assertThat(fragments.get(0).value).isEqualTo("test2");
}
/**
* @see DATACOUCH-159
*/
@Test
public void shouldDeserialiseLongsAndInts() {
final long longValue = new Date().getTime();
final int intValue = new Random().nextInt();
template.save(new SimpleWithLongAndInt("simpleWithLong:simple", longValue, intValue));
SimpleWithLongAndInt document = template.findById("simpleWithLong:simple", SimpleWithLongAndInt.class);
assertThat(document).isNotNull();
assertThat(document.getLongValue()).isEqualTo(longValue);
assertThat(document.getIntValue()).isEqualTo(intValue);
template.save(new SimpleWithLongAndInt("simpleWithLong:simple:other", intValue, intValue));
document = template.findById("simpleWithLong:simple:other", SimpleWithLongAndInt.class);
assertThat(document).isNotNull();
assertThat(document.getLongValue()).isEqualTo(intValue);
assertThat(document.getIntValue()).isEqualTo(intValue);
}
@Test
public void shouldDeserialiseEnums() {
SimpleWithEnum simpleWithEnum = new SimpleWithEnum("simpleWithEnum:enum", SimpleWithEnum.Type.BIG);
template.save(simpleWithEnum);
simpleWithEnum = template.findById("simpleWithEnum:enum", SimpleWithEnum.class);
assertThat(simpleWithEnum).isNotNull();
assertThat(SimpleWithEnum.Type.BIG).isEqualTo(simpleWithEnum.getType());
}
@Test
public void shouldDeserialiseClass() {
SimpleWithClass simpleWithClass = new SimpleWithClass("simpleWithClass:class", Integer.class);
simpleWithClass.setValue("The dish ran away with the spoon.");
template.save(simpleWithClass);
simpleWithClass = template.findById("simpleWithClass:class", SimpleWithClass.class);
assertThat(simpleWithClass).isNotNull();
assertThat(simpleWithClass.getValue())
.isEqualTo("The dish ran away with the spoon.");
}
@Test
public void shouldHandleCASVersionOnInsert() throws Exception {
removeIfExist("versionedClass:1");
VersionedClass versionedClass = new VersionedClass("versionedClass:1", "foobar");
assertThat(versionedClass.getVersion()).isEqualTo(0);
template.insert(versionedClass);
RawJsonDocument rawStored = client.get("versionedClass:1", RawJsonDocument.class);
assertThat(versionedClass.getVersion()).isEqualTo(rawStored.cas());
}
@Test
public void versionShouldNotUpdateOnSecondInsert() throws Exception {
removeIfExist("versionedClass:2");
VersionedClass versionedClass = new VersionedClass("versionedClass:2", "foobar");
template.insert(versionedClass);
long version1 = versionedClass.getVersion();
try {
template.insert(versionedClass);
} catch (OptimisticLockingFailureException e) {
//ignore, since this insert should fail
}
long version2 = versionedClass.getVersion();
assertThat(version1 > 0).isTrue();
assertThat(version2 > 0).isTrue();
assertThat(version2).isEqualTo(version1);
}
@Test
public void shouldSaveDocumentOnMatchingVersion() throws Exception {
removeIfExist("versionedClass:3");
VersionedClass versionedClass = new VersionedClass("versionedClass:3", "foobar");
template.insert(versionedClass);
long version1 = versionedClass.getVersion();
versionedClass.setField("foobar2");
template.save(versionedClass);
long version2 = versionedClass.getVersion();
assertThat(version1 > 0).isTrue();
assertThat(version2 > 0).isTrue();
assertThat(version2).isNotEqualTo(version1);
assertThat(template.findById("versionedClass:3", VersionedClass.class).getField())
.isEqualTo("foobar2");
}
@Test(expected = OptimisticLockingFailureException.class)
public void shouldNotSaveDocumentOnNotMatchingVersion() throws Exception {
removeIfExist("versionedClass:4");
VersionedClass versionedClass = new VersionedClass("versionedClass:4", "foobar");
template.insert(versionedClass);
RawJsonDocument toCompare = RawJsonDocument.create("versionedClass:4", "different");
assertThat(client.upsert(toCompare)).isNotNull();
versionedClass.setField("foobar2");
//save (aka upsert) won't error in case of CAS mismatch anymore
template.update(versionedClass);
}
@Test
public void shouldUpdateDocumentOnMatchingVersion() throws Exception {
removeIfExist("versionedClass:5");
VersionedClass versionedClass = new VersionedClass("versionedClass:5", "foobar");
template.insert(versionedClass);
long version1 = versionedClass.getVersion();
versionedClass.setField("foobar2");
template.update(versionedClass);
long version2 = versionedClass.getVersion();
assertThat(version1 > 0).isTrue();
assertThat(version2 > 0).isTrue();
assertThat(version2).isNotEqualTo(version1);
assertThat(template.findById("versionedClass:5", VersionedClass.class).getField())
.isEqualTo("foobar2");
}
@Test(expected = OptimisticLockingFailureException.class)
public void shouldNotUpdateDocumentOnNotMatchingVersion() throws Exception {
removeIfExist("versionedClass:6");
VersionedClass versionedClass = new VersionedClass("versionedClass:6", "foobar");
template.insert(versionedClass);
RawJsonDocument toCompare = RawJsonDocument.create("versionedClass:6", "different");
assertThat(client.upsert(toCompare)).isNotNull();
versionedClass.setField("foobar2");
template.update(versionedClass);
}
@Test
public void shouldLoadVersionPropertyOnFind() throws Exception {
removeIfExist("versionedClass:7");
VersionedClass versionedClass = new VersionedClass("versionedClass:7", "foobar");
template.insert(versionedClass);
assertThat(versionedClass.getVersion() > 0).isTrue();
VersionedClass foundClass = template.findById("versionedClass:7", VersionedClass.class);
assertThat(foundClass.getVersion()).isEqualTo(versionedClass.getVersion());
}
@Test
public void shouldUpdateAlreadyExistingDocument() throws Exception {
final String key = testName.getMethodName();
removeIfExist(key);
final AtomicLong counter = new AtomicLong();
VersionedClass initial = new VersionedClass(key, "value-0");
template.save(initial);
AsyncUtils.executeConcurrently(3, new Callable<Void>() {
@Override
public Void call() throws Exception {
boolean saved = false;
while(!saved) {
long counterValue = counter.incrementAndGet();
VersionedClass messageData = template.findById(key, VersionedClass.class);
messageData.field = "value-" + counterValue;
try {
template.save(messageData);
saved = true;
} catch (OptimisticLockingFailureException e) {
}
}
return null;
}
});
VersionedClass actual = template.findById(key, VersionedClass.class);
assertThat(actual.field).isNotEqualTo(initial.field);
assertThat(actual.version).isNotEqualTo(initial.version);
}
@Test
public void shouldInsertOnlyFirstDocumentAndNextAttemptsShouldFailWithOptimisticLockingException() throws Exception {
final String key = testName.getMethodName();
removeIfExist(key);
final AtomicLong counter = new AtomicLong();
final AtomicLong optimisticLockCounter = new AtomicLong();
AsyncUtils.executeConcurrently(5, new Callable<Void>() {
@Override
public Void call() throws Exception {
long counterValue = counter.incrementAndGet();
String data = "value-" + counterValue;
VersionedClass messageData = new VersionedClass(key, data);
try {
template.insert(messageData);
} catch (OptimisticLockingFailureException e) {
optimisticLockCounter.incrementAndGet();
}
//should save operation throw OptimisticLockingFailureException on next attempts to save?
return null;
}
});
assertThat(optimisticLockCounter.intValue()).isEqualTo(4);
}
/**
* @see DATACOUCH-59
*/
@Test
public void expiryWhenTouchOnReadDocument() throws InterruptedException {
String id = "simple-doc-with-update-expiry-for-read";
DocumentWithTouchOnRead doc = new DocumentWithTouchOnRead(id);
template.save(doc);
Thread.sleep(1000);
assertThat(template.findById(id, DocumentWithTouchOnRead.class)).isNotNull();
Thread.sleep(1000);
assertThat(template.findById(id, DocumentWithTouchOnRead.class)).isNotNull();
Thread.sleep(3000);
assertThat(template.findById(id, DocumentWithTouchOnRead.class)).isNull();
}
/**
* @see DATACOUCH-227
*/
@Test
public void shouldRetainOrderWhenQueryingViewOrdered() {
ViewQuery q = ViewQuery.from("test_beers", "by_name");
q.descending().includeDocsOrdered(true);
String prev = null;
List<Beer> beers = template.findByView(q, Beer.class);
assertThat(q.isIncludeDocs()).isTrue();
assertThat(q.isOrderRetained()).isTrue();
assertThat(q.includeDocsTarget()).isEqualTo(RawJsonDocument.class);
for (Beer beer : beers) {
if (prev != null) {
assertThat(beer.getName().compareTo(prev) < 0).describedAs(beer.getName() + " not alphabetically < to " + prev).isTrue();
}
prev = beer.getName();
}
}
/**
* A sample document with just an id and property.
*/
@Document
static class SimplePerson {
@Id
private final String id;
@Field
private final String name;
public SimplePerson(String id, String name) {
this.id = id;
this.name = name;
}
}
/**
* A sample document that expires in 2 seconds.
*/
@Document(expiry = 2)
static class DocumentWithExpiry {
@Id
private final String id;
public DocumentWithExpiry(String id) {
this.id = id;
}
}
/**
* A sample document that expires in 2 seconds and touchOnRead set.
*/
@Document(expiry = 2, touchOnRead = true)
static class DocumentWithTouchOnRead {
@Id
private final String id;
public DocumentWithTouchOnRead(String id) {
this.id = id;
}
}
@Document
static class ComplexPerson {
@Id
private final String id;
@Field
private final List<String> firstnames;
@Field
private final List<Integer> votes;
@Field
private final Map<String, Boolean> info1;
@Field
private final Map<String, Integer> info2;
public ComplexPerson(String id, List<String> firstnames,
List<Integer> votes, Map<String, Boolean> info1,
Map<String, Integer> info2) {
this.id = id;
this.firstnames = firstnames;
this.votes = votes;
this.info1 = info1;
this.info2 = info2;
}
List<String> getFirstnames() {
return firstnames;
}
List<Integer> getVotes() {
return votes;
}
Map<String, Boolean> getInfo1() {
return info1;
}
Map<String, Integer> getInfo2() {
return info2;
}
String getId() {
return id;
}
}
@Document
static class SimpleWithLongAndInt {
@Id
private String id;
private long longValue;
private int intValue;
SimpleWithLongAndInt(final String id, final long longValue, int intValue) {
this.id = id;
this.longValue = longValue;
this.intValue = intValue;
}
String getId() {
return id;
}
long getLongValue() {
return longValue;
}
void setLongValue(final long value) {
this.longValue = value;
}
public int getIntValue() {
return intValue;
}
public void setIntValue(int intValue) {
this.intValue = intValue;
}
}
static class SimpleWithEnum {
@Id
private String id;
private enum Type {
BIG
}
private Type type;
SimpleWithEnum(final String id, final Type type) {
this.id = id;
this.type = type;
}
String getId() {
return id;
}
void setId(final String id) {
this.id = id;
}
Type getType() {
return type;
}
void setType(final Type type) {
this.type = type;
}
}
static class SimpleWithClass {
@Id
private String id;
private Class<Integer> integerClass;
private String value;
SimpleWithClass(final String id, final Class<Integer> integerClass) {
this.id = id;
this.integerClass = integerClass;
}
String getId() {
return id;
}
void setId(final String id) {
this.id = id;
}
Class<Integer> getIntegerClass() {
return integerClass;
}
void setIntegerClass(final Class<Integer> integerClass) {
this.integerClass = integerClass;
}
String getValue() {
return value;
}
void setValue(final String value) {
this.value = value;
}
}
static class VersionedClass {
@Id
private String id;
@Version
private long version;
private String field;
VersionedClass(String id, String field) {
this.id = id;
this.field = field;
}
public String getId() {
return id;
}
public long getVersion() {
return version;
}
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
@Override
public String toString() {
return "VersionedClass{" +
"id='" + id + '\'' +
", version=" + version +
", field='" + field + '\'' +
'}';
}
}
@Document
static class FullFragment {
@Id
private String id;
private long criteria;
private String type;
private String value;
public FullFragment(String id, long criteria, String type, String value) {
this.id = id;
this.criteria = criteria;
this.type = type;
this.value = value;
}
public String getId() {
return id;
}
public long getCriteria() {
return criteria;
}
public String getType() {
return type;
}
public String getValue() {
return value;
}
public void setCriteria(long criteria) {
this.criteria = criteria;
}
public void setType(String type) {
this.type = type;
}
public void setValue(String value) {
this.value = value;
}
}
static class Fragment {
public String value;
}
}

View File

@@ -1,64 +0,0 @@
package org.springframework.data.couchbase.core;
import com.couchbase.client.java.repository.annotation.Id;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.IntegrationTestCustomKeySettings;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.couchbase.core.mapping.KeySettings;
import org.springframework.test.context.ContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
/**
* @author Subhashni Balakrishnan
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = IntegrationTestCustomKeySettings.class)
public class CouchbaseTemplateKeySettingsIntegrationTests {
@Rule
public TestName testName = new TestName();
@Autowired
private CouchbaseTemplate template;
@Before
public void setup() {
if (template.keySettings() == null) {
template.keySettings(KeySettings.build().prefix("MyAppPrefix").suffix("MyAppSuffix").delimiter("::"));
}
}
@Test
public void shouldAddCustomKeySettings() throws Exception {
SimpleClass simpleClass = new SimpleClass();
String generatedId = template.getGeneratedId(simpleClass);
assertThat(generatedId).as("Id generated should include custom key settings")
.isEqualTo("MyAppPrefix::myId::MyAppSuffix");
}
@Test
public void shouldNotAllowKeySettingsToBeChanged() {
try {
template.keySettings(KeySettings.build().prefix("MyAppPrefix").suffix("MyAppSuffix").delimiter("::"));
fail("excepted unsupportedOperationException");
} catch(Exception ex) {
}
}
@Document
static class SimpleClass {
@Id
public String id = "myId";
}
}

View File

@@ -0,0 +1,125 @@
/*
* 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.core;
import static org.junit.jupiter.api.Assertions.*;
import java.io.IOException;
import java.util.UUID;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.data.couchbase.SimpleCouchbaseClientFactory;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.domain.User;
import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests;
import org.springframework.data.couchbase.util.ClusterType;
import org.springframework.data.couchbase.util.IgnoreWhen;
class CouchbaseTemplateKeyValueIntegrationTests extends ClusterAwareIntegrationTests {
private static CouchbaseClientFactory couchbaseClientFactory;
private CouchbaseTemplate couchbaseTemplate;
@BeforeAll
static void beforeAll() {
couchbaseClientFactory = new SimpleCouchbaseClientFactory(connectionString(), authenticator(), bucketName());
}
@AfterAll
static void afterAll() throws IOException {
couchbaseClientFactory.close();
}
@BeforeEach
void beforeEach() {
CouchbaseConverter couchbaseConverter = new MappingCouchbaseConverter();
couchbaseTemplate = new CouchbaseTemplate(couchbaseClientFactory, couchbaseConverter);
}
@Test
void upsertAndFindById() {
User user = new User(UUID.randomUUID().toString(), "firstname", "lastname");
User modified = couchbaseTemplate.upsertById(User.class).one(user);
assertEquals(user, modified);
User found = couchbaseTemplate.findById(User.class).one(user.getId());
assertEquals(user, found);
}
@Test
void findDocWhichDoesNotExist() {
assertThrows(DataRetrievalFailureException.class,
() -> couchbaseTemplate.findById(User.class).one(UUID.randomUUID().toString()));
}
@Test
void upsertAndReplaceById() {
User user = new User(UUID.randomUUID().toString(), "firstname", "lastname");
User modified = couchbaseTemplate.upsertById(User.class).one(user);
assertEquals(user, modified);
User toReplace = new User(modified.getId(), "some other", "lastname");
couchbaseTemplate.replaceById(User.class).one(toReplace);
User loaded = couchbaseTemplate.findById(User.class).one(toReplace.getId());
assertEquals("some other", loaded.getFirstname());
}
@Test
void upsertAndRemoveById() {
User user = new User(UUID.randomUUID().toString(), "firstname", "lastname");
User modified = couchbaseTemplate.upsertById(User.class).one(user);
assertEquals(user, modified);
RemoveResult removeResult = couchbaseTemplate.removeById().one(user.getId());
assertEquals(user.getId(), removeResult.getId());
assertTrue(removeResult.getCas() != 0);
assertTrue(removeResult.getMutationToken().isPresent());
assertThrows(DataRetrievalFailureException.class, () -> couchbaseTemplate.findById(User.class).one(user.getId()));
}
@Test
void insertById() {
User user = new User(UUID.randomUUID().toString(), "firstname", "lastname");
User inserted = couchbaseTemplate.insertById(User.class).one(user);
assertEquals(user, inserted);
assertThrows(DuplicateKeyException.class, () -> couchbaseTemplate.insertById(User.class).one(user));
}
@Test
@IgnoreWhen(clusterTypes = ClusterType.MOCKED)
void existsById() {
String id = UUID.randomUUID().toString();
assertFalse(couchbaseTemplate.existsById().one(id));
User user = new User(id, "firstname", "lastname");
User inserted = couchbaseTemplate.insertById(User.class).one(user);
assertEquals(user, inserted);
assertTrue(couchbaseTemplate.existsById().one(id));
}
}

View File

@@ -0,0 +1,104 @@
/*
* 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.core;
import static org.junit.jupiter.api.Assertions.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.data.couchbase.SimpleCouchbaseClientFactory;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.domain.User;
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 com.couchbase.client.core.error.IndexExistsException;
import com.couchbase.client.java.query.QueryScanConsistency;
@IgnoreWhen(missesCapabilities = Capabilities.QUERY, clusterTypes = ClusterType.MOCKED)
class CouchbaseTemplateQueryIntegrationTests extends ClusterAwareIntegrationTests {
private static CouchbaseClientFactory couchbaseClientFactory;
private CouchbaseTemplate couchbaseTemplate;
@BeforeAll
static void beforeAll() {
couchbaseClientFactory = new SimpleCouchbaseClientFactory(connectionString(), authenticator(), bucketName());
try {
couchbaseClientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName());
} catch (IndexExistsException ex) {
// ignore, all good.
}
}
@AfterAll
static void afterAll() throws IOException {
couchbaseClientFactory.close();
}
@BeforeEach
void beforeEach() {
CouchbaseConverter couchbaseConverter = new MappingCouchbaseConverter();
couchbaseTemplate = new CouchbaseTemplate(couchbaseClientFactory, couchbaseConverter);
}
@Test
void findByQuery() {
User user1 = new User(UUID.randomUUID().toString(), "user1", "user1");
User user2 = new User(UUID.randomUUID().toString(), "user2", "user2");
couchbaseTemplate.upsertById(User.class).all(Arrays.asList(user1, user2));
final List<User> foundUsers = couchbaseTemplate.findByQuery(User.class)
.consistentWith(QueryScanConsistency.REQUEST_PLUS).all();
assertEquals(2, foundUsers.size());
for (User u : foundUsers) {
assertTrue(u.equals(user1) || u.equals(user2));
}
}
@Test
void removeByQuery() {
User user1 = new User(UUID.randomUUID().toString(), "user1", "user1");
User user2 = new User(UUID.randomUUID().toString(), "user2", "user2");
couchbaseTemplate.upsertById(User.class).all(Arrays.asList(user1, user2));
assertTrue(couchbaseTemplate.existsById().one(user1.getId()));
assertTrue(couchbaseTemplate.existsById().one(user2.getId()));
couchbaseTemplate.removeByQuery(User.class).consistentWith(QueryScanConsistency.REQUEST_PLUS).all();
assertThrows(DataRetrievalFailureException.class, () -> couchbaseTemplate.findById(User.class).one(user1.getId()));
assertThrows(DataRetrievalFailureException.class, () -> couchbaseTemplate.findById(User.class).one(user2.getId()));
}
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright 2013-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.core;
import java.util.Collections;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.cluster.ClusterInfo;
import com.couchbase.client.java.query.Index;
import com.couchbase.client.java.query.N1qlQuery;
import com.couchbase.client.java.view.DefaultView;
import com.couchbase.client.java.view.DesignDocument;
import com.couchbase.client.java.view.View;
import org.springframework.data.couchbase.config.BeanNames;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
/**
* @author Michael Nitschinger
*/
public class CouchbaseTemplateQueryListener extends DependencyInjectionTestExecutionListener {
@Override
public void beforeTestClass(final TestContext testContext) throws Exception {
Bucket client = (Bucket) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_BUCKET);
ClusterInfo clusterInfo = (ClusterInfo) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_CLUSTER_INFO);
populateTestData(client, clusterInfo);
createAndWaitForDesignDocs(client);
client.query(N1qlQuery.simple(Index.createPrimaryIndex().on(client.name())));
}
private void populateTestData(Bucket client, ClusterInfo clusterInfo) {
CouchbaseTemplate template = new CouchbaseTemplate(clusterInfo, client);
for (int i = 0; i < 100; i++) {
Beer b = new Beer("testbeer-" + i, "MyBeer" + i, true, "");
template.save(b);
}
}
private void createAndWaitForDesignDocs(Bucket client) {
String mapFunction = "function (doc, meta) { if(doc._class == "
+ "\"org.springframework.data.couchbase.core.Beer\") { emit(doc.name, null); } }";
View view = DefaultView.create("by_name", mapFunction);
DesignDocument designDoc = DesignDocument.create("test_beers", Collections.singletonList(view));
client.bucketManager().upsertDesignDocument(designDoc);
}
@Override
public void afterTestClass(final TestContext testContext) throws Exception {
Bucket client = (Bucket) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_BUCKET);
ClusterInfo clusterInfo = (ClusterInfo) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_CLUSTER_INFO);
CouchbaseTemplate template = new CouchbaseTemplate(clusterInfo, client);
for (int i = 0; i < 100; i++) {
Beer b = new Beer("testbeer-" + i, "MyBeer" + i, true, "");
template.remove(b);
}
}
}

View File

@@ -0,0 +1,88 @@
/*
* 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.core;
import static org.junit.jupiter.api.Assertions.*;
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.CouchbaseClientFactory;
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
import org.springframework.data.couchbase.core.convert.DefaultCouchbaseTypeMapper;
import org.springframework.data.couchbase.domain.User;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import com.couchbase.client.java.kv.GetResult;
@SpringJUnitConfig(CustomTypeKeyIntegrationTests.Config.class)
public class CustomTypeKeyIntegrationTests extends ClusterAwareIntegrationTests {
private static final String CUSTOM_TYPE_KEY = "javaClass";
@Autowired private CouchbaseOperations operations;
@Autowired private CouchbaseClientFactory clientFactory;
@Test
void saveSimpleEntityCorrectlyWithDifferentTypeKey() {
User user = new User(UUID.randomUUID().toString(), "firstname", "lastname");
User modified = operations.upsertById(User.class).one(user);
assertEquals(user, modified);
GetResult getResult = clientFactory.getCollection(null).get(user.getId());
assertEquals("org.springframework.data.couchbase.domain.User",
getResult.contentAsObject().getString(CUSTOM_TYPE_KEY));
assertFalse(getResult.contentAsObject().containsKey(DefaultCouchbaseTypeMapper.DEFAULT_TYPE_KEY));
}
@Configuration
@EnableCouchbaseRepositories("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();
}
@Override
public String typeKey() {
return CUSTOM_TYPE_KEY;
}
}
}

View File

@@ -1,88 +0,0 @@
/*
* Copyright 2017-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.core;
import lombok.EqualsAndHashCode;
import org.springframework.data.annotation.Id;
import com.couchbase.client.java.repository.annotation.Field;
/**
* Test class for persisting and loading from {@link RxJavaCouchbaseTemplate}.
*
* @author Subhashni Balakrishnan
* @author Alex Derkach
*/
@EqualsAndHashCode
public class ReactiveBeer {
@Id
private final String id;
private String name;
@Field("is_active")
private boolean active = true;
@Field("desc")
private String description;
public ReactiveBeer(String id, String name, Boolean active, String description) {
this.id = id;
this.name = name;
this.active = active;
this.description = description;
}
@Override
public String toString() {
return "Beer [id=" + id + ", name=" + name + ", active=" + active + ", description=" + description + "]";
}
public ReactiveBeer setName(String name) {
this.name = name;
return this;
}
public String getName() {
return name;
}
public ReactiveBeer setActive(boolean active) {
this.active = active;
return this;
}
public boolean getActive() {
return active;
}
public ReactiveBeer setDescription(String description) {
this.description = description;
return this;
}
public String getDescription() {
return description;
}
public String getId() {
return id;
}
}

View File

@@ -1,75 +0,0 @@
/*
* Copyright 2017-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.core;
import java.util.Collections;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.cluster.ClusterInfo;
import com.couchbase.client.java.error.DocumentDoesNotExistException;
import com.couchbase.client.java.query.Index;
import com.couchbase.client.java.query.N1qlQuery;
import com.couchbase.client.java.view.*;
import org.springframework.data.couchbase.config.BeanNames;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import rx.Observable;
/**
* @author Subhashni Balakrishnan
*/
public class RxCouchbaseTemplateQueryListener extends DependencyInjectionTestExecutionListener {
@Override
public void beforeTestClass(final TestContext testContext) throws Exception {
Bucket client = (Bucket) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_BUCKET);
ClusterInfo clusterInfo = (ClusterInfo) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_CLUSTER_INFO);
populateTestData(client, clusterInfo);
createAndWaitForDesignDocs(client);
client.query(N1qlQuery.simple(Index.createPrimaryIndex().on(client.name())));
}
private void populateTestData(Bucket client, ClusterInfo clusterInfo) {
RxJavaCouchbaseTemplate template = new RxJavaCouchbaseTemplate(clusterInfo, client);
for (int i = 0; i < 100; i++) {
ReactiveBeer b = new ReactiveBeer("testbeer-" + i, "MyBeer" + i, true, "");
template.save(b).subscribe();
}
}
private void createAndWaitForDesignDocs(Bucket client) {
String mapFunction = "function (doc, meta) { if(doc._class == "
+ "\"org.springframework.data.couchbase.core.ReactiveBeer\") { emit(doc.name, null); } }";
View view = DefaultView.create("by_name", mapFunction);
DesignDocument designDoc = DesignDocument.create("reactive_test_beers", Collections.singletonList(view));
client.bucketManager().upsertDesignDocument(designDoc);
}
@Override
public void afterTestClass(final TestContext testContext) throws Exception {
Bucket client = (Bucket) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_BUCKET);
ClusterInfo clusterInfo = (ClusterInfo) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_CLUSTER_INFO);
RxJavaCouchbaseTemplate template = new RxJavaCouchbaseTemplate(clusterInfo, client);
for (int i = 0; i < 100; i++) {
ReactiveBeer b = new ReactiveBeer("testbeer-" + i, "MyBeer" + i, true, "");
template.remove(b).subscribe();
}
}
}

View File

@@ -1,735 +0,0 @@
/*
* Copyright 2017-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.core;
import static com.couchbase.client.java.query.Select.select;
import static com.couchbase.client.java.query.dsl.Expression.*;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.util.*;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.PersistTo;
import com.couchbase.client.java.ReplicateTo;
import com.couchbase.client.java.document.RawJsonDocument;
import com.couchbase.client.java.query.AsyncN1qlQueryResult;
import com.couchbase.client.java.query.N1qlParams;
import com.couchbase.client.java.query.N1qlQuery;
import com.couchbase.client.java.query.consistency.ScanConsistency;
import com.couchbase.client.java.repository.annotation.Field;
import com.couchbase.client.java.view.Stale;
import com.couchbase.client.java.view.ViewQuery;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Version;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.ReactiveIntegrationTestApplicationConfig;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import rx.observers.TestSubscriber;
/**
* @author Subhashni Balakrishnan
* @author Alex Derkach
**/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = ReactiveIntegrationTestApplicationConfig.class)
@TestExecutionListeners(RxCouchbaseTemplateQueryListener.class)
public class RxJavaCouchbaseTemplateIntegrationTests {
@Rule
public TestName testName = new TestName();
@Autowired
private Bucket client;
@Autowired
private RxJavaCouchbaseOperations template;
private static final ObjectMapper MAPPER = new ObjectMapper();
private static final String DEFAULT_ID = "reactivebeers:awesome-stout";
private static final String DEFAULT_NAME = "The Awesome Stout";
private static final boolean DEFAULT_ACTIVE = false;
private static final String DEFAULT_DESCRIPTION = "";
@Before
public void setUp() throws Exception {
removeIfExist(DEFAULT_ID);
}
private void removeIfExist(String key) {
TestSubscriber<Object> subscriber = TestSubscriber.create();
template.remove(key)
.subscribe(subscriber);
subscriber.awaitTerminalEvent();
}
private void removeCollectionIfExist(Collection<ReactiveBeer> beers) {
TestSubscriber<Object> subscriber = TestSubscriber.create();
template.remove(beers, PersistTo.MASTER, ReplicateTo.NONE)
.subscribe(subscriber);
subscriber.awaitTerminalEvent();
}
@Test
public void upsertNonVersionedEntityCorrectlyWhenSaveIsCalled() throws Exception {
String newName = DEFAULT_NAME + "Second";
ReactiveBeer firstBeer = new ReactiveBeer(DEFAULT_ID, DEFAULT_NAME, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION);
ReactiveBeer secondBeer = new ReactiveBeer(DEFAULT_ID, newName, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION);
TestSubscriber<ReactiveBeer> firstSaveSubscriber = TestSubscriber.create();
TestSubscriber<ReactiveBeer> secondSaveSubscriber = TestSubscriber.create();
template.save(firstBeer).subscribe(firstSaveSubscriber);
template.save(secondBeer).subscribe(secondSaveSubscriber);
AsyncUtils.awaitCompletedWithAnyValue(firstSaveSubscriber);
AsyncUtils.awaitCompletedWithAnyValue(secondSaveSubscriber);
validateBeer(DEFAULT_ID, newName, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION, ReactiveBeer.class);
}
@Test
public void replaceVersionedEntityCorrectlyWhenSaveIsCalledAndCasIsNotZero() throws Exception {
String newName = DEFAULT_NAME + "Second";
VersionedReactiveBeer firstBeer = new VersionedReactiveBeer(DEFAULT_ID, DEFAULT_NAME, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION);
VersionedReactiveBeer secondBeer = new VersionedReactiveBeer(DEFAULT_ID, newName, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION);
long version = template.save(firstBeer).toBlocking().single().getVersion();
assertThat(version > 0).isTrue();
secondBeer.setVersion(version);
long newVersion = template.save(secondBeer).toBlocking().single().getVersion();
assertThat(newVersion > 0).isTrue();
assertThat(newVersion).isNotEqualTo(version);
validateBeer(DEFAULT_ID, newName, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION, VersionedReactiveBeer.class);
}
@Test
public void throwExceptionWhenSaveIsCalledAndCasIsMissmatched() throws Exception {
String newName = DEFAULT_NAME + "Second";
VersionedReactiveBeer firstBeer = new VersionedReactiveBeer(DEFAULT_ID, DEFAULT_NAME, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION);
VersionedReactiveBeer secondBeer = new VersionedReactiveBeer(DEFAULT_ID, newName, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION);
TestSubscriber<VersionedReactiveBeer> secondSaveSubscriber = TestSubscriber.create();
long version = template.save(firstBeer).toBlocking().single().getVersion();
assertThat(version > 0).isTrue();
secondBeer.setVersion(version + 1234);
template.save(secondBeer).subscribe(secondSaveSubscriber);
AsyncUtils.awaitError(secondSaveSubscriber, OptimisticLockingFailureException.class);
validateBeer(DEFAULT_ID, DEFAULT_NAME, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION, VersionedReactiveBeer.class);
}
@Test
public void throwExceptionWhenSaveIsCalledAndCasIsZeroAndEntityAlreadyExists() throws Exception {
String newName = DEFAULT_NAME + "Second";
VersionedReactiveBeer firstBeer = new VersionedReactiveBeer(DEFAULT_ID, DEFAULT_NAME, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION);
VersionedReactiveBeer secondBeer = new VersionedReactiveBeer(DEFAULT_ID, newName, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION);
TestSubscriber<VersionedReactiveBeer> secondSaveSubscriber = TestSubscriber.create();
long version = template.save(firstBeer).toBlocking().single().getVersion();
assertThat(version > 0).isTrue();
template.save(secondBeer).subscribe(secondSaveSubscriber);
AsyncUtils.awaitError(secondSaveSubscriber, OptimisticLockingFailureException.class);
validateBeer(DEFAULT_ID, DEFAULT_NAME, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION, VersionedReactiveBeer.class);
}
@Test
public void saveCollectionCorrectly() throws Exception {
Collection<ReactiveBeer> beers = new ArrayList<>();
TestSubscriber<ReactiveBeer> testSubscriber = TestSubscriber.create();
String name = DEFAULT_NAME;
int collectionSize = 10000;
for (int i = 0; i < collectionSize; i++) {
beers.add(new ReactiveBeer("beerCollItem" + i, name + i, false, ""));
}
removeCollectionIfExist(beers);
template.save(beers).subscribe(testSubscriber);
AsyncUtils.awaitCompletedWithValueCount(testSubscriber, collectionSize);
}
@Test
public void insertSimpleEntityCorrectly() throws Exception {
TestSubscriber<ReactiveBeer> testSubscriber = TestSubscriber.create();
ReactiveBeer beer = new ReactiveBeer(DEFAULT_ID, DEFAULT_NAME, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION);
template.insert(beer).subscribe(testSubscriber);
AsyncUtils.awaitCompletedWithAnyValue(testSubscriber);
validateBeer(DEFAULT_ID, DEFAULT_NAME, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION, ReactiveBeer.class);
}
@Test
public void expectErrorWhenDocumentExistsAndInsertIsCalled() throws Exception {
TestSubscriber<ReactiveBeer> firstInsertSubscriber = TestSubscriber.create();
TestSubscriber<ReactiveBeer> secondInsertSubscriber = TestSubscriber.create();
ReactiveBeer beer = new ReactiveBeer(DEFAULT_ID, DEFAULT_NAME, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION);
template.insert(beer).subscribe(firstInsertSubscriber);
AsyncUtils.awaitCompletedWithAnyValue(firstInsertSubscriber);
template.insert(beer).subscribe(secondInsertSubscriber);
AsyncUtils.awaitError(secondInsertSubscriber, OptimisticLockingFailureException.class);
}
@Test
public void insertCollectionCorrectly() throws Exception {
TestSubscriber<ReactiveBeer> testSubscriber = TestSubscriber.create();
Collection<ReactiveBeer> beers = new ArrayList<>();
String name = DEFAULT_NAME;
int collectionSize = 10000;
for (int i = 0; i < collectionSize; i++) {
beers.add(new ReactiveBeer("beerCollItem" + i, name + i, false, ""));
}
removeCollectionIfExist(beers);
template.insert(beers).subscribe(testSubscriber);
AsyncUtils.awaitCompletedWithValueCount(testSubscriber, collectionSize);
}
@Test
public void replaceSimpleEntityCorrectly() throws Exception {
TestSubscriber<VersionedReactiveBeer> firstInsertSubscriber = TestSubscriber.create();
TestSubscriber<VersionedReactiveBeer> replaceTestSubscriber = TestSubscriber.create();
String newName = DEFAULT_NAME + " New";
VersionedReactiveBeer beer = new VersionedReactiveBeer(DEFAULT_ID, DEFAULT_NAME, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION);
template.insert(beer).subscribe(firstInsertSubscriber);
AsyncUtils.awaitCompletedWithAnyValue(firstInsertSubscriber);
template.findById(DEFAULT_ID, VersionedReactiveBeer.class)
.doOnNext(v -> v.setName(newName))
.flatMap(v -> template.update(v))
.subscribe(replaceTestSubscriber);
AsyncUtils.awaitCompletedWithAnyValue(replaceTestSubscriber);
validateBeer(DEFAULT_ID, newName, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION, VersionedReactiveBeer.class);
}
@Test
public void expectErrorWhenReplacingSimpleEntityWhichDoesNotExist() throws Exception {
TestSubscriber<VersionedReactiveBeer> testSubscriber = TestSubscriber.create();
VersionedReactiveBeer beer = new VersionedReactiveBeer(DEFAULT_ID, DEFAULT_NAME, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION);
template.update(beer).subscribe(testSubscriber);
AsyncUtils.awaitError(testSubscriber, DataRetrievalFailureException.class);
}
@Test
public void removeDocument() {
TestSubscriber<ReactiveBeer> firstInsertSubscriber = TestSubscriber.create();
TestSubscriber<ReactiveBeer> firstFindSubscriber = TestSubscriber.create();
TestSubscriber<ReactiveBeer> removalTestSubscriber = TestSubscriber.create();
TestSubscriber<ReactiveBeer> secondFindSubscriber = TestSubscriber.create();
ReactiveBeer beer = new ReactiveBeer(DEFAULT_ID, DEFAULT_NAME, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION);
template.save(beer).subscribe(firstInsertSubscriber);
AsyncUtils.awaitCompletedWithAnyValue(firstInsertSubscriber);
template.findById(DEFAULT_ID, ReactiveBeer.class).subscribe(firstFindSubscriber);
AsyncUtils.awaitCompletedWithValueCount(firstFindSubscriber, 1);
template.remove(beer).subscribe(removalTestSubscriber);
AsyncUtils.awaitCompletedWithValueCount(removalTestSubscriber, 1);
template.findById(DEFAULT_ID, ReactiveBeer.class).subscribe(secondFindSubscriber);
AsyncUtils.awaitValue(secondFindSubscriber, null);
}
@Test
public void storeListsAndMaps() {
String id = "persons:lots-of-names";
List<String> names = new ArrayList<String>();
names.add("Michael");
names.add("Thomas");
names.add(null);
List<Integer> votes = new LinkedList<Integer>();
Map<String, Boolean> info1 = new HashMap<String, Boolean>();
info1.put("foo", true);
info1.put("bar", false);
info1.put("nullValue", null);
Map<String, Integer> info2 = new HashMap<String, Integer>();
ComplexPerson complex = new ComplexPerson(id, names, votes, info1, info2);
template.save(complex).subscribe();
assertThat(client.get(id)).isNotNull();
ComplexPerson response = template.findById(id, ComplexPerson.class).toBlocking().single();
assertThat(response.getFirstnames()).isEqualTo(names);
assertThat(response.getVotes()).isEqualTo(votes);
assertThat(response.getId()).isEqualTo(id);
assertThat(response.getInfo1()).isEqualTo(info1);
assertThat(response.getInfo2()).isEqualTo(info2);
}
@Test
public void validFindById() {
TestSubscriber<ReactiveBeer> saveSubscriber = TestSubscriber.create();
TestSubscriber<ReactiveBeer> findSubscriber = TestSubscriber.create();
ReactiveBeer beer = new ReactiveBeer(DEFAULT_ID, DEFAULT_NAME, DEFAULT_ACTIVE, DEFAULT_DESCRIPTION);
template.save(beer).subscribe(saveSubscriber);
AsyncUtils.awaitCompletedWithAnyValue(saveSubscriber);
template.findById(DEFAULT_ID, ReactiveBeer.class).subscribe(findSubscriber);
AsyncUtils.awaitValue(findSubscriber, beer);
}
@Test
public void shouldLoadAndMapViewDocs() {
ViewQuery query = ViewQuery.from("reactive_test_beers", "by_name");
query.stale(Stale.FALSE);
final List<ReactiveBeer> beers = template.findByView(query, ReactiveBeer.class).toList().toBlocking().single();
assertThat(beers.size() > 0).isTrue();
for (ReactiveBeer beer : beers) {
assertThat(beer.getId()).isNotNull();
assertThat(beer.getName()).isNotNull();
assertThat(beer.getActive()).isNotNull();
}
}
@Test
public void shouldQueryRaw() {
N1qlQuery query = N1qlQuery.simple(select("name").from(i(client.name())).limit(1));
AsyncN1qlQueryResult queryResult = template.queryN1QL(query).toBlocking().single();
assertThat(queryResult.finalSuccess().toBlocking().single()).isTrue();
assertThat(queryResult.rows().toList().toBlocking().single().isEmpty()).isFalse();
}
@Test
public void shouldQueryWithMapping() {
FullFragment ff1 = new FullFragment("fullFragment1", 1, "fullFragment", "test1");
FullFragment ff2 = new FullFragment("fullFragment2", 2, "fullFragment", "test2");
template.save(Arrays.asList(ff1, ff2)).subscribe();
N1qlQuery query = N1qlQuery.simple(select(i("value")) //"value" is a n1ql keyword apparently
.from(i(client.name()))
.where(x("type").eq(s("fullFragment"))
.and(x("criteria").gt(1))),
N1qlParams.build().consistency(ScanConsistency.REQUEST_PLUS));
List<Fragment> fragments = template.findByN1QLProjection(query, Fragment.class).toList().toBlocking().single();
assertThat(fragments).isNotNull();
assertThat(fragments.isEmpty()).isFalse();
assertThat(fragments.size()).isEqualTo(1);
assertThat(fragments.get(0).value).isEqualTo("test2");
}
@Test
public void shouldDeserialiseLongsAndInts() {
final long longValue = new Date().getTime();
final int intValue = new Random().nextInt();
template.save(new SimpleWithLongAndInt("simpleWithLong:simple", longValue, intValue)).toBlocking().single();
SimpleWithLongAndInt document = template.findById("simpleWithLong:simple", SimpleWithLongAndInt.class).toBlocking().single();
assertThat(document).isNotNull();
assertThat(document.getLongValue()).isEqualTo(longValue);
assertThat(document.getIntValue()).isEqualTo(intValue);
template.save(new SimpleWithLongAndInt("simpleWithLong:simple:other", intValue, intValue)).toBlocking().single();
document = template.findById("simpleWithLong:simple:other", SimpleWithLongAndInt.class).toBlocking().single();
assertThat(document).isNotNull();
assertThat(document.getLongValue()).isEqualTo(intValue);
assertThat(document.getIntValue()).isEqualTo(intValue);
}
@Test
public void shouldDeserialiseEnums() {
SimpleWithEnum simpleWithEnum = new SimpleWithEnum("simpleWithEnum:enum", SimpleWithEnum.Type.BIG);
template.save(simpleWithEnum).toBlocking().single();
simpleWithEnum = template.findById("simpleWithEnum:enum", SimpleWithEnum.class).toBlocking().single();
assertThat(simpleWithEnum).isNotNull();
assertThat(SimpleWithEnum.Type.BIG).isEqualTo(simpleWithEnum.getType());
}
@Test
public void shouldDeserialiseClass() {
SimpleWithClass simpleWithClass = new SimpleWithClass("simpleWithClass:class", Integer.class);
simpleWithClass.setValue("The dish ran away with the spoon.");
template.save(simpleWithClass).toBlocking().single();
simpleWithClass = template.findById("simpleWithClass:class", SimpleWithClass.class).toBlocking().single();
assertThat(simpleWithClass).isNotNull();
assertThat(simpleWithClass.getValue())
.isEqualTo("The dish ran away with the spoon.");
}
@Test
public void expiryWhenTouchOnReadDocument() throws InterruptedException {
String id = "simple-doc-with-update-expiry-for-read";
DocumentWithTouchOnRead doc = new DocumentWithTouchOnRead(id);
template.save(doc).subscribe();
Thread.sleep(1000);
assertThat(template.findById(id, DocumentWithTouchOnRead.class).toBlocking()
.single()).isNotNull();
Thread.sleep(1000);
assertThat(template.findById(id, DocumentWithTouchOnRead.class).toBlocking()
.single()).isNotNull();
Thread.sleep(3000);
assertThat(template.findById(id, DocumentWithTouchOnRead.class).toBlocking()
.single()).isNull();
}
@Test
public void shouldRetainOrderWhenQueryingViewOrdered() {
ViewQuery q = ViewQuery.from("reactive_test_beers", "by_name");
q.descending().includeDocsOrdered(true);
String prev = null;
List<ReactiveBeer> beers = template.findByView(q, ReactiveBeer.class).toList().toBlocking().single();
assertThat(q.isIncludeDocs()).isTrue();
assertThat(q.isOrderRetained()).isTrue();
assertThat(q.includeDocsTarget()).isEqualTo(RawJsonDocument.class);
for (ReactiveBeer beer : beers) {
if (prev != null) {
assertThat(beer.getName().compareTo(prev) < 0).describedAs(beer.getName() + " not alphabetically < to " + prev).isTrue();
}
prev = beer.getName();
}
}
private void validateBeer(String id, String name, boolean active, String description, Class<?> clazz) throws IOException {
RawJsonDocument resultDoc = client.get(id, RawJsonDocument.class);
assertThat(resultDoc).isNotNull();
String result = resultDoc.content();
assertThat(result).isNotNull();
Map<String, Object> resultConv = MAPPER.readValue(result, new TypeReference<Map<String, Object>>() {});
assertThat(resultConv.get(MappingCouchbaseConverter.TYPEKEY_DEFAULT)).isNotNull();
assertThat(resultConv.get("javaClass")).isNull();
assertThat(resultConv.get(MappingCouchbaseConverter.TYPEKEY_DEFAULT))
.isEqualTo(clazz.getCanonicalName());
assertThat(resultConv.get("is_active")).isEqualTo(active);
assertThat(resultConv.get("name")).isEqualTo(name);
assertThat(resultConv.get("desc")).isEqualTo(description);
}
/**
* A sample document with just an id and property.
*/
@Document
static class SimplePerson {
@Id
private final String id;
@Field
private final String name;
public SimplePerson(String id, String name) {
this.id = id;
this.name = name;
}
}
/**
* A sample document that expires in 2 seconds.
*/
@Document(expiry = 2)
static class DocumentWithExpiry {
@Id
private final String id;
public DocumentWithExpiry(String id) {
this.id = id;
}
}
/**
* A sample document that expires in 2 seconds and touchOnRead set.
*/
@Document(expiry = 2, touchOnRead = true)
static class DocumentWithTouchOnRead {
@Id
private final String id;
public DocumentWithTouchOnRead(String id) {
this.id = id;
}
}
@Document
static class ComplexPerson {
@Id
private final String id;
@Field
private final List<String> firstnames;
@Field
private final List<Integer> votes;
@Field
private final Map<String, Boolean> info1;
@Field
private final Map<String, Integer> info2;
public ComplexPerson(String id, List<String> firstnames,
List<Integer> votes, Map<String, Boolean> info1,
Map<String, Integer> info2) {
this.id = id;
this.firstnames = firstnames;
this.votes = votes;
this.info1 = info1;
this.info2 = info2;
}
List<String> getFirstnames() {
return firstnames;
}
List<Integer> getVotes() {
return votes;
}
Map<String, Boolean> getInfo1() {
return info1;
}
Map<String, Integer> getInfo2() {
return info2;
}
String getId() {
return id;
}
}
@Document
static class SimpleWithLongAndInt {
@Id
private String id;
private long longValue;
private int intValue;
SimpleWithLongAndInt(final String id, final long longValue, int intValue) {
this.id = id;
this.longValue = longValue;
this.intValue = intValue;
}
String getId() {
return id;
}
long getLongValue() {
return longValue;
}
void setLongValue(final long value) {
this.longValue = value;
}
public int getIntValue() {
return intValue;
}
public void setIntValue(int intValue) {
this.intValue = intValue;
}
}
static class SimpleWithEnum {
@Id
private String id;
private enum Type {
BIG
}
Type type;
SimpleWithEnum(final String id, final Type type) {
this.id = id;
this.type = type;
}
String getId() {
return id;
}
void setId(final String id) {
this.id = id;
}
Type getType() {
return type;
}
void setType(final Type type) {
this.type = type;
}
}
static class SimpleWithClass {
@Id
private String id;
private Class<Integer> integerClass;
private String value;
SimpleWithClass(final String id, final Class<Integer> integerClass) {
this.id = id;
this.integerClass = integerClass;
}
String getId() {
return id;
}
void setId(final String id) {
this.id = id;
}
Class<Integer> getIntegerClass() {
return integerClass;
}
void setIntegerClass(final Class<Integer> integerClass) {
this.integerClass = integerClass;
}
String getValue() {
return value;
}
void setValue(final String value) {
this.value = value;
}
}
static class VersionedClass {
@Id
private String id;
@Version
private long version;
private String field;
VersionedClass(String id, String field) {
this.id = id;
this.field = field;
}
public String getId() {
return id;
}
public long getVersion() {
return version;
}
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
@Override
public String toString() {
return "VersionedClass{" +
"id='" + id + '\'' +
", version=" + version +
", field='" + field + '\'' +
'}';
}
}
@Document
static class FullFragment {
@Id
private String id;
private long criteria;
private String type;
private String value;
public FullFragment(String id, long criteria, String type, String value) {
this.id = id;
this.criteria = criteria;
this.type = type;
this.value = value;
}
public String getId() {
return id;
}
public long getCriteria() {
return criteria;
}
public String getType() {
return type;
}
public String getValue() {
return value;
}
public void setCriteria(long criteria) {
this.criteria = criteria;
}
public void setType(String type) {
this.type = type;
}
public void setValue(String value) {
this.value = value;
}
}
static class Fragment {
public String value;
}
}

View File

@@ -1,78 +0,0 @@
/*
* Copyright 2013-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.core;
import java.util.Map;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.document.RawJsonDocument;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.IntegrationTestCustomTypeKeyConfig;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.test.context.ContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests the Java Config template around type key modification (DATACOUCH-134)
*
* @author Simon Baslé
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = IntegrationTestCustomTypeKeyConfig.class)
public class TypeKeyIntegrationTests {
@Autowired
private Bucket client;
@Autowired
private CouchbaseTemplate template;
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* see DATACOUCH-134
*/
@Test
public void saveSimpleEntityCorrectlyWithDifferentTypeKey() throws Exception {
String id = "beers:awesome-stout";
String name = "The Awesome Stout";
boolean active = false;
Beer beer = new Beer(id, name, active, "");
template.save(beer);
RawJsonDocument resultDoc = client.get(id, RawJsonDocument.class);
assertThat(resultDoc).isNotNull();
String result = resultDoc.content();
assertThat(result).isNotNull();
Map<String, Object> resultConv = MAPPER.readValue(result, new TypeReference<Map<String, Object>>() {});
assertThat(resultConv.get(MappingCouchbaseConverter.TYPEKEY_DEFAULT)).isNull();
assertThat(resultConv.get("javaClass")).isNotNull();
assertThat(resultConv.get("javaClass"))
.isEqualTo("org.springframework.data.couchbase.core.Beer");
assertThat(resultConv.get("is_active")).isEqualTo(false);
assertThat(resultConv.get("name")).isEqualTo("The Awesome Stout");
}
}

View File

@@ -1,97 +0,0 @@
/*
* Copyright 2017-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.core;
import com.couchbase.client.java.repository.annotation.Field;
import lombok.EqualsAndHashCode;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Version;
/**
* Test class for persisting and loading from {@link RxJavaCouchbaseTemplate}.
*
* @author Alex Derkach
*/
@EqualsAndHashCode
public class VersionedReactiveBeer {
@Id
private final String id;
private String name;
@Field("is_active")
private boolean active = true;
@Field("desc")
private String description;
@Version
private long version;
public VersionedReactiveBeer(String id, String name, Boolean active, String description) {
this.id = id;
this.name = name;
this.active = active;
this.description = description;
}
@Override
public String toString() {
return "Beer [id=" + id + ", name=" + name + ", active=" + active + ", description=" + description + "]";
}
public VersionedReactiveBeer setName(String name) {
this.name = name;
return this;
}
public String getName() {
return name;
}
public VersionedReactiveBeer setActive(boolean active) {
this.active = active;
return this;
}
public boolean getActive() {
return active;
}
public VersionedReactiveBeer setDescription(String description) {
this.description = description;
return this;
}
public String getDescription() {
return description;
}
public String getId() {
return id;
}
public void setVersion(long version) {
this.version = version;
}
public long getVersion() {
return version;
}
}

View File

@@ -1,169 +0,0 @@
package org.springframework.data.couchbase.core.convert.join;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
import java.lang.annotation.Annotation;
import com.couchbase.client.java.CouchbaseBucket;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.query.FetchType;
import org.springframework.data.couchbase.core.query.HashSide;
import org.springframework.data.couchbase.core.query.N1qlJoin;
import org.springframework.data.util.TypeInformation;
import org.springframework.data.couchbase.core.convert.join.N1qlJoinResolver.N1qlJoinResolverParameters;
/**
* Unit tests for {@link N1qlJoinResolver}
*/
public class N1qlJoinResolverTest {
static CouchbaseTemplate template;
static TypeInformation<Entity> entity;
static TypeInformation<Entity> associatedEntity;
static String entityClassName;
@BeforeClass
public static void setup() {
template = mock(CouchbaseTemplate.class);
CouchbaseBucket bucket = mock(CouchbaseBucket.class);
when(bucket.name()).thenReturn("B");
when(template.getCouchbaseBucket()).thenReturn(bucket);
CouchbaseConverter converter = mock(CouchbaseConverter.class);
when(converter.getTypeKey()).thenReturn("_class");
when(template.getConverter()).thenReturn(converter);
entity = mock(TypeInformation.class);
doReturn(Entity.class).when(entity).getType();
associatedEntity = mock(TypeInformation.class);
doReturn(Entity.class).when(associatedEntity).getType();
entityClassName = Entity.class.getName();
}
private static N1qlJoin createAnnotation(String on, String where, String index, String rightIndex, HashSide hashSide, String[] keys) {
N1qlJoin joinDefinition = new N1qlJoin() {
@Override
public Class<? extends Annotation> annotationType() {
return N1qlJoin.class;
}
@Override
public String on() {
return on;
}
@Override
public FetchType fetchType() {
return FetchType.IMMEDIATE;
}
@Override
public String where() {
return where;
}
@Override
public String index() {
return index;
}
@Override
public String rightIndex() {
return rightIndex;
}
@Override
public HashSide hashside() {
return hashSide;
}
@Override
public String[] keys() {
return keys;
}
};
return joinDefinition;
}
static public class Entity {
}
@Test
public void shouldBuildQueryWithIndex() {
N1qlJoin joinDefinition = createAnnotation("A=B", "", "leftIndex", "", HashSide.NONE, new String[0]);
N1qlJoinResolverParameters parameters = new N1qlJoinResolverParameters(joinDefinition, "mydoc", entity, associatedEntity);
String statement = N1qlJoinResolver.buildQuery(template, parameters);
String expected = "SELECT META(rks).id AS _ID, META(rks).cas AS _CAS, (rks).* FROM `B` lks USE INDEX(leftIndex) JOIN B rks ON A=B" +
" AND lks._class = \"" + entityClassName + "\"" + " AND " +
"rks._class = \"" + entityClassName + "\" WHERE META(lks).id=\"mydoc\"";
assertThat(expected).isEqualTo(statement);
}
@Test
public void shouldBuildQueryWithRightIndex() {
N1qlJoin joinDefinition = createAnnotation("A=B", "", "", "rightIndex", HashSide.NONE, new String[0]);
N1qlJoinResolverParameters parameters = new N1qlJoinResolverParameters(joinDefinition, "mydoc", entity, associatedEntity);
String statement = N1qlJoinResolver.buildQuery(template, parameters);
String expected = "SELECT META(rks).id AS _ID, META(rks).cas AS _CAS, (rks).* FROM `B` lks JOIN B rks USE INDEX(rightIndex) ON A=B" +
" AND lks._class = \"" + entityClassName + "\"" + " AND " +
"rks._class = \"" + entityClassName + "\" WHERE META(lks).id=\"mydoc\"";
assertThat(expected).isEqualTo(statement);
}
@Test
public void shouldBuildQueryWithHashProbe() {
N1qlJoin joinDefinition = createAnnotation("A=B", "", "", "", HashSide.PROBE, new String[0]);
N1qlJoinResolverParameters parameters = new N1qlJoinResolverParameters(joinDefinition, "mydoc", entity, associatedEntity);
String statement = N1qlJoinResolver.buildQuery(template, parameters);
String expected = "SELECT META(rks).id AS _ID, META(rks).cas AS _CAS, (rks).* FROM `B` lks JOIN B rks USE HASH(probe) ON A=B" +
" AND lks._class = \"" + entityClassName + "\"" + " AND " +
"rks._class = \"" + entityClassName + "\" WHERE META(lks).id=\"mydoc\"";
assertThat(expected).isEqualTo(statement);
}
@Test
public void shouldBuildQueryWithHashBuild() {
N1qlJoin joinDefinition = createAnnotation("A=B", "", "", "", HashSide.BUILD, new String[0]);
N1qlJoinResolverParameters parameters = new N1qlJoinResolverParameters(joinDefinition, "mydoc", entity, associatedEntity);
String statement = N1qlJoinResolver.buildQuery(template, parameters);
String expected = "SELECT META(rks).id AS _ID, META(rks).cas AS _CAS, (rks).* FROM `B` lks JOIN B rks USE HASH(build) ON A=B" +
" AND lks._class = \"" + entityClassName + "\"" + " AND " +
"rks._class = \"" + entityClassName + "\" WHERE META(lks).id=\"mydoc\"";
assertThat(expected).isEqualTo(statement);
}
@Test
public void shouldBuildQueryWithKeys() {
N1qlJoin joinDefinition = createAnnotation("A=B", "", "", "", HashSide.NONE, new String[]{"x", "y"});
N1qlJoinResolverParameters parameters = new N1qlJoinResolverParameters(joinDefinition, "mydoc", entity, associatedEntity);
String statement = N1qlJoinResolver.buildQuery(template, parameters);
String expected = "SELECT META(rks).id AS _ID, META(rks).cas AS _CAS, (rks).* FROM `B` lks JOIN B rks USE KEYS [\"x\",\"y\"] ON A=B" +
" AND lks._class = \"" + entityClassName + "\"" + " AND " +
"rks._class = \"" + entityClassName + "\" WHERE META(lks).id=\"mydoc\"";
assertThat(expected).isEqualTo(statement);
}
@Test
public void shouldBuildQueryWithWhere() {
N1qlJoin joinDefinition = createAnnotation("A=B", "C=D", "", "", HashSide.NONE, new String[0]);
N1qlJoinResolverParameters parameters = new N1qlJoinResolverParameters(joinDefinition, "mydoc", entity, associatedEntity);
String statement = N1qlJoinResolver.buildQuery(template, parameters);
String expected = "SELECT META(rks).id AS _ID, META(rks).cas AS _CAS, (rks).* FROM `B` lks JOIN B rks ON A=B" +
" AND lks._class = \"" + entityClassName + "\"" + " AND " +
"rks._class = \"" + entityClassName + "\" WHERE META(lks).id=\"mydoc\" AND C=D";
assertThat(expected).isEqualTo(statement);
}
@Test
public void shouldBuildQueryWithMultipleHints() {
N1qlJoin joinDefinition = createAnnotation("A=B", "", "leftIndex", "rightIndex", HashSide.BUILD, new String[]{"x"});
N1qlJoinResolverParameters parameters = new N1qlJoinResolverParameters(joinDefinition, "mydoc", entity, associatedEntity);
String statement = N1qlJoinResolver.buildQuery(template, parameters);
String expected = "SELECT META(rks).id AS _ID, META(rks).cas AS _CAS, (rks).* FROM `B` lks USE INDEX(leftIndex) JOIN B rks USE INDEX(rightIndex)" +
" HASH(build) KEYS [\"x\"] ON A=B AND lks._class = \"" + entityClassName + "\"" + " AND " +
"rks._class = \"" + entityClassName + "\" WHERE META(lks).id=\"mydoc\"";
assertThat(expected).isEqualTo(statement);
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2013-2020 the original author or authors.
* 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
* 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,
@@ -16,11 +16,11 @@
package org.springframework.data.couchbase.core.convert.translation;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import static org.junit.jupiter.api.Assertions.*;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
/**
* Verifies the functionality of a {@link JacksonTranslationService}.
@@ -29,39 +29,40 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public class JacksonTranslationServiceTests {
private TranslationService service;
private static TranslationService service;
@Before
public void setup() {
service = new JacksonTranslationService();
((JacksonTranslationService) service).afterPropertiesSet();
}
@BeforeAll
static void beforeAll() {
service = new JacksonTranslationService();
((JacksonTranslationService) service).afterPropertiesSet();
}
@Test
public void shouldEncodeNonASCII() {
CouchbaseDocument doc = new CouchbaseDocument("key");
doc.put("language", "русский");
String expected = "{\"language\":\"русский\"}";
assertThat(service.encode(doc)).isEqualTo(expected);
}
@Test
void shouldEncodeNonASCII() {
CouchbaseDocument doc = new CouchbaseDocument("key");
doc.put("language", "русский");
String expected = "{\"language\":\"русский\"}";
assertEquals(expected, service.encode(doc));
}
@Test
public void shouldDecodeNonASCII() {
String source = "{\"language\":\"русский\"}";
CouchbaseDocument target = new CouchbaseDocument();
service.decode(source, target);
assertThat(target.get("language")).isEqualTo("русский");
}
@Test
void shouldDecodeNonASCII() {
String source = "{\"language\":\"русский\"}";
CouchbaseDocument target = new CouchbaseDocument();
service.decode(source, target);
assertEquals("русский", target.get("language"));
}
@Test
public void shouldDecodeAdHocFragment() {
String source = "{\"language\":\"french\"}";
LanguageFragment f = service.decodeFragment(source, LanguageFragment.class);
assertThat(f).isNotNull();
assertThat(f.language).isEqualTo("french");
}
@Test
void shouldDecodeAdHocFragment() {
String source = "{\"language\":\"french\"}";
LanguageFragment f = service.decodeFragment(source, LanguageFragment.class);
assertNotNull(f);
assertEquals("french", f.language);
}
static class LanguageFragment {
public String language;
}
private static class LanguageFragment {
public String language;
}
}

View File

@@ -16,305 +16,252 @@
package org.springframework.data.couchbase.core.mapping;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.mock.env.MockPropertySource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Verifies the correct behavior of annotation at the class level on persistable objects.
*
* @author Simon Baslé
*/
@RunWith(SpringJUnit4ClassRunner.class)
@TestPropertySource(properties = {
"valid.document.expiry = 10",
"invalid.document.expiry = abc"
})
@ContextConfiguration(classes = BasicCouchbasePersistentEntityTests.class)
@SpringJUnitConfig
@TestPropertySource(properties = { "valid.document.expiry = 10", "invalid.document.expiry = abc" })
public class BasicCouchbasePersistentEntityTests {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Autowired ConfigurableEnvironment environment;
@Autowired
ConfigurableEnvironment environment;
@Test
void testNoExpiryByDefault() {
CouchbasePersistentEntity<DefaultExpiry> entity = new BasicCouchbasePersistentEntity<>(
ClassTypeInformation.from(DefaultExpiry.class));
@Test
public void testNoExpiryByDefault() {
CouchbasePersistentEntity<DefaultExpiry> entity = new BasicCouchbasePersistentEntity<DefaultExpiry>(
ClassTypeInformation.from(DefaultExpiry.class));
assertThat(entity.getExpiry()).isEqualTo(0);
}
assertThat(entity.getExpiry()).isEqualTo(0);
}
@Test
void testDefaultExpiryUnitIsSeconds() {
CouchbasePersistentEntity<DefaultExpiryUnit> entity = new BasicCouchbasePersistentEntity<>(
ClassTypeInformation.from(DefaultExpiryUnit.class));
@Test
public void testDefaultExpiryUnitIsSeconds() {
CouchbasePersistentEntity<DefaultExpiryUnit> entity = new BasicCouchbasePersistentEntity<DefaultExpiryUnit>(
ClassTypeInformation.from(DefaultExpiryUnit.class));
assertThat(entity.getExpiry()).isEqualTo(78);
}
assertThat(entity.getExpiry()).isEqualTo(78);
}
@Test
void testLargeExpiry30DaysStillInSeconds() {
CouchbasePersistentEntity<LimitDaysExpiry> entityUnder = new BasicCouchbasePersistentEntity<>(
ClassTypeInformation.from(LimitDaysExpiry.class));
assertThat(entityUnder.getExpiry()).isEqualTo(30 * 24 * 60 * 60);
}
@Test
public void testLargeExpiry30DaysStillInSeconds() {
CouchbasePersistentEntity<LimitDaysExpiry> entityUnder = new BasicCouchbasePersistentEntity<LimitDaysExpiry>(
ClassTypeInformation.from(LimitDaysExpiry.class));
assertThat(entityUnder.getExpiry()).isEqualTo(30 * 24 * 60 * 60);
}
@Test
void testLargeExpiry31DaysIsConvertedToUnixUtcTime() {
CouchbasePersistentEntity<OverLimitDaysExpiry> entityOver = new BasicCouchbasePersistentEntity<>(
ClassTypeInformation.from(OverLimitDaysExpiry.class));
@Test
public void testLargeExpiry31DaysIsConvertedToUnixUtcTime() {
CouchbasePersistentEntity<OverLimitDaysExpiry> entityOver = new BasicCouchbasePersistentEntity<OverLimitDaysExpiry>(
ClassTypeInformation.from(OverLimitDaysExpiry.class));
int expiryOver = entityOver.getExpiry();
Calendar expected = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
expected.add(Calendar.DAY_OF_YEAR, 31);
int expiryOver = entityOver.getExpiry();
Calendar expected = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
expected.add(Calendar.DAY_OF_YEAR, 31);
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
calendar.clear();
calendar.add(Calendar.SECOND, expiryOver);
assertThat(calendar.get(Calendar.YEAR)).isEqualTo(expected.get(Calendar.YEAR));
assertThat(calendar.get(Calendar.MONTH)).isEqualTo(expected.get(Calendar.MONTH));
assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(expected.get(Calendar.DAY_OF_MONTH));
assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(expected.get(Calendar.HOUR_OF_DAY));
assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(expected.get(Calendar.MINUTE));
assertThat(calendar.get(Calendar.SECOND)).isEqualTo(expected.get(Calendar.SECOND));
}
Date dateOver = new Date(expiryOver * 1000L);
System.out.println(entityOver + " => " + dateOver);
@Test
void testLargeExpiryExpression31DaysIsConvertedToUnixUtcTime() {
BasicCouchbasePersistentEntity<OverLimitDaysExpiryExpression> entityOver = new BasicCouchbasePersistentEntity<>(
ClassTypeInformation.from(OverLimitDaysExpiryExpression.class));
entityOver.setEnvironment(environment);
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
calendar.clear();
calendar.add(Calendar.SECOND, expiryOver);
assertThat(calendar.get(Calendar.YEAR)).isEqualTo(expected.get(Calendar.YEAR));
assertThat(calendar.get(Calendar.MONTH)).isEqualTo(expected.get(Calendar.MONTH));
assertThat(calendar.get(Calendar.DAY_OF_MONTH))
.isEqualTo(expected.get(Calendar.DAY_OF_MONTH));
assertThat(calendar.get(Calendar.HOUR_OF_DAY))
.isEqualTo(expected.get(Calendar.HOUR_OF_DAY));
assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(expected.get(Calendar.MINUTE));
assertThat(calendar.get(Calendar.SECOND)).isEqualTo(expected.get(Calendar.SECOND));
}
int expiryOver = entityOver.getExpiry();
Calendar expected = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
expected.add(Calendar.DAY_OF_YEAR, 31);
@Test
public void testLargeExpiryExpression31DaysIsConvertedToUnixUtcTime() {
BasicCouchbasePersistentEntity<OverLimitDaysExpiryExpression> entityOver = new BasicCouchbasePersistentEntity<OverLimitDaysExpiryExpression>(
ClassTypeInformation.from(OverLimitDaysExpiryExpression.class));
entityOver.setEnvironment(environment);
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
calendar.clear();
calendar.add(Calendar.SECOND, expiryOver);
assertThat(calendar.get(Calendar.YEAR)).isEqualTo(expected.get(Calendar.YEAR));
assertThat(calendar.get(Calendar.MONTH)).isEqualTo(expected.get(Calendar.MONTH));
assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(expected.get(Calendar.DAY_OF_MONTH));
assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(expected.get(Calendar.HOUR_OF_DAY));
assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(expected.get(Calendar.MINUTE));
assertThat(calendar.get(Calendar.SECOND)).isEqualTo(expected.get(Calendar.SECOND));
}
int expiryOver = entityOver.getExpiry();
Calendar expected = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
expected.add(Calendar.DAY_OF_YEAR, 31);
@Test
void testLargeExpiry31DaysInSecondsIsConvertedToUnixUtcTime() {
CouchbasePersistentEntity<OverLimitSecondsExpiry> entityOver = new BasicCouchbasePersistentEntity<>(
ClassTypeInformation.from(OverLimitSecondsExpiry.class));
Date dateOver = new Date(expiryOver * 1000L);
System.out.println(entityOver + " => " + dateOver);
int expiryOver = entityOver.getExpiry();
Calendar expected = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
expected.add(Calendar.DAY_OF_YEAR, 31);
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
calendar.clear();
calendar.add(Calendar.SECOND, expiryOver);
assertThat(calendar.get(Calendar.YEAR)).isEqualTo(expected.get(Calendar.YEAR));
assertThat(calendar.get(Calendar.MONTH)).isEqualTo(expected.get(Calendar.MONTH));
assertThat(calendar.get(Calendar.DAY_OF_MONTH))
.isEqualTo(expected.get(Calendar.DAY_OF_MONTH));
assertThat(calendar.get(Calendar.HOUR_OF_DAY))
.isEqualTo(expected.get(Calendar.HOUR_OF_DAY));
assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(expected.get(Calendar.MINUTE));
assertThat(calendar.get(Calendar.SECOND)).isEqualTo(expected.get(Calendar.SECOND));
}
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
calendar.clear();
calendar.add(Calendar.SECOND, expiryOver);
assertThat(calendar.get(Calendar.YEAR)).isEqualTo(expected.get(Calendar.YEAR));
assertThat(calendar.get(Calendar.MONTH)).isEqualTo(expected.get(Calendar.MONTH));
assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(expected.get(Calendar.DAY_OF_MONTH));
assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(expected.get(Calendar.HOUR_OF_DAY));
assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(expected.get(Calendar.MINUTE));
assertThat(calendar.get(Calendar.SECOND)).isEqualTo(expected.get(Calendar.SECOND));
}
@Test
public void testLargeExpiry31DaysInSecondsIsConvertedToUnixUtcTime() {
CouchbasePersistentEntity<OverLimitSecondsExpiry> entityOver = new BasicCouchbasePersistentEntity<OverLimitSecondsExpiry>(
ClassTypeInformation.from(OverLimitSecondsExpiry.class));
@Test
void doesNotUseGetExpiry() {
assertThat(getBasicCouchbasePersistentEntity(SimpleDocument.class).getExpiry()).isEqualTo(0);
}
int expiryOver = entityOver.getExpiry();
Calendar expected = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
expected.add(Calendar.DAY_OF_YEAR, 31);
@Test
void usesGetExpiry() {
assertThat(getBasicCouchbasePersistentEntity(SimpleDocumentWithExpiry.class).getExpiry()).isEqualTo(10);
}
Date dateOver = new Date(expiryOver * 1000L);
System.out.println(entityOver + " => " + dateOver);
@Test
void doesNotUseIsUpdateExpiryForRead() {
assertThat(getBasicCouchbasePersistentEntity(SimpleDocument.class).isTouchOnRead()).isFalse();
assertThat(getBasicCouchbasePersistentEntity(SimpleDocumentWithExpiry.class).isTouchOnRead()).isFalse();
}
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
calendar.clear();
calendar.add(Calendar.SECOND, expiryOver);
assertThat(calendar.get(Calendar.YEAR)).isEqualTo(expected.get(Calendar.YEAR));
assertThat(calendar.get(Calendar.MONTH)).isEqualTo(expected.get(Calendar.MONTH));
assertThat(calendar.get(Calendar.DAY_OF_MONTH))
.isEqualTo(expected.get(Calendar.DAY_OF_MONTH));
assertThat(calendar.get(Calendar.HOUR_OF_DAY))
.isEqualTo(expected.get(Calendar.HOUR_OF_DAY));
assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(expected.get(Calendar.MINUTE));
assertThat(calendar.get(Calendar.SECOND)).isEqualTo(expected.get(Calendar.SECOND));
}
@Test
void usesTouchOnRead() {
assertThat(getBasicCouchbasePersistentEntity(SimpleDocumentWithTouchOnRead.class).isTouchOnRead()).isTrue();
}
@Test
public void doesNotUseGetExpiry() throws Exception {
assertThat(getBasicCouchbasePersistentEntity(SimpleDocument.class).getExpiry())
.isEqualTo(0);
}
@Test
void usesGetExpiryExpression() {
assertThat(getBasicCouchbasePersistentEntity(ConstantExpiryExpression.class).getExpiry()).isEqualTo(10);
}
@Test
public void usesGetExpiry() throws Exception {
assertThat(getBasicCouchbasePersistentEntity(SimpleDocumentWithExpiry.class)
.getExpiry()).isEqualTo(10);
}
@Test
void usesGetExpiryFromValidExpression() {
assertThat(getBasicCouchbasePersistentEntity(ExpiryWithValidExpression.class).getExpiry()).isEqualTo(10);
}
@Test
public void doesNotUseIsUpdateExpiryForRead() throws Exception {
assertThat(getBasicCouchbasePersistentEntity(SimpleDocument.class).isTouchOnRead())
.isFalse();
assertThat(getBasicCouchbasePersistentEntity(SimpleDocumentWithExpiry.class)
.isTouchOnRead()).isFalse();
}
@Test
void doesNotAllowUseExpiryFromInvalidExpression() {
assertThrows(IllegalArgumentException.class,
() -> getBasicCouchbasePersistentEntity(ExpiryWithInvalidExpression.class).getExpiry());
}
@Test
public void usesTouchOnRead() throws Exception {
assertThat(getBasicCouchbasePersistentEntity(SimpleDocumentWithTouchOnRead.class)
.isTouchOnRead()).isTrue();
}
@Test
void usesGetExpiryExpressionAndRespectsPropertyUpdates() {
BasicCouchbasePersistentEntity entity = getBasicCouchbasePersistentEntity(ExpiryWithValidExpression.class);
assertThat(entity.getExpiry()).isEqualTo(10);
@Test
public void usesGetExpiryExpression() throws Exception {
assertThat(getBasicCouchbasePersistentEntity(ConstantExpiryExpression.class)
.getExpiry()).isEqualTo(10);
}
environment.getPropertySources().addFirst(new MockPropertySource().withProperty("valid.document.expiry", "20"));
assertThat(entity.getExpiry()).isEqualTo(20);
}
@Test
public void usesGetExpiryFromValidExpression() throws Exception {
assertThat(getBasicCouchbasePersistentEntity(ExpiryWithValidExpression.class)
.getExpiry()).isEqualTo(10);
}
@Test
void failsIfExpiryExpressionMissesRequiredProperty() {
assertThrows(IllegalArgumentException.class,
() -> getBasicCouchbasePersistentEntity(ExpiryWithMissingProperty.class).getExpiry());
}
@Test
public void doesNotAllowUseExpiryFromInvalidExpression() throws Exception {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Invalid Integer value for expiry expression: abc");
assertThat(getBasicCouchbasePersistentEntity(ExpiryWithInvalidExpression.class)
.getExpiry()).isEqualTo(10);
}
@Test
void doesNotAllowUseExpiryAndExpressionSimultaneously() {
assertThrows(IllegalArgumentException.class,
() -> getBasicCouchbasePersistentEntity(ExpiryAndExpression.class).getExpiry());
}
@Test
public void usesGetExpiryExpressionAndRespectsPropertyUpdates() throws Exception {
BasicCouchbasePersistentEntity entity = getBasicCouchbasePersistentEntity(ExpiryWithValidExpression.class);
assertThat(entity.getExpiry()).isEqualTo(10);
private BasicCouchbasePersistentEntity getBasicCouchbasePersistentEntity(Class<?> clazz) {
BasicCouchbasePersistentEntity basicCouchbasePersistentEntity = new BasicCouchbasePersistentEntity(
ClassTypeInformation.from(clazz));
basicCouchbasePersistentEntity.setEnvironment(environment);
return basicCouchbasePersistentEntity;
}
environment.getPropertySources().addFirst(new MockPropertySource().withProperty("valid.document.expiry", "20"));
assertThat(entity.getExpiry()).isEqualTo(20);
}
@Configuration
static class Config {}
@Test
public void failsIfExpiryExpressionMissesRequiredProperty() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Could not resolve placeholder 'missing.expiry'");
getBasicCouchbasePersistentEntity(ExpiryWithMissingProperty.class).getExpiry();
}
public static class SimpleDocument {}
@Test
public void doesNotAllowUseExpiryAndExpressionSimultaneously() throws Exception {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("You cannot use 'expiry' and 'expiryExpression' at the same time");
expectedException.expectMessage(ExpiryAndExpression.class.getName());
getBasicCouchbasePersistentEntity(ExpiryAndExpression.class).getExpiry();
}
@Document(expiry = 10)
public static class SimpleDocumentWithExpiry {}
private BasicCouchbasePersistentEntity getBasicCouchbasePersistentEntity(Class<?> clazz) {
BasicCouchbasePersistentEntity basicCouchbasePersistentEntity = new BasicCouchbasePersistentEntity(ClassTypeInformation.from(clazz));
basicCouchbasePersistentEntity.setEnvironment(environment);
return basicCouchbasePersistentEntity;
}
@Document(expiry = 10, touchOnRead = true)
public static class SimpleDocumentWithTouchOnRead {}
public static class SimpleDocument {
}
/**
* Simple POJO to test default expiry.
*/
@Document
private class DefaultExpiry {}
@Document(expiry = 10)
public static class SimpleDocumentWithExpiry {
}
/**
* Simple POJO to test default expiry unit.
*/
@Document(expiry = 78)
private class DefaultExpiryUnit {}
@Document(expiry = 10, touchOnRead = true)
public static class SimpleDocumentWithTouchOnRead {
}
/**
* Simple POJO to test limit expiry.
*/
@Document(expiry = 30, expiryUnit = TimeUnit.DAYS)
private class LimitDaysExpiry {}
/**
* Simple POJO to test default expiry.
*/
@Document
private class DefaultExpiry {
}
/**
* Simple POJO to test larger than 30 days expiry.
*/
@Document(expiry = 31, expiryUnit = TimeUnit.DAYS)
public class OverLimitDaysExpiry {}
/**
* Simple POJO to test default expiry unit.
*/
@Document(expiry = 78)
private class DefaultExpiryUnit {
}
/**
* Simple POJO to test larger than 30 days expiry defined as an expression
*/
@Document(expiryExpression = "${document.expiry.larger.than.30days:31}", expiryUnit = TimeUnit.DAYS)
public class OverLimitDaysExpiryExpression {}
/**
* Simple POJO to test limit expiry.
*/
@Document(expiry = 30, expiryUnit = TimeUnit.DAYS)
private class LimitDaysExpiry {
}
/**
* Simple POJO to test larger than 30 days expiry, when expressed in default time unit (SECONDS).
*/
@Document(expiry = 31 * 24 * 60 * 60)
public class OverLimitSecondsExpiry {}
/**
* Simple POJO to test larger than 30 days expiry.
*/
@Document(expiry = 31, expiryUnit = TimeUnit.DAYS)
public class OverLimitDaysExpiry {
}
/**
* Simple POJO to test constant expiry expression
*/
@Document(expiryExpression = "10")
private class ConstantExpiryExpression {}
/**
* Simple POJO to test larger than 30 days expiry defined as an expression
*/
@Document(expiryExpression = "${document.expiry.larger.than.30days:31}", expiryUnit = TimeUnit.DAYS)
public class OverLimitDaysExpiryExpression {
}
/**
* Simple POJO to test valid expiry expression by resolving simple property from environment
*/
@Document(expiryExpression = "${valid.document.expiry}")
private class ExpiryWithValidExpression {}
/**
* Simple POJO to test larger than 30 days expiry, when expressed in default time unit (SECONDS).
*/
@Document(expiry = 31 * 24 * 60 * 60)
public class OverLimitSecondsExpiry {
}
/**
* Simple POJO to test invalid expiry expression
*/
@Document(expiryExpression = "${invalid.document.expiry}")
private class ExpiryWithInvalidExpression {}
/**
* Simple POJO to test constant expiry expression
*/
@Document(expiryExpression = "10")
private class ConstantExpiryExpression {
}
/**
* Simple POJO to test expiry expression logic failure to resolve property placeholder
*/
@Document(expiryExpression = "${missing.expiry}")
private class ExpiryWithMissingProperty {}
/**
* Simple POJO to test valid expiry expression by resolving simple property from environment
*/
@Document(expiryExpression = "${valid.document.expiry}")
private class ExpiryWithValidExpression {
}
/**
* Simple POJO to test invalid expiry expression
*/
@Document(expiryExpression = "${invalid.document.expiry}")
private class ExpiryWithInvalidExpression {
}
/**
* Simple POJO to test expiry expression logic failure to resolve property placeholder
*/
@Document(expiryExpression = "${missing.expiry}")
private class ExpiryWithMissingProperty {
}
/**
* Simple POJO to test that expiry and expiry expression cannot be used simultaneously
*/
@Document(expiry = 10, expiryExpression = "10")
private class ExpiryAndExpression {
}
/**
* Simple POJO to test that expiry and expiry expression cannot be used simultaneously
*/
@Document(expiry = 10, expiryExpression = "10")
private class ExpiryAndExpression {}
}

View File

@@ -16,11 +16,12 @@
package org.springframework.data.couchbase.core.mapping;
import java.lang.reflect.Field;
import java.util.Optional;
import static org.assertj.core.api.Assertions.*;
import org.junit.Before;
import org.junit.Test;
import java.lang.reflect.Field;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
@@ -28,8 +29,6 @@ import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Verifies the correct behavior of properties on persistable objects.
*
@@ -38,133 +37,77 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public class BasicCouchbasePersistentPropertyTests {
/**
* Holds the entity to test against (contains the properties).
*/
CouchbasePersistentEntity<Beer> entity;
/**
* Holds the entity to test against (contains the properties).
*/
CouchbasePersistentEntity<Beer> entity;
/**
* Create an instance of the demo entity.
*/
@Before
public void setUp() {
entity = new BasicCouchbasePersistentEntity<Beer>(
ClassTypeInformation.from(Beer.class));
}
/**
* Create an instance of the demo entity.
*/
@BeforeEach
void beforeEach() {
entity = new BasicCouchbasePersistentEntity<>(ClassTypeInformation.from(Beer.class));
}
/**
* Verifies the name of the property without annotations.
*/
@Test
public void usesPropertyFieldName() {
Field field = ReflectionUtils.findField(Beer.class, "description");
assertThat(getPropertyFor(field).getFieldName()).isEqualTo("description");
}
/**
* Verifies the name of the property without annotations.
*/
@Test
void usesPropertyFieldName() {
Field field = ReflectionUtils.findField(Beer.class, "description");
assertThat(getPropertyFor(field).getFieldName()).isEqualTo("description");
}
/**
* Verifies the name of the property with custom name annotation.
*/
@Test
public void usesAnnotatedFieldName() {
Field field = ReflectionUtils.findField(Beer.class, "name");
assertThat(getPropertyFor(field).getFieldName()).isEqualTo("foobar");
}
/**
* Verifies the name of the property with custom name annotation.
*/
@Test
void usesAnnotatedFieldName() {
Field field = ReflectionUtils.findField(Beer.class, "name");
assertThat(getPropertyFor(field).getFieldName()).isEqualTo("name");
}
@Test
public void testPrefersSpringIdAnnotation() {
BasicCouchbasePersistentEntity<Beer> test = new BasicCouchbasePersistentEntity<Beer>(
ClassTypeInformation.from(Beer.class));
@Test
void testSdkIdAnnotationEvaluatedAfterSpringIdAnnotationIsIgnored() {
BasicCouchbasePersistentEntity<Beer> test = new BasicCouchbasePersistentEntity<>(
ClassTypeInformation.from(Beer.class));
Field springIdField = ReflectionUtils.findField(Beer.class, "springId");
CouchbasePersistentProperty springIdProperty = getPropertyFor(springIdField);
Field sdkIdField = ReflectionUtils.findField(Beer.class, "sdkId");
CouchbasePersistentProperty sdkIdProperty = getPropertyFor(sdkIdField);
Field springIdField = ReflectionUtils.findField(Beer.class, "springId");
CouchbasePersistentProperty springIdProperty = getPropertyFor(springIdField);
test.addPersistentProperty(sdkIdProperty);
test.addPersistentProperty(springIdProperty);
// here this simulates the order in which the annotations would be found
// when "overriding" Spring @Id with SDK's @Id...
test.addPersistentProperty(springIdProperty);
assertThat(sdkIdProperty.getFieldName()).isEqualTo("sdkId");
assertThat(springIdProperty.getFieldName()).isEqualTo("springId");
assertThat(test.getIdProperty()).isEqualTo(springIdProperty);
}
assertThat(sdkIdProperty.isIdProperty()).isTrue();
assertThat(springIdProperty.isIdProperty()).isTrue();
CouchbasePersistentProperty property = test.getIdProperty();
assertThat(property).isEqualTo(springIdProperty);
}
@Test
public void testAcceptsSdkIdAnnotation() {
BasicCouchbasePersistentEntity<SdkIdentified> test = new BasicCouchbasePersistentEntity<SdkIdentified>(
ClassTypeInformation.from(SdkIdentified.class));
Field id = ReflectionUtils.findField(SdkIdentified.class, "id");
CouchbasePersistentProperty idProperty = getPropertyFor(id);
test.addPersistentProperty(idProperty);
CouchbasePersistentProperty property = test.getIdProperty();
assertThat(property).isEqualTo(idProperty);
}
@Test
public void testSdkIdAnnotationEvaluatedAfterSpringIdAnnotationIsIgnored() {
BasicCouchbasePersistentEntity<Beer> test = new BasicCouchbasePersistentEntity<Beer>(
ClassTypeInformation.from(Beer.class));
Field sdkIdField = ReflectionUtils.findField(Beer.class, "sdkId");
CouchbasePersistentProperty sdkIdProperty = getPropertyFor(sdkIdField);
Field springIdField = ReflectionUtils.findField(Beer.class, "springId");
CouchbasePersistentProperty springIdProperty = getPropertyFor(springIdField);
//here this simulates the order in which the annotations would be found
// when "overriding" Spring @Id with SDK's @Id...
test.addPersistentProperty(springIdProperty);
assertThat(test.getIdProperty()).isEqualTo(springIdProperty);
test.addPersistentProperty(sdkIdProperty);
assertThat(test.getIdProperty()).isEqualTo(springIdProperty);
}
/**
* Helper method to create a property out of the field.
*
* @param field the field to retrieve the properties from.
* @return the actual BasicCouchbasePersistentProperty instance.
*/
private CouchbasePersistentProperty getPropertyFor(Field field) {
/**
* Helper method to create a property out of the field.
*
* @param field the field to retrieve the properties from.
* @return the actual BasicCouchbasePersistentProperty instance.
*/
private CouchbasePersistentProperty getPropertyFor(Field field) {
ClassTypeInformation<?> type = ClassTypeInformation.from(field.getDeclaringClass());
return new BasicCouchbasePersistentProperty(Property.of(type, field), entity, SimpleTypeHolder.DEFAULT,
PropertyNameFieldNamingStrategy.INSTANCE);
}
}
/**
* Simple POJO to test attribute properties and annotations.
*/
public class Beer {
/**
* Simple POJO to test attribute properties and annotations.
*/
public class Beer {
@com.couchbase.client.java.repository.annotation.Id
private String sdkId;
@org.springframework.data.couchbase.core.mapping.Field String name;
String description;
@Id private String springId;
@Id
private String springId;
public String getId() {
return springId;
}
}
@com.couchbase.client.java.repository.annotation.Field("foobar")
String name;
String description;
public String getId() {
return springId;
}
}
/**
* Simple POJO to test that a single ID property from the SDK is taken into account.
*/
public class SdkIdentified {
@com.couchbase.client.java.repository.annotation.Id
private String id;
String value;
}
}

View File

@@ -1,98 +0,0 @@
/*
* Copyright 2017-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.core.mapping;
import java.util.Arrays;
import java.util.UUID;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.data.couchbase.TestContainerResource;
import org.springframework.data.couchbase.core.convert.CouchbaseCustomConversions;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory;
import org.springframework.data.couchbase.repository.support.IndexManager;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.springframework.data.couchbase.CouchbaseTestHelper.getRepositoryWithRetry;
/**
* @author Subhashni Balakrishnan
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
public class CustomConverterIntegrationTests {
@Autowired
private MappingCouchbaseConverter converter;
@Autowired
private RepositoryOperationsMapping operationsMapping;
@Autowired
private IndexManager indexManager;
private TestUUIDRepository repository;
@WritingConverter
public enum UUIDToStringConverter implements Converter<UUID, String> {
INSTANCE;
@Override
public String convert(UUID source) {
return source.toString();
}
}
@ReadingConverter
public enum StringToUUIDConverter implements Converter<String, UUID> {
INSTANCE;
@Override
public UUID convert(String source) {
return UUID.fromString(source);
}
}
@Before
public void setup() {
converter.setCustomConversions(new CouchbaseCustomConversions(Arrays.asList(UUIDToStringConverter.INSTANCE, StringToUUIDConverter.INSTANCE)));
converter.afterPropertiesSet();
RepositoryFactorySupport factory = new CouchbaseRepositoryFactory(operationsMapping, indexManager);
repository = getRepositoryWithRetry(factory, TestUUIDRepository.class);
}
@Test
public void testIdConversion() {
TestUUID doc = new TestUUID();
doc.id = UUID.randomUUID();
repository.save(doc);
repository.findById(doc.id);
}
}

View File

@@ -16,30 +16,20 @@
package org.springframework.data.couchbase.core.mapping;
import java.text.Format;
import java.text.SimpleDateFormat;
import static org.assertj.core.api.Assertions.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.annotation.Id;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.couchbase.UnitTestApplicationConfig;
import org.springframework.data.couchbase.core.convert.CouchbaseCustomConversions;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.couchbase.client.java.repository.annotation.Field;
import com.couchbase.client.java.repository.annotation.Id;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests to verify custom mapping logic.
@@ -47,142 +37,130 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Michael Nitschinger
* @author Mark Paluch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = UnitTestApplicationConfig.class)
public class CustomConvertersTests {
@Autowired
private MappingCouchbaseConverter converter;
private MappingCouchbaseConverter converter;
@After
public void cleanup() {
converter.setCustomConversions(new CouchbaseCustomConversions(Collections.emptyList()));
}
@BeforeEach
void beforeEach() {
converter = new MappingCouchbaseConverter();
}
@Test
public void shouldWriteWithCustomConverter() {
List<Object> converters = new ArrayList<Object>();
converters.add(DateToStringConverter.INSTANCE);
converter.setCustomConversions(new CouchbaseCustomConversions(converters));
converter.afterPropertiesSet();
@Test
void shouldWriteWithCustomConverter() {
List<Object> converters = new ArrayList<>();
converters.add(DateToStringConverter.INSTANCE);
converter.setCustomConversions(new CouchbaseCustomConversions(converters));
converter.afterPropertiesSet();
Date date = new Date();
BlogPost post = new BlogPost();
post.created = date;
Date date = new Date();
BlogPost post = new BlogPost();
post.created = date;
CouchbaseDocument doc = new CouchbaseDocument();
converter.write(post, doc);
CouchbaseDocument doc = new CouchbaseDocument();
converter.write(post, doc);
assertThat(doc.getPayload().get("created")).isEqualTo(date.toString());
}
assertThat(doc.getPayload().get("created")).isEqualTo(date.toString());
}
@Test
public void shouldReadWithCustomConverter() {
List<Object> converters = new ArrayList<Object>();
converters.add(IntegerToStringConverter.INSTANCE);
converter.setCustomConversions(new CouchbaseCustomConversions(converters));
converter.afterPropertiesSet();
@Test
void shouldReadWithCustomConverter() {
List<Object> converters = new ArrayList<>();
converters.add(IntegerToStringConverter.INSTANCE);
converter.setCustomConversions(new CouchbaseCustomConversions(converters));
converter.afterPropertiesSet();
CouchbaseDocument doc = new CouchbaseDocument();
doc.getPayload().put("content", 10);
Counter loaded = converter.read(Counter.class, doc);
assertThat(loaded.content).isEqualTo("even");
}
CouchbaseDocument doc = new CouchbaseDocument();
doc.getPayload().put("content", 10);
Counter loaded = converter.read(Counter.class, doc);
assertThat(loaded.content).isEqualTo("even");
}
@Test
public void shouldWriteConvertFullDocument() {
List<Object> converters = new ArrayList<Object>();
converters.add(BlogPostToCouchbaseDocumentConverter.INSTANCE);
converter.setCustomConversions(new CouchbaseCustomConversions(converters));
converter.afterPropertiesSet();
@Test
void shouldWriteConvertFullDocument() {
List<Object> converters = new ArrayList<>();
converters.add(BlogPostToCouchbaseDocumentConverter.INSTANCE);
converter.setCustomConversions(new CouchbaseCustomConversions(converters));
converter.afterPropertiesSet();
BlogPost post = new BlogPost();
post.id = "foobar";
post.title = "The Foo of the Bar";
BlogPost post = new BlogPost();
post.id = "foobar";
post.title = "The Foo of the Bar";
CouchbaseDocument doc = new CouchbaseDocument();
converter.write(post, doc);
CouchbaseDocument doc = new CouchbaseDocument();
converter.write(post, doc);
assertThat(doc.getPayload().get("title")).isEqualTo("The Foo of the Bar");
assertThat(doc.getPayload().get("slug")).isEqualTo("the_foo_of_the_bar");
}
assertThat(doc.getPayload().get("title")).isEqualTo("The Foo of the Bar");
assertThat(doc.getPayload().get("slug")).isEqualTo("the_foo_of_the_bar");
}
@Test
public void shouldReadConvertFullDocument() {
List<Object> converters = new ArrayList<Object>();
converters.add(CouchbaseDocumentToBlogPostConverter.INSTANCE);
converter.setCustomConversions(new CouchbaseCustomConversions(converters));
converter.afterPropertiesSet();
@Test
void shouldReadConvertFullDocument() {
List<Object> converters = new ArrayList<>();
converters.add(CouchbaseDocumentToBlogPostConverter.INSTANCE);
converter.setCustomConversions(new CouchbaseCustomConversions(converters));
converter.afterPropertiesSet();
CouchbaseDocument doc = new CouchbaseDocument();
doc.getPayload().put("title", "My Title");
CouchbaseDocument doc = new CouchbaseDocument();
doc.getPayload().put("title", "My Title");
BlogPost loaded = converter.read(BlogPost.class, doc);
assertThat(loaded.id).isEqualTo("modified");
assertThat(loaded.title).isEqualTo("My Title!!");
}
BlogPost loaded = converter.read(BlogPost.class, doc);
assertThat(loaded.id).isEqualTo("modified");
assertThat(loaded.title).isEqualTo("My Title!!");
}
public static class BlogPost {
@Id //also tests DATACOUCH-145 (this is SDK's @Id)
public String id = "key";
public enum IntegerToStringConverter implements Converter<Integer, String> {
INSTANCE;
@Field
public Date created;
@Override
public String convert(Integer source) {
return source % 2 == 0 ? "even" : "odd";
}
}
@Field
public String title;
public enum DateToStringConverter implements Converter<Date, String> {
INSTANCE;
}
@Override
public String convert(Date source) {
return source.toString();
}
}
public class Counter {
@Field
public String content;
}
@WritingConverter
public enum BlogPostToCouchbaseDocumentConverter implements Converter<BlogPost, CouchbaseDocument> {
INSTANCE;
public static enum IntegerToStringConverter implements Converter<Integer, String> {
INSTANCE;
@Override
public CouchbaseDocument convert(BlogPost source) {
return new CouchbaseDocument().setId(source.id).put("title", source.title).put("slug",
source.title.toLowerCase().replaceAll(" ", "_"));
}
}
@Override
public String convert(Integer source) {
return source % 2 == 0 ? "even" : "odd";
}
}
@ReadingConverter
public enum CouchbaseDocumentToBlogPostConverter implements Converter<CouchbaseDocument, BlogPost> {
INSTANCE;
public static enum DateToStringConverter implements Converter<Date, String> {
INSTANCE;
@Override
public BlogPost convert(CouchbaseDocument source) {
BlogPost post = new BlogPost();
post.id = "modified";
post.title = source.getPayload().get("title") + "!!";
return post;
}
}
public static Format FORMATTER = new SimpleDateFormat("yyyy HH");
public static class BlogPost {
@Id public String id = "key";
@Override
public String convert(Date source) {
return source.toString();
}
}
@Field public Date created;
@WritingConverter
public static enum BlogPostToCouchbaseDocumentConverter implements Converter<BlogPost, CouchbaseDocument> {
INSTANCE;
@Field public String title;
@Override
public CouchbaseDocument convert(BlogPost source) {
return new CouchbaseDocument()
.setId(source.id)
.put("title", source.title)
.put("slug", source.title.toLowerCase().replaceAll(" ", "_"));
}
}
@ReadingConverter
public static enum CouchbaseDocumentToBlogPostConverter implements Converter<CouchbaseDocument, BlogPost> {
INSTANCE;
@Override
public BlogPost convert(CouchbaseDocument source) {
BlogPost post = new BlogPost();
post.id = "modified";
post.title = source.getPayload().get("title") + "!!";
return post;
}
}
}
public static class Counter {
@Field public String content;
}
}

View File

@@ -1,30 +0,0 @@
/*
* Copyright 2017-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.core.mapping;
import java.util.UUID;
import com.couchbase.client.java.repository.annotation.Id;
import org.springframework.data.couchbase.core.query.ViewIndexed;
/**
* @author Subhashni Balakrishnan
*/
@ViewIndexed(designDoc = "testUUID", viewName = "all")
public class TestUUID {
@Id
public UUID id;
}

View File

@@ -1,27 +0,0 @@
/*
* Copyright 2017-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.core.mapping;
import java.util.UUID;
import org.springframework.data.couchbase.repository.CouchbaseRepository;
/**
* @author Subhashni Balakrishnan
*/
public interface TestUUIDRepository extends CouchbaseRepository<TestUUID, UUID> {
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright 2013-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.core.mapping.event;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Michael Nitschinger
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = EventContextConfiguration.class)
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
public class AbstractCouchbaseEventListenerTests {
@Autowired
private CouchbaseTemplate couchbaseTemplate;
@Autowired
private SimpleMappingEventListener eventListener;
@Test
public void shouldEmitEvents() {
int beforeSave = eventListener.onBeforeSaveEvents.size();
int afterSave = eventListener.onAfterSaveEvents.size();
int beforeConvert = eventListener.onBeforeConvertEvents.size();
couchbaseTemplate.save(new User("john smith", 18));
assertThat(eventListener.onBeforeSaveEvents.size()).isEqualTo(beforeSave + 1);
assertThat(eventListener.onAfterSaveEvents.size()).isEqualTo(afterSave + 1);
assertThat(eventListener.onBeforeConvertEvents.size()).isEqualTo(beforeConvert + 1);
}
}

View File

@@ -1,45 +0,0 @@
/*
* Copyright 2013-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.core.mapping.event;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.UnitTestApplicationConfig;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
/**
* @author Michael Nitschinger
*/
@Configuration
public class EventContextConfiguration extends UnitTestApplicationConfig {
@Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
@Bean
public ValidatingCouchbaseEventListener validatingCouchbaseEventListener() {
return new ValidatingCouchbaseEventListener(validator());
}
@Bean
public SimpleMappingEventListener simpleMappingEventListener() {
return new SimpleMappingEventListener();
}
}

View File

@@ -1,47 +0,0 @@
/*
* Copyright 2013-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.core.mapping.event;
import java.util.ArrayList;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
/**
* @author Michael Nitschinger
*/
public class SimpleMappingEventListener extends AbstractCouchbaseEventListener<Object> {
public final ArrayList<BeforeConvertEvent<Object>> onBeforeConvertEvents = new ArrayList<BeforeConvertEvent<Object>>();
public final ArrayList<BeforeSaveEvent<Object>> onBeforeSaveEvents = new ArrayList<BeforeSaveEvent<Object>>();
public final ArrayList<AfterSaveEvent<Object>> onAfterSaveEvents = new ArrayList<AfterSaveEvent<Object>>();
@Override
public void onBeforeConvert(Object source) {
onBeforeConvertEvents.add(new BeforeConvertEvent<Object>(source));
}
@Override
public void onBeforeSave(Object source, CouchbaseDocument doc) {
onBeforeSaveEvents.add(new BeforeSaveEvent<Object>(source, doc));
}
@Override
public void onAfterSave(Object source, CouchbaseDocument doc) {
onAfterSaveEvents.add(new AfterSaveEvent<Object>(source, doc));
}
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright 2013-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.core.mapping.event;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
import org.springframework.data.annotation.Id;
/**
* @author Michael Nitschinger
*/
public class User {
@Id
private String id;
@Size(min = 10)
private String name;
@Min(18)
private Integer age;
public User(String name, Integer age) {
id = "id";
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright 2013-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.core.mapping.event;
import javax.validation.ConstraintViolationException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
/**
* @author Michael Nitschinger
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = EventContextConfiguration.class)
public class ValidatingCouchbaseEventListenerTests {
@Autowired
CouchbaseTemplate template;
@Test
public void shouldThrowConstraintViolationException() {
User user = new User("john", 17);
try {
template.save(user);
fail("Expected ConstraintViolationException");
}
catch (ConstraintViolationException e) {
assertThat(e.getConstraintViolations().size()).isEqualTo(2);
}
}
@Test
public void shouldNotThrowAnyExceptions() {
template.save(new User("john smith", 18));
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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.core.query;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.data.couchbase.core.query.QueryCriteria.*;
class QueryCriteriaTests {
@Test
void testSimpleCriteria() {
QueryCriteria c = where("name").is("Bubba");
assertEquals("`name` = \"Bubba\"", c.export());
}
@Test
public void testNullValue() {
QueryCriteria c = where("name").is(null);
assertEquals("`name` = null", c.export());
}
@Test
void testSimpleNumber() {
QueryCriteria c = where("name").is(5);
assertEquals("`name` = 5", c.export());
}
@Test
void testNotEqualCriteria() {
QueryCriteria c = where("name").ne("Bubba");
assertEquals("`name` != \"Bubba\"", c.export());
}
@Test
void testChainedCriteria() {
QueryCriteria c = where("name").is("Bubba").and("age").lt(21).or("country").is("Austria");
assertEquals("`name` = \"Bubba\" and `age` < 21 or `country` = \"Austria\"", c.export());
}
@Test
void testNestedAndCriteria() {
QueryCriteria c = where("name").is("Bubba").and(where("age").gt(12).or("country").is("Austria"));
assertEquals("`name` = \"Bubba\" and (`age` > 12 or `country` = \"Austria\")", c.export());
}
@Test
void testNestedOrCriteria() {
QueryCriteria c = where("name").is("Bubba").or(where("age").gt(12).or("country").is("Austria"));
assertEquals("`name` = \"Bubba\" or (`age` > 12 or `country` = \"Austria\")", c.export());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-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.
@@ -14,40 +14,36 @@
* limitations under the License.
*/
package org.springframework.data.couchbase.repository.cdi;
package org.springframework.data.couchbase.domain;
import org.springframework.data.annotation.Id;
import com.couchbase.client.java.repository.annotation.Field;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.couchbase.core.mapping.Document;
/**
* @author Mark Paluch
*/
public class Person {
@Document
public class Airport {
@Id String id;
@Id private String id;
String iata;
@Field private String name;
String icao;
public Person() {}
public Person(String id, String name) {
@PersistenceConstructor
public Airport(String id, String iata, String icao) {
this.id = id;
this.name = name;
this.iata = iata;
this.icao = icao;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
public String getIata() {
return iata;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
public String getIcao() {
return icao;
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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.domain;
import org.springframework.data.couchbase.repository.ScanConsistency;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import com.couchbase.client.java.query.QueryScanConsistency;
import java.util.List;
@Repository
public interface AirportRepository extends PagingAndSortingRepository<Airport, String> {
@Override
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
Iterable<Airport> findAll();
List<Airport> findAllByIata(String iata);
}

View File

@@ -0,0 +1,72 @@
/*
* 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 java.util.Objects;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.couchbase.core.mapping.Document;
@Document
public class User {
@Id private String id;
private String firstname;
private String lastname;
@PersistenceConstructor
public User(final String id, final String firstname, final String lastname) {
this.id = id;
this.firstname = firstname;
this.lastname = lastname;
}
public String getId() {
return id;
}
public String getFirstname() {
return firstname;
}
public String getLastname() {
return lastname;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
User user = (User) o;
return Objects.equals(id, user.id) && Objects.equals(firstname, user.firstname)
&& Objects.equals(lastname, user.lastname);
}
@Override
public int hashCode() {
return Objects.hash(id, firstname, lastname);
}
@Override
public String toString() {
return "User{" + "id='" + id + '\'' + ", firstname='" + firstname + '\'' + ", lastname='" + lastname + '\'' + '}';
}
}

View File

@@ -13,11 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.repository.join;
import org.springframework.data.couchbase.core.query.N1qlSecondaryIndexed;
import org.springframework.data.couchbase.repository.CouchbaseRepository;
package org.springframework.data.couchbase.domain;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface UserRepository extends PagingAndSortingRepository<User, String> {
List<User> findByFirstname(String firstname);
List<User> findByFirstnameAndLastname(String firstname, String lastname);
@N1qlSecondaryIndexed(indexName = "bookIndex")
interface BookRepository extends CouchbaseRepository<Book, String> {
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright 2013-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.monitor;
import com.couchbase.client.java.Bucket;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.test.context.ContextConfiguration;
import static org.assertj.core.api.Assertions.*;
/**
* @author Michael Nitschinger
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
public class ClientInfoIntegrationTests {
/**
* Contains a reference to the actual CouchbaseClient.
*/
@Autowired
private Bucket client;
private ClientInfo ci;
@Before
public void setup() throws Exception {
ci = new ClientInfo(client);
}
@Test
public void hostNames() {
String hostnames = ci.getHostNames();
assertThat(hostnames).isNotEmpty();
}
}

View File

@@ -1,64 +0,0 @@
/*
* Copyright 2013-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.monitor;
import com.couchbase.client.java.Bucket;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.assertj.core.api.Assertions.*;
/**
* @author Michael Nitschinger
*/
@Ignore(value = "Cant run get cluster info on test container")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
public class ClusterInfoIntegrationTests {
/**
* Contains a reference to the actual CouchbaseClient.
*/
@Autowired
private Bucket client;
private ClusterInfo ci;
@Before
public void setup() throws Exception {
ci = new ClusterInfo(client);
}
@Test
public void totalDiskAssigned() {
assertThat(ci.getTotalDiskAssigned()).isGreaterThan(0);
}
@Test
public void totalRAMUsed() {
assertThat(ci.getTotalRAMUsed()).isGreaterThan(0);
}
}

View File

@@ -1,96 +0,0 @@
/*
* 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 org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.couchbase.core.mapping.id.GeneratedValue;
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory;
import org.springframework.data.couchbase.repository.support.IndexManager;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import org.springframework.test.context.ContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.data.couchbase.core.mapping.id.GenerationStrategy.UNIQUE;
/**
* @author Maxence Labusquiere
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
public class CouchbaseIdGenerationIntegrationTests {
@Autowired
private RepositoryOperationsMapping operationsMapping;
@Autowired
private IndexManager indexManager;
private CrudRepository<SimpleClassWithGeneratedIdValueUsingUUID, String> entityRepository;
@Before
public void setUp() throws Exception {
entityRepository = new CouchbaseRepositoryFactory(operationsMapping, indexManager).getRepository(EntityRepository.class);
}
@Test
public void idFieldEntityIsFillWithGeneratedValueOnSave() {
SimpleClassWithGeneratedIdValueUsingUUID entity = new SimpleClassWithGeneratedIdValueUsingUUID();
SimpleClassWithGeneratedIdValueUsingUUID savedEntity = entityRepository.save(entity);
assertThat(savedEntity.id != null).as("Expected generated value").isTrue();
if (entityRepository.existsById(savedEntity.id)) {
entityRepository.existsById(savedEntity.id);
}
}
@Test
public void ifIdFieldIsAlreadySetNothingIsDone() {
String id = "AnId";
SimpleClassWithGeneratedIdValueUsingUUID entity = new SimpleClassWithGeneratedIdValueUsingUUID();
entity.setId(id);
SimpleClassWithGeneratedIdValueUsingUUID savedEntity = entityRepository.save(entity);
assertThat(savedEntity.id == id).as("Expected same id instance").isTrue();
if (entityRepository.existsById(savedEntity.id)) {
entityRepository.existsById(savedEntity.id);
}
}
@Document
static class SimpleClassWithGeneratedIdValueUsingUUID {
@Id
@GeneratedValue(strategy = UNIQUE)
public String id;
public String value = "new";
public void setId(String id) {
this.id = id;
}
}
@Repository
interface EntityRepository extends CrudRepository<SimpleClassWithGeneratedIdValueUsingUUID, String> {
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.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.test.context.junit.jupiter.SpringJUnitConfig;
@SpringJUnitConfig(CouchbaseRepositoryKeyValueIntegrationTests.Config.class)
public class CouchbaseRepositoryKeyValueIntegrationTests extends ClusterAwareIntegrationTests {
@Autowired UserRepository userRepository;
@Test
void saveAndFindById() {
User user = new User(UUID.randomUUID().toString(), "f", "l");
assertFalse(userRepository.existsById(user.getId()));
userRepository.save(user);
Optional<User> found = userRepository.findById(user.getId());
assertTrue(found.isPresent());
found.ifPresent(u -> assertEquals(user, u));
assertTrue(userRepository.existsById(user.getId()));
}
@Configuration
@EnableCouchbaseRepositories("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();
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* 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.repository.config.EnableCouchbaseRepositories;
import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import com.couchbase.client.core.error.IndexExistsException;
@SpringJUnitConfig(CouchbaseRepositoryQueryIntegrationTests.Config.class)
public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegrationTests {
@Autowired CouchbaseClientFactory clientFactory;
@Autowired AirportRepository 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);
List<Airport> all = StreamSupport.stream(airportRepository.findAll().spliterator(), false)
.collect(Collectors.toList());
assertFalse(all.isEmpty());
assertTrue(all.stream().anyMatch(a -> a.getId().equals("airports::vie")));
}
@Test
void findBySimpleProperty() {
List<Airport> airports = airportRepository.findAllByIata("vie");
// TODO
System.err.println(airports);
}
@Configuration
@EnableCouchbaseRepositories("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();
}
}
}

View File

@@ -1,187 +0,0 @@
/*
* Copyright 2013-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 java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.view.Stale;
import com.couchbase.client.java.view.ViewQuery;
import com.couchbase.client.java.view.ViewResult;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory;
import org.springframework.data.couchbase.repository.support.IndexManager;
import org.springframework.data.mapping.PropertyReferenceException;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
/**
* @author David Harrigan
* @author Simon Baslé
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
@TestExecutionListeners(CouchbaseRepositoryViewListener.class)
public class CouchbaseRepositoryViewIntegrationTests {
@Autowired
private Bucket client;
@Autowired
private RepositoryOperationsMapping operationsMapping;
@Autowired
private IndexManager indexManager;
private CustomUserRepository repository;
@Before
public void setup() throws Exception {
repository = new CouchbaseRepositoryFactory(operationsMapping, indexManager).getRepository(CustomUserRepository.class);
}
@Test
public void shouldFindAllWithCustomView() {
client.query(ViewQuery.from("user", "customFindAllView").stale(Stale.FALSE));
Iterable<User> allUsers = repository.findAll();
assertThat(allUsers).hasSize(100);
}
@Test
public void shouldCountWithCustomView() {
ViewResult clientResult = client.query(ViewQuery.from("userCustom", "customCountView")
.reduce().stale(Stale.FALSE));
final Object clientRowValue = clientResult.allRows().get(0).value();
final long value = repository.count();
assertThat(value).isEqualTo(100L);
assertThat(clientRowValue).isInstanceOf(Number.class);
assertThat(((Number) clientRowValue).longValue()).isEqualTo(value);
}
@Test
public void shouldDetectMethodNameWithoutPropertyAndIssueGenericQueryOnView() {
Iterable<User> users = repository.findRandomMethodName();
assertThat(users).isNotNull();
assertThat(users.iterator().hasNext()).isTrue();
try {
repository.findIncorrectExplicitView();
fail("Expected InvalidDataAccessResourceException");
} catch (InvalidDataAccessResourceUsageException e) {
assertThat(e.getMessage().startsWith("View user/allSomething does not exist"))
.as(e.getMessage()).isTrue();
}
}
@Test(expected = PropertyReferenceException.class)
public void shouldFailDeriveOnBadProperty() {
repository.findAllByUsernameEqualAndUserblablaIs("uname-1", "blabla");
}
@Test
public void shouldDeriveViewParametersAndReduce() {
long count = repository.countByUsernameGreaterThanEqualAndUsernameLessThan("uname-8", "uname-9");
assertThat(count).isEqualTo(12);
}
@Test
public void shouldDeriveViewParametersAndReduceNonNumerical() {
JsonObject reduceResult = repository.findByAgeLessThan(50);
assertThat(reduceResult).isNotNull();
assertThat((long) reduceResult.getLong("count")).isEqualTo(51);
assertThat((long) reduceResult.getLong("max")).isEqualTo(50);
assertThat((long) reduceResult.getLong("min")).isEqualTo(0);
assertThat((long) reduceResult.getLong("sum")).isEqualTo(1275);
}
@Test
public void shouldDeriveViewParameters() {
String lowKey = "uname-1";
String middleKey = "uname-10";
String highKey = "uname-11";
List<String> keys = Arrays.asList(lowKey, middleKey, highKey);
User u1 = repository.findByUsernameIs(lowKey).get(0);
User u2 = repository.findByUsernameIs(middleKey).get(0);
User u3 = repository.findByUsernameIs(highKey).get(0);
assertThat(u1.getUsername()).isEqualTo(lowKey);
assertThat(u2.getUsername()).isEqualTo(middleKey);
assertThat(u3.getUsername()).isEqualTo(highKey);
List<User> in = repository.findAllByUsernameIn(keys);
List<User> gteLte = repository.findByUsernameGreaterThanEqualAndUsernameLessThanEqual(lowKey, highKey);
List<User> between = repository.findByUsernameBetween(lowKey, highKey);
List<User> gteLimited = repository.findTop3ByUsernameGreaterThanEqual(lowKey);
// the results are unordered, so compare using Set
Set<User> expected = new HashSet<>(Arrays.asList(u1, u2, u3));
assertThat(new HashSet<>(in)).isEqualTo(expected);
assertThat(new HashSet<>(gteLte)).isEqualTo(expected);
assertThat(new HashSet<>(between)).isEqualTo(expected);
assertThat(new HashSet<>(gteLimited)).isEqualTo(expected);
}
@Test
public void shouldDeriveToEmptyClause() {
List<User> users = repository.findAllByUsername();
assertThat(users).isNotNull();
assertThat(users.size()).isEqualTo(100);
}
@Test
public void shouldDetermineViewNameFromMethodPrefix() {
try {
repository.findByIncorrectView();
fail("Expected InvalidDataAccessResourceException");
} catch (InvalidDataAccessResourceUsageException e) {
assertThat(e.getMessage().startsWith("View user/byIncorrectView does not exist"))
.as(e.getMessage()).isTrue();
}
}
@Test
public void shouldDetermineViewNameFromCountPrefixAndReduce() {
long count = repository.countCustomFindAllView();
assertThat(count).isEqualTo(100);
try {
repository.countCustomFindInvalid();
fail("Expected InvalidDataAccessResourceException");
} catch (InvalidDataAccessResourceUsageException e) {
assertThat(e.getMessage().startsWith("View user/customFindInvalid does not exist"))
.as(e.getMessage()).isTrue();
}
}
}

View File

@@ -1,74 +0,0 @@
/*
* Copyright 2013-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 java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.PersistTo;
import com.couchbase.client.java.ReplicateTo;
import com.couchbase.client.java.cluster.ClusterInfo;
import com.couchbase.client.java.view.DefaultView;
import com.couchbase.client.java.view.DesignDocument;
import com.couchbase.client.java.view.View;
import org.springframework.data.couchbase.config.BeanNames;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
/**
* @author Michael Nitschinger
* @author Simon Baslé
*/
public class CouchbaseRepositoryViewListener extends DependencyInjectionTestExecutionListener {
@Override
public void beforeTestClass(final TestContext testContext) throws Exception {
Bucket client = (Bucket) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_BUCKET);
ClusterInfo clusterInfo = (ClusterInfo) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_CLUSTER_INFO);
populateTestData(client, clusterInfo);
createAndWaitForDesignDocs(client);
}
private void populateTestData(final Bucket client, ClusterInfo clusterInfo) {
CouchbaseTemplate template = new CouchbaseTemplate(clusterInfo, client);
for (int i = 0; i < 100; i++) {
template.save(new User("testuser-" + i, "uname-" + i, i), PersistTo.MASTER, ReplicateTo.NONE);
}
}
private void createAndWaitForDesignDocs(final Bucket client) {
String mapFunction = "function (doc, meta) { if(doc._class == \"org.springframework.data.couchbase.repository.User\") { emit(null, null); } }";
String mapFunctionName = "function (doc, meta) { if(doc._class == \"org.springframework.data.couchbase.repository.User\") { emit(doc.username, null); } }";
String mapFunctionAge = "function (doc, meta) { if(doc._class == \"org.springframework.data.couchbase.repository.User\") { emit(doc.age, doc.age); } }";
View view = DefaultView.create("customFindAllView", mapFunction, "_count");
View customFindByNameView = DefaultView.create("customFindByNameView", mapFunctionName, "_count");
View customFindByAgeStatsView = DefaultView.create("customFindByAgeStatsView", mapFunctionAge, "_stats");
List<View> views = Arrays.asList(view, customFindByNameView, customFindByAgeStatsView);
DesignDocument designDoc = DesignDocument.create("user", views);
client.bucketManager().upsertDesignDocument(designDoc);
view = DefaultView.create("customCountView", mapFunction, "_count");
views = Collections.singletonList(view);
designDoc = DesignDocument.create("userCustom", views);
client.bucketManager().upsertDesignDocument(designDoc);
}
}

View File

@@ -1,80 +0,0 @@
/*
* Copyright 2013-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 java.util.List;
import com.couchbase.client.java.document.json.JsonObject;
import org.springframework.data.couchbase.core.query.View;
/**
* @author David Harrigan
* @author Simon Baslé
*/
public interface CustomUserRepository extends CouchbaseRepository<User, String> {
@Override
@View(designDocument = "user", viewName = "customFindAllView")
Iterable<User> findAll();
@Override
@View(designDocument = "userCustom", viewName = "customCountView")
long count();
@View(viewName = "allSomething")
Iterable<User> findIncorrectExplicitView();
@View(viewName = "customFindAllView")
Iterable<User> findRandomMethodName();
@View(viewName = "customFindByNameView")
long countByUsernameGreaterThanEqualAndUsernameLessThan(String lowBound, String highBound);
@View(viewName = "customFindByNameView")
List<User> findByUsernameIs(String lowKey);
@View(viewName = "customFindByNameView")
List<User> findAllByUsernameIn(List<String> keys);
@View(viewName = "customFindByNameView")
List<User> findByUsernameGreaterThanEqualAndUsernameLessThanEqual(String lowKey, String highKey);
@View(viewName = "customFindByNameView")
List<User> findByUsernameBetween(String lowKey, String highKey);
@View(viewName = "customFindByNameView")
List<User> findTop3ByUsernameGreaterThanEqual(String lowKey);
@View(viewName = "customFindAllView")
List<User> findAllByUsername();
@View(viewName = "customFindAllView")
List<User> findAllByUsernameEqualAndUserblablaIs(String s, String blabla);
@View
List<User> findByIncorrectView();
@View
long countCustomFindAllView();
@View
long countCustomFindInvalid();
@View(viewName = "customFindByAgeStatsView", reduce = true)
JsonObject findByAgeLessThan(int maxAge);
}

View File

@@ -1,57 +0,0 @@
package org.springframework.data.couchbase.repository;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import com.couchbase.client.java.document.json.JsonArray;
import org.springframework.data.couchbase.core.query.Dimensional;
import org.springframework.data.geo.Box;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.geo.Polygon;
import org.springframework.data.repository.CrudRepository;
public interface DimensionalPartyRepository extends CrudRepository<Party, String> {
@Dimensional(designDocument = "partyGeo", spatialViewName = "byLocation")
List<Party> findByLocationNear(Point p, Distance d);
@Dimensional(designDocument = "partyGeo", spatialViewName = "byLocation")
List<Party> findByLocationWithin(Box boundingBox);
@Dimensional(designDocument = "partyGeo", spatialViewName = "byLocation")
List<Party> findByLocationWithin(Polygon zone);
@Dimensional(designDocument = "partyGeo", spatialViewName = "byLocation")
List<Party> findByLocationWithin(Point[] points);
@Dimensional(designDocument = "partyGeo", spatialViewName = "byLocation")
List<Party> findByLocationWithin(Point point);
@Dimensional(designDocument = "partyGeo", spatialViewName = "byLocation")
List<Party> findByLocationWithin(JsonArray range);
@Dimensional(designDocument = "partyGeo", spatialViewName = "byLocation")
List<Party> findByLocationWithin(Point lowerLeft, Point upperRight);
@Dimensional(designDocument = "partyGeo", spatialViewName = "byLocationAndAttendees", dimensions = 3)
List<Party> findByLocationWithin(Circle zone);
@Dimensional(designDocument = "partyGeo", spatialViewName = "byLocationAndAttendees", dimensions = 3)
List<Party> findByLocationWithinAndAttendeesGreaterThan(Polygon zone, double minAttendees);
@Dimensional(designDocument = "partyGeo", spatialViewName = "byLocationAndAttendees", dimensions = 3)
List<Party> findByLocationWithin(JsonArray startRange, JsonArray endRange);
//TODO more coverage of operators?
@IndexedByLocation
List<Party> findByLocationIsWithin(Point a, Point b);
@Dimensional(designDocument = "partyGeo", spatialViewName = "byLocation", dimensions = 2)
@Retention(RetentionPolicy.RUNTIME)
@interface IndexedByLocation { }
}

View File

@@ -1,346 +0,0 @@
package org.springframework.data.couchbase.repository;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.springframework.data.couchbase.CouchbaseTestHelper.getRepositoryWithRetry;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.couchbase.client.java.document.json.JsonArray;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.data.couchbase.core.CouchbaseQueryExecutionException;
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory;
import org.springframework.data.couchbase.repository.support.IndexManager;
import org.springframework.data.geo.Box;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.geo.Polygon;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
/**
* @author Simon Baslé
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
@TestExecutionListeners(PartyPopulatorListener.class)
public class DimensionalQueryIntegrationTests {
@Autowired
private RepositoryOperationsMapping templateMapping;
@Autowired
private IndexManager indexManager;
private DimensionalPartyRepository repository;
@Before
public void setup() throws Exception {
RepositoryFactorySupport factory = new CouchbaseRepositoryFactory(templateMapping, indexManager);
repository = getRepositoryWithRetry(factory, DimensionalPartyRepository.class);
}
@Test
public void testFindWithingPolygon() {
Set<String> expectedKeys = new HashSet<String>();
expectedKeys.add("testparty-2");
expectedKeys.add("testparty-3");
expectedKeys.add("testparty-4");
expectedKeys.add("testparty-5");
//a zone that engulfs parties 2, 3, 4 and 5, in the shape of a downward arrow pointing right
Polygon zone = new Polygon(new Point(1, -2),
new Point(3, -1.5),
new Point(6, -4),
new Point(5.5, -5.5),
new Point(3, -5));
List<Party> parties = repository.findByLocationWithin(zone);
assertThat(parties.size()).isEqualTo(4);
for (Party party : parties) {
assertThat(expectedKeys.contains(party.getKey())).isTrue();
}
}
@Test
public void testByLocationNear() {
Set<String> expectedKeys = new HashSet<String>();
expectedKeys.add("testparty-0");
expectedKeys.add("testparty-1");
List<Party> parties = repository.findByLocationNear(new Point(0, 0), new Distance(1.5));
assertThat(parties.size()).isEqualTo(2);
for (Party party : parties) {
assertThat(expectedKeys.contains(party.getKey())).isTrue();
}
//with this one, testparty-2 is within the bounding box but not in correct distance
parties = repository.findByLocationNear(new Point(0, 0), new Distance(2.5));
assertThat(parties.size()).isEqualTo(2);
for (Party party : parties) {
assertThat(expectedKeys.contains(party.getKey())).isTrue();
}
//here we adjust the distance so that testparty-2 falls just on the edge
parties = repository.findByLocationNear(new Point(0, 0), new Distance(2.8284271247461903));
expectedKeys.add("testparty-2");
assertThat(parties.size()).isEqualTo(3);
for (Party party : parties) {
assertThat(expectedKeys.contains(party.getKey())).isTrue();
}
}
@Test
public void testByLocationAndSparseAttendees() {
Set<String> expectedKeys = new HashSet<String>();
expectedKeys.add("testparty-4");
expectedKeys.add("testparty-5");
//a zone that engulfs parties 2, 3, 4 and 5 (with resp. 120, 130, 140 and 150 attendees)
//in the shape of a downward arrow pointing right
Polygon zone = new Polygon(new Point(1, -2),
new Point(3, -1.5),
new Point(6, -4),
new Point(5.5, -5.5),
new Point(3, -5));
//first check the zone contains 4 parties
List<Party> allPartiesInZone = repository.findByLocationWithinAndAttendeesGreaterThan(zone, -1);
List<Party> allPartiesInZoneWithoutAttendeeCriteria = repository.findByLocationWithin(zone);
assertThat(allPartiesInZone.size()).as(allPartiesInZone.toString()).isEqualTo(4);
assertThat(allPartiesInZone).isEqualTo(allPartiesInZoneWithoutAttendeeCriteria);
//check parties are limited by the attendees
List<Party> parties = repository.findByLocationWithinAndAttendeesGreaterThan(zone, 140);
for (Party party : parties) {
System.out.println(party.getKey() + " : " + party.getLocation() + " " + party.getAttendees());
}
assertThat(parties.size()).as(parties.toString()).isEqualTo(2);
for (Party party : parties) {
assertThat(party.getAttendees() >= 140).isTrue();
assertThat(expectedKeys.contains(party.getKey())).isTrue();
}
}
@Test
public void testByLocationInCircle() {
Circle zoneBboxFalse = new Circle(new Point(-5,5), new Distance(5.5));
Circle zoneEdge = new Circle(new Point(-5, 0), new Distance(5));
Circle zoneInside = new Circle(new Point(6, -6), new Distance(10));
Circle zoneEmpty = new Circle(new Point(6,6), new Distance(3));
List<Party> parties = repository.findByLocationWithin(zoneBboxFalse);
assertThat(parties.size()).isEqualTo(0);
parties = repository.findByLocationWithin(zoneEdge);
assertThat(parties.size()).isEqualTo(1);
assertThat(parties.get(0).getKey()).isEqualTo("testparty-0");
parties = repository.findByLocationWithin(zoneInside);
assertThat(parties.size()).isEqualTo(12); //all the parties except the special one at 100, 100
parties = repository.findByLocationWithin(zoneEmpty);
assertThat(parties.size()).isEqualTo(0);
}
@Test
public void testByLocationInBox() {
Box zone1 = new Box(new Point(-10.5, -0.5), new Point(0.5, 10.5));
Box zone2 = new Box(new Point(-4, -16), new Point(16, 4));
Box zoneEmpty = new Box(new Point(3, 3), new Point(9, 9));
List<Party> parties = repository.findByLocationWithin(zone1);
assertThat(parties.size()).isEqualTo(1);
assertThat(parties.get(0).getKey()).isEqualTo("testparty-0");
parties = repository.findByLocationWithin(zone2);
assertThat(parties.size()).isEqualTo(12); //all the parties except the special one at 100, 100
parties = repository.findByLocationWithin(zoneEmpty);
assertThat(parties.size()).isEqualTo(0);
}
@Test
public void testByLocationInPolygon() {
//slightly skewed square triangle that cuts just short of (0,0)
//bounding box of (-1,-1),(0.5,1)
Polygon zoneFalsePositive = new Polygon(
new Point(-1, 1),
new Point(0.5, 1),
new Point(-1, -1)
);
//square triangle that comes through (0,0)
//bounding box of (-1,-1),(1,1)
Polygon zoneEdge = new Polygon(
new Point(-1, 1),
new Point(1, 1),
new Point(-1, -1)
);
//bounding box of (-4, -16),(16,4)
Polygon zoneWithin = new Polygon(
new Point(-4, -10),
new Point(-3, 4),
new Point(14, 2),
new Point(16, -16));
//bounding box of (3,3)(9,9)
Polygon zoneEmpty = new Polygon(
new Point(3, 3),
new Point(3, 7),
new Point(6, 7),
new Point(6, 9),
new Point(9, 9),
new Point(9, 5),
new Point(6, 5),
new Point(6, 3));
List<Party> parties = repository.findByLocationWithin(zoneFalsePositive);
assertThat(parties.size())
.as("points outside a polygon but within bounding box shouldn't be considered within")
.isEqualTo(0);
parties = repository.findByLocationWithin(zoneEdge);
assertThat(parties.size())
.as("point on edge of a polygon shouldn't be considered within").isEqualTo(0);
parties = repository.findByLocationWithin(zoneWithin);
assertThat(parties.size()).isEqualTo(12); //all the parties except the special one at 100, 100
parties = repository.findByLocationWithin(zoneEmpty);
assertThat(parties.size()).isEqualTo(0);
}
@Test
public void testByLocationWithinTwoPoints() {
Point zone1LowerLeft = new Point(-10.5, -0.5);
Point zone1UpperRight = new Point(0.5, 10.5);
Point zone2LowerLeft = new Point(-4, -16);
Point zone2UpperRight = new Point(16, 4);
Point zoneEmptyLowerLeft = new Point(3, 3);
Point zoneEmptyUpperRight = new Point(9, 9);
List<Party> parties = repository.findByLocationWithin(zone1LowerLeft, zone1UpperRight);
assertThat(parties.size()).isEqualTo(1);
assertThat(parties.get(0).getKey()).isEqualTo("testparty-0");
parties = repository.findByLocationWithin(zone2LowerLeft, zone2UpperRight);
assertThat(parties.size()).isEqualTo(12); //all the parties except the special one at 100, 100
parties = repository.findByLocationWithin(zoneEmptyLowerLeft, zoneEmptyUpperRight);
assertThat(parties.size()).isEqualTo(0);
}
@Test
public void testPolygonAndArrayOfPointsProduceSameResult() {
Set<String> expectedKeys = new HashSet<String>();
expectedKeys.add("testparty-2");
expectedKeys.add("testparty-3");
expectedKeys.add("testparty-4");
expectedKeys.add("testparty-5");
//a zone that engulfs parties 2, 3, 4 and 5, in the shape of a downward arrow pointing right
Polygon zone = new Polygon(new Point(1, -2),
new Point(3, -1.5),
new Point(6, -4),
new Point(5.5, -5.5),
new Point(3, -5));
Point[] points = zone.getPoints().toArray(new Point[5]);
List<Party> fromZone = repository.findByLocationWithin(zone);
List<Party> fromPoints = repository.findByLocationWithin(points);
assertThat(fromZone.size()).isEqualTo(4);
assertThat(fromPoints).isEqualTo(fromZone);
Set<String> keys = new HashSet<String>();
for (Party party : fromZone) {
keys.add(party.getKey());
}
assertThat(keys).isEqualTo(expectedKeys);
}
@Test
public void testProvidingOnePointIsRejected() {
try {
repository.findByLocationWithin(new Point(0, 0));
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage())
.isEqualTo("Cannot compute a bounding box for within, 2 Point needed, missing parameter");
}
try {
repository.findByLocationWithin(new Point(0, 0), null);
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage())
.isEqualTo("Cannot compute a bounding box for within, 2 Point needed, got null");
}
}
@Test
public void testProvidingOneJsonArrayIsRejected() {
try {
List<Party> parties = repository.findByLocationWithin(JsonArray.from(0,0));
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage())
.isEqualTo("2 JsonArray required for within: startRange and endRange, missing parameter");
}
}
@Test(expected = CouchbaseQueryExecutionException.class)
public void testJsonArrayWithNonNumericalValueProducesServerSideError() {
repository.findByLocationWithin(JsonArray.from("toto", -2), JsonArray.from(4, 1));
}
@Test
public void testWithinJsonArrayRangesFiltersLocationAndAttendees() {
List<Party> parties = repository.findByLocationWithin(JsonArray.from(0, -4, 115), JsonArray.from(4, 1, 132));
assertThat(parties.size()).isEqualTo(2);
}
@Test
public void testDimensionalAnnotationCanBeUsedAsMeta() {
try {
//will trigger a specific message if parsed by SpatialViewQueryCreator
repository.findByLocationIsWithin(new Point(0,0), null);
fail("expected IllegalArgumentException from SpatialViewQueryCreator");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage())
.isEqualTo("Cannot compute a bounding box for within, 2 Point needed, got null");
}
//when it is correctly formed, it actually returns data
assertThat(repository
.findByLocationIsWithin(new Point(-10.5, -0.5), new Point(0.5, 10.5)).size())
.isEqualTo(1);
}
@Test
public void testFindWithinBoxCornerOrderDoesMatter() {
Point a = new Point(0, -4);
Point b = new Point(6, -2);
Box box1 = new Box(a, b);
Box box2 = new Box(b, a);
final List<Party> parties1 = repository.findByLocationWithin(box1);
final List<Party> parties2 = repository.findByLocationWithin(box2);
assertThat(parties1.size()).isEqualTo(3);
assertThat(parties2).isNotEqualTo(parties1);
}
}

View File

@@ -1,38 +0,0 @@
package org.springframework.data.couchbase.repository;
import com.couchbase.client.java.repository.annotation.Field;
import org.springframework.data.annotation.Id;
public class Item {
@Id
public String id;
@Field("desc")
public String description;
public Item(String id, String description) {
this.id = id;
this.description = description;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Item item = (Item) o;
if (!id.equals(item.id)) return false;
return !(description != null ? !description.equals(item.description) : item.description != null);
}
@Override
public int hashCode() {
int result = id.hashCode();
result = 31 * result + (description != null ? description.hashCode() : 0);
return result;
}
}

View File

@@ -1,12 +0,0 @@
package org.springframework.data.couchbase.repository;
import java.util.List;
import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed;
import org.springframework.data.repository.CrudRepository;
@N1qlPrimaryIndexed
public interface ItemRepository extends CrudRepository<Item, String> {
List<Object> findAllByDescriptionNotNull();
}

View File

@@ -1,269 +0,0 @@
/*
* Copyright 2013-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.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.springframework.data.couchbase.CouchbaseTestHelper.getRepositoryWithRetry;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataRetrievalFailureException;
;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory;
import org.springframework.data.couchbase.repository.support.IndexManager;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.model.MappingInstantiationException;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* This tests PaginAndSortingRepository features in the Couchbase connector.
*
* @author Simon Baslé
* @author Subhashni Balakrishnan
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
@TestExecutionListeners(PartyPopulatorListener.class)
public class N1qlCouchbaseRepositoryIntegrationTests {
@Autowired
private RepositoryOperationsMapping operationsMapping;
@Autowired
private IndexManager indexManager;
private PartyPagingRepository repository;
private PartyRepository partyRepository;
private ItemRepository itemRepository;
private final String KEY_PARTY = "Party1";
private final String KEY_ITEM = "Item1";
@Before
public void setup() throws Exception {
RepositoryFactorySupport factory = new CouchbaseRepositoryFactory(operationsMapping, indexManager);
repository = getRepositoryWithRetry(factory, PartyPagingRepository.class);
partyRepository = getRepositoryWithRetry(factory, PartyRepository.class);
itemRepository = getRepositoryWithRetry(factory, ItemRepository.class);
partyRepository.save(new Party(KEY_PARTY, "partyName", "MatchingDescription", null, 1, null));
itemRepository.save(new Item(KEY_ITEM, "MatchingDescription"));
}
@After
public void cleanUp() {
try { itemRepository.deleteById(KEY_ITEM); } catch (DataRetrievalFailureException e) {}
try { partyRepository.deleteById(KEY_PARTY); } catch (DataRetrievalFailureException e) {}
}
@Test
public void shouldBeThreadsafe() {
// This doesn't guarantee it, but we should catch most thread issues without
// taking too long here...
int runs = 50;
for (int i=0; i<runs; i++) {
doShouldBeThreadsafe();
}
}
public void doShouldBeThreadsafe() {
int threads = 50;
ExecutorService service = Executors.newFixedThreadPool(threads);
List<Callable<Boolean>> callables = new ArrayList<>();
for (int thread = 0; thread < threads; ++thread) {
final int counter = thread;
Callable<Boolean> booleanSupplier = () -> {
String expectedName = "party like it's 199" + counter%12;
String foundName = partyRepository.findByName(expectedName).get(0).getName();
return expectedName.equals(foundName); //should never get false
};
callables.add(booleanSupplier);
}
try {
List<Future<Boolean>> futures = service.invokeAll(callables);
service.shutdown();
service.awaitTermination(5, TimeUnit.SECONDS);
for (Future<Boolean> future: futures) {
assertThat(future.get()).isTrue();
}
} catch (InterruptedException | ExecutionException e) {
fail("Threads failed to run " + e.getMessage());
}
}
@Test
public void shouldFindAllWithSort() {
Iterable<Party> allByAttendanceDesc = repository.findAll(Sort.by(Sort.Direction.DESC, "attendees"));
long previousAttendance = Long.MAX_VALUE;
for (Party party : allByAttendanceDesc) {
assertThat(party.getAttendees() <= previousAttendance).isTrue();
previousAttendance = party.getAttendees();
}
assertThat(previousAttendance == Long.MAX_VALUE)
.as("Expected to find several parties").isFalse();
}
@Test
public void shouldSortOnRenamedFieldIfJsonNameIsProvidedInSort() {
Iterable<Party> parties = repository.findAll(Sort.by(Sort.Direction.DESC, "desc"));
String previousDesc = null;
for (Party party : parties) {
if (previousDesc != null) {
assertThat(party.getDescription().compareTo(previousDesc) <= 0).isTrue();
}
previousDesc = party.getDescription();
}
assertThat(previousDesc).as("Expected to find several parties").isNotNull();
}
@Test
public void shouldSortWithoutCaseSensitivity() {
Iterable<Party> parties = repository.findAll(Sort.by(new Sort.Order(Sort.Direction.DESC, "desc").ignoreCase()));
String previousDesc = null;
for (Party party : parties) {
if (previousDesc != null) {
assertThat(party.getDescription().compareToIgnoreCase(previousDesc) <= 0)
.isTrue();
}
previousDesc = party.getDescription();
}
assertThat(previousDesc).as("Expected to find several parties").isNotNull();
}
@Test
public void shouldPageThroughEntities() {
Pageable pageable = PageRequest.of(0, 8);
Page<Party> page1 = repository.findAll(pageable);
assertThat(page1.getTotalElements() >= 12)
.as("Query for parties should be atleast 12").isTrue();
assertThat(page1.getNumberOfElements()).isEqualTo(8);
}
@Test
public void shouldPageThroughSortedEntities() {
Pageable pageable = PageRequest.of(0, 8, Sort.Direction.DESC, "attendees");
Page<Party> page1 = repository.findAll(pageable);
assertThat(page1.getTotalElements() >= 12)
.as("Query for parties should be atleast 12").isTrue();
assertThat(page1.getNumberOfElements()).isEqualTo(8);
List<Party> parties = page1.getContent();
Long previousAttendees = null;
for (Party party : parties) {
if (previousAttendees != null) {
assertThat(party.getAttendees() <= previousAttendees).isTrue();
}
previousAttendees = party.getAttendees();
}
}
@Test
public void testWrapWhereCriteria() {
List<Party> partyList = partyRepository.findByDescriptionOrName("MatchingDescription", "partyName");
assertThat(partyList.size() == 1).isTrue();
}
@Test
public void shouldPageWithStringBasedQuery() {
Pageable pageable = PageRequest.of(0, 8, Sort.Direction.DESC, "attendees");
Page<Party> page1 = partyRepository.findPartiesWithAttendee(1, pageable);
assertThat(page1.getTotalElements() >= 12)
.as("Query for parties with attendees should be atleast 12").isTrue();
assertThat(page1.getNumberOfElements()).isEqualTo(8);
List<Party> parties = page1.getContent();
Long previousAttendees = null;
for (Party party : parties) {
if (previousAttendees != null) {
assertThat(party.getAttendees() <= previousAttendees).isTrue();
}
previousAttendees = party.getAttendees();
}
Page<Party> page2 = partyRepository.findPartiesWithAttendee(1, page1.nextPageable());
assertThat(page2.getNumberOfElements()).isEqualTo(8);
parties = page2.getContent();
for (Party party : parties) {
if (previousAttendees != null) {
assertThat(party.getAttendees() <= previousAttendees).isTrue();
}
previousAttendees = party.getAttendees();
}
}
//Fails on deserialization as a different entity item is also present
@Test(expected = MappingInstantiationException.class)
public void shouldFailWithMissingFilterStringBasedQuery() {
Sort sort = Sort.by(Sort.Direction.DESC, "attendees");
partyRepository.findParties(sort);
}
@Test
public void testDeleteQuery() {
partyRepository.save(new Party("testDeleteQuery", "delete", "delete", null, 0, null));
List<Party> partyList = partyRepository.removeByDescriptionOrName("delete", "delete");
assertThat(partyList.size() == 1).isTrue();
}
@Test
public void testSpelDateConvertion() {
final String key = "testSpelDateConvertion";
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(2018, Calendar.SEPTEMBER, 10);
Date date = cal.getTime();
partyRepository.save(new Party(key, "", "", date, 0, null));
List<Party> partyList = partyRepository.getByEventDate(date);
assertThat(partyList.size() == 1).isTrue();
assertThat(key).as("Key mismatch").isEqualTo(partyList.get(0).getKey());
}
@Test
public void testN1qlQueryWithInvalidValue() {
partyRepository.save(new Party("testN1qlQueryWithInvalidValue", "", "testN1qlQueryWithInvalidValue", null, 0, null));
final String description = "testN1qlQueryWithInvalidValue* OR `description` LIKE \"\"";
List<Party> partyList = partyRepository.findByDescriptionStartingWith(description);
assertThat(partyList.size() == 0).isTrue();
}
}

View File

@@ -1,166 +0,0 @@
/*
* Copyright 2013-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.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.springframework.data.couchbase.CouchbaseTestHelper.getRepositoryWithRetry;
import java.util.Date;
import java.util.List;
import com.couchbase.client.java.Bucket;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataRetrievalFailureException;
;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.data.couchbase.core.CouchbaseQueryExecutionException;
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory;
import org.springframework.data.couchbase.repository.support.IndexManager;
import org.springframework.data.geo.Point;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Simon Baslé
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
public class N1qlCrudRepositoryIntegrationTests {
@Autowired
private Bucket client;
@Autowired
private RepositoryOperationsMapping operationsMapping;
@Autowired
private IndexManager indexManager;
private PartyRepository partyRepository;
private ItemRepository itemRepository;
private static final String KEY_ITEM = "itemNotParty";
private static final String KEY_PARTY = "partyNotItem";
private static final String KEY_PARTY_KEYWORD = "partyHasKeyword";
private static final Item item = new Item(KEY_ITEM, "short description");
private static final Party party = new Party(KEY_PARTY, "partyName", "short description", new Date(), 120, new Point(500, 500));
@Before
public void setup() throws Exception {
CouchbaseRepositoryFactory factory = new CouchbaseRepositoryFactory(operationsMapping, indexManager);
partyRepository = getRepositoryWithRetry(factory, PartyRepository.class);
itemRepository = getRepositoryWithRetry(factory, ItemRepository.class);
itemRepository.save(item);
partyRepository.save(party);
}
@After
public void cleanUp() {
try { itemRepository.deleteById(KEY_ITEM); } catch (DataRetrievalFailureException e) {}
try { partyRepository.deleteById(KEY_PARTY); } catch (DataRetrievalFailureException e) {}
try { partyRepository.deleteById(KEY_PARTY_KEYWORD); } catch (DataRetrievalFailureException e) {}
}
@Test
public void shouldDistinguishBetweenItemsAndParties() {
List<Object> items = itemRepository.findAllByDescriptionNotNull();
List<Object> parties = partyRepository.findAllByDescriptionNotNull();
assertThat(items.contains(item)).isTrue();
assertThat(parties.contains(party)).isTrue();
assertThat(items.contains(party)).isFalse();
assertThat(parties.contains(item)).isFalse();
}
@Test
public void shouldSaveObjectWithN1qlKeywordField() {
Party partyHasKeyword = new Party(KEY_PARTY_KEYWORD, "party", "desc is a N1QL keyword", new Date(), 40, new Point(500, 500));
partyRepository.save(partyHasKeyword);
List<Object> parties = partyRepository.findAllByDescriptionNotNull();
assertThat(client.exists(KEY_PARTY_KEYWORD)).isTrue();
assertThat(parties.contains(partyHasKeyword)).isTrue();
for (Object o : parties) {
if (!(o instanceof Party)) {
fail("expected only Party objects");
}
}
}
@Test
public void shouldGenerateCountProjection() {
Party partyHasKeyword = new Party(KEY_PARTY_KEYWORD, "party", null, new Date(), 40, new Point(500, 500));
partyRepository.save(partyHasKeyword);
long countTotal = partyRepository.count();
long countCustom = partyRepository.countAllByDescriptionNotNull();
assertThat(countCustom).isEqualTo(countTotal - 1);
}
@Test
public void shouldCountWhenReturningLongAndUsingStringSelectFromSpEL() {
Party partyHasKeyword = new Party(KEY_PARTY_KEYWORD, "party", "second party", new Date(), 40, new Point(500, 500));
partyRepository.save(partyHasKeyword);
long countTotal = partyRepository.count();
long countCustom = partyRepository.countCustom();
assertThat(countCustom).isEqualTo(countTotal);
}
@Test
public void shouldCustomCountWhenReturningLongAndUsingStringWithoutSpEL() {
Party partyHasKeyword = new Party(KEY_PARTY_KEYWORD, "party", "second party", new Date(), 40, new Point(500, 500));
partyRepository.save(partyHasKeyword);
long countTotal = partyRepository.count();
long countCustom = partyRepository.countCustomPlusFive();
assertThat(countCustom).isEqualTo(countTotal + 5);
}
@Test(expected = CouchbaseQueryExecutionException.class)
public void shouldFailConversionWithStringReturnType() {
Party partyHasKeyword = new Party(KEY_PARTY_KEYWORD, "party", "desc is a N1QL keyword", new Date(), 40, new Point(500, 500));
partyRepository.save(partyHasKeyword);
String someString = partyRepository.findSomeString();
}
@Test
public void shouldDoNumericProjectionWithStringBasedQuery() {
Party partyHasKeyword = new Party(KEY_PARTY_KEYWORD, "party", "desc is a N1QL keyword", new Date(), 4000000, new Point(500, 500));
partyRepository.save(partyHasKeyword);
long max = partyRepository.findMaxAttendees();
assertThat(max).isEqualTo(4000000);
}
@Test
public void shouldDoBooleanProjectionWithStringBasedQuery() {
boolean someBoolean = partyRepository.justABoolean();
assertThat(someBoolean).isEqualTo(true);
}
}

View File

@@ -1,141 +0,0 @@
/*
* Copyright 2013-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.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.springframework.data.couchbase.CouchbaseTestHelper.getRepositoryWithRetry;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory;
import org.springframework.data.couchbase.repository.support.IndexManager;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
/**
* @author Simon Baslé
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
@TestExecutionListeners(PartyPopulatorListener.class)
public class N1qlPlaceholderIntegrationTests {
@Autowired
private RepositoryOperationsMapping operationsMapping;
@Autowired
private IndexManager indexManager;
private CouchbaseRepositoryFactory factory;
private PartyRepository partyRepository;
@Before
public void setup() throws Exception {
factory = new CouchbaseRepositoryFactory(operationsMapping, indexManager);
partyRepository = getRepositoryWithRetry(factory, PartyRepository.class);
}
@Test
public void shouldFindUsingNamedParameters() {
String included = "90";
String excluded = "New Year";
int min = 200;
List<Party> result = partyRepository.findAllWithNamedParams(excluded, included, min);
assertThat(result.size()).isEqualTo(2);
for (Party party : result) {
assertThat(party.getDescription().contains(included)).isTrue();
assertThat(party.getDescription().contains(excluded)).isFalse();
assertThat(party.getAttendees() >= min).isTrue();
}
}
@Test
public void shouldFindUsingPositionalParameters() {
String included = "90";
String excluded = "New Year";
int min = 200;
List<Party> result = partyRepository.findAllWithPositionalParams(excluded, included, min);
assertThat(result.size()).isEqualTo(2);
for (Party party : result) {
assertThat(party.getDescription().contains(included)).isTrue();
assertThat(party.getDescription().contains(excluded)).isFalse();
assertThat(party.getAttendees() >= min).isTrue();
}
}
@Test
public void shouldIgnoreQuotedNamedParamsAndParamAnnotationIfPosUsed() {
String included = "90";
String excluded = "New Year";
int min = 200;
List<Party> result = partyRepository.findAllWithPositionalParamsAndQuotedNamedParams(excluded, included, min);
assertThat(result.size()).isEqualTo(2);
for (Party party : result) {
assertThat(party.getDescription().contains(included)).isTrue();
assertThat(party.getDescription().contains(excluded)).isFalse();
assertThat(party.getAttendees() >= min).isTrue();
}
}
private interface BadRepository extends CrudRepository<Party, String> {
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $2 || '%' AND attendees >= $3" +
" AND `desc` NOT LIKE '%' || $included || '%'")
List<Party> findAllWithMixedParamsInQuery(@Param("excluded") String ex, @Param("included") String inc, @Param("min") long min);
}
@Test
public void shouldFailUsingMixedParameters() {
try {
factory.getRepository(BadRepository.class);
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage()).as(e.toString())
.isEqualTo("Using both named (1) and positional (2) placeholders is not supported, please choose " +
"one over the other in findAllWithMixedParamsInQuery");
}
}
@Test
public void deleteQueryTest() {
String included = "90";
String excluded = "New Year";
int max = 200;
List<Party> result = partyRepository.removeWithPositionalParams(excluded, included, max);
assertThat(result.size()).isEqualTo(10);
for (Party party : result) {
assertThat(party.getDescription().contains(included)).isTrue();
assertThat(party.getDescription().contains(excluded)).isFalse();
assertThat(party.getAttendees() < max).isTrue();
}
}
}

View File

@@ -1,92 +0,0 @@
package org.springframework.data.couchbase.repository;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.data.couchbase.CouchbaseTestHelper.getRepositoryWithRetry;
import java.util.ArrayList;
import java.util.List;
import com.couchbase.client.java.Bucket;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory;
import org.springframework.data.couchbase.repository.support.IndexManager;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
@TestExecutionListeners(SimpleCouchbaseRepositoryListener.class)
public class PageAndSliceIntegrationTests {
@Autowired
private Bucket client;
@Autowired
private RepositoryOperationsMapping operationsMapping;
@Autowired
private IndexManager indexManager;
private UserRepository repository;
@Before
public void setup() throws Exception {
RepositoryFactorySupport factory = new CouchbaseRepositoryFactory(operationsMapping, indexManager);
repository = getRepositoryWithRetry(factory, UserRepository.class);
}
@Test
public void shouldPageThroughResults() {
Page<User> page1 = repository.findByAgeGreaterThan(9, PageRequest.of(0, 40)); //there are 90 matching users
Page<User> page2 = repository.findByAgeGreaterThan(9, page1.nextPageable());
Page<User> page3 = repository.findByAgeGreaterThan(9, page2.nextPageable());
assertThat(page1.getTotalElements()).isEqualTo(90);
assertThat(page1.getTotalPages()).isEqualTo(3);
assertThat(page1.hasContent()).isTrue();
assertThat(page1.hasNext()).isTrue();
assertThat(page1.getNumberOfElements()).isEqualTo(40);
assertThat(page2.hasContent()).isTrue();
assertThat(page2.hasNext()).isTrue();
assertThat(page2.getNumberOfElements()).isEqualTo(40);
assertThat(page3.hasContent()).isTrue();
assertThat(page3.hasNext()).isFalse();
assertThat(page3.getNumberOfElements()).isEqualTo(10);
}
@Test(expected = UnsupportedOperationException.class)
public void shouldThrowWhenPageableIsNullInPageQuery() {
repository.findByAgeGreaterThan(9, null);
}
@Test
public void shouldSliceThroughResults() {
int count = 0;
List<User> allMatching = new ArrayList<User>(10);
Slice<User> slice = repository.findByAgeLessThan(9, PageRequest.of(0, 3)); //9 matching users (ages 0-8)
allMatching.addAll(slice.getContent());
while(slice.hasNext()) {
slice = repository.findByAgeLessThan(9, slice.nextPageable());
allMatching.addAll(slice.getContent());
assertThat(slice.getContent().size()).isEqualTo(3);
}
assertThat(allMatching.size()).isEqualTo(9);
}
@Test(expected = UnsupportedOperationException.class)
public void shouldThrowWhenPageableIsNullSliceQuery() {
repository.findByAgeLessThan(9, null);
}
}

View File

@@ -1,88 +0,0 @@
package org.springframework.data.couchbase.repository;
import java.util.Date;
import org.springframework.data.annotation.Id;
import org.springframework.data.geo.Point;
import com.couchbase.client.java.repository.annotation.Field;
/**
* An entity used to test conversion of parameters in query derivations.
*
* @author Simon Baslé
*/
public class Party {
@Id
private final String key;
private final String name;
@Field("desc")
private final String description;
private final Date eventDate;
private final long attendees;
private final Point location;
public Party(String key, String name, String description, Date eventDate, long attendees, Point location) {
this.key = key;
this.name = name;
this.description = description;
this.eventDate = eventDate;
this.attendees = attendees;
this.location = location;
}
public String getKey() {
return key;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public Date getEventDate() {
return eventDate;
}
public long getAttendees() {
return attendees;
}
public Point getLocation() {
return location;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Party party = (Party) o;
return key.equals(party.key);
}
@Override
public int hashCode() {
return key.hashCode();
}
@Override
public String toString() {
return "Party{" +
"name='" + name + '\'' +
", eventDate=" + eventDate +
", location=" + location +
'}';
}
}

View File

@@ -1,4 +0,0 @@
package org.springframework.data.couchbase.repository;
public interface PartyPagingRepository extends CouchbasePagingAndSortingRepository<Party, String> {
}

View File

@@ -1,87 +0,0 @@
package org.springframework.data.couchbase.repository;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.PersistTo;
import com.couchbase.client.java.ReplicateTo;
import com.couchbase.client.java.cluster.ClusterInfo;
import com.couchbase.client.java.view.DefaultView;
import com.couchbase.client.java.view.DesignDocument;
import com.couchbase.client.java.view.SpatialView;
import com.couchbase.client.java.view.View;
import org.springframework.data.couchbase.config.BeanNames;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.geo.Point;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
/**
* @author Simon Baslé
*/
public class PartyPopulatorListener extends DependencyInjectionTestExecutionListener {
@Override
public void beforeTestClass(final TestContext testContext) throws Exception {
Bucket client = (Bucket) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_BUCKET);
ClusterInfo clusterInfo = (ClusterInfo) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_CLUSTER_INFO);
populateTestData(client, clusterInfo);
createAndWaitForDesignDocs(client);
}
private void populateTestData(Bucket client, ClusterInfo clusterInfo) {
CouchbaseTemplate template = new CouchbaseTemplate(clusterInfo, client);
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(Calendar.YEAR, 2015);
cal.set(Calendar.DAY_OF_MONTH, 10);
cal.set(Calendar.MONTH, Calendar.JANUARY);
for (int i = 0; i < 12; i++) {
Party p = new Party("testparty-" + i, "party like it's 199" + i,
"An awesome party, 90's themed, every 10 of the month",
cal.getTime(), 100 + i * 10,
new Point(i, -i));
template.save(p, PersistTo.MASTER, ReplicateTo.NONE);
cal.roll(Calendar.MONTH, true);
}
cal.clear();
cal.set(Calendar.YEAR, 1990);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 01);
template.save(new Party("aTestParty", "New Year's Eve 90", "Happy New Year", cal.getTime(), 1230000, new Point(100, 100)));
template.save(new Party("lowercaseParty", "lowercase party", "lowercase party", cal.getTime(), 1000, new Point(100, 100)));
template.save(new Party("uppercaseParty", "Uppercase party", "Uppercase party", cal.getTime(), 1000, new Point(100, 100)));
}
private void createAndWaitForDesignDocs(Bucket client) {
//standard views
List<View> views = new ArrayList<View>();
String mapFunction = "function (doc, meta) { if(doc._class == \"" + Party.class.getName() + "\") " +
"{ emit(doc.eventDate, null); } }";
views.add(DefaultView.create("byDate", mapFunction, "_count"));
//create the view design document
DesignDocument designDoc = DesignDocument.create("party", views);
client.bucketManager().upsertDesignDocument(designDoc);
//geo views
List<View> geoViews = new ArrayList<View>();
mapFunction = "function (doc, meta) { if(doc._class == \"" + Party.class.getName() + "\") " +
"{ emit([doc.location.x, doc.location.y], null); } }";
geoViews.add(SpatialView.create("byLocation", mapFunction));
mapFunction = "function (doc, meta) { if(doc._class == \"" + Party.class.getName() + "\") " +
"{ emit([doc.location.x, doc.location.y, doc.attendees], null); } }";
geoViews.add(SpatialView.create("byLocationAndAttendees", mapFunction));
//create the geo views design document
DesignDocument geoDesignDoc = DesignDocument.create("partyGeo", geoViews);
client.bucketManager().upsertDesignDocument(geoDesignDoc);
}
}

View File

@@ -1,102 +0,0 @@
/*
* Copyright 2017-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 com.couchbase.client.java.query.consistency.ScanConsistency;
import java.util.Date;
import java.util.List;
import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed;
import org.springframework.data.couchbase.core.query.N1qlSecondaryIndexed;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.query.View;
import org.springframework.data.couchbase.core.query.ViewIndexed;
import org.springframework.data.couchbase.core.query.WithConsistency;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.Param;
/**
* @author Simon Baslé
* @author Subhashni Balakrishnan
*/
@ViewIndexed(designDoc = "party", viewName = "all")
@N1qlPrimaryIndexed
@N1qlSecondaryIndexed(indexName = "party")
public interface PartyRepository extends CouchbaseRepository<Party, String> {
List<Party> findByAttendeesGreaterThanEqual(int minAttendees);
List<Party> findByName(String name);
List<Party> findByEventDateIs(Date targetDate);
@View(designDocument = "party", viewName = "byDate")
List<Party> findFirst3ByEventDateGreaterThanEqual(Date targetDate);
List<Object> findAllByDescriptionNotNull();
long countAllByDescriptionNotNull();
@WithConsistency(ScanConsistency.NOT_BOUNDED)
@Query("SELECT MAX(attendees) FROM #{#n1ql.bucket} WHERE #{#n1ql.filter}")
long findMaxAttendees();
@Query("SELECT `desc` FROM #{#n1ql.bucket} WHERE #{#n1ql.filter}")
String findSomeString();
@Query("SELECT count(*) + 5 FROM #{#n1ql.bucket} WHERE #{#n1ql.filter}")
long countCustomPlusFive();
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter}")
long countCustom();
@Query("SELECT 1 = 1")
boolean justABoolean();
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND `attendees` >= $1")
Page<Party> findPartiesWithAttendee(int count, Pageable pageable);
@Query("#{#n1ql.selectEntity}")
List<Party> findParties(Sort sort);
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $included || '%' AND attendees >= $min" +
" AND `desc` NOT LIKE '%' || $excluded || '%'")
List<Party> findAllWithNamedParams(@Param("excluded") String ex, @Param("included") String inc, @Param("min") long minimumAttendees);
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $2 || '%' AND attendees >= $3" +
" AND `desc` NOT LIKE '%' || $1 || '%'")
List<Party> findAllWithPositionalParams(String ex, String inc, long minimumAttendees);
@Query("#{#n1ql.delete} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $2 || '%' AND attendees < $3" +
" AND `desc` NOT LIKE '%' || $1 || '%' #{#n1ql.returning}")
List<Party> removeWithPositionalParams(String ex, String inc, long minimumAttendees);
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $2 || '%' AND attendees >= $3" +
" AND `desc` NOT LIKE '%' || $1 || '%' AND `desc` != \"this is \\\"$excluded\\\"\"")
List<Party> findAllWithPositionalParamsAndQuotedNamedParams(@Param("excluded") String ex, @Param("included") String inc, @Param("min") long min);
List<Party> findByDescriptionOrName(String description, String name);
List<Party> removeByDescriptionOrName(String description, String name);
@Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and eventDate = $1")
List<Party> getByEventDate(Date eventDate);
List<Party> findByDescriptionStartingWith(String description);
}

View File

@@ -1,93 +0,0 @@
package org.springframework.data.couchbase.repository;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.document.JsonDocument;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory;
import org.springframework.data.couchbase.repository.support.IndexManager;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Simon Baslé
* @author Mark Paluch
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
@TestExecutionListeners(PartyPopulatorListener.class)
public class QueryDerivationConversionIntegrationTests {
@Autowired
private Bucket client;
@Autowired
private RepositoryOperationsMapping operationsMapping;
@Autowired
private IndexManager indexManager;
private PartyRepository repository;
@Before
public void setup() throws Exception {
RepositoryFactorySupport factory = new CouchbaseRepositoryFactory(operationsMapping, indexManager);
repository = factory.getRepository(PartyRepository.class);
}
@Test
public void testConvertsDateParameterInN1qlQuery() {
Optional<Party> partyApril = repository.findById("testparty-3");
assertThat(partyApril.isPresent()).isTrue();
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(2015, Calendar.APRIL, 10);
Date find = cal.getTime();
List<Party> parties = repository.findByEventDateIs(find);
assertThat(parties).isNotNull();
assertThat(parties.size()).isEqualTo(1);
assertThat(parties.get(0).getEventDate()).isEqualTo(find);
JsonDocument doc = client.get(parties.get(0).getKey());
assertThat(doc.content().get("eventDate")).isEqualTo(find.getTime());
}
@Test
public void testAcceptLongParameterInN1qlQuery() {
List<Party> newYear90 = repository.findByAttendeesGreaterThanEqual(1200000);
assertThat(newYear90).isNotNull();
assertThat(newYear90.size()).isEqualTo(1);
assertThat(newYear90.get(0).getKey()).isEqualTo("aTestParty");
}
@Test
public void testConvertDateParameterInViewQuery() {
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(2015, Calendar.AUGUST, 12);
Date find = cal.getTime();
List<Party> afterSummerParties = repository.findFirst3ByEventDateGreaterThanEqual(find);
assertThat(afterSummerParties).isNotNull();
assertThat(afterSummerParties.size()).isEqualTo(3);
for (Party afterSummerParty : afterSummerParties) {
assert(afterSummerParty.getEventDate().after(find));
}
}
}

View File

@@ -1,157 +0,0 @@
/*
* Copyright 2017-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.assertj.core.api.Assertions.assertThat;
import static org.springframework.data.couchbase.CouchbaseTestHelper.getRepositoryWithRetry;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.ReactiveIntegrationTestApplicationConfig;
import org.springframework.data.couchbase.repository.config.ReactiveRepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.support.IndexManager;
import org.springframework.data.couchbase.repository.support.ReactiveCouchbaseRepositoryFactory;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.core.support.ReactiveRepositoryFactorySupport;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
/**
* This tests ReactiveSortingRepository features in the Couchbase connector.
*
* @author Subhashni Balakrishnan
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = ReactiveIntegrationTestApplicationConfig.class)
@TestExecutionListeners(PartyPopulatorListener.class)
public class ReactiveN1qlCouchbaseRepositoryIntegrationTests {
@Autowired
private ReactiveRepositoryOperationsMapping operationsMapping;
@Autowired
private IndexManager indexManager;
private ReactivePartySortingRepository repository;
private ReactivePartyRepository partyRepository;
private ItemRepository itemRepository;
private final String KEY_PARTY = "ReactiveParty1";
private final String KEY_ITEM = "ReactiveItem1";
@Before
public void setup() throws Exception {
ReactiveRepositoryFactorySupport factory = new ReactiveCouchbaseRepositoryFactory(operationsMapping, indexManager);
repository = getRepositoryWithRetry(factory, ReactivePartySortingRepository.class);
partyRepository = getRepositoryWithRetry(factory, ReactivePartyRepository.class);
itemRepository = getRepositoryWithRetry(factory, ItemRepository.class);
}
@After
public void cleanUp() {
try { itemRepository.deleteById(KEY_ITEM); } catch (DataRetrievalFailureException e) {}
try { partyRepository.deleteById(KEY_PARTY); } catch (DataRetrievalFailureException e) {}
}
@Test
public void shouldFindAllWithSort() {
Iterable<Party> allByAttendanceDesc = repository.findAll(Sort.by(Sort.Direction.DESC, "attendees")).collectList().block();
long previousAttendance = Long.MAX_VALUE;
for (Party party : allByAttendanceDesc) {
assertThat(party.getAttendees() <= previousAttendance).isTrue();
previousAttendance = party.getAttendees();
}
assertThat(previousAttendance == Long.MAX_VALUE)
.as("Expected to find several parties").isFalse();
}
@Test
public void shouldSortOnRenamedFieldIfJsonNameIsProvidedInSort() {
Iterable<Party> parties = repository.findAll(Sort.by(Sort.Direction.DESC, "desc")).collectList().block();
String previousDesc = null;
for (Party party : parties) {
if (previousDesc != null) {
assertThat(party.getDescription().compareTo(previousDesc) <= 0).isTrue();
}
previousDesc = party.getDescription();
}
assertThat(previousDesc).as("Expected to find several parties").isNotNull();
}
@Test
public void shouldSortWithoutCaseSensitivity() {
Iterable<Party> parties = repository.findAll(Sort.by(new Sort.Order(Sort.Direction.DESC, "desc").ignoreCase())).collectList().block();
String previousDesc = null;
for(Party party : parties) {
if (previousDesc != null) {
assertThat(party.getDescription().compareToIgnoreCase(previousDesc) <= 0)
.isTrue();
}
previousDesc = party.getDescription();
}
assertThat(previousDesc).as("Expected to find several parties").isNotNull();
}
@Test
public void testCustomSpelCountQuery() {
long count = partyRepository.countCustom().block();
assertThat(count >= 12).as("Count query for parties should be atleast 12")
.isTrue();
}
@Test
public void testPartTreeQuery() {
long count = partyRepository.countAllByDescriptionNotNull().block();
assertThat(count >= 12)
.as("Count query for parties with description not null should be atleast 12")
.isTrue();
}
@Test
public void testSpelDateConvertion() {
final String key = "testReactiveSpelDateConvertion";
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(2018, Calendar.SEPTEMBER, 15);
Date date = cal.getTime();
partyRepository.save(new Party(key, "", "", date, 0, null)).block();
List<Party> partyList = partyRepository.getByEventDate(date).collectList().block();
assertThat(partyList.size() == 1).isTrue();
assertThat(key).as("Key mismatch").isEqualTo(partyList.get(0).getKey());
}
@Test
public void testN1qlQueryWithInvalidValue() {
partyRepository.save(new Party("testReactiveN1qlQueryWithInvalidValue", "", "testReactiveN1qlQueryWithInvalidValue", null, 0, null));
final String description = "testReactiveN1qlQueryWithInvalidValue* OR `description` LIKE \"\"";
List<Party> partyList = partyRepository.findByDescriptionStartingWith(description).collectList().block();
assertThat(partyList.size() == 0).isTrue();
}
}

View File

@@ -1,64 +0,0 @@
package org.springframework.data.couchbase.repository;
import java.util.Date;
import org.springframework.data.couchbase.core.query.N1qlSecondaryIndexed;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.query.View;
import org.springframework.data.couchbase.core.query.ViewIndexed;
import org.springframework.data.repository.query.Param;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* @author Subhashni Balakrishnan
*/
@ViewIndexed(designDoc = "reactiveParty", viewName = "all")
@N1qlSecondaryIndexed(indexName = "reactiveParty")
public interface ReactivePartyRepository extends ReactiveCouchbaseRepository<Party, String> {
Flux<Party> findByAttendeesGreaterThanEqual(int minAttendees);
Flux<Party> findByEventDateIs(Date targetDate);
@View(designDocument = "reactiveParty", viewName = "byDate")
Flux<Party> findFirst3ByEventDateGreaterThanEqual(Date targetDate);
Flux<Object> findAllByDescriptionNotNull();
Mono<Long> countAllByDescriptionNotNull();
@Query("SELECT MAX(attendees) FROM #{#n1ql.bucket} WHERE #{#n1ql.filter}")
Mono<Long> findMaxAttendees();
@Query("SELECT `desc` FROM #{#n1ql.bucket} WHERE #{#n1ql.filter}")
Mono<String> findSomeString();
@Query("SELECT count(*) + 5 FROM #{#n1ql.bucket} WHERE #{#n1ql.filter}")
Mono<Long> countCustomPlusFive();
@Query("SELECT count(*) FROM #{#n1ql.bucket} WHERE #{#n1ql.filter}")
Mono<Long> countCustom();
@Query("SELECT 1 = 1")
Mono<Boolean> justABoolean();
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $included || '%' AND attendees >= $min" +
" AND `desc` NOT LIKE '%' || $excluded || '%'")
Flux<Party> findAllWithNamedParams(@Param("excluded") String ex, @Param("included") String inc, @Param("min") long minimumAttendees);
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $2 || '%' AND attendees >= $3" +
" AND `desc` NOT LIKE '%' || $1 || '%'")
Flux<Party> findAllWithPositionalParams(String ex, String inc, long minimumAttendees);
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $2 || '%' AND attendees >= $3" +
" AND `desc` NOT LIKE '%' || $1 || '%' AND `desc` != \"this is \\\"$excluded\\\"\"")
Flux<Party> findAllWithPositionalParamsAndQuotedNamedParams(@Param("excluded") String ex, @Param("included") String inc, @Param("min") long min);
Flux<Party> findByDescriptionOrName(String description, String name);
@Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and eventDate = $1")
Flux<Party> getByEventDate(Date eventDate);
Flux<Party> findByDescriptionStartingWith(String description);
}

View File

@@ -1,9 +0,0 @@
package org.springframework.data.couchbase.repository;
/**
* @author Subhashni Balakrishnan
*/
public interface ReactivePartySortingRepository extends ReactiveCouchbaseSortingRepository<Party, String> {
}

View File

@@ -1,38 +0,0 @@
package org.springframework.data.couchbase.repository;
import com.couchbase.client.java.repository.annotation.Field;
import com.couchbase.client.java.repository.annotation.Id;
import org.springframework.data.couchbase.core.mapping.id.GeneratedValue;
import org.springframework.data.couchbase.core.mapping.id.GenerationStrategy;
/**
* @author David Kelly
*/
public class ReactivePlace {
@Id
@GeneratedValue(strategy= GenerationStrategy.UNIQUE)
public String id;
@Field("name")
public String name;
public String getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setId(String id) {
this.id = id;
}
public ReactivePlace(String name) {
this.name = name;
}
}

View File

@@ -1,57 +0,0 @@
package org.springframework.data.couchbase.repository;
import com.couchbase.client.java.Bucket;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.ReactiveIntegrationTestApplicationConfig;
import org.springframework.data.couchbase.repository.config.ReactiveRepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.support.IndexManager;
import org.springframework.data.couchbase.repository.support.ReactiveCouchbaseRepositoryFactory;
import org.springframework.data.repository.core.support.ReactiveRepositoryFactorySupport;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import static junit.framework.TestCase.assertNull;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.data.couchbase.CouchbaseTestHelper.getRepositoryWithRetry;
/**
* @author David Kelly
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = ReactiveIntegrationTestApplicationConfig.class)
@TestExecutionListeners(SimpleReactiveCouchbaseRepositoryListener.class)
public class ReactivePlaceIntegrationTests {
@Rule
public TestName testName = new TestName();
@Autowired
private Bucket client;
@Autowired
private ReactiveRepositoryOperationsMapping operationsMapping;
@Autowired
private IndexManager indexManager;
private ReactivePlaceRepository repository;
@Before
public void setup() throws Exception {
ReactiveRepositoryFactorySupport factory = new ReactiveCouchbaseRepositoryFactory(operationsMapping, indexManager);
repository = getRepositoryWithRetry(factory, ReactivePlaceRepository.class);
}
@Test
public void shouldReturnGeneratedId() {
ReactivePlace place = new ReactivePlace("somePlace");
assertNull(place.getId());
ReactivePlace returned = repository.save(place).block();
assertThat(returned.getId()).isNotNull();
}
}

View File

@@ -1,20 +0,0 @@
package org.springframework.data.couchbase.repository;
import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.query.ViewIndexed;
import reactor.core.publisher.Flux;
import java.util.List;
/**
* @author David Kelly
*/
@N1qlPrimaryIndexed
public interface ReactivePlaceRepository extends ReactiveCouchbaseRepository<ReactivePlace, String> {
@Query("#{#n1ql.selectEntity} WHERE name = $1")
Flux<ReactivePlace> findByName(String name);
}

View File

@@ -1,67 +0,0 @@
/*
* Copyright 2017-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 org.springframework.data.annotation.Id;
/**
* @author Subhashni Balakrishnan
*/
public class ReactiveUser {
@Id
private final String key;
private final String username;
private final int age;
public ReactiveUser(String key, String username, int age) {
this.key = key;
this.username = username;
this.age = age;
}
public String getUsername() {
return username;
}
public String getKey() {
return key;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return this.key;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ReactiveUser user = (ReactiveUser) o;
return key.equals(user.key);
}
@Override
public int hashCode() {
return key.hashCode();
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2017-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 com.couchbase.client.java.query.consistency.ScanConsistency;
import java.util.List;
import com.couchbase.client.java.view.ViewQuery;
import org.springframework.data.couchbase.core.query.N1qlSecondaryIndexed;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.query.View;
import org.springframework.data.couchbase.core.query.ViewIndexed;
import org.springframework.data.couchbase.core.query.WithConsistency;
import reactor.core.publisher.Flux;
/**
* @author Subhashni Balakrishnan
*/
@ViewIndexed(designDoc = "reactiveUser", viewName = "all")
@N1qlSecondaryIndexed(indexName = "reactiveUser")
public interface ReactiveUserRepository extends ReactiveCouchbaseRepository<ReactiveUser, String> {
@View(designDocument = "user", viewName = "all")
Flux<ReactiveUser> customViewQuery(ViewQuery query);
@Query("#{#n1ql.selectEntity} WHERE username = $1 and #{#n1ql.filter}")
Flux<ReactiveUser> findByUsername(String username);
@Query("SELECT * FROM #{#n1ql.bucket} WHERE username = $1 and #{#n1ql.filter}")
Flux<ReactiveUser> findByUsernameBadSelect(String username);
@Query("#{#n1ql.selectEntity} WHERE username LIKE '%-#{3 + 1}' and #{#n1ql.filter}")
Flux<ReactiveUser> findByUsernameWithSpelAndPlaceholder();
@Query
Flux<ReactiveUser> findByUsernameRegexAndUsernameIn(String regex, List<String> sample);
Flux<ReactiveUser> findByUsernameContains(String contains);
Flux<ReactiveUser> findByUsernameNear(String place);//this is to check that there's a N1QL derivation AND it fails
}

View File

@@ -1,194 +0,0 @@
package org.springframework.data.couchbase.repository;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.springframework.data.domain.Sort.Direction;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.query.Consistency;
import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation;
import org.springframework.data.couchbase.repository.support.N1qlCouchbaseRepository;
import org.springframework.data.couchbase.repository.support.ViewMetadataProvider;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.error.DocumentDoesNotExistException;
import com.couchbase.client.java.query.N1qlQuery;
import com.couchbase.client.java.view.ViewQuery;
import com.couchbase.client.java.view.ViewResult;
import com.couchbase.client.java.view.ViewRow;
public class RepositoryIndexUsageTest {
private static final org.springframework.data.couchbase.core.query.Consistency CONSISTENCY = Consistency.STRONGLY_CONSISTENT;
private CouchbaseOperations couchbaseOperations;
private N1qlCouchbaseRepository<String, String> repository;
@Before
public void initMocks() {
ViewRow mockCountRow1 = mock(ViewRow.class);
when(mockCountRow1.value()).thenReturn("100");
when(mockCountRow1.id()).thenReturn("id1");
ViewRow mockCountRow2 = mock(ViewRow.class);
when(mockCountRow2.value()).thenReturn("200");
when(mockCountRow2.id()).thenReturn("id2");
List<ViewRow> allCountRows = Arrays.asList(mockCountRow1, mockCountRow2);
ViewResult mockCountResult = mock(ViewResult.class);
when(mockCountResult.iterator()).thenReturn(allCountRows.iterator());
Bucket mockBucket = mock(Bucket.class);
when(mockBucket.name()).thenReturn("mockBucket");
CouchbaseConverter mockConverter = mock(CouchbaseConverter.class);
when(mockConverter.getTypeKey()).thenReturn("mockType");
when(mockConverter.convertForWriteIfNeeded(any(Object.class))).thenAnswer(
invocation -> invocation.getArgument(0));
couchbaseOperations = mock(CouchbaseOperations.class);
when(couchbaseOperations.getDefaultConsistency()).thenReturn(CONSISTENCY);
when(couchbaseOperations.getCouchbaseBucket()).thenReturn(mockBucket);
when(couchbaseOperations.getConverter()).thenReturn(mockConverter);
when(couchbaseOperations.findByView(any(ViewQuery.class), any(Class.class))).thenReturn(allCountRows);
when(couchbaseOperations.findByN1QL(any(N1qlQuery.class), any(Class.class))).thenReturn(Collections.emptyList());
when(couchbaseOperations.queryView(any(ViewQuery.class))).thenReturn(mockCountResult);
when(couchbaseOperations.queryN1QL(any(N1qlQuery.class))).thenReturn(null);
CouchbaseEntityInformation metadata = mock(CouchbaseEntityInformation.class);
when(metadata.getJavaType()).thenReturn(String.class);
repository = new N1qlCouchbaseRepository<String, String>(metadata, couchbaseOperations);
repository.setViewMetadataProvider(mock(ViewMetadataProvider.class));
}
@Test
public void testFindAllUsesViewWithConfiguredConsistency() {
String expectedQueryParams = "ViewQuery(string/all){params=\"reduce=false&stale=false\"}";
repository.findAll();
verify(couchbaseOperations, never()).queryView(any(ViewQuery.class));
verify(couchbaseOperations, never()).findByN1QL(any(N1qlQuery.class), any(Class.class));
verify(couchbaseOperations, never()).queryN1QL(any(N1qlQuery.class));
ArgumentCaptor<ViewQuery> queryCaptor = ArgumentCaptor.forClass(ViewQuery.class);
verify(couchbaseOperations).findByView(queryCaptor.capture(), any(Class.class));
String sQuery = queryCaptor.getValue().toString();
assertThat(sQuery).isEqualTo(expectedQueryParams);
}
@Test
public void testFindAllKeysUsesViewWithConfiguredConsistency() {
String expectedQueryParams = "ViewQuery(string/all){params=\"reduce=false&stale=false\", keys=\"[\"someKey\"]\"}";
repository.findAllById(Collections.singleton("someKey"));
verify(couchbaseOperations, never()).queryView(any(ViewQuery.class));
verify(couchbaseOperations, never()).findByN1QL(any(N1qlQuery.class), any(Class.class));
verify(couchbaseOperations, never()).queryN1QL(any(N1qlQuery.class));
ArgumentCaptor<ViewQuery> queryCaptor = ArgumentCaptor.forClass(ViewQuery.class);
verify(couchbaseOperations).findByView(queryCaptor.capture(), any(Class.class));
String sQuery = queryCaptor.getValue().toString();
assertThat(sQuery).isEqualTo(expectedQueryParams);
}
@Test
public void testCountUsesViewWithConfiguredConsistencyAndReduces() {
String expectedQueryParams = "ViewQuery(string/all){params=\"reduce=true&stale=false\"}";
repository.count();
verify(couchbaseOperations, never()).findByView(any(ViewQuery.class), any(Class.class));
verify(couchbaseOperations, never()).findByN1QL(any(N1qlQuery.class), any(Class.class));
verify(couchbaseOperations, never()).queryN1QL(any(N1qlQuery.class));
ArgumentCaptor<ViewQuery> queryCaptor = ArgumentCaptor.forClass(ViewQuery.class);
verify(couchbaseOperations).queryView(queryCaptor.capture());
String sQuery = queryCaptor.getValue().toString();
assertThat(sQuery).isEqualTo(expectedQueryParams);
}
@Test
public void testCountParsesAndAddsLongValuesFromRows() {
long count = repository.count();
assertThat(count).isEqualTo(300L);
}
@Test
public void testDeleteAllUsesViewWithConfiguredConsistency() {
String expectedQueryParams = "ViewQuery(string/all){params=\"reduce=false&stale=false\"}";
repository.deleteAll();
verify(couchbaseOperations, never()).findByView(any(ViewQuery.class), any(Class.class));
verify(couchbaseOperations, never()).findByN1QL(any(N1qlQuery.class), any(Class.class));
verify(couchbaseOperations, never()).queryN1QL(any(N1qlQuery.class));
ArgumentCaptor<ViewQuery> queryCaptor = ArgumentCaptor.forClass(ViewQuery.class);
verify(couchbaseOperations).queryView(queryCaptor.capture());
String sQuery = queryCaptor.getValue().toString();
assertThat(sQuery).isEqualTo(expectedQueryParams);
}
@Test
public void testFindAllSortedUsesN1qlWithConfiguredConsistencyAndOrderBy() {
String expectedOrderClause = "ORDER BY `length` ASC";
Sort sort = Sort.by(Direction.ASC, "length");
repository.findAll(sort);
verify(couchbaseOperations, never()).findByView(any(ViewQuery.class), any(Class.class));
verify(couchbaseOperations, never()).queryView(any(ViewQuery.class));
verify(couchbaseOperations, never()).queryN1QL(any(N1qlQuery.class));
ArgumentCaptor<N1qlQuery> queryCaptor = ArgumentCaptor.forClass(N1qlQuery.class);
verify(couchbaseOperations).findByN1QL(queryCaptor.capture(), any(Class.class));
JsonObject query = queryCaptor.getValue().n1ql();
assertThat(query.getString("scan_consistency"))
.isEqualTo(CONSISTENCY.n1qlConsistency().n1ql());
String statement = query.getString("statement");
assertThat(statement.contains(expectedOrderClause))
.as("Expected " + expectedOrderClause + " in " + statement).isTrue();
}
@Test
public void testFindAllPagedUsesUsesN1qlConfiguredConsistencyAndLimitOffset() {
String expectedLimitClause = "LIMIT 10 OFFSET 0";
repository.findAll(PageRequest.of(0, 10));
verify(couchbaseOperations, never()).findByView(any(ViewQuery.class), any(Class.class));
verify(couchbaseOperations, never()).queryView(any(ViewQuery.class));
verify(couchbaseOperations, never()).queryN1QL(any(N1qlQuery.class));
ArgumentCaptor<N1qlQuery> queryCaptor = ArgumentCaptor.forClass(N1qlQuery.class);
verify(couchbaseOperations).findByN1QL(queryCaptor.capture(), any(Class.class));
JsonObject query = queryCaptor.getValue().n1ql();
assertThat(query.getString("scan_consistency"))
.isEqualTo(CONSISTENCY.n1qlConsistency().n1ql());
String statement = query.getString("statement");
assertThat(statement.contains(expectedLimitClause))
.as("Expected " + expectedLimitClause + " in " + statement).isTrue();
}
@Test
public void testDeleteAllSwallowsDocumentDoesNotExistException() {
doThrow(new DataRetrievalFailureException("ignored", new DocumentDoesNotExistException())).when(couchbaseOperations).remove("id1");
doThrow(new DataRetrievalFailureException("thrown")).when(couchbaseOperations).remove("id2");
try {
repository.deleteAll();
fail("Expected DataRetrievalFailureException on id2");
} catch (DataRetrievalFailureException e) {
if (!"thrown".equals(e.getMessage())) {
fail("DataRetrievalFailureException caused by DocumentDoesNotExistException should have been ignored");
}
}
verify(couchbaseOperations).remove("id1");
verify(couchbaseOperations).remove("id2");
}
}

View File

@@ -1,371 +0,0 @@
/*
* Copyright 2013-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.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.springframework.data.couchbase.CouchbaseTestHelper.getRepositoryWithRetry;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Version;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.data.couchbase.core.AsyncUtils;
import org.springframework.data.couchbase.core.CouchbaseQueryExecutionException;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory;
import org.springframework.data.couchbase.repository.support.IndexManager;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.document.JsonDocument;
import com.couchbase.client.java.error.CASMismatchException;
import com.couchbase.client.java.error.DocumentDoesNotExistException;
import com.couchbase.client.java.view.Stale;
import com.couchbase.client.java.view.ViewQuery;
/**
* @author Michael Nitschinger
* @author Mark Paluch
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
@TestExecutionListeners(SimpleCouchbaseRepositoryListener.class)
public class SimpleCouchbaseRepositoryIntegrationTests {
@Rule
public TestName testName = new TestName();
@Autowired
private Bucket client;
@Autowired
private RepositoryOperationsMapping operationsMapping;
@Autowired
private IndexManager indexManager;
private UserRepository repository;
private VersionedDataRepository versionedDataRepository;
@Before
public void setup() throws Exception {
RepositoryFactorySupport factory = new CouchbaseRepositoryFactory(operationsMapping, indexManager);
repository = getRepositoryWithRetry(factory, UserRepository.class);
versionedDataRepository = getRepositoryWithRetry(factory, VersionedDataRepository.class);
}
private void remove(String key) {
try {
client.remove(key);
} catch (DocumentDoesNotExistException e) {
}
}
@Test
public void simpleCrud() {
String key = "my_unique_user_key";
User instance = new User(key, "foobar", 22);
repository.save(instance);
Optional<User> found = repository.findById(key);
assertThat(found.isPresent()).isTrue();
found.ifPresent(actual -> {
assertThat(actual.getKey()).isEqualTo(instance.getKey());
assertThat(actual.getUsername()).isEqualTo(instance.getUsername());
assertThat(repository.existsById(key)).isTrue();
repository.delete(actual);
});
assertThat(repository.findById(key).isPresent()).isFalse();
assertThat(repository.existsById(key)).isFalse();
}
@Test
/**
* This test uses/assumes a default viewName called "all" that is configured on Couchbase.
*/
public void shouldFindAll() {
// do a non-stale query to populate data for testing.
client.query(ViewQuery.from("user", "all").stale(Stale.FALSE));
Iterable<User> allUsers = repository.findAll();
int size = 0;
for (User u : allUsers) {
size++;
assertThat(u.getKey()).isNotNull();
assertThat(u.getUsername()).isNotNull();
}
assertThat(size).isEqualTo(100);
}
@Test
public void shouldCount() {
// do a non-stale query to populate data for testing.
client.query(ViewQuery.from("user", "all").stale(Stale.FALSE));
assertThat(repository.count()).isEqualTo(100);
}
@Test
@Ignore("View based query with copy of params from a ViewQuery in the method parameter not implemented")
//TODO re-enable test once ViewQuery parameters other than designDoc/viewName can be copied
public void shouldFindCustom() {
Iterable<User> users = repository.customViewQuery(ViewQuery.from("", "").limit(2).stale(Stale.FALSE));
int size = 0;
for (User u : users) {
size++;
assertThat(u.getKey()).isNotNull();
assertThat(u.getUsername()).isNotNull();
}
assertThat(size).isEqualTo(2);
}
@Test
public void shouldFindByUsernameUsingN1ql() {
User user = repository.findByUsername("uname-1");
assertThat(user).isNotNull();
assertThat(user.getKey()).isEqualTo("testuser-1");
assertThat(user.getUsername()).isEqualTo("uname-1");
}
@Test
public void shouldFailFindByUsernameWithNoIdOrCas() {
try {
User user = repository.findByUsernameBadSelect("uname-1");
fail("shouldFailFindByUsernameWithNoIdOrCas");
} catch (CouchbaseQueryExecutionException e) {
assertThat(e.getMessage().contains("_ID")).as("_ID expected in exception " + e)
.isTrue();
assertThat(e.getMessage().contains("_CAS")).as("_CAS expected in exception " + e)
.isTrue();
} catch (Exception e) {
fail("CouchbaseQueryExecutionException expected");
}
}
@Test
public void shouldFindFromUsernameInlineWithSpelParsing() {
User user = repository.findByUsernameWithSpelAndPlaceholder();
assertThat(user).isNotNull();
assertThat(user.getKey()).isEqualTo("testuser-4");
assertThat(user.getUsername()).isEqualTo("uname-4");
}
@Test
public void shouldFindFromDeriveQueryWithRegexpAndIn() {
User user = repository.findByUsernameRegexAndUsernameIn("uname-[123]", Arrays.asList("uname-2", "uname-4"));
assertThat(user).isNotNull();
assertThat(user.getKey()).isEqualTo("testuser-2");
assertThat(user.getUsername()).isEqualTo("uname-2");
}
@Test
public void shouldFindContainsWithoutAnnotation() {
List<User> users = repository.findByUsernameContains("-9");
assertThat(users).isNotNull();
assertThat(users.isEmpty()).isFalse();
for (User user : users) {
assertThat(user.getUsername().startsWith("uname-9")).isTrue();
}
}
@Test
public void shouldDefaultToN1qlQueryDerivation() {
try {
User u = repository.findByUsernameNear("london");
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
if (!e.getMessage().contains("N1QL")) {
fail(e.getMessage());
}
}
}
@Test
public void shouldTakeVersionIntoAccountWhenDoingMultipleUpdates() {
final String key = "versionedUserTest";
VersionedData initial = new VersionedData(key, "ABCD");
versionedDataRepository.save(initial);
assertThat(initial.version).isNotEqualTo(0L);
Optional<VersionedData> fetch1 = versionedDataRepository.findById(key);
assertThat(fetch1.isPresent()).isTrue();
fetch1.ifPresent(actual -> {
assertThat(actual).isNotSameAs(initial);
assertThat(initial.version).isEqualTo(actual.version);
});
VersionedData versionedData = fetch1.get();
JsonDocument bypass = client.get(key);
bypass.content().put("data", "BBBB");
JsonDocument bypassed = client.upsert(bypass);
assertThat(versionedData.version).isNotEqualTo(bypassed.cas());
System.out.println(bypassed.cas());
try {
versionedData.setData("ZZZZ");
versionedDataRepository.save(versionedData);
fail("Expected CAS failure");
} catch (OptimisticLockingFailureException e) {
//success
assertThat(e.getCause() instanceof CASMismatchException)
.as("optimistic locking should have CASMismatchException as cause, got " + e
.getCause()).isTrue();
} finally {
client.remove(key);
}
}
// todo: investigate cause of intermittent failure
@Ignore("Fails intermittently (see DATACOUCH-452)")
@Test
public void shouldUpdateDocumentConcurrently() throws Exception {
final String key = testName.getMethodName();
remove(key);
final AtomicLong counter = new AtomicLong();
final AtomicLong updatedCounter = new AtomicLong();
VersionedData initial = new VersionedData(key, "value-initial");
versionedDataRepository.save(initial);
assertThat(initial.version).isNotEqualTo(0L);
Callable<Void> task = new Callable<Void>() {
@Override
public Void call() throws Exception {
boolean updated = false;
while(!updated) {
long counterValue = counter.incrementAndGet();
VersionedData messageData = versionedDataRepository.findById(key).get();
messageData.data = "value-" + counterValue;
try {
versionedDataRepository.save(messageData);
updated = true;
updatedCounter.incrementAndGet();
} catch (OptimisticLockingFailureException e) {
}
}
return null;
}
};
AsyncUtils.executeConcurrently(5, task);
assertThat(versionedDataRepository.findById(key).get().data)
.isNotEqualTo(initial.data);
assertThat(updatedCounter.intValue()).isEqualTo(5);
}
@Test
public void shouldFailOnMultipleConcurrentSaves() throws Exception {
final String key = testName.getMethodName();
remove(key);
final AtomicLong counter = new AtomicLong();
final AtomicLong optimisticLockCounter = new AtomicLong();
Callable<Void> task = new Callable<Void>() {
@Override
public Void call() throws Exception {
long counterValue = counter.incrementAndGet();
VersionedData messageData = new VersionedData(key, "value-" + counterValue);
try {
versionedDataRepository.save(messageData);
} catch (OptimisticLockingFailureException e) {
optimisticLockCounter.incrementAndGet();
}
return null;
}
};
AsyncUtils.executeConcurrently(5, task);
assertThat(optimisticLockCounter.intValue()).isEqualTo(4);
}
public interface VersionedDataRepository extends CouchbaseRepository<VersionedData, String> { }
@Document
public static class VersionedData {
@Id
private final String key;
@Version
public long version = 0L;
private String data;
public VersionedData(String key, String data) {
this.key = key;
this.data = data;
}
public String getKey() {
return key;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
@Override
public String toString() {
return this.key + " " + this.data;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VersionedData vd = (VersionedData) o;
return key.equals(vd.key);
}
@Override
public int hashCode() {
return key.hashCode();
}
}
}

View File

@@ -1,67 +0,0 @@
/*
* Copyright 2013-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 java.util.Collections;
import java.util.List;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.PersistTo;
import com.couchbase.client.java.ReplicateTo;
import com.couchbase.client.java.cluster.ClusterInfo;
import com.couchbase.client.java.view.DefaultView;
import com.couchbase.client.java.view.DesignDocument;
import com.couchbase.client.java.view.View;
import org.springframework.data.couchbase.config.BeanNames;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
/**
* @author Michael Nitschinger
*/
public class SimpleCouchbaseRepositoryListener extends DependencyInjectionTestExecutionListener {
@Override
public void beforeTestClass(final TestContext testContext) throws Exception {
Bucket client = (Bucket) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_BUCKET);
ClusterInfo clusterInfo = (ClusterInfo) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_CLUSTER_INFO);
populateTestData(client, clusterInfo);
createAndWaitForDesignDocs(client);
}
private void populateTestData(Bucket client, ClusterInfo clusterInfo) {
CouchbaseTemplate template = new CouchbaseTemplate(clusterInfo, client);
for (int i = 0; i < 100; i++) {
User u = new User("testuser-" + i, "uname-" + i, i);
template.save(u, PersistTo.MASTER, ReplicateTo.NONE);
}
}
private void createAndWaitForDesignDocs(Bucket client) {
String mapFunction = "function (doc, meta) { if(doc._class == \"org.springframework.data.couchbase.repository." +
"User\") { emit(null, null); } }";
View view = DefaultView.create("all", mapFunction, "_count");
List<View> views = Collections.singletonList(view);
DesignDocument designDoc = DesignDocument.create("user", views);
client.bucketManager().upsertDesignDocument(designDoc);
}
}

View File

@@ -1,89 +0,0 @@
/*
* Copyright 2017-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 com.couchbase.client.java.Bucket;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.ReactiveIntegrationTestApplicationConfig;
import org.springframework.data.couchbase.repository.config.ReactiveRepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.support.IndexManager;
import org.springframework.data.couchbase.repository.support.ReactiveCouchbaseRepositoryFactory;
import org.springframework.data.repository.core.support.ReactiveRepositoryFactorySupport;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.data.couchbase.CouchbaseTestHelper.getRepositoryWithRetry;
/**
* @author David Kelly
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = ReactiveIntegrationTestApplicationConfig.class)
@TestExecutionListeners(SimpleReactiveCouchbaseRepositoryListener.class)
public class SimpleReactiveCouchbaseRepositoryDeleteAllIntegrationTests {
// We do these tests here, rather in the simple crud tests, so we don't interact
// with those tests that make assumptions about what documents should be
// in the repo.
@Rule
public TestName testName = new TestName();
@Autowired
private Bucket client;
@Autowired
private ReactiveRepositoryOperationsMapping operationsMapping;
@Autowired
private IndexManager indexManager;
private ReactiveUserRepository repository;
private long getCount() {
return repository.count().block().longValue();
}
@Before
public void setup() throws Exception {
ReactiveRepositoryFactorySupport factory = new ReactiveCouchbaseRepositoryFactory(operationsMapping, indexManager);
repository = getRepositoryWithRetry(factory, ReactiveUserRepository.class);
}
@Test
public void simpleDeleteAll() {
String key = "my_unique_user_key";
// in case there are other tests later, lets insure there is at least one
// User in the repository
ReactiveUser instance = new ReactiveUser(key, "foobar", 22);
repository.save(instance).block();
// we put a user in, lets be sure the count reflects that.
assertThat(getCount() > 0L).isTrue();
repository.deleteAll().block();
// after deleteAll, we should have a count of 0
assertThat(getCount()).isEqualTo(0L);
}
}

View File

@@ -1,188 +0,0 @@
/*
* Copyright 2017-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.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.springframework.data.couchbase.CouchbaseTestHelper.getRepositoryWithRetry;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.ReactiveIntegrationTestApplicationConfig;
import org.springframework.data.couchbase.core.CouchbaseQueryExecutionException;
import org.springframework.data.couchbase.repository.config.ReactiveRepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.support.IndexManager;
import org.springframework.data.couchbase.repository.support.ReactiveCouchbaseRepositoryFactory;
import org.springframework.data.repository.core.support.ReactiveRepositoryFactorySupport;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.error.DocumentDoesNotExistException;
import com.couchbase.client.java.view.Stale;
import com.couchbase.client.java.view.ViewQuery;
/**
* @author Subhashni Balakrishnan
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = ReactiveIntegrationTestApplicationConfig.class)
@TestExecutionListeners(SimpleReactiveCouchbaseRepositoryListener.class)
public class SimpleReactiveCouchbaseRepositoryIntegrationTests {
@Rule
public TestName testName = new TestName();
@Autowired
private Bucket client;
@Autowired
private ReactiveRepositoryOperationsMapping operationsMapping;
@Autowired
private IndexManager indexManager;
private ReactiveUserRepository repository;
@Before
public void setup() throws Exception {
ReactiveRepositoryFactorySupport factory = new ReactiveCouchbaseRepositoryFactory(operationsMapping, indexManager);
repository = getRepositoryWithRetry(factory, ReactiveUserRepository.class);
}
private void remove(String key) {
try {
client.remove(key);
} catch (DocumentDoesNotExistException e) {
}
}
@Test
public void simpleCrud() {
String key = "my_unique_user_key";
ReactiveUser instance = new ReactiveUser(key, "foobar", 22);
repository.save(instance).block();
ReactiveUser found = repository.findById(key).block();
assertThat(found.getKey()).isEqualTo(instance.getKey());
assertThat(found.getUsername()).isEqualTo(instance.getUsername());
assertThat(repository.existsById(key).block()).isTrue();
repository.delete(found).block();
assertThat(repository.findById(key).block()).isNull();
assertThat(repository.existsById(key).block()).isFalse();
}
@Test
/**
* This test uses/assumes a default viewName called "all" that is configured on Couchbase.
*/
public void shouldFindAll() {
// do a non-stale query to populate data for testing.
client.query(ViewQuery.from("reactiveUser", "all").stale(Stale.FALSE));
List<ReactiveUser> allUsers = repository.findAll().collectList().block();
int size = 0;
for (ReactiveUser u : allUsers) {
size++;
assertThat(u.getKey()).isNotNull();
assertThat(u.getUsername()).isNotNull();
}
assertThat(size).isEqualTo(100);
}
@Test
public void shouldCount() {
// do a non-stale query to populate data for testing.
client.query(ViewQuery.from("reactiveUser", "all").stale(Stale.FALSE));
assertThat(repository.count().block().toString()).isEqualTo("100");
}
@Test
public void shouldFindByUsernameUsingN1ql() {
ReactiveUser user = repository.findByUsername("reactiveuname-1").single().block();
assertThat(user).isNotNull();
assertThat(user.getKey()).isEqualTo("reactivetestuser-1");
assertThat(user.getUsername()).isEqualTo("reactiveuname-1");
}
@Test
public void shouldFailFindByUsernameWithNoIdOrCas() {
try {
ReactiveUser user = repository.findByUsernameBadSelect("reactiveuname-1").single().block();
fail("shouldFailFindByUsernameWithNoIdOrCas");
} catch (CouchbaseQueryExecutionException e) {
assertThat(e.getMessage().contains("_ID"))
.as("_ID expected in exception " + e).isTrue();
assertThat(e.getMessage().contains("_CAS"))
.as("_CAS expected in exception " + e).isTrue();
} catch (Exception e) {
fail("CouchbaseQueryExecutionException expected");
}
}
@Test
public void shouldFindFromUsernameInlineWithSpelParsing() {
ReactiveUser user = repository.findByUsernameWithSpelAndPlaceholder().take(1).blockLast();
assertThat(user).isNotNull();
assert(user.getUsername().startsWith("reactive"));
assert(user.getUsername().startsWith("reactive"));
}
@Test
public void shouldFindFromDeriveQueryWithRegexpAndIn() {
ReactiveUser user = repository.findByUsernameRegexAndUsernameIn("reactiveuname-[123]", Arrays.asList("reactiveuname-2", "reactiveuname-4")).take(1).blockLast();
assertThat(user).isNotNull();
assertThat(user.getKey()).isEqualTo("reactivetestuser-2");
assertThat(user.getUsername()).isEqualTo("reactiveuname-2");
}
@Test
public void shouldFindContainsWithoutAnnotation() {
List<ReactiveUser> users = repository.findByUsernameContains("reactive").collectList().block();
assertThat(users).isNotNull();
assertThat(users.isEmpty()).isFalse();
for (ReactiveUser user : users) {
assertThat(user.getUsername().startsWith("reactive")).isTrue();
}
}
@Test
public void shouldDefaultToN1qlQueryDerivation() {
try {
ReactiveUser u = repository.findByUsernameNear("london").single().block();
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
if (!e.getMessage().contains("N1QL")) {
fail(e.getMessage());
}
}
}
}

View File

@@ -1,50 +0,0 @@
package org.springframework.data.couchbase.repository;
import java.util.Collections;
import java.util.List;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.PersistTo;
import com.couchbase.client.java.ReplicateTo;
import com.couchbase.client.java.cluster.ClusterInfo;
import com.couchbase.client.java.view.DefaultView;
import com.couchbase.client.java.view.DesignDocument;
import com.couchbase.client.java.view.View;
import org.springframework.data.couchbase.config.BeanNames;
import org.springframework.data.couchbase.core.RxJavaCouchbaseTemplate;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
/**
* @author Subhashni Balakrishnan
*/
public class SimpleReactiveCouchbaseRepositoryListener extends DependencyInjectionTestExecutionListener {
@Override
public void beforeTestClass(final TestContext testContext) throws Exception {
Bucket client = (Bucket) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_BUCKET);
ClusterInfo clusterInfo = (ClusterInfo) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_CLUSTER_INFO);
populateTestData(client, clusterInfo);
createAndWaitForDesignDocs(client);
}
private void populateTestData(Bucket client, ClusterInfo clusterInfo) {
RxJavaCouchbaseTemplate template = new RxJavaCouchbaseTemplate(clusterInfo, client);
for (int i = 0; i < 100; i++) {
ReactiveUser u = new ReactiveUser("reactivetestuser-" + i, "reactiveuname-" + i, i);
template.save(u, PersistTo.MASTER, ReplicateTo.NONE).subscribe();
}
}
private void createAndWaitForDesignDocs(Bucket client) {
String mapFunction = "function (doc, meta) { if(doc._class == \"org.springframework.data.couchbase.repository." +
"ReactiveUser\") { emit(null, null); } }";
View view = DefaultView.create("all", mapFunction, "_count");
List<View> views = Collections.singletonList(view);
DesignDocument designDoc = DesignDocument.create("reactiveUser", views);
client.bucketManager().upsertDesignDocument(designDoc);
}
}

View File

@@ -1,69 +0,0 @@
/*
* Copyright 2013-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 org.springframework.data.annotation.Id;
/**
* @author Michael Nitschinger
* @author Simon Baslé
*/
public class User {
@Id
private final String key;
private final String username;
private final int age;
public User(String key, String username, int age) {
this.key = key;
this.username = username;
this.age = age;
}
public String getUsername() {
return username;
}
public String getKey() {
return key;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return this.key;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return key.equals(user.key);
}
@Override
public int hashCode() {
return key.hashCode();
}
}

View File

@@ -1,60 +0,0 @@
/*
* Copyright 2013-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 java.util.List;
import com.couchbase.client.java.view.ViewQuery;
import org.springframework.data.couchbase.core.query.N1qlSecondaryIndexed;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.query.View;
import org.springframework.data.couchbase.core.query.ViewIndexed;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
/**
* @author Michael Nitschinger
*/
@ViewIndexed(designDoc = "user", viewName = "all")
@N1qlSecondaryIndexed(indexName = "User")
public interface UserRepository extends CouchbaseRepository<User, String> {
@View(designDocument = "user", viewName = "all")
Iterable<User> customViewQuery(ViewQuery query);
@Query("#{#n1ql.selectEntity} WHERE username = $1 and #{#n1ql.filter}")
User findByUsername(String username);
@Query("SELECT * FROM #{#n1ql.bucket} WHERE username = $1 and #{#n1ql.filter} ")
User findByUsernameBadSelect(String username);
@Query("#{#n1ql.selectEntity} WHERE username LIKE '%-4' and #{#n1ql.filter}")
User findByUsernameWithSpelAndPlaceholder();
@Query
User findByUsernameRegexAndUsernameIn(String regex, List<String> sample);
List<User> findByUsernameContains(String contains);
User findByUsernameNear(String place);//this is to check that there's a N1QL derivation AND it fails
Page<User> findByAgeGreaterThan(int minAge, Pageable pageable);
Slice<User> findByAgeLessThan(int maxAge, Pageable pageable);
}

View File

@@ -1,17 +0,0 @@
package org.springframework.data.couchbase.repository.auditing;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
@Configuration
@EnableCouchbaseRepositories
@EnableCouchbaseAuditing(modifyOnCreate = false)
public class AuditedApplicationConfig extends IntegrationTestApplicationConfig {
@Bean
public AuditedAuditorAware couchbaseAuditorAware() {
return new AuditedAuditorAware();
}
}

View File

@@ -1,19 +0,0 @@
package org.springframework.data.couchbase.repository.auditing;
import org.springframework.data.domain.AuditorAware;
import java.util.Optional;
public class AuditedAuditorAware implements AuditorAware<String> {
private Optional<String> auditor = Optional.of("auditor");
@Override
public Optional<String> getCurrentAuditor() {
return auditor;
}
public void setAuditor(String auditor) {
this.auditor = Optional.of(auditor);
}
}

View File

@@ -1,129 +0,0 @@
package org.springframework.data.couchbase.repository.auditing;
import java.util.Date;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.annotation.Version;
import org.springframework.data.couchbase.core.mapping.Document;
@Document
public class AuditedItem {
@Id
private final String id;
private String value;
@CreatedBy
private String creator;
@LastModifiedBy
private String lastModifiedBy;
@LastModifiedDate
private Date lastModification;
@CreatedDate
private Date creationDate;
@Version
private long version;
public AuditedItem(String id, String value) {
this.id = id;
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getCreator() {
return creator;
}
public void setCreator(String creator) {
this.creator = creator;
}
public String getLastModifiedBy() {
return lastModifiedBy;
}
public void setLastModifiedBy(String lastModifiedBy) {
this.lastModifiedBy = lastModifiedBy;
}
public Date getLastModification() {
return lastModification;
}
public void setLastModification(Date lastModification) {
this.lastModification = lastModification;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
@Override
public String toString() {
return "AuditedItem{" +
"id='" + id + '\'' +
", value='" + value + '\'' +
", creator='" + creator + '\'' +
", lastModifiedBy='" + lastModifiedBy + '\'' +
", lastModification=" + lastModification +
", creationDate=" + creationDate +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AuditedItem that = (AuditedItem) o;
if (!id.equals(that.id)) return false;
if (!value.equals(that.value)) return false;
if (creator != null ? !creator.equals(that.creator) : that.creator != null) return false;
if (lastModifiedBy != null ? !lastModifiedBy.equals(that.lastModifiedBy) : that.lastModifiedBy != null)
return false;
if (lastModification != null ? !lastModification.equals(that.lastModification) : that.lastModification != null)
return false;
return creationDate != null ? creationDate.equals(that.creationDate) : that.creationDate == null;
}
@Override
public int hashCode() {
int result = id.hashCode();
result = 31 * result + value.hashCode();
result = 31 * result + (creator != null ? creator.hashCode() : 0);
result = 31 * result + (lastModifiedBy != null ? lastModifiedBy.hashCode() : 0);
result = 31 * result + (lastModification != null ? lastModification.hashCode() : 0);
result = 31 * result + (creationDate != null ? creationDate.hashCode() : 0);
return result;
}
}

View File

@@ -1,7 +0,0 @@
package org.springframework.data.couchbase.repository.auditing;
import org.springframework.data.couchbase.repository.CouchbaseRepository;
public interface AuditedRepository extends CouchbaseRepository<AuditedItem, String> {
}

View File

@@ -1,113 +0,0 @@
package org.springframework.data.couchbase.repository.auditing;
import java.util.Date;
import java.util.Optional;
import org.junit.After;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.TestContainerResource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Simon Baslé
* @author Mark Paluch
*/
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = AuditedApplicationConfig.class)
public class AuditingIntegrationTests {
@Autowired
private AuditedRepository repository;
@Autowired
private AuditedAuditorAware auditorAware;
private static final String KEY = "auditedTest";
@After
public void cleanupAuditedEntity() {
repository.getCouchbaseOperations().getCouchbaseBucket().remove(KEY);
}
@Test
public void testCreationEventIsRegistered() {
assertThat(repository.existsById(KEY)).isFalse();
Date start = new Date();
AuditedItem item = new AuditedItem(KEY, "creation");
auditorAware.setAuditor("auditor");
repository.save(item);
Optional<AuditedItem> persisted = repository.findById(KEY);
assertThat(persisted.isPresent()).isTrue();
persisted.ifPresent(actual -> {
assertThat(actual.getCreationDate()).as("expected creation date audit trail")
.isNotNull();
assertThat(actual.getCreator()).as("expected creation user audit trail")
.isEqualTo("auditor");
assertThat(actual.getCreationDate().after(start)).as("creation date is too early")
.isTrue();
assertThat(actual.getCreationDate().before(new Date()))
.as("creation date is too late").isTrue();
assertThat(actual.getLastModification())
.as("expected modification date to be empty").isNull();
assertThat(actual.getLastModifiedBy()).as("expected modification user to be empty")
.isNull();
assertThat(actual.getVersion()).as("expected version to be non null").isNotNull();
assertThat(actual.getVersion() > 0L).as("expected version to be greater than 0")
.isTrue();
});
}
@Test
public void testUpdateEventIsRegistered() {
assertThat(repository.existsById(KEY)).isFalse();
String expectedCreator = "user1";
String expectedUpdater = "user2";
AuditedItem item = new AuditedItem(KEY, "creation");
auditorAware.setAuditor(expectedCreator);
repository.save(item);
AuditedItem created = repository.findById(KEY).orElse(null);
auditorAware.setAuditor(expectedUpdater);
repository.save(item);
AuditedItem updated = repository.findById(KEY).orElse(null);
assertThat(updated).as("expected entity to be persisted").isNotNull();
assertThat(updated.getCreationDate()).as("expected creation date audit trail")
.isNotNull();
assertThat(updated.getCreator()).as("expected creation user audit trail")
.isEqualTo(expectedCreator);
assertThat(updated.getLastModification()).as("expected modification date audit trail")
.isNotNull();
assertThat(updated.getCreationDate().before(updated.getLastModification()))
.as("expected modification date to be after creation date").isTrue();
assertThat(updated.getLastModifiedBy())
.as("expected modification user to be the modifier")
.isEqualTo(expectedUpdater);
assertThat(updated.getVersion()).as("expected version to be non null").isNotNull();
assertThat(updated.getVersion() > 0L).as("expected version to be greater than 0")
.isTrue();
assertThat(created.getVersion() != updated.getVersion())
.as("expected updated version to be different from the one at creation")
.isTrue();
}
}

View File

@@ -1,27 +0,0 @@
/*
* Copyright 2014-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.cdi;
/**
* @author Mark Paluch
*/
public class CdiPersonFragmentImpl implements CdiPersonFragment {
@Override
public int returnTwo() {
return 2;
}
}

View File

@@ -1,25 +0,0 @@
/*
* Copyright 2014-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.cdi;
import org.springframework.data.couchbase.repository.CouchbaseRepository;
/**
* @author Mark Paluch
*/
public interface CdiPersonRepository extends CouchbaseRepository<Person, String>, CdiPersonFragment {
}

View File

@@ -1,50 +0,0 @@
/*
* Copyright 2014-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.cdi;
import javax.inject.Inject;
import com.couchbase.client.java.Bucket;
/**
* @author Mark Paluch
*/
class CdiRepositoryClient {
@Inject
private CdiPersonRepository cdiPersonRepository;
@Inject
@OtherQualifier
@PersonDB
private QualifiedPersonRepository qualifiedPersonRepository;
@Inject
private Bucket couchbaseClient;
public CdiPersonRepository getCdiPersonRepository() {
return cdiPersonRepository;
}
public QualifiedPersonRepository getQualifiedPersonRepository() {
return qualifiedPersonRepository;
}
public Bucket getCouchbaseClient() {
return couchbaseClient;
}
}

View File

@@ -1,140 +0,0 @@
/*
* Copyright 2014-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.cdi;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.view.DefaultView;
import com.couchbase.client.java.view.DesignDocument;
import com.couchbase.client.java.view.View;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.test.context.ContextConfiguration;
import javax.enterprise.inject.se.SeContainer;
import javax.enterprise.inject.se.SeContainerInitializer;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Mark Paluch
*/
@SuppressWarnings("SpringJavaAutowiringInspection")
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = CdiRepositoryIntegrationTests.class)
public class CdiRepositoryIntegrationTests {
private static SeContainer cdiContainer;
private CdiPersonRepository repository;
private QualifiedPersonRepository qualifiedPersonRepository;
private Bucket couchbaseClient;
@BeforeClass
public static void init() {
cdiContainer = SeContainerInitializer.newInstance() //
.disableDiscovery() //
.addPackages(CdiRepositoryClient.class) //
.initialize();
}
@AfterClass
public static void shutdown() {
cdiContainer.close();
}
@Before
public void setUp() {
CdiRepositoryClient repositoryClient = cdiContainer.select(CdiRepositoryClient.class).get();
repository = repositoryClient.getCdiPersonRepository();
qualifiedPersonRepository = repositoryClient.getQualifiedPersonRepository();
couchbaseClient = repositoryClient.getCouchbaseClient();
createAndWaitForDesignDocs(couchbaseClient);
}
private void createAndWaitForDesignDocs(Bucket client) {
String mapFunction = "function (doc, meta) { if(doc._class == \"" + Person.class.getName()
+ "\") { emit(null, null); } }";
View view = DefaultView.create("all", mapFunction, "_count");
List<View> views = Collections.singletonList(view);
DesignDocument designDoc = DesignDocument.create("person", views);
client.bucketManager().upsertDesignDocument(designDoc);
}
/**
* @see DATACOUCH-109
*/
@Test
public void testCdiRepository() {
assertThat(repository).isNotNull();
repository.deleteAll();
Person bean = new Person("key", "username");
repository.save(bean);
assertThat(repository.existsById(bean.getId())).isTrue();
Optional<Person> retrieved = repository.findById(bean.getId());
assertThat(retrieved.isPresent()).isTrue();
retrieved.ifPresent(actual -> {
assertThat(actual.getName()).isEqualTo(bean.getName());
assertThat(actual.getId()).isEqualTo(bean.getId());
});
}
/**
* @see DATACOUCH-203
*/
@Test
public void testQualifiedCdiRepository() {
assertThat(qualifiedPersonRepository).isNotNull();
qualifiedPersonRepository.deleteAll();
Person bean = new Person("key", "username");
qualifiedPersonRepository.save(bean);
assertThat(qualifiedPersonRepository.existsById(bean.getId())).isTrue();
Optional<Person> retrieved = qualifiedPersonRepository.findById(bean.getId());
assertThat(retrieved.isPresent()).isTrue();
retrieved.ifPresent(actual -> {
assertThat(actual.getName()).isEqualTo(bean.getName());
assertThat(actual.getId()).isEqualTo(bean.getId());
});
}
/**
* @see DATACOUCH-109
*/
@Test
public void testCustomRepository() {
assertThat(repository.returnTwo()).isEqualTo(2);
}
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright 2014-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.cdi;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Disposes;
import javax.enterprise.inject.Produces;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.CouchbaseCluster;
import org.springframework.data.couchbase.config.CouchbaseBucketFactoryBean;
/**
* Producer for {@link Bucket}. A default {@link CouchbaseCluster} with defaults
* from {@link CouchbaseBucketFactoryBean} are sufficient for our test.
*
* @author Mark Paluch
* @author Simon Baslé
*/
class CouchbaseClientProducer {
@Produces
@ApplicationScoped
public Cluster cluster() {
return CouchbaseCluster.create();
}
@Produces
public Bucket createCouchbaseClient(Cluster cluster) throws Exception {
CouchbaseBucketFactoryBean couchbaseFactoryBean = new CouchbaseBucketFactoryBean(cluster, "protected", "protected", "password");
couchbaseFactoryBean.afterPropertiesSet();
return couchbaseFactoryBean.getObject();
}
public void close(@Disposes Bucket couchbaseClient) {
couchbaseClient.close();
}
public void close(@Disposes Cluster cluster) {
cluster.disconnect();
}
}

Some files were not shown because too many files have changed in this diff Show More