Skip to content

Commit 47f637b

Browse files
committed
feat: allow cli to connect to a couchbase auditstore
1 parent f69dc74 commit 47f637b

File tree

8 files changed

+345
-27
lines changed

8 files changed

+345
-27
lines changed

cli/flamingock-cli/CLI-README.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,19 @@ flamingock:
115115
# secret-key: "your-secret" # Optional, uses AWS credential chain
116116
```
117117

118+
### Couchbase Example
119+
```yaml
120+
flamingock:
121+
service-identifier: "flamingock-cli"
122+
audit:
123+
couchbase:
124+
endpoint: "http://localhost:8000"
125+
username: "your-username"
126+
password: "your-password"
127+
bucket-name: "test"
128+
table: "flamingockAuditLog" # Optional, defaults to "flamingockAuditLog"
129+
```
130+
118131
### Configuration File Resolution
119132
1. Command line argument: `--config /path/to/file.yml`
120133
2. Default: `flamingock.yml` in current directory
@@ -144,6 +157,7 @@ client.markAsRolledBack(changeId);
144157
### Database Support
145158
- **MongoDB**: Uses MongoDB Sync driver (not Spring Data)
146159
- **DynamoDB**: Uses AWS SDK v2
160+
- **Couchbase**: Uses Couchbase driver v3.7.3
147161
- **Auto-detection**: Based on YAML configuration structure
148162
- **Validation**: Connection testing during client creation
149163

@@ -179,12 +193,12 @@ Uses SLF4J with simple implementation for clean, professional output.
179193
## Build System Integration
180194

181195
### Gradle Tasks
182-
| Task | Description |
183-
|------|-------------|
184-
| `:cli:flamingock-cli:build` | Full build with distribution |
185-
| `:cli:flamingock-cli:uberJar` | Create self-contained JAR only |
196+
| Task | Description |
197+
|--------------------------------------------|----------------------------------|
198+
| `:cli:flamingock-cli:build` | Full build with distribution |
199+
| `:cli:flamingock-cli:uberJar` | Create self-contained JAR only |
186200
| `:cli:flamingock-cli:generateDistribution` | Generate executable scripts only |
187-
| `:cli:flamingock-cli:clean` | Remove all CLI artifacts |
201+
| `:cli:flamingock-cli:clean` | Remove all CLI artifacts |
188202

189203
### Automatic Distribution
190204
The build process automatically:

cli/flamingock-cli/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ dependencies {
2323
// Database clients (community edition)
2424
implementation("org.mongodb:mongodb-driver-sync:4.9.1")
2525
implementation("software.amazon.awssdk:dynamodb:2.20.0")
26+
implementation ("com.couchbase.client:java-client:3.7.3")
2627

2728
// SLF4J API - needed for interface compatibility (provided by flamingock-core)
2829
// implementation("org.slf4j:slf4j-api:1.7.36") // Already provided by core dependencies
@@ -36,6 +37,7 @@ dependencies {
3637
testImplementation("org.assertj:assertj-core:3.24.2")
3738
testImplementation("org.testcontainers:junit-jupiter:1.19.3")
3839
testImplementation("org.testcontainers:mongodb:1.19.3")
40+
testImplementation("org.testcontainers:couchbase:1.21.3")
3941
testImplementation("com.github.stefanbirkner:system-lambda:1.2.1")
4042

4143
}

cli/flamingock-cli/src/dist/flamingock-sample.yml

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,22 @@ flamingock:
77
mongodb:
88
connection-string: "mongodb://localhost:27017"
99
database: "test"
10-
collection: "flamingockAuditLog" # Optional, defaults to "flamingockAuditLog"
10+
collection: "flamingockAuditLog" # Optional, defaults to "flamingockAuditLog"
1111

12-
# DynamoDB Configuration (uncomment and modify to use)
13-
# audit:
14-
# dynamodb:
15-
# region: "us-east-1"
16-
# table: "flamingockAuditLog" # Optional, defaults to "flamingockAuditLog"
17-
# # endpoint: "http://localhost:8000" # Optional for local DynamoDB
18-
# # access-key: "your-access-key" # Optional if using IAM roles
19-
# # secret-key: "your-secret-key" # Optional if using IAM roles
12+
# DynamoDB Configuration (uncomment and modify to use)
13+
# audit:
14+
# dynamodb:
15+
# region: "us-east-1"
16+
# table: "flamingockAuditLog" # Optional, defaults to "flamingockAuditLog"
17+
# # endpoint: "http://localhost:8000" # Optional for local DynamoDB
18+
# # access-key: "your-access-key" # Optional if using IAM roles
19+
# # secret-key: "your-secret-key" # Optional if using IAM roles
20+
21+
# Couchbase Configuration (uncomment and modify to use)
22+
# audit:
23+
# couchbase:
24+
# endpoint: "http://localhost:8000"
25+
# username: "your-username"
26+
# password: "your-password"
27+
# bucket-name: "test"
28+
# table: "flamingockAuditLog" # Optional, defaults to "flamingockAuditLog"

cli/flamingock-cli/src/main/java/io/flamingock/cli/config/ConfigLoader.java

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public static FlamingockConfig loadConfig(String configFilePath) throws IOExcept
4141
@SuppressWarnings("unchecked")
4242
private static FlamingockConfig parseConfig(Map<String, Object> yamlData) {
4343
FlamingockConfig config = new FlamingockConfig();
44-
44+
4545
Map<String, Object> flamingockData = (Map<String, Object>) yamlData.get("flamingock");
4646
if (flamingockData == null) {
4747
throw new IllegalArgumentException("Missing 'flamingock' section in configuration file");
@@ -56,7 +56,7 @@ private static FlamingockConfig parseConfig(Map<String, Object> yamlData) {
5656
Map<String, Object> auditData = (Map<String, Object>) flamingockData.get("audit");
5757
if (auditData != null) {
5858
DatabaseConfig databaseConfig = new DatabaseConfig();
59-
59+
6060
// Parse MongoDB config
6161
Map<String, Object> mongoData = (Map<String, Object>) auditData.get("mongodb");
6262
if (mongoData != null) {
@@ -71,7 +71,7 @@ private static FlamingockConfig parseConfig(Map<String, Object> yamlData) {
7171
}
7272
databaseConfig.setMongodb(mongoConfig);
7373
}
74-
74+
7575
// Parse DynamoDB config
7676
Map<String, Object> dynamoData = (Map<String, Object>) auditData.get("dynamodb");
7777
if (dynamoData != null) {
@@ -85,7 +85,21 @@ private static FlamingockConfig parseConfig(Map<String, Object> yamlData) {
8585
}
8686
databaseConfig.setDynamodb(dynamoConfig);
8787
}
88-
88+
89+
// Parse Couchbase config
90+
Map<String, Object> couchbaseData = (Map<String, Object>) auditData.get("couchbase");
91+
if (couchbaseData != null) {
92+
DatabaseConfig.CouchbaseConfig couchbaseConfig = new DatabaseConfig.CouchbaseConfig();
93+
couchbaseConfig.setBucketName((String) couchbaseData.get("bucket-name"));
94+
couchbaseConfig.setEndpoint((String) couchbaseData.get("endpoint"));
95+
couchbaseConfig.setUsername((String) couchbaseData.get("username"));
96+
couchbaseConfig.setPassword((String) couchbaseData.get("password"));
97+
if (couchbaseData.get("properties") != null) {
98+
couchbaseConfig.setProperties((Map<String, String>) couchbaseData.get("properties"));
99+
}
100+
databaseConfig.setCouchbase(couchbaseConfig);
101+
}
102+
89103
config.setAudit(databaseConfig);
90104
}
91105

@@ -96,24 +110,27 @@ public static DatabaseType detectDatabaseType(FlamingockConfig config) {
96110
if (config.getAudit() == null) {
97111
throw new IllegalArgumentException("No audit configuration found");
98112
}
99-
113+
100114
boolean hasMongoDB = config.getAudit().getMongodb() != null;
101115
boolean hasDynamoDB = config.getAudit().getDynamodb() != null;
102-
103-
if (hasMongoDB && hasDynamoDB) {
116+
boolean hasCouchbase = config.getAudit().getCouchbase() != null;
117+
118+
if (hasMongoDB && hasDynamoDB) { // TODO: Check if more than one DB is configured
104119
throw new IllegalArgumentException("Multiple database configurations found. Please configure only one database type.");
105120
}
106-
121+
107122
if (hasMongoDB) {
108123
return DatabaseType.MONGODB;
109124
} else if (hasDynamoDB) {
110125
return DatabaseType.DYNAMODB;
126+
} else if (hasCouchbase) {
127+
return DatabaseType.COUCHBASE;
111128
} else {
112-
throw new IllegalArgumentException("No supported database configuration found. Please configure MongoDB or DynamoDB.");
129+
throw new IllegalArgumentException("No supported database configuration found. Please configure MongoDB, DynamoDB or Couchbase.");
113130
}
114131
}
115132

116133
public enum DatabaseType {
117-
MONGODB, DYNAMODB
134+
MONGODB, DYNAMODB, COUCHBASE
118135
}
119-
}
136+
}

cli/flamingock-cli/src/main/java/io/flamingock/cli/config/DatabaseConfig.java

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
public class DatabaseConfig {
2121
private MongoDBConfig mongodb;
2222
private DynamoDBConfig dynamodb;
23+
private CouchbaseConfig couchbase;
2324

2425
public MongoDBConfig getMongodb() {
2526
return mongodb;
@@ -37,6 +38,14 @@ public void setDynamodb(DynamoDBConfig dynamodb) {
3738
this.dynamodb = dynamodb;
3839
}
3940

41+
public CouchbaseConfig getCouchbase() {
42+
return couchbase;
43+
}
44+
45+
public void setCouchbase(CouchbaseConfig couchbase) {
46+
this.couchbase = couchbase;
47+
}
48+
4049
public static class MongoDBConfig {
4150
private String connectionString;
4251
private String database;
@@ -141,4 +150,52 @@ public void setProperties(Map<String, String> properties) {
141150
this.properties = properties;
142151
}
143152
}
144-
}
153+
154+
public static class CouchbaseConfig {
155+
private String bucketName;
156+
private String endpoint;
157+
private String username;
158+
private String password;
159+
private Map<String, String> properties;
160+
161+
public String getBucketName() {
162+
return bucketName;
163+
}
164+
165+
public void setBucketName(String bucketName) {
166+
this.bucketName = bucketName;
167+
}
168+
169+
public String getEndpoint() {
170+
return endpoint;
171+
}
172+
173+
public void setEndpoint(String endpoint) {
174+
this.endpoint = endpoint;
175+
}
176+
177+
public String getUsername() {
178+
return username;
179+
}
180+
181+
public void setUsername(String username) {
182+
this.username = username;
183+
}
184+
185+
public String getPassword() {
186+
return password;
187+
}
188+
189+
public void setPassword(String password) {
190+
this.password = password;
191+
}
192+
193+
public Map<String, String> getProperties() {
194+
return properties;
195+
}
196+
197+
public void setProperties(Map<String, String> properties) {
198+
this.properties = properties;
199+
}
200+
}
201+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2025 Flamingock (https://www.flamingock.io)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.flamingock.cli.factory;
17+
18+
import com.couchbase.client.java.Cluster;
19+
import io.flamingock.cli.config.DatabaseConfig;
20+
21+
public class CouchbaseClusterFactory {
22+
23+
public static Cluster createCouchbaseCluster(DatabaseConfig.CouchbaseConfig config) {
24+
if (config == null) {
25+
throw new IllegalArgumentException("Couchbase configuration is required");
26+
}
27+
28+
if (config.getBucketName() == null) {
29+
throw new IllegalArgumentException("Bucket name is required");
30+
}
31+
32+
if (config.getEndpoint() == null) {
33+
throw new IllegalArgumentException("Couchbase endpoint is required");
34+
}
35+
36+
if (config.getUsername() == null) {
37+
throw new IllegalArgumentException("Couchbase username is required");
38+
}
39+
40+
if (config.getPassword() == null) {
41+
throw new IllegalArgumentException("Couchbase password is required");
42+
}
43+
44+
try {
45+
Cluster couchbaseCluster = Cluster.connect(config.getEndpoint(), config.getUsername(), config.getPassword());
46+
47+
// Test the connection by listing collections in bucket
48+
couchbaseCluster.bucket(config.getBucketName()).collections();
49+
50+
return couchbaseCluster;
51+
} catch (Exception e) {
52+
throw new RuntimeException("Failed to create Couchbase cluster: " + e.getMessage(), e);
53+
}
54+
}
55+
}

cli/flamingock-cli/src/main/java/io/flamingock/cli/service/AuditService.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@
1515
*/
1616
package io.flamingock.cli.service;
1717

18+
import com.couchbase.client.java.Cluster;
1819
import com.mongodb.client.MongoClient;
19-
import com.mongodb.client.MongoDatabase;
2020
import io.flamingock.cli.config.ConfigLoader;
2121
import io.flamingock.cli.config.DatabaseConfig;
2222
import io.flamingock.cli.config.FlamingockConfig;
23+
import io.flamingock.cli.factory.CouchbaseClusterFactory;
2324
import io.flamingock.cli.factory.DynamoDBClientFactory;
2425
import io.flamingock.cli.factory.MongoClientFactory;
26+
import io.flamingock.community.couchbase.driver.CouchbaseAuditStore;
2527
import io.flamingock.community.dynamodb.driver.DynamoDBAuditStore;
2628
import io.flamingock.community.mongodb.sync.driver.MongoDBSyncAuditStore;
2729
import io.flamingock.internal.common.core.audit.AuditEntry;
@@ -138,6 +140,8 @@ private AuditStore<?> createAuditStore(Context context) {
138140
return createMongoAuditStore(context);
139141
case DYNAMODB:
140142
return createDynamoAuditStore(context);
143+
case COUCHBASE:
144+
return createCouchbaseAuditStore(context);
141145
default:
142146
throw new IllegalStateException("Unsupported database type: " + databaseType);
143147
}
@@ -160,4 +164,13 @@ private AuditStore<?> createDynamoAuditStore(Context context) {
160164

161165
return new DynamoDBAuditStore(dynamoClient);
162166
}
167+
168+
private AuditStore<?> createCouchbaseAuditStore(Context context) {
169+
DatabaseConfig.CouchbaseConfig couchbaseConfig = config.getAudit().getCouchbase();
170+
171+
// Create Couchbase cluster
172+
Cluster couchbaseCluster = CouchbaseClusterFactory.createCouchbaseCluster(couchbaseConfig);
173+
174+
return new CouchbaseAuditStore(couchbaseCluster, couchbaseConfig.getBucketName());
175+
}
163176
}

0 commit comments

Comments
 (0)