diff --git a/spring/couchbase-springboot-v3-driver/pom.xml b/spring/couchbase-springboot-v3-driver/pom.xml
new file mode 100644
index 0000000..2a2b7f5
--- /dev/null
+++ b/spring/couchbase-springboot-v3-driver/pom.xml
@@ -0,0 +1,100 @@
+
+
+ 4.0.0
+
+
+ io.mongock
+ spring-jdk17
+ 5.3.6-SNAPSHOT
+
+
+ couchbase-springboot-v3-driver
+ jar
+
+
+
+
+ io.mongock
+ mongock-driver-couchbase-bom
+ ${mongock.community.version}
+ pom
+ import
+
+
+
+
+
+
+
+ io.mongock
+ mongock-driver-core
+
+
+ io.mongock
+ mongock-api
+
+
+ io.mongock
+ couchbase-driver
+
+
+ io.mongock
+ mongock-springboot-v3
+ ${project.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ ${spring-boot-3.version}
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ ${spring-boot-3.version}
+ true
+
+
+ org.springframework.data
+ spring-data-couchbase
+ ${spring-data-5.couchbase.version}
+
+
+ com.couchbase.client
+ java-client
+ ${couchbase-java-client.version}
+
+
+
+
+ io.mongock
+ mongock-test-util
+ ${mongock.community.version}
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring-boot-3.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit.jupiter.version}
+ test
+
+
+ org.slf4j
+ slf4j-simple
+ ${slf4j-api.version}
+ test
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring/couchbase-springboot-v3-driver/src/main/java/io/mongock/driver/couchbase/springboot/config/CouchbaseConfiguration.java b/spring/couchbase-springboot-v3-driver/src/main/java/io/mongock/driver/couchbase/springboot/config/CouchbaseConfiguration.java
new file mode 100644
index 0000000..c4ac909
--- /dev/null
+++ b/spring/couchbase-springboot-v3-driver/src/main/java/io/mongock/driver/couchbase/springboot/config/CouchbaseConfiguration.java
@@ -0,0 +1,42 @@
+package io.mongock.driver.couchbase.springboot.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Configuration properties for Mongock Couchbase.
+ *
+ * @author Tigran Babloyan
+ */
+@Configuration
+@ConfigurationProperties("mongock.couchbase")
+public class CouchbaseConfiguration {
+ /**
+ * The custom scope to be used by the Mongock.
+ * Can be used for Couchbase Server 7+ to set custom scope on the stored data.
+ *
+ * Note: If scope is set the collection needs to be set as well.
+ */
+ private String scope;
+ /**
+ * The custom collection to be used by the Mongock.
+ * Can be used for Couchbase Server 7+ to set custom collection on the stored data.
+ */
+ private String collection;
+
+ public String getScope() {
+ return scope;
+ }
+
+ public void setScope(String scope) {
+ this.scope = scope;
+ }
+
+ public String getCollection() {
+ return collection;
+ }
+
+ public void setCollection(String collection) {
+ this.collection = collection;
+ }
+}
diff --git a/spring/couchbase-springboot-v3-driver/src/main/java/io/mongock/driver/couchbase/springboot/config/CouchbaseSpringbootContext.java b/spring/couchbase-springboot-v3-driver/src/main/java/io/mongock/driver/couchbase/springboot/config/CouchbaseSpringbootContext.java
new file mode 100644
index 0000000..c7d360b
--- /dev/null
+++ b/spring/couchbase-springboot-v3-driver/src/main/java/io/mongock/driver/couchbase/springboot/config/CouchbaseSpringbootContext.java
@@ -0,0 +1,52 @@
+package io.mongock.driver.couchbase.springboot.config;
+
+import com.couchbase.client.java.Collection;
+import io.mongock.api.config.MongockConfiguration;
+import io.mongock.driver.api.driver.ConnectionDriver;
+import io.mongock.driver.couchbase.driver.CouchbaseDriver;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.couchbase.CouchbaseClientFactory;
+import org.springframework.util.StringUtils;
+
+
+/**
+ * {@link EnableAutoConfiguration Auto-configuration} for Mongock Couchbase support.
+ *
+ * @author Tigran Babloyan
+ */
+@AutoConfiguration
+@ConditionalOnExpression("${mongock.enabled:true}")
+@ConditionalOnBean({MongockConfiguration.class, CouchbaseClientFactory.class})
+@EnableConfigurationProperties(CouchbaseConfiguration.class)
+@AutoConfigureAfter(CouchbaseDataAutoConfiguration.class)
+public class CouchbaseSpringbootContext {
+
+ @Bean
+ public ConnectionDriver connectionDriver(CouchbaseClientFactory couchbaseClientFactory,
+ CouchbaseConfiguration couchbaseConfiguration,
+ MongockConfiguration mongockConfig) {
+ Collection collection = isCustomCollection(couchbaseConfiguration) ?
+ couchbaseClientFactory.withScope(couchbaseConfiguration.getScope()).getCollection(couchbaseConfiguration.getCollection()) :
+ couchbaseClientFactory.getDefaultCollection();
+ CouchbaseDriver driver = CouchbaseDriver.withLockStrategy(couchbaseClientFactory.getCluster(),
+ collection,
+ mongockConfig.getLockAcquiredForMillis(),
+ mongockConfig.getLockQuitTryingAfterMillis(),
+ mongockConfig.getLockTryFrequencyMillis());
+ driver.setIndexCreation(mongockConfig.isIndexCreation());
+ return driver;
+ }
+
+ private boolean isCustomCollection(CouchbaseConfiguration couchbaseConfiguration){
+ return StringUtils.hasText(couchbaseConfiguration.getCollection()) &&
+ StringUtils.hasText(couchbaseConfiguration.getScope());
+ }
+}
diff --git a/spring/couchbase-springboot-v3-driver/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring/couchbase-springboot-v3-driver/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000..b535b48
--- /dev/null
+++ b/spring/couchbase-springboot-v3-driver/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+io.mongock.driver.couchbase.springboot.config.CouchbaseSpringbootContext
\ No newline at end of file
diff --git a/spring/couchbase-springboot-v3-driver/src/test/java/io/mongock/driver/couchbase/springboot/config/CouchbaseSpringbootContextTest.java b/spring/couchbase-springboot-v3-driver/src/test/java/io/mongock/driver/couchbase/springboot/config/CouchbaseSpringbootContextTest.java
new file mode 100644
index 0000000..77eef54
--- /dev/null
+++ b/spring/couchbase-springboot-v3-driver/src/test/java/io/mongock/driver/couchbase/springboot/config/CouchbaseSpringbootContextTest.java
@@ -0,0 +1,114 @@
+package io.mongock.driver.couchbase.springboot.config;
+
+import com.couchbase.client.core.io.CollectionIdentifier;
+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 io.mongock.api.config.MongockConfiguration;
+import io.mongock.driver.api.driver.ConnectionDriver;
+import io.mongock.driver.couchbase.driver.CouchbaseDriver;
+import io.mongock.runner.springboot.base.config.MongockSpringConfiguration;
+import io.mongock.util.test.ReflectionUtils;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.data.couchbase.SimpleCouchbaseClientFactory;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.*;
+
+class CouchbaseSpringbootContextTest {
+
+ private static final String NONE_DEFAULT_NAME = "nondefault";
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
+
+ @Test
+ void test_driver_autoconfigured() {
+ contextRunner
+ .withPropertyValues("mongock.enabled=true")
+ .withBean(SimpleCouchbaseClientFactory.class, mockCluster(), "mongock", null)
+ .withConfiguration(AutoConfigurations.of(
+ MongockConfiguration.class,
+ CouchbaseSpringbootContext.class))
+ .run(context -> {
+ assertThat(context).hasSingleBean(ConnectionDriver.class);
+ CouchbaseDriver driver = context.getBean(CouchbaseDriver.class);
+ assertThat(collectionName(driver)).isEqualTo(CollectionIdentifier.DEFAULT_COLLECTION);
+ assertThat(driver.isIndexCreation()).isTrue();
+ }
+ );
+ }
+
+ @Test
+ void test_driver_missing_on_mongock_disabled() {
+ contextRunner
+ .withPropertyValues("mongock.enabled=false")
+ .withBean(SimpleCouchbaseClientFactory.class, mockCluster(), "mongock", null)
+ .withConfiguration(AutoConfigurations.of(
+ MongockConfiguration.class,
+ CouchbaseSpringbootContext.class))
+ .run((context) -> assertThat(context)
+ .doesNotHaveBean(ConnectionDriver.class));
+ }
+
+ @Test
+ void test_context_collection_modifications() {
+ contextRunner
+ .withPropertyValues("mongock.enabled=true", "mongock.couchbase.scope=nondefault", "mongock.couchbase.collection=nondefault")
+ .withBean(SimpleCouchbaseClientFactory.class, mockCluster(), "mongock", null)
+ .withConfiguration(AutoConfigurations.of(
+ MongockConfiguration.class,
+ CouchbaseSpringbootContext.class))
+ .run(context -> {
+ assertThat(context).hasSingleBean(ConnectionDriver.class);
+ CouchbaseDriver driver = context.getBean(CouchbaseDriver.class);
+ assertThat(collectionName(driver)).isEqualTo(NONE_DEFAULT_NAME);
+ assertThat(driver.isIndexCreation()).isTrue();
+ }
+ );
+ }
+
+ @Test
+ void test_context_index_creation_disabled() {
+ contextRunner
+ .withPropertyValues("mongock.enabled=true", "mongock.index-creation=false", "mongock.indexCreation=false")
+ .withBean(SimpleCouchbaseClientFactory.class, mockCluster(), "mongock", null)
+ .withUserConfiguration(
+ MongockSpringConfiguration.class,
+ CouchbaseSpringbootContext.class
+ )
+ .run(context -> {
+ assertThat(context).hasSingleBean(ConnectionDriver.class);
+ CouchbaseDriver driver = context.getBean(CouchbaseDriver.class);
+ assertThat(driver.isIndexCreation()).isFalse();
+ }
+ );
+ }
+
+ private String collectionName(ConnectionDriver connectionDriver) {
+ Collection collection = (Collection) ReflectionUtils.getPrivateField(connectionDriver, CouchbaseDriver.class, "collection");
+ return collection.name();
+ }
+
+ private Cluster mockCluster() {
+ Cluster cluster = mock(Cluster.class);
+ Bucket bucket = mock(Bucket.class);
+ Scope defaultScope = mock(Scope.class);
+ Scope customeScope = mock(Scope.class);
+ Collection defaultCollection = mock(Collection.class);
+ doReturn(CollectionIdentifier.DEFAULT_COLLECTION).when(defaultCollection).name();
+ Collection customCollection = mock(Collection.class);
+ doReturn(NONE_DEFAULT_NAME).when(customCollection).name();
+ doReturn(bucket).when(cluster).bucket(any());
+ doReturn(defaultScope).when(bucket).defaultScope();
+ doReturn(defaultScope).when(bucket).scope(eq(CollectionIdentifier.DEFAULT_SCOPE));
+ doReturn(customeScope).when(bucket).scope(eq(NONE_DEFAULT_NAME));
+ doReturn(defaultCollection).when(bucket).defaultCollection();
+ doReturn(defaultCollection).when(defaultScope).collection(any());
+ doReturn(customCollection).when(customeScope).collection(any());
+ doReturn(CollectionIdentifier.DEFAULT_SCOPE).when(defaultScope).name();
+ return cluster;
+ }
+
+}
diff --git a/spring/pom.xml b/spring/pom.xml
index 7ae0ecb..0fa8993 100644
--- a/spring/pom.xml
+++ b/spring/pom.xml
@@ -17,10 +17,13 @@
[3.0.0-RC1, 4.0.0)
4.0.0-RC1
4.8.0-beta0
+ 5.2.1
+ 3.5.1
mongock-springboot-v3
mongodb-springdata-v4-driver
+ couchbase-springboot-v3-driver