Skip to content
This repository was archived by the owner on Apr 13, 2022. It is now read-only.

Commit a5f4bbe

Browse files
authored
Merge pull request #17 from airvin/iterator-methods
Iterator methods
2 parents f5745bd + 04ac4a8 commit a5f4bbe

File tree

5 files changed

+102
-39
lines changed

5 files changed

+102
-39
lines changed

examples/Marbles.hs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import Shim ( start
1616
)
1717

1818
import Peer.ProposalResponse as Pb
19+
import Peer.ChaincodeShim as Pb
1920
import Ledger.Queryresult.KvQueryResult as Pb
2021

2122
import Data.Text ( Text
@@ -177,22 +178,23 @@ getMarblesByRange s params = if Prelude.length params == 2
177178
trace (show resultBytes) (pure $ successPayload Nothing)
178179
else pure $ errorPayload "Incorrect arguments. Need a start key and an end key"
179180

181+
-- TODO: include retrieval of next set of results using the returned bookmark (next TODO)
180182
getMarblesByRangeWithPagination :: DefaultChaincodeStub -> [Text] -> IO Pb.Response
181183
getMarblesByRangeWithPagination s params = if Prelude.length params == 4
182184
then do
183185
e <- getStateByRangeWithPagination s (params !! 0) (params !! 1) (read (unpack $ params !! 2) :: Int) (params !! 3)
184186
case e of
185187
Left _ -> pure $ errorPayload "Failed to get marbles"
186-
Right _ -> pure $ successPayload $ Just "The payload"
188+
Right (sqi, metadata) -> do
189+
resultBytes <- generateResultBytesForPagination (sqi, metadata) ""
190+
trace (show resultBytes) (pure $ successPayload Nothing)
187191
else pure $ errorPayload "Incorrect arguments. Need start key, end key, pageSize and bookmark"
188192

189193
generateResultBytes :: StateQueryIterator -> Text -> IO (Either Error BSU.ByteString)
190194
generateResultBytes sqi text = do
191195
hasNextBool <- hasNext sqi
192-
if hasNextBool then do
196+
if (trace $ "hasNext in generateResultBytes: " ++ show hasNextBool) hasNextBool then do
193197
eeKV <- next sqi
194-
-- TODO: We need to check that the Either Error KV returned from next
195-
-- is correct and append the showable version of KVs instead of "abc".
196198
case eeKV of
197199
Left e -> pure $ Left e
198200
Right kv ->
@@ -203,6 +205,21 @@ generateResultBytes sqi text = do
203205
generateResultBytes sqi (append text (makeKVString kv))
204206
else pure $ Right $ TSE.encodeUtf8 text
205207

208+
generateResultBytesForPagination:: (StateQueryIterator, Pb.QueryResponseMetadata) -> Text -> IO (Either Error BSU.ByteString)
209+
generateResultBytesForPagination (sqi, md) text = do
210+
hasNextBool <- hasNext sqi
211+
if (trace $ "hasNext in generateResultBytesForPagination: " ++ show hasNextBool) hasNextBool then do
212+
eeKV <- next sqi
213+
case eeKV of
214+
Left e -> pure $ Left e
215+
Right kv ->
216+
let
217+
makeKVString :: Pb.KV -> Text
218+
makeKVString kv_ = pack "Key: " <> TL.toStrict (Pb.kvKey kv_) <> pack ", Value: " <> TSE.decodeUtf8 (kvValue kv_)
219+
in
220+
generateResultBytesForPagination (sqi, md) (append text (makeKVString kv))
221+
else pure $ Right $ TSE.encodeUtf8 text
222+
206223
parseMarble :: [Text] -> Marble
207224
parseMarble params = Marble { objectType = "marble"
208225
, name = params !! 0

examples/readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ peer chaincode invoke -n mycc -c '{"Args":["readMarble","marble1"]}' -C myc
3232
peer chaincode invoke -n mycc -c '{"Args":["deleteMarble","marble1"]}' -C myc
3333
peer chaincode invoke -n mycc -c '{"Args":["transferMarble","marble1", "Nick"]}' -C myc
3434
peer chaincode invoke -n mycc -c '{"Args":["getMarblesByRange","marble1", "marble3"]}' -C myc
35+
peer chaincode invoke -n mycc -c '{"Args":["getMarblesByRangeWithPagination","marble1", "marble3", "1", ""]}' -C myc
3536
```
3637

3738
## Fabcar Chaincode

readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,4 @@ peer chaincode invoke -n mycc -c '{"Args":["del","a"]}' -C myc
8888
- [ ] Add support for concurrent transactions
8989
- [ ] Finish implementing all stub functions
9090
- [ ] Publish to Hackage
91-
- [ ] Add traces throughout the chaincode examples
91+
- [ ] Improve logging

src/Messages.hs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,16 @@ delStatePayload :: Text -> Pb.DelState
6666
delStatePayload key =
6767
Pb.DelState {delStateKey = fromStrict key, delStateCollection = ""}
6868

69-
getStateByRangePayload :: Text -> Text -> Pb.GetStateByRange
70-
getStateByRangePayload startKey endKey = Pb.GetStateByRange {
69+
getStateByRangePayload :: Text -> Text -> Maybe Pb.QueryMetadata -> Pb.GetStateByRange
70+
getStateByRangePayload startKey endKey metaData = Pb.GetStateByRange {
7171
getStateByRangeStartKey = fromStrict startKey
7272
, getStateByRangeEndKey = fromStrict endKey
7373
, getStateByRangeCollection = ""
74-
, getStateByRangeMetadata = BSU.fromString ""
74+
, getStateByRangeMetadata = case metaData of
75+
-- This is an example of how to encode a Pb type into a bytestring
76+
-- https://hackage.haskell.org/package/proto3-wire-1.2.0/docs/Proto3-Wire-Tutorial.html
77+
Just metaData -> LBS.toStrict $ Wire.toLazyByteString $ encodeMessage (FieldNumber 1) metaData
78+
Nothing -> BSU.fromString ""
7579
}
7680

7781
queryNextStatePayload :: Text -> Pb.QueryStateNext

src/Stub.hs

Lines changed: 72 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
module Stub where
55

6-
6+
-- import Data.Int (fromIntegral)
77
import Data.Bifunctor
88
import Data.ByteString as BS
99
import Data.Text
@@ -23,7 +23,6 @@ import Data.Vector as Vector
2323
, (!)
2424
)
2525
import qualified Data.ByteString.Lazy as LBS
26-
import Data.IORef (readIORef, newIORef, modifyIORef)
2726
import Control.Monad.Except (ExceptT(..), runExceptT)
2827

2928
import qualified Peer.ChaincodeShim as Pb
@@ -39,6 +38,7 @@ import Interfaces
3938
import Messages
4039
import Types
4140

41+
import Debug.Trace
4242
-- NOTE: When support for concurrency transaction is added, this function will no longer be required
4343
-- as the stub function will block and listen for responses over a channel when the code is concurrent
4444
listenForResponse :: StreamRecv Pb.ChaincodeMessage -> IO (Either Error ByteString)
@@ -125,47 +125,41 @@ instance ChaincodeStubInterface DefaultChaincodeStub where
125125
-- TODO: Implement better error handling/checks etc
126126
-- getStateByRange :: ccs -> Text -> Text -> IO (Either Error StateQueryIterator)
127127
getStateByRange ccs startKey endKey =
128-
let payload = getStateByRangePayload startKey endKey
129-
message = buildChaincodeMessage GET_STATE_BY_RANGE payload (txId ccs) (channelId ccs)
130-
-- ExceptT is a monad transformer that allows us to compose these by binding over IO Either
131-
bsToSqi :: ByteString -> ExceptT Error IO StateQueryIterator
132-
bsToSqi bs =
133-
let eeaQueryResponse = parse (decodeMessage (FieldNumber 1)) bs :: Either ParseError Pb.QueryResponse
134-
in
135-
case eeaQueryResponse of
136-
-- TODO: refactor out pattern matching, e.g. using >>= or <*>
137-
Left err -> ExceptT $ pure $ Left $ DecodeError err
138-
Right queryResponse -> ExceptT $ do
139-
-- queryResponse and currentLoc are IORefs as they need to be mutated
140-
-- as a part of the next() function
141-
queryResponseIORef <- newIORef queryResponse
142-
currentLocIORef <- newIORef 0
143-
pure $ Right StateQueryIterator {
144-
sqiChaincodeStub = ccs
145-
, sqiChannelId = getChannelId ccs
146-
, sqiTxId = getTxId ccs
147-
, sqiResponse = queryResponseIORef
148-
, sqiCurrentLoc = currentLocIORef
149-
}
128+
let payload = getStateByRangePayload startKey endKey Nothing
129+
message = buildChaincodeMessage GET_STATE_BY_RANGE payload (txId ccs) (channelId ccs)
150130
in do
151131
e <- (sendStream ccs) message
152132
case e of
153133
Left err -> error ("Error while streaming: " ++ show err)
154134
Right _ -> pure ()
155-
runExceptT $ ExceptT (listenForResponse (recvStream ccs)) >>= bsToSqi
135+
runExceptT $ ExceptT (listenForResponse (recvStream ccs)) >>= (bsToSqi ccs)
156136

157137
-- TODO: We need to implement this so we can test the fetchNextQueryResult functionality
158-
-- getStateByRangeWithPagination :: ccs -> String -> String -> Int32 -> String -> Either Error (StateQueryIterator, Pb.QueryResponseMetadata)
159-
getStateByRangeWithPagination ccs startKey endKey pageSize bookmark = pure $ Left $ Error "Not implemented"
138+
-- getStateByRangeWithPagination :: ccs -> Text -> Text -> Int -> Text -> IO (Either Error (StateQueryIterator, Pb.QueryResponseMetadata))
139+
getStateByRangeWithPagination ccs startKey endKey pageSize bookmark =
140+
let metadata = Pb.QueryMetadata {
141+
Pb.queryMetadataPageSize = fromIntegral pageSize
142+
, Pb.queryMetadataBookmark = TL.fromStrict bookmark
143+
}
144+
payload = (trace "Building getStateByRangeWithPagination payload") getStateByRangePayload startKey endKey $ Just metadata
145+
message = buildChaincodeMessage GET_STATE_BY_RANGE payload (txId ccs) (channelId ccs)
146+
in do
147+
e <- (sendStream ccs) message
148+
case e of
149+
Left err -> error ("Error while streaming: " ++ show err)
150+
Right _ -> pure ()
151+
runExceptT $ ExceptT (listenForResponse (recvStream ccs)) >>= (bsToSqiAndMeta ccs)
152+
160153

161154
-- TODO : implement all these interface functions
162155
instance StateQueryIteratorInterface StateQueryIterator where
163156
-- TODO: remove the IO from this function (possibly with the State monad)
164157
-- hasNext :: sqi -> IO Bool
165158
hasNext sqi = do
166159
queryResponse <- readIORef $ sqiResponse sqi
167-
currentLoc <- readIORef $ sqiCurrentLoc sqi
168-
pure $ currentLoc < Prelude.length (Pb.queryResponseResults queryResponse) || (Pb.queryResponseHasMore queryResponse)
160+
currentLoc <- (trace $ "Query response: " ++ show queryResponse) readIORef $ sqiCurrentLoc sqi
161+
pure $ (currentLoc < Prelude.length (Pb.queryResponseResults queryResponse))
162+
|| (Pb.queryResponseHasMore queryResponse)
169163
-- close :: sqi -> IO (Maybe Error)
170164
close _ = pure Nothing
171165
-- next :: sqi -> IO (Either Error Pb.KV)
@@ -176,6 +170,53 @@ instance StateQueryIteratorInterface StateQueryIterator where
176170
Right queryResultBytes -> pure $ first DecodeError (parse (decodeMessage (FieldNumber 1)) (Pb.queryResultBytesResultBytes queryResultBytes) :: Either ParseError Pb.KV)
177171

178172

173+
-- ExceptT is a monad transformer that allows us to compose these by binding over IO Either
174+
bsToSqi :: DefaultChaincodeStub -> ByteString -> ExceptT Error IO StateQueryIterator
175+
bsToSqi ccs bs =
176+
let eeaQueryResponse = parse (decodeMessage (FieldNumber 1)) bs :: Either ParseError Pb.QueryResponse
177+
in
178+
case eeaQueryResponse of
179+
-- TODO: refactor out pattern matching, e.g. using >>= or <*>
180+
Left err -> ExceptT $ pure $ Left $ DecodeError err
181+
Right queryResponse -> ExceptT $ do
182+
-- queryResponse and currentLoc are IORefs as they need to be mutated
183+
-- as a part of the next() function
184+
queryResponseIORef <- newIORef queryResponse
185+
currentLocIORef <- newIORef 0
186+
pure $ Right StateQueryIterator {
187+
sqiChaincodeStub = ccs
188+
, sqiChannelId = getChannelId ccs
189+
, sqiTxId = getTxId ccs
190+
, sqiResponse = queryResponseIORef
191+
, sqiCurrentLoc = currentLocIORef
192+
}
193+
194+
-- ExceptT is a monad transformer that allows us to compose these by binding over IO Either
195+
bsToSqiAndMeta :: DefaultChaincodeStub -> ByteString -> ExceptT Error IO (StateQueryIterator, Pb.QueryResponseMetadata)
196+
bsToSqiAndMeta ccs bs =
197+
let eeaQueryResponse = parse (decodeMessage (FieldNumber 1)) bs :: Either ParseError Pb.QueryResponse
198+
in
199+
case eeaQueryResponse of
200+
-- TODO: refactor out pattern matching, e.g. using >>= or <*>
201+
Left err -> ExceptT $ pure $ Left $ DecodeError err
202+
Right queryResponse ->
203+
let eeMetadata = parse (decodeMessage (FieldNumber 1)) (Pb.queryResponseMetadata queryResponse) :: Either ParseError Pb.QueryResponseMetadata
204+
in
205+
case eeMetadata of
206+
Left err -> ExceptT $ pure $ Left $ DecodeError err
207+
Right metadata -> (trace $ "Metadata from bsToSqiAndMeta: " ++ show metadata) ExceptT $ do
208+
-- queryResponse and currentLoc are IORefs as they need to be mutated
209+
-- as a part of the next() function
210+
queryResponseIORef <- newIORef queryResponse
211+
currentLocIORef <- newIORef 0
212+
pure $ Right (StateQueryIterator {
213+
sqiChaincodeStub = ccs
214+
, sqiChannelId = getChannelId ccs
215+
, sqiTxId = getTxId ccs
216+
, sqiResponse = queryResponseIORef
217+
, sqiCurrentLoc = currentLocIORef
218+
}, metadata)
219+
179220
nextResult :: StateQueryIterator -> IO (Either Error Pb.QueryResultBytes)
180221
nextResult sqi = do
181222
currentLoc <- readIORef $ sqiCurrentLoc sqi
@@ -187,10 +228,10 @@ nextResult sqi = do
187228
modifyIORef (sqiCurrentLoc sqi) (+ 1)
188229
if ((currentLoc + 1) == Prelude.length (Pb.queryResponseResults $ queryResponse)) then
189230
do
190-
fetchNextQueryResult sqi
231+
(trace "Fetching next query result from the peer") fetchNextQueryResult sqi
191232
queryResult
192233
else
193-
queryResult
234+
(trace "Returning local query result") queryResult
194235
else pure $ Left $ Error "Invalid iterator state"
195236

196237

0 commit comments

Comments
 (0)