From fe8d1da6edcac462ac982f0025eaacea55707b2c Mon Sep 17 00:00:00 2001 From: Luis Sanchez Date: Sat, 22 Apr 2017 00:29:08 -0400 Subject: [PATCH] [FAB-3311] java cc get history for key - new KeyModification type - QueryResultsIterator ChaincodeStub.getHistoryForKey(key) - QueryResultsIterator Handler.handleGetHistoryForKey(key) Change-Id: I3d4e8014d3c2dfa210483350968e439edbe756c7 Signed-off-by: Luis Sanchez --- .../fabric/shim/ChaincodeStub.java | 9 ++ .../fabric/shim/impl/ChaincodeStubImpl.java | 20 ++++ .../hyperledger/fabric/shim/impl/Handler.java | 6 ++ .../fabric/shim/impl/KeyModificationImpl.java | 89 +++++++++++++++++ .../fabric/shim/ledger/KeyModification.java | 56 +++++++++++ .../shim/impl/KeyModificationImplTest.java | 99 +++++++++++++++++++ 6 files changed, 279 insertions(+) create mode 100644 core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/impl/KeyModificationImpl.java create mode 100644 core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/ledger/KeyModification.java create mode 100644 core/chaincode/shim/java/src/test/java/org/hyperledger/fabric/shim/impl/KeyModificationImplTest.java diff --git a/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/ChaincodeStub.java b/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/ChaincodeStub.java index 519859c594e..a1150e31e1a 100644 --- a/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/ChaincodeStub.java +++ b/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/ChaincodeStub.java @@ -24,6 +24,7 @@ import org.hyperledger.fabric.protos.peer.ChaincodeEventPackage.ChaincodeEvent; import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response; import org.hyperledger.fabric.shim.ledger.CompositeKey; +import org.hyperledger.fabric.shim.ledger.KeyModification; import org.hyperledger.fabric.shim.ledger.KeyValue; import org.hyperledger.fabric.shim.ledger.QueryResultsIterator; @@ -175,6 +176,14 @@ public interface ChaincodeStub { */ QueryResultsIterator getQueryResult(String query); + /** + * Returns the history of the specified key's values across time. + * + * @param key + * @return an {@link Iterable} of {@link KeyModification} + */ + QueryResultsIterator getHistoryForKey(String key); + /** * Defines the CHAINCODE type event that will be posted to interested * clients when the chaincode's result is committed to the ledger. diff --git a/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/impl/ChaincodeStubImpl.java b/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/impl/ChaincodeStubImpl.java index ee4566aa3b2..fdb1dad5f14 100644 --- a/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/impl/ChaincodeStubImpl.java +++ b/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/impl/ChaincodeStubImpl.java @@ -25,12 +25,14 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.hyperledger.fabric.protos.ledger.queryresult.KvQueryResult; import org.hyperledger.fabric.protos.ledger.queryresult.KvQueryResult.KV; import org.hyperledger.fabric.protos.peer.ChaincodeEventPackage.ChaincodeEvent; import org.hyperledger.fabric.protos.peer.ChaincodeShim.QueryResultBytes; import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response; import org.hyperledger.fabric.shim.ChaincodeStub; import org.hyperledger.fabric.shim.ledger.CompositeKey; +import org.hyperledger.fabric.shim.ledger.KeyModification; import org.hyperledger.fabric.shim.ledger.KeyValue; import org.hyperledger.fabric.shim.ledger.QueryResultsIterator; @@ -198,6 +200,24 @@ public QueryResultsIterator getQueryResult(String query) { ); } + @Override + public QueryResultsIterator getHistoryForKey(String key) { + return new QueryResultsIteratorImpl(this.handler, getTxId(), + handler.handleGetHistoryForKey(getTxId(), key), + queryResultBytesToKeyModification.andThen(KeyModificationImpl::new) + ); + } + + private Function queryResultBytesToKeyModification = new Function() { + public KvQueryResult.KeyModification apply(QueryResultBytes queryResultBytes) { + try { + return KvQueryResult.KeyModification.parseFrom(queryResultBytes.getResultBytes()); + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException(e); + } + }; + }; + /* (non-Javadoc) * @see org.hyperledger.fabric.shim.ChaincodeStub#invokeChaincode(java.lang.String, java.util.List, java.lang.String) */ diff --git a/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/impl/Handler.java b/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/impl/Handler.java index 54b436aaa75..fab30eb84e8 100644 --- a/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/impl/Handler.java +++ b/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/impl/Handler.java @@ -599,6 +599,12 @@ QueryResponse handleGetQueryResult(String txId, String query) { .build().toByteString()); } + QueryResponse handleGetHistoryForKey(String txId, String key) { + return invokeQueryResponseMessage(txId, Type.GET_HISTORY_FOR_KEY, GetQueryResult.newBuilder() + .setQuery(key) + .build().toByteString()); + } + private QueryResponse invokeQueryResponseMessage(String txId, ChaincodeMessage.Type type, ByteString payload) { try { return QueryResponse.parseFrom(invokeChaincodeSupport(txId, type, payload) diff --git a/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/impl/KeyModificationImpl.java b/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/impl/KeyModificationImpl.java new file mode 100644 index 00000000000..fc57c264207 --- /dev/null +++ b/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/impl/KeyModificationImpl.java @@ -0,0 +1,89 @@ +/* +Copyright IBM 2017 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 org.hyperledger.fabric.shim.impl; + +import java.time.Instant; + +import org.hyperledger.fabric.protos.ledger.queryresult.KvQueryResult; +import org.hyperledger.fabric.shim.ledger.KeyModification; + +import com.google.protobuf.ByteString; + +public class KeyModificationImpl implements KeyModification { + + private final String txId; + private final ByteString value; + private final java.time.Instant timestamp; + private final boolean deleted; + + KeyModificationImpl(KvQueryResult.KeyModification km) { + this.txId = km.getTxId(); + this.value = km.getValue(); + this.timestamp = Instant.ofEpochSecond(km.getTimestamp().getSeconds(), km.getTimestamp().getNanos()); + this.deleted = km.getIsDelete(); + } + + /* + * (non-Javadoc) + * + * @see org.hyperledger.fabric.shim.impl.KeyModification#getTxId() + */ + @Override + public String getTxId() { + return txId; + } + + /* + * (non-Javadoc) + * + * @see org.hyperledger.fabric.shim.impl.KeyModification#getValue() + */ + @Override + public byte[] getValue() { + return value.toByteArray(); + } + + /* + * (non-Javadoc) + * + * @see org.hyperledger.fabric.shim.impl.KeyModification#getStringValue() + */ + @Override + public String getStringValue() { + return value.toStringUtf8(); + } + + /* + * (non-Javadoc) + * + * @see org.hyperledger.fabric.shim.impl.KeyModification#getTimestamp() + */ + @Override + public java.time.Instant getTimestamp() { + return timestamp; + } + + /* + * (non-Javadoc) + * + * @see org.hyperledger.fabric.shim.impl.KeyModification#isDeleted() + */ + @Override + public boolean isDeleted() { + return deleted; + } + +} diff --git a/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/ledger/KeyModification.java b/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/ledger/KeyModification.java new file mode 100644 index 00000000000..75592f89773 --- /dev/null +++ b/core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/ledger/KeyModification.java @@ -0,0 +1,56 @@ +/* +Copyright IBM 2017 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 org.hyperledger.fabric.shim.ledger; + +public interface KeyModification { + + /** + * Returns the transaction id. + * + * @return + */ + String getTxId(); + + /** + * Returns the key's value at the time returned by {@link #getTimestamp()}. + * + * @return + */ + byte[] getValue(); + + /** + * Returns the key's value at the time returned by {@link #getTimestamp()}, + * decoded as a UTF-8 string. + * + * @return + */ + String getStringValue(); + + /** + * Returns the timestamp of the key modification entry. + * + * @return + */ + java.time.Instant getTimestamp(); + + /** + * Returns the deletion marker. + * + * @return + */ + boolean isDeleted(); + +} \ No newline at end of file diff --git a/core/chaincode/shim/java/src/test/java/org/hyperledger/fabric/shim/impl/KeyModificationImplTest.java b/core/chaincode/shim/java/src/test/java/org/hyperledger/fabric/shim/impl/KeyModificationImplTest.java new file mode 100644 index 00000000000..cac8d03039d --- /dev/null +++ b/core/chaincode/shim/java/src/test/java/org/hyperledger/fabric/shim/impl/KeyModificationImplTest.java @@ -0,0 +1,99 @@ +/* +Copyright IBM 2017 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 org.hyperledger.fabric.shim.impl; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import java.util.stream.Stream; + +import org.hyperledger.fabric.protos.ledger.queryresult.KvQueryResult; +import org.hyperledger.fabric.shim.ledger.KeyModification; +import org.junit.Test; + +import com.google.protobuf.ByteString; +import com.google.protobuf.Timestamp; + +public class KeyModificationImplTest { + + @Test + public void testKeyModificationImpl() { + new KeyModificationImpl(KvQueryResult.KeyModification.newBuilder() + .setTxId("txid") + .setValue(ByteString.copyFromUtf8("value")) + .setTimestamp(Timestamp.newBuilder() + .setSeconds(1234567890) + .setNanos(123456789)) + .setIsDelete(true) + .build() + ); + } + + @Test + public void testGetTxId() { + final KeyModification km = new KeyModificationImpl(KvQueryResult.KeyModification.newBuilder() + .setTxId("txid") + .build() + ); + assertThat(km.getTxId(), is(equalTo("txid"))); + } + + @Test + public void testGetValue() { + final KeyModification km = new KeyModificationImpl(KvQueryResult.KeyModification.newBuilder() + .setValue(ByteString.copyFromUtf8("value")) + .build() + ); + assertThat(km.getValue(), is(equalTo("value".getBytes(UTF_8)))); + } + + @Test + public void testGetStringValue() { + final KeyModification km = new KeyModificationImpl(KvQueryResult.KeyModification.newBuilder() + .setValue(ByteString.copyFromUtf8("value")) + .build() + ); + assertThat(km.getStringValue(), is(equalTo("value"))); + } + + @Test + public void testGetTimestamp() { + final KeyModification km = new KeyModificationImpl(KvQueryResult.KeyModification.newBuilder() + .setTimestamp(Timestamp.newBuilder() + .setSeconds(1234567890L) + .setNanos(123456789)) + .build() + ); + assertThat(km.getTimestamp(), hasProperty("epochSecond", equalTo(1234567890L))); + assertThat(km.getTimestamp(), hasProperty("nano", equalTo(123456789))); + } + + @Test + public void testIsDeleted() { + Stream.of(true, false) + .forEach(b -> { + final KeyModification km = new KeyModificationImpl(KvQueryResult.KeyModification.newBuilder() + .setIsDelete(b) + .build() + ); + assertThat(km.isDeleted(), is(b)); + }); + } + +}