From 76c16cef6bbfaba34cb910fb333ceb4c54ae6947 Mon Sep 17 00:00:00 2001 From: moxiaoying <1159230165@qq.com> Date: Wed, 7 May 2025 22:22:22 +0800 Subject: [PATCH] feat: add sync client Signed-off-by: moxiaoying <1159230165@qq.com> --- .../client/api/OpenGeminiSyncClient.java | 114 +++++ .../client/impl/OpenGeminiClientFactory.java | 16 +- .../client/impl/OpenGeminiSyncClientImpl.java | 100 ++++ .../client/impl/FactoryTestCase.java | 43 ++ ... => OpenGeminiAsyncClientFactoryTest.java} | 10 +- ...st.java => OpenGeminiAsyncClientTest.java} | 57 +-- ...va => OpenGeminiAsyncClientWriteTest.java} | 10 +- ...penGeminiAsyncClientWrongAddressTest.java} | 10 +- .../impl/OpenGeminiSyncClientFactoryTest.java | 133 +++++ .../client/impl/OpenGeminiSyncClientTest.java | 453 ++++++++++++++++++ .../impl/OpenGeminiSyncClientWriteTest.java | 207 ++++++++ .../OpenGeminiSyncClientWrongAddressTest.java | 79 +++ .../io/opengemini/client/impl/TestBase.java | 4 + .../config/OpenGeminiAutoConfiguration.java | 10 + 14 files changed, 1204 insertions(+), 42 deletions(-) create mode 100644 opengemini-client-api/src/main/java/io/opengemini/client/api/OpenGeminiSyncClient.java create mode 100644 opengemini-client/src/main/java/io/opengemini/client/impl/OpenGeminiSyncClientImpl.java create mode 100644 opengemini-client/src/test/java/io/opengemini/client/impl/FactoryTestCase.java rename opengemini-client/src/test/java/io/opengemini/client/impl/{OpenGeminiClientFactoryTest.java => OpenGeminiAsyncClientFactoryTest.java} (96%) rename opengemini-client/src/test/java/io/opengemini/client/impl/{OpenGeminiClientTest.java => OpenGeminiAsyncClientTest.java} (89%) rename opengemini-client/src/test/java/io/opengemini/client/impl/{OpenGeminiClientWriteTest.java => OpenGeminiAsyncClientWriteTest.java} (96%) rename opengemini-client/src/test/java/io/opengemini/client/impl/{OpenGeminiWrongAddressTest.java => OpenGeminiAsyncClientWrongAddressTest.java} (91%) create mode 100644 opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiSyncClientFactoryTest.java create mode 100644 opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiSyncClientTest.java create mode 100644 opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiSyncClientWriteTest.java create mode 100644 opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiSyncClientWrongAddressTest.java diff --git a/opengemini-client-api/src/main/java/io/opengemini/client/api/OpenGeminiSyncClient.java b/opengemini-client-api/src/main/java/io/opengemini/client/api/OpenGeminiSyncClient.java new file mode 100644 index 00000000..90dfa120 --- /dev/null +++ b/opengemini-client-api/src/main/java/io/opengemini/client/api/OpenGeminiSyncClient.java @@ -0,0 +1,114 @@ +/* + * Copyright 2024 openGemini 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 + * + * http://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 io.opengemini.client.api; + +import java.util.List; + +/** +// * Interface to access a OpenGemini database provides a set of blocking methods. + */ +public interface OpenGeminiSyncClient extends AutoCloseable { + + /** + * Create a new database. + * + * @param database the name of the new database. + */ + void createDatabase(String database) throws OpenGeminiException; + + /** + * Drop a database. + * + * @param database the name of the database to drop. + */ + void dropDatabase(String database) throws OpenGeminiException; + + /** + * Show all available databases. + */ + List showDatabases() throws OpenGeminiException; + + /** + * Create a retention policy. + * + * @param database the name of the database. + * @param rpConfig the config of the retention policy + * @param isDefault if the retention policy is the default retention policy for the database or not + */ + void createRetentionPolicy(String database, RpConfig rpConfig, boolean isDefault) throws OpenGeminiException; + + /** + * Show all available retention policies. + * + * @param database the name of the database. + */ + List showRetentionPolicies(String database) throws OpenGeminiException; + + /** + * Drop a retention policy. + * + * @param database the name of the database. + * @param retentionPolicy the name of the retention policy to drop. + */ + void dropRetentionPolicy(String database, String retentionPolicy) throws OpenGeminiException; + + /** + * Execute a query against a database. + * + * @param query the query to execute. + */ + QueryResult query(Query query) throws OpenGeminiException; + + /** + * Write a single point to the database. + * + * @param database the name of the database. + * @param point the point to write. + */ + void write(String database, Point point) throws OpenGeminiException; + + /** + * Write points to the database. + * + * @param database the name of the database. + * @param points the points to write. + */ + void write(String database, List points) throws OpenGeminiException; + + /** + * Write a single point to the database. + * + * @param database the name of the database. + * @param retentionPolicy the name of the retention policy. + * @param point the point to write. + */ + void write(String database, String retentionPolicy, Point point) throws OpenGeminiException; + + /** + * Write points to the database. + * + * @param database the name of the database. + * @param retentionPolicy the name of the retention policy. + * @param points the points to write. + */ + void write(String database, String retentionPolicy, List points) throws OpenGeminiException; + + /** + * Ping the OpenGemini server + */ + Pong ping() throws OpenGeminiException; +} diff --git a/opengemini-client/src/main/java/io/opengemini/client/impl/OpenGeminiClientFactory.java b/opengemini-client/src/main/java/io/opengemini/client/impl/OpenGeminiClientFactory.java index 1a12862d..92b5558d 100644 --- a/opengemini-client/src/main/java/io/opengemini/client/impl/OpenGeminiClientFactory.java +++ b/opengemini-client/src/main/java/io/opengemini/client/impl/OpenGeminiClientFactory.java @@ -20,11 +20,24 @@ import io.opengemini.client.api.AuthType; import io.opengemini.client.api.BatchConfig; import io.opengemini.client.api.Configuration; +import io.opengemini.client.api.OpenGeminiAsyncClient; import io.opengemini.client.api.OpenGeminiException; +import io.opengemini.client.api.OpenGeminiSyncClient; import org.jetbrains.annotations.NotNull; public class OpenGeminiClientFactory { - public static OpenGeminiClient create(@NotNull Configuration configuration) throws OpenGeminiException { + public static OpenGeminiAsyncClient create(@NotNull Configuration configuration) throws OpenGeminiException { + validateConf(configuration); + return new OpenGeminiClient(configuration); + } + + public static OpenGeminiSyncClient createSyncClient(@NotNull Configuration configuration) + throws OpenGeminiException { + validateConf(configuration); + return new OpenGeminiSyncClientImpl(configuration); + } + + private static void validateConf(@NotNull Configuration configuration) throws OpenGeminiException { if (configuration.getAddresses() == null || configuration.getAddresses().isEmpty()) { throw new OpenGeminiException("must have at least one address"); } @@ -52,6 +65,5 @@ public static OpenGeminiClient create(@NotNull Configuration configuration) thro throw new OpenGeminiException("batch enabled, batch size must be great than 0"); } } - return new OpenGeminiClient(configuration); } } diff --git a/opengemini-client/src/main/java/io/opengemini/client/impl/OpenGeminiSyncClientImpl.java b/opengemini-client/src/main/java/io/opengemini/client/impl/OpenGeminiSyncClientImpl.java new file mode 100644 index 00000000..dba2cedf --- /dev/null +++ b/opengemini-client/src/main/java/io/opengemini/client/impl/OpenGeminiSyncClientImpl.java @@ -0,0 +1,100 @@ +package io.opengemini.client.impl; + +import io.opengemini.client.api.Configuration; +import io.opengemini.client.api.OpenGeminiException; +import io.opengemini.client.api.OpenGeminiSyncClient; +import io.opengemini.client.api.Point; +import io.opengemini.client.api.Pong; +import io.opengemini.client.api.Query; +import io.opengemini.client.api.QueryResult; +import io.opengemini.client.api.RetentionPolicy; +import io.opengemini.client.api.RpConfig; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +public class OpenGeminiSyncClientImpl implements OpenGeminiSyncClient { + protected Configuration conf; + private OpenGeminiClient openGeminiAsyncClient; + + OpenGeminiSyncClientImpl(Configuration conf) { + this.conf = conf; + this.openGeminiAsyncClient = new OpenGeminiClient(conf); + } + + @Override + public void createDatabase(String database) throws OpenGeminiException { + wrapFuture(openGeminiAsyncClient.createDatabase(database)); + } + + @Override + public void dropDatabase(String database) throws OpenGeminiException { + wrapFuture(openGeminiAsyncClient.dropDatabase(database)); + } + + @Override + public List showDatabases() throws OpenGeminiException { + return wrapFuture(openGeminiAsyncClient.showDatabases()); + } + + @Override + public void createRetentionPolicy(String database, RpConfig rpConfig, boolean isDefault) + throws OpenGeminiException { + wrapFuture(openGeminiAsyncClient.createRetentionPolicy(database, rpConfig, isDefault)); + } + + @Override + public List showRetentionPolicies(String database) throws OpenGeminiException { + return wrapFuture(openGeminiAsyncClient.showRetentionPolicies(database)); + } + + @Override + public void dropRetentionPolicy(String database, String retentionPolicy) throws OpenGeminiException { + wrapFuture(openGeminiAsyncClient.dropRetentionPolicy(database, retentionPolicy)); + } + + @Override + public QueryResult query(Query query) throws OpenGeminiException { + return wrapFuture(openGeminiAsyncClient.query(query)); + } + + @Override + public void write(String database, Point point) throws OpenGeminiException { + wrapFuture(openGeminiAsyncClient.write(database, point)); + } + + @Override + public void write(String database, List points) throws OpenGeminiException { + wrapFuture(openGeminiAsyncClient.write(database, points)); + } + + @Override + public void write(String database, String retentionPolicy, Point point) throws OpenGeminiException { + wrapFuture(openGeminiAsyncClient.write(database, retentionPolicy, point)); + } + + @Override + public void write(String database, String retentionPolicy, List points) throws OpenGeminiException { + wrapFuture(openGeminiAsyncClient.write(database, retentionPolicy, points)); + } + + @Override + public Pong ping() throws OpenGeminiException { + return wrapFuture(openGeminiAsyncClient.ping()); + } + + @Override + public void close() throws IOException { + openGeminiAsyncClient.close(); + } + + private T wrapFuture(CompletableFuture future) throws OpenGeminiException { + try { + return future.get(conf.getHttpConfig().timeout().toMillis(), TimeUnit.MILLISECONDS); + } catch (Exception e) { + throw new OpenGeminiException(e); + } + } +} diff --git a/opengemini-client/src/test/java/io/opengemini/client/impl/FactoryTestCase.java b/opengemini-client/src/test/java/io/opengemini/client/impl/FactoryTestCase.java new file mode 100644 index 00000000..ef59726d --- /dev/null +++ b/opengemini-client/src/test/java/io/opengemini/client/impl/FactoryTestCase.java @@ -0,0 +1,43 @@ +/* + * Copyright 2025 openGemini 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 + * + * http://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 io.opengemini.client.impl; + +import org.junit.jupiter.api.Test; + +public interface FactoryTestCase { + + @Test + void testGetClientWithNullAddresses(); + + @Test + void testGetClientWithEmptyAddresses(); + + @Test + void testGetClientWithEmptyToken(); + + @Test + void testGetClientWithEmptyUserName(); + + @Test + void testGetClientWithNullPassword(); + + @Test + void testGetClientWithInvalidBatchInterval(); + + @Test + void testGetClientWithInvalidBatchSize(); +} diff --git a/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiClientFactoryTest.java b/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiAsyncClientFactoryTest.java similarity index 96% rename from opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiClientFactoryTest.java rename to opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiAsyncClientFactoryTest.java index b8429696..95f2c883 100644 --- a/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiClientFactoryTest.java +++ b/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiAsyncClientFactoryTest.java @@ -29,8 +29,7 @@ import java.util.ArrayList; import java.util.List; -public class OpenGeminiClientFactoryTest { - +public class OpenGeminiAsyncClientFactoryTest implements FactoryTestCase { private static Configuration configuration; private static AuthConfig authConfig; @@ -44,6 +43,7 @@ public static void setUp() { batchConfig = new BatchConfig(); } + @Override @Test public void testGetClientWithNullAddresses() { configuration.setAddresses(null); @@ -53,6 +53,7 @@ public void testGetClientWithNullAddresses() { Assertions.assertEquals("must have at least one address", actualException.getMessage()); } + @Override @Test public void testGetClientWithEmptyAddresses() { configuration.setAddresses(new ArrayList<>()); @@ -63,6 +64,7 @@ public void testGetClientWithEmptyAddresses() { Assertions.assertEquals("must have at least one address", actualException.getMessage()); } + @Override @Test public void testGetClientWithEmptyToken() { configuration.setAddresses(List.of(new Address())); @@ -76,6 +78,7 @@ public void testGetClientWithEmptyToken() { Assertions.assertEquals("invalid auth config due to empty token", actualException.getMessage()); } + @Override @Test public void testGetClientWithEmptyUserName() { configuration.setAddresses(List.of(new Address())); @@ -90,6 +93,7 @@ public void testGetClientWithEmptyUserName() { Assertions.assertEquals("invalid auth config due to empty username", actualException.getMessage()); } + @Override @Test public void testGetClientWithNullPassword() { configuration.setAddresses(List.of(new Address())); @@ -104,6 +108,7 @@ public void testGetClientWithNullPassword() { Assertions.assertEquals("invalid auth config due to empty password", actualException.getMessage()); } + @Override @Test public void testGetClientWithInvalidBatchInterval() { configuration.setAddresses(List.of(new Address())); @@ -117,6 +122,7 @@ public void testGetClientWithInvalidBatchInterval() { Assertions.assertEquals("batch enabled, batch interval must be great than 0", actualException.getMessage()); } + @Override @Test public void testGetClientWithInvalidBatchSize() { configuration.setAddresses(List.of(new Address())); diff --git a/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiClientTest.java b/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiAsyncClientTest.java similarity index 89% rename from opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiClientTest.java rename to opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiAsyncClientTest.java index f031de32..902829c7 100644 --- a/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiClientTest.java +++ b/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiAsyncClientTest.java @@ -22,6 +22,7 @@ import io.opengemini.client.api.AuthConfig; import io.opengemini.client.api.AuthType; import io.opengemini.client.api.Configuration; +import io.opengemini.client.api.OpenGeminiAsyncClient; import io.opengemini.client.api.OpenGeminiException; import io.opengemini.client.api.Point; import io.opengemini.client.api.Pong; @@ -52,7 +53,7 @@ import java.util.concurrent.ExecutionException; @TestInstance(TestInstance.Lifecycle.PER_CLASS) -class OpenGeminiClientTest extends TestBase { +class OpenGeminiAsyncClientTest extends TestBase { private final List clients = new ArrayList<>(); protected List clientList() throws OpenGeminiException { @@ -74,12 +75,12 @@ protected List clientList() throws OpenGeminiException { .httpConfig(httpConfig) .gzipEnabled(false) .build(); - clients.add(OpenGeminiClientFactory.create(configuration)); + clients.add((OpenGeminiClient) OpenGeminiClientFactory.create(configuration)); } return clients; } - private OpenGeminiClient openGeminiClient; + private OpenGeminiClient openGeminiAsyncClient; @BeforeEach void setUp() { @@ -93,27 +94,27 @@ void setUp() { .authConfig(new AuthConfig(AuthType.PASSWORD, "test", "testPwd123@".toCharArray(), null)) .gzipEnabled(false) .build(); - this.openGeminiClient = new OpenGeminiClient(configuration); + this.openGeminiAsyncClient = new OpenGeminiClient(configuration); } @Test void testDatabase() throws Exception { String databaseTestName = "testDatabase_0001"; - CompletableFuture createdb = openGeminiClient.createDatabase(databaseTestName); + CompletableFuture createdb = openGeminiAsyncClient.createDatabase(databaseTestName); createdb.get(); - CompletableFuture> rstFuture = openGeminiClient.showDatabases(); + CompletableFuture> rstFuture = openGeminiAsyncClient.showDatabases(); List rst = rstFuture.get(); Assertions.assertTrue(rst.contains(databaseTestName)); - CompletableFuture dropdb = openGeminiClient.dropDatabase(databaseTestName); + CompletableFuture dropdb = openGeminiAsyncClient.dropDatabase(databaseTestName); dropdb.get(); } @Test void testShowField() throws Exception { String databaseTestName = "database_test_0001"; - CompletableFuture createdb = openGeminiClient.createDatabase(databaseTestName); + CompletableFuture createdb = openGeminiAsyncClient.createDatabase(databaseTestName); createdb.get(); String measureTestName = "measure_test"; @@ -121,13 +122,13 @@ void testShowField() throws Exception { Query createMeasurementQuery = new Query(("CREATE MEASUREMENT %s (tag1 TAG,tag2 TAG,tag3 TAG, " + "field1 INT64 FIELD, field2 BOOL, field3 STRING, field4 FLOAT64)") .formatted(measureTestName), databaseTestName, rpTestName); - CompletableFuture rstFuture = openGeminiClient.query(createMeasurementQuery); + CompletableFuture rstFuture = openGeminiAsyncClient.query(createMeasurementQuery); QueryResult rst = rstFuture.get(); Assertions.assertEquals(1, rst.getResults().size()); Query showFieldQuery = new Query("SHOW TAG KEYS FROM %s limit 3 OFFSET 0".formatted(measureTestName), databaseTestName, rpTestName); - CompletableFuture showRstFuture = openGeminiClient.query(showFieldQuery); + CompletableFuture showRstFuture = openGeminiAsyncClient.query(showFieldQuery); QueryResult showRst = showRstFuture.get(); Assertions.assertEquals(1, showRst.getResults().size()); Series series = showRst.getResults().get(0).getSeries().get(0); @@ -136,7 +137,7 @@ void testShowField() throws Exception { Assertions.assertEquals(series.getValues(), List.of( List.of("tag1"), List.of("tag2"), List.of("tag3"))); - CompletableFuture dropdb = openGeminiClient.dropDatabase(databaseTestName); + CompletableFuture dropdb = openGeminiAsyncClient.dropDatabase(databaseTestName); dropdb.get(); } @@ -165,7 +166,7 @@ void testRetentionPolicyNormal() { rps.add(new RpConfig(testRpNameBase + 3, "365d", "", "")); String database = "testRpDatabase0001"; - CompletableFuture createdb = openGeminiClient.createDatabase(database); + CompletableFuture createdb = openGeminiAsyncClient.createDatabase(database); createdb.get(); for (int i = 0; i < rps.size(); i++) { @@ -173,20 +174,20 @@ void testRetentionPolicyNormal() { if (i == 4) { isDefaultRp = Boolean.TRUE; } - CompletableFuture createRsp = openGeminiClient.createRetentionPolicy( + CompletableFuture createRsp = openGeminiAsyncClient.createRetentionPolicy( database, rps.get(i), isDefaultRp); createRsp.get(); Thread.sleep(2000); - CompletableFuture> showRpRsp = openGeminiClient.showRetentionPolicies(database); + CompletableFuture> showRpRsp = openGeminiAsyncClient.showRetentionPolicies(database); List rsp = showRpRsp.get(); - CompletableFuture dropRsp = openGeminiClient.dropRetentionPolicy(database, rps.get(i).getName()); + CompletableFuture dropRsp = openGeminiAsyncClient.dropRetentionPolicy(database, rps.get(i).getName()); dropRsp.get(); String testRpName = testRpNameBase + i; Assertions.assertTrue(rsp.stream().anyMatch(x -> x.getName().equals(testRpName))); } - CompletableFuture dropdb = openGeminiClient.dropDatabase(database); + CompletableFuture dropdb = openGeminiAsyncClient.dropDatabase(database); dropdb.get(); } @@ -197,10 +198,10 @@ void testRetentionPolicyError() { RpConfig rp = new RpConfig(testRpName + 0, "d3d", "", ""); String database = "testRpDatabase0002"; - CompletableFuture createdb = openGeminiClient.createDatabase(database); + CompletableFuture createdb = openGeminiAsyncClient.createDatabase(database); createdb.get(); - CompletableFuture createRsp = openGeminiClient.createRetentionPolicy( + CompletableFuture createRsp = openGeminiAsyncClient.createRetentionPolicy( database, rp, Boolean.FALSE); ExecutionException e = Assertions.assertThrows(ExecutionException.class, createRsp::get); @@ -210,33 +211,33 @@ void testRetentionPolicyError() { Thread.sleep(2000); - CompletableFuture> showRpRsp = openGeminiClient.showRetentionPolicies(database); + CompletableFuture> showRpRsp = openGeminiAsyncClient.showRetentionPolicies(database); List rsp = showRpRsp.get(); - CompletableFuture dropRsp = openGeminiClient.dropRetentionPolicy(database, rp.getName()); + CompletableFuture dropRsp = openGeminiAsyncClient.dropRetentionPolicy(database, rp.getName()); dropRsp.get(); for (RetentionPolicy retentionPolicy : rsp) { Assertions.assertFalse(retentionPolicy.getName().contains(testRpName + 0)); } - openGeminiClient.dropDatabase(database).get(); + openGeminiAsyncClient.dropDatabase(database).get(); } @SneakyThrows @Test void testQueryPrecision() { String databaseName = "query_precision_0001"; - CompletableFuture createdb = openGeminiClient.createDatabase(databaseName); + CompletableFuture createdb = openGeminiAsyncClient.createDatabase(databaseName); createdb.get(); String measurementName = "query_precision_ms_0001"; Point testPoint = generalTestPoint(measurementName, 1, 1); - CompletableFuture writeRsp = openGeminiClient.write(databaseName, testPoint); + CompletableFuture writeRsp = openGeminiAsyncClient.write(databaseName, testPoint); writeRsp.get(); Thread.sleep(3000); Query selectQuery = new Query("select * from " + measurementName, databaseName, ""); - CompletableFuture rst = openGeminiClient.query(selectQuery); + CompletableFuture rst = openGeminiAsyncClient.query(selectQuery); QueryResult queryResult = rst.get(); Series x = queryResult.getResults().get(0).getSeries().get(0); @@ -246,7 +247,7 @@ void testQueryPrecision() { Assertions.assertTrue(timeValueStr.startsWith("20") && timeValueStr.endsWith("Z")); selectQuery = new Query("select * from " + measurementName, databaseName, "", Precision.PRECISIONNANOSECOND); - rst = openGeminiClient.query(selectQuery); + rst = openGeminiAsyncClient.query(selectQuery); queryResult = rst.get(); x = queryResult.getResults().get(0).getSeries().get(0); @@ -255,7 +256,7 @@ void testQueryPrecision() { long timeValueDouble = (Long) timeValue; Assertions.assertTrue(timeValueDouble > 1724778721457052741L); - CompletableFuture dropdb = openGeminiClient.dropDatabase(databaseName); + CompletableFuture dropdb = openGeminiAsyncClient.dropDatabase(databaseName); dropdb.get(); } @@ -461,10 +462,10 @@ void testQueryPrecision(OpenGeminiClient client) throws Exception { @AfterAll void closeClients() { - for (OpenGeminiClient client : clients) { + for (OpenGeminiAsyncClient client : clients) { try { client.close(); - } catch (IOException e) { + } catch (Exception e) { // ignore exception } } diff --git a/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiClientWriteTest.java b/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiAsyncClientWriteTest.java similarity index 96% rename from opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiClientWriteTest.java rename to opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiAsyncClientWriteTest.java index 9aa5d9fe..7f70c863 100644 --- a/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiClientWriteTest.java +++ b/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiAsyncClientWriteTest.java @@ -20,6 +20,7 @@ import io.github.openfacade.http.HttpClientEngine; import io.opengemini.client.api.Address; import io.opengemini.client.api.Configuration; +import io.opengemini.client.api.OpenGeminiAsyncClient; import io.opengemini.client.api.OpenGeminiException; import io.opengemini.client.api.Point; import io.opengemini.client.api.Query; @@ -32,7 +33,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import java.io.IOException; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; @@ -42,7 +42,7 @@ import java.util.concurrent.CompletableFuture; @TestInstance(TestInstance.Lifecycle.PER_CLASS) -class OpenGeminiClientWriteTest extends TestBase { +class OpenGeminiAsyncClientWriteTest extends TestBase{ private final List clients = new ArrayList<>(); protected List clientList() throws OpenGeminiException { @@ -64,7 +64,7 @@ protected List clientList() throws OpenGeminiException { .httpConfig(httpConfig) .gzipEnabled(false) .build(); - clients.add(OpenGeminiClientFactory.create(configuration)); + clients.add((OpenGeminiClient) OpenGeminiClientFactory.create(configuration)); } return clients; } @@ -213,10 +213,10 @@ private static Point testPoint(String measurementName, int valueIndex, int field @AfterAll void closeClients() { - for (OpenGeminiClient client : clients) { + for (OpenGeminiAsyncClient client : clients) { try { client.close(); - } catch (IOException e) { + } catch (Exception e) { // ignore exception } } diff --git a/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiWrongAddressTest.java b/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiAsyncClientWrongAddressTest.java similarity index 91% rename from opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiWrongAddressTest.java rename to opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiAsyncClientWrongAddressTest.java index f4c10eef..a5221744 100644 --- a/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiWrongAddressTest.java +++ b/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiAsyncClientWrongAddressTest.java @@ -20,6 +20,7 @@ import io.github.openfacade.http.HttpClientEngine; import io.opengemini.client.api.Address; import io.opengemini.client.api.Configuration; +import io.opengemini.client.api.OpenGeminiAsyncClient; import io.opengemini.client.api.OpenGeminiException; import io.opengemini.client.api.Query; import io.opengemini.client.api.QueryResult; @@ -29,7 +30,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import java.io.IOException; import java.time.Duration; import java.util.ArrayList; import java.util.Collections; @@ -38,7 +38,7 @@ import java.util.concurrent.ExecutionException; @TestInstance(TestInstance.Lifecycle.PER_CLASS) -class OpenGeminiWrongAddressTest { +class OpenGeminiAsyncClientWrongAddressTest { private final List clients = new ArrayList<>(); protected List clientList() throws OpenGeminiException { @@ -57,7 +57,7 @@ protected List clientList() throws OpenGeminiException { Configuration configuration = Configuration.builder() .addresses(Collections.singletonList(new Address("127.0.0.1", 28086))) .httpConfig(httpConfig).gzipEnabled(false).build(); - clients.add(OpenGeminiClientFactory.create(configuration)); + clients.add((OpenGeminiClient)OpenGeminiClientFactory.create(configuration)); } return clients; } @@ -72,10 +72,10 @@ void queryWithWrongAddress(OpenGeminiClient client) { @AfterAll void closeClients() { - for (OpenGeminiClient client : clients) { + for (OpenGeminiAsyncClient client : clients) { try { client.close(); - } catch (IOException e) { + } catch (Exception e) { // ignore exception } } diff --git a/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiSyncClientFactoryTest.java b/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiSyncClientFactoryTest.java new file mode 100644 index 00000000..5ea6b4f1 --- /dev/null +++ b/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiSyncClientFactoryTest.java @@ -0,0 +1,133 @@ +/* + * Copyright 2025 openGemini 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 + * + * http://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 io.opengemini.client.impl; + +import io.opengemini.client.api.Address; +import io.opengemini.client.api.AuthConfig; +import io.opengemini.client.api.AuthType; +import io.opengemini.client.api.BatchConfig; +import io.opengemini.client.api.Configuration; +import io.opengemini.client.api.OpenGeminiException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +public class OpenGeminiSyncClientFactoryTest implements FactoryTestCase { + + private static Configuration configuration; + + private static AuthConfig authConfig; + + private static BatchConfig batchConfig; + + @BeforeAll + public static void setUp() { + configuration = new Configuration(); + authConfig = new AuthConfig(); + batchConfig = new BatchConfig(); + } + + @Test + public void testGetClientWithNullAddresses() { + configuration.setAddresses(null); + Throwable actualException = Assertions.assertThrows(OpenGeminiException.class, () -> { + OpenGeminiClientFactory.createSyncClient(configuration); + }); + Assertions.assertEquals("must have at least one address", actualException.getMessage()); + } + + @Test + public void testGetClientWithEmptyAddresses() { + configuration.setAddresses(new ArrayList<>()); + + Throwable actualException = Assertions.assertThrows(OpenGeminiException.class, () -> { + OpenGeminiClientFactory.createSyncClient(configuration); + }); + Assertions.assertEquals("must have at least one address", actualException.getMessage()); + } + + @Test + public void testGetClientWithEmptyToken() { + configuration.setAddresses(List.of(new Address())); + authConfig.setAuthType(AuthType.TOKEN); + authConfig.setToken(""); + configuration.setAuthConfig(authConfig); + + Throwable actualException = Assertions.assertThrows(OpenGeminiException.class, () -> { + OpenGeminiClientFactory.createSyncClient(configuration); + }); + Assertions.assertEquals("invalid auth config due to empty token", actualException.getMessage()); + } + + @Test + public void testGetClientWithEmptyUserName() { + configuration.setAddresses(List.of(new Address())); + authConfig.setAuthType(AuthType.PASSWORD); + authConfig.setPassword("pass".toCharArray()); + authConfig.setUsername(""); + configuration.setAuthConfig(authConfig); + + Throwable actualException = Assertions.assertThrows(OpenGeminiException.class, () -> { + OpenGeminiClientFactory.createSyncClient(configuration); + }); + Assertions.assertEquals("invalid auth config due to empty username", actualException.getMessage()); + } + + @Test + public void testGetClientWithNullPassword() { + configuration.setAddresses(List.of(new Address())); + authConfig.setAuthType(AuthType.PASSWORD); + authConfig.setPassword(null); + authConfig.setUsername("user"); + configuration.setAuthConfig(authConfig); + + Throwable actualException = Assertions.assertThrows(OpenGeminiException.class, () -> { + OpenGeminiClientFactory.createSyncClient(configuration); + }); + Assertions.assertEquals("invalid auth config due to empty password", actualException.getMessage()); + } + + @Test + public void testGetClientWithInvalidBatchInterval() { + configuration.setAddresses(List.of(new Address())); + authConfig.setAuthType(null); + batchConfig.setBatchInterval(-1); + configuration.setBatchConfig(batchConfig); + + Throwable actualException = Assertions.assertThrows(OpenGeminiException.class, () -> { + OpenGeminiClientFactory.createSyncClient(configuration); + }); + Assertions.assertEquals("batch enabled, batch interval must be great than 0", actualException.getMessage()); + } + + @Test + public void testGetClientWithInvalidBatchSize() { + configuration.setAddresses(List.of(new Address())); + authConfig.setAuthType(null); + batchConfig.setBatchInterval(1); + batchConfig.setBatchSize(-1); + configuration.setBatchConfig(batchConfig); + + Throwable actualException = Assertions.assertThrows(OpenGeminiException.class, () -> { + OpenGeminiClientFactory.createSyncClient(configuration); + }); + Assertions.assertEquals("batch enabled, batch size must be great than 0", actualException.getMessage()); + } +} diff --git a/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiSyncClientTest.java b/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiSyncClientTest.java new file mode 100644 index 00000000..bc4301ec --- /dev/null +++ b/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiSyncClientTest.java @@ -0,0 +1,453 @@ +/* + * Copyright 2025 openGemini 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 + * + * http://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 io.opengemini.client.impl; + +import io.github.openfacade.http.HttpClientConfig; +import io.github.openfacade.http.HttpClientEngine; +import io.opengemini.client.api.Address; +import io.opengemini.client.api.AuthConfig; +import io.opengemini.client.api.AuthType; +import io.opengemini.client.api.Configuration; +import io.opengemini.client.api.OpenGeminiException; +import io.opengemini.client.api.OpenGeminiSyncClient; +import io.opengemini.client.api.Point; +import io.opengemini.client.api.Pong; +import io.opengemini.client.api.Precision; +import io.opengemini.client.api.Query; +import io.opengemini.client.api.QueryResult; +import io.opengemini.client.api.RetentionPolicy; +import io.opengemini.client.api.RpConfig; +import io.opengemini.client.api.Series; +import lombok.SneakyThrows; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class OpenGeminiSyncClientTest extends TestBase { + private final List clients = new ArrayList<>(); + + protected List clientList() throws OpenGeminiException { + List engines = new ArrayList<>(); + engines.add(HttpClientEngine.Async); + engines.add(HttpClientEngine.Java); + engines.add(HttpClientEngine.Java8); + engines.add(HttpClientEngine.OkHttp); + List clients = new ArrayList<>(); + for (HttpClientEngine engine : engines) { + HttpClientConfig httpConfig = new HttpClientConfig.Builder() + .engine(engine) + .connectTimeout(Duration.ofSeconds(3)) + .timeout(Duration.ofSeconds(3)) + .build(); + Configuration configuration = + Configuration.builder() + .addresses(Collections.singletonList(new Address("127.0.0.1", 8086))) + .httpConfig(httpConfig) + .gzipEnabled(false) + .build(); + clients.add((OpenGeminiSyncClientImpl) OpenGeminiClientFactory.createSyncClient(configuration)); + } + return clients; + } + + private OpenGeminiSyncClient openGeminiSyncClient; + + @SneakyThrows + @BeforeEach + void setUp() { + HttpClientConfig httpConfig = new HttpClientConfig.Builder() + .connectTimeout(Duration.ofSeconds(3)) + .timeout(Duration.ofSeconds(3)) + .build(); + Configuration configuration = Configuration.builder() + .addresses(Collections.singletonList(new Address("127.0.0.1", 8086))) + .httpConfig(httpConfig) + .authConfig(new AuthConfig(AuthType.PASSWORD, "test", "testPwd123@".toCharArray(), null)) + .gzipEnabled(false) + .build(); + this.openGeminiSyncClient = OpenGeminiClientFactory.createSyncClient(configuration); + } + + @Test + void testDatabase() throws Exception { + String databaseTestName = "testDatabase_0001"; + openGeminiSyncClient.createDatabase(databaseTestName); + + List rst = openGeminiSyncClient.showDatabases(); + Assertions.assertTrue(rst.contains(databaseTestName)); + + openGeminiSyncClient.dropDatabase(databaseTestName); + } + + @Test + void testShowField() throws Exception { + String databaseTestName = "database_test_0001"; + openGeminiSyncClient.createDatabase(databaseTestName); + + String measureTestName = "measure_test"; + String rpTestName = ""; + Query createMeasurementQuery = new Query(("CREATE MEASUREMENT %s (tag1 TAG,tag2 TAG,tag3 TAG, " + + "field1 INT64 FIELD, field2 BOOL, field3 STRING, field4 FLOAT64)") + .formatted(measureTestName), databaseTestName, rpTestName); + QueryResult rst = openGeminiSyncClient.query(createMeasurementQuery); + Assertions.assertEquals(1, rst.getResults().size()); + + Query showFieldQuery = new Query("SHOW TAG KEYS FROM %s limit 3 OFFSET 0".formatted(measureTestName), + databaseTestName, rpTestName); + QueryResult showRst = openGeminiSyncClient.query(showFieldQuery); + Assertions.assertEquals(1, showRst.getResults().size()); + Series series = showRst.getResults().get(0).getSeries().get(0); + Assertions.assertEquals(series.getName(), measureTestName); + Assertions.assertEquals(series.getColumns(), Collections.singletonList("tagKey")); + Assertions.assertEquals(series.getValues(), List.of( + List.of("tag1"), List.of("tag2"), List.of("tag3"))); + + openGeminiSyncClient.dropDatabase(databaseTestName); + } + + private Point generalTestPoint(String measurementName, int valueIndex, int fieldCount) { + Point testPoint = new Point(); + testPoint.setMeasurement(measurementName); + HashMap tags = new HashMap<>(); + HashMap fields = new HashMap<>(); + for (int i = 0; i < fieldCount; i++) { + tags.put("tag" + i, "value" + valueIndex); + fields.put("field" + i, "value" + valueIndex); + } + testPoint.setTags(tags); + testPoint.setFields(fields); + return testPoint; + } + + @SneakyThrows + @Test + void testRetentionPolicyNormal() { + String testRpNameBase = "testRpName"; + ArrayList rps = new ArrayList<>(); + rps.add(new RpConfig(testRpNameBase + 0, "3d", "", "")); + rps.add(new RpConfig(testRpNameBase + 1, "3d", "1h", "")); + rps.add(new RpConfig(testRpNameBase + 2, "3d", "1h", "7h")); + rps.add(new RpConfig(testRpNameBase + 3, "365d", "", "")); + + String database = "testRpDatabase0001"; + openGeminiSyncClient.createDatabase(database); + + for (int i = 0; i < rps.size(); i++) { + boolean isDefaultRp = Boolean.FALSE; + if (i == 4) { + isDefaultRp = Boolean.TRUE; + } + openGeminiSyncClient.createRetentionPolicy( + database, rps.get(i), isDefaultRp); + Thread.sleep(2000); + + List rsp = openGeminiSyncClient.showRetentionPolicies(database); + openGeminiSyncClient.dropRetentionPolicy(database, rps.get(i).getName()); + String testRpName = testRpNameBase + i; + Assertions.assertTrue(rsp.stream().anyMatch(x -> x.getName().equals(testRpName))); + } + + openGeminiSyncClient.dropDatabase(database); + } + + @SneakyThrows + @Test + void testRetentionPolicyError() { + String testRpName = "testRpName"; + RpConfig rp = new RpConfig(testRpName + 0, "d3d", "", ""); + + String database = "testRpDatabase0002"; + openGeminiSyncClient.createDatabase(database); + + Executable createRsp = () -> openGeminiSyncClient.createRetentionPolicy(database, rp, Boolean.FALSE); + ExecutionException e = Assertions.assertThrows(ExecutionException.class, createRsp); + Assertions.assertInstanceOf(OpenGeminiException.class, e.getCause()); + Assertions.assertTrue(e.getCause().getMessage().contains( + "syntax error: unexpected IDENT, expecting DURATIONVAL")); + + Thread.sleep(2000); + + List showRpRsp = openGeminiSyncClient.showRetentionPolicies(database); + openGeminiSyncClient.dropRetentionPolicy(database, rp.getName()); + for (RetentionPolicy retentionPolicy : showRpRsp) { + Assertions.assertFalse(retentionPolicy.getName().contains(testRpName + 0)); + } + + openGeminiSyncClient.dropDatabase(database); + } + + @SneakyThrows + @Test + void testQueryPrecision() { + String databaseName = "query_precision_0001"; + openGeminiSyncClient.createDatabase(databaseName); + + String measurementName = "query_precision_ms_0001"; + Point testPoint = generalTestPoint(measurementName, 1, 1); + + openGeminiSyncClient.write(databaseName, testPoint); + Thread.sleep(3000); + + Query selectQuery = new Query("select * from " + measurementName, databaseName, ""); + QueryResult queryResult = openGeminiSyncClient.query(selectQuery); + + Series x = queryResult.getResults().get(0).getSeries().get(0); + Object timeValue = x.getValues().get(0).get(0); + Assertions.assertInstanceOf(String.class, timeValue); + String timeValueStr = (String) timeValue; + Assertions.assertTrue(timeValueStr.startsWith("20") && timeValueStr.endsWith("Z")); + + selectQuery = new Query("select * from " + measurementName, databaseName, "", Precision.PRECISIONNANOSECOND); + queryResult = openGeminiSyncClient.query(selectQuery); + + x = queryResult.getResults().get(0).getSeries().get(0); + timeValue = x.getValues().get(0).get(0); + Assertions.assertInstanceOf(Long.class, timeValue); + long timeValueDouble = (Long) timeValue; + Assertions.assertTrue(timeValueDouble > 1724778721457052741L); + + openGeminiSyncClient.dropDatabase(databaseName); + } + + @Test + void query_should_throws_exception_when_address_is_wrong() throws IOException { + Configuration configuration = Configuration.builder() + .addresses(Collections.singletonList(new Address("127.0.0.1", 28086))) + .httpConfig(new HttpClientConfig.Builder().build()) + .gzipEnabled(false) + .build(); + + try (OpenGeminiClient wrongClient = new OpenGeminiClient(configuration)) { + Query showDatabasesQuery = new Query("SHOW DATABASES"); + CompletableFuture rstFuture = wrongClient.query(showDatabasesQuery); + Assertions.assertThrows(ExecutionException.class, rstFuture::get); + } + } + + @ParameterizedTest + @MethodSource("clientList") + void database_lifecycle_for_create_show_drop(OpenGeminiClient client) throws Exception { + String databaseTestName = "db_test_lifecycle" + httpEngine(client); + CompletableFuture createDbFuture = client.createDatabase(databaseTestName); + createDbFuture.get(); + + CompletableFuture> showDbFuture = client.showDatabases(); + Assertions.assertTrue(showDbFuture.get().contains(databaseTestName)); + + CompletableFuture dropDbFuture = client.dropDatabase(databaseTestName); + dropDbFuture.get(); + } + + @ParameterizedTest + @MethodSource("clientList") + void query_for_show_tag_keys(OpenGeminiClient client) throws Exception { + String databaseTestName = "db_test_ms_tag_keys" + httpEngine(client); + CompletableFuture createDbFuture = client.createDatabase(databaseTestName); + createDbFuture.get(); + + String measureTestName = "ms_test_tag_keys" + httpEngine(client); + + String command = String.format(Locale.ROOT, ("CREATE MEASUREMENT %s (tag1 TAG,tag2 TAG,tag3 TAG, " + + "field1 INT64 FIELD, field2 BOOL, field3 STRING, field4 FLOAT64)"), measureTestName); + Query createMeasurementQuery = new Query(command, databaseTestName, null); + CompletableFuture createMsFuture = client.query(createMeasurementQuery); + QueryResult rst = createMsFuture.get(); + Assertions.assertEquals(1, rst.getResults().size()); + + Query showFieldQuery = new Query( + String.format(Locale.ROOT, "SHOW TAG KEYS FROM %s limit 3 OFFSET 0", measureTestName), databaseTestName, + null); + CompletableFuture showTagKeysFuture = client.query(showFieldQuery); + QueryResult showRst = showTagKeysFuture.get(); + + CompletableFuture dropDbFuture = client.dropDatabase(databaseTestName); + dropDbFuture.get(); + + Assertions.assertEquals(1, showRst.getResults().size()); + Series series = showRst.getResults().get(0).getSeries().get(0); + Assertions.assertEquals(series.getName(), measureTestName); + Assertions.assertEquals(series.getColumns(), Collections.singletonList("tagKey")); + List> values = new ArrayList<>(); + values.add(Collections.singletonList("tag1")); + values.add(Collections.singletonList("tag2")); + values.add(Collections.singletonList("tag3")); + Assertions.assertEquals(series.getValues(), values); + } + + @ParameterizedTest + @MethodSource("clientList") + void retention_policy_lifecycle_for_create_show_drop(OpenGeminiClient client) throws Exception { + String testRpNameBase = "rp_test_lifecycle" + httpEngine(client); + ArrayList rps = new ArrayList<>(); + rps.add(new RpConfig(testRpNameBase + 0, "3d", "", "")); + rps.add(new RpConfig(testRpNameBase + 1, "3d", "1h", "")); + rps.add(new RpConfig(testRpNameBase + 2, "3d", "1h", "7h")); + rps.add(new RpConfig(testRpNameBase + 3, "365d", "", "")); + + String database = "db_test_rp_lifecycle" + httpEngine(client); + CompletableFuture createDbFuture = client.createDatabase(database); + createDbFuture.get(); + + for (int i = 0; i < rps.size(); i++) { + boolean isDefaultRp = Boolean.FALSE; + if (i == rps.size() - 1) { + isDefaultRp = Boolean.TRUE; + } + CompletableFuture createRsp = client.createRetentionPolicy(database, rps.get(i), + isDefaultRp); + createRsp.get(); + Thread.sleep(2000); + + CompletableFuture> showRpRsp = client.showRetentionPolicies(database); + List rsp = showRpRsp.get(); + CompletableFuture dropRsp = client.dropRetentionPolicy(database, + rps.get(i).getName()); + dropRsp.get(); + String testRpName = testRpNameBase + i; + Assertions.assertTrue(rsp.stream().anyMatch(x -> x.getName().equals(testRpName))); + } + + CompletableFuture dropDbFuture = client.dropDatabase(database); + dropDbFuture.get(); + } + + @ParameterizedTest + @MethodSource("clientList") + void retention_policy_create_failed_for_wrong_duration_param(OpenGeminiClient client) throws Exception { + String database = "db_test_rp_lifecycle_2" + httpEngine(client); + CompletableFuture createDbFuture = client.createDatabase(database); + createDbFuture.get(); + + String testRpName = "rp_test_lifecycle_2" + httpEngine(client); + RpConfig rpConfig = new RpConfig(testRpName, "d3d", "", ""); + CompletableFuture createRpFuture = client.createRetentionPolicy(database, rpConfig, + Boolean.FALSE); + + // todo jdk doesn't throw ExecutionException + Exception exception = Assertions.assertThrows(Exception.class, createRpFuture::get); + if (exception instanceof ExecutionException exp) { + if (exp.getCause() instanceof OpenGeminiException e) { + Assertions.assertTrue( + e.getMessage().contains("syntax error: unexpected IDENT, expecting DURATIONVAL")); + } + } + Thread.sleep(2000); + + CompletableFuture> showRpRsp = client.showRetentionPolicies(database); + List rsp = showRpRsp.get(); + CompletableFuture dropRpFuture = client.dropRetentionPolicy(database, rpConfig.getName()); + dropRpFuture.get(); + + Assertions.assertFalse(rsp.stream().anyMatch(retentionPolicy -> retentionPolicy.getName().equals(testRpName))); + + CompletableFuture dropDbFuture = client.dropDatabase(database); + dropDbFuture.get(); + } + + @ParameterizedTest + @MethodSource("clientList") + void ping(OpenGeminiClient client) throws Exception { + CompletableFuture pingFuture = client.ping(); + Pong pong = pingFuture.get(); + + Assertions.assertNotNull(pong.getVersion()); + } + + private static Point testPoint(String measurementName, int valueIndex, int fieldCount) { + Point testPoint = new Point(); + testPoint.setMeasurement(measurementName); + HashMap tags = new HashMap<>(); + HashMap fields = new HashMap<>(); + for (int i = 0; i < fieldCount; i++) { + tags.put("tag" + i, "value" + valueIndex); + fields.put("field" + i, "value" + valueIndex); + } + testPoint.setTags(tags); + testPoint.setFields(fields); + return testPoint; + } + + + @ParameterizedTest + @MethodSource("clientList") + void testQueryPrecision(OpenGeminiClient client) throws Exception { + String databaseName = "query_precision_0001" + httpEngine(client); + CompletableFuture createdb = client.createDatabase(databaseName); + createdb.get(); + + String measurementName = "query_precision_ms_0001" + httpEngine(client); + Point testPoint1 = testPoint(measurementName, 1, 1); + Point testPoint2 = testPoint(measurementName, 2, 1); + Point testPoint3 = testPoint(measurementName, 3, 1); + + CompletableFuture writeRsp = client.write(databaseName, + Arrays.asList(testPoint1, testPoint2, testPoint3)); + writeRsp.get(); + Thread.sleep(3000); + + Query selectQuery = new Query("select * from " + measurementName, databaseName, ""); + CompletableFuture rst = client.query(selectQuery); + QueryResult queryResult = rst.get(); + + Series x = queryResult.getResults().get(0).getSeries().get(0); + Object timeValue = x.getValues().get(0).get(0); + Assertions.assertInstanceOf(String.class, timeValue); + String timeValueStr = (String) timeValue; + Assertions.assertTrue(timeValueStr.startsWith("20") && timeValueStr.endsWith("Z")); + + selectQuery = new Query("select * from " + measurementName, databaseName, "", Precision.PRECISIONNANOSECOND); + rst = client.query(selectQuery); + queryResult = rst.get(); + + x = queryResult.getResults().get(0).getSeries().get(0); + timeValue = x.getValues().get(0).get(0); + Assertions.assertInstanceOf(Long.class, timeValue); + long timeValueDouble = (Long) timeValue; + Assertions.assertTrue(timeValueDouble > 1724778721457052741L); + + CompletableFuture dropdb = client.dropDatabase(databaseName); + dropdb.get(); + } + + @AfterAll + void closeClients() { + for (OpenGeminiSyncClient client : clients) { + try { + client.close(); + } catch (Exception e) { + // ignore exception + } + } + } +} diff --git a/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiSyncClientWriteTest.java b/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiSyncClientWriteTest.java new file mode 100644 index 00000000..cbf6672b --- /dev/null +++ b/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiSyncClientWriteTest.java @@ -0,0 +1,207 @@ +/* + * Copyright 2025 openGemini 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 + * + * http://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 io.opengemini.client.impl; + +import io.github.openfacade.http.HttpClientConfig; +import io.github.openfacade.http.HttpClientEngine; +import io.opengemini.client.api.Address; +import io.opengemini.client.api.Configuration; +import io.opengemini.client.api.OpenGeminiException; +import io.opengemini.client.api.OpenGeminiSyncClient; +import io.opengemini.client.api.Point; +import io.opengemini.client.api.Query; +import io.opengemini.client.api.QueryResult; +import io.opengemini.client.api.RpConfig; +import io.opengemini.client.api.Series; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class OpenGeminiSyncClientWriteTest extends TestBase{ + private final List clients = new ArrayList<>(); + + protected List clientList() throws OpenGeminiException { + List engines = new ArrayList<>(); + engines.add(HttpClientEngine.Async); + engines.add(HttpClientEngine.Java); + engines.add(HttpClientEngine.Java8); + engines.add(HttpClientEngine.OkHttp); + List clients = new ArrayList<>(); + for (HttpClientEngine engine : engines) { + HttpClientConfig httpConfig = new HttpClientConfig.Builder() + .engine(engine) + .connectTimeout(Duration.ofSeconds(3)) + .timeout(Duration.ofSeconds(3)) + .build(); + Configuration configuration = + Configuration.builder() + .addresses(Collections.singletonList(new Address("127.0.0.1", 8086))) + .httpConfig(httpConfig) + .gzipEnabled(false) + .build(); + clients.add((OpenGeminiSyncClientImpl)OpenGeminiClientFactory.createSyncClient(configuration)); + } + return clients; + } + + @ParameterizedTest + @MethodSource("clientList") + void write_point_success_and_query_success(OpenGeminiSyncClientImpl client) throws Exception { + String databaseName = "db_test_write" + httpEngine(client); + client.createDatabase(databaseName); + + String measurementName = "ms_test_write" + httpEngine(client); + Point testPoint = testPoint(measurementName, 1, 1); + + client.write(databaseName, testPoint); + Thread.sleep(5000); + + Query selectQuery = new Query("select * from " + measurementName, databaseName, null); + QueryResult queryResult = client.query(selectQuery); + + client.dropDatabase(databaseName); + + Series x = queryResult.getResults().get(0).getSeries().get(0); + Assertions.assertEquals(x.getValues().size(), 1); + Assertions.assertTrue(x.getValues().get(0).contains("value1")); + Assertions.assertTrue(x.getColumns().contains("field0")); + Assertions.assertTrue(x.getColumns().contains("tag0")); + } + + @ParameterizedTest + @MethodSource("clientList") + void write_point_with_more_fields(OpenGeminiSyncClientImpl client) throws Exception { + String databaseName = "db_test_write_more_fields" + httpEngine(client); + client.createDatabase(databaseName); + + String measurementName = "md_test_write_more_fields" + httpEngine(client); + Point testPoint = testPoint(measurementName, 1, 30); + + client.write(databaseName, testPoint); + Thread.sleep(3000); + + Query selectQuery = new Query("select * from " + measurementName, databaseName, ""); + QueryResult queryResult = client.query(selectQuery); + + client.dropDatabase(databaseName); + + Series x = queryResult.getResults().get(0).getSeries().get(0); + Assertions.assertEquals(x.getValues().size(), 1); + Assertions.assertTrue(x.getValues().get(0).contains("value1")); + Assertions.assertTrue(x.getColumns().contains("field0")); + Assertions.assertTrue(x.getColumns().contains("tag0")); + Assertions.assertTrue(x.getColumns().contains("field29")); + Assertions.assertTrue(x.getColumns().contains("tag29")); + } + + @ParameterizedTest + @MethodSource("clientList") + void write_empty_batch_points(OpenGeminiSyncClientImpl client) throws Exception { + String databaseName = "db_test_write_batch" + httpEngine(client); + + client.write(databaseName, new ArrayList<>()); + } + + @ParameterizedTest + @MethodSource("clientList") + void write_batch_points(OpenGeminiSyncClientImpl client) throws Exception { + String databaseName = "db_test_write_batch" + httpEngine(client); + client.createDatabase(databaseName); + + String measurementName = "ms_test_write_batch" + httpEngine(client); + Point testPoint1 = testPoint(measurementName, 1, 1); + Point testPoint2 = testPoint(measurementName, 2, 1); + Point testPoint3 = testPoint(measurementName, 3, 1); + + client.write(databaseName, Arrays.asList(testPoint1, testPoint2, testPoint3)); + Thread.sleep(3000); + + Query selectQuery = new Query("select * from " + measurementName, databaseName, ""); + QueryResult queryResult = client.query(selectQuery); + + client.dropDatabase(databaseName); + + Series x = queryResult.getResults().get(0).getSeries().get(0); + Assertions.assertEquals(x.getValues().size(), 3); + Assertions.assertTrue(x.getColumns().contains("field0")); + Assertions.assertTrue(x.getColumns().contains("tag0")); + } + + @ParameterizedTest + @MethodSource("clientList") + void write_batch_points_with_rp(OpenGeminiSyncClientImpl client) throws Exception { + String databaseName = "db_test_write_batch" + httpEngine(client); + client.createDatabase(databaseName); + + String rpName = "rp_test_write_batch" + httpEngine(client); + client.createRetentionPolicy(databaseName, new RpConfig(rpName, "3d", "", ""), false); + + String measurementName = "ms_test_write_batch" + httpEngine(client); + Point testPoint1 = testPoint(measurementName, 1, 1); + Point testPoint2 = testPoint(measurementName, 2, 1); + Point testPoint3 = testPoint(measurementName, 3, 1); + + client.write(databaseName, rpName, Arrays.asList(testPoint1, testPoint2, testPoint3)); + Thread.sleep(3000); + + Query selectQuery = new Query("select * from " + measurementName, databaseName, rpName); + QueryResult queryResult = client.query(selectQuery); + + client.dropRetentionPolicy(databaseName, rpName); + client.dropDatabase(databaseName); + + Series x = queryResult.getResults().get(0).getSeries().get(0); + Assertions.assertEquals(x.getValues().size(), 3); + Assertions.assertTrue(x.getColumns().contains("field0")); + Assertions.assertTrue(x.getColumns().contains("tag0")); + } + + private static Point testPoint(String measurementName, int valueIndex, int fieldCount) { + Point testPoint = new Point(); + testPoint.setMeasurement(measurementName); + HashMap tags = new HashMap<>(); + HashMap fields = new HashMap<>(); + for (int i = 0; i < fieldCount; i++) { + tags.put("tag" + i, "value" + valueIndex); + fields.put("field" + i, "value" + valueIndex); + } + testPoint.setTags(tags); + testPoint.setFields(fields); + return testPoint; + } + + @AfterAll + void closeClients() { + for (OpenGeminiSyncClient client : clients) { + try { + client.close(); + } catch (Exception e) { + // ignore exception + } + } + } +} diff --git a/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiSyncClientWrongAddressTest.java b/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiSyncClientWrongAddressTest.java new file mode 100644 index 00000000..3b37481f --- /dev/null +++ b/opengemini-client/src/test/java/io/opengemini/client/impl/OpenGeminiSyncClientWrongAddressTest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2025 openGemini 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 + * + * http://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 io.opengemini.client.impl; + +import io.github.openfacade.http.HttpClientConfig; +import io.github.openfacade.http.HttpClientEngine; +import io.opengemini.client.api.Address; +import io.opengemini.client.api.Configuration; +import io.opengemini.client.api.OpenGeminiException; +import io.opengemini.client.api.Query; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class OpenGeminiSyncClientWrongAddressTest { + private final List clients = new ArrayList<>(); + + protected List clientList() throws OpenGeminiException { + List engines = new ArrayList<>(); + engines.add(HttpClientEngine.Async); + engines.add(HttpClientEngine.Java); + engines.add(HttpClientEngine.Java8); + engines.add(HttpClientEngine.OkHttp); + List clients = new ArrayList<>(); + for (HttpClientEngine engine : engines) { + HttpClientConfig httpConfig = new HttpClientConfig.Builder() + .engine(engine) + .connectTimeout(Duration.ofSeconds(3)) + .timeout(Duration.ofSeconds(3)) + .build(); + Configuration configuration = Configuration.builder() + .addresses(Collections.singletonList(new Address("127.0.0.1", 28086))) + .httpConfig(httpConfig).gzipEnabled(false).build(); + clients.add((OpenGeminiSyncClientImpl)OpenGeminiClientFactory.create(configuration)); + } + return clients; + } + + @ParameterizedTest + @MethodSource("clientList") + void queryWithWrongAddress(OpenGeminiSyncClientImpl client) { + Query showDatabasesQuery = new Query("SHOW DATABASES"); + Assertions.assertThrows(ExecutionException.class, () -> client.query(showDatabasesQuery)); + } + + @AfterAll + void closeClients() { + for (OpenGeminiSyncClientImpl client : clients) { + try { + client.close(); + } catch (Exception e) { + // ignore exception + } + } + } +} diff --git a/opengemini-client/src/test/java/io/opengemini/client/impl/TestBase.java b/opengemini-client/src/test/java/io/opengemini/client/impl/TestBase.java index 5267cb09..8d61a4f6 100644 --- a/opengemini-client/src/test/java/io/opengemini/client/impl/TestBase.java +++ b/opengemini-client/src/test/java/io/opengemini/client/impl/TestBase.java @@ -22,4 +22,8 @@ class TestBase { protected HttpClientEngine httpEngine(OpenGeminiClient client) { return client.conf.getHttpConfig().engine(); } + + protected HttpClientEngine httpEngine(OpenGeminiSyncClientImpl client) { + return client.conf.getHttpConfig().engine(); + } } diff --git a/spring/opengemini-spring-boot-starter/src/main/java/io/opengemini/client/spring/data/config/OpenGeminiAutoConfiguration.java b/spring/opengemini-spring-boot-starter/src/main/java/io/opengemini/client/spring/data/config/OpenGeminiAutoConfiguration.java index 620a04dc..a9a9f4be 100644 --- a/spring/opengemini-spring-boot-starter/src/main/java/io/opengemini/client/spring/data/config/OpenGeminiAutoConfiguration.java +++ b/spring/opengemini-spring-boot-starter/src/main/java/io/opengemini/client/spring/data/config/OpenGeminiAutoConfiguration.java @@ -18,6 +18,7 @@ import io.opengemini.client.api.OpenGeminiAsyncClient; import io.opengemini.client.api.OpenGeminiException; +import io.opengemini.client.api.OpenGeminiSyncClient; import io.opengemini.client.impl.OpenGeminiClientFactory; import io.opengemini.client.spring.data.core.ClientConfigurationBuilderCustomizer; import io.opengemini.client.spring.data.core.DefaultOpenGeminiSerializerFactory; @@ -56,6 +57,15 @@ public OpenGeminiAsyncClient openGeminiAsyncClient(OpenGeminiProperties properti return OpenGeminiClientFactory.create(converter.toConfiguration()); } + @Bean + @ConditionalOnMissingBean(OpenGeminiSyncClient.class) + public OpenGeminiSyncClient openGeminiSyncClient(OpenGeminiProperties properties, + ObjectProvider customizers) + throws OpenGeminiException { + OpenGeminiPropertiesConverter converter = new OpenGeminiPropertiesConverter(properties, customizers); + return OpenGeminiClientFactory.createSyncClient(converter.toConfiguration()); + } + @Bean @ConditionalOnMissingBean(OpenGeminiSerializerFactory.class) public OpenGeminiSerializerFactory openGeminiSerializerFactory() {