twirp (empty) → 0.2.0.0
raw patch · 7 files changed
+253/−0 lines, 7 filesdep +aesondep +basedep +bytestringsetup-changed
Dependencies added: aeson, base, bytestring, http-media, http-types, proto-lens, proto-lens-jsonpb, proto-lens-runtime, servant, text, twirp, wai
Files
- LICENSE +30/−0
- README.md +87/−0
- Setup.hs +2/−0
- src/Twirp.hs +25/−0
- src/Twirp/Middleware/Errors.hs +33/−0
- test/Spec.hs +2/−0
- twirp.cabal +74/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Timothy Clem (c) 2019++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Author name here nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,87 @@+# twirp-haskell++Very, very alpha implementation of Twirp service generation for Haskell.++This project provides a number of things:++1. A protoc plugin for generating a Haskell Twirp service (based on Servant) for Services defined in a proto file.+2. A haskell library, `twirp` for quickly serving up the generated application.++It requires the use of [proto-lens](https://github.com/google/proto-lens) to generate haskell datatypes from proto messages.++An example, end-to-end application is included in `app`.++## Requirements++1. Install `protoc` (e.g., `brew install protoc`)+2. Install the required protoc plugins:+ - `cabal install proto-lens-protoc`+ - `go get github.com/tclem/twirp-haskell/protoc-gen-twirp_haskell`+ - `go get github.com/tclem/proto-lens-jsonpb/protoc-gen-jsonpb_haskell`++## Usage++Use the protoc plugin to generate a twirp service and associated protobuf types from a proto file.++```+protoc -I=. --proto_path=./proto \+ --plugin=protoc-gen-haskell=`which proto-lens-protoc` --haskell_out=./app \+ --jsonpb_haskell_out=./app \+ --plugin=protoc-gen-twirp_haskell=./script/run-twirp_haskell --twirp_haskell_out=./app/Twirp/Example/Haberdasher \+ haberdasher.proto+```++The result is a couple of files that describe your service. First, here are the types that define a Servant API:+++``` haskell+-- Code generated by protoc-gen-twirp_haskell 0.1.0, DO NOT EDIT.+{-# LANGUAGE TypeOperators #-}+module Twirp.Example.Haberdasher.Haberdasher where++import Servant+import Twirp++import Proto.Haberdasher++-- This is an example set of twirp services.++type HaberdasherAPI+ = "twirp" :> "twirp.example.haberdasher.Haberdasher" :> HaberdasherService+ :<|> "twirp" :> "twirp.example.haberdasher.Health" :> HealthService++-- Haberdasher service makes hats for clients.+type HaberdasherService+ -- MakeHat produces a hat of mysterious, randomly-selected color!+ = "MakeHat" :> ReqBody [Protobuf, JSON] Size :> Post '[Protobuf, JSON] Hat+ -- Get paid+ :<|> "GetBill" :> ReqBody [Protobuf, JSON] Hat :> Post '[Protobuf, JSON] Bill++-- Health check service+type HealthService+ = "Check" :> ReqBody [Protobuf, JSON] Ping :> Post '[Protobuf, JSON] Pong+```++The datatypes are defined in [`Proto.Haberdasher`](app/Proto/Haberdasher.hs).++Plugging this into an existing warp/wai server is straightforward. See [`app/Main.hs`](app/Main.hs) for the full details:++``` haskell+type RequestID = String++type API+ = Header "X-Request-Id" RequestID+ :> HaberdasherAPI++main :: IO ()+main = run 8003 app++app :: Application+app = twirpErrorResponses apiApp++apiApp :: Application+apiApp = serve (Proxy :: Proxy API) server++server :: Server API+server _requestID = (makeHat :<|> getBill) :<|> checkHealth+```
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/Twirp.hs view
@@ -0,0 +1,25 @@+module Twirp+ ( Protobuf+ , module Middleware+ ) where++import Twirp.Middleware.Errors as Middleware++import Data.Bifunctor (first)+import Data.ByteString.Lazy.Char8 as BC+import Data.ByteString.Builder as BB+import Network.HTTP.Media ((//))+import Data.ProtoLens.Encoding as Proto+import Data.ProtoLens.Message (Message)+import Servant.API++data Protobuf++instance Accept Protobuf where+ contentType _ = "application" // "protobuf"++instance Message a => MimeRender Protobuf a where+ mimeRender _ = BB.toLazyByteString . Proto.buildMessage++instance Message a => MimeUnrender Protobuf a where+ mimeUnrender _ = first show . Proto.decodeMessage . BC.toStrict
+ src/Twirp/Middleware/Errors.hs view
@@ -0,0 +1,33 @@+{-# LANGUAGE DerivingVia, DeriveAnyClass, ScopedTypeVariables #-}+module Twirp.Middleware.Errors where++import Data.Aeson+import GHC.Generics+import Network.HTTP.Types+import Network.Wai++data TwirpError = TwirpError { code :: String, msg :: String }+ deriving stock (Eq, Show, Generic)+ deriving anyclass (ToJSON)++-- Rewrite error responses to use Twirp's error codes and JSON encoding.+-- See: https://github.com/twitchtv/twirp/blob/master/docs/errors.md+twirpErrorResponses :: Middleware+twirpErrorResponses = modifyResponse go+ where+ go response = let status = responseStatus response in+ case statusCode status of+ 400 -> responseLBS status headers (encode badRequest)+ 404 -> responseLBS status headers (encode notFound)+ 408 -> responseLBS status headers (encode canceled)+ 500 -> responseLBS status headers (encode serverError)+ 503 -> responseLBS status headers (encode unavailable)+ _ -> response++ headers = [(hContentType, "application/json; charset=utf-8")]++ badRequest = TwirpError "invalid_argument" "Bad Request"+ notFound = TwirpError "not_found" "Not found"+ canceled = TwirpError "canceled" "Request Timeout"+ unavailable = TwirpError "unavailable" "Service Unavailable"+ serverError = TwirpError "internal" "Internal Server Error"
+ test/Spec.hs view
@@ -0,0 +1,2 @@+main :: IO ()+main = putStrLn "Test suite not yet implemented"
+ twirp.cabal view
@@ -0,0 +1,74 @@+cabal-version: 2.4++name: twirp+version: 0.2.0.0+synopsis: Haskell twirp foundations+description: Please see the README on GitHub at <https://github.com/tclem/twirp-haskell#readme>+homepage: https://github.com/tclem/twirp-haskell#readme+bug-reports: https://github.com/tclem/twirp-haskell/issues+license: BSD-3-Clause+license-file: LICENSE+author: Timothy Clem+maintainer: timothy.clem@gmail.com+copyright: 2019 Timothy Clem+category: Web+build-type: Simple+extra-source-files: README.md++tested-with: GHC == 8.6.5++-- GHC extensions shared between targets+common haskell+ default-language: Haskell2010+ default-extensions: DataKinds+ , DeriveFoldable+ , DeriveFunctor+ , DeriveGeneric+ , DeriveTraversable+ , FlexibleContexts+ , FlexibleInstances+ , MultiParamTypeClasses+ , OverloadedStrings+ , RecordWildCards+ , StandaloneDeriving+ , StrictData+ , TypeApplications+ ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints++common dependencies+ build-depends:+ base >=4.7 && <5+ , aeson ^>= 1.4.5.0+ , bytestring >= 0.10.8+ , http-media >= 0.8.0.0+ , http-types >= 0.12.3+ , proto-lens >= 0.5.0.0+ , proto-lens-jsonpb >= 0.2.0.0+ , proto-lens-runtime >= 0.5 && <0.7+ , servant ^>= 0.16.2+ , text ^>= 1.2.4.0+ , wai ^>= 3.2.2.1++library+ import: haskell, dependencies+ exposed-modules:+ Twirp+ Twirp.Middleware.Errors+ hs-source-dirs:+ src+ ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints+ build-depends:++test-suite twirp-test+ import: haskell, dependencies+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ hs-source-dirs:+ test+ ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N+ build-depends:+ twirp++source-repository head+ type: git+ location: https://github.com/tclem/twirp-haskell