Skip to content

Commit

Permalink
Merge pull request #69 from MartinWitt/passing-falcon
Browse files Browse the repository at this point in the history
feat: Add dev service with testcontainer
  • Loading branch information
programmatix authored Oct 23, 2023
2 parents 1fe4d1f + 74b45b9 commit a795d78
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 3 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This extension is currently in alpha status. It supports:
- Dependency injecting a Couchbase `Cluster`.
- Configuring the Cluster through `application.properties`. Currently a minimal set of configuration options is provided.
- Graal/native-image, though so far it has been minimally tested with basic cases.
- A dev service that starts a Couchbase server in a Docker container. With this you can develop your Quarkus app without having to install Couchbase on your machine.

Please try it out and provide feedback, ideas and bug reports [on Github](https://github.com/quarkiverse/quarkus-couchbase/issues).

Expand Down
5 changes: 5 additions & 0 deletions deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-internal</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>couchbase</artifactId>
<version>1.18.3</version>
</dependency>
</dependencies>
<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.couchbase.quarkus.extension.deployment;


import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.eclipse.microprofile.config.ConfigProvider;
import org.testcontainers.couchbase.CouchbaseContainer;
import org.testcontainers.couchbase.CouchbaseService;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.BuildSteps;
import io.quarkus.deployment.builditem.CuratedApplicationShutdownBuildItem;
import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
import io.quarkus.deployment.builditem.DevServicesResultBuildItem.RunningDevService;
import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig;

/**
* This class provides a Quarkus DevService for Couchbase. The dev service provides all non enterprise features of Couchbase.
*/
@BuildSteps(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class)

public class CouchbaseDevService {


static volatile List<RunningDevService> devServices = new ArrayList<>();

@BuildStep
List<DevServicesResultBuildItem> startCouchBase(
CuratedApplicationShutdownBuildItem closeBuildItem) {
QuarkusCouchbaseContainer couchbase = startContainer();
devServices.add(new RunningDevService(CouchbaseQuarkusExtensionProcessor.FEATURE,
couchbase.getContainerId(), couchbase::close, Map.of()));
closeBuildItem.addCloseTask(couchbase::close, true);
return devServices.stream().map(RunningDevService::toBuildItem).collect(Collectors.toList());

}

private QuarkusCouchbaseContainer startContainer() {
String userName = ConfigProvider.getConfig()
.getOptionalValue("quarkus.couchbase.username", String.class).orElse("Administrator");;
String password = ConfigProvider.getConfig()
.getOptionalValue("quarkus.couchbase.password", String.class).orElse("password");
String version = ConfigProvider.getConfig().getValue("quarkus.couchbase.version", String.class);
QuarkusCouchbaseContainer couchbase =
new QuarkusCouchbaseContainer(version, userName, password);
couchbase.start();
return couchbase;
}

/**
* A {@link CouchbaseContainer} that exposes all ports to fixed values.
* <p>
* This is needed because the default {@link CouchbaseContainer} exposes ports to random values.
*/
private static class QuarkusCouchbaseContainer extends CouchbaseContainer {

// copied from org.testcontainers.couchbase.CouchbaseContainer
private static final int MGMT_PORT = 8091;
private static final int MGMT_SSL_PORT = 18091;
private static final int VIEW_PORT = 8092;
private static final int VIEW_SSL_PORT = 18092;
private static final int QUERY_PORT = 8093;
private static final int QUERY_SSL_PORT = 18093;
private static final int SEARCH_PORT = 8094;
private static final int SEARCH_SSL_PORT = 18094;
private static final int ANALYTICS_PORT = 8095;
private static final int ANALYTICS_SSL_PORT = 18095;
private static final int EVENTING_PORT = 8096;
private static final int EVENTING_SSL_PORT = 18096;
private static final int KV_PORT = 11210;
private static final int KV_SSL_PORT = 11207;


public QuarkusCouchbaseContainer(String version, String userName, String password) {
super("couchbase/server:" + version);
withCredentials(userName, password);
// we enable all non-enterprise services because we don't know which ones are needed
withEnabledServices(CouchbaseService.EVENTING, CouchbaseService.INDEX, CouchbaseService.KV,
CouchbaseService.QUERY, CouchbaseService.SEARCH);
addFixedExposedPort(MGMT_PORT, MGMT_PORT);
addFixedExposedPort(MGMT_SSL_PORT, MGMT_SSL_PORT);
addFixedExposedPort(ANALYTICS_PORT, ANALYTICS_PORT);
addFixedExposedPort(VIEW_PORT, VIEW_PORT);
addFixedExposedPort(VIEW_SSL_PORT, VIEW_SSL_PORT);
addFixedExposedPort(ANALYTICS_PORT, ANALYTICS_PORT);
addFixedExposedPort(ANALYTICS_SSL_PORT, ANALYTICS_SSL_PORT);
addFixedExposedPort(QUERY_PORT, QUERY_PORT);
addFixedExposedPort(QUERY_SSL_PORT, QUERY_SSL_PORT);
addFixedExposedPort(SEARCH_PORT, SEARCH_PORT);
addFixedExposedPort(SEARCH_SSL_PORT, SEARCH_SSL_PORT);
addFixedExposedPort(EVENTING_PORT, EVENTING_PORT);
addFixedExposedPort(EVENTING_SSL_PORT, EVENTING_SSL_PORT);
addFixedExposedPort(KV_PORT, KV_PORT);
addFixedExposedPort(KV_SSL_PORT, KV_SSL_PORT);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import io.quarkus.deployment.builditem.FeatureBuildItem;

class CouchbaseQuarkusExtensionProcessor {
private static final String FEATURE = "quarkus-couchbase";
public static final String FEATURE = "quarkus-couchbase";

@BuildStep
FeatureBuildItem feature() {
Expand Down
5 changes: 3 additions & 2 deletions integration-tests/src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
quarkus.couchbase.connection-string=localhost
quarkus.couchbase.username=admin
quarkus.couchbase.password=admin
quarkus.couchbase.username=Administrator
# needs to be at least 8 characters
quarkus.couchbase.password=password
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.couchbase.quarkus.extension.it;

import java.time.Duration;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.Collection;
import com.couchbase.client.java.Scope;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.kv.GetResult;
import com.couchbase.client.java.kv.MutationResult;
import com.couchbase.client.java.manager.bucket.BucketSettings;
import com.couchbase.client.java.manager.collection.CollectionSpec;
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
public class DevServiceTest {


Cluster cluster;

public DevServiceTest(Cluster cluster) {
this.cluster = cluster;
}

@Test
void createBucketTest() {
// tests creating a bucket and waiting for it to be ready
cluster.buckets().createBucket(BucketSettings.create("testBucket"));
cluster.bucket("testBucket").waitUntilReady(Duration.ofMinutes(2));
Assertions.assertNotNull(cluster.buckets().getAllBuckets().get("testBucket"));
cluster.buckets().dropBucket("testBucket");
}

@Test
void insertDocument() {
// this is an adapted example from the couchbase doc and licensed under their terms.
//TODO: what license is this under?
String bucketName = "travel-sample";
cluster.buckets().createBucket(BucketSettings.create(bucketName));
// get a bucket reference
Bucket bucket = cluster.bucket(bucketName);
bucket.waitUntilReady(Duration.ofSeconds(120));
bucket.collections().createScope("tenant_agent_00");
bucket.collections().createCollection(CollectionSpec.create("users", "tenant_agent_00"));
// get a user-defined collection reference
Scope scope = bucket.scope("tenant_agent_00");
Collection collection = scope.collection("users");

// Upsert Document
MutationResult upsertResult =
collection.upsert("my-document", JsonObject.create().put("name", "mike"));
Assertions.assertNotNull(upsertResult,
"Upsert result is null and therefore the operation failed.");
// Get Document
GetResult getResult = collection.get("my-document");
String name = getResult.contentAsObject().getString("name");
Assertions.assertEquals("mike", name);
// cleanup afterwards
// TODO: maybe we need a @AfterEach method to clean up?
cluster.buckets().dropBucket(bucketName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.couchbase.quarkus.extension.runtime;

import java.util.List;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
Expand All @@ -38,4 +39,10 @@ public final class CouchbaseConfig {
*/
@ConfigItem
public String password;
/**
* The version of the Couchbase server to connect to during the dev/test phase as dev service.
* Default is latest, see https://hub.docker.com/_/couchbase for available versions.
*/
@ConfigItem(defaultValue = "latest")
public String version;
}

0 comments on commit a795d78

Please sign in to comment.