Skip to content

Commit 13e6ea0

Browse files
authored
4.x: Fix Mongo Dbclient + related example (helidon-io#8130)
* Mongo db fix + example tests Signed-off-by: tvallin <thibault.vallin@oracle.com>
1 parent 32a0c1f commit 13e6ea0

13 files changed

Lines changed: 346 additions & 56 deletions

File tree

dbclient/mongodb/src/main/java/io/helidon/dbclient/mongodb/StatementParsers.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ private static CharClass charClass(char c) {
223223
},
224224
// Transitions from STRING state
225225
{
226+
//LETTER: regular part of the JSON string, keep processing it
227+
State.STRING,
226228
// NUMBER: regular part of the JSON string, keep processing it
227229
State.STRING,
228230
// QUOTE: end of JSON string processing, go back to STATEMENT state

dependencies/pom.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@
146146
<version.lib.slf4j>2.0.9</version.lib.slf4j>
147147
<version.lib.smallrye-openapi>3.3.4</version.lib.smallrye-openapi>
148148
<version.lib.snakeyaml>2.0</version.lib.snakeyaml>
149+
<version.lib.testcontainers>1.19.3</version.lib.testcontainers>
149150
<version.lib.typesafe-config>1.4.2</version.lib.typesafe-config>
150151
<version.lib.tyrus>2.1.4</version.lib.tyrus>
151152
<version.lib.weld-api>5.0.SP3</version.lib.weld-api>
@@ -1265,6 +1266,16 @@
12651266
<artifactId>hamcrest-core</artifactId>
12661267
<version>${version.lib.hamcrest}</version>
12671268
</dependency>
1269+
<dependency>
1270+
<groupId>org.testcontainers</groupId>
1271+
<artifactId>junit-jupiter</artifactId>
1272+
<version>${version.lib.testcontainers}</version>
1273+
</dependency>
1274+
<dependency>
1275+
<groupId>org.testcontainers</groupId>
1276+
<artifactId>mongodb</artifactId>
1277+
<version>${version.lib.testcontainers}</version>
1278+
</dependency>
12681279
<dependency>
12691280
<groupId>org.apache.activemq</groupId>
12701281
<artifactId>activemq-kahadb-store</artifactId>

examples/dbclient/mongodb/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ The query operation adds database trace.
3838
`curl` commands:
3939

4040
- `curl http://localhost:8079/db` - list all Pokemon in the database
41-
- `curl -i -X PUT -d '{"name":"Squirtle","type":"water"}' http://localhost:8079/db` - add a new pokemon
41+
- `curl -i -X PUT -H 'Content-type: application/json' -d '{"name":"Squirtle","type":"water"}' http://localhost:8079/db` - add a new pokemon
4242
- `curl http://localhost:8079/db/Squirtle` - get a single pokemon
4343
- `curl -i -X DELETE http://localhost:8079/db/Squirtle` - delete a single pokemon
4444
- `curl -i -X DELETE http://localhost:8079/db` - delete all pokemon

examples/dbclient/mongodb/pom.xml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,26 @@
105105
<artifactId>helidon-examples-dbclient-common</artifactId>
106106
<version>${project.version}</version>
107107
</dependency>
108+
<dependency>
109+
<groupId>io.helidon.logging</groupId>
110+
<artifactId>helidon-logging-jul</artifactId>
111+
<scope>runtime</scope>
112+
</dependency>
113+
<dependency>
114+
<groupId>org.testcontainers</groupId>
115+
<artifactId>junit-jupiter</artifactId>
116+
<scope>test</scope>
117+
</dependency>
118+
<dependency>
119+
<groupId>org.testcontainers</groupId>
120+
<artifactId>mongodb</artifactId>
121+
<scope>test</scope>
122+
</dependency>
123+
<dependency>
124+
<groupId>io.helidon.webserver.testing.junit5</groupId>
125+
<artifactId>helidon-webserver-testing-junit5</artifactId>
126+
<scope>test</scope>
127+
</dependency>
108128
</dependencies>
109129

110130
<build>
@@ -118,6 +138,18 @@
118138
</execution>
119139
</executions>
120140
</plugin>
141+
<plugin>
142+
<groupId>org.apache.maven.plugins</groupId>
143+
<artifactId>maven-failsafe-plugin</artifactId>
144+
<executions>
145+
<execution>
146+
<goals>
147+
<goal>integration-test</goal>
148+
<goal>verify</goal>
149+
</goals>
150+
</execution>
151+
</executions>
152+
</plugin>
121153
</plugins>
122154
</build>
123155
</project>

examples/dbclient/mongodb/src/main/java/io/helidon/examples/dbclient/mongo/MongoDbExampleMain.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.helidon.dbclient.tracing.DbClientTracing;
2424
import io.helidon.logging.common.LogConfig;
2525
import io.helidon.webserver.WebServer;
26+
import io.helidon.webserver.WebServerConfig;
2627
import io.helidon.webserver.http.HttpRouting;
2728

2829
/**
@@ -55,17 +56,20 @@ static WebServer startServer() {
5556
// load logging configuration
5657
LogConfig.configureRuntime();
5758

59+
WebServer server = setupServer(WebServer.builder());
60+
61+
System.out.println("WEB server is up! http://localhost:" + server.port() + "/");
62+
return server;
63+
}
64+
65+
static WebServer setupServer(WebServerConfig.Builder builder) {
5866
// By default, this will pick up application.yaml from the classpath
5967
Config config = Config.create();
6068

61-
WebServer server = WebServer.builder()
62-
.routing(routing -> routing(routing, config))
69+
return builder.routing(routing -> routing(routing, config))
6370
.config(config.get("server"))
6471
.build()
6572
.start();
66-
67-
System.out.println("WEB server is up! http://localhost:" + server.port() + "/");
68-
return server;
6973
}
7074

7175
/**
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright (c) 2023 Oracle and/or its affiliates.
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+
17+
package io.helidon.examples.dbclient.mongo;
18+
19+
import java.util.List;
20+
import java.util.Map;
21+
22+
import io.helidon.config.Config;
23+
import io.helidon.http.Status;
24+
import io.helidon.http.media.jsonb.JsonbSupport;
25+
import io.helidon.http.media.jsonp.JsonpSupport;
26+
import io.helidon.webclient.api.ClientResponseTyped;
27+
import io.helidon.webclient.api.WebClient;
28+
import io.helidon.webserver.WebServer;
29+
30+
import jakarta.json.Json;
31+
import jakarta.json.JsonArray;
32+
import jakarta.json.JsonBuilderFactory;
33+
import jakarta.json.JsonObject;
34+
import org.junit.jupiter.api.AfterAll;
35+
import org.junit.jupiter.api.BeforeAll;
36+
import org.junit.jupiter.api.Test;
37+
import org.testcontainers.containers.MongoDBContainer;
38+
import org.testcontainers.junit.jupiter.Container;
39+
import org.testcontainers.junit.jupiter.Testcontainers;
40+
41+
import static org.hamcrest.MatcherAssert.assertThat;
42+
import static org.hamcrest.Matchers.is;
43+
44+
@Testcontainers(disabledWithoutDocker = true)
45+
public class MainIT {
46+
47+
@Container
48+
static final MongoDBContainer container = new MongoDBContainer("mongo")
49+
.withExposedPorts(27017);
50+
51+
private static final JsonBuilderFactory JSON_FACTORY = Json.createBuilderFactory(Map.of());
52+
private static final String CONNECTION_URL_KEY = "db.connection.url";
53+
54+
private static WebServer server;
55+
private static WebClient client;
56+
57+
@BeforeAll
58+
static void beforeAll() {
59+
String url = String.format("mongodb://127.0.0.1:%s/pokemon", container.getMappedPort(27017));
60+
System.setProperty(CONNECTION_URL_KEY, url);
61+
server = MongoDbExampleMain.setupServer(WebServer.builder());
62+
client = WebClient.create(config -> config.baseUri("http://localhost:" + server.port())
63+
.addMediaSupport(JsonbSupport.create(Config.create()))
64+
.addMediaSupport(JsonpSupport.create()));
65+
}
66+
67+
@AfterAll
68+
static void afterAll() {
69+
if (server != null && server.isRunning()) {
70+
server.stop();
71+
}
72+
System.clearProperty(CONNECTION_URL_KEY);
73+
}
74+
75+
@Test
76+
void testListAndDeleteAllPokemons() {
77+
List<String> names = listAllPokemons();
78+
assertThat(names.isEmpty(), is(true));
79+
80+
String endpoint = String.format("/db/%s/type/%s", "Raticate", 1);
81+
ClientResponseTyped<String> response = client.post(endpoint).request(String.class);
82+
assertThat(response.status(), is(Status.OK_200));
83+
84+
names = listAllPokemons();
85+
assertThat(names.size(), is(1));
86+
assertThat(names.getFirst(), is("Raticate"));
87+
88+
response = client.delete("/db").request(String.class);
89+
assertThat(response.status(), is(Status.OK_200));
90+
91+
names = listAllPokemons();
92+
assertThat(names.isEmpty(), is(true));
93+
}
94+
95+
@Test
96+
void testAddUpdateDeletePokemon() {
97+
ClientResponseTyped<String> response;
98+
ClientResponseTyped<JsonObject> jsonResponse;
99+
JsonObject pokemon = JSON_FACTORY.createObjectBuilder()
100+
.add("type", 1)
101+
.add("name", "Raticate")
102+
.build();
103+
104+
// Add new pokemon
105+
response = client.put("/db").submit(pokemon, String.class);
106+
assertThat(response.entity(), is("Inserted: 1 values"));
107+
108+
// Get the new pokemon added
109+
jsonResponse = client.get("/db/Raticate").request(JsonObject.class);
110+
assertThat(jsonResponse.status(), is(Status.OK_200));
111+
assertThat(jsonResponse.entity().getString("_id"), is("Raticate"));
112+
assertThat(jsonResponse.entity().getString("type"), is("1"));
113+
114+
// Update pokemon
115+
response = client.put("/db/Raticate/type/2").request(String.class);
116+
assertThat(response.status(), is(Status.OK_200));
117+
118+
// Verify updated pokemon
119+
jsonResponse = client.get("/db/Raticate").request(JsonObject.class);
120+
assertThat(jsonResponse.status(), is(Status.OK_200));
121+
assertThat(jsonResponse.entity().getString("_id"), is("Raticate"));
122+
assertThat(jsonResponse.entity().getString("type"), is("2"));
123+
124+
// Delete Pokemon
125+
response = client.delete("/db/Raticate").request(String.class);
126+
assertThat(response.status(), is(Status.OK_200));
127+
128+
// Verify pokemon is correctly deleted
129+
response = client.get("/db/Raticate").request(String.class);
130+
assertThat(response.status(), is(Status.NOT_FOUND_404));
131+
}
132+
133+
private List<String> listAllPokemons() {
134+
ClientResponseTyped<JsonArray> response = client.get("/db").request(JsonArray.class);
135+
assertThat(response.status(), is(Status.OK_200));
136+
return response.entity().stream().map(e -> e.asJsonObject().getString("_id")).toList();
137+
}
138+
}

examples/dbclient/pokemons/pom.xml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,25 @@
120120
<groupId>io.helidon.http.media</groupId>
121121
<artifactId>helidon-http-media-jsonb</artifactId>
122122
</dependency>
123+
<dependency>
124+
<groupId>org.mongodb</groupId>
125+
<artifactId>mongodb-driver-sync</artifactId>
126+
</dependency>
123127
<dependency>
124128
<groupId>io.helidon.logging</groupId>
125129
<artifactId>helidon-logging-jul</artifactId>
126130
<scope>runtime</scope>
127131
</dependency>
132+
<dependency>
133+
<groupId>org.testcontainers</groupId>
134+
<artifactId>junit-jupiter</artifactId>
135+
<scope>test</scope>
136+
</dependency>
137+
<dependency>
138+
<groupId>org.testcontainers</groupId>
139+
<artifactId>mongodb</artifactId>
140+
<scope>test</scope>
141+
</dependency>
128142
<dependency>
129143
<groupId>io.helidon.webclient</groupId>
130144
<artifactId>helidon-webclient</artifactId>
@@ -158,6 +172,18 @@
158172
</execution>
159173
</executions>
160174
</plugin>
175+
<plugin>
176+
<groupId>org.apache.maven.plugins</groupId>
177+
<artifactId>maven-failsafe-plugin</artifactId>
178+
<executions>
179+
<execution>
180+
<goals>
181+
<goal>integration-test</goal>
182+
<goal>verify</goal>
183+
</goals>
184+
</execution>
185+
</executions>
186+
</plugin>
161187
</plugins>
162188
</build>
163189
</project>

examples/dbclient/pokemons/src/main/java/io/helidon/examples/dbclient/pokemons/Main.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.helidon.dbclient.health.DbClientHealthCheck;
2424
import io.helidon.logging.common.LogConfig;
2525
import io.helidon.webserver.WebServer;
26+
import io.helidon.webserver.WebServerConfig;
2627
import io.helidon.webserver.http.HttpRouting;
2728
import io.helidon.webserver.observe.ObserveFeature;
2829
import io.helidon.webserver.observe.health.HealthObserver;
@@ -73,25 +74,29 @@ private static void startServer() {
7374
Config config = mongo ? Config.create(ConfigSources.classpath(MONGO_CFG)) : Config.create();
7475
Config.global(config);
7576

77+
WebServer server = setupServer(WebServer.builder());
78+
79+
System.out.println("WEB server is up! http://localhost:" + server.port() + "/");
80+
}
81+
82+
static WebServer setupServer(WebServerConfig.Builder builder) {
83+
84+
Config config = Config.global();
7685
// Client services are added through a service loader - see mongoDB example for explicit services
7786
DbClient dbClient = DbClient.create(config.get("db"));
7887
Contexts.globalContext().register(dbClient);
7988

8089
ObserveFeature observe = ObserveFeature.builder()
8190
.config(config.get("server.features.observe"))
8291
.addObserver(HealthObserver.builder()
83-
.addCheck(DbClientHealthCheck.create(dbClient, config.get("db.health-check")))
84-
.build())
92+
.addCheck(DbClientHealthCheck.create(dbClient, config.get("db.health-check")))
93+
.build())
8594
.build();
86-
87-
WebServer server = WebServer.builder()
88-
.config(config.get("server"))
95+
return builder.config(config.get("server"))
8996
.addFeature(observe)
9097
.routing(Main::routing)
9198
.build()
9299
.start();
93-
94-
System.out.println("WEB server is up! http://localhost:" + server.port() + "/");
95100
}
96101

97102
/**

examples/dbclient/pokemons/src/main/java/io/helidon/examples/dbclient/pokemons/PokemonMapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import io.helidon.dbclient.DbRow;
2626

2727
/**
28-
* Maps database statements to {@link io.helidon.examples.dbclient.common.Pokemon} class.
28+
* Maps database statements to {@link io.helidon.examples.dbclient.pokemons.Pokemon} class.
2929
*/
3030
public class PokemonMapper implements DbMapper<Pokemon> {
3131

examples/dbclient/pokemons/src/main/resources/mongo.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@ db:
2626
connection:
2727
url: "mongodb://127.0.0.1:27017/pokemon"
2828
init-schema: false
29+
# Transactions are not supported
30+
init-data: false
2931
services:
3032
tracing:
3133
- enabled: true
32-
metrics:
33-
- type: METER
34+
health-check:
35+
type: "query"
36+
statementName: "health-check"
3437
statements:
3538
# Health check statement. HealthCheck statement type must be a query.
3639
health-check: '{

0 commit comments

Comments
 (0)