Skip to content

Support testing against Capella. #1386

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,14 @@
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.4.0</version>
<version>4.8.1</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-tls</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import java.util.UUID;
import java.util.stream.Collectors;

import com.couchbase.client.core.msg.kv.DurabilityLevel;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
Expand Down Expand Up @@ -58,7 +57,7 @@

import com.couchbase.client.core.error.AmbiguousTimeoutException;
import com.couchbase.client.core.error.UnambiguousTimeoutException;
import com.couchbase.client.core.io.CollectionIdentifier;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.analytics.AnalyticsOptions;
import com.couchbase.client.java.kv.ExistsOptions;
import com.couchbase.client.java.kv.GetAnyReplicaOptions;
Expand Down Expand Up @@ -765,9 +764,6 @@ public void testScopeCollectionAnnotation() {
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(scopeName).inCollection(collectionName)
.matching(query).all();
assertEquals(saved, found.get(0), "should have found what was saved");
List<UserCol> notfound = couchbaseTemplate.findByQuery(UserCol.class).inScope(CollectionIdentifier.DEFAULT_SCOPE)
.inCollection(CollectionIdentifier.DEFAULT_COLLECTION).matching(query).all();
assertEquals(0, notfound.size(), "should not have found what was saved");
couchbaseTemplate.removeByQuery(UserCol.class).inScope(scopeName).inCollection(collectionName).matching(query)
.all();
} finally {
Expand All @@ -789,9 +785,6 @@ public void testScopeCollectionRepoWith() {
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(scopeName).inCollection(collectionName)
.matching(query).all();
assertEquals(saved, found.get(0), "should have found what was saved");
List<UserCol> notfound = couchbaseTemplate.findByQuery(UserCol.class).inScope(CollectionIdentifier.DEFAULT_SCOPE)
.inCollection(CollectionIdentifier.DEFAULT_COLLECTION).matching(query).all();
assertEquals(0, notfound.size(), "should not have found what was saved");
couchbaseTemplate.removeByQuery(UserCol.class).inScope(scopeName).inCollection(collectionName).matching(query)
.all();
} finally {
Expand All @@ -810,28 +803,28 @@ void testFluentApi() {
RemoveResult rr;
result = couchbaseTemplate.insertById(User.class).withDurability(dl).inScope(scopeName).inCollection(collectionName)
.one(user1);
assertEquals(user1,result);
assertEquals(user1, result);
result = couchbaseTemplate.upsertById(User.class).withDurability(dl).inScope(scopeName).inCollection(collectionName)
.one(user1);
assertEquals(user1,result);
result = couchbaseTemplate.replaceById(User.class).withDurability(dl).inScope(scopeName).inCollection(collectionName)
.one(user1);
assertEquals(user1,result);
assertEquals(user1, result);
result = couchbaseTemplate.replaceById(User.class).withDurability(dl).inScope(scopeName)
.inCollection(collectionName).one(user1);
assertEquals(user1, result);
rr = couchbaseTemplate.removeById(User.class).withDurability(dl).inScope(scopeName).inCollection(collectionName)
.one(user1.getId());
assertEquals(rr.getId(), user1.getId());
assertEquals(user1,result);
result = reactiveCouchbaseTemplate.insertById(User.class).withDurability(dl).inScope(scopeName).inCollection(collectionName)
.one(user1).block();
assertEquals(user1,result);
result = reactiveCouchbaseTemplate.upsertById(User.class).withDurability(dl).inScope(scopeName).inCollection(collectionName)
.one(user1).block();
assertEquals(user1,result);
assertEquals(user1, result);
result = reactiveCouchbaseTemplate.insertById(User.class).withDurability(dl).inScope(scopeName)
.inCollection(collectionName).one(user1).block();
assertEquals(user1, result);
result = reactiveCouchbaseTemplate.upsertById(User.class).withDurability(dl).inScope(scopeName)
.inCollection(collectionName).one(user1).block();
assertEquals(user1, result);
result = reactiveCouchbaseTemplate.replaceById(User.class).withDurability(dl).inScope(scopeName)
.inCollection(collectionName).one(user1).block();
assertEquals(user1,result);
rr = reactiveCouchbaseTemplate.removeById(User.class).withDurability(dl).inScope(scopeName).inCollection(collectionName)
.one(user1.getId()).block();
assertEquals(user1, result);
rr = reactiveCouchbaseTemplate.removeById(User.class).withDurability(dl).inScope(scopeName)
.inCollection(collectionName).one(user1.getId()).block();
assertEquals(rr.getId(), user1.getId());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
import org.springframework.data.couchbase.util.IgnoreWhen;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import com.couchbase.client.core.deps.io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import com.couchbase.client.core.env.SecurityConfig;
import com.couchbase.client.java.env.ClusterEnvironment;
import com.couchbase.client.java.kv.GetResult;

/**
Expand Down Expand Up @@ -91,6 +94,14 @@ public String getBucketName() {
return bucketName();
}

@Override
protected void configureEnvironment(ClusterEnvironment.Builder builder) {
if (config().isUsingCloud()) {
builder.securityConfig(
SecurityConfig.builder().trustManagerFactory(InsecureTrustManagerFactory.INSTANCE).enableTls(true));
}
}

@Override
public String typeKey() {
return CUSTOM_TYPE_KEY;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
* Copyright 2022 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 static com.couchbase.client.java.query.QueryOptions.queryOptions;
import static java.nio.charset.StandardCharsets.UTF_8;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.tls.HandshakeCertificates;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import com.couchbase.client.core.deps.io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import com.couchbase.client.core.env.IoConfig;
import com.couchbase.client.core.env.SecurityConfig;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.ClusterOptions;
import com.couchbase.client.java.Collection;
import com.couchbase.client.java.env.ClusterEnvironment;
import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.manager.query.CreatePrimaryQueryIndexOptions;
import com.couchbase.client.java.query.QueryResult;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
* Sample code for connecting to Capella through both the control-plane and the data-plane. An Access Key and a Secret
* Key are required and a bucket named "my_bucket" on the 'last' cluster.
*/
public class CapellaConnectSample {

static final String cbc_access_key = "3gcpgyTBzOetdETYxOAtmLYBe3f9ZSVN";
static final String cbc_secret_key = "PWiACuJIZUlv0fCZaIQbhI44NDXVZCDdRBbpdaWlACioN7jkuOINCUVrU2QL1jVO";
static final String hostname = "cloudapi.cloud.couchbase.com";
static final HandshakeCertificates clientCertificates = new HandshakeCertificates.Builder()
.addPlatformTrustedCertificates().addInsecureHost(hostname).build();
static final OkHttpClient httpClient = new OkHttpClient.Builder()
.sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager()).build();

protected static final ObjectMapper MAPPER = new ObjectMapper();
static final String authorizationHeaderLabel = "Authorization";
static final String timestampHeaderLabel = "Couchbase-Timestamp";

public static void main(String... args) {
String endpoint = null; // "cb.zsibzkbgllfbcj8g.cloud.couchbase.com";
List<String> clusterIds = getClustersControlPlane();
for (String id : clusterIds) {
endpoint = getClusterControlPlane(id);
}

// Update this to your cluster
String bucketName = "my_bucket";
String username = "user";
String password = "Couch0base!";
// User Input ends here.

ClusterEnvironment env = ClusterEnvironment.builder()
.securityConfig(SecurityConfig.enableTls(true).trustManagerFactory(InsecureTrustManagerFactory.INSTANCE))
.ioConfig(IoConfig.enableDnsSrv(true)).build();

// Initialize the Connection
Cluster cluster = Cluster.connect(endpoint, ClusterOptions.clusterOptions(username, password).environment(env));
Bucket bucket = cluster.bucket(bucketName);
bucket.waitUntilReady(Duration.parse("PT10S"));
Collection collection = bucket.defaultCollection();

cluster.queryIndexes().createPrimaryIndex(bucketName,
CreatePrimaryQueryIndexOptions.createPrimaryQueryIndexOptions().ignoreIfExists(true));

// Create a JSON Document
JsonObject arthur = JsonObject.create().put("name", "Arthur").put("email", "[email protected]")
.put("interests", JsonArray.from("Holy Grail", "African Swallows"));

// Store the Document
collection.upsert("u:king_arthur", arthur);

// Load the Document and print it
// Prints Content and Metadata of the stored Document
System.err.println(collection.get("u:king_arthur"));

// Perform a N1QL Query
QueryResult result = cluster.query(String.format("SELECT name FROM `%s` WHERE $1 IN interests", bucketName),
queryOptions().parameters(JsonArray.from("African Swallows")));

// Print each found Row
for (JsonObject row : result.rowsAsObject()) {
System.err.println(row);
}

cluster.disconnect();
}

public static List<String> getClustersControlPlane() {
List<String> clusterIds = new ArrayList<>();
Map<String, Object> decoded = doRequest(hostname, "GET", "/v3/clusters");
HashMap data = (HashMap) decoded.get("data");
List<Map> items = (List<Map>) data.get("items");
for (Map m : items) {
clusterIds.add((String) m.get("id"));
}
return clusterIds;
}

public static String getClusterControlPlane(String clusterId) {
String endpointsSrv;
Map<String, Object> decoded = doRequest(hostname, "GET", "/v3/clusters/" + clusterId);
endpointsSrv = (String) decoded.get("endpointsSrv");
return endpointsSrv;
}

private static Map<String, Object> doRequest(String hostname, String cbc_api_method, String cbc_api_endpoint) {
Map<String, Object> decoded;
String responseString;
try {
String cbc_api_now = Long.toString(System.currentTimeMillis());
String authorizationValue = getApiSignature(cbc_api_method, cbc_api_endpoint, cbc_api_now);
String urlString = "https://" + hostname + cbc_api_endpoint;
System.err.println("curl --header \"" + authorizationHeaderLabel + ": " + authorizationValue + "\" --header \""
+ timestampHeaderLabel + ": " + cbc_api_now + "\" " + urlString);
Response response = httpClient.newCall(new Request.Builder().header(authorizationHeaderLabel, authorizationValue)
.header(timestampHeaderLabel, cbc_api_now).url(urlString).build()).execute();
responseString = response.body().string();
System.err.println(responseString);
} catch (IOException | NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException(e);
}

try {
decoded = (Map<String, Object>) MAPPER.readValue(responseString.getBytes(UTF_8), Map.class);
} catch (IOException e) {
throw new RuntimeException("Error decoding, raw: " + responseString, e);
}
return decoded;
}

private static String getApiSignature(String cbc_api_method, String cbc_api_endpoint, String cbc_api_now)
throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
String cbc_api_message = cbc_api_method + '\n' + cbc_api_endpoint + '\n' + cbc_api_now;
return "Bearer " + cbc_access_key + ':' + new String(Base64.getEncoder()
.encode(hmac("hmacSHA256", cbc_secret_key.getBytes("utf-8"), cbc_api_message.getBytes("utf-8"))));
}

static byte[] hmac(String algorithm, byte[] key, byte[] message)
throws NoSuchAlgorithmException, InvalidKeyException {
Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(key, algorithm));
return mac.doFinal(message);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,13 @@
package org.springframework.data.couchbase.domain;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.auditing.DateTimeProvider;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.data.couchbase.SimpleCouchbaseClientFactory;
import org.springframework.data.couchbase.cache.CouchbaseCacheConfiguration;
import org.springframework.data.couchbase.cache.CouchbaseCacheManager;
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
Expand All @@ -45,6 +42,9 @@
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;

import com.couchbase.client.core.deps.com.fasterxml.jackson.databind.DeserializationFeature;
import com.couchbase.client.core.deps.io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import com.couchbase.client.core.env.SecurityConfig;
import com.couchbase.client.java.env.ClusterEnvironment;
import com.couchbase.client.java.json.JacksonTransformers;

/**
Expand Down Expand Up @@ -109,6 +109,14 @@ public String getBucketName() {
return clusterGet("bucketName", bucketname);
}

@Override
protected void configureEnvironment(ClusterEnvironment.Builder builder) {
if (getConnectionString().contains("cloud.couchbase.com")) {
builder.securityConfig(
SecurityConfig.builder().trustManagerFactory(InsecureTrustManagerFactory.INSTANCE).enableTls(true));
}
}

@Bean(name = "auditorAwareRef")
public NaiveAuditorAware testAuditorAware() {
return new NaiveAuditorAware();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,11 @@
import org.springframework.data.util.Pair;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import com.couchbase.client.core.deps.io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import com.couchbase.client.core.env.SecurityConfig;
import com.couchbase.client.java.Collection;
import com.couchbase.client.java.ReactiveCollection;
import com.couchbase.client.java.env.ClusterEnvironment;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.kv.GetResult;
import com.couchbase.client.java.query.QueryOptions;
Expand Down Expand Up @@ -264,5 +267,13 @@ public String getBucketName() {
return bucketName();
}

@Override
protected void configureEnvironment(ClusterEnvironment.Builder builder) {
if (config().isUsingCloud()) {
builder.securityConfig(
SecurityConfig.builder().trustManagerFactory(InsecureTrustManagerFactory.INSTANCE).enableTls(true));
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
import org.springframework.data.couchbase.util.IgnoreWhen;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import com.couchbase.client.core.deps.io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import com.couchbase.client.core.env.SecurityConfig;
import com.couchbase.client.java.env.ClusterEnvironment;

/**
* Abstract Repository tests
*
Expand Down Expand Up @@ -128,6 +132,14 @@ public String getBucketName() {
return bucketName();
}

@Override
protected void configureEnvironment(ClusterEnvironment.Builder builder) {
if (config().isUsingCloud()) {
builder.securityConfig(
SecurityConfig.builder().trustManagerFactory(InsecureTrustManagerFactory.INSTANCE).enableTls(true));
}
}

/**
* This uses a CustomMappingCouchbaseConverter instead of MappingCouchbaseConverter, which in turn uses
* AbstractTypeMapper which has special mapping for AbstractUser
Expand Down
Loading