mirror of
https://github.com/unclechu/gRPC-haskell.git
synced 2024-11-22 19:19:42 +01:00
Client helpers, re-exports, and fixups (#70)
* Derive `Show` instance for `ClientSSLKeyCertPair` * Add a couple client helper functions & add additional exports * Add `grpc-haskell-core` target; add `c2hs` dep * Fix examples broken by #68 * rm `build-tools` and `include-dirs` directives from toplevel `.cabal` * More `ClientConfig` fixes * Ensure examples are built
This commit is contained in:
parent
c80269089c
commit
28288a17b7
9 changed files with 121 additions and 46 deletions
|
@ -97,7 +97,7 @@ serverOpts =
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = bracket startServer stopServer $ const $ withGRPC $ \grpc ->
|
main = bracket startServer stopServer $ const $ withGRPC $ \grpc ->
|
||||||
withClient grpc (ClientConfig "localhost" 50051 [] Nothing) $ \c -> do
|
withClient grpc (ClientConfig "localhost" 50051 [] Nothing Nothing) $ \c -> do
|
||||||
rmAdd <- clientRegisterMethodNormal c addMethod
|
rmAdd <- clientRegisterMethodNormal c addMethod
|
||||||
rmClientStream <- clientRegisterMethodClientStreaming c addClientStreamMethod
|
rmClientStream <- clientRegisterMethodClientStreaming c addClientStreamMethod
|
||||||
rmServerStream <- clientRegisterMethodServerStreaming c addServerStreamMethod
|
rmServerStream <- clientRegisterMethodServerStreaming c addServerStreamMethod
|
||||||
|
|
|
@ -37,8 +37,9 @@ data Client = Client {clientChannel :: C.Channel,
|
||||||
}
|
}
|
||||||
|
|
||||||
data ClientSSLKeyCertPair = ClientSSLKeyCertPair
|
data ClientSSLKeyCertPair = ClientSSLKeyCertPair
|
||||||
{clientPrivateKey :: FilePath,
|
{ clientPrivateKey :: FilePath,
|
||||||
clientCert :: FilePath}
|
clientCert :: FilePath
|
||||||
|
} deriving Show
|
||||||
|
|
||||||
-- | SSL configuration for the client. It's perfectly acceptable for both fields
|
-- | SSL configuration for the client. It's perfectly acceptable for both fields
|
||||||
-- to be 'Nothing', in which case default fallbacks will be used for the server
|
-- to be 'Nothing', in which case default fallbacks will be used for the server
|
||||||
|
|
|
@ -36,7 +36,7 @@ main = do
|
||||||
cfg = ClientConfig
|
cfg = ClientConfig
|
||||||
(Host . fromMaybe "localhost" . unHelpful $ bind)
|
(Host . fromMaybe "localhost" . unHelpful $ bind)
|
||||||
(Port . fromMaybe 50051 . unHelpful $ port)
|
(Port . fromMaybe 50051 . unHelpful $ port)
|
||||||
[] Nothing
|
[] Nothing Nothing
|
||||||
withGRPC $ \g -> withClient g cfg $ \c -> do
|
withGRPC $ \g -> withClient g cfg $ \c -> do
|
||||||
Echo{..} <- echoClient c
|
Echo{..} <- echoClient c
|
||||||
echoDoEcho (ClientNormalRequest rqt 5 mempty) >>= \case
|
echoDoEcho (ClientNormalRequest rqt 5 mempty) >>= \case
|
||||||
|
|
|
@ -110,7 +110,7 @@ doHelloBi c n = do
|
||||||
|
|
||||||
highlevelMain :: IO ()
|
highlevelMain :: IO ()
|
||||||
highlevelMain = withGRPC $ \g ->
|
highlevelMain = withGRPC $ \g ->
|
||||||
withClient g (ClientConfig "localhost" 50051 [] Nothing) $ \c -> do
|
withClient g (ClientConfig "localhost" 50051 [] Nothing Nothing) $ \c -> do
|
||||||
let n = 100000
|
let n = 100000
|
||||||
putStrLn "-------------- HelloSS --------------"
|
putStrLn "-------------- HelloSS --------------"
|
||||||
doHelloSS c n
|
doHelloSS c n
|
||||||
|
|
|
@ -11,6 +11,7 @@ clientConfig = ClientConfig { clientServerHost = "localhost"
|
||||||
, clientServerPort = 50051
|
, clientServerPort = 50051
|
||||||
, clientArgs = []
|
, clientArgs = []
|
||||||
, clientSSLConfig = Nothing
|
, clientSSLConfig = Nothing
|
||||||
|
, clientAuthority = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
|
|
|
@ -159,6 +159,7 @@ clientConfig = ClientConfig { clientServerHost = "localhost"
|
||||||
, clientServerPort = 50051
|
, clientServerPort = 50051
|
||||||
, clientArgs = []
|
, clientArgs = []
|
||||||
, clientSSLConfig = Nothing
|
, clientSSLConfig = Nothing
|
||||||
|
, clientAuthority = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
|
|
|
@ -30,6 +30,7 @@ library
|
||||||
, grpc-haskell-core
|
, grpc-haskell-core
|
||||||
|
|
||||||
, async ==2.1.*
|
, async ==2.1.*
|
||||||
|
, managed >= 1.0.5
|
||||||
|
|
||||||
exposed-modules:
|
exposed-modules:
|
||||||
Network.GRPC.HighLevel
|
Network.GRPC.HighLevel
|
||||||
|
@ -37,10 +38,8 @@ library
|
||||||
Network.GRPC.HighLevel.Server
|
Network.GRPC.HighLevel.Server
|
||||||
Network.GRPC.HighLevel.Server.Unregistered
|
Network.GRPC.HighLevel.Server.Unregistered
|
||||||
Network.GRPC.HighLevel.Client
|
Network.GRPC.HighLevel.Client
|
||||||
build-tools: c2hs
|
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
ghc-options: -Wall -fwarn-incomplete-patterns -fno-warn-unused-do-bind
|
ghc-options: -Wall -fwarn-incomplete-patterns -fno-warn-unused-do-bind
|
||||||
include-dirs: include
|
|
||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
default-extensions: CPP
|
default-extensions: CPP
|
||||||
CC-Options: -std=c99
|
CC-Options: -std=c99
|
||||||
|
|
33
release.nix
33
release.nix
|
@ -213,7 +213,10 @@ let
|
||||||
(haskellPackagesNew.callPackage ./nix/proto3-suite.nix {});
|
(haskellPackagesNew.callPackage ./nix/proto3-suite.nix {});
|
||||||
|
|
||||||
grpc-haskell-core =
|
grpc-haskell-core =
|
||||||
usesGRPC (haskellPackagesNew.callPackage ./core { });
|
usesGRPC
|
||||||
|
(pkgs.haskell.lib.overrideCabal
|
||||||
|
(haskellPackagesNew.callPackage ./core { })
|
||||||
|
(_: { buildDepends = [ haskellPackagesNew.c2hs ]; }));
|
||||||
|
|
||||||
grpc-haskell-no-tests =
|
grpc-haskell-no-tests =
|
||||||
usesGRPC
|
usesGRPC
|
||||||
|
@ -242,11 +245,18 @@ let
|
||||||
]);
|
]);
|
||||||
|
|
||||||
in rec {
|
in rec {
|
||||||
|
configureFlags = (oldDerivation.configureFlags or []) ++ [
|
||||||
|
"--flags=with-examples"
|
||||||
|
];
|
||||||
|
|
||||||
buildDepends = [
|
buildDepends = [
|
||||||
pkgs.makeWrapper
|
pkgs.makeWrapper
|
||||||
# Give our nix-shell its own cabal so we don't pick up one
|
# Give our nix-shell its own cabal so we don't pick up one
|
||||||
# from the user's environment by accident.
|
# from the user's environment by accident.
|
||||||
haskellPackagesNew.cabal-install
|
haskellPackagesNew.cabal-install
|
||||||
|
|
||||||
|
# And likewise for c2hs
|
||||||
|
haskellPackagesNew.c2hs
|
||||||
];
|
];
|
||||||
|
|
||||||
patches = [ tests/tests.patch ];
|
patches = [ tests/tests.patch ];
|
||||||
|
@ -299,11 +309,18 @@ let
|
||||||
pkgs = import nixpkgs { inherit config; };
|
pkgs = import nixpkgs { inherit config; };
|
||||||
|
|
||||||
in
|
in
|
||||||
{ grpc-haskell-linux = linuxPkgs.haskellPackages.grpc-haskell;
|
{
|
||||||
grpc-haskell-darwin = darwinPkgs.haskellPackages.grpc-haskell;
|
grpc-haskell-core-linux = linuxPkgs.haskellPackages.grpc-haskell-core;
|
||||||
grpc-haskell = pkgs.haskellPackages.grpc-haskell;
|
grpc-haskell-core-darwin = darwinPkgs.haskellPackages.grpc-haskell-core;
|
||||||
grpc-haskell-no-tests = pkgs.haskellPackages.grpc-haskell-no-tests;
|
grpc-haskell-core = pkgs.haskellPackages.grpc-haskell-core;
|
||||||
grpc-linux = linuxPkgs.grpc;
|
|
||||||
grpc-darwin = darwinPkgs.grpc;
|
grpc-haskell-linux = linuxPkgs.haskellPackages.grpc-haskell;
|
||||||
grpc = pkgs.grpc;
|
grpc-haskell-darwin = darwinPkgs.haskellPackages.grpc-haskell;
|
||||||
|
grpc-haskell = pkgs.haskellPackages.grpc-haskell;
|
||||||
|
grpc-haskell-no-tests = pkgs.haskellPackages.grpc-haskell-no-tests;
|
||||||
|
|
||||||
|
grpc-linux = linuxPkgs.grpc;
|
||||||
|
grpc-darwin = darwinPkgs.grpc;
|
||||||
|
|
||||||
|
grpc = pkgs.grpc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,44 +1,58 @@
|
||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE GADTs #-}
|
||||||
|
{-# LANGUAGE KindSignatures #-}
|
||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
{-# LANGUAGE KindSignatures #-}
|
|
||||||
{-# LANGUAGE DataKinds #-}
|
|
||||||
{-# LANGUAGE GADTs #-}
|
|
||||||
|
|
||||||
module Network.GRPC.HighLevel.Client
|
module Network.GRPC.HighLevel.Client
|
||||||
( RegisteredMethod
|
( ClientError(..)
|
||||||
, TimeoutSeconds
|
, ClientRegisterable(..)
|
||||||
, MetadataMap(..)
|
|
||||||
, StatusDetails(..)
|
|
||||||
, GRPCMethodType(..)
|
|
||||||
, StreamRecv
|
|
||||||
, StreamSend
|
|
||||||
, WritesDone
|
|
||||||
, LL.Client
|
|
||||||
, ServiceClient
|
|
||||||
, ClientError(..)
|
|
||||||
, ClientRequest(..)
|
, ClientRequest(..)
|
||||||
, ClientResult(..)
|
, ClientResult(..)
|
||||||
, ClientRegisterable(..)
|
, GRPCMethodType(..)
|
||||||
|
, MetadataMap(..)
|
||||||
|
, RegisteredMethod
|
||||||
|
, ServiceClient
|
||||||
|
, StatusCode(..)
|
||||||
|
, StatusDetails(..)
|
||||||
|
, StreamRecv
|
||||||
|
, StreamSend
|
||||||
|
, TimeoutSeconds
|
||||||
|
, WritesDone
|
||||||
|
|
||||||
|
, LL.Client
|
||||||
|
, LL.ClientConfig(..)
|
||||||
|
, LL.ClientSSLConfig(..)
|
||||||
|
, LL.ClientSSLKeyCertPair(..)
|
||||||
|
, LL.Host(..)
|
||||||
|
, LL.Port(..)
|
||||||
|
|
||||||
, clientRequest
|
, clientRequest
|
||||||
|
|
||||||
|
-- * Client utility functions
|
||||||
|
, acquireClient
|
||||||
|
, simplifyUnary
|
||||||
)
|
)
|
||||||
|
|
||||||
where
|
where
|
||||||
|
|
||||||
import qualified Network.GRPC.LowLevel.Client as LL
|
import Control.Monad.Managed (Managed, liftIO,
|
||||||
import qualified Network.GRPC.LowLevel.Call as LL
|
managed)
|
||||||
import Network.GRPC.LowLevel.CompletionQueue (TimeoutSeconds)
|
import qualified Data.ByteString.Lazy as BL
|
||||||
import Network.GRPC.LowLevel ( GRPCMethodType(..)
|
import Network.GRPC.HighLevel.Server (convertRecv,
|
||||||
, StatusCode(..)
|
convertSend)
|
||||||
, StatusDetails(..)
|
import Network.GRPC.LowLevel (GRPCIOError (..),
|
||||||
, MetadataMap(..)
|
GRPCMethodType (..),
|
||||||
, GRPCIOError(..)
|
MetadataMap (..),
|
||||||
, StreamRecv
|
StatusCode (..),
|
||||||
, StreamSend )
|
StatusDetails (..),
|
||||||
import Network.GRPC.LowLevel.Op (WritesDone)
|
StreamRecv, StreamSend)
|
||||||
import Network.GRPC.HighLevel.Server (convertRecv, convertSend)
|
import qualified Network.GRPC.LowLevel as LL
|
||||||
|
import Network.GRPC.LowLevel.CompletionQueue (TimeoutSeconds)
|
||||||
import Proto3.Suite (Message, toLazyByteString, fromByteString)
|
import Network.GRPC.LowLevel.Op (WritesDone)
|
||||||
import Proto3.Wire.Decode (ParseError)
|
import Proto3.Suite (Message, fromByteString,
|
||||||
import qualified Data.ByteString.Lazy as BL
|
toLazyByteString)
|
||||||
|
import Proto3.Wire.Decode (ParseError)
|
||||||
|
|
||||||
newtype RegisteredMethod (mt :: GRPCMethodType) request response
|
newtype RegisteredMethod (mt :: GRPCMethodType) request response
|
||||||
= RegisteredMethod (LL.RegisteredMethod mt)
|
= RegisteredMethod (LL.RegisteredMethod mt)
|
||||||
|
@ -121,3 +135,45 @@ clientRequest client (RegisteredMethod method) (ClientBiDiRequest timeout meta h
|
||||||
mkResponse (Left ioError_) = ClientErrorResponse (ClientIOError ioError_)
|
mkResponse (Left ioError_) = ClientErrorResponse (ClientIOError ioError_)
|
||||||
mkResponse (Right (meta_, rspCode_, details_)) =
|
mkResponse (Right (meta_, rspCode_, details_)) =
|
||||||
ClientBiDiResponse meta_ rspCode_ details_
|
ClientBiDiResponse meta_ rspCode_ details_
|
||||||
|
|
||||||
|
acquireClient
|
||||||
|
:: LL.ClientConfig
|
||||||
|
-- ^ The client configuration (host, port, SSL settings, etc)
|
||||||
|
-> (LL.Client -> IO (ServiceClient service))
|
||||||
|
-- ^ The client implementation (typically generated)
|
||||||
|
-> Managed (ServiceClient service)
|
||||||
|
acquireClient cfg impl = do
|
||||||
|
g <- managed LL.withGRPC
|
||||||
|
c <- managed (LL.withClient g cfg)
|
||||||
|
liftIO (impl c)
|
||||||
|
|
||||||
|
-- | A utility for simplifying gRPC client requests in common cases; you can use
|
||||||
|
-- this to avoid 'ClientRequest' and 'ClientResponse' pattern-matching
|
||||||
|
-- boilerplate at call sites.
|
||||||
|
simplifyUnary :: TimeoutSeconds
|
||||||
|
-- ^ RPC call timeout, in seconds
|
||||||
|
-> MetadataMap
|
||||||
|
-- ^ RPC call metadata
|
||||||
|
-> (ClientError -> IO (b, StatusDetails))
|
||||||
|
-- ^ Handler for client errors
|
||||||
|
-> (b -> StatusCode -> StatusDetails -> IO (b, StatusDetails))
|
||||||
|
-- ^ Handler for non-StatusOK responses
|
||||||
|
-> (ClientRequest 'Normal a b -> IO (ClientResult 'Normal b))
|
||||||
|
-- ^ gRPC function implementation (typically generated by gRPC-haskell)
|
||||||
|
-> (a -> IO (b, StatusDetails))
|
||||||
|
-- ^ The simplified happy-path (StatusOk) unary call action
|
||||||
|
simplifyUnary timeout meta clientError nonStatusOkError f x = do
|
||||||
|
|
||||||
|
let request = ClientNormalRequest x timeout meta
|
||||||
|
|
||||||
|
response <- f request
|
||||||
|
|
||||||
|
case response of
|
||||||
|
ClientNormalResponse y _ _ StatusOk details
|
||||||
|
-> pure (y, details)
|
||||||
|
|
||||||
|
ClientNormalResponse y _ _ code details
|
||||||
|
-> nonStatusOkError y code details
|
||||||
|
|
||||||
|
ClientErrorResponse err
|
||||||
|
-> clientError err
|
||||||
|
|
Loading…
Reference in a new issue