rest-core 0.39 → 0.39.0.1
raw patch · 14 files changed
+147/−81 lines, 14 filesdep ~aesondep ~basedep ~transformers-compat
Dependency ranges changed: aeson, base, transformers-compat
Files
- rest-core.cabal +9/−8
- src/Rest.hs +2/−0
- src/Rest/Api.hs +6/−2
- src/Rest/Container.hs +1/−5
- src/Rest/Driver/Perform.hs +9/−6
- src/Rest/Driver/RestM.hs +13/−4
- src/Rest/Driver/Routing/Internal.hs +27/−23
- src/Rest/Driver/Types.hs +1/−1
- src/Rest/Error.hs +5/−1
- src/Rest/Handler.hs +5/−1
- src/Rest/Resource.hs +5/−2
- src/Rest/Run.hs +6/−2
- src/Rest/Schema.hs +4/−4
- tests/Runner.hs +54/−22
rest-core.cabal view
@@ -1,5 +1,5 @@ name: rest-core-version: 0.39+version: 0.39.0.1 description: Rest API library. synopsis: Rest API library. maintainer: code@silk.co@@ -40,13 +40,13 @@ Rest.Schema Rest.ShowUrl build-depends:- base >= 4.5 && < 4.10- , aeson >= 0.7 && < 0.12+ base >= 4.5 && < 4.12+ , aeson >= 0.7 && < 1.4 , aeson-utils >= 0.2 && < 0.4 , base-compat >= 0.8 && < 0.10 , bytestring >= 0.9 && < 0.11 , case-insensitive >= 1.2 && < 1.3- , errors >= 1.4 && < 2.2+ , errors >= 1.4 && < 2.3 , fclabels == 2.0.* , hxt >= 9.2 && < 9.4 , hxt-pickle-utils == 0.1.*@@ -61,7 +61,7 @@ , split >= 0.1 && < 0.3 , text >= 0.11 && < 1.3 , transformers >= 0.2 && < 0.6- , transformers-compat >= 0.3 && < 0.6+ , transformers-compat >= 0.3 && < 0.7 , unordered-containers == 0.2.* , uri-encode == 1.5.* , utf8-string >= 0.3 && < 1.1@@ -73,13 +73,14 @@ main-is: Runner.hs type: exitcode-stdio-1.0 build-depends:- base >= 4.5 && < 4.10- , HUnit >= 1.2 && < 1.4+ base >= 4.5 && < 4.12+ , aeson >= 0.7 && < 1.4+ , HUnit >= 1.2 && < 1.7 , bytestring >= 0.9 && < 0.11 , mtl >= 2.0 && < 2.3 , rest-core , test-framework == 0.8.* , test-framework-hunit == 0.3.* , transformers >= 0.3 && < 0.6- , transformers-compat >= 0.3 && < 0.6+ , transformers-compat >= 0.3 && < 0.7 , unordered-containers == 0.2.*
src/Rest.hs view
@@ -26,3 +26,5 @@ ) import Rest.Resource (Resource, mkResource, mkResourceId, mkResourceReader, mkResourceReaderWith, Void) import Rest.Schema++{-# ANN module "HLint: ignore Use import/export shortcut" #-}
src/Rest/Api.hs view
@@ -1,8 +1,12 @@ {-# LANGUAGE- GADTs+ CPP+ , GADTs , KindSignatures , NoImplicitPrelude #-}+#if MIN_VERSION_base(4,9,0)+{-# OPTIONS_GHC -Wno-redundant-constraints #-}+#endif -- | This module allows you to combine 'Resource's into an 'Api'. This -- can then be served using 'rest-happstack' or 'rest-snap', or used -- to generate clients or documentation using 'rest-gen'.@@ -129,7 +133,7 @@ -- | Get the latest version of an API. latest :: VersionSet m -> Maybe (Version, Some1 (Router m))-latest = headMay . reverse . sortBy (compare `on` fst)+latest = headMay . sortBy (flip compare `on` fst) -- | Parse a 'String' as a 'Version'. The string should contain two or -- three numbers separated by dots, e.g. @1.12.3@.
src/Rest/Container.hs view
@@ -1,11 +1,7 @@ {-# LANGUAGE DataKinds- , DeriveDataTypeable- , EmptyDataDecls- , FlexibleInstances , GADTs , ScopedTypeVariables- , TemplateHaskell , TypeFamilies #-} module Rest.Container@@ -84,7 +80,7 @@ mkStatusDict :: forall e o. [Error e] -> [Output o] -> Maybe (Outputs ('Just (Status e o))) mkStatusDict es os =- case mapMaybe mappingDictO (intersect es os) of+ case mapMaybe mappingDictO (es `intersect` os) of [] -> Nothing sos -> Just (Dicts sos) where
src/Rest/Driver/Perform.hs view
@@ -1,10 +1,14 @@ {-# LANGUAGE- FlexibleContexts+ CPP+ , FlexibleContexts , GADTs , OverloadedStrings , RankNTypes , ScopedTypeVariables #-}+#if MIN_VERSION_base(4,9,0)+{-# OPTIONS_GHC -Wno-redundant-constraints #-}+#endif module Rest.Driver.Perform ( Rest (..) , failureWriter@@ -29,11 +33,10 @@ import Data.List.Split import Data.Maybe import Data.Text.Lazy.Encoding (decodeUtf8)-import Data.UUID (UUID)+import Data.UUID.V4 (nextRandom) import Network.Multipart (BodyPart (..), MultiPart (..), showMultipartBody) import Safe import System.IO.Unsafe-import System.Random (randomIO) import Text.Xml.Pickle import qualified Data.ByteString.Lazy as B@@ -355,14 +358,14 @@ tryOutputs :: Rest m => (t -> Format -> ExceptT (Last DataError) m a) -> t -> ExceptT (Reason e) m a tryOutputs try outputs = do formats <- lift acceptM- rethrowLast $ (msum $ try outputs <$> formats) <|> unsupportedFormat formats+ rethrowLast $ msum (try outputs <$> formats) <|> unsupportedFormat formats where rethrowLast :: Monad m => ExceptT (Last DataError) m a -> ExceptT (Reason e) m a rethrowLast = either (maybe (error "Rest.Driver.Perform: ExceptT threw Last Nothing, this is a bug") (throwError . OutputError) . getLast) return <=< lift . runExceptT outputMultipart :: Rest m => [BodyPart] -> m UTF8.ByteString outputMultipart vs =- do let boundary = show $ unsafePerformIO (randomIO :: IO UUID)+ do let boundary = show $ unsafePerformIO nextRandom setHeader "Content-Type" ("multipart/mixed; boundary=" ++ boundary) return $ showMultipartBody boundary (MultiPart vs) @@ -388,7 +391,7 @@ in (fromQuery ++ fromAccept) where allFormats :: Maybe Format -> [Format]- allFormats ct = (maybe id (:) ct) [minBound .. maxBound]+ allFormats ct = maybe id (:) ct [minBound .. maxBound] splitter :: Maybe Format -> String -> [Format] splitter ct hdr = nub (match ct =<< takeWhile (/= ';') . trim <$> splitOn "," hdr)
src/Rest/Driver/RestM.hs view
@@ -1,4 +1,10 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE+ CPP+ , GeneralizedNewtypeDeriving+ #-}+#if MIN_VERSION_base(4,9,0)+{-# OPTIONS_GHC -Wno-redundant-constraints #-}+#endif module Rest.Driver.RestM ( RestM , runRestM@@ -44,12 +50,15 @@ , responseCode :: Maybe Int } deriving Show -instance Monoid RestOutput where- mempty = RestOutput { headersSet = H.empty, responseCode = Nothing }- o1 `mappend` o2 = RestOutput+instance Semigroup RestOutput where+ o1 <> o2 = RestOutput { headersSet = headersSet o2 `H.union` headersSet o1 , responseCode = responseCode o2 <|> responseCode o1 }++instance Monoid RestOutput where+ mempty = RestOutput { headersSet = H.empty, responseCode = Nothing }+ mappend = (<>) outputHeader :: String -> String -> RestOutput outputHeader h v = mempty { headersSet = H.singleton h v }
src/Rest/Driver/Routing/Internal.hs view
@@ -1,5 +1,5 @@ {-# LANGUAGE- DeriveDataTypeable+ CPP , ExistentialQuantification , FlexibleContexts , GADTs@@ -7,9 +7,12 @@ , NamedFieldPuns , RankNTypes , ScopedTypeVariables- , StandaloneDeriving , TupleSections #-}+#if MIN_VERSION_base(4,9,0)+{-# OPTIONS_GHC -Wno-redundant-constraints #-}+#endif+ module Rest.Driver.Routing.Internal where import Prelude hiding (id, (.))@@ -51,6 +54,8 @@ import qualified Rest.Driver.RestM as Rest +{-# ANN module "HLint: ignore Reduce duplication" #-}+ type UriParts = [String] apiError :: (MonadError (Reason e) m) => Reason e -> m a@@ -76,11 +81,11 @@ . unRouter route :: (Applicative m, Monad m) => Maybe Method -> UriParts -> Rest.Api m -> Either Reason_ (RunnableHandler m)-route mtd uri = routeWith defaultConfig mtd uri+route = routeWith defaultConfig routeWith :: Config m -> Maybe Method -> UriParts -> Rest.Api m -> Either Reason_ (RunnableHandler m) routeWith _ Nothing _ _ = apiError UnsupportedMethod-routeWith cfg (Just mtd) uri api = runRouter cfg mtd uri $ do+routeWith cfg (Just mtd) uri api = runRouter cfg mtd uri $ case api of Rest.Unversioned (Some1 router) -> routeRoot router Rest.Versioned vrs -> do@@ -102,7 +107,7 @@ return (RunnableHandler id (mkMultiGetHandler cfg root)) routeRouter :: Rest.Router m s -> Router n (RunnableHandler m)-routeRouter (Rest.Embed resource@(Rest.Resource { Rest.schema }) subRouters) =+routeRouter (Rest.Embed resource@Rest.Resource { Rest.schema } subRouters) = case schema of (Rest.Schema mToplevel step) -> maybe (apiError UnsupportedRoute) return =<< runMaybeT ( routeToplevel resource subRouters mToplevel@@ -114,7 +119,7 @@ -> [Some1 (Rest.Router s)] -> Maybe (Rest.Cardinality sid mid) -> MaybeT (Router n) (RunnableHandler m)-routeToplevel resource@(Rest.Resource { Rest.list }) subRouters mToplevel =+routeToplevel resource@Rest.Resource { Rest.list } subRouters mToplevel = hoistMaybe mToplevel >>= \toplevel -> case toplevel of Rest.Single sid -> lift $ withSubresource sid resource subRouters@@ -124,7 +129,7 @@ lift $ routeListHandler (list mid) routeCreate :: Rest.Resource m s sid mid aid -> MaybeT (Router n) (RunnableHandler m)-routeCreate (Rest.Resource { Rest.create }) = guardNullPath >> guardMethod POST >>+routeCreate Rest.Resource { Rest.create } = guardNullPath >> guardMethod POST >> maybe (apiError UnsupportedRoute) (return . RunnableHandler id) create routeStep :: Rest.Resource m s sid mid aid@@ -143,7 +148,7 @@ -> [Some1 (Rest.Router s)] -> Rest.Endpoint sid mid aid -> Router n (RunnableHandler m)-routeNamed resource@(Rest.Resource { Rest.list, Rest.statics }) subRouters h =+routeNamed resource@Rest.Resource { Rest.list, Rest.statics } subRouters h = case h of Left aid -> noRestPath >> hasMethod POST >> return (RunnableHandler id (statics aid)) Right (Rest.Single getter) -> routeGetter getter resource subRouters@@ -153,10 +158,10 @@ -> [Some1 (Rest.Router s)] -> Rest.Cardinality (Rest.Id sid) (Rest.Id mid) -> Router n (RunnableHandler m)-routeUnnamed resource@(Rest.Resource { Rest.list }) subRouters cardinality =+routeUnnamed resource@Rest.Resource { Rest.list } subRouters cardinality = case cardinality of- Rest.Single sBy -> withSegment (multi resource sBy) $ \seg ->- parseIdent sBy seg >>= \sid -> withSubresource sid resource subRouters+ Rest.Single sBy -> withSegment (multi resource sBy) $+ parseIdent sBy >=> \sid -> withSubresource sid resource subRouters Rest.Many mBy -> do seg <- popSegment mid <- parseIdent mBy seg@@ -171,13 +176,13 @@ routeGetter getter resource subRouters = case getter of Rest.Singleton sid -> getOrDeep sid- Rest.By sBy -> withSegment (multi resource sBy) $ \seg ->- parseIdent sBy seg >>= getOrDeep+ Rest.By sBy -> withSegment (multi resource sBy) $+ parseIdent sBy >=> getOrDeep where getOrDeep sid = withSubresource sid resource subRouters multi :: Rest.Resource m s sid mid aid -> Rest.Id sid -> Router n (RunnableHandler m)-multi (Rest.Resource { Rest.update, Rest.remove, Rest.enter }) sBy =+multi Rest.Resource { Rest.update, Rest.remove, Rest.enter } sBy = asks method >>= \mtd -> case mtd of PUT -> handleOrNotFound update@@ -206,7 +211,7 @@ -> Rest.Resource m s sid mid aid -> [Some1 (Rest.Router s)] -> Router n (RunnableHandler m)-withSubresource sid resource@(Rest.Resource { Rest.enter, Rest.selects, Rest.actions }) subRouters =+withSubresource sid resource@Rest.Resource { Rest.enter, Rest.selects, Rest.actions } subRouters = withSegment (routeSingle sid resource) $ \seg -> case lookup seg selects of Just select -> noRestPath >> hasMethod GET >> return (RunnableHandler (enter sid) select)@@ -220,7 +225,7 @@ Nothing -> apiError UnsupportedRoute routeSingle :: sid -> Rest.Resource m s sid mid aid -> Router n (RunnableHandler m)-routeSingle sid (Rest.Resource { Rest.enter, Rest.get, Rest.update, Rest.remove }) =+routeSingle sid Rest.Resource { Rest.enter, Rest.get, Rest.update, Rest.remove } = asks method >>= \mtd -> case mtd of GET -> handleOrNotFound get@@ -231,7 +236,7 @@ handleOrNotFound = maybe (apiError UnsupportedRoute) (return . RunnableHandler (enter sid)) routeName :: String -> Router n ()-routeName ident = when (not . null $ ident) $+routeName ident = unless (null ident) $ do identStr <- popSegment when (identStr /= ident) $ apiError UnsupportedRoute@@ -287,9 +292,8 @@ hasMethod :: Method -> Router n () hasMethod wantedMethod = asks method >>= \mtd ->- if mtd == wantedMethod- then return ()- else apiError UnsupportedMethod+ unless (mtd == wantedMethod) $+ apiError UnsupportedMethod guardMethod :: (MonadPlus m, MonadReader (RouterData c) m) => Method -> m () guardMethod mtd = asks method >>= guard . (== mtd)@@ -322,7 +326,7 @@ return . StringHashMap.fromList $ zipWith (\(k, _) b -> (k, eitherToStatus b)) (StringHashMap.toList vs) bs mkMultiGetHandler :: forall m s. (Applicative m, Monad m) => Config m -> Rest.Router m s -> Handler m-mkMultiGetHandler cfg root = mkInputHandler (xmlJsonI . multipartO) $ \(Resources rs) -> (runMultiResources cfg) cfg root rs+mkMultiGetHandler cfg root = mkInputHandler (xmlJsonI . multipartO) $ \(Resources rs) -> runMultiResources cfg cfg root rs defaultRunMultiResources :: (Applicative m, Monad m) => Config m -> Rest.Router m s -> [Resource] -> ExceptT Reason_ m [BodyPart] defaultRunMultiResources cfg root rs = lift $ forM rs (runResource cfg root)@@ -342,8 +346,8 @@ toRestInput :: Resource -> Rest.RestInput toRestInput r = Rest.emptyInput- { Rest.headers = H.map (R.unValue) . StringHashMap.toHashMap . R.headers $ r- , Rest.parameters = H.map (R.unValue) . StringHashMap.toHashMap . R.parameters $ r+ { Rest.headers = H.map R.unValue . StringHashMap.toHashMap . R.headers $ r+ , Rest.parameters = H.map R.unValue . StringHashMap.toHashMap . R.parameters $ r , Rest.body = LUTF8.fromString (R.input r) , Rest.method = Just (R.method r) , Rest.paths = splitUriString (R.uri r)
src/Rest/Driver/Types.hs view
@@ -27,7 +27,7 @@ mapHandler :: Run m n -> RunnableHandler m -> RunnableHandler n mapHandler run (RunnableHandler run' h) = RunnableHandler (run . run') h -data Config m+newtype Config m = Config { runMultiResources :: forall s. Config m -> Router m s -> [Resource] -> ExceptT Reason_ m [BodyPart] }
src/Rest/Error.hs view
@@ -1,8 +1,12 @@ {-# LANGUAGE- FlexibleContexts+ CPP+ , FlexibleContexts , GADTs , NoImplicitPrelude #-}+#if MIN_VERSION_base(4,9,0)+{-# OPTIONS_GHC -Wno-redundant-constraints #-}+#endif -- | Error types that can be returned by handlers, as well as some -- utilities for manipulating these errors. module Rest.Error
src/Rest/Handler.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE- DataKinds+ CPP+ , DataKinds , DeriveDataTypeable , GADTs , KindSignatures@@ -7,6 +8,9 @@ , TupleSections , TypeFamilies #-}+#if MIN_VERSION_base(4,9,0)+{-# OPTIONS_GHC -Wno-redundant-constraints #-}+#endif -- | Handlers for endpoints in a 'Resource'. module Rest.Handler ( -- * Single handlers.
src/Rest/Resource.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE- FlexibleContexts+ CPP+ , FlexibleContexts , FlexibleInstances , GADTs , KindSignatures@@ -7,9 +8,11 @@ , NamedFieldPuns , NoImplicitPrelude , RankNTypes- , TemplateHaskell , TypeSynonymInstances #-}+#if MIN_VERSION_base(4,9,0)+{-# OPTIONS_GHC -Wno-redundant-constraints #-}+#endif -- | A 'Resource' type for representing a REST resource, as well as -- smart constructors for empty resources which can then be filled in -- using record updates.
src/Rest/Run.hs view
@@ -1,7 +1,11 @@ {-# LANGUAGE- NoImplicitPrelude+ CPP+ , NoImplicitPrelude , RankNTypes #-}+#if MIN_VERSION_base(4,9,0)+{-# OPTIONS_GHC -Wno-redundant-constraints #-}+#endif module Rest.Run ( apiToHandler , apiToHandler'@@ -23,7 +27,7 @@ apiToHandler = apiToHandler' id apiToHandler' :: (Applicative m, Monad m) => Rest n => Run m n -> Api m -> n UTF8.ByteString-apiToHandler' run = apiToHandlerWith I.defaultConfig run+apiToHandler' = apiToHandlerWith I.defaultConfig apiToHandlerWith :: Config m -> Rest n => Run m n -> Api m -> n UTF8.ByteString apiToHandlerWith cfg run api = do
src/Rest/Schema.hs view
@@ -82,7 +82,7 @@ -- | A single resource endpoint with an identifier that can be read. -singleRead :: (Show a, Read a, Info a) => (a -> sid) -> Endpoint sid mid aid+singleRead :: (Read a, Info a) => (a -> sid) -> Endpoint sid mid aid singleRead = singleIdent ReadId -- | A single resource identified as specified by the 'Ident'.@@ -102,7 +102,7 @@ -- | A listing with an identifier that can be read. -listingRead :: (Show a, Read a, Info a) => (a -> mid) -> Endpoint sid mid aid+listingRead :: (Read a, Info a) => (a -> mid) -> Endpoint sid mid aid listingRead = listingIdent ReadId -- | A listing identified as specified by the 'Ident'.@@ -118,7 +118,7 @@ -- | An unnamed single resource with an identifier that can be read. -unnamedSingleRead :: (Show a, Read a, Info a) => (a -> sid) -> Step sid mid aid+unnamedSingleRead :: (Read a, Info a) => (a -> sid) -> Step sid mid aid unnamedSingleRead = unnamedSingleIdent ReadId -- | An unnamed single resource identified as specified by the@@ -133,7 +133,7 @@ unnamedListing = unnamedListingIdent StringId -- | An unnamed listing with an identifier that can be read.-unnamedListingRead :: (Show a, Read a, Info a) => (a -> mid) -> Step sid mid aid+unnamedListingRead :: (Read a, Info a) => (a -> mid) -> Step sid mid aid unnamedListingRead = unnamedListingIdent ReadId -- | An unnamed listing identified as specified by the 'Ident'.
tests/Runner.hs view
@@ -1,32 +1,38 @@ {-# LANGUAGE- OverloadedStrings+ CPP+ , OverloadedStrings+ , RankNTypes , ScopedTypeVariables #-}--import Control.Applicative+#if MIN_VERSION_base(4,9,0)+{-# OPTIONS_GHC -Wno-redundant-constraints #-}+#endif import Control.Monad+import Control.Monad.Except+import Control.Monad.Identity import Control.Monad.Reader+import Data.Aeson import Data.Monoid import Test.Framework (defaultMain) import Test.Framework.Providers.HUnit (testCase) import Test.HUnit (Assertion, assertEqual, assertFailure)--import qualified Data.HashMap.Strict as H+import qualified Data.HashMap.Strict as H +import Rest hiding (input) import Rest.Api hiding (route)-import Rest.Dictionary+import Rest.Dictionary (Format (..), Ident (..)) import Rest.Driver.Perform (accept)-import Rest.Driver.RestM (runRestM_) import Rest.Driver.Routing import Rest.Driver.Types-import Rest.Handler import Rest.Resource-import Rest.Schema import qualified Rest.Api as Rest-import qualified Rest.Driver.RestM as RestM+import qualified Rest.Container as C+import qualified Rest.Driver.RestM as RM+import qualified Rest.Resource as Res+import qualified Rest.Run as Run main :: IO ()-main = do+main = defaultMain [ testCase "Top level listing." testListing , testCase "Top level listing (trailing slash)." testListingTrailingSlash , testCase "Top level singleton." testToplevelSingleton@@ -44,21 +50,19 @@ , testCase "Multi-PUT." testMultiPut , testCase "Multi-POST" testMultiPost , testCase "Accept headers." testAcceptHeaders+ , testCase "Test listing count" testListingCount ] -testListing :: Assertion-testListing = checkRoute GET "resource" (Rest.root -/ Rest.route resource)+listResource :: Resource IO IO Void () Void+listResource = mkResourceId { name = "resource", schema = Schema (Just (Many ())) (Named []), list = listHandler } where- resource :: Resource IO IO Void () Void- resource = mkResourceId { name = "resource", schema = Schema (Just (Many ())) (Named []), list = listHandler } listHandler () = mkListing id $ \_ -> return [] +testListing :: Assertion+testListing = checkRoute GET "resource" (Rest.root -/ Rest.route listResource)+ testListingTrailingSlash :: Assertion-testListingTrailingSlash = checkRoute GET "resource/" (Rest.root -/ Rest.route resource)- where- resource :: Resource IO IO Void () Void- resource = mkResourceId { name = "resource", schema = Schema (Just (Many ())) (Named []), list = listHandler }- listHandler () = mkListing id $ \_ -> return []+testListingTrailingSlash = checkRoute GET "resource/" (Rest.root -/ Rest.route listResource) testToplevelSingleton :: Assertion testToplevelSingleton = checkSingleRoute "resource" resource handler_@@ -159,7 +163,7 @@ resource = mkResourceReader { name = "resource" , schema = Schema Nothing (Named [("foo", Right (Single (By (Id StringId id))))])- , update = Just (mkConstHandler xmlJsonO (liftM void ask))+ , update = Just (mkConstHandler xmlJsonO (fmap void ask)) } testMultiPost :: Assertion@@ -181,7 +185,7 @@ checkRoutes :: (Applicative m, Monad m) => [(Method, Uri)] -> Rest.Router m s -> Assertion checkRoutes reqs router =- do forM_ reqs $ uncurry $ checkRouteWithIgnoredMethods (map fst reqs) router+ forM_ reqs $ uncurry $ checkRouteWithIgnoredMethods (map fst reqs) router checkRouteWithIgnoredMethods :: (Applicative m, Monad m) => [Method] -> Rest.Router m s -> Method -> Uri -> Assertion checkRouteWithIgnoredMethods ignoredMethods router method uri =@@ -208,3 +212,31 @@ testAcceptHeaders = do let fmt = accept (Just "text/json") Nothing Nothing assertEqual "Accept json format." [JsonFormat] fmt++testListingCount :: Assertion+testListingCount = assertEqual "listing count" (Right $ C.List 0 5 [1..5::Int]) (eitherDecode bs)+ where+ (bs, _meta) = runIdentity . RM.runRestM input $ Run.apiToHandler api+ input :: RM.RestInput+ input = RM.emptyInput+ { RM.parameters = H.fromList [("count", "5")]+ , RM.paths = splitUriString "resource"+ , RM.headers = H.fromList+ [ ("Accept" , "application/json")+ , ("Content-Type", "application/json")+ ]+ }+ api :: Api (RM.RestM Identity)+ api = Unversioned (Some1 $ Rest.root -/ Rest.route resource)+ resource :: Resource (RM.RestM Identity) (RM.RestM Identity) String () Void+ resource = mkResourceId+ { Res.name = "resource"+ , Res.schema = withListing () $ named []+ , Res.list = const listHandler+ }+ where+ listHandler :: ListHandler (RM.RestM Identity)+ listHandler = mkListing jsonO h+ where+ h :: Range -> ExceptT Reason_ (RM.RestM Identity) [Int]+ h _rng = return [1..10]