servant-response (empty) → 0.1
raw patch · 5 files changed
+183/−0 lines, 5 filesdep +aesondep +basedep +http-typessetup-changed
Dependencies added: aeson, base, http-types, text
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- servant-response.cabal +24/−0
- src/Servant/Response.hs +48/−0
- src/Servant/Response/Prelude.hs +79/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014, Alp Mestanogullari++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 Alp Mestanogullari 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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ servant-response.cabal view
@@ -0,0 +1,24 @@+name: servant-response+version: 0.1+synopsis: Machinery to express how servant should turn results of database operations into proper JSON-encodable response types+description: Machinery to express how servant should turn results of database operations into proper JSON-encodable response types+homepage: http://github.com/zalora/servant+license: BSD3+license-file: LICENSE+author: Alp Mestanogullari+maintainer: alp@zalora.com+copyright: 2014 Zalora SEA+category: Web+build-type: Simple +cabal-version: >=1.10++library+ exposed-modules: Servant.Response, Servant.Response.Prelude + build-depends:+ base >=4.5 && <5+ , aeson >= 0.7+ , http-types >= 0.8+ , text >= 1.0+ hs-source-dirs: src+ default-language: Haskell2010+ ghc-options: -Wall
+ src/Servant/Response.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE FunctionalDependencies,+ MultiParamTypeClasses #-}+{- |+Module : Servant.Response+Copyright : (c) Zalora SEA 2014+License : BSD3+Maintainer : Alp Mestanogullari <alp@zalora.com>+Stability : experimental++This module contains a generic 'Response' class for tying the result+of some \"database operation\" to some response type of yours,+or to the standard ones from "Servant.Scotty.Prelude" for example.++-}+module Servant.Response+ ( -- * The 'Response' class+ Response(..)+ , -- * Useful for defining your instances+ module Network.HTTP.Types.Status+ ) where++import Data.Aeson hiding (json)+import Network.HTTP.Types.Status++-- | A class that ties return types of your database operations+-- and the output that will be generated to communicate+-- the result.+--+-- * The first type, @resp@, is the response type that will be encoded+-- in JSON and sent as the response body.+--+-- * The second type, @result@, is the result type of your \"database\"+-- or \"context\" operation.+--+-- For example, if you're adding an item, and if you're using+-- postgresql-simple, you'll probably want to use the+-- 'Response' instances defined in the servant-postgresql package,+-- in the @Servant.PostgreSQL.Prelude@ module.+--+-- It lets you specify, given a value of your result, if no+-- exception is thrown, what response should be sent as JSON+-- to the client along with what HTTP status.+--+-- There's a functional dependency at play: the result type+-- of a database operation determines the representation that'll be+-- picked for generating the json output.+class ToJSON resp => Response resp result | result -> resp where+ toResponse :: result -> (resp, Status)
+ src/Servant/Response/Prelude.hs view
@@ -0,0 +1,79 @@+{-# LANGUAGE DeriveGeneric, MultiParamTypeClasses, OverloadedStrings #-}+module Servant.Response.Prelude+ ( -- * 'Response' class+ module Servant.Response+ , -- * Useful reusable types and instances+ UpdateResponse(..)+ , LookupResponse(..)+ ) where++import Data.Aeson+import Data.Text+import GHC.Generics+import Network.HTTP.Types.Status+import Servant.Response++-- | A generic response type for any "effectul" operation+-- on a 'Resource', like adding, updating or deleting an item.+--+-- It simply holds a 'Bool' that indicates whether the operation+-- was successful or not, and if it wasn't, it'll embed a text+-- describing what went wrong and is meant to be tagged+-- (see the @o@ type parameter) with the operation it's associated to.+--+-- This lets us have different instances for the standard+-- /Add/ and /Update/ operations for example, where the former+-- should respond with HTTP status code /201/ if the entry was created,+-- whereas the latter should just use status code /200/.+--+-- You can of course skip this one and use a more appropriate+-- for your particular application.+data UpdateResponse o =+ UpdateResponse { success :: !Bool + , msg :: !Text+ }+ deriving (Eq, Show, Generic)++-- | e.g:+--+-- > { "success" : false, "msg" : "couldn't add item: blabla"}+instance ToJSON (UpdateResponse o) where++-- | A generic response type for an operation performing+-- some kind of (potentially failing) lookup of an item+--+-- This is useful when writing a web application, where you+-- want to send for example a JSON message saying the item wasn't found+-- along with status 404 when the item isn't found, but just send the item+-- if it could be found. This is (purposefully) isomorphic to 'Maybe'.+data LookupResponse a =+ NotFound+ | Found !a+ deriving (Eq, Show)++-- | If you have some type convertible to JSON,+-- you can wrap it in 'LookupResponse' whenever you are+-- looking up a value associated to some identifier+-- where the lookup may fail.+-- It'll send the JSON-encoded value if found or+--+-- > { "message" : "Not found" }+--+-- if not found. This makes sure you send /valid/+-- JSON through the wires even when the target doesn't exist.+instance ToJSON a => ToJSON (LookupResponse a) where+ toJSON NotFound = object [ "message" .= ("Not found" :: Text) ]+ toJSON (Found x) = toJSON x++-- | Make 'LookupResponse' a proper 'Response' for+-- 'Service.Context.Context' lookups returning a 'Maybe' value,+-- returning 404 when Nothing is returned, along with a not found+-- message in json. Used by 'View'.+instance ToJSON a => Response (LookupResponse a) (Maybe a) where+ toResponse Nothing = (NotFound, status404)+ toResponse (Just v) = (Found v, status200)++-- | Just send the list of entries as a JSON array,+-- with status code 200. Used by 'ListAll'.+instance ToJSON a => Response [a] [a] where+ toResponse list = (list, status200)