diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 5705bdf..1f52101 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -23,7 +23,7 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle - run: ./gradlew build + run: ./gradlew clean build - name: Coveralls env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} diff --git a/build.gradle b/build.gradle index 46bc2e9..b7d84c8 100644 --- a/build.gradle +++ b/build.gradle @@ -133,14 +133,7 @@ task immudbStart { pb = new ProcessBuilder() pb.command("/bin/bash", "immudb/start.sh") Process proc = pb.start() - - proc.waitFor(10, TimeUnit.SECONDS) - - if (proc.isAlive()) { - println "immudb has been started." - } else { - throw new GradleException("Process exit bad: " + proc.exitValue()) - } + proc.waitFor(15, TimeUnit.SECONDS) } task immudbStop(type: Exec) { @@ -167,6 +160,10 @@ test { } // Run the unit tests sequentially. maxParallelForks = 1 + + testLogging { + events "FAILED" + } } test.dependsOn immudbStart diff --git a/immudb/clean.sh b/immudb/clean.sh index 93583f0..2a16f6a 100755 --- a/immudb/clean.sh +++ b/immudb/clean.sh @@ -11,7 +11,6 @@ then fi rm -rf data -rm -rf states -echo "immudb's data and immudb4j's states folders were removed." +echo "immudb's data folder was removed." diff --git a/immudb/start.sh b/immudb/start.sh index 3e357e4..277a6e2 100755 --- a/immudb/start.sh +++ b/immudb/start.sh @@ -13,10 +13,10 @@ then echo "Downloading immudb..." if [[ "$OSTYPE" == "linux-gnu"* ]]; then - URL=https://github.com/vchain-us/immudb/releases/download/v1.3.0/immudb-v1.3.0-linux-amd64 + URL=https://github.com/vchain-us/immudb/releases/download/v1.4.0/immudb-v1.4.0-linux-amd64 wget -O immudb $URL elif [[ "$OSTYPE" == "darwin"* ]]; then - URL=https://github.com/vchain-us/immudb/releases/download/v1.3.0/immudb-v1.3.0-darwin-amd64 + URL=https://github.com/vchain-us/immudb/releases/download/v1.4.0/immudb-v1.4.0-darwin-amd64 curl -o immudb -L $URL fi diff --git a/src/main/java/io/codenotary/immudb4j/Entry.java b/src/main/java/io/codenotary/immudb4j/Entry.java index 87b6e37..a89c5b7 100644 --- a/src/main/java/io/codenotary/immudb4j/Entry.java +++ b/src/main/java/io/codenotary/immudb4j/Entry.java @@ -28,8 +28,15 @@ public class Entry { private Reference referencedBy; + private long revision; + private Entry() {} + public Entry(byte[] key, byte[] value) { + this.key = key; + this.value = value; + } + public static Entry valueOf(ImmudbProto.Entry e) { final Entry entry = new Entry(); @@ -45,6 +52,8 @@ public static Entry valueOf(ImmudbProto.Entry e) { entry.referencedBy = Reference.valueOf(e.getReferencedBy()); } + entry.revision = e.getRevision(); + return entry; } @@ -68,6 +77,10 @@ public Reference getReferenceBy() { return referencedBy; } + public long getRevision() { + return revision; + } + public byte[] getEncodedKey() { if (referencedBy == null) { return Utils.wrapWithPrefix(key, Consts.SET_KEY_PREFIX); diff --git a/src/main/java/io/codenotary/immudb4j/FileImmuStateHolder.java b/src/main/java/io/codenotary/immudb4j/FileImmuStateHolder.java index 9f2be64..b30d701 100644 --- a/src/main/java/io/codenotary/immudb4j/FileImmuStateHolder.java +++ b/src/main/java/io/codenotary/immudb4j/FileImmuStateHolder.java @@ -59,21 +59,21 @@ private FileImmuStateHolder(Builder builder) throws IOException, IllegalStateExc } @Override - public synchronized ImmuState getState(String serverUuid, String database) { - return stateHolder.getState(serverUuid, database); + public synchronized ImmuState getState(String database) { + return stateHolder.getState(database); } @Override - public synchronized void setState(String serverUuid, ImmuState state) throws IllegalStateException { + public synchronized void setState(ImmuState state) throws IllegalStateException { - ImmuState currentState = stateHolder.getState(serverUuid, state.getDatabase()); + ImmuState currentState = stateHolder.getState(state.getDatabase()); if (currentState != null && currentState.getTxId() >= state.getTxId()) { return; } - stateHolder.setState(serverUuid, state); + stateHolder.setState(state); - Path newStateFile = statesFolder.resolve("state_" + serverUuid + "_" + state.getDatabase() + "_" + System.nanoTime()); + Path newStateFile = statesFolder.resolve("state_" + state.getDatabase() + "_" + System.nanoTime()); if (Files.exists(newStateFile)) { throw new RuntimeException("Failed attempting to create a new state file. Please retry."); diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index b62ae36..ca00ac3 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -19,7 +19,10 @@ import com.google.protobuf.Empty; import io.codenotary.immudb.ImmuServiceGrpc; import io.codenotary.immudb.ImmudbProto; +import io.codenotary.immudb.ImmudbProto.Chunk; import io.codenotary.immudb.ImmudbProto.ScanRequest; +import io.codenotary.immudb.ImmudbProto.Score; +import io.codenotary.immudb4j.basics.LatchHolder; import io.codenotary.immudb4j.crypto.CryptoUtils; import io.codenotary.immudb4j.crypto.DualProof; import io.codenotary.immudb4j.crypto.InclusionProof; @@ -28,16 +31,22 @@ import io.codenotary.immudb4j.user.User; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; -import io.grpc.Metadata; import io.grpc.StatusRuntimeException; -import io.grpc.stub.MetadataUtils; +import io.grpc.stub.StreamObserver; +import io.grpc.ConnectivityState; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.util.ArrayList; import java.util.Arrays; +import java.util.Iterator; import java.util.List; +import java.util.NoSuchElementException; +import java.util.Timer; +import java.util.TimerTask; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -49,119 +58,146 @@ */ public class ImmuClient { - private static final String AUTH_HEADER = "authorization"; - private final ImmuServiceGrpc.ImmuServiceBlockingStub stub; - private final boolean withAuth; + private final PublicKey serverSigningKey; private final ImmuStateHolder stateHolder; + private long keepAlivePeriod; + private int chunkSize; + private ManagedChannel channel; - private String authToken; - private String currentServerUuid; - private String currentDb = "defaultdb"; - private final PublicKey serverSigningKey; - public ImmuClient(Builder builder) { - this.stub = createStubFrom(builder); - this.withAuth = builder.isWithAuth(); - this.stateHolder = builder.getStateHolder(); - this.serverSigningKey = builder.getServerSigningKey(); - } + private final ImmuServiceGrpc.ImmuServiceBlockingStub blockingStub; + private final ImmuServiceGrpc.ImmuServiceStub nonBlockingStub; - public static Builder newBuilder() { - return new Builder(); - } + private Session session; + private Timer sessionHeartBeat; - private ImmuServiceGrpc.ImmuServiceBlockingStub createStubFrom(Builder builder) { - channel = ManagedChannelBuilder.forAddress(builder.getServerUrl(), builder.getServerPort()) + public ImmuClient(Builder builder) { + stateHolder = builder.getStateHolder(); + serverSigningKey = builder.getServerSigningKey(); + keepAlivePeriod = builder.getKeepAlivePeriod(); + chunkSize = builder.getChunkSize(); + + channel = ManagedChannelBuilder + .forAddress(builder.getServerUrl(), builder.getServerPort()) .usePlaintext() - .intercept(new ImmuServerUUIDInterceptor(this)) + .intercept(new ImmudbAuthRequestInterceptor(this)) .build(); - return ImmuServiceGrpc.newBlockingStub(channel); - } - - // --------------------------------------------------------------------- - // These two currentServerUuid related methods are not publicly exposed, - // since these should be called by the ImmuServerUUIDInterceptor only. - - void setCurrentServerUuid(String serverUuid) { - currentServerUuid = serverUuid; + blockingStub = ImmuServiceGrpc.newBlockingStub(channel); + nonBlockingStub = ImmuServiceGrpc.newStub(channel); } - String getCurrentServerUuid() { - return currentServerUuid; + public static Builder newBuilder() { + return new Builder(); } - // --------------------------------------------------------------------- - public synchronized void shutdown() { + public synchronized void shutdown() throws InterruptedException { if (channel == null) { return; } - channel.shutdown(); - if (!channel.isShutdown()) { - try { - channel.awaitTermination(2, TimeUnit.SECONDS); - } catch (InterruptedException e) { - // nothing to do here. - } + + if (session != null) { + closeSession(); + } + + try { + channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); + } finally { + channel = null; } - channel = null; } - public synchronized boolean isShutdown() { - return channel == null; + protected synchronized Session getSession() { + return session; } - private ImmuServiceGrpc.ImmuServiceBlockingStub getStub() { - if (!withAuth || authToken == null) { - return stub; + public synchronized void openSession(String database, String username, String password) { + if (session != null) { + throw new IllegalStateException("session already opened"); } - Metadata metadata = new Metadata(); - metadata.put(Metadata.Key.of(AUTH_HEADER, Metadata.ASCII_STRING_MARSHALLER), "Bearer " + authToken); - - return MetadataUtils.attachHeaders(stub, metadata); - } - - public synchronized void login(String username, String password) { - ImmudbProto.LoginRequest loginRequest = ImmudbProto.LoginRequest + final ImmudbProto.OpenSessionRequest req = ImmudbProto.OpenSessionRequest .newBuilder() - .setUser(Utils.toByteString(username)) + .setDatabaseName(database) + .setUsername(Utils.toByteString(username)) .setPassword(Utils.toByteString(password)) .build(); - ImmudbProto.LoginResponse loginResponse = getStub().login(loginRequest); - authToken = loginResponse.getToken(); + final ImmudbProto.OpenSessionResponse resp = this.blockingStub.openSession(req); + + session = new Session(resp.getSessionID(), database); + + sessionHeartBeat = new Timer(); + + sessionHeartBeat.schedule(new TimerTask() { + @Override + public void run() { + try { + synchronized (ImmuClient.this) { + if (session != null) { + blockingStub.keepAlive(Empty.getDefaultInstance()); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 0, keepAlivePeriod); } - public synchronized void logout() { - getStub().logout(com.google.protobuf.Empty.getDefaultInstance()); - authToken = null; + public synchronized void closeSession() { + if (session == null) { + throw new IllegalStateException("no open session"); + } + + sessionHeartBeat.cancel(); + + try { + blockingStub.closeSession(Empty.getDefaultInstance()); + } finally { + session = null; + } } /** * Get the locally saved state of the current database. * If nothing exists already, it is fetched from the server and save it locally. */ - public ImmuState state() throws VerificationException { - ImmuState state = stateHolder.getState(currentServerUuid, currentDb); + private ImmuState state() throws VerificationException { + if (session == null) { + throw new IllegalStateException("no open session"); + } + + ImmuState state = stateHolder.getState(session.getDatabase()); + if (state == null) { state = currentState(); - stateHolder.setState(currentServerUuid, state); + stateHolder.setState(state); } + return state; } /** * Get the current database state that exists on the server. - * It may throw a RuntimeException if server's state signature verification fails + * It may throw VerificationException if server's state signature verification + * fails * (if this feature is enabled on the client side, at least). + * Note: local state is not updated because this is not a verified operation */ - public ImmuState currentState() throws VerificationException { - final Empty empty = com.google.protobuf.Empty.getDefaultInstance(); - final ImmudbProto.ImmutableState state = getStub().currentState(empty); + public synchronized ImmuState currentState() throws VerificationException { + if (session == null) { + throw new IllegalStateException("no open session"); + } + + final ImmudbProto.ImmutableState state = blockingStub.currentState(Empty.getDefaultInstance()); final ImmuState immuState = ImmuState.valueOf(state); + if (!session.getDatabase().equals(immuState.getDatabase())) { + throw new VerificationException("database mismatch"); + } + if (!immuState.checkSignature(serverSigningKey)) { throw new VerificationException("State signature verification failed"); } @@ -174,31 +210,87 @@ public ImmuState currentState() throws VerificationException { // public void createDatabase(String database) { - final ImmudbProto.CreateDatabaseRequest db = ImmudbProto.CreateDatabaseRequest.newBuilder() + createDatabase(database, false); + } + + public synchronized void createDatabase(String database, boolean ifNotExists) { + if (session == null) { + throw new IllegalStateException("no open session"); + } + + final ImmudbProto.CreateDatabaseRequest req = ImmudbProto.CreateDatabaseRequest.newBuilder() .setName(database) + .setIfNotExists(ifNotExists) .build(); - getStub().createDatabaseV2(db); + blockingStub.createDatabaseV2(req); } - public synchronized void useDatabase(String database) { - final ImmudbProto.Database db = ImmudbProto.Database.newBuilder() - .setDatabaseName(database) + // LoadDatabase loads database on the server. A database is not loaded + // if it has AutoLoad setting set to false or if it failed to load during + // immudb startup. + // + // This call requires SysAdmin permission level or admin permission to the + // database. + public synchronized void loadDatabase(String database) { + if (session == null) { + throw new IllegalStateException("no open session"); + } + + final ImmudbProto.LoadDatabaseRequest req = ImmudbProto.LoadDatabaseRequest.newBuilder() + .setDatabase(database) + .build(); + + blockingStub.loadDatabase(req); + } + + // UnloadDatabase unloads database on the server. Such database becomes + // inaccessible + // by the client and server frees internal resources allocated for that + // database. + // + // This call requires SysAdmin permission level or admin permission to the + // database. + public synchronized void unloadDatabase(String database) { + if (session == null) { + throw new IllegalStateException("no open session"); + } + + final ImmudbProto.UnloadDatabaseRequest req = ImmudbProto.UnloadDatabaseRequest.newBuilder() + .setDatabase(database) .build(); - final ImmudbProto.UseDatabaseReply response = getStub().useDatabase(db); + blockingStub.unloadDatabase(req); + } + + // DeleteDatabase removes an unloaded database. + // This also removes locally stored files used by the database. + // + // This call requires SysAdmin permission level or admin permission to the + // database. + public synchronized void deleteDatabase(String database) { + if (session == null) { + throw new IllegalStateException("no open session"); + } + + final ImmudbProto.DeleteDatabaseRequest req = ImmudbProto.DeleteDatabaseRequest.newBuilder() + .setDatabase(database) + .build(); - authToken = response.getToken(); - currentDb = database; + blockingStub.deleteDatabase(req); } - public List databases() { + public synchronized List databases() { + if (session == null) { + throw new IllegalStateException("no open session"); + } + final ImmudbProto.DatabaseListRequestV2 req = ImmudbProto.DatabaseListRequestV2.newBuilder().build(); - final ImmudbProto.DatabaseListResponseV2 res = getStub().databaseListV2(req); - - final List list = new ArrayList<>(res.getDatabasesCount()); - - for (ImmudbProto.DatabaseWithSettings db : res.getDatabasesList()) { + final ImmudbProto.DatabaseListResponseV2 resp = blockingStub.databaseListV2(req); + + final List list = new ArrayList<>(resp.getDatabasesCount()); + + for (ImmudbProto.DatabaseWithSettings db : resp.getDatabasesList()) { list.add(db.getName()); } @@ -221,14 +313,14 @@ public Entry getAtTx(String key, long tx) throws KeyNotFoundException { return getAtTx(Utils.toByteArray(key), tx); } - public Entry getAtTx(byte[] key, long tx) throws KeyNotFoundException { - final ImmudbProto.KeyRequest req =ImmudbProto.KeyRequest.newBuilder() - .setKey(Utils.toByteString(key)) - .setAtTx(tx) - .build(); + public synchronized Entry getAtTx(byte[] key, long tx) throws KeyNotFoundException { + final ImmudbProto.KeyRequest req = ImmudbProto.KeyRequest.newBuilder() + .setKey(Utils.toByteString(key)) + .setAtTx(tx) + .build(); try { - return Entry.valueOf(getStub().get(req)); + return Entry.valueOf(blockingStub.get(req)); } catch (StatusRuntimeException e) { if (e.getMessage().contains("key not found")) { throw new KeyNotFoundException(); @@ -242,14 +334,14 @@ public Entry getSinceTx(String key, long tx) throws KeyNotFoundException { return getSinceTx(Utils.toByteArray(key), tx); } - public Entry getSinceTx(byte[] key, long tx) throws KeyNotFoundException { - final ImmudbProto.KeyRequest req =ImmudbProto.KeyRequest.newBuilder() + public synchronized Entry getSinceTx(byte[] key, long tx) throws KeyNotFoundException { + final ImmudbProto.KeyRequest req = ImmudbProto.KeyRequest.newBuilder() .setKey(Utils.toByteString(key)) .setSinceTx(tx) .build(); try { - return Entry.valueOf(getStub().get(req)); + return Entry.valueOf(blockingStub.get(req)); } catch (StatusRuntimeException e) { if (e.getMessage().contains("key not found")) { throw new KeyNotFoundException(); @@ -263,14 +355,14 @@ public Entry getAtRevision(String key, long rev) throws KeyNotFoundException { return getAtRevision(Utils.toByteArray(key), rev); } - public Entry getAtRevision(byte[] key, long rev) throws KeyNotFoundException { - final ImmudbProto.KeyRequest req =ImmudbProto.KeyRequest.newBuilder() + public synchronized Entry getAtRevision(byte[] key, long rev) throws KeyNotFoundException { + final ImmudbProto.KeyRequest req = ImmudbProto.KeyRequest.newBuilder() .setKey(Utils.toByteString(key)) .setAtRevision(rev) .build(); try { - return Entry.valueOf(getStub().get(req)); + return Entry.valueOf(blockingStub.get(req)); } catch (StatusRuntimeException e) { if (e.getMessage().contains("key not found")) { throw new KeyNotFoundException(); @@ -280,15 +372,15 @@ public Entry getAtRevision(byte[] key, long rev) throws KeyNotFoundException { } } - public List getAll(List keys) { + public synchronized List getAll(List keys) { final List keysBS = new ArrayList<>(keys.size()); - + for (String key : keys) { keysBS.add(Utils.toByteString(key)); } final ImmudbProto.KeyListRequest req = ImmudbProto.KeyListRequest.newBuilder().addAllKeys(keysBS).build(); - final ImmudbProto.Entries entries = getStub().getAll(req); + final ImmudbProto.Entries entries = blockingStub.getAll(req); final List result = new ArrayList<>(entries.getEntriesCount()); @@ -311,51 +403,62 @@ public Entry verifiedGetAtTx(String key, long tx) throws KeyNotFoundException, V return verifiedGetAtTx(Utils.toByteArray(key), tx); } - public Entry verifiedGetAtTx(byte[] key, long tx) throws KeyNotFoundException, VerificationException { + public synchronized Entry verifiedGetAtTx(byte[] key, long tx) throws KeyNotFoundException, VerificationException { + final ImmuState state = state(); + final ImmudbProto.KeyRequest keyReq = ImmudbProto.KeyRequest.newBuilder() .setKey(Utils.toByteString(key)) .setAtTx(tx) .build(); - return verifiedGet(keyReq, state()); + return verifiedGet(keyReq, state); } public Entry verifiedGetSinceTx(String key, long tx) throws KeyNotFoundException, VerificationException { return verifiedGetSinceTx(Utils.toByteArray(key), tx); } - public Entry verifiedGetSinceTx(byte[] key, long tx) throws KeyNotFoundException, VerificationException { + public synchronized Entry verifiedGetSinceTx(byte[] key, long tx) + throws KeyNotFoundException, VerificationException { + + final ImmuState state = state(); + final ImmudbProto.KeyRequest keyReq = ImmudbProto.KeyRequest.newBuilder() .setKey(Utils.toByteString(key)) .setSinceTx(tx) .build(); - return verifiedGet(keyReq, state()); + return verifiedGet(keyReq, state); } public Entry verifiedGetAtRevision(String key, long rev) throws KeyNotFoundException, VerificationException { return verifiedGetAtRevision(Utils.toByteArray(key), rev); } - public Entry verifiedGetAtRevision(byte[] key, long rev) throws KeyNotFoundException, VerificationException { + public synchronized Entry verifiedGetAtRevision(byte[] key, long rev) + throws KeyNotFoundException, VerificationException { + + final ImmuState state = state(); + final ImmudbProto.KeyRequest keyReq = ImmudbProto.KeyRequest.newBuilder() .setKey(Utils.toByteString(key)) .setAtRevision(rev) .build(); - return verifiedGet(keyReq, state()); + return verifiedGet(keyReq, state); } - private Entry verifiedGet(ImmudbProto.KeyRequest keyReq, ImmuState state) throws KeyNotFoundException, VerificationException { + private Entry verifiedGet(ImmudbProto.KeyRequest keyReq, ImmuState state) + throws KeyNotFoundException, VerificationException { final ImmudbProto.VerifiableGetRequest vGetReq = ImmudbProto.VerifiableGetRequest.newBuilder() .setKeyRequest(keyReq) .setProveSinceTx(state.getTxId()) .build(); - + final ImmudbProto.VerifiableEntry vEntry; try { - vEntry = getStub().verifiableGet(vGetReq); + vEntry = blockingStub.verifiableGet(vGetReq); } catch (StatusRuntimeException e) { if (e.getMessage().contains("key not found")) { throw new KeyNotFoundException(); @@ -371,14 +474,15 @@ private Entry verifiedGet(ImmudbProto.KeyRequest keyReq, ImmuState state) throws long sourceId, targetId; byte[] sourceAlh; byte[] targetAlh; - + final Entry entry = Entry.valueOf(vEntry.getEntry()); if (entry.getReferenceBy() == null && !Arrays.equals(keyReq.getKey().toByteArray(), entry.getKey())) { throw new VerificationException("Data is corrupted: entry does not belong to specified key"); } - if (entry.getReferenceBy() != null && !Arrays.equals(keyReq.getKey().toByteArray(), entry.getReferenceBy().getKey())) { + if (entry.getReferenceBy() != null + && !Arrays.equals(keyReq.getKey().toByteArray(), entry.getReferenceBy().getKey())) { throw new VerificationException("Data is corrupted: entry does not belong to specified key"); } @@ -420,14 +524,13 @@ private Entry verifiedGet(ImmudbProto.KeyRequest keyReq, ImmuState state) throws sourceId, targetId, sourceAlh, - targetAlh - )) { + targetAlh)) { throw new VerificationException("Dual proof verification failed."); } } final ImmuState newState = new ImmuState( - currentDb, + session.getDatabase(), targetId, targetAlh, vEntry.getVerifiableTx().getSignature().toByteArray()); @@ -436,7 +539,7 @@ private Entry verifiedGet(ImmudbProto.KeyRequest keyReq, ImmuState state) throws throw new VerificationException("State signature verification failed"); } - stateHolder.setState(currentServerUuid, newState); + stateHolder.setState(newState); return Entry.valueOf(vEntry.getEntry()); } @@ -449,13 +552,13 @@ public TxHeader delete(String key) throws KeyNotFoundException { return delete(Utils.toByteArray(key)); } - public TxHeader delete(byte[] key) throws KeyNotFoundException { + public synchronized TxHeader delete(byte[] key) throws KeyNotFoundException { try { final ImmudbProto.DeleteKeysRequest req = ImmudbProto.DeleteKeysRequest.newBuilder() .addKeys(Utils.toByteString(key)) .build(); - return TxHeader.valueOf(getStub().delete(req)); + return TxHeader.valueOf(blockingStub.delete(req)); } catch (StatusRuntimeException e) { if (e.getMessage().contains("key not found")) { throw new KeyNotFoundException(); @@ -469,19 +572,19 @@ public TxHeader delete(byte[] key) throws KeyNotFoundException { // ========== HISTORY ========== // - public List history(String key, int limit, long offset, boolean desc) throws KeyNotFoundException { - return history(Utils.toByteArray(key), limit, offset, desc); + public List historyAll(String key, long offset, boolean desc, int limit) throws KeyNotFoundException { + return historyAll(Utils.toByteArray(key), offset, desc, limit); } - public List history(byte[] key, int limit, long offset, boolean desc) throws KeyNotFoundException { + public synchronized List historyAll(byte[] key, long offset, boolean desc, int limit) + throws KeyNotFoundException { try { - ImmudbProto.Entries entries = getStub().history(ImmudbProto.HistoryRequest.newBuilder() - .setKey(Utils.toByteString(key)) - .setLimit(limit) - .setOffset(offset) - .setDesc(desc) - .build() - ); + ImmudbProto.Entries entries = blockingStub.history(ImmudbProto.HistoryRequest.newBuilder() + .setKey(Utils.toByteString(key)) + .setDesc(desc) + .setOffset(offset) + .setLimit(limit) + .build()); return buildList(entries); } catch (StatusRuntimeException e) { @@ -497,51 +600,44 @@ public List history(byte[] key, int limit, long offset, boolean desc) thr // ========== SCAN ========== // - public List scan(String prefix) { - return scan(Utils.toByteArray(prefix)); + public List scanAll(String prefix) { + return scanAll(Utils.toByteArray(prefix)); } - public List scan(byte[] prefix) { - return scan(prefix, 0, false); + public List scanAll(byte[] prefix) { + return scanAll(prefix, false, 0); } - public List scan(String prefix, long limit, boolean desc) { - return scan(Utils.toByteArray(prefix), limit, desc); + public List scanAll(String prefix, boolean desc, long limit) { + return scanAll(Utils.toByteArray(prefix), null, desc, limit); } - public List scan(byte[] prefix, long limit, boolean desc) { - return scan(prefix, null, limit, desc); + public List scanAll(byte[] prefix, boolean desc, long limit) { + return scanAll(prefix, null, desc, limit); } - public List scan(String prefix, String seekKey, long limit, boolean desc) { - return scan(Utils.toByteArray(prefix), Utils.toByteArray(seekKey), limit, desc); + public List scanAll(byte[] prefix, byte[] seekKey, boolean desc, long limit) { + return scanAll(prefix, seekKey, null, desc, limit); } - public List scan(String prefix, String seekKey, String endKey, long limit, boolean desc) { - return scan(Utils.toByteArray(prefix), Utils.toByteArray(seekKey), Utils.toByteArray(endKey), limit, desc); + public List scanAll(byte[] prefix, byte[] seekKey, byte[] endKey, boolean desc, long limit) { + return scanAll(prefix, seekKey, endKey, false, false, desc, limit); } - public List scan(byte[] prefix, byte[] seekKey, long limit, boolean desc) { - return scan(prefix, seekKey, null, limit, desc); - } - - public List scan(byte[] prefix, byte[] seekKey, byte[] endKey, long limit, boolean desc) { - return scan(prefix, seekKey, endKey, false, false, limit, desc); - } - - public List scan(byte[] prefix, byte[] seekKey, byte[] endKey, boolean inclusiveSeek, boolean inclusiveEnd, - long limit, boolean desc) { + public synchronized List scanAll(byte[] prefix, byte[] seekKey, byte[] endKey, boolean inclusiveSeek, + boolean inclusiveEnd, + boolean desc, long limit) { final ImmudbProto.ScanRequest req = ScanRequest.newBuilder() .setPrefix(Utils.toByteString(prefix)) .setSeekKey(Utils.toByteString(seekKey)) .setEndKey(Utils.toByteString(endKey)) .setInclusiveSeek(inclusiveSeek) .setInclusiveEnd(inclusiveEnd) - .setLimit(limit) .setDesc(desc) + .setLimit(limit) .build(); - final ImmudbProto.Entries entries = getStub().scan(req); + final ImmudbProto.Entries entries = blockingStub.scan(req); return buildList(entries); } @@ -553,15 +649,15 @@ public TxHeader set(String key, byte[] value) throws CorruptedDataException { return set(Utils.toByteArray(key), value); } - public TxHeader set(byte[] key, byte[] value) throws CorruptedDataException { + public synchronized TxHeader set(byte[] key, byte[] value) throws CorruptedDataException { final ImmudbProto.KeyValue kv = ImmudbProto.KeyValue.newBuilder() .setKey(Utils.toByteString(key)) .setValue(Utils.toByteString(value)) .build(); final ImmudbProto.SetRequest req = ImmudbProto.SetRequest.newBuilder().addKVs(kv).build(); - final ImmudbProto.TxHeader txHdr = getStub().set(req); - + final ImmudbProto.TxHeader txHdr = blockingStub.set(req); + if (txHdr.getNentries() != 1) { throw new CorruptedDataException(); } @@ -569,9 +665,9 @@ public TxHeader set(byte[] key, byte[] value) throws CorruptedDataException { return TxHeader.valueOf(txHdr); } - public TxHeader setAll(List kvList) throws CorruptedDataException { + public synchronized TxHeader setAll(List kvList) throws CorruptedDataException { final ImmudbProto.SetRequest.Builder reqBuilder = ImmudbProto.SetRequest.newBuilder(); - + for (KVPair kv : kvList) { ImmudbProto.KeyValue.Builder kvBuilder = ImmudbProto.KeyValue.newBuilder(); @@ -581,7 +677,7 @@ public TxHeader setAll(List kvList) throws CorruptedDataException { reqBuilder.addKVs(kvBuilder.build()); } - final ImmudbProto.TxHeader txHdr = getStub().set(reqBuilder.build()); + final ImmudbProto.TxHeader txHdr = blockingStub.set(reqBuilder.build()); if (txHdr.getNentries() != kvList.size()) { throw new CorruptedDataException(); @@ -602,7 +698,8 @@ public TxHeader setReference(String key, String referencedKey, long atTx) throws return setReference(Utils.toByteArray(key), Utils.toByteArray(referencedKey), atTx); } - public TxHeader setReference(byte[] key, byte[] referencedKey, long atTx) throws CorruptedDataException { + public synchronized TxHeader setReference(byte[] key, byte[] referencedKey, long atTx) + throws CorruptedDataException { final ImmudbProto.ReferenceRequest req = ImmudbProto.ReferenceRequest.newBuilder() .setKey(Utils.toByteString(key)) .setReferencedKey(Utils.toByteString(referencedKey)) @@ -610,8 +707,8 @@ public TxHeader setReference(byte[] key, byte[] referencedKey, long atTx) throws .setBoundRef(atTx > 0) .build(); - final ImmudbProto.TxHeader txHdr = getStub().setReference(req); - + final ImmudbProto.TxHeader txHdr = blockingStub.setReference(req); + if (txHdr.getNentries() != 1) { throw new CorruptedDataException(); } @@ -619,34 +716,34 @@ public TxHeader setReference(byte[] key, byte[] referencedKey, long atTx) throws return TxHeader.valueOf(txHdr); } - public TxHeader verifiedSet(String key, byte[] value) throws VerificationException { + public TxHeader verifiedSet(String key, byte[] value) throws VerificationException { return verifiedSet(Utils.toByteArray(key), value); } - public TxHeader verifiedSet(byte[] key, byte[] value) throws VerificationException { + public synchronized TxHeader verifiedSet(byte[] key, byte[] value) throws VerificationException { final ImmuState state = state(); + final ImmudbProto.KeyValue kv = ImmudbProto.KeyValue.newBuilder() - .setKey(Utils.toByteString(key)) - .setValue(Utils.toByteString(value)) - .build(); - + .setKey(Utils.toByteString(key)) + .setValue(Utils.toByteString(value)) + .build(); + final ImmudbProto.VerifiableSetRequest vSetReq = ImmudbProto.VerifiableSetRequest.newBuilder() .setSetRequest(ImmudbProto.SetRequest.newBuilder().addKVs(kv).build()) .setProveSinceTx(state.getTxId()) .build(); - - final ImmudbProto.VerifiableTx vtx = getStub().verifiableSet(vSetReq); - + + final ImmudbProto.VerifiableTx vtx = blockingStub.verifiableSet(vSetReq); + final int ne = vtx.getTx().getHeader().getNentries(); - + if (ne != 1 || vtx.getTx().getEntriesList().size() != 1) { throw new VerificationException( - String.format("Got back %d entries (in tx metadata) instead of 1.", ne) - ); + String.format("Got back %d entries (in tx metadata) instead of 1.", ne)); } - + final Tx tx; - + try { tx = Tx.valueOf(vtx.getTx()); } catch (Exception e) { @@ -658,8 +755,7 @@ public TxHeader verifiedSet(byte[] key, byte[] value) throws VerificationExcepti final Entry entry = Entry.valueOf(ImmudbProto.Entry.newBuilder() .setKey(Utils.toByteString(key)) .setValue(Utils.toByteString(value)) - .build() - ); + .build()); final InclusionProof inclusionProof = tx.proof(entry.getEncodedKey()); @@ -673,17 +769,18 @@ public TxHeader verifiedSet(byte[] key, byte[] value) throws VerificationExcepti throw new VerificationException("State signature verification failed"); } - stateHolder.setState(currentServerUuid, newState); + stateHolder.setState(newState); return TxHeader.valueOf(vtx.getTx().getHeader()); } - public TxHeader verifiedSetReference(byte[] key, byte[] referencedKey) throws VerificationException { return verifiedSetReference(key, referencedKey, 0); } - public TxHeader verifiedSetReference(byte[] key, byte[] referencedKey, long atTx) throws VerificationException { + public synchronized TxHeader verifiedSetReference(byte[] key, byte[] referencedKey, long atTx) + throws VerificationException { + final ImmuState state = state(); final ImmudbProto.ReferenceRequest refReq = ImmudbProto.ReferenceRequest.newBuilder() @@ -698,8 +795,8 @@ public TxHeader verifiedSetReference(byte[] key, byte[] referencedKey, long atTx .setProveSinceTx(state.getTxId()) .build(); - final ImmudbProto.VerifiableTx vtx = getStub().verifiableSetReference(vRefReq); - + final ImmudbProto.VerifiableTx vtx = blockingStub.verifiableSetReference(vRefReq); + final int vtxNentries = vtx.getTx().getHeader().getNentries(); if (vtxNentries != 1) { throw new VerificationException(String.format("Data is corrupted (verifTx has %d Nentries instead of 1).", @@ -720,18 +817,17 @@ public TxHeader verifiedSetReference(byte[] key, byte[] referencedKey, long atTx .setReferencedBy(ImmudbProto.Reference.newBuilder() .setKey(Utils.toByteString(key)) .setAtTx(atTx) - .build() - ) - .build() - ); + .build()) + .build()); final InclusionProof inclusionProof = tx.proof(entry.getEncodedKey()); - + if (!CryptoUtils.verifyInclusion(inclusionProof, entry.digestFor(txHeader.getVersion()), txHeader.getEh())) { throw new VerificationException("Data is corrupted (inclusion verification failed)."); } - if (Arrays.equals(txHeader.getEh(), CryptoUtils.digestFrom(vtx.getDualProof().getTargetTxHeader().getEH().toByteArray()))) { + if (Arrays.equals(txHeader.getEh(), + CryptoUtils.digestFrom(vtx.getDualProof().getTargetTxHeader().getEH().toByteArray()))) { throw new VerificationException("Data is corrupted (different digests)."); } @@ -741,7 +837,7 @@ public TxHeader verifiedSetReference(byte[] key, byte[] referencedKey, long atTx throw new VerificationException("State signature verification failed"); } - stateHolder.setState(currentServerUuid, newState); + stateHolder.setState(newState); return TxHeader.valueOf(vtx.getTx().getHeader()); } @@ -760,13 +856,12 @@ private ImmuState verifyDualProof(ImmudbProto.VerifiableTx vtx, Tx tx, ImmuState sourceId, targetId, sourceAlh, - targetAlh - )) { + targetAlh)) { throw new VerificationException("Data is corrupted (dual proof verification failed)."); } } - return new ImmuState(currentDb, targetId, targetAlh, vtx.getSignature().getSignature().toByteArray()); + return new ImmuState(state.getDatabase(), targetId, targetAlh, vtx.getSignature().getSignature().toByteArray()); } // @@ -781,17 +876,16 @@ public TxHeader zAdd(byte[] set, byte[] key, double score) throws CorruptedDataE return zAdd(set, key, 0, score); } - public TxHeader zAdd(byte[] set, byte[] key, long atTx, double score) throws CorruptedDataException { - final ImmudbProto.TxHeader txHdr = getStub().zAdd( + public synchronized TxHeader zAdd(byte[] set, byte[] key, long atTx, double score) throws CorruptedDataException { + final ImmudbProto.TxHeader txHdr = blockingStub.zAdd( ImmudbProto.ZAddRequest.newBuilder() .setSet(Utils.toByteString(set)) .setKey(Utils.toByteString(key)) .setAtTx(atTx) .setScore(score) .setBoundRef(atTx > 0) - .build() - ); - + .build()); + if (txHdr.getNentries() != 1) { throw new CorruptedDataException(); } @@ -799,20 +893,21 @@ public TxHeader zAdd(byte[] set, byte[] key, long atTx, double score) throws Cor return TxHeader.valueOf(txHdr); } - public TxHeader verifiedZAdd(String set, String key, double score) throws VerificationException { return verifiedZAdd(Utils.toByteArray(set), Utils.toByteArray(key), score); } public TxHeader verifiedZAdd(byte[] set, byte[] key, double score) throws VerificationException { - return verifiedZAdd(set, key,0, score); + return verifiedZAdd(set, key, 0, score); } public TxHeader verifiedZAdd(String set, String key, long atTx, double score) throws VerificationException { return verifiedZAdd(Utils.toByteArray(set), Utils.toByteArray(key), atTx, score); } - public TxHeader verifiedZAdd(byte[] set, byte[] key, long atTx, double score) throws VerificationException { + public synchronized TxHeader verifiedZAdd(byte[] set, byte[] key, long atTx, double score) + throws VerificationException { + final ImmuState state = state(); final ImmudbProto.ZAddRequest zAddReq = ImmudbProto.ZAddRequest.newBuilder() @@ -822,13 +917,13 @@ public TxHeader verifiedZAdd(byte[] set, byte[] key, long atTx, double score) th .setBoundRef(atTx > 0) .setScore(score) .build(); - + final ImmudbProto.VerifiableZAddRequest vZAddReq = ImmudbProto.VerifiableZAddRequest.newBuilder() .setZAddRequest(zAddReq) .setProveSinceTx(state.getTxId()) .build(); - - final ImmudbProto.VerifiableTx vtx = getStub().verifiableZAdd(vZAddReq); + + final ImmudbProto.VerifiableTx vtx = blockingStub.verifiableZAdd(vZAddReq); if (vtx.getTx().getHeader().getNentries() != 1) { throw new VerificationException("Data is corrupted."); @@ -848,8 +943,7 @@ public TxHeader verifiedZAdd(byte[] set, byte[] key, long atTx, double score) th .setKey(Utils.toByteString(key)) .setAtTx(atTx) .setScore(score) - .build() - ); + .build()); InclusionProof inclusionProof = tx.proof(entry.getEncodedKey()); @@ -857,7 +951,8 @@ public TxHeader verifiedZAdd(byte[] set, byte[] key, long atTx, double score) th throw new VerificationException("Data is corrupted (inclusion verification failed)."); } - if (!Arrays.equals(txHeader.getEh(), CryptoUtils.digestFrom(vtx.getDualProof().getTargetTxHeader().getEH().toByteArray()))) { + if (!Arrays.equals(txHeader.getEh(), + CryptoUtils.digestFrom(vtx.getDualProof().getTargetTxHeader().getEH().toByteArray()))) { throw new VerificationException("Data is corrupted (different digests)."); } @@ -867,35 +962,61 @@ public TxHeader verifiedZAdd(byte[] set, byte[] key, long atTx, double score) th throw new VerificationException("State signature verification failed"); } - stateHolder.setState(currentServerUuid, newState); + stateHolder.setState(newState); return TxHeader.valueOf(vtx.getTx().getHeader()); } - public List zScan(String set, long limit, boolean reverse) { - return zScan(Utils.toByteArray(set), limit, reverse); + public List zScanAll(String set) { + return zScanAll(set, false, 0); } - public List zScan(byte[] set, long limit, boolean reverse) { - final ImmudbProto.ZScanRequest req = ImmudbProto.ZScanRequest - .newBuilder() - .setSet(Utils.toByteString(set)) - .setLimit(limit) + public List zScanAll(String set, boolean reverse, long limit) { + return pzScanAll(Utils.toByteArray(set), null, null, null, null, 0, false, reverse, limit); + } + + public List zScanAll(byte[] set, double minScore, double maxScore, boolean reverse, long limit) { + return pzScanAll(set, minScore, maxScore, null, null, 0, false, false, 0); + } + + public List zScanAll(byte[] set, double minScore, double maxScore, double seekScore, byte[] seekKey, + long seekAtTx, boolean inclusiveSeek, boolean reverse, long limit) { + return pzScanAll(set, minScore, maxScore, seekScore, seekKey, seekAtTx, inclusiveSeek, reverse, limit); + } + + private synchronized List pzScanAll(byte[] set, Double minScore, Double maxScore, Double seekScore, + byte[] seekKey, + long seekAtTx, boolean inclusiveSeek, boolean reverse, long limit) { + + final ImmudbProto.ZScanRequest.Builder reqBuilder = ImmudbProto.ZScanRequest.newBuilder(); + + reqBuilder.setSet(Utils.toByteString(set)) + .setSeekKey(Utils.toByteString(seekKey)) + .setSeekAtTx(seekAtTx) + .setInclusiveSeek(inclusiveSeek) .setDesc(reverse) - .build(); + .setLimit(limit); - final ImmudbProto.ZEntries zEntries = getStub().zScan(req); + if (seekScore != null) { + reqBuilder.setSeekScore(seekScore); + } + + if (minScore != null) { + reqBuilder.setMinScore(Score.newBuilder().setScore(minScore).build()); + } + + if (maxScore != null) { + reqBuilder.setMaxScore(Score.newBuilder().setScore(maxScore).build()); + } + + final ImmudbProto.ZEntries zEntries = blockingStub.zScan(reqBuilder.build()); return buildList(zEntries); } - // - // ========== TX ========== - // - - public Tx txById(long txId) throws TxNotFoundException, NoSuchAlgorithmException { + public synchronized Tx txById(long txId) throws TxNotFoundException, NoSuchAlgorithmException { try { - final ImmudbProto.Tx tx = getStub().txById(ImmudbProto.TxRequest.newBuilder().setTx(txId).build()); + final ImmudbProto.Tx tx = blockingStub.txById(ImmudbProto.TxRequest.newBuilder().setTx(txId).build()); return Tx.valueOf(tx); } catch (StatusRuntimeException e) { if (e.getMessage().contains("tx not found")) { @@ -906,17 +1027,18 @@ public Tx txById(long txId) throws TxNotFoundException, NoSuchAlgorithmException } } - public Tx verifiedTxById(long txId) throws TxNotFoundException, VerificationException { + public synchronized Tx verifiedTxById(long txId) throws TxNotFoundException, VerificationException { final ImmuState state = state(); + final ImmudbProto.VerifiableTxRequest vTxReq = ImmudbProto.VerifiableTxRequest.newBuilder() .setTx(txId) .setProveSinceTx(state.getTxId()) .build(); - + final ImmudbProto.VerifiableTx vtx; try { - vtx = getStub().verifiableTxById(vTxReq); + vtx = blockingStub.verifiableTxById(vTxReq); } catch (StatusRuntimeException e) { if (e.getMessage().contains("tx not found")) { throw new TxNotFoundException(); @@ -950,8 +1072,7 @@ public Tx verifiedTxById(long txId) throws TxNotFoundException, VerificationExce sourceId, targetId, sourceAlh, - targetAlh - )) { + targetAlh)) { throw new VerificationException("Data is corrupted (dual proof verification failed)."); } } @@ -963,52 +1084,394 @@ public Tx verifiedTxById(long txId) throws TxNotFoundException, VerificationExce throw new VerificationException("Failed to extract the transaction.", e); } - final ImmuState newState = new ImmuState(currentDb, targetId, targetAlh, vtx.getSignature().getSignature().toByteArray()); + final ImmuState newState = new ImmuState(state.getDatabase(), targetId, targetAlh, + vtx.getSignature().getSignature().toByteArray()); if (!newState.checkSignature(serverSigningKey)) { throw new VerificationException("State signature verification failed"); } - stateHolder.setState(currentServerUuid, newState); + stateHolder.setState(newState); return tx; } - public List txScan(long initialTxId) { + public synchronized List txScanAll(long initialTxId) { final ImmudbProto.TxScanRequest req = ImmudbProto.TxScanRequest.newBuilder().setInitialTx(initialTxId).build(); - final ImmudbProto.TxList txList = getStub().txScan(req); + final ImmudbProto.TxList txList = blockingStub.txScan(req); + return buildList(txList); } - public List txScan(long initialTxId, int limit, boolean desc) { + public synchronized List txScanAll(long initialTxId, int limit, boolean desc) { final ImmudbProto.TxScanRequest req = ImmudbProto.TxScanRequest .newBuilder() .setInitialTx(initialTxId) - .setLimit(limit) .setDesc(desc) + .setLimit(limit) .build(); - final ImmudbProto.TxList txList = getStub().txScan(req); + + final ImmudbProto.TxList txList = blockingStub.txScan(req); return buildList(txList); } // - // ========== HEALTH ========== + // ========== STREAMS ========== // - public boolean healthCheck() { - return getStub().health(Empty.getDefaultInstance()).getStatus(); + private StreamObserver txHeaderStreamObserver(LatchHolder latchHolder) { + return new StreamObserver() { + @Override + public void onCompleted() { + } + + @Override + public void onError(Throwable cause) { + throw new RuntimeException(cause); + } + + @Override + public void onNext(ImmudbProto.TxHeader hdr) { + latchHolder.doneWith(hdr); + } + }; + } + + private void chunkIt(byte[] bs, StreamObserver streamObserver) { + final ByteBuffer buf = ByteBuffer.allocate(chunkSize).order(ByteOrder.BIG_ENDIAN); + + buf.putLong(bs.length); + + int i = 0; + + while (i < bs.length) { + final int chunkContentLen = Math.min(bs.length, buf.remaining()); + + buf.put(bs, i, chunkContentLen); + + buf.flip(); + + byte[] chunkContent = new byte[buf.limit()]; + buf.get(chunkContent); + + final Chunk chunk = Chunk.newBuilder().setContent(Utils.toByteString(chunkContent)).build(); + + streamObserver.onNext(chunk); + + buf.clear(); + + i += chunkContentLen; + } + } + + private byte[] dechunkIt(Iterator chunks) { + final Chunk firstChunk = chunks.next(); + final byte[] firstChunkContent = firstChunk.getContent().toByteArray(); + + if (firstChunkContent.length < Long.BYTES) { + throw new RuntimeException("invalid chunk"); + } + + final ByteBuffer b = ByteBuffer.allocate(Long.BYTES).order(ByteOrder.BIG_ENDIAN); + b.put(firstChunkContent, 0, Long.BYTES); + + int payloadSize = (int) b.getLong(0); + + final ByteBuffer buf = ByteBuffer.allocate(payloadSize); + buf.put(firstChunkContent, Long.BYTES, firstChunkContent.length - Long.BYTES); + + while (buf.position() < payloadSize) { + Chunk chunk = chunks.next(); + buf.put(chunk.getContent().toByteArray()); + } + + return buf.array(); } + private Iterator entryIterator(Iterator chunks) { + return new Iterator() { + + @Override + public boolean hasNext() { + try { + return chunks.hasNext(); + } catch (StatusRuntimeException e) { + if (e.getMessage().contains("key not found")) { + return false; + } + + throw e; + } + } + + @Override + public Entry next() { + return new Entry(dechunkIt(chunks), dechunkIt(chunks)); + } + + }; + } + + private Iterator zentryIterator(Iterator chunks) { + return new Iterator() { + + @Override + public boolean hasNext() { + try { + return chunks.hasNext(); + } catch (StatusRuntimeException e) { + if (e.getMessage().contains("key not found")) { + return false; + } + + throw e; + } + } + + @Override + public ZEntry next() { + final byte[] set = dechunkIt(chunks); + final byte[] key = dechunkIt(chunks); + + final ByteBuffer b = ByteBuffer.allocate(Double.BYTES + Long.BYTES).order(ByteOrder.BIG_ENDIAN); + b.put(dechunkIt(chunks)); // score + b.put(dechunkIt(chunks)); // atTx + + double score = b.getDouble(0); + long atTx = b.getLong(Double.BYTES); + + final byte[] value = dechunkIt(chunks); + + final Entry entry = new Entry(key, value); + + return new ZEntry(set, key, score, atTx, entry); + } + + }; + } + + // + // ========== STREAM SET ========== + // + + public TxHeader streamSet(String key, byte[] value) throws InterruptedException, CorruptedDataException { + return streamSet(Utils.toByteArray(key), value); + } + + public synchronized TxHeader streamSet(byte[] key, byte[] value) + throws InterruptedException, CorruptedDataException { + final LatchHolder latchHolder = new LatchHolder<>(); + final StreamObserver streamObserver = nonBlockingStub.streamSet(txHeaderStreamObserver(latchHolder)); + + chunkIt(key, streamObserver); + chunkIt(value, streamObserver); + + streamObserver.onCompleted(); + + final ImmudbProto.TxHeader txHdr = latchHolder.awaitValue(); + + if (txHdr.getNentries() != 1) { + throw new CorruptedDataException(); + } + + return TxHeader.valueOf(txHdr); + } + + public synchronized TxHeader streamSetAll(List kvList) throws InterruptedException, CorruptedDataException { + final LatchHolder latchHolder = new LatchHolder<>(); + final StreamObserver streamObserver = nonBlockingStub.streamSet(txHeaderStreamObserver(latchHolder)); + + for (KVPair kv : kvList) { + chunkIt(kv.getKey(), streamObserver); + chunkIt(kv.getValue(), streamObserver); + } + + streamObserver.onCompleted(); + + final ImmudbProto.TxHeader txHdr = latchHolder.awaitValue(); + + if (txHdr.getNentries() != kvList.size()) { + throw new CorruptedDataException(); + } + + return TxHeader.valueOf(txHdr); + } + + // + // ========== STREAM GET ========== + // + + public Entry streamGet(String key) throws KeyNotFoundException { + return streamGet(Utils.toByteArray(key)); + } + + public synchronized Entry streamGet(byte[] key) throws KeyNotFoundException { + final ImmudbProto.KeyRequest req = ImmudbProto.KeyRequest.newBuilder() + .setKey(Utils.toByteString(key)) + .build(); + + try { + final Iterator chunks = blockingStub.streamGet(req); + return new Entry(dechunkIt(chunks), dechunkIt(chunks)); + } catch (StatusRuntimeException e) { + if (e.getMessage().contains("key not found")) { + throw new KeyNotFoundException(); + } + + throw e; + } + } + + // + // ========== STREAM SCAN ========== + // + + public Iterator scan(String prefix) { + return scan(Utils.toByteArray(prefix)); + } + + public Iterator scan(byte[] prefix) { + return scan(prefix, false, 0); + } + + public Iterator scan(String prefix, boolean desc, long limit) { + return scan(Utils.toByteArray(prefix), desc, limit); + } + + public Iterator scan(byte[] prefix, boolean desc, long limit) { + return scan(prefix, null, desc, limit); + } + + public Iterator scan(byte[] prefix, byte[] seekKey, boolean desc, long limit) { + return scan(prefix, seekKey, null, desc, limit); + } + + public Iterator scan(byte[] prefix, byte[] seekKey, byte[] endKey, boolean desc, long limit) { + return scan(prefix, seekKey, endKey, false, false, desc, limit); + } + + public synchronized Iterator scan(byte[] prefix, byte[] seekKey, byte[] endKey, boolean inclusiveSeek, + boolean inclusiveEnd, + boolean desc, long limit) { + + final ImmudbProto.ScanRequest req = ScanRequest.newBuilder() + .setPrefix(Utils.toByteString(prefix)) + .setSeekKey(Utils.toByteString(seekKey)) + .setEndKey(Utils.toByteString(endKey)) + .setInclusiveSeek(inclusiveSeek) + .setInclusiveEnd(inclusiveEnd) + .setDesc(desc) + .setLimit(limit) + .build(); + + final Iterator chunks = blockingStub.streamScan(req); + + return entryIterator(chunks); + } + + // + // ========== STREAM ZSCAN ========== + // + + public Iterator zScan(String set) { + return zScan(set, false, 0); + } + + public Iterator zScan(String set, boolean reverse, long limit) { + return pzScan(Utils.toByteArray(set), null, null, null, null, 0, false, reverse, limit); + } + + public Iterator zScan(byte[] set, double minScore, double maxScore, boolean reverse, long limit) { + return pzScan(set, minScore, maxScore, null, null, 0, false, false, 0); + } + + public Iterator zScan(byte[] set, double minScore, double maxScore, double seekScore, byte[] seekKey, + long seekAtTx, boolean inclusiveSeek, boolean reverse, long limit) { + return pzScan(set, minScore, maxScore, seekScore, seekKey, seekAtTx, inclusiveSeek, reverse, limit); + } + + private synchronized Iterator pzScan(byte[] set, Double minScore, Double maxScore, Double seekScore, + byte[] seekKey, + long seekAtTx, boolean inclusiveSeek, boolean reverse, long limit) { + + final ImmudbProto.ZScanRequest.Builder reqBuilder = ImmudbProto.ZScanRequest.newBuilder(); + + reqBuilder.setSet(Utils.toByteString(set)) + .setSeekKey(Utils.toByteString(seekKey)) + .setSeekAtTx(seekAtTx) + .setInclusiveSeek(inclusiveSeek) + .setDesc(reverse) + .setLimit(limit); + + if (seekScore != null) { + reqBuilder.setSeekScore(seekScore); + } + + if (minScore != null) { + reqBuilder.setMinScore(Score.newBuilder().setScore(minScore).build()); + } + + if (maxScore != null) { + reqBuilder.setMaxScore(Score.newBuilder().setScore(maxScore).build()); + } + + final Iterator chunks = blockingStub.streamZScan(reqBuilder.build()); + + return zentryIterator(chunks); + } + + // + // ========== STREAM HISTORY ========== + // + + public Iterator history(String key, long offset, boolean desc, int limit) throws KeyNotFoundException { + return history(Utils.toByteArray(key), offset, desc, limit); + } + + public synchronized Iterator history(byte[] key, long offset, boolean desc, int limit) + throws KeyNotFoundException { + try { + ImmudbProto.HistoryRequest req = ImmudbProto.HistoryRequest.newBuilder() + .setKey(Utils.toByteString(key)) + .setDesc(desc) + .setOffset(offset) + .setLimit(limit) + .build(); + + final Iterator chunks = blockingStub.streamHistory(req); + + return entryIterator(chunks); + } catch (StatusRuntimeException e) { + if (e.getMessage().contains("key not found")) { + throw new KeyNotFoundException(); + } + + throw e; + } + } + + // + // ========== HEALTH ========== + // + public boolean isConnected() { - return channel != null; + return channel != null && channel.getState(false) == ConnectivityState.READY; + } + + public boolean isShutdown() { + return channel != null && channel.isShutdown(); + } + + public synchronized boolean healthCheck() { + return blockingStub.serverInfo(ImmudbProto.ServerInfoRequest.getDefaultInstance()) != null; } // // ========== USER MGMT ========== // - public List listUsers() { - final ImmudbProto.UserList userList = getStub().listUsers(Empty.getDefaultInstance()); + public synchronized List listUsers() { + final ImmudbProto.UserList userList = blockingStub.listUsers(Empty.getDefaultInstance()); return userList.getUsersList() .stream() @@ -1018,8 +1481,7 @@ public List listUsers() { .setCreatedAt(u.getCreatedat()) .setCreatedBy(u.getCreatedby()) .setPermissions(buildPermissions(u.getPermissionsList())) - .build() - ) + .build()) .collect(Collectors.toList()); } @@ -1030,7 +1492,7 @@ private List buildPermissions(List permissio .collect(Collectors.toList()); } - public void createUser(String user, String password, Permission permission, String database) { + public synchronized void createUser(String user, String password, Permission permission, String database) { final ImmudbProto.CreateUserRequest createUserRequest = ImmudbProto.CreateUserRequest.newBuilder() .setUser(Utils.toByteString(user)) .setPassword(Utils.toByteString(password)) @@ -1039,10 +1501,20 @@ public void createUser(String user, String password, Permission permission, Stri .build(); // noinspection ResultOfMethodCallIgnored - getStub().createUser(createUserRequest); + blockingStub.createUser(createUserRequest); } - public void changePassword(String user, String oldPassword, String newPassword) { + public synchronized void activateUser(String user, boolean active) { + final ImmudbProto.SetActiveUserRequest req = ImmudbProto.SetActiveUserRequest.newBuilder() + .setUsername(user) + .setActive(active) + .build(); + + // noinspection ResultOfMethodCallIgnored + blockingStub.setActiveUser(req); + } + + public synchronized void changePassword(String user, String oldPassword, String newPassword) { final ImmudbProto.ChangePasswordRequest changePasswordRequest = ImmudbProto.ChangePasswordRequest.newBuilder() .setUser(Utils.toByteString(user)) .setOldPassword(Utils.toByteString(oldPassword)) @@ -1050,24 +1522,48 @@ public void changePassword(String user, String oldPassword, String newPassword) .build(); // noinspection ResultOfMethodCallIgnored - getStub().changePassword(changePasswordRequest); + blockingStub.changePassword(changePasswordRequest); + } + + public synchronized void grantPermission(String user, String database, Permission permissions) { + final ImmudbProto.ChangePermissionRequest req = ImmudbProto.ChangePermissionRequest.newBuilder() + .setUsername(user) + .setAction(ImmudbProto.PermissionAction.GRANT) + .setDatabase(database) + .setPermission(permissions.permissionCode) + .build(); + + // noinspection ResultOfMethodCallIgnored + blockingStub.changePermission(req); + } + + public synchronized void revokePermission(String user, String database, Permission permissions) { + final ImmudbProto.ChangePermissionRequest req = ImmudbProto.ChangePermissionRequest.newBuilder() + .setUsername(user) + .setAction(ImmudbProto.PermissionAction.REVOKE) + .setDatabase(database) + .setPermission(permissions.permissionCode) + .build(); + + // noinspection ResultOfMethodCallIgnored + blockingStub.changePermission(req); } // // ========== INDEX MGMT ========== // - public void flushIndex(float cleanupPercentage, boolean synced) { + public synchronized void flushIndex(float cleanupPercentage) { ImmudbProto.FlushIndexRequest req = ImmudbProto.FlushIndexRequest.newBuilder() .setCleanupPercentage(cleanupPercentage) - .setSynced(synced) + .setSynced(true) .build(); - getStub().flushIndex(req); + blockingStub.flushIndex(req); } - public void compactIndex() { - getStub().compactIndex(Empty.getDefaultInstance()); + public synchronized void compactIndex() { + blockingStub.compactIndex(Empty.getDefaultInstance()); } // @@ -1112,15 +1608,18 @@ public static class Builder { private PublicKey serverSigningKey; - private boolean withAuth; + private long keepAlivePeriod; + + private int chunkSize; private ImmuStateHolder stateHolder; private Builder() { - this.serverUrl = "localhost"; - this.serverPort = 3322; - this.stateHolder = new SerializableImmuStateHolder(); - this.withAuth = true; + serverUrl = "localhost"; + serverPort = 3322; + stateHolder = new SerializableImmuStateHolder(); + keepAlivePeriod = 60 * 1000; // 1 minute + chunkSize = 64 * 1024; // 64 * 1024 64 KiB } public ImmuClient build() { @@ -1128,7 +1627,7 @@ public ImmuClient build() { } public String getServerUrl() { - return this.serverUrl; + return serverUrl; } public Builder withServerUrl(String serverUrl) { @@ -1154,16 +1653,16 @@ public PublicKey getServerSigningKey() { * be used for verifying the state signature received from the server. */ public Builder withServerSigningKey(String publicKeyFilename) throws Exception { - this.serverSigningKey = CryptoUtils.getDERPublicKey(publicKeyFilename); + serverSigningKey = CryptoUtils.getDERPublicKey(publicKeyFilename); return this; } - public boolean isWithAuth() { - return withAuth; + public long getKeepAlivePeriod() { + return keepAlivePeriod; } - public Builder withAuth(boolean withAuth) { - this.withAuth = withAuth; + public Builder withKeepAlivePeriod(long keepAlivePeriod) { + this.keepAlivePeriod = keepAlivePeriod; return this; } @@ -1176,6 +1675,18 @@ public Builder withStateHolder(ImmuStateHolder stateHolder) { return this; } + public Builder withChunkSize(int chunkSize) { + if (chunkSize < Long.BYTES) { + throw new RuntimeException("invalid chunk size"); + } + + this.chunkSize = chunkSize; + return this; + } + + public int getChunkSize() { + return chunkSize; + } } } diff --git a/src/main/java/io/codenotary/immudb4j/ImmuServerUUIDInterceptor.java b/src/main/java/io/codenotary/immudb4j/ImmuServerUUIDInterceptor.java deleted file mode 100644 index f665143..0000000 --- a/src/main/java/io/codenotary/immudb4j/ImmuServerUUIDInterceptor.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.codenotary.immudb4j; - -import io.grpc.*; -import io.grpc.ForwardingClientCallListener.SimpleForwardingClientCallListener; - - -public class ImmuServerUUIDInterceptor implements ClientInterceptor { - - private static final String SERVER_UUID = "immudb-uuid"; - - private final ImmuClient client; - - public ImmuServerUUIDInterceptor(ImmuClient client) { - this.client = client; - } - - @Override - public ClientCall interceptCall( - MethodDescriptor method, - CallOptions callOptions, Channel next) { - - return new ForwardingClientCall.SimpleForwardingClientCall(next.newCall(method, callOptions)) { - - @Override - public void start(Listener responseListener, Metadata headers) { - - SimpleForwardingClientCallListener listener = new SimpleForwardingClientCallListener(responseListener) { - - @Override - public void onHeaders(Metadata headers) { - String serverUuid = headers.get(Metadata.Key.of(SERVER_UUID, Metadata.ASCII_STRING_MARSHALLER)); - if (serverUuid != null && !serverUuid.equals(client.getCurrentServerUuid())) { - client.setCurrentServerUuid(serverUuid); - } - super.onHeaders(headers); - } - }; - super.start(listener, headers); - } - - }; - - } - -} diff --git a/src/main/java/io/codenotary/immudb4j/ImmuStateHolder.java b/src/main/java/io/codenotary/immudb4j/ImmuStateHolder.java index 7938b11..f2b7821 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuStateHolder.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuStateHolder.java @@ -18,8 +18,8 @@ public interface ImmuStateHolder { - ImmuState getState(String serverUuid, String database); + ImmuState getState(String database); - void setState(String serverUuid, ImmuState state); + void setState(ImmuState state); } diff --git a/src/main/java/io/codenotary/immudb4j/ImmudbAuthRequestInterceptor.java b/src/main/java/io/codenotary/immudb4j/ImmudbAuthRequestInterceptor.java new file mode 100644 index 0000000..8b4b430 --- /dev/null +++ b/src/main/java/io/codenotary/immudb4j/ImmudbAuthRequestInterceptor.java @@ -0,0 +1,37 @@ +package io.codenotary.immudb4j; + +import io.grpc.*; + + +public class ImmudbAuthRequestInterceptor implements ClientInterceptor { + + private static final String SESSION_ID = "sessionid"; + + private final ImmuClient client; + + public ImmudbAuthRequestInterceptor(ImmuClient client) { + this.client = client; + } + + @Override + public ClientCall interceptCall( + MethodDescriptor method, + CallOptions callOptions, Channel next) { + + return new ForwardingClientCall.SimpleForwardingClientCall(next.newCall(method, callOptions)) { + + @Override + public void start(Listener responseListener, Metadata headers) { + final Session session = client.getSession(); + + if (session != null) { + headers.put(Metadata.Key.of(SESSION_ID, Metadata.ASCII_STRING_MARSHALLER), session.getSessionID()); + } + + super.start(responseListener, headers); + } + + }; + } + +} diff --git a/src/main/java/io/codenotary/immudb4j/SerializableImmuStateHolder.java b/src/main/java/io/codenotary/immudb4j/SerializableImmuStateHolder.java index b72a867..1489e6f 100644 --- a/src/main/java/io/codenotary/immudb4j/SerializableImmuStateHolder.java +++ b/src/main/java/io/codenotary/immudb4j/SerializableImmuStateHolder.java @@ -18,7 +18,11 @@ import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; -import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.util.HashMap; @@ -27,7 +31,7 @@ public class SerializableImmuStateHolder implements ImmuStateHolder { /** - * Mapping "{serverUuid}_{databaseName}" to the appropriate state. + * Mapping "{databaseName}" to the appropriate state. */ private Map statesMap = new HashMap<>(); @@ -46,13 +50,13 @@ public void writeTo(OutputStream os) throws IOException { } @Override - public ImmuState getState(String serverUuid, String database) { - return this.statesMap.get(serverUuid + "_" + database); + public ImmuState getState(String database) { + return this.statesMap.get(database); } @Override - public void setState(String serverUuid, ImmuState state) { - this.statesMap.put(serverUuid + "_" + state.getDatabase(), state); + public void setState(ImmuState state) { + this.statesMap.put(state.getDatabase(), state); } } diff --git a/src/test/java/io/codenotary/immudb4j/UseDatabaseTest.java b/src/main/java/io/codenotary/immudb4j/Session.java similarity index 58% rename from src/test/java/io/codenotary/immudb4j/UseDatabaseTest.java rename to src/main/java/io/codenotary/immudb4j/Session.java index 37edd34..5b4d130 100644 --- a/src/test/java/io/codenotary/immudb4j/UseDatabaseTest.java +++ b/src/main/java/io/codenotary/immudb4j/Session.java @@ -15,25 +15,22 @@ */ package io.codenotary.immudb4j; -import org.testng.Assert; -import org.testng.annotations.Test; +public class Session { -import java.util.List; + private String sessionID; + private String database; + public Session(String sessionID, String database) { + this.sessionID = sessionID; + this.database = database; + } -public class UseDatabaseTest extends ImmuClientIntegrationTest { - - @Test(testName = "useDatabase") - public void t1() { - - immuClient.login("immudb", "immudb"); - - immuClient.useDatabase("defaultdb"); - - List databases = immuClient.databases(); - Assert.assertTrue(databases.size() > 0); + public String getSessionID() { + return sessionID; + } - immuClient.logout(); + public String getDatabase() { + return database; } } diff --git a/src/main/java/io/codenotary/immudb4j/TxHeader.java b/src/main/java/io/codenotary/immudb4j/TxHeader.java index f7cecf3..b982a34 100644 --- a/src/main/java/io/codenotary/immudb4j/TxHeader.java +++ b/src/main/java/io/codenotary/immudb4j/TxHeader.java @@ -20,7 +20,6 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.Base64; public class TxHeader { private final int version; diff --git a/src/main/java/io/codenotary/immudb4j/Utils.java b/src/main/java/io/codenotary/immudb4j/Utils.java index 2366b9d..2a74d37 100644 --- a/src/main/java/io/codenotary/immudb4j/Utils.java +++ b/src/main/java/io/codenotary/immudb4j/Utils.java @@ -119,5 +119,4 @@ public static void copy(byte[] src, int srcPos, int length, byte[] dest) { public static void copy(byte[] src, int srcPos, int length, byte[] dest, int destPos) { System.arraycopy(src, srcPos, dest, destPos, length); } - } diff --git a/src/main/java/io/codenotary/immudb4j/ZEntry.java b/src/main/java/io/codenotary/immudb4j/ZEntry.java index c13303e..6012d74 100644 --- a/src/main/java/io/codenotary/immudb4j/ZEntry.java +++ b/src/main/java/io/codenotary/immudb4j/ZEntry.java @@ -39,6 +39,14 @@ public class ZEntry { private ZEntry() {} + public ZEntry(byte[] set, byte[] key, double score, long atTx, Entry entry) { + this.set = set; + this.key = key; + this.score = score; + this.atTx = atTx; + this.entry = entry; + } + public static ZEntry valueOf(ImmudbProto.ZEntry e) { final ZEntry entry = new ZEntry(); @@ -98,5 +106,5 @@ public byte[] digestFor(int version) { return kv.digestFor(version); } - + } diff --git a/src/main/java/io/codenotary/immudb4j/basics/LatchHolder.java b/src/main/java/io/codenotary/immudb4j/basics/LatchHolder.java new file mode 100644 index 0000000..37ab10e --- /dev/null +++ b/src/main/java/io/codenotary/immudb4j/basics/LatchHolder.java @@ -0,0 +1,38 @@ +/* +Copyright 2022 CodeNotary, Inc. All rights reserved. + +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.codenotary.immudb4j.basics; + +import java.util.concurrent.CountDownLatch; + +public class LatchHolder { + + private T value; + private CountDownLatch doneLatch; + + public LatchHolder() { + doneLatch = new CountDownLatch(1); + } + + public T awaitValue() throws InterruptedException { + doneLatch.await(); + return value; + } + + public void doneWith(T value) { + this.value = value; + doneLatch.countDown(); + } +} \ No newline at end of file diff --git a/src/main/proto/schema.proto b/src/main/proto/schema.proto index 92f9fd6..dfa61f9 100644 --- a/src/main/proto/schema.proto +++ b/src/main/proto/schema.proto @@ -18,446 +18,443 @@ import "google/protobuf/struct.proto"; package immudb.schema; -option go_package = "github.com/codenotary/immudb/pkg/api/schema"; option java_package = "io.codenotary.immudb"; option java_outer_classname = "ImmudbProto"; message Key { - bytes key = 1; + bytes key = 1; } message Permission { - string database = 1; - uint32 permission = 2; + string database = 1; + uint32 permission = 2; } message User { - bytes user = 1; - repeated Permission permissions = 3; - string createdby = 4; - string createdat = 5; - bool active = 6; + bytes user = 1; + repeated Permission permissions = 3; + string createdby = 4; + string createdat = 5; + bool active = 6; } message UserList { - repeated User users = 1; + repeated User users = 1; } message CreateUserRequest { - bytes user = 1; - bytes password = 2; - uint32 permission = 3; - string database = 4; + bytes user = 1; + bytes password = 2; + uint32 permission = 3; + string database = 4; } message UserRequest { - bytes user = 1; + bytes user = 1; } message ChangePasswordRequest { - bytes user = 1; - bytes oldPassword = 2; - bytes newPassword = 3; -} - -message LoginRequest { bytes user = 1; - bytes password = 2; -} - -message LoginResponse { - string token = 1; - bytes warning = 2; + bytes oldPassword = 2; + bytes newPassword = 3; } message OpenSessionRequest { - bytes username = 1; - bytes password = 2; - string databaseName = 3; + bytes username = 1; + bytes password = 2; + string databaseName = 3; } message OpenSessionResponse { - string sessionID = 1; - string serverUUID = 2; + string sessionID = 1; + string serverUUID = 2; } //////////////////////////////////////////////////////// message Precondition { - message KeyMustExistPrecondition { - bytes key = 1; - } + message KeyMustExistPrecondition { + bytes key = 1; + } - message KeyMustNotExistPrecondition { - bytes key = 1; - } + message KeyMustNotExistPrecondition { + bytes key = 1; + } - message KeyNotModifiedAfterTXPrecondition { - bytes key = 1; - uint64 txID = 2; - } + message KeyNotModifiedAfterTXPrecondition { + bytes key = 1; + uint64 txID = 2; + } - oneof precondition { - KeyMustExistPrecondition keyMustExist = 1; - KeyMustNotExistPrecondition keyMustNotExist = 2; - KeyNotModifiedAfterTXPrecondition keyNotModifiedAfterTX = 3; - } + oneof precondition { + KeyMustExistPrecondition keyMustExist = 1; + KeyMustNotExistPrecondition keyMustNotExist = 2; + KeyNotModifiedAfterTXPrecondition keyNotModifiedAfterTX = 3; + } } message KeyValue { - bytes key = 1; - bytes value = 2; - KVMetadata metadata = 3; + bytes key = 1; + bytes value = 2; + KVMetadata metadata = 3; } message Entry { - uint64 tx = 1; - bytes key = 2; - bytes value = 3; - Reference referencedBy = 4; - KVMetadata metadata = 5; - bool expired = 6; + uint64 tx = 1; + bytes key = 2; + bytes value = 3; + Reference referencedBy = 4; + KVMetadata metadata = 5; + bool expired = 6; + uint64 revision = 7; } message Reference { - uint64 tx = 1; - bytes key = 2; - uint64 atTx = 3; - KVMetadata metadata = 4; + uint64 tx = 1; + bytes key = 2; + uint64 atTx = 3; + KVMetadata metadata = 4; + uint64 revision = 5; } message Op { - oneof operation { - KeyValue kv = 1; - ZAddRequest zAdd = 2; - ReferenceRequest ref = 3; - } + oneof operation { + KeyValue kv = 1; + ZAddRequest zAdd = 2; + ReferenceRequest ref = 3; + } } message ExecAllRequest { - repeated Op Operations = 1; - bool noWait = 2; - repeated Precondition preconditions = 3; + repeated Op Operations = 1; + bool noWait = 2; + repeated Precondition preconditions = 3; } message Entries { - repeated Entry entries = 1; + repeated Entry entries = 1; } message ZEntry { - bytes set = 1; - bytes key = 2; - Entry entry = 3; - double score = 4; - uint64 atTx = 5; + bytes set = 1; + bytes key = 2; + Entry entry = 3; + double score = 4; + uint64 atTx = 5; } message ZEntries { - repeated ZEntry entries = 1; + repeated ZEntry entries = 1; } message ScanRequest { - bytes seekKey = 1; - bytes endKey = 7; - bytes prefix = 2; - bool desc = 3; - uint64 limit = 4; - uint64 sinceTx = 5; - bool noWait = 6; - bool inclusiveSeek = 8; - bool inclusiveEnd = 9; + bytes seekKey = 1; + bytes endKey = 7; + bytes prefix = 2; + bool desc = 3; + uint64 limit = 4; + uint64 sinceTx = 5; + bool noWait = 6; + bool inclusiveSeek = 8; // If set to true, results will include seekKey + bool inclusiveEnd = 9; // If set to true, results will include endKey if needed + uint64 offset = 10; // Specify the initial entry to be returned by excluding the initial set of entries } message KeyPrefix { - bytes prefix = 1; + bytes prefix = 1; } message EntryCount { - uint64 count = 1; + uint64 count = 1; } /////////////// message Signature { - bytes publicKey = 1; - bytes signature = 2; + bytes publicKey = 1; + bytes signature = 2; } message TxHeader { - uint64 id = 1; - bytes prevAlh = 2; - int64 ts = 3; - int32 nentries = 4; - bytes eH = 5; - uint64 blTxId = 6; - bytes blRoot = 7; - int32 version = 8; - TxMetadata metadata = 9; + uint64 id = 1; + bytes prevAlh = 2; + int64 ts = 3; + int32 nentries = 4; + bytes eH = 5; + uint64 blTxId = 6; + bytes blRoot = 7; + int32 version = 8; + TxMetadata metadata = 9; } message TxMetadata { } message LinearProof { - uint64 sourceTxId = 1; - uint64 TargetTxId = 2; - repeated bytes terms = 3; + uint64 sourceTxId = 1; + uint64 TargetTxId = 2; + repeated bytes terms = 3; } message DualProof { - TxHeader sourceTxHeader = 1; - TxHeader targetTxHeader = 2; + TxHeader sourceTxHeader = 1; + TxHeader targetTxHeader = 2; - repeated bytes inclusionProof = 3; - repeated bytes consistencyProof = 4; + repeated bytes inclusionProof = 3; + repeated bytes consistencyProof = 4; - bytes targetBlTxAlh = 5; - repeated bytes lastInclusionProof = 6; + bytes targetBlTxAlh = 5; + repeated bytes lastInclusionProof = 6; - LinearProof linearProof = 7; + LinearProof linearProof = 7; } message Tx { - TxHeader header = 1; - repeated TxEntry entries = 2; - repeated Entry kvEntries = 3; - repeated ZEntry zEntries = 4; + TxHeader header = 1; + repeated TxEntry entries = 2; + repeated Entry kvEntries = 3; + repeated ZEntry zEntries = 4; } message TxEntry { - bytes key = 1; - bytes hValue = 2; - int32 vLen = 3; - KVMetadata metadata = 4; - bytes value = 5; // value must be ignored when len(value) == 0 and vLen > 0. Otherwise, sha256(value) must be equal to hValue + bytes key = 1; + bytes hValue = 2; + int32 vLen = 3; + KVMetadata metadata = 4; + bytes value = 5; // value must be ignored when len(value) == 0 and vLen > 0. Otherwise, sha256(value) must be equal to hValue } message KVMetadata { - bool deleted = 1; - Expiration expiration = 2; - bool nonIndexable = 3; + bool deleted = 1; + Expiration expiration = 2; + bool nonIndexable = 3; } message Expiration { - int64 expiresAt = 1; + int64 expiresAt = 1; } message VerifiableTx { - Tx tx = 1; - DualProof dualProof = 2; - Signature signature = 3; + Tx tx = 1; + DualProof dualProof = 2; + Signature signature = 3; } ////////////////// message VerifiableEntry { - Entry entry = 1; - VerifiableTx verifiableTx = 2; - InclusionProof inclusionProof = 3; + Entry entry = 1; + VerifiableTx verifiableTx = 2; + InclusionProof inclusionProof = 3; } message InclusionProof { - int32 leaf = 1; - int32 width = 2; - repeated bytes terms = 3; + int32 leaf = 1; + int32 width = 2; + repeated bytes terms = 3; } message SetRequest { - repeated KeyValue KVs = 1; - bool noWait = 2; - repeated Precondition preconditions = 3; + repeated KeyValue KVs = 1; + bool noWait = 2; + repeated Precondition preconditions = 3; } message KeyRequest { - bytes key = 1; - uint64 atTx = 2; - uint64 sinceTx = 3; - bool noWait = 4; - int64 atRevision = 5; + bytes key = 1; + uint64 atTx = 2; // if > 0, query for the value exactly at given transaction + + + // if 0 (and nowait=false), wait for the index to be up=to-date + uint64 sinceTx = 3; + + // if set to true - do not wait for any indexing update considering only the currently indexed state + bool noWait = 4; + + // if > 0, get the nth version of the value, 1 being the first version, 2 being the second and so on + // if < 0, get the historical nth value of the key, -1 being the previous version, -2 being the one before and so on + int64 atRevision = 5; + } message KeyListRequest { - repeated bytes keys = 1; - uint64 sinceTx = 2; + repeated bytes keys = 1; + uint64 sinceTx = 2; } message DeleteKeysRequest { - repeated bytes keys = 1; - uint64 sinceTx = 2; - bool noWait = 3; + repeated bytes keys = 1; + uint64 sinceTx = 2; + bool noWait = 3; } message VerifiableSetRequest { - SetRequest setRequest = 1; - uint64 proveSinceTx = 2; + SetRequest setRequest = 1; + uint64 proveSinceTx = 2; } message VerifiableGetRequest { - KeyRequest keyRequest = 1; - uint64 proveSinceTx = 2; + KeyRequest keyRequest = 1; + uint64 proveSinceTx = 2; +} + +// ServerInfoRequest exists to provide extensibility for rpc ServerInfo. +message ServerInfoRequest {} + +// ServerInfoResponse contains information about the server instance. +message ServerInfoResponse { + // The version of the server instance. + string version = 1; } message HealthResponse { - bool status = 1; - string version = 2; + bool status = 1; + string version = 2; } message DatabaseHealthResponse { - uint32 pendingRequests = 1; - int64 lastRequestCompletedAt = 2; + uint32 pendingRequests = 1; + int64 lastRequestCompletedAt = 2; } message ImmutableState { - string db = 1; - uint64 txId = 2; - bytes txHash = 3; - Signature signature = 4; + string db = 1; + uint64 txId = 2; + bytes txHash = 3; + Signature signature = 4; } message ReferenceRequest { - bytes key = 1; - bytes referencedKey = 2; - uint64 atTx = 3; - bool boundRef = 4; - bool noWait = 5; - repeated Precondition preconditions = 6; + bytes key = 1; + bytes referencedKey = 2; + uint64 atTx = 3; + bool boundRef = 4; + bool noWait = 5; + repeated Precondition preconditions = 6; } message VerifiableReferenceRequest { - ReferenceRequest referenceRequest = 1; - uint64 proveSinceTx = 2; + ReferenceRequest referenceRequest = 1; + uint64 proveSinceTx = 2; } message ZAddRequest { - bytes set = 1; - double score = 2; - bytes key = 3; - uint64 atTx = 4; - bool boundRef = 5; - bool noWait = 6; + bytes set = 1; + double score = 2; + bytes key = 3; + uint64 atTx = 4; + bool boundRef = 5; + bool noWait = 6; } message Score { - double score = 1; + double score = 1; } message ZScanRequest { - bytes set = 1; - bytes seekKey = 2; - double seekScore = 3; - uint64 seekAtTx = 4; - bool inclusiveSeek = 5; - uint64 limit = 6; - bool desc = 7; - Score minScore = 8; - Score maxScore = 9; - uint64 sinceTx = 10; - bool noWait = 11; + bytes set = 1; + bytes seekKey = 2; + double seekScore = 3; + uint64 seekAtTx = 4; + bool inclusiveSeek = 5; + uint64 limit = 6; + bool desc = 7; + Score minScore = 8; + Score maxScore = 9; + uint64 sinceTx = 10; + bool noWait = 11; + uint64 offset = 12; // Specify the initial entry to be returned by excluding the initial set of entries } message HistoryRequest { - bytes key = 1; - uint64 offset = 2; - int32 limit = 3; - bool desc = 4; - uint64 sinceTx = 5; + bytes key = 1; + uint64 offset = 2; // Specify the initial entry to be returned by excluding the initial set of entries + int32 limit = 3; + bool desc = 4; + uint64 sinceTx = 5; } message VerifiableZAddRequest { - ZAddRequest zAddRequest = 1; - uint64 proveSinceTx = 2; + ZAddRequest zAddRequest = 1; + uint64 proveSinceTx = 2; } message TxRequest { - uint64 tx = 1; - EntriesSpec entriesSpec = 2; - uint64 sinceTx = 3; - bool noWait = 4; + uint64 tx = 1; + EntriesSpec entriesSpec = 2; + uint64 sinceTx = 3; + bool noWait = 4; + bool keepReferencesUnresolved = 5; } message EntriesSpec { - EntryTypeSpec kvEntriesSpec = 1; - EntryTypeSpec zEntriesSpec = 2; - EntryTypeSpec sqlEntriesSpec = 3; + EntryTypeSpec kvEntriesSpec = 1; + EntryTypeSpec zEntriesSpec = 2; + EntryTypeSpec sqlEntriesSpec = 3; } message EntryTypeSpec { - EntryTypeAction action = 1; + EntryTypeAction action = 1; } enum EntryTypeAction { - EXCLUDE = 0; - ONLY_DIGEST = 1; - RAW_VALUE = 2; - RESOLVE = 3; + EXCLUDE = 0; + ONLY_DIGEST = 1; + RAW_VALUE = 2; + RESOLVE = 3; } message VerifiableTxRequest { - uint64 tx = 1; - uint64 proveSinceTx = 2; - EntriesSpec entriesSpec = 3; - uint64 sinceTx = 4; - bool noWait = 5; + uint64 tx = 1; + uint64 proveSinceTx = 2; + EntriesSpec entriesSpec = 3; + uint64 sinceTx = 4; + bool noWait = 5; + bool keepReferencesUnresolved = 6; } message TxScanRequest { - uint64 initialTx = 1; - uint32 limit = 2; - bool desc = 3; - EntriesSpec entriesSpec = 4; - uint64 sinceTx = 5; - bool noWait = 6; + uint64 initialTx = 1; + uint32 limit = 2; + bool desc = 3; + EntriesSpec entriesSpec = 4; + uint64 sinceTx = 5; + bool noWait = 6; } message TxList { - repeated Tx txs = 1; + repeated Tx txs = 1; } message ExportTxRequest { - uint64 tx = 1; + uint64 tx = 1; } message Database { - string databaseName = 1; -} - -message DatabaseSettings { - string databaseName = 1; - - bool replica = 2; - string masterDatabase = 3; - string masterAddress = 4; - uint32 masterPort = 5; - string followerUsername = 6; - string followerPassword = 7; - - uint32 fileSize = 8; - uint32 maxKeyLen = 9; - uint32 maxValueLen = 10; - uint32 maxTxEntries = 11; - - bool excludeCommitTime = 12; + string databaseName = 1; } message CreateDatabaseRequest { - string name = 1; - DatabaseNullableSettings settings = 2; + string name = 1; + DatabaseNullableSettings settings = 2; + bool ifNotExists = 3; } message CreateDatabaseResponse { - string name = 1; - DatabaseNullableSettings settings = 2; + string name = 1; + DatabaseNullableSettings settings = 2; + bool alreadyExisted = 3; } message UpdateDatabaseRequest { - string database = 1; - DatabaseNullableSettings settings = 2; + string database = 1; + DatabaseNullableSettings settings = 2; } message UpdateDatabaseResponse { // Reserved to reply with more advanced response later - string database = 1; - DatabaseNullableSettings settings = 2; + string database = 1; + DatabaseNullableSettings settings = 2; } message DatabaseSettingsRequest { @@ -465,279 +462,292 @@ message DatabaseSettingsRequest { } message DatabaseSettingsResponse { - string database = 1; - DatabaseNullableSettings settings = 2; + string database = 1; + DatabaseNullableSettings settings = 2; } message NullableUint32 { - uint32 value = 1; + uint32 value = 1; } message NullableUint64 { - uint64 value = 1; + uint64 value = 1; } message NullableFloat { - float value = 1; + float value = 1; } message NullableBool { - bool value = 1; + bool value = 1; } message NullableString { - string value = 1; + string value = 1; +} + +message NullableMilliseconds { + int64 value = 1; } message DatabaseNullableSettings { - ReplicationNullableSettings replicationSettings = 2; + ReplicationNullableSettings replicationSettings = 2; - NullableUint32 fileSize = 8; - NullableUint32 maxKeyLen = 9; - NullableUint32 maxValueLen = 10; - NullableUint32 maxTxEntries = 11; + NullableUint32 fileSize = 8; + NullableUint32 maxKeyLen = 9; + NullableUint32 maxValueLen = 10; + NullableUint32 maxTxEntries = 11; - NullableBool excludeCommitTime = 12; + NullableBool excludeCommitTime = 12; - NullableUint32 maxConcurrency = 13; - NullableUint32 maxIOConcurrency = 14; + NullableUint32 maxConcurrency = 13; + NullableUint32 maxIOConcurrency = 14; - NullableUint32 txLogCacheSize = 15; - NullableUint32 vLogMaxOpenedFiles = 16; - NullableUint32 txLogMaxOpenedFiles = 17; - NullableUint32 commitLogMaxOpenedFiles = 18; + NullableUint32 txLogCacheSize = 15; + NullableUint32 vLogMaxOpenedFiles = 16; + NullableUint32 txLogMaxOpenedFiles = 17; + NullableUint32 commitLogMaxOpenedFiles = 18; - IndexNullableSettings indexSettings = 19; + IndexNullableSettings indexSettings = 19; - NullableUint32 writeTxHeaderVersion = 20; + NullableUint32 writeTxHeaderVersion = 20; - NullableBool autoload = 21; + NullableBool autoload = 21; + + NullableUint32 readTxPoolSize = 22; + + NullableMilliseconds syncFrequency = 23; + + NullableUint32 writeBufferSize = 24; + + AHTNullableSettings ahtSettings = 25; } message ReplicationNullableSettings { - NullableBool replica = 1; - NullableString masterDatabase = 2; - NullableString masterAddress = 3; - NullableUint32 masterPort = 4; - NullableString followerUsername = 5; - NullableString followerPassword = 6; + NullableBool replica = 1; + NullableString masterDatabase = 2; + NullableString masterAddress = 3; + NullableUint32 masterPort = 4; + NullableString followerUsername = 5; + NullableString followerPassword = 6; } message IndexNullableSettings { - NullableUint32 flushThreshold = 1; - NullableUint32 syncThreshold = 2; - NullableUint32 cacheSize = 3; - NullableUint32 maxNodeSize = 4; - NullableUint32 maxActiveSnapshots = 5; - NullableUint64 renewSnapRootAfter = 6; - NullableUint32 compactionThld = 7; - NullableUint32 delayDuringCompaction = 8; - NullableUint32 nodesLogMaxOpenedFiles = 9; - NullableUint32 historyLogMaxOpenedFiles = 10; - NullableUint32 commitLogMaxOpenedFiles = 11; - NullableUint32 flushBufferSize = 12; - NullableFloat cleanupPercentage = 13; + NullableUint32 flushThreshold = 1; + NullableUint32 syncThreshold = 2; + NullableUint32 cacheSize = 3; + NullableUint32 maxNodeSize = 4; + NullableUint32 maxActiveSnapshots = 5; + NullableUint64 renewSnapRootAfter = 6; + NullableUint32 compactionThld = 7; + NullableUint32 delayDuringCompaction = 8; + NullableUint32 nodesLogMaxOpenedFiles = 9; + NullableUint32 historyLogMaxOpenedFiles = 10; + NullableUint32 commitLogMaxOpenedFiles = 11; + NullableUint32 flushBufferSize = 12; + NullableFloat cleanupPercentage = 13; +} + +message AHTNullableSettings { + NullableUint32 syncThreshold = 1; + NullableUint32 writeBufferSize = 2; } message LoadDatabaseRequest { - string database = 1; - // may add createIfNotExist + string database = 1; + // may add createIfNotExist } message LoadDatabaseResponse { - string database = 1; - // may add setttings + string database = 1; + // may add setttings } message UnloadDatabaseRequest { - string database = 1; + string database = 1; } message UnloadDatabaseResponse { - string database = 1; + string database = 1; } message DeleteDatabaseRequest { - string database = 1; + string database = 1; } message DeleteDatabaseResponse { - string database = 1; + string database = 1; } message FlushIndexRequest { - float cleanupPercentage = 1; - bool synced = 2; + float cleanupPercentage = 1; + bool synced = 2; } message FlushIndexResponse { - string database = 1; + string database = 1; } message Table { - string tableName = 1; + string tableName = 1; } message SQLGetRequest { - string table = 1; - repeated SQLValue pkValues = 2; - uint64 atTx = 3; - uint64 sinceTx = 4; + string table = 1; + repeated SQLValue pkValues = 2; + uint64 atTx = 3; + uint64 sinceTx = 4; } message VerifiableSQLGetRequest { - SQLGetRequest sqlGetRequest = 1; - uint64 proveSinceTx = 2; + SQLGetRequest sqlGetRequest = 1; + uint64 proveSinceTx = 2; } message SQLEntry { - uint64 tx = 1; - bytes key = 2; - bytes value = 3; - KVMetadata metadata = 4; + uint64 tx = 1; + bytes key = 2; + bytes value = 3; + KVMetadata metadata = 4; } message VerifiableSQLEntry { - reserved 6; - SQLEntry sqlEntry = 1; - VerifiableTx verifiableTx = 2; - InclusionProof inclusionProof = 3; - uint32 DatabaseId = 4; - uint32 TableId = 5; - repeated uint32 PKIDs = 16; - map ColNamesById = 8; - map ColIdsByName = 9; - map ColTypesById = 10; - map ColLenById = 11; + reserved 6; + SQLEntry sqlEntry = 1; + VerifiableTx verifiableTx = 2; + InclusionProof inclusionProof = 3; + uint32 DatabaseId = 4; + uint32 TableId = 5; + repeated uint32 PKIDs = 16; + map ColNamesById = 8; + map ColIdsByName = 9; + map ColTypesById = 10; + map ColLenById = 11; } message UseDatabaseReply{ - string token = 1; + string token = 1; } enum PermissionAction { - GRANT = 0; - REVOKE = 1; + GRANT = 0; + REVOKE = 1; } message ChangePermissionRequest { - PermissionAction action = 1; - string username = 2; - string database = 3; - uint32 permission = 4; + PermissionAction action = 1; + string username = 2; + string database = 3; + uint32 permission = 4; } message SetActiveUserRequest { - bool active = 1; - string username = 2; -} - -message DatabaseListResponse { - repeated Database databases = 1; + bool active = 1; + string username = 2; } message DatabaseListRequestV2 { } message DatabaseListResponseV2 { - repeated DatabaseWithSettings databases = 1; + repeated DatabaseWithSettings databases = 1; } message DatabaseWithSettings { - string name = 1; - DatabaseNullableSettings settings = 2; - bool loaded = 3; + string name = 1; + DatabaseNullableSettings settings = 2; + bool loaded = 3; } message Chunk { - bytes content = 1; + bytes content = 1; } message UseSnapshotRequest { - uint64 sinceTx = 1; - uint64 asBeforeTx = 2; + uint64 sinceTx = 1; + uint64 asBeforeTx = 2; } message SQLExecRequest { - string sql = 1; - repeated NamedParam params = 2; - bool noWait = 3; + string sql = 1; + repeated NamedParam params = 2; + bool noWait = 3; } message SQLQueryRequest { - string sql = 1; - repeated NamedParam params = 2; - bool reuseSnapshot = 3; + string sql = 1; + repeated NamedParam params = 2; + bool reuseSnapshot = 3; } message NamedParam { - string name = 1; - SQLValue value = 2; + string name = 1; + SQLValue value = 2; } message SQLExecResult { - repeated CommittedSQLTx txs = 5; - bool ongoingTx = 6; + repeated CommittedSQLTx txs = 5; + bool ongoingTx = 6; } message CommittedSQLTx { - TxHeader header = 1; - uint32 updatedRows = 2; - map lastInsertedPKs = 3; - map firstInsertedPKs = 4; + TxHeader header = 1; + uint32 updatedRows = 2; + map lastInsertedPKs = 3; + map firstInsertedPKs = 4; } message SQLQueryResult { - repeated Column columns = 2; - repeated Row rows = 1; + repeated Column columns = 2; + repeated Row rows = 1; } message Column { - string name = 1; - string type = 2; + string name = 1; + string type = 2; } message Row { - repeated string columns = 1; - repeated SQLValue values = 2; + repeated string columns = 1; + repeated SQLValue values = 2; } message SQLValue { - oneof value { - google.protobuf.NullValue null = 1; - int64 n = 2; - string s = 3; - bool b = 4; - bytes bs = 5; - int64 ts = 6; - } + oneof value { + google.protobuf.NullValue null = 1; + int64 n = 2; + string s = 3; + bool b = 4; + bytes bs = 5; + int64 ts = 6; + } } enum TxMode { - ReadOnly = 0; - WriteOnly = 1; - ReadWrite = 2; + ReadOnly = 0; + WriteOnly = 1; + ReadWrite = 2; } message NewTxRequest { - TxMode mode = 1; + TxMode mode = 1; } message NewTxResponse { - string transactionID = 1; + string transactionID = 1; } message ErrorInfo { - string code = 1; - string cause = 2; + string code = 1; + string cause = 2; } message DebugInfo { - string stack = 1; + string stack = 1; } message RetryInfo { - int32 retry_delay = 1; + int32 retry_delay = 1; } // immudb gRPC & REST service @@ -768,14 +778,6 @@ service ImmuService { rpc TxSQLExec(SQLExecRequest) returns (google.protobuf.Empty) {}; rpc TxSQLQuery(SQLQueryRequest) returns (SQLQueryResult) {}; - rpc Login (LoginRequest) returns (LoginResponse){ - option deprecated = true; - }; - - rpc Logout (google.protobuf.Empty) returns (google.protobuf.Empty){ - option deprecated = true; - }; - rpc Set (SetRequest) returns (TxHeader){ }; @@ -800,14 +802,6 @@ service ImmuService { rpc Scan(ScanRequest) returns (Entries){ }; - // NOT YET SUPPORTED - rpc Count(KeyPrefix) returns (EntryCount){ - }; - - // NOT YET SUPPORTED - rpc CountAll(google.protobuf.Empty) returns (EntryCount){ - }; - rpc TxById(TxRequest) returns (Tx){ }; @@ -820,11 +814,13 @@ service ImmuService { rpc History(HistoryRequest) returns (Entries){ }; - rpc Health (google.protobuf.Empty) returns (HealthResponse){ - }; + // ServerInfo returns information about the server instance. + // ServerInfoRequest is defined for future extensions. + rpc ServerInfo (ServerInfoRequest) returns (ServerInfoResponse){ + }; rpc DatabaseHealth (google.protobuf.Empty) returns (DatabaseHealthResponse){ - }; + }; rpc CurrentState (google.protobuf.Empty) returns (ImmutableState){ }; diff --git a/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java b/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java index e3f214d..2b45252 100644 --- a/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java +++ b/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java @@ -17,23 +17,24 @@ import com.google.common.base.Charsets; import io.codenotary.immudb4j.exceptions.CorruptedDataException; +import io.codenotary.immudb4j.exceptions.KeyNotFoundException; import io.codenotary.immudb4j.exceptions.VerificationException; +import io.grpc.StatusRuntimeException; + import org.testng.Assert; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; - public class BasicImmuClientTest extends ImmuClientIntegrationTest { @Test(testName = "set, get") - public void t1() throws VerificationException, CorruptedDataException { - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + public void t1() throws VerificationException, CorruptedDataException, InterruptedException { + immuClient.openSession("defaultdb", "immudb", "immudb"); - byte[] v0 = new byte[]{0, 1, 2, 3}; - byte[] v1 = new byte[]{3, 2, 1, 0}; + byte[] v0 = new byte[] { 0, 1, 2, 3 }; + byte[] v1 = new byte[] { 3, 2, 1, 0 }; TxHeader hdr0 = immuClient.set("k0", v0); Assert.assertNotNull(hdr0); @@ -53,7 +54,7 @@ public void t1() throws VerificationException, CorruptedDataException { Entry ventry1 = immuClient.verifiedGet("k1"); Assert.assertEquals(ventry1.getValue(), v1); - byte[] v2 = new byte[]{0, 1, 2, 3}; + byte[] v2 = new byte[] { 0, 1, 2, 3 }; TxHeader hdr2 = immuClient.verifiedSet("k2", v2); Assert.assertNotNull(hdr2); @@ -65,21 +66,63 @@ public void t1() throws VerificationException, CorruptedDataException { Assert.assertNotNull(e); Assert.assertEquals(e.getValue(), v2); - immuClient.logout(); + Assert.assertEquals(v2, immuClient.getSinceTx("k2", ventry2.getRevision()).getValue()); + + immuClient.set("k0", v1); + + Assert.assertEquals(v0, immuClient.getAtRevision("k0", entry0.getRevision()).getValue()); + Assert.assertEquals(v1, immuClient.getAtRevision("k0", entry0.getRevision()+1).getValue()); + + Assert.assertEquals(v0, immuClient.verifiedGetAtRevision("k0", entry0.getRevision()).getValue()); + Assert.assertEquals(v1, immuClient.verifiedGetAtRevision("k0", entry0.getRevision()+1).getValue()); + + try { + immuClient.verifiedGet("non-existent-key"); + Assert.fail("Failed at verifiedGet."); + } catch (KeyNotFoundException _) { + } + + try { + immuClient.getSinceTx("non-existent-key", 1); + Assert.fail("Failed at getSinceTx."); + } catch (KeyNotFoundException _) { + } + + try { + immuClient.verifiedGetSinceTx("non-existent-key", 1); + Assert.fail("Failed at verifiedGetSinceTx."); + } catch (KeyNotFoundException _) { + } + + try { + immuClient.getAtRevision("k0", entry0.getRevision()+2); + Assert.fail("Failed at getSinceTx."); + } catch (StatusRuntimeException e1) { + Assert.assertTrue(e1.getMessage().contains("invalid key revision number")); + } + + try { + immuClient.verifiedGetAtRevision("k0", entry0.getRevision()+2); + Assert.fail("Failed at verifiedGetAtRevision."); + } catch (StatusRuntimeException e1) { + Assert.assertTrue(e1.getMessage().contains("invalid key revision number")); + } + + + immuClient.closeSession(); } @Test(testName = "setAll, getAll") public void t2() { - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); List keys = new ArrayList<>(); keys.add("k0"); keys.add("k1"); List values = new ArrayList<>(); - values.add(new byte[]{0, 1, 0, 1}); - values.add(new byte[]{1, 0, 1, 0}); + values.add(new byte[] { 0, 1, 0, 1 }); + values.add(new byte[] { 1, 0, 1, 0 }); KVListBuilder kvListBuilder = KVListBuilder.newBuilder(); @@ -109,7 +152,6 @@ public void t2() { Assert.assertEquals(entry.getValue(), values.get(i)); } - immuClient.logout(); + immuClient.closeSession(); } - } diff --git a/src/test/java/io/codenotary/immudb4j/BasicsTest.java b/src/test/java/io/codenotary/immudb4j/BasicsTest.java index e762002..d5ed5b9 100644 --- a/src/test/java/io/codenotary/immudb4j/BasicsTest.java +++ b/src/test/java/io/codenotary/immudb4j/BasicsTest.java @@ -15,6 +15,8 @@ */ package io.codenotary.immudb4j; +import io.codenotary.immudb4j.basics.LatchHolder; + // Note: This test is more for the sake of code coverage, as you may see. import io.codenotary.immudb4j.basics.Pair; @@ -58,4 +60,22 @@ public void t1() { Assert.assertNotEquals(triple, Triple.of("aDifferent", "", "")); } + @Test(testName = "LatchHolder test") + public void t2() throws InterruptedException { + final LatchHolder latchHolder = new LatchHolder<>(); + + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + + latchHolder.doneWith(true); + } + }).run(); + + Assert.assertTrue(latchHolder.awaitValue()); + } } diff --git a/src/test/java/io/codenotary/immudb4j/HTreeTest.java b/src/test/java/io/codenotary/immudb4j/HTreeTest.java index f68cf4c..9c74e5c 100644 --- a/src/test/java/io/codenotary/immudb4j/HTreeTest.java +++ b/src/test/java/io/codenotary/immudb4j/HTreeTest.java @@ -17,7 +17,6 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.security.NoSuchAlgorithmException; import org.testng.Assert; import org.testng.annotations.Test; @@ -29,13 +28,11 @@ public class HTreeTest { @Test(testName = "Empty HTree", expectedExceptions = IllegalArgumentException.class) public void t1() { - new HTree(0); } @Test(testName = "HTree init & root", expectedExceptions = IllegalStateException.class) public void t2() { - final int maxWidth = 1000; HTree tree = new HTree(maxWidth); @@ -46,7 +43,6 @@ public void t2() { @Test(testName = "HTree buildWith, root, inclusionProof, verifyInclusion") public void t3() { - final int maxWidth = 1000; HTree tree = new HTree(maxWidth); diff --git a/src/test/java/io/codenotary/immudb4j/HealthCheckAndIndexCompactionTest.java b/src/test/java/io/codenotary/immudb4j/HealthCheckAndIndexCompactionTest.java new file mode 100644 index 0000000..9b57b5c --- /dev/null +++ b/src/test/java/io/codenotary/immudb4j/HealthCheckAndIndexCompactionTest.java @@ -0,0 +1,67 @@ +/* +Copyright 2022 CodeNotary, Inc. All rights reserved. + +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.codenotary.immudb4j; + +import io.grpc.StatusRuntimeException; + +import java.rmi.UnexpectedException; + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class HealthCheckAndIndexCompactionTest extends ImmuClientIntegrationTest { + + @Test(testName = "openSession (with default credentials), healthCheck, logout") + public void t1() throws UnexpectedException { + try { + immuClient.openSession("defaultdb", "immudb", "immudb"); + } catch (Exception e) { + throw new UnexpectedException(e.getMessage()); + } + + Assert.assertTrue(immuClient.healthCheck()); + + immuClient.flushIndex(10.0f); + + immuClient.closeSession(); + } + + @Test(testName = "openSession (with wrong credentials)", expectedExceptions = StatusRuntimeException.class) + public void t2() { + immuClient.openSession("defaultdb", "immudb", "incorrect_password"); + } + + @Test(testName = "openSession with session already open", expectedExceptions = IllegalStateException.class) + public void t3() throws UnexpectedException { + try { + immuClient.openSession("defaultdb", "immudb", "immudb"); + } catch (Exception e) { + throw new UnexpectedException(e.getMessage()); + } + + try { + immuClient.openSession("defaultdb", "immudb", "immudb"); + } finally { + immuClient.closeSession(); + } + } + + @Test(testName = "openSession with no open session", expectedExceptions = IllegalStateException.class) + public void t4() { + immuClient.closeSession(); + } + +} diff --git a/src/test/java/io/codenotary/immudb4j/HistoryTest.java b/src/test/java/io/codenotary/immudb4j/HistoryTest.java index 4618ca7..b210cf4 100644 --- a/src/test/java/io/codenotary/immudb4j/HistoryTest.java +++ b/src/test/java/io/codenotary/immudb4j/HistoryTest.java @@ -17,20 +17,19 @@ import io.codenotary.immudb4j.exceptions.CorruptedDataException; import io.codenotary.immudb4j.exceptions.KeyNotFoundException; -import io.grpc.StatusRuntimeException; import org.testng.Assert; import org.testng.annotations.Test; import java.nio.charset.StandardCharsets; +import java.util.Iterator; import java.util.List; +import java.util.NoSuchElementException; public class HistoryTest extends ImmuClientIntegrationTest { @Test(testName = "set, history", priority = 2) public void t1() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); byte[] value1 = {0, 1, 2, 3}; byte[] value2 = {4, 5, 6, 7}; @@ -46,7 +45,7 @@ public void t1() { Assert.fail("Failed at set.", e); } - List historyResponse1 = immuClient.history("history1", 10, 0, false); + List historyResponse1 = immuClient.historyAll("history1", 0, false, 2); Assert.assertEquals(historyResponse1.size(), 2); @@ -56,7 +55,7 @@ public void t1() { Assert.assertEquals(historyResponse1.get(1).getKey(), "history1".getBytes(StandardCharsets.UTF_8)); Assert.assertEquals(historyResponse1.get(1).getValue(), value2); - List historyResponse2 = immuClient.history("history2", 10, 0, false); + List historyResponse2 = immuClient.historyAll("history2", 0, false, 3); Assert.assertEquals(historyResponse2.size(), 3); @@ -69,18 +68,37 @@ public void t1() { Assert.assertEquals(historyResponse2.get(2).getKey(), "history2".getBytes(StandardCharsets.UTF_8)); Assert.assertEquals(historyResponse2.get(2).getValue(), value3); - historyResponse2 = immuClient.history("history2", 10, 2, false); + historyResponse2 = immuClient.historyAll("history2", 2, false, 1); Assert.assertNotNull(historyResponse2); Assert.assertEquals(historyResponse2.size(), 1); + Iterator entriesIt = immuClient.history("history2", 2, false, 1); + Assert.assertTrue(entriesIt.hasNext()); + + Entry entry = entriesIt.next(); + Assert.assertNotNull(entry); + + Assert.assertFalse(entriesIt.hasNext()); + try { - immuClient.history("nonExisting", 10, 0, false); + entriesIt.next(); + Assert.fail("NoSuchElementException exception expected"); + } catch (NoSuchElementException e) { + // exception is expected here + } + + try { + immuClient.historyAll("nonExisting", 0, false, 0); Assert.fail("key not found exception expected"); } catch (KeyNotFoundException e) { // exception is expected here } - - immuClient.logout(); + + Iterator entriesIt2 = immuClient.history("nonExisting", 0, false, 0); + + Assert.assertFalse(entriesIt2.hasNext()); + + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/ImmuClientIntegrationTest.java b/src/test/java/io/codenotary/immudb4j/ImmuClientIntegrationTest.java index 19e42bf..b7b0873 100644 --- a/src/test/java/io/codenotary/immudb4j/ImmuClientIntegrationTest.java +++ b/src/test/java/io/codenotary/immudb4j/ImmuClientIntegrationTest.java @@ -18,16 +18,22 @@ import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; +import java.io.File; import java.io.IOException; +import java.nio.file.Files; public abstract class ImmuClientIntegrationTest { protected static ImmuClient immuClient; + protected static File statesDir; @BeforeClass public static void beforeClass() throws IOException { + statesDir = Files.createTempDirectory("immudb_states").toFile(); + statesDir.deleteOnExit(); + FileImmuStateHolder stateHolder = FileImmuStateHolder.newBuilder() - .withStatesFolder("immudb/states") + .withStatesFolder(statesDir.getAbsolutePath()) .build(); immuClient = ImmuClient.newBuilder() @@ -38,7 +44,7 @@ public static void beforeClass() throws IOException { } @AfterClass - public static void afterClass() { + public static void afterClass() throws InterruptedException { immuClient.shutdown(); } diff --git a/src/test/java/io/codenotary/immudb4j/ListDatabasesTest.java b/src/test/java/io/codenotary/immudb4j/ListDatabasesTest.java index de15f97..8a6bf72 100644 --- a/src/test/java/io/codenotary/immudb4j/ListDatabasesTest.java +++ b/src/test/java/io/codenotary/immudb4j/ListDatabasesTest.java @@ -21,10 +21,14 @@ public class ListDatabasesTest extends ImmuClientIntegrationTest { - @Test(testName = "databases") - public void t1() { + @Test(testName = "databases without open session", expectedExceptions = IllegalStateException.class) + public void t1() { + immuClient.databases(); + } - immuClient.login("immudb", "immudb"); + @Test(testName = "databases") + public void t2() { + immuClient.openSession("defaultdb", "immudb", "immudb"); List databases = immuClient.databases(); if (databases.size() > 0) { @@ -36,7 +40,7 @@ public void t1() { System.out.print(">>> There are no databases."); } - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/ListUsersTest.java b/src/test/java/io/codenotary/immudb4j/ListUsersTest.java index 5ee4e82..afe30c0 100644 --- a/src/test/java/io/codenotary/immudb4j/ListUsersTest.java +++ b/src/test/java/io/codenotary/immudb4j/ListUsersTest.java @@ -21,12 +21,11 @@ public class ListUsersTest extends ImmuClientIntegrationTest { @Test(testName = "listUsers") public void t1() { - - immuClient.login("immudb", "immudb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); immuClient.listUsers().forEach(user -> System.out.printf(">>> Got user %s", user)); - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/LoginAndHealthCheckAndCleanIndexTest.java b/src/test/java/io/codenotary/immudb4j/LoginAndHealthCheckAndCleanIndexTest.java deleted file mode 100644 index fa049c9..0000000 --- a/src/test/java/io/codenotary/immudb4j/LoginAndHealthCheckAndCleanIndexTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright 2022 CodeNotary, Inc. All rights reserved. - -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.codenotary.immudb4j; - -import io.grpc.StatusRuntimeException; -import org.testng.Assert; -import org.testng.annotations.Test; - -public class LoginAndHealthCheckAndCleanIndexTest extends ImmuClientIntegrationTest { - - @Test(testName = "login (with default credentials), healthCheck, logout") - public void t1() { - - immuClient.login("immudb", "immudb"); - - boolean isHealthy = immuClient.healthCheck(); - Assert.assertTrue(isHealthy); - - immuClient.compactIndex(); - - immuClient.logout(); - } - - @Test(testName = "login (with wrong credentials)", expectedExceptions = StatusRuntimeException.class) - public void t2() { - - immuClient.login("immudb", "incorrect_password"); - } - -} diff --git a/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java b/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java index 4d0de41..8b9f1ef 100644 --- a/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java +++ b/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java @@ -17,6 +17,8 @@ import io.codenotary.immudb4j.exceptions.CorruptedDataException; import io.codenotary.immudb4j.exceptions.VerificationException; +import io.grpc.StatusRuntimeException; + import org.testng.Assert; import org.testng.annotations.Test; @@ -24,32 +26,58 @@ public class MultidatabaseTest extends ImmuClientIntegrationTest { + @Test(testName = "createDatabase without open session", expectedExceptions = IllegalStateException.class) + public void t1() { + immuClient.createDatabase("db1"); + } + + @Test(testName = "loadDatabase without open session", expectedExceptions = IllegalStateException.class) + public void t2() { + immuClient.loadDatabase("db1"); + } + + @Test(testName = "unloadDatabase without open session", expectedExceptions = IllegalStateException.class) + public void t3() { + immuClient.unloadDatabase("db1"); + } + + @Test(testName = "deleteDatabase without open session", expectedExceptions = IllegalStateException.class) + public void t4() { + immuClient.deleteDatabase("db1"); + } + @Test(testName = "Interacting with multiple databases (creating them, setting, and getting, listing)") - public void t1() throws VerificationException { + public void t5() throws VerificationException { + immuClient.openSession("defaultdb", "immudb", "immudb"); - immuClient.login("immudb", "immudb"); + immuClient.createDatabase("db1", true); + immuClient.createDatabase("db2", true); - immuClient.createDatabase("db1"); - immuClient.createDatabase("db2"); + immuClient.closeSession(); - immuClient.useDatabase("db1"); - byte[] v0 = new byte[]{0, 1, 2, 3}; + immuClient.openSession("db1", "immudb", "immudb"); + + byte[] v0 = new byte[] { 0, 1, 2, 3 }; try { immuClient.set("k0", v0); } catch (CorruptedDataException e) { Assert.fail("Failed at set.", e); } - immuClient.useDatabase("db2"); + immuClient.closeSession(); + + immuClient.openSession("db2", "immudb", "immudb"); - byte[] v1 = new byte[]{3, 2, 1, 0}; + byte[] v1 = new byte[] { 3, 2, 1, 0 }; try { immuClient.set("k1", v1); } catch (CorruptedDataException e) { Assert.fail("Failed at set.", e); } - immuClient.useDatabase("db1"); + immuClient.closeSession(); + + immuClient.openSession("db1", "immudb", "immudb"); Entry entry1 = immuClient.get("k0"); Assert.assertNotNull(entry1); @@ -59,7 +87,9 @@ public void t1() throws VerificationException { Assert.assertNotNull(ventry1); Assert.assertEquals(ventry1.getValue(), v0); - immuClient.useDatabase("db2"); + immuClient.closeSession(); + + immuClient.openSession("db2", "immudb", "immudb"); Entry entry2 = immuClient.get("k1"); Assert.assertEquals(entry2.getValue(), v1); @@ -74,7 +104,28 @@ public void t1() throws VerificationException { Assert.assertTrue(dbs.contains("db1")); Assert.assertTrue(dbs.contains("db2")); - immuClient.logout(); + immuClient.closeSession(); } + @Test(testName = "create, unload and delete database") + public void t6() { + immuClient.openSession("defaultdb", "immudb", "immudb"); + + immuClient.createDatabase("manageddb"); + + immuClient.unloadDatabase("manageddb"); + + immuClient.deleteDatabase("manageddb"); + + /* + try { + immuClient.loadDatabase("manageddb"); + Assert.fail("exception expected"); + } catch (StatusRuntimeException e) { + Assert.assertTrue(e.getMessage().contains("database does not exist")); + } + */ + + immuClient.closeSession(); + } } diff --git a/src/test/java/io/codenotary/immudb4j/MultithreadTest.java b/src/test/java/io/codenotary/immudb4j/MultithreadTest.java index 07d3f07..412a1dc 100644 --- a/src/test/java/io/codenotary/immudb4j/MultithreadTest.java +++ b/src/test/java/io/codenotary/immudb4j/MultithreadTest.java @@ -29,12 +29,10 @@ public class MultithreadTest extends ImmuClientIntegrationTest { @Test(testName = "Multithread without key overlap") public void t1() throws InterruptedException, VerificationException { + immuClient.openSession("defaultdb", "immudb", "immudb"); - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); - - final int threadCount = 10; - final int keyCount = 100; + final int threadCount = 5; + final int keyCount = 10; CountDownLatch latch = new CountDownLatch(threadCount); AtomicInteger succeeded = new AtomicInteger(0); @@ -71,16 +69,16 @@ public void t1() throws InterruptedException, VerificationException { immuClient.verifiedGet("t" + i + "k" + i); } } + + immuClient.closeSession(); } @Test(testName = "Multithread with key overlap") public void t2() throws InterruptedException, VerificationException { + immuClient.openSession("defaultdb", "immudb", "immudb"); - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); - - final int threadCount = 10; - final int keyCount = 100; + final int threadCount = 5; + final int keyCount = 10; CountDownLatch latch = new CountDownLatch(threadCount); AtomicInteger succeeded = new AtomicInteger(0); @@ -118,6 +116,8 @@ public void t2() throws InterruptedException, VerificationException { immuClient.verifiedGet("k" + i); } } + + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/ReferenceTest.java b/src/test/java/io/codenotary/immudb4j/ReferenceTest.java index 99c4e84..cd9b0b7 100644 --- a/src/test/java/io/codenotary/immudb4j/ReferenceTest.java +++ b/src/test/java/io/codenotary/immudb4j/ReferenceTest.java @@ -25,8 +25,7 @@ public class ReferenceTest extends ImmuClientIntegrationTest { @Test(testName = "set, setReference, setReferenceAt") public void t1() { - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); byte[] key = "testRef".getBytes(StandardCharsets.UTF_8); byte[] val = "abc".getBytes(StandardCharsets.UTF_8); @@ -57,7 +56,7 @@ public void t1() { } Assert.assertNotNull(ref2TxHdr); - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/ScanTest.java b/src/test/java/io/codenotary/immudb4j/ScanTest.java index eda915d..3fe658a 100644 --- a/src/test/java/io/codenotary/immudb4j/ScanTest.java +++ b/src/test/java/io/codenotary/immudb4j/ScanTest.java @@ -20,18 +20,17 @@ import org.testng.annotations.Test; import java.nio.charset.StandardCharsets; +import java.util.Iterator; import java.util.List; public class ScanTest extends ImmuClientIntegrationTest { - @Test(testName = "scan", priority = 2) + @Test(testName = "scan zscan") public void t1() { + immuClient.openSession("defaultdb", "immudb", "immudb"); - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); - - byte[] value1 = {0, 1, 2, 3}; - byte[] value2 = {4, 5, 6, 7}; + byte[] value1 = { 0, 1, 2, 3 }; + byte[] value2 = { 4, 5, 6, 7 }; try { immuClient.set("scan1", value1); @@ -40,31 +39,6 @@ public void t1() { Assert.fail("Failed at set.", e); } - List scanResult = immuClient.scan("scan", 5, false); - System.out.println(scanResult.size()); - - Assert.assertEquals(scanResult.size(), 2); - Assert.assertEquals(scanResult.get(0).getKey(), "scan1".getBytes(StandardCharsets.UTF_8)); - Assert.assertEquals(scanResult.get(0).getValue(), value1); - Assert.assertEquals(scanResult.get(1).getKey(), "scan2".getBytes(StandardCharsets.UTF_8)); - Assert.assertEquals(scanResult.get(1).getValue(), value2); - - Assert.assertTrue(immuClient.scan("scan").size() > 0); - - Assert.assertEquals(immuClient.scan("scan", "scan1", 1, false).size(), 1); - - immuClient.logout(); - } - - @Test(testName = "set, zAdd, zScan", priority = 3) - public void t2() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); - - byte[] value1 = {0, 1, 2, 3}; - byte[] value2 = {4, 5, 6, 7}; - try { immuClient.set("zadd1", value1); immuClient.set("zadd2", value2); @@ -82,16 +56,57 @@ public void t2() { Assert.fail("Failed to zAdd", e); } - List zScan1 = immuClient.zScan("set1", 5, false); + List scanResult = immuClient.scanAll("scan"); + System.out.println(scanResult.size()); + + Assert.assertEquals(scanResult.size(), 2); + Assert.assertEquals(scanResult.get(0).getKey(), "scan1".getBytes(StandardCharsets.UTF_8)); + Assert.assertEquals(scanResult.get(0).getValue(), value1); + Assert.assertEquals(scanResult.get(1).getKey(), "scan2".getBytes(StandardCharsets.UTF_8)); + Assert.assertEquals(scanResult.get(1).getValue(), value2); + + Assert.assertTrue(immuClient.scanAll("scan").size() > 0); + + Assert.assertEquals(immuClient.scanAll("scan".getBytes(), "scan1".getBytes(), false, 1).size(), 1); + + Iterator scanResult1 = immuClient.scan("scan", false, 5); + + int i = 0; + + while (scanResult1.hasNext()) { + Assert.assertEquals(scanResult1.next().getKey(), scanResult.get(i).getKey()); + i++; + } + + Assert.assertEquals(i, 2); + + Assert.assertFalse(immuClient.scan("nonexistent-prefix").hasNext()); + + List zScan1 = immuClient.zScanAll("set1", false, 5); Assert.assertEquals(zScan1.size(), 2); + Assert.assertEquals(zScan1.get(0).getSet(), "set1".getBytes(StandardCharsets.UTF_8)); Assert.assertEquals(zScan1.get(0).getKey(), "zadd1".getBytes(StandardCharsets.UTF_8)); + Assert.assertEquals(zScan1.get(0).getScore(), 1.0); + Assert.assertEquals(zScan1.get(0).getAtTx(), 0); Assert.assertEquals(zScan1.get(0).getEntry().getValue(), value1); - List zScan2 = immuClient.zScan("set2", 5, false); + List zScan2 = immuClient.zScanAll("set2"); Assert.assertEquals(zScan2.size(), 2); - immuClient.logout(); + Iterator zScan3 = immuClient.zScan("set2"); + i = 0; + + while (zScan3.hasNext()) { + Assert.assertEquals(zScan3.next().getKey(), zScan2.get(i).getKey()); + i++; + } + + Assert.assertEquals(i, 2); + + Assert.assertFalse(immuClient.zScan("nonexistent-set").hasNext()); + + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/SetAllAndGetAllTest.java b/src/test/java/io/codenotary/immudb4j/SetAllAndGetAllTest.java index e35a92c..4c879b9 100644 --- a/src/test/java/io/codenotary/immudb4j/SetAllAndGetAllTest.java +++ b/src/test/java/io/codenotary/immudb4j/SetAllAndGetAllTest.java @@ -26,9 +26,7 @@ public class SetAllAndGetAllTest extends ImmuClientIntegrationTest { @Test(testName = "setAll & getAll") public void t1() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); String key1 = "sga-key1"; byte[] val1 = new byte[] { 1 }; @@ -44,8 +42,8 @@ public void t1() { .entries(); try { - TxHeader txMd = immuClient.setAll(kvs); - Assert.assertNotNull(txMd); + TxHeader txHdr = immuClient.setAll(kvs); + Assert.assertNotNull(txHdr); } catch (CorruptedDataException e) { Assert.fail("Failed at SetAll.", e); } @@ -59,7 +57,7 @@ public void t1() { Assert.assertEquals(got.get(i).getValue(), kvs.get(i).getValue(), String.format("Expected: %s got: %s", kvs.get(i), got.get(i))); } - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java b/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java index 2e15ec3..4f711cd 100644 --- a/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java +++ b/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java @@ -20,15 +20,11 @@ import org.testng.Assert; import org.testng.annotations.Test; -import java.nio.charset.StandardCharsets; - public class SetAndGetTest extends ImmuClientIntegrationTest { @Test(testName = "set, get") public void t1() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); String key = "key1"; byte[] val = new byte[]{1, 2, 3, 4, 5}; @@ -69,7 +65,7 @@ public void t1() { Assert.fail("key not found exception expected"); } - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/ShutdownTest.java b/src/test/java/io/codenotary/immudb4j/ShutdownTest.java index 542ec99..c3e5c65 100644 --- a/src/test/java/io/codenotary/immudb4j/ShutdownTest.java +++ b/src/test/java/io/codenotary/immudb4j/ShutdownTest.java @@ -22,18 +22,14 @@ public class ShutdownTest extends ImmuClientIntegrationTest { @Test(testName = "Login attempt after shutdown", expectedExceptions = StatusRuntimeException.class) - public void t1() { - + public void t1() throws InterruptedException { Assert.assertFalse(immuClient.isShutdown()); - immuClient.shutdown(); - - Assert.assertTrue(immuClient.isShutdown()); - - Assert.assertFalse(immuClient.isConnected()); - - immuClient.login("immudb", "immudb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); + immuClient.shutdown(); + + immuClient.openSession("defaultdb", "immudb", "immudb"); } } diff --git a/src/test/java/io/codenotary/immudb4j/StateTest.java b/src/test/java/io/codenotary/immudb4j/StateTest.java index 1a03876..5040c1c 100644 --- a/src/test/java/io/codenotary/immudb4j/StateTest.java +++ b/src/test/java/io/codenotary/immudb4j/StateTest.java @@ -28,11 +28,14 @@ public class StateTest extends ImmuClientIntegrationTest { private static final String publicKeyResource = "test_public_key.pem"; + @Test(testName = "currentState without open session", expectedExceptions = IllegalStateException.class) + public void t1() throws VerificationException { + immuClient.currentState(); + } + @Test(testName = "currentState") public void t2() throws VerificationException { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); ImmuState currState = immuClient.currentState(); @@ -50,23 +53,24 @@ public void t2() throws VerificationException { publicKey = CryptoUtils.getDERPublicKey(publicKeyFile.getAbsolutePath()); } catch (Exception e) { // Not a test itself fault, but we cannot continue it. - immuClient.logout(); + immuClient.closeSession(); return; } - // The signature verification in this case should fail for the same aforementioned reason. + // The signature verification in this case should fail for the same + // aforementioned reason. Assert.assertFalse(currState.checkSignature(publicKey)); // Again, "covering" `checkSignature` when there is a `signature` attached. - ImmuState someState = new ImmuState(currState.getDatabase(), currState.getTxId(), currState.getTxHash(), new byte[1]); + ImmuState someState = new ImmuState(currState.getDatabase(), currState.getTxId(), currState.getTxHash(), + new byte[1]); Assert.assertFalse(someState.checkSignature(publicKey)); - immuClient.logout(); + immuClient.closeSession(); } @Test(testName = "currentState with server signature checking, but only on the client side") public void t3() { - // Provisioning the client side with the public key file. ClassLoader classLoader = getClass().getClassLoader(); File publicKeyFile = new File(Objects.requireNonNull(classLoader.getResource(publicKeyResource)).getFile()); @@ -85,28 +89,27 @@ public void t3() { return; } - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); try { immuClient.currentState(); - Assert.fail("Did not fail as it should in this case when the signingKey is provisioned only on the client side"); + Assert.fail( + "Did not fail as it should in this case when the signingKey is provisioned only on the client side"); } catch (VerificationException ignored) { - // Expected this since in the current tests setup, immudb does not have that state signature feature active. - // (this feature is active when starting it like: `immudb --signingKey test_private_key.pem`). + // Expected this since in the current tests setup, immudb does not have that + // state signature feature active. + // (this feature is active when starting it like: `immudb --signingKey + // test_private_key.pem`). } - immuClient.logout(); + immuClient.closeSession(); } - - - @Test(testName = "currentState with server signature checking", - description = "Testing `checkSignature` (indirectly, through `currentState`), " + - "the (state signing) feature being set up on both server and client side. " + - "This could remain a manual test, that's why it is disabled." + - "Of course, it must be `enabled = true`, if you want to run it from IDE or cli.", - enabled = false) + @Test(testName = "currentState with server signature checking", description = "Testing `checkSignature` (indirectly, through `currentState`), " + + + "the (state signing) feature being set up on both server and client side. " + + "This could remain a manual test, that's why it is disabled." + + "Of course, it must be `enabled = true`, if you want to run it from IDE or cli.", enabled = false) public void t4() { // Provisioning the client side with the public key file. @@ -128,19 +131,20 @@ public void t4() { return; } - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); try { ImmuState state = immuClient.currentState(); - // In this case, it should be ok as long as the immudb server has been started accordingly - // from `immudb` directory (on this repo root) using: `./immudb --signingKey test_private_key.pem` + // In this case, it should be ok as long as the immudb server has been started + // accordingly + // from `immudb` directory (on this repo root) using: `./immudb --signingKey + // test_private_key.pem` Assert.assertNotNull(state); } catch (VerificationException e) { Assert.fail(e.getMessage(), e.getCause()); } - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/StreamSetAllTest.java b/src/test/java/io/codenotary/immudb4j/StreamSetAllTest.java new file mode 100644 index 0000000..a08fe4a --- /dev/null +++ b/src/test/java/io/codenotary/immudb4j/StreamSetAllTest.java @@ -0,0 +1,63 @@ +/* +Copyright 2022 CodeNotary, Inc. All rights reserved. + +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.codenotary.immudb4j; + +import io.codenotary.immudb4j.exceptions.CorruptedDataException; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.List; + +public class StreamSetAllTest extends ImmuClientIntegrationTest { + + @Test(testName = "setAll & getAll") + public void t1() { + immuClient.openSession("defaultdb", "immudb", "immudb"); + + String key1 = "sga-key1"; + byte[] val1 = new byte[] { 1 }; + String key2 = "sga-key2"; + byte[] val2 = new byte[] { 2, 3 }; + String key3 = "sga-key3"; + byte[] val3 = new byte[] { 3, 4, 5 }; + + final List kvs = KVListBuilder.newBuilder() + .add(new KVPair(key1, val1)) + .add(new KVPair(key2, val2)) + .add(new KVPair(key3, val3)) + .entries(); + + try { + TxHeader txHdr = immuClient.streamSetAll(kvs); + Assert.assertNotNull(txHdr); + } catch (InterruptedException|CorruptedDataException e) { + Assert.fail("Failed at SetAll.", e); + } + + List keys = Arrays.asList(key1, key2, key3); + List got = immuClient.getAll(keys); + + Assert.assertEquals(kvs.size(), got.size()); + + for (int i = 0; i < kvs.size(); i++) { + Assert.assertEquals(got.get(i).getValue(), kvs.get(i).getValue(), String.format("Expected: %s got: %s", kvs.get(i), got.get(i))); + } + + immuClient.closeSession(); + } + +} diff --git a/src/test/java/io/codenotary/immudb4j/StreamSetAndGetTest.java b/src/test/java/io/codenotary/immudb4j/StreamSetAndGetTest.java new file mode 100644 index 0000000..c5ce019 --- /dev/null +++ b/src/test/java/io/codenotary/immudb4j/StreamSetAndGetTest.java @@ -0,0 +1,58 @@ +/* +Copyright 2022 CodeNotary, Inc. All rights reserved. + +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.codenotary.immudb4j; + +import io.codenotary.immudb4j.exceptions.CorruptedDataException; +import io.codenotary.immudb4j.exceptions.KeyNotFoundException; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class StreamSetAndGetTest extends ImmuClientIntegrationTest { + + @Test(testName = "stream set, get") + public void t1() { + immuClient.openSession("defaultdb", "immudb", "immudb"); + + String key = "key1"; + byte[] val = new byte[]{1, 2, 3, 4, 5}; + + TxHeader txHdr = null; + try { + txHdr = immuClient.streamSet(key, val); + } catch (CorruptedDataException|InterruptedException e) { + Assert.fail("Failed at set.", e); + } + Assert.assertNotNull(txHdr); + + Entry entry1 = immuClient.streamGet(key); + Assert.assertNotNull(entry1); + Assert.assertEquals(entry1.getValue(), val); + + immuClient.delete(key); + + try { + immuClient.streamGet(key); + Assert.fail("key not found exception expected"); + } catch (KeyNotFoundException e) { + // expected + } catch (Exception e) { + Assert.fail("key not found exception expected"); + } + + immuClient.closeSession(); + } + +} diff --git a/src/test/java/io/codenotary/immudb4j/TxTest.java b/src/test/java/io/codenotary/immudb4j/TxTest.java index a58cc7a..11a26a5 100644 --- a/src/test/java/io/codenotary/immudb4j/TxTest.java +++ b/src/test/java/io/codenotary/immudb4j/TxTest.java @@ -16,6 +16,7 @@ package io.codenotary.immudb4j; import io.codenotary.immudb4j.exceptions.CorruptedDataException; +import io.codenotary.immudb4j.exceptions.TxNotFoundException; import io.codenotary.immudb4j.exceptions.VerificationException; import org.testng.Assert; import org.testng.annotations.Test; @@ -27,10 +28,8 @@ public class TxTest extends ImmuClientIntegrationTest { @Test(testName = "verifiedSet, txById, verifiedTxById") - public void t1() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + public void t1() throws NoSuchAlgorithmException, VerificationException{ + immuClient.openSession("defaultdb", "immudb", "immudb"); String key = "test-txid"; byte[] val = "test-txid-value".getBytes(StandardCharsets.UTF_8); @@ -42,12 +41,7 @@ public void t1() { Assert.fail("Failed at verifiedSet", e); } - Tx tx = null; - try { - tx = immuClient.txById(txHdr.getId()); - } catch (NoSuchAlgorithmException e) { - Assert.fail("Failed at txById", e); - } + Tx tx = immuClient.txById(txHdr.getId()); Assert.assertEquals(txHdr.getId(), tx.getHeader().getId()); @@ -59,14 +53,24 @@ public void t1() { Assert.assertEquals(txHdr.getId(), tx.getHeader().getId()); - immuClient.logout(); + try { + immuClient.txById(txHdr.getId()+1); + Assert.fail("Failed at txById."); + } catch (TxNotFoundException _) { + } + + try { + immuClient.verifiedTxById(txHdr.getId()+1); + Assert.fail("Failed at verifiedTxById."); + } catch (TxNotFoundException _) { + } + + immuClient.closeSession(); } @Test(testName = "set, txScan") public void t2() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); String key = "txtest-t2"; byte[] val1 = "immuRocks!".getBytes(StandardCharsets.UTF_8); @@ -83,17 +87,17 @@ public void t2() { Assert.fail("Failed at set.", e); } - List txs = immuClient.txScan(initialTxId, 1, false); + List txs = immuClient.txScanAll(initialTxId, 1, false); Assert.assertNotNull(txs); Assert.assertEquals(txs.size(), 1); - txs = immuClient.txScan(initialTxId, 2, false); + txs = immuClient.txScanAll(initialTxId, 2, false); Assert.assertNotNull(txs); Assert.assertEquals(txs.size(), 2); - Assert.assertNotNull(immuClient.txScan(initialTxId)); + Assert.assertNotNull(immuClient.txScanAll(initialTxId)); - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java b/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java index 7681568..cd6e4a1 100644 --- a/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java +++ b/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java @@ -22,22 +22,17 @@ import org.testng.annotations.Test; import java.util.Collections; -import java.util.List; -import java.util.Optional; - public class UserMgmtTest extends ImmuClientIntegrationTest { @Test(testName = "createUser, listUsers", priority = 100) public void t1() { - String database = "defaultdb"; String username = "testCreateUser"; String password = "testTest123!"; Permission permission = Permission.PERMISSION_RW; - immuClient.login("immudb", "immudb"); - immuClient.useDatabase(database); + immuClient.openSession(database, "immudb", "immudb"); // Should not contain testCreateUser. Skipping it as not valid for the current unit tests setup // (where a clean immudb server is started for each Test class). @@ -51,9 +46,9 @@ public void t1() { } // Should contain testCreateUser. - System.out.println(">>> listUsers:"); - List users = immuClient.listUsers(); - users.forEach(user -> System.out.println("\t- " + user)); + //System.out.println(">>> listUsers:"); + //List users = immuClient.listUsers(); + //users.forEach(user -> System.out.println("\t- " + user)); // TODO: Temporary commented since currently there's a bug on immudb's side. // The next release will include the fix of 'listUsers'. This commit includes the fix: @@ -68,36 +63,41 @@ public void t1() { // Assert.assertEquals(user.getCreatedBy(), "immudb"); // Assert.assertEquals(user.getPermissions().get(0), permission); - immuClient.logout(); + immuClient.closeSession(); } @Test(testName = "createUser, changePassword", priority = 101) public void t2() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); try { - immuClient.createUser("testUser", "testTest123!", Permission.PERMISSION_ADMIN, "defaultdb"); + immuClient.createUser("testUser", "testTest123!", Permission.PERMISSION_R, "defaultdb"); } catch (StatusRuntimeException e) { // The user could already exist, ignoring this. System.out.println(">>> UserMgmtTest > t2 > createUser exception: " + e.getMessage()); } + immuClient.activateUser("testUser", true); + + immuClient.revokePermission("testUser", "defaultdb", Permission.PERMISSION_R); + + immuClient.grantPermission("testUser", "defaultdb", Permission.PERMISSION_RW); + immuClient.changePassword("testUser", "testTest123!", "newTestTest123!"); - immuClient.logout(); + immuClient.closeSession(); // This must fail. try { - immuClient.login("testUser", "testTest123!"); - Assert.fail("Login with wrong (old) password must fail."); + immuClient.openSession("testUser", "testTest123", "defaultdb"); + Assert.fail("should fail with wrong (old) password must fail."); } catch (StatusRuntimeException e) { // Login failed, everything's fine. } - immuClient.login("testUser", "newTestTest123!"); - immuClient.logout(); + immuClient.openSession("defaultdb", "testUser", "newTestTest123!"); + + immuClient.closeSession(); // Some basic test to temporary (until t1 test above can be used) increase the code coverage. User myUser = new User.UserBuilder().setUser("myUsername").setCreatedAt("someTimestamp").setCreatedBy("me") diff --git a/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java b/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java index c85873a..a74b9dc 100644 --- a/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java +++ b/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java @@ -20,15 +20,14 @@ import org.testng.Assert; import org.testng.annotations.Test; +import java.io.IOException; import java.nio.charset.StandardCharsets; public class VerifiedSetAndGetTest extends ImmuClientIntegrationTest { @Test(testName = "set, verifiedGet") public void t1() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); String key = "vsg"; byte[] val = "test-set-vget".getBytes(StandardCharsets.UTF_8); @@ -48,14 +47,12 @@ public void t1() { Assert.assertEquals(val, vEntry.getValue()); - immuClient.logout(); + immuClient.closeSession(); } @Test(testName = "verifiedSet, verifiedGet, verifiedGetAt, verifiedGetSince") public void t2() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); byte[] key = "vsg".getBytes(StandardCharsets.UTF_8); byte[] val = "test-vset-vget".getBytes(StandardCharsets.UTF_8); @@ -96,9 +93,32 @@ public void t2() { } // Assert.assertNotNull(txMd); - immuClient.logout(); + immuClient.closeSession(); } + @Test(testName = "Login attempt after shutdown") + public void t3() throws InterruptedException, IllegalStateException, IOException, VerificationException { + immuClient.openSession("defaultdb", "immudb", "immudb"); + + immuClient.verifiedSet("key1", "val1".getBytes()); + + immuClient.closeSession(); + + immuClient.shutdown(); + FileImmuStateHolder stateHolder = FileImmuStateHolder.newBuilder() + .withStatesFolder(statesDir.getAbsolutePath()) + .build(); + + immuClient = ImmuClient.newBuilder() + .withStateHolder(stateHolder) + .withServerUrl("localhost") + .withServerPort(3322) + .build(); + + immuClient.openSession("defaultdb", "immudb", "immudb"); + + immuClient.verifiedGet("key1"); + } } diff --git a/src/test/java/io/codenotary/immudb4j/ZAddTest.java b/src/test/java/io/codenotary/immudb4j/ZAddTest.java index f8030ce..c00cba1 100644 --- a/src/test/java/io/codenotary/immudb4j/ZAddTest.java +++ b/src/test/java/io/codenotary/immudb4j/ZAddTest.java @@ -26,9 +26,7 @@ public class ZAddTest extends ImmuClientIntegrationTest { @Test(testName = "zAdd, verifiedZAdd, verifiedZAddAt") public void t1() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); String set = "test-zadd"; String key1 = "test-zadd-key1"; @@ -61,7 +59,7 @@ public void t1() { } Assert.assertNotNull(txHdr); - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/tests.sh b/tests.sh index 0825e5d..0399a7e 100755 --- a/tests.sh +++ b/tests.sh @@ -9,17 +9,27 @@ echo ## Unit Tests TESTS="${TESTS} BasicImmuClientTest" +TESTS="${TESTS} BasicsTest" +TESTS="${TESTS} CryptoUtilsTest" +TESTS="${TESTS} ExceptionsTest" +TESTS="${TESTS} FileImmuStateHolderTest" +TESTS="${TESTS} HealthCheckAndIndexCompactionTest" +TESTS="${TESTS} HistoryTest" TESTS="${TESTS} HTreeTest" -TESTS="${TESTS} ListDatabasesTest ListUsersTest" -TESTS="${TESTS} LoginAndHealthCheckTest" -TESTS="${TESTS} MultidatabaseTest MultithreadTest" +TESTS="${TESTS} ListDatabasesTest" +TESTS="${TESTS} ListUsersTest" +TESTS="${TESTS} MultidatabaseTest" +TESTS="${TESTS} MultithreadTest" TESTS="${TESTS} ReferenceTest" -TESTS="${TESTS} ScanAndHistoryTest" -TESTS="${TESTS} SetAndGetTest SetAllAndGetAllTest" +TESTS="${TESTS} ScanTest" +TESTS="${TESTS} SetAllAndGetAllTest" +TESTS="${TESTS} SetAndGetTest" +TESTS="${TESTS} StreamSetAndGetTest" +TESTS="${TESTS} StreamSetAllTest" TESTS="${TESTS} ShutdownTest" TESTS="${TESTS} StateTest" TESTS="${TESTS} TxTest" -TESTS="${TESTS} UseDatabaseTest UserMgmtTest" +TESTS="${TESTS} UserMgmtTest" TESTS="${TESTS} VerifiedSetAndGetTest" TESTS="${TESTS} ZAddTest"