diff --git a/.travis.yml b/.travis.yml index 7d1cb600..4f58b019 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,14 +55,14 @@ jobs: script: - echo "Test libraries" # this image is needed for the sdk IAVLSpec - - docker run --name iavl -p 8090:8090 -p 8091:8091 -d foamspace/iavl:latest /iavlserver -db-name "test" -datadir "." -grpc-endpoint "0.0.0.0:8090" -gateway-endpoint "0.0.0.0:8091" + - docker run --name iavl -p 8090:8090 -p 8091:8091 -d foamspace/iavl:grpc -db-name "test" -datadir "." -grpc-endpoint "0.0.0.0:8090" -gateway-endpoint "0.0.0.0:8091" - make test-libraries - docker rm -f iavl - echo "Test Libraries with Nix" - echo "This stage is currently disabled as Nix doesn't seem to play well with libsecp256k1" - echo "Test IAVL Client" - - docker run --name iavl -p 8090:8090 -p 8091:8091 -d foamspace/iavl:latest /iavlserver -db-name "test" -datadir "." -grpc-endpoint "0.0.0.0:8090" -gateway-endpoint "0.0.0.0:8091" + - docker run --name iavl -p 8090:8090 -p 8091:8091 -d foamspace/iavl:grpc -db-name "test" -datadir "." -grpc-endpoint "0.0.0.0:8090" -gateway-endpoint "0.0.0.0:8091" - make test-iavl-client - docker rm -f iavl diff --git a/hs-abci-docs/nameservice/docker-compose.yaml b/hs-abci-docs/nameservice/docker-compose.yaml index ee4d3891..aa92a5e6 100644 --- a/hs-abci-docs/nameservice/docker-compose.yaml +++ b/hs-abci-docs/nameservice/docker-compose.yaml @@ -57,8 +57,8 @@ services: - "9200" - "5601" iavl: - image: foamspace/iavl:latest - command: /iavlserver -db-name "test" -datadir "." -grpc-endpoint "0.0.0.0:8090" -gateway-endpoint "0.0.0.0:8091" + image: foamspace/iavl:grpc + command: ["-db-name", "test", "-datadir", ".", " -grpc-endpoint", "0.0.0.0:8090", "-gateway-endpoint", "0.0.0.0:8091"] ports: - "8090-8091:8091-8091" expose: diff --git a/hs-abci-docs/simple-storage/docker-compose.yaml b/hs-abci-docs/simple-storage/docker-compose.yaml index 9750c6f3..37574f69 100644 --- a/hs-abci-docs/simple-storage/docker-compose.yaml +++ b/hs-abci-docs/simple-storage/docker-compose.yaml @@ -30,8 +30,8 @@ services: expose: - "26658" iavl: - image: foamspace/iavl:latest - command: /iavlserver -db-name "test" -datadir "." -grpc-endpoint "0.0.0.0:8090" -gateway-endpoint "0.0.0.0:8091" + image: foamspace/iavl:grpc + command: ["-db-name", "test", "-datadir", ".", " -grpc-endpoint", "0.0.0.0:8090", "-gateway-endpoint", "0.0.0.0:8091"] ports: - "8090:8090" - "8091:8091" diff --git a/hs-iavl-client/package.yaml b/hs-iavl-client/package.yaml index 72b9e23c..e8fe8f2d 100644 --- a/hs-iavl-client/package.yaml +++ b/hs-iavl-client/package.yaml @@ -59,6 +59,8 @@ library: generated-exposed-modules: - Proto.Iavl.Api - Proto.Iavl.Api_Fields + - Proto.Iavl.Proof + - Proto.Iavl.Proof_Fields - Proto.Google.Api.Http - Proto.Google.Api.Http_Fields - Proto.Google.Protobuf.Empty diff --git a/hs-iavl-client/protos/iavl/api.proto b/hs-iavl-client/protos/iavl/api.proto index 2daec57d..7df53d7b 100644 --- a/hs-iavl-client/protos/iavl/api.proto +++ b/hs-iavl-client/protos/iavl/api.proto @@ -1,8 +1,12 @@ syntax = "proto3"; -package proto; +package iavl; + +option go_package = "proto/proto"; -import "google/protobuf/empty.proto"; import "google/api/annotations.proto"; +import "google/protobuf/empty.proto"; +import "iavl/proof.proto"; + // ---------------------------------------------------------------------------- // gRPC service @@ -14,7 +18,13 @@ service IAVLService { // has a given key at a specific tree version. rpc Has(HasRequest) returns (HasResponse) { option (google.api.http) = { - get: "/v1/tree/{version}/has" + get: "/v1/has" + }; + } + + rpc HasVersioned(HasVersionedRequest) returns (HasResponse) { + option (google.api.http) = { + get: "/v1/has_versioned" }; } @@ -22,7 +32,7 @@ service IAVLService { // key based on the current state (version) of the tree. rpc Get(GetRequest) returns (GetResponse) { option (google.api.http) = { - get: "/v1/tree/get" + get: "/v1/get" }; } @@ -31,15 +41,15 @@ service IAVLService { // verifiable Merkle proof. rpc GetWithProof(GetRequest) returns (GetWithProofResponse) { option (google.api.http) = { - get: "/v1/tree/get_with_proof" + get: "/v1/get_with_proof" }; } - + // GetVersioned returns a result containing the IAVL tree version and value // for a given key at a specific tree version. rpc GetVersioned(GetVersionedRequest) returns (GetResponse) { option (google.api.http) = { - get: "/v1/tree/{version}/get_versioned" + get: "/v1/{version}/get_versioned" }; } @@ -48,7 +58,7 @@ service IAVLService { // proof. rpc GetVersionedWithProof(GetVersionedRequest) returns (GetWithProofResponse) { option (google.api.http) = { - get: "/v1/tree/{version}/get_versioned_with_proof" + get: "/v1/{version}/get_versioned_with_proof" }; } @@ -56,7 +66,7 @@ service IAVLService { // based on the current state (version) of the tree. rpc Set(SetRequest) returns (SetResponse) { option (google.api.http) = { - post: "/v1/tree/set" + post: "/v1/set" body: "*" }; } @@ -65,7 +75,7 @@ service IAVLService { // based on the current state (version) of the tree. rpc Remove(RemoveRequest) returns (RemoveResponse) { option (google.api.http) = { - post: "/v1/tree/remove" + post: "/v1/remove" body: "*" }; } @@ -75,7 +85,7 @@ service IAVLService { // new version number. rpc SaveVersion(google.protobuf.Empty) returns (SaveVersionResponse) { option (google.api.http) = { - post: "/v1/tree/save_version" + post: "/v1/save_version" body: "*" }; } @@ -85,7 +95,7 @@ service IAVLService { // hash of the versioned tree that was deleted. rpc DeleteVersion(DeleteVersionRequest) returns (DeleteVersionResponse) { option (google.api.http) = { - post: "/v1/tree/delete_version" + post: "/v1/delete_version" body: "*" }; } @@ -93,22 +103,22 @@ service IAVLService { // Version returns the IAVL tree version based on the current state. rpc Version(google.protobuf.Empty) returns (VersionResponse) { option (google.api.http) = { - get: "/v1/tree/version" + get: "/v1/version" }; } // Hash returns the IAVL tree root hash based on the current state. rpc Hash(google.protobuf.Empty) returns (HashResponse) { option (google.api.http) = { - get: "/v1/tree/hash" + get: "/v1/hash" }; } - + // VersionExists returns a result containing a boolean on whether or not a given // version exists in the IAVL tree. rpc VersionExists(VersionExistsRequest) returns (VersionExistsResponse) { option (google.api.http) = { - get: "/v1/tree/version_exists" + get: "/v1/version_exists" }; } @@ -116,7 +126,7 @@ service IAVLService { // invalid. rpc Verify(VerifyRequest) returns (google.protobuf.Empty) { option (google.api.http) = { - get: "/v1/tree/range_proof/verify" + get: "/v1/range_proof/verify" }; } @@ -124,7 +134,7 @@ service IAVLService { // an error if the proof or key is invalid. rpc VerifyItem(VerifyItemRequest) returns (google.protobuf.Empty) { option (google.api.http) = { - get: "/v1/tree/range_proof/verify_item" + get: "/v1/range_proof/verify_item" }; } @@ -132,7 +142,7 @@ service IAVLService { // returning an error if the proof or key is invalid. rpc VerifyAbsence(VerifyAbsenceRequest) returns (google.protobuf.Empty) { option (google.api.http) = { - get: "/v1/tree/range_proof/verify_absence" + get: "/v1/range_proof/verify_absence" }; } @@ -140,10 +150,55 @@ service IAVLService { // any unsaved modifications. rpc Rollback(google.protobuf.Empty) returns (google.protobuf.Empty) { option (google.api.http) = { - post: "/v1/tree/rollback" + post: "/v1/rollback" body: "*" }; } + + // Returns the committed versions + rpc GetAvailableVersions(google.protobuf.Empty) returns (GetAvailableVersionsResponse) { + option (google.api.http) = { + get: "/v1/available_versions" + }; + } + + // Load the most recent version + rpc Load(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/v1/load" + body: "*" + }; + } + + // Load a specific version + rpc LoadVersion(LoadVersionRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/v1/load_version" + body: "*" + }; + } + + // Load a specific version and delete all the more recent versions + rpc LoadVersionForOverwriting(LoadVersionForOverwritingRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/v1/load_version_for_overwriting" + body: "*" + }; + } + + // Get the number of leaves in the tree + rpc Size(google.protobuf.Empty) returns (SizeResponse) { + option (google.api.http) = { + get: "/v1/size" + }; + } + + rpc List(ListElementsRequest) returns (stream ListElementsResponse) { + option (google.api.http) = { + get: "/v1/list" + }; + } + } // ---------------------------------------------------------------------------- @@ -151,6 +206,10 @@ service IAVLService { // ---------------------------------------------------------------------------- message HasRequest { + bytes key = 1; +} + +message HasVersionedRequest { int64 version = 1; bytes key = 2; } @@ -183,22 +242,37 @@ message VersionExistsRequest { message VerifyRequest { bytes root_hash = 1; - RangeProof proof = 2; + iavl.RangeProof proof = 2; } message VerifyItemRequest { bytes root_hash = 1; - RangeProof proof = 2; + iavl.RangeProof proof = 2; bytes key = 3; bytes value = 4; } message VerifyAbsenceRequest { bytes root_hash = 1; - RangeProof proof = 2; + iavl.RangeProof proof = 2; bytes key = 3; } +message LoadVersionRequest { + int64 version = 1; +} + +message LoadVersionForOverwritingRequest { + int64 version = 1; +} + +message ListElementsRequest { + bytes from_key = 1; + bytes to_key = 2; + bool ascending = 3; +} + + // ---------------------------------------------------------------------------- // Response types // ---------------------------------------------------------------------------- @@ -245,30 +319,19 @@ message VersionExistsResponse { message GetWithProofResponse { bytes value = 1; - RangeProof proof = 2; + iavl.RangeProof proof = 2; } -message ProofInnerNode { - int32 height = 1; - int64 size = 2; - int64 version = 3; - bytes left = 4; - bytes right = 5; +message GetAvailableVersionsResponse { + repeated int64 versions = 1; } -message ProofLeafNode { - bytes key = 1; - bytes value_hash = 2; - int64 version = 3; -} -message PathToLeaf { - repeated ProofInnerNode nodes = 1; +message SizeResponse { + int64 size = 1; } -message RangeProof { +message ListElementsResponse { bytes key = 1; - PathToLeaf left_path = 2; - repeated PathToLeaf inner_nodes = 3; - repeated ProofLeafNode leaves = 4; + bytes value = 2; } diff --git a/hs-iavl-client/protos/iavl/proof.proto b/hs-iavl-client/protos/iavl/proof.proto new file mode 100644 index 00000000..0d199546 --- /dev/null +++ b/hs-iavl-client/protos/iavl/proof.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; +package iavl; + +option go_package = "proto/iavl"; + +// ValueOp is a Protobuf representation of iavl.ValueOp. +message ValueOp { + RangeProof proof = 1; +} + +// AbsenceOp is a Protobuf representation of iavl.AbsenceOp. +message AbsenceOp { + RangeProof proof = 1; +} + +// RangeProof is a Protobuf representation of iavl.RangeProof. +message RangeProof { + repeated ProofInnerNode left_path = 1; + repeated PathToLeaf inner_nodes = 2; + repeated ProofLeafNode leaves = 3; +} + +// PathToLeaf is a Protobuf representation of iavl.PathToLeaf. +message PathToLeaf { + repeated ProofInnerNode inners = 1; +} + +// ProofInnerNode is a Protobuf representation of iavl.ProofInnerNode. +message ProofInnerNode { + sint32 height = 1; + int64 size = 2; + int64 version = 3; + bytes left = 4; + bytes right = 5; +} + +// ProofLeafNode is a Protobuf representation of iavl.ProofInnerNode. +message ProofLeafNode { + bytes key = 1; + bytes value_hash = 2; + int64 version = 3; +} diff --git a/hs-iavl-client/src/Database/IAVL/RPC.hs b/hs-iavl-client/src/Database/IAVL/RPC.hs index 052f16af..f5053dc3 100644 --- a/hs-iavl-client/src/Database/IAVL/RPC.hs +++ b/hs-iavl-client/src/Database/IAVL/RPC.hs @@ -149,3 +149,12 @@ has -> Api.HasRequest -> ClientIO (Either TooMuchConcurrency (RawReply Api.HasResponse)) has = rawUnary (RPC :: RPC Api.IAVLService "has") + +-------------------------------------------------------------------------------- +-- | has_versioned +-------------------------------------------------------------------------------- +hasVersioned + :: GrpcClient + -> Api.HasVersionedRequest + -> ClientIO (Either TooMuchConcurrency (RawReply Api.HasResponse)) +hasVersioned = rawUnary (RPC :: RPC Api.IAVLService "hasVersioned") diff --git a/hs-iavl-client/test/Database/IAVL/RPCSpec.hs b/hs-iavl-client/test/Database/IAVL/RPCSpec.hs index 15990e07..daf6af9e 100644 --- a/hs-iavl-client/test/Database/IAVL/RPCSpec.hs +++ b/hs-iavl-client/test/Database/IAVL/RPCSpec.hs @@ -1,6 +1,6 @@ module Database.IAVL.RPCSpec (spec) where -import Control.Lens ((&), (.~), (^.)) +import Control.Lens ((&), (.~), (^.), (^?), _Left, _Right) import Control.Monad (void) import Data.ProtoLens.Message (defMessage) import Database.IAVL.RPC @@ -17,76 +17,77 @@ spec = beforeAll (initGrpcClient $ GrpcConfig "0.0.0.0" 8090) $ do testValue = "test-value" testKey2 = "test-key-2" testValue2 = "test-value-2" + emptyHash = "\227\176\196B\152\252\FS\DC4\154\251\244\200\153o\185$'\174A\228d\155\147L\164\149\153\ESCxR\184U" rootWithTestKey = "`\241\167\226\242u\194\221L!\200\202\159\232\131\\\ESC\ESC\158wZ\164yw\248\194jW\145:\206\209" describe "IAVL RPC calls" $ do it "should call `hash` RPC method on empty Iavl store and get empty root hash" $ \gc -> do res <- runGrpc $ hash gc - res ^. Api.rootHash `shouldBe` "" + res ^? _Right . Api.rootHash `shouldBe` Just emptyHash it "should call `set` RPC method and get false as result since it does not already exist" $ \gc -> do let setReq = defMessage & Api.key .~ testKey & Api.value .~ testValue res <- runGrpc $ set gc setReq - res ^. Api.result `shouldBe` False + res ^? _Right . Api.result `shouldBe` Just False it "should call `get` RPC method and get the expected value" $ \gc -> do let getReq = defMessage & Api.key .~ testKey res <- runGrpc $ get gc getReq - res ^. Api.value `shouldBe` testValue - res ^. Api.index `shouldBe` 0 + res ^? _Right . Api.value `shouldBe` Just testValue + res ^? _Right . Api.index `shouldBe` Just 0 it "should call `get` RPC method on a newly set key and get the expected value and index" $ \gc -> do let setReq = defMessage & Api.key .~ testKey2 & Api.value .~ testValue2 sres <- runGrpc $ set gc setReq - sres ^. Api.result `shouldBe` False + sres ^? _Right . Api.result `shouldBe` Just False let getReq = defMessage & Api.key .~ testKey2 gres <- runGrpc $ get gc getReq - gres ^. Api.value `shouldBe` testValue2 - gres ^. Api.index `shouldBe` 1 + gres ^? _Right . Api.value `shouldBe` Just testValue2 + gres ^? _Right . Api.index `shouldBe` Just 1 it "should call `get` RPC method and fail to get the expected value" $ \gc -> do let getReq = defMessage & Api.key .~ "non-existing-key" res <- runGrpc $ get gc getReq - res ^. Api.value `shouldBe` "" + res ^? _Left `shouldBe` Just "the key requested does not exist" it "should call `saveVersion` RPC method and get the latest hash" $ \gc -> do _ <- runGrpc $ saveVersion gc res <- runGrpc $ hash gc - res ^. Api.rootHash `shouldBe` rootWithTestKey + res ^? _Right . Api.rootHash `shouldBe` Just rootWithTestKey it "should call `getWithProof` RPC method and get value from earlier version" $ \gc -> do let getReq = defMessage & Api.key .~ testKey getRes <- runGrpc $ getWithProof gc getReq - getRes ^. Api.value `shouldBe` testValue + getRes ^? _Right . Api.value `shouldBe` Just testValue it "should call `getVersioned` RPC method and get value from earlier version" $ \gc -> do let newVal = "new-value" setReq = defMessage & Api.key .~ testKey & Api.value .~ newVal res <- runGrpc $ set gc setReq - res ^. Api.result `shouldBe` True + res ^? _Right . Api.result `shouldBe` Just True _ <- runGrpc $ saveVersion gc let getReq = defMessage & Api.key .~ testKey & Api.version .~ 1 getRes <- runGrpc $ getVersioned gc getReq - getRes ^. Api.value `shouldBe` testValue + getRes ^? _Right . Api.value `shouldBe` Just testValue it "should call `getVersionedWithProof` RPC method and get value from earlier version" $ \gc -> do let newVal = "new-value-2" setReq = defMessage & Api.key .~ testKey & Api.value .~ newVal res <- runGrpc $ set gc setReq - res ^. Api.result `shouldBe` True + res ^? _Right . Api.result `shouldBe` Just True _ <- runGrpc $ saveVersion gc let getReq = defMessage & Api.key .~ testKey & Api.version .~ 1 getRes <- runGrpc $ getVersionedWithProof gc getReq - getRes ^. Api.value `shouldBe` testValue + getRes ^? _Right . Api.value `shouldBe` Just testValue it "should call `remove` RPC method" $ \gc -> do let key = "key-to-remove" @@ -94,30 +95,40 @@ spec = beforeAll (initGrpcClient $ GrpcConfig "0.0.0.0" 8090) $ do setReq = defMessage & Api.key .~ key & Api.value .~ value res <- runGrpc $ set gc setReq - res ^. Api.result `shouldBe` False + res ^? _Right . Api.result `shouldBe` Just False _ <- runGrpc $ saveVersion gc let removeReq = defMessage & Api.key .~ key removeRes <- runGrpc $ remove gc removeReq - removeRes ^. Api.value `shouldBe` value + removeRes ^? _Right . Api.value `shouldBe` Just value let getReq = defMessage & Api.key .~ key getRes <- runGrpc $ get gc getReq - getRes ^. Api.value `shouldBe` "" + getRes ^? _Left `shouldBe` Just "the key requested does not exist" it "should call `verify` RPC method" $ \gc -> do let getReq = defMessage & Api.key .~ testKey & Api.version .~ 1 - getRes <- runGrpc $ getVersionedWithProof gc getReq + egetRes <- runGrpc $ getVersionedWithProof gc getReq + + getRes <- case egetRes of + Right a -> pure a + Left err -> fail err + let verifyReq = defMessage & Api.rootHash .~ rootWithTestKey & Api.proof .~ (getRes ^. Api.proof) void . runGrpc $ verify gc verifyReq + it "should call `verifyItem` RPC method" $ \gc -> do let getReq = defMessage & Api.key .~ testKey & Api.version .~ 1 - getRes <- runGrpc $ getVersionedWithProof gc getReq + egetRes <- runGrpc $ getVersionedWithProof gc getReq + + getRes <- case egetRes of + Right a -> pure a + Left err -> fail err let verifyReq = defMessage & Api.rootHash .~ rootWithTestKey & Api.proof .~ (getRes ^. Api.proof) @@ -125,10 +136,16 @@ spec = beforeAll (initGrpcClient $ GrpcConfig "0.0.0.0" 8090) $ do & Api.value .~ testValue void . runGrpc $ verifyItem gc verifyReq + it "should call `verifyAbsence` RPC method" $ \gc -> do let getReq = defMessage & Api.key .~ testKey & Api.version .~ 1 - getRes <- runGrpc $ getVersionedWithProof gc getReq + egetRes <- runGrpc $ getVersionedWithProof gc getReq + + getRes <- case egetRes of + Right a -> pure a + Left err -> fail err + let verifyAbReq = defMessage & Api.rootHash .~ rootWithTestKey & Api.proof .~ (getRes ^. Api.proof) @@ -138,45 +155,45 @@ spec = beforeAll (initGrpcClient $ GrpcConfig "0.0.0.0" 8090) $ do it "should call `versionExists` RPC method on existing version" $ \gc -> do let verExistsReq = defMessage & Api.version .~ 1 verExistsRes <- runGrpc $ versionExists gc verExistsReq - verExistsRes ^. Api.result `shouldBe` True + verExistsRes ^? _Right . Api.result `shouldBe` Just True it "should call `version` RPC method and get latest version number" $ \gc -> do verRes <- runGrpc $ version gc - verRes ^. Api.version `shouldBe` 4 + verRes ^? _Right . Api.version `shouldBe` Just 4 it "should call `versionExists` RPC method on non-existing version" $ \gc -> do let verExistsReq = defMessage & Api.version .~ 25 verExistsRes <- runGrpc $ versionExists gc verExistsReq - verExistsRes ^. Api.result `shouldBe` False + verExistsRes ^? _Right . Api.result `shouldBe` Just False it "should call `has` RPC method" $ \gc -> do let hasReq = defMessage & Api.key .~ testKey & Api.version .~ 1 - hasRes <- runGrpc $ has gc hasReq - hasRes ^. Api.result `shouldBe` True + hasRes <- runGrpc $ hasVersioned gc hasReq + hasRes ^? _Right . Api.result `shouldBe` Just True it "should call `has` RPC method and fail" $ \gc -> do let hasReq = defMessage & Api.key .~ "non-existing-key" & Api.version .~ 1 - hasRes <- runGrpc $ has gc hasReq - hasRes ^. Api.result `shouldBe` False + hasRes <- runGrpc $ hasVersioned gc hasReq + hasRes ^? _Right . Api.result `shouldBe` Just False it "should call `deleteVersion` RPC method and get False for non-existing version" $ \gc -> do let delVerReq = defMessage & Api.version .~ 1 let verExistsReq = defMessage & Api.version .~ 1 delVerRes <- runGrpc $ deleteVersion gc delVerReq verExistsRes <- runGrpc $ versionExists gc verExistsReq - delVerRes ^. Api.rootHash `shouldBe` rootWithTestKey - verExistsRes ^. Api.result `shouldBe` False + delVerRes ^? _Right . Api.rootHash `shouldBe` Just rootWithTestKey + verExistsRes ^? _Right . Api.result `shouldBe` Just False it "should call `rollback` RPC method without changing the version" $ \gc -> do verRes <- runGrpc $ version gc - let oldVersion = verRes ^. Api.version + let oldVersion = verRes ^? _Right . Api.version _ <- runGrpc $ rollback gc verRes' <- runGrpc $ version gc - let newVersion = verRes' ^. Api.version + let newVersion = verRes' ^? _Right . Api.version oldVersion `shouldBe` newVersion @@ -186,17 +203,17 @@ spec = beforeAll (initGrpcClient $ GrpcConfig "0.0.0.0" 8090) $ do setReq = defMessage & Api.key .~ key & Api.value .~ value res <- runGrpc $ set gc setReq - res ^. Api.result `shouldBe` False + res ^? _Right . Api.result `shouldBe` Just False let getReq = defMessage & Api.key .~ key getRes <- runGrpc $ get gc getReq - getRes ^. Api.value `shouldBe` value - getRes ^. Api.index `shouldBe` 1 + getRes ^? _Right . Api.value `shouldBe` Just value + getRes ^? _Right . Api.index `shouldBe` Just 1 _ <- runGrpc $ rollback gc getRes' <- runGrpc $ get gc getReq - getRes' ^. Api.value `shouldBe` "" + getRes' ^? _Left `shouldBe` Just "the key requested does not exist" it "should call `has` RPC method on current working tree" $ \gc -> do let key = "key-has" @@ -204,23 +221,24 @@ spec = beforeAll (initGrpcClient $ GrpcConfig "0.0.0.0" 8090) $ do setReq = defMessage & Api.key .~ key & Api.value .~ value res <- runGrpc $ set gc setReq - res ^. Api.result `shouldBe` False - verRes <- runGrpc $ version gc + res ^? _Right . Api.result `shouldBe` Just False + everRes <- runGrpc $ version gc + verRes <- case everRes of + Right a -> pure a + Left e -> fail e + let currentVersion = verRes ^. Api.version hasReq = defMessage & Api.key .~ key & Api.version .~ currentVersion print $ "The current version is " <> show currentVersion - hasRes <- runGrpc $ has gc hasReq - hasRes ^. Api.result `shouldBe` False + hasRes <- runGrpc $ hasVersioned gc hasReq + hasRes ^? _Right . Api.result `shouldBe` Just False -runGrpc :: ClientIO (Either TooMuchConcurrency (RawReply a)) -> IO a +runGrpc :: ClientIO (Either TooMuchConcurrency (RawReply a)) -> IO (Either String a) runGrpc f = runClientIO f >>= \case - Right (Right (Right (_, _, Right res))) -> pure res - Right (Right (Right (_, _, Left err))) -> error ("Error running grpc call: " <> show err) - Right (Right (Left err)) -> error ("Error running grpc call: " <> show err) - Right (Left err) -> error ("Error running grpc call: " <> show err) - Left err -> error ("Error running grpc call: " <> show err) - - - + Right (Right (Right (_, _, Right res))) -> pure $ Right res + Right (Right (Right (_, _, Left err))) -> pure $ Left err + Right (Right (Left err)) -> error ("Error running grpc call (2): " <> show err) + Right (Left err) -> error ("Error running grpc call (3): " <> show err) + Left err -> error ("Error running grpc call (4): " <> show err)