gingersnap 0.2.0.0 → 0.2.0.1
raw patch · 3 files changed
+73/−16 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- README.md +54/−3
- gingersnap.cabal +1/−1
- src/Gingersnap/Core.hs +18/−12
README.md view
@@ -23,6 +23,7 @@ -- For our automatic JSON instance: import GHC.Generics (Generic) import Gingersnap.Core+import Network.HTTP.Types.Status (internalServerError500) import Snap.Core -- From the 'snap-server' package: import Snap.Http.Server (quickHttpServe)@@ -82,6 +83,7 @@ quickHttpServe $ route [ ("one", one ctx) , ("two", two ctx)+ , ("three", three ctx) ] ``` @@ -158,13 +160,62 @@ only roll back if there's an exception, and in contrast to manually beginning a transaction, which provides no check that we properly commit or rollback (e.g. we could forget to commit/rollback in one case among many in a complicated- statement)+ branching expression) +## Not everything's rspGood +What if we don't want to send a successful ok200 message? Or maybe we don't even+want to commit our transaction? These might come in handy then:++ rspBadCommit :: ApiErr ae => ae -> Rsp+ rspBadRollback :: ApiErr ae => ae -> Rsp++(`rspBad` is an alias for `rspBadRollback`)++`ApiErr` is a simple typeclass that ensures our error type can be converted to+a HTTP status code and JSON value.++We don't need to (and won't be) exploring the `ApiErr` typeclass here directly+, since we've got a good default instance with the `DefaultApiErr` type.++```haskell+three :: Ctx -> Snap ()+three ctx =+ inTransaction ctx $ \conn -> do+ [Only d] <- query_ conn " SELECT random () "+ pure $ if d >= (0.5 :: Double)+ then rspGood d+ else rspBad $ DefaultApiErr_Custom internalServerError500 $+ "number too small: "++ show d+```++ $ curl -v 'localhost:8000/three'+ [...]+ < HTTP/1.1 500 Internal Server Error+ [...]+ {"errorCode":6,"errorVals":[],"errorMessage":"number too small: 0.163489528931677"}+ $ curl -v 'localhost:8000/three'+ [...]+ < HTTP/1.1 200 OK+ [...]+ {"result":0.594665706157684}++If you'd like to write your own `ApiErr` instance (which you probably should if+you're building something "real":+ - The definition of "errResult" for "DefaultApiErr" might be a good guide+ for how to get a consistent JSON result.+ - You'll want to make that type the "CtxErrType" associated type for IsCtx++## Not everything's JSON++A JSON API is usually JSON but if you'd like to e.g. send a CSV or a file there+are functions like `rspGoodCSV` and (the most general) `rspGoodLBS`.++If you don't see a function you need it's easy to define your own.+ ## Other things to discover This tutorial is a work in progress, and these'll be the next concepts to be touched on: - - "Not everything's rspGood": rspBadRollback, ApiErr, and others- - reqObject and (.!) for consuming JSON+ - reqObject and (.!) for receiving JSON
gingersnap.cabal view
@@ -1,5 +1,5 @@ name: gingersnap-version: 0.2.0.0+version: 0.2.0.1 synopsis: snap-core + aeson + postgresql-simple = delicious description: JSON API idioms for snap-core, preventing DB connection leaks.
src/Gingersnap/Core.hs view
@@ -25,20 +25,28 @@ -- * DB Transactions , inTransaction+ , inTransactionMode , inTransaction_readOnly , inTransaction_override- , inTransactionMode , rspIsGood -- * IsCtx , IsCtx(..) + -- * JSON requests+ , reqObject+ , reqObject'+ , (.!)+ , (.!?)+ , ReqObject(..)+ -- * Errors+ , errorEarlyCode+ , ApiErr(..) , ErrResult(..) , DefaultApiErr(..) - , errorEarlyCode , ctxErr -- * Internals@@ -47,12 +55,6 @@ , RspPayload(..) , ShouldCommitOrRollback(..) - , reqObject- , reqObject'- , (.!)- , (.!?)- , ReqObject(..)- -- * Reexports, for convenience , Pool , createPool@@ -105,8 +107,8 @@ errResult :: apiErr -> ErrResult -- | The request object is missing a required key.- -- E.g. the request is {"first": "Tom"} but we need both a "first" and a- -- "last"+ -- E.g. the request is @{"first": "Tom"}@ but we need both a @"first"@ and a+ -- @"last"@ apiErr_missingRequestKey :: Text -> apiErr -- | We can't process the request because the request is malformed JSON or@@ -290,8 +292,8 @@ inTransaction ctx actionThatReturnsAnRsp = do inTransactionMode ctx PSQL.Serializable PSQL.ReadWrite actionThatReturnsAnRsp --- | An endpoint that uses 'inTransaction_readOnly' will keep responding even--- when the server is in read-only mode.+-- | Creates a read-only transaction and will keep responding even if the+-- server's in read-only mode. -- -- Note that you the programmer are asserting the DB queries are read-only. -- There's nothing in this library or in postgresql-simple which statically@@ -312,6 +314,10 @@ inTransaction_override ctx action = inTransaction_internal ctx PSQL.Serializable PSQL.ReadWrite action +-- | The most general version of 'inTransaction'.+-- +-- An endpoint that uses 'ReadOnly' will keep responding even when the server+-- is in read-only mode. inTransactionMode :: IsCtx ctx => ctx -> PSQL.IsolationLevel -> PSQL.ReadWriteMode -> (Connection -> IO Rsp) -> Snap () inTransactionMode ctx isolationLevel' readWriteMode' actionThatReturnsAResponse = do readOnlyMode <- liftIO $ ctxGetReadOnlyMode ctx