From 3ce6f3a38d8ea28693d5eb43043cc74e78234d26 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Thu, 2 Jun 2022 13:11:14 -0300 Subject: [PATCH] feat(src/main): add new kv and index-related apis Signed-off-by: Jeronimo Irazabal --- build.gradle | 4 +- .../io/codenotary/immudb4j/ImmuClient.java | 154 ++++++++++++++++-- src/main/proto/schema.proto | 4 + .../io/codenotary/immudb4j/SetAndGetTest.java | 25 ++- .../immudb4j/VerifiedSetAndGetTest.java | 2 +- 5 files changed, 169 insertions(+), 20 deletions(-) diff --git a/build.gradle b/build.gradle index b5bfb16..46bc2e9 100644 --- a/build.gradle +++ b/build.gradle @@ -40,13 +40,13 @@ apply plugin: 'signing' group = 'io.codenotary' archivesBaseName = 'immudb4j' -version = '0.9.10.1' +version = '0.9.10.2' sourceCompatibility = 1.8 targetCompatibility = 1.8 def protocVersion = '3.19.4' -def grpcVersion = '1.43.2' +def grpcVersion = '1.44.1' protobuf { protoc { diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index 55dff68..5c1438a 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -214,17 +214,17 @@ public Entry get(String key) throws KeyNotFoundException { } public Entry get(byte[] key) throws KeyNotFoundException { - return get(key, 0); + return getAtTx(key, 0); } - public Entry get(String key, long atTx) throws KeyNotFoundException { - return get(Utils.toByteArray(key), atTx); + public Entry getAtTx(String key, long tx) throws KeyNotFoundException { + return getAtTx(Utils.toByteArray(key), tx); } - public Entry get(byte[] key, long atTx) throws KeyNotFoundException { + public Entry getAtTx(byte[] key, long tx) throws KeyNotFoundException { final ImmudbProto.KeyRequest req =ImmudbProto.KeyRequest.newBuilder() .setKey(Utils.toByteString(key)) - .setAtTx(atTx) + .setAtTx(tx) .build(); try { @@ -238,6 +238,48 @@ public Entry get(byte[] key, long atTx) throws KeyNotFoundException { } } + 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() + .setKey(Utils.toByteString(key)) + .setSinceTx(tx) + .build(); + + try { + return Entry.valueOf(getStub().get(req)); + } catch (StatusRuntimeException e) { + if (e.getMessage().contains("key not found")) { + throw new KeyNotFoundException(); + } + + throw e; + } + } + + 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() + .setKey(Utils.toByteString(key)) + .setAtRevision(rev) + .build(); + + try { + return Entry.valueOf(getStub().get(req)); + } catch (StatusRuntimeException e) { + if (e.getMessage().contains("key not found")) { + throw new KeyNotFoundException(); + } + + throw e; + } + } + public List getAll(List keys) { final List keysBS = new ArrayList<>(keys.size()); @@ -258,27 +300,53 @@ public List getAll(List keys) { } public Entry verifiedGet(String key) throws KeyNotFoundException, VerificationException { - return verifiedGet(key, 0); + return verifiedGetAtTx(key, 0); } public Entry verifiedGet(byte[] key) throws KeyNotFoundException, VerificationException { - return verifiedGet(key, 0); + return verifiedGetAtTx(key, 0); + } + + public Entry verifiedGetAtTx(String key, long tx) throws KeyNotFoundException, VerificationException { + return verifiedGetAtTx(Utils.toByteArray(key), tx); } - public Entry verifiedGet(String key, long atTx) throws KeyNotFoundException, VerificationException { - return verifiedGet(Utils.toByteArray(key), atTx); + public Entry verifiedGetAtTx(byte[] key, long tx) throws KeyNotFoundException, VerificationException { + final ImmudbProto.KeyRequest keyReq = ImmudbProto.KeyRequest.newBuilder() + .setKey(Utils.toByteString(key)) + .setAtTx(tx) + .build(); + + return verifiedGet(keyReq, state()); } - public Entry verifiedGet(byte[] key, long atTx) throws KeyNotFoundException, VerificationException { - return verifiedGet(key, atTx, state()); + public Entry verifiedGetSinceTx(String key, long tx) throws KeyNotFoundException, VerificationException { + return verifiedGetSinceTx(Utils.toByteArray(key), tx); } - public Entry verifiedGet(byte[] key, long atTx, ImmuState state) throws KeyNotFoundException, VerificationException { + public Entry verifiedGetSinceTx(byte[] key, long tx) throws KeyNotFoundException, VerificationException { final ImmudbProto.KeyRequest keyReq = ImmudbProto.KeyRequest.newBuilder() .setKey(Utils.toByteString(key)) - .setAtTx(atTx) + .setSinceTx(tx) .build(); + 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 { + final ImmudbProto.KeyRequest keyReq = ImmudbProto.KeyRequest.newBuilder() + .setKey(Utils.toByteString(key)) + .setAtRevision(rev) + .build(); + + return verifiedGet(keyReq, state()); + } + + private Entry verifiedGet(ImmudbProto.KeyRequest keyReq, ImmuState state) throws KeyNotFoundException, VerificationException { final ImmudbProto.VerifiableGetRequest vGetReq = ImmudbProto.VerifiableGetRequest.newBuilder() .setKeyRequest(keyReq) .setProveSinceTx(state.txId) @@ -296,6 +364,14 @@ public Entry verifiedGet(byte[] key, long atTx, ImmuState state) throws KeyNotFo final Entry entry = Entry.valueOf(vEntry.getEntry()); + if (entry.getReferenceBy() == null && !Arrays.equals(keyReq.getKey().toByteArray(), entry.getKey())) { + throw new RuntimeException("Data is corrupted: entry does not belong to specified key"); + } + + if (entry.getReferenceBy() != null && !Arrays.equals(keyReq.getKey().toByteArray(), entry.getReferenceBy().getKey())) { + throw new RuntimeException("Data is corrupted: entry does not belong to specified key"); + } + if (entry.getMetadata() != null && entry.getMetadata().deleted()) { throw new RuntimeException("Data is corrupted: entry is marked as deleted"); } @@ -355,6 +431,30 @@ public Entry verifiedGet(byte[] key, long atTx, ImmuState state) throws KeyNotFo return Entry.valueOf(vEntry.getEntry()); } + // + // ========== DELETE ========== + // + + public TxHeader delete(String key) throws KeyNotFoundException { + return delete(Utils.toByteArray(key)); + } + + public 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)); + } catch (StatusRuntimeException e) { + if (e.getMessage().contains("key not found")) { + throw new KeyNotFoundException(); + } + + throw e; + } + } + // // ========== HISTORY ========== // @@ -407,10 +507,26 @@ public List scan(String prefix, String seekKey, long limit, boolean desc) return scan(Utils.toByteArray(prefix), Utils.toByteArray(seekKey), limit, desc); } + 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 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) { 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) .build(); @@ -928,11 +1044,19 @@ public void changePassword(String user, String oldPassword, String newPassword) } // - // ========== USER MGMT ========== + // ========== INDEX MGMT ========== // + public void flushIndex(float cleanupPercentage, boolean synced) { + ImmudbProto.FlushIndexRequest req = ImmudbProto.FlushIndexRequest.newBuilder() + .setCleanupPercentage(cleanupPercentage) + .setSynced(synced) + .build(); + + getStub().flushIndex(req); + } + public void compactIndex() { - //noinspection ResultOfMethodCallIgnored getStub().compactIndex(Empty.getDefaultInstance()); } diff --git a/src/main/proto/schema.proto b/src/main/proto/schema.proto index 65ac448..92f9fd6 100644 --- a/src/main/proto/schema.proto +++ b/src/main/proto/schema.proto @@ -159,11 +159,14 @@ message ZEntries { 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; } message KeyPrefix { @@ -271,6 +274,7 @@ message KeyRequest { uint64 atTx = 2; uint64 sinceTx = 3; bool noWait = 4; + int64 atRevision = 5; } message KeyListRequest { diff --git a/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java b/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java index c30aefe..c47fa78 100644 --- a/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java +++ b/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java @@ -16,6 +16,7 @@ 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; @@ -29,7 +30,7 @@ public void t1() { immuClient.login("immudb", "immudb"); immuClient.useDatabase("defaultdb"); - byte[] key = "key1".getBytes(StandardCharsets.UTF_8); + String key = "key1"; byte[] val = new byte[]{1, 2, 3, 4, 5}; TxHeader txHdr = null; @@ -44,10 +45,30 @@ public void t1() { Assert.assertNotNull(entry1); Assert.assertEquals(entry1.getValue(), val); - Entry entry1At = immuClient.get(key, txHdr.id); + Entry entry1At = immuClient.getAtTx(key, txHdr.id); Assert.assertNotNull(entry1At); Assert.assertEquals(entry1At.getValue(), val); + immuClient.delete(key); + + try { + immuClient.get(key); + Assert.fail("key not found exception expected"); + } catch (KeyNotFoundException _) { + // expected + } catch (Exception _) { + Assert.fail("key not found exception expected"); + } + + try { + immuClient.delete(key); + Assert.fail("key not found exception expected"); + } catch (KeyNotFoundException _) { + // expected + } catch (Exception _) { + Assert.fail("key not found exception expected"); + } + immuClient.logout(); } diff --git a/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java b/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java index b730b02..c85873a 100644 --- a/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java +++ b/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java @@ -79,7 +79,7 @@ public void t2() { // verifiedGetAt try { - vEntry = immuClient.verifiedGet(key, vEntry.getTx()); + vEntry = immuClient.verifiedGetAtTx(key, vEntry.getTx()); } catch (VerificationException e) { Assert.fail("Failed at verifiedGetAt. Cause: " + e.getMessage(), e); }