packages feed

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 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