on master help sign in
extract servant code from fuzz machinery
commit53ab47e4c115a9f99342b71ca8db1b8ee0761bdd authorMark Wotton <mwotton@gmail.com> dateMon 15 Mar 2021, 11:18 (6 years ago) parent8276fb6
8 files changed+236−187
Makefile+1−1 roboservant.cabal+3−1 src/Roboservant/Direct.hs+18−19 src/Roboservant/Types.hs+2 src/Roboservant/Types/BuildFrom.hs−1 src/Roboservant/Types/ReifiedApi.hs+7−165 src/Roboservant/Types/ReifiedApi/Server.hs+204 test/Spec.hs+1
Makefile+1−1
@@ -1,4 +1,4 @@
11testwatch:
2− ghcid -T main -c 'stack repl --test --ghc-options=-fobject-code' --allow-eval --restart="stack.yaml" --restart="package.yaml" -W
2+ ghcid -T main -c 'stack repl roboservant:lib roboservant:test:roboservant-test --test --ghc-options=-fobject-code' --allow-eval --restart="stack.yaml" --restart="package.yaml" -W
33pedanticwatch:
44 ghcid -c 'stack repl --test --ghc-options=-fobject-code' --allow-eval --restart="stack.yaml" --restart="package.yaml" -W
roboservant.cabal+3−1
@@ -4,7 +4,7 @@ cabal-version: 1.12
44--
55-- see: https://github.com/sol/hpack
66--
7−-- hash: 48773018ec618e9eba02a126e3aeb93781a1d68ce28e2c405c35e9536bf94f70
7+-- hash: fa8c07f0c2fc10f18d3e925b8f702612c0c9fd81faa7942f901af388d509c231
88
99name: roboservant
1010version: 0.1.0.2
@@ -29,6 +29,7 @@ library
2929 exposed-modules:
3030 Roboservant
3131 Roboservant.Direct
32+ Roboservant.Server
3233 Roboservant.Types
3334 Roboservant.Types.Breakdown
3435 Roboservant.Types.BuildFrom
@@ -36,6 +37,7 @@ library
3637 Roboservant.Types.FlattenServer
3738 Roboservant.Types.Internal
3839 Roboservant.Types.ReifiedApi
40+ Roboservant.Types.ReifiedApi.Server
3941 other-modules:
4042 Paths_roboservant
4143 hs-source-dirs:
src/Roboservant/Direct.hs+18−19
@@ -12,7 +12,7 @@
1212{-# LANGUAGE UndecidableInstances #-}
1313
1414module Roboservant.Direct
15− ( fuzz,
15+ ( fuzz',
1616 Config (..),
1717 -- TODO come up with something smarter than exporting all this, we should
1818 -- have some nice error-display functions
@@ -56,16 +56,15 @@ import GHC.Generics ((:*:) (..))
5656import Roboservant.Types
5757 ( ApiOffset (..),
5858 Argument (..),
59− FlattenServer (..),
59+ InteractionError,
6060 Provenance (..),
61+ ReifiedApi,
6162 ReifiedEndpoint (..),
6263 Stash (..),
6364 StashValue (..),
64− ToReifiedApi (..),
6565 TypedF,
6666 )
6767import Roboservant.Types.Config
68−import Servant (Endpoints, Proxy (Proxy), Server, ServerError (..))
6968import System.Random (Random (randomR), StdGen, mkStdGen)
7069import qualified Type.Reflection as R
7170
@@ -105,7 +104,7 @@ data EndpointOption
105104 = forall as.
106105 (V.RecordToList as, V.RMap as) =>
107106 EndpointOption
108− { eoCall :: V.Curried as (IO (Either ServerError (NonEmpty (Dynamic, Int)))),
107+ { eoCall :: V.Curried as (IO (Either InteractionError (NonEmpty (Dynamic, Int)))),
109108 eoArgs :: V.Rec (TypedF StashValue) as
110109 }
111110
@@ -121,13 +120,18 @@ data Report
121120 }
122121 deriving (Show)
123122
124−fuzz ::
125− forall api.
126− (FlattenServer api, ToReifiedApi (Endpoints api)) =>
127− Server api ->
123+
124+
125+-- fuzzClient :: Client api -> Config -> IO (Maybe Report)
126+-- fuzzClient = undefined
127+
128+
129+
130+fuzz' ::
131+ ReifiedApi ->
128132 Config ->
129133 IO (Maybe Report)
130−fuzz server Config {..} = handle (pure . Just . formatException) $ do
134+fuzz' reifiedApi Config {..} = handle (pure . Just . formatException) $ do
131135 let path = []
132136 stash = addToStash seed mempty
133137 currentRng = mkStdGen rngSeed
@@ -173,7 +177,7 @@ fuzz server Config {..} = handle (pure . Just . formatException) $ do
173177 else do
174178 _ <- action
175179 untilDone (n -1, deadline) action
176− reifiedApi = toReifiedApi (flattenServer @api server) (Proxy @(Endpoints api))
180+
177181 routeCount = length reifiedApi
178182 elementOrFail ::
179183 (MonadState FuzzState m, MonadIO m) =>
@@ -190,7 +194,7 @@ fuzz server Config {..} = handle (pure . Just . formatException) $ do
190194 ( forall as.
191195 (V.RecordToList as, V.RMap as) =>
192196 FuzzOp ->
193− V.Curried as (IO (Either ServerError (NonEmpty (Dynamic, Int)))) ->
197+ V.Curried as (IO (Either InteractionError (NonEmpty (Dynamic, Int)))) ->
194198 V.Rec (TypedF V.Identity) as ->
195199 m r
196200 ) ->
@@ -231,18 +235,13 @@ fuzz server Config {..} = handle (pure . Just . formatException) $ do
231235 execute ::
232236 (MonadState FuzzState m, MonadIO m, V.RecordToList as, V.RMap as) =>
233237 FuzzOp ->
234− V.Curried as (IO (Either ServerError (NonEmpty (Dynamic, Int)))) ->
238+ V.Curried as (IO (Either InteractionError (NonEmpty (Dynamic, Int)))) ->
235239 V.Rec (TypedF V.Identity) as ->
236240 m ()
237241 execute fuzzop func args = do
238242 (liftIO . logInfo . show . (fuzzop,) . stash) =<< get
239243 liftIO (V.runcurry' func argVals) >>= \case
240− -- parameterise this
241− Left (serverError :: ServerError) ->
242− case errHTTPCode serverError of
243− 500 -> throw serverError
244− _ ->
245− liftIO . logInfo . show $ ("ignoring non-500 error", serverError)
244+ Left (e::InteractionError) -> throw e
246245 Right (dyn :: NEL.NonEmpty (Dynamic, Int)) -> do
247246 modify'
248247 ( \fs@FuzzState {..} ->
src/Roboservant/Types.hs+2
@@ -14,6 +14,8 @@ module Roboservant.Types
1414 module Roboservant.Types.ReifiedApi,
1515 module Roboservant.Types.Internal,
1616 module Roboservant.Types.Config,
17+ Atom, Compound
18+
1719 )
1820where
1921
src/Roboservant/Types/BuildFrom.hs−1
@@ -12,7 +12,6 @@
1212
1313module Roboservant.Types.BuildFrom where
1414
15−import Control.Monad (filterM)
1615import qualified Data.Dependent.Map as DM
1716import Data.Hashable
1817import qualified Data.IntSet as IntSet
src/Roboservant/Types/ReifiedApi.hs+7−165
@@ -15,25 +15,17 @@
1515{-# LANGUAGE CPP #-}
1616module Roboservant.Types.ReifiedApi where
1717
18−import Control.Monad.Except (runExceptT)
19−import Data.Bifunctor
2018import Data.Dynamic (Dynamic)
21−import Data.Kind
19+import Control.Exception(Exception)
2220import Data.List.NonEmpty (NonEmpty)
2321import Data.Typeable (Typeable)
2422import GHC.Generics ((:*:)(..))
25−import GHC.TypeLits (Symbol)
2623import Roboservant.Types.Internal
27−import Roboservant.Types.Breakdown
28−import Roboservant.Types.BuildFrom
29−import Roboservant.Types.FlattenServer
30−import Servant
31−import Servant.API.Modifiers(FoldRequired,FoldLenient)
24+
3225import qualified Data.Text as T
3326import qualified Data.Vinyl as V
3427import qualified Data.Vinyl.Curry as V
3528import qualified Type.Reflection as R
36−import Data.Hashable(Hashable)
3729
3830newtype ApiOffset = ApiOffset Int
3931 deriving (Eq, Show, Ord)
@@ -47,164 +39,14 @@ newtype Argument a = Argument
4739
4840data ReifiedEndpoint = forall as. (V.RecordToList as, V.RMap as) => ReifiedEndpoint
4941 { reArguments :: V.Rec (TypedF Argument) as
50− , reEndpointFunc :: V.Curried as (IO (Either ServerError (NonEmpty (Dynamic,Int))))
42+ , reEndpointFunc :: V.Curried as (IO (Either InteractionError (NonEmpty (Dynamic,Int))))
5143 }
5244
53−type ReifiedApi = [(ApiOffset, ReifiedEndpoint)]
45+type ReifiedApi = [(ApiOffset, ReifiedEndpoint )]
5446
5547tagType :: Typeable a => f a -> TypedF f a
5648tagType = (R.typeRep :*:)
5749
58−class ToReifiedApi (endpoints ) where
59−
60− toReifiedApi :: Bundled endpoints -> Proxy endpoints -> ReifiedApi
61−
62−class ( V.RecordToList (EndpointArgs endpoint)
63− , V.RMap (EndpointArgs endpoint)
64− ) => ToReifiedEndpoint (endpoint :: *) where
65− type EndpointArgs endpoint :: [Type]
66− type EndpointRes endpoint :: Type
67−
68− reifiedEndpointArguments :: V.Rec (TypedF Argument) (EndpointArgs endpoint)
69−
70−instance ToReifiedApi '[] where
71− toReifiedApi NoEndpoints _ = []
72−
73−instance
74− ( Typeable (EndpointRes endpoint)
75−
76− , NormalizeFunction (ServerT endpoint Handler)
77− , Normal (ServerT endpoint Handler) ~ V.Curried (EndpointArgs endpoint) (IO (Either ServerError (NonEmpty (Dynamic,Int))))
78− , ToReifiedEndpoint endpoint
79− , ToReifiedApi endpoints
80− ) =>
81− ToReifiedApi (endpoint : endpoints)
82− where
83− toReifiedApi (endpoint `AnEndpoint` endpoints) _ =
84− (0, ReifiedEndpoint
85− { reArguments = reifiedEndpointArguments @endpoint
86− , reEndpointFunc = normalize endpoint
87− }
88− )
89− : (map . first) (+1)
90− (toReifiedApi endpoints (Proxy @endpoints))
91−
92−class NormalizeFunction m where
93− type Normal m
94− normalize :: m -> Normal m
95−
96−instance NormalizeFunction x => NormalizeFunction (r -> x) where
97− type Normal (r -> x) = r -> Normal x
98− normalize = fmap normalize
99−
100−instance (Typeable x, Hashable x, Breakdown x) => NormalizeFunction (Handler x) where
101− type Normal (Handler x) = IO (Either ServerError (NonEmpty (Dynamic,Int)))
102− normalize handler = (runExceptT . runHandler') handler >>= \case
103− Left serverError -> pure (Left serverError)
104− Right x -> pure $ Right $ breakdown x
105−
106−instance
107− (Typeable responseType, Breakdown responseType) =>
108− ToReifiedEndpoint (Verb method statusCode contentTypes responseType)
109− where
110− type EndpointArgs (Verb method statusCode contentTypes responseType) = '[]
111− type EndpointRes (Verb method statusCode contentTypes responseType) = responseType
112− reifiedEndpointArguments = V.RNil
113−
114−instance
115− (ToReifiedEndpoint endpoint) =>
116− ToReifiedEndpoint ((x :: Symbol) :> endpoint)
117− where
118− type EndpointArgs ((x :: Symbol) :> endpoint) = EndpointArgs endpoint
119− type EndpointRes ((x :: Symbol) :> endpoint) = EndpointRes endpoint
120− reifiedEndpointArguments = reifiedEndpointArguments @endpoint
121−
122−instance
123− (ToReifiedEndpoint endpoint) =>
124− ToReifiedEndpoint (Description s :> endpoint)
125− where
126− type EndpointArgs (Description s :> endpoint) = EndpointArgs endpoint
127− type EndpointRes (Description s :> endpoint) = EndpointRes endpoint
128− reifiedEndpointArguments = reifiedEndpointArguments @endpoint
129−
130−instance
131− (ToReifiedEndpoint endpoint) =>
132− ToReifiedEndpoint (Summary s :> endpoint)
133− where
134− type EndpointArgs (Summary s :> endpoint) = EndpointArgs endpoint
135− type EndpointRes (Summary s :> endpoint) = EndpointRes endpoint
136− reifiedEndpointArguments = reifiedEndpointArguments @endpoint
137−
138−instance
139− (Typeable requestType
140− ,BuildFrom requestType
141− ,ToReifiedEndpoint endpoint) =>
142− ToReifiedEndpoint (QueryFlag name :> endpoint)
143− where
144− type EndpointArgs (QueryFlag name :> endpoint) = Bool ': EndpointArgs endpoint
145− type EndpointRes (QueryFlag name :> endpoint) = EndpointRes endpoint
146− reifiedEndpointArguments = tagType (Argument (buildFrom @Bool)) V.:& reifiedEndpointArguments @endpoint
147−
148−type IfLenient s mods t = If (FoldLenient mods) (Either s t) t
149−type IfRequired mods t = If (FoldRequired mods) t (Maybe t)
150−type IfRequiredLenient s mods t = IfRequired mods (IfLenient s mods t)
151−
152−instance
153− ( BuildFrom (IfRequiredLenient T.Text mods paramType)
154− , ToReifiedEndpoint endpoint
155− ) =>
156− ToReifiedEndpoint (QueryParam' mods name paramType :> endpoint)
157− where
158− type EndpointArgs (QueryParam' mods name paramType :> endpoint) = IfRequiredLenient T.Text mods paramType ': EndpointArgs endpoint
159− type EndpointRes (QueryParam' mods name paramType :> endpoint) = EndpointRes endpoint
160− reifiedEndpointArguments =
161− tagType (Argument (buildFrom @(IfRequiredLenient T.Text mods paramType)))
162− V.:& reifiedEndpointArguments @endpoint
163−
164−instance
165− ( BuildFrom (IfRequiredLenient T.Text mods headerType)
166− , ToReifiedEndpoint endpoint
167− ) =>
168− ToReifiedEndpoint (Header' mods headerName headerType :> endpoint)
169− where
170− type EndpointArgs (Header' mods headerName headerType :> endpoint) = IfRequiredLenient T.Text mods headerType ': EndpointArgs endpoint
171− type EndpointRes (Header' mods headerName headerType :> endpoint) = EndpointRes endpoint
172− reifiedEndpointArguments =
173− tagType (Argument (buildFrom @(IfRequiredLenient T.Text mods headerType)))
174− V.:& reifiedEndpointArguments @endpoint
175−
176−#if MIN_VERSION_servant(0,17,0)
177−instance
178− ( BuildFrom (IfLenient String mods captureType)
179− , ToReifiedEndpoint endpoint) =>
180− ToReifiedEndpoint (Capture' mods name captureType :> endpoint)
181− where
182− type EndpointArgs (Capture' mods name captureType :> endpoint) = IfLenient String mods captureType ': EndpointArgs endpoint
183− type EndpointRes (Capture' mods name captureType :> endpoint) = EndpointRes endpoint
184− reifiedEndpointArguments =
185− tagType (Argument (buildFrom @(IfLenient String mods captureType)))
186− V.:& reifiedEndpointArguments @endpoint
187−#else
188−instance
189− ( BuildFrom captureType
190− , ToReifiedEndpoint endpoint) =>
191− ToReifiedEndpoint (Capture' mods name captureType :> endpoint)
192− where
193− type EndpointArgs (Capture' mods name captureType :> endpoint) = captureType ': EndpointArgs endpoint
194− type EndpointRes (Capture' mods name captureType :> endpoint) = EndpointRes endpoint
195− reifiedEndpointArguments =
196− tagType (Argument (buildFrom @(captureType)))
197− V.:& reifiedEndpointArguments @endpoint
198−
199−#endif
200−
201−instance
202− ( BuildFrom (IfLenient String mods requestType)
203− , ToReifiedEndpoint endpoint) =>
204− ToReifiedEndpoint (ReqBody' mods contentTypes requestType :> endpoint)
205− where
206− type EndpointArgs (ReqBody' mods contentTypes requestType :> endpoint) = IfLenient String mods requestType ': EndpointArgs endpoint
207− type EndpointRes (ReqBody' mods contentTypes requestType :> endpoint) = EndpointRes endpoint
208− reifiedEndpointArguments =
209− tagType (Argument (buildFrom @(IfLenient String mods requestType)))
210− V.:& reifiedEndpointArguments @endpoint
50+newtype InteractionError = InteractionError T.Text
51+ deriving Show
52+instance Exception InteractionError
src/Roboservant/Types/ReifiedApi/Server.hsnew file+204
@@ -0,0 +1,204 @@
1+{-# LANGUAGE RankNTypes #-}
2+{-# LANGUAGE TypeApplications #-}
3+{-# LANGUAGE PolyKinds #-}
4+{-# LANGUAGE AllowAmbiguousTypes #-}
5+{-# LANGUAGE FlexibleContexts #-}
6+{-# LANGUAGE GADTs #-}
7+{-# LANGUAGE LambdaCase #-}
8+{-# LANGUAGE PolyKinds #-}
9+{-# LANGUAGE RankNTypes #-}
10+{-# LANGUAGE RecordWildCards #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE TupleSections #-}
13+{-# LANGUAGE TypeApplications #-}
14+{-# LANGUAGE TypeFamilies #-}
15+{-# LANGUAGE UndecidableInstances #-}
16+{-# LANGUAGE CPP #-}
17+{-# LANGUAGE DataKinds #-}
18+{-# LANGUAGE TypeOperators #-}
19+{-# LANGUAGE FlexibleInstances #-}
20+module Roboservant.Types.ReifiedApi.Server where
21+
22+import Servant
23+import Servant.API.Modifiers(FoldRequired,FoldLenient)
24+
25+import Control.Monad.Except (runExceptT)
26+import Data.Bifunctor
27+import Data.Dynamic (Dynamic)
28+import Data.Kind
29+import Data.List.NonEmpty (NonEmpty)
30+import Data.Typeable (Typeable)
31+import GHC.TypeLits (Symbol)
32+import Roboservant.Types.Breakdown
33+import Roboservant.Types.BuildFrom
34+import Roboservant.Types.FlattenServer
35+import Roboservant.Types.ReifiedApi
36+
37+import qualified Data.Text as T
38+import qualified Data.Vinyl as V
39+import qualified Data.Vinyl.Curry as V
40+import Data.Hashable(Hashable)
41+
42+
43+class ToReifiedApi (endpoints ) where
44+ toReifiedApi :: Bundled endpoints -> Proxy endpoints -> ReifiedApi
45+
46+class ( V.RecordToList (EndpointArgs endpoint)
47+ , V.RMap (EndpointArgs endpoint)
48+ ) => ToReifiedEndpoint (endpoint :: *) where
49+ type EndpointArgs endpoint :: [Type]
50+ type EndpointRes endpoint :: Type
51+
52+ reifiedEndpointArguments :: V.Rec (TypedF Argument) (EndpointArgs endpoint)
53+
54+instance ToReifiedApi '[] where
55+ toReifiedApi NoEndpoints _ = []
56+
57+instance
58+ ( Typeable (EndpointRes endpoint)
59+
60+ , NormalizeFunction (ServerT endpoint Handler)
61+ , Normal (ServerT endpoint Handler) ~ V.Curried (EndpointArgs endpoint) (IO (Either InteractionError (NonEmpty (Dynamic,Int))))
62+ , ToReifiedEndpoint endpoint
63+ , ToReifiedApi endpoints
64+ ) =>
65+ ToReifiedApi (endpoint : endpoints)
66+ where
67+ toReifiedApi (endpoint `AnEndpoint` endpoints) _ =
68+ (0, ReifiedEndpoint
69+ { reArguments = reifiedEndpointArguments @endpoint
70+ , reEndpointFunc = normalize endpoint
71+ }
72+ )
73+ : (map . first) (+1)
74+ (toReifiedApi endpoints (Proxy @endpoints))
75+
76+class NormalizeFunction m where
77+ type Normal m
78+ normalize :: m -> Normal m
79+
80+instance NormalizeFunction x => NormalizeFunction (r -> x) where
81+ type Normal (r -> x) = r -> Normal x
82+ normalize = fmap normalize
83+
84+instance (Typeable x, Hashable x, Breakdown x) => NormalizeFunction (Handler x) where
85+ type Normal (Handler x) = IO (Either InteractionError (NonEmpty (Dynamic,Int)))
86+ normalize handler = (runExceptT . runHandler') handler >>= \case
87+ Left serverError -> pure (Left (renderServerError serverError))
88+ where
89+ -- | TODO improve this
90+ renderServerError :: ServerError -> InteractionError
91+ renderServerError = InteractionError . T.pack . show
92+ Right x -> pure $ Right $ breakdown x
93+
94+instance
95+ (Typeable responseType, Breakdown responseType) =>
96+ ToReifiedEndpoint (Verb method statusCode contentTypes responseType)
97+ where
98+ type EndpointArgs (Verb method statusCode contentTypes responseType) = '[]
99+ type EndpointRes (Verb method statusCode contentTypes responseType) = responseType
100+ reifiedEndpointArguments = V.RNil
101+
102+instance
103+ (ToReifiedEndpoint endpoint) =>
104+ ToReifiedEndpoint ((x :: Symbol) :> endpoint)
105+ where
106+ type EndpointArgs ((x :: Symbol) :> endpoint) = EndpointArgs endpoint
107+ type EndpointRes ((x :: Symbol) :> endpoint) = EndpointRes endpoint
108+ reifiedEndpointArguments = reifiedEndpointArguments @endpoint
109+
110+instance
111+ (ToReifiedEndpoint endpoint) =>
112+ ToReifiedEndpoint (Description s :> endpoint)
113+ where
114+ type EndpointArgs (Description s :> endpoint) = EndpointArgs endpoint
115+ type EndpointRes (Description s :> endpoint) = EndpointRes endpoint
116+ reifiedEndpointArguments = reifiedEndpointArguments @endpoint
117+
118+instance
119+ (ToReifiedEndpoint endpoint) =>
120+ ToReifiedEndpoint (Summary s :> endpoint)
121+ where
122+ type EndpointArgs (Summary s :> endpoint) = EndpointArgs endpoint
123+ type EndpointRes (Summary s :> endpoint) = EndpointRes endpoint
124+ reifiedEndpointArguments = reifiedEndpointArguments @endpoint
125+
126+instance
127+ (Typeable requestType
128+ ,BuildFrom requestType
129+ ,ToReifiedEndpoint endpoint) =>
130+ ToReifiedEndpoint (QueryFlag name :> endpoint)
131+ where
132+ type EndpointArgs (QueryFlag name :> endpoint) = Bool ': EndpointArgs endpoint
133+ type EndpointRes (QueryFlag name :> endpoint) = EndpointRes endpoint
134+ reifiedEndpointArguments = tagType (Argument (buildFrom @Bool)) V.:& reifiedEndpointArguments @endpoint
135+
136+type IfLenient s mods t = If (FoldLenient mods) (Either s t) t
137+type IfRequired mods t = If (FoldRequired mods) t (Maybe t)
138+type IfRequiredLenient s mods t = IfRequired mods (IfLenient s mods t)
139+
140+instance
141+ ( BuildFrom (IfRequiredLenient T.Text mods paramType)
142+ , ToReifiedEndpoint endpoint
143+ ) =>
144+ ToReifiedEndpoint (QueryParam' mods name paramType :> endpoint)
145+ where
146+ type EndpointArgs (QueryParam' mods name paramType :> endpoint) = IfRequiredLenient T.Text mods paramType ': EndpointArgs endpoint
147+ type EndpointRes (QueryParam' mods name paramType :> endpoint) = EndpointRes endpoint
148+ reifiedEndpointArguments =
149+ tagType (Argument (buildFrom @(IfRequiredLenient T.Text mods paramType)))
150+ V.:& reifiedEndpointArguments @endpoint
151+
152+instance
153+ ( BuildFrom (IfRequiredLenient T.Text mods headerType)
154+ , ToReifiedEndpoint endpoint
155+ ) =>
156+ ToReifiedEndpoint (Header' mods headerName headerType :> endpoint)
157+ where
158+ type EndpointArgs (Header' mods headerName headerType :> endpoint) = IfRequiredLenient T.Text mods headerType ': EndpointArgs endpoint
159+ type EndpointRes (Header' mods headerName headerType :> endpoint) = EndpointRes endpoint
160+ reifiedEndpointArguments =
161+ tagType (Argument (buildFrom @(IfRequiredLenient T.Text mods headerType)))
162+ V.:& reifiedEndpointArguments @endpoint
163+
164+#if MIN_VERSION_servant(0,17,0)
165+instance
166+ ( BuildFrom (IfLenient String mods captureType)
167+ , ToReifiedEndpoint endpoint) =>
168+ ToReifiedEndpoint (Capture' mods name captureType :> endpoint)
169+ where
170+ type EndpointArgs (Capture' mods name captureType :> endpoint) = IfLenient String mods captureType ': EndpointArgs endpoint
171+ type EndpointRes (Capture' mods name captureType :> endpoint) = EndpointRes endpoint
172+ reifiedEndpointArguments =
173+ tagType (Argument (buildFrom @(IfLenient String mods captureType)))
174+ V.:& reifiedEndpointArguments @endpoint
175+#else
176+instance
177+ ( BuildFrom captureType
178+ , ToReifiedEndpoint endpoint) =>
179+ ToReifiedEndpoint (Capture' mods name captureType :> endpoint)
180+ where
181+ type EndpointArgs (Capture' mods name captureType :> endpoint) = captureType ': EndpointArgs endpoint
182+ type EndpointRes (Capture' mods name captureType :> endpoint) = EndpointRes endpoint
183+ reifiedEndpointArguments =
184+ tagType (Argument (buildFrom @(captureType)))
185+ V.:& reifiedEndpointArguments @endpoint
186+
187+#endif
188+
189+instance
190+ ( BuildFrom (IfLenient String mods requestType)
191+ , ToReifiedEndpoint endpoint) =>
192+ ToReifiedEndpoint (ReqBody' mods contentTypes requestType :> endpoint)
193+ where
194+ type EndpointArgs (ReqBody' mods contentTypes requestType :> endpoint) = IfLenient String mods requestType ': EndpointArgs endpoint
195+ type EndpointRes (ReqBody' mods contentTypes requestType :> endpoint) = EndpointRes endpoint
196+ reifiedEndpointArguments =
197+ tagType (Argument (buildFrom @(IfLenient String mods requestType)))
198+ V.:& reifiedEndpointArguments @endpoint
199+
200+
201+-- case errHTTPCode serverError of
202+-- 500 -> throw serverError
203+-- _ ->
204+-- liftIO . logInfo . show $ ("ignoring non-500 error", serverError)
test/Spec.hs+1
@@ -17,6 +17,7 @@ import qualified Nested
1717import qualified Post
1818import qualified Product
1919import qualified Roboservant as RS
20+import qualified Roboservant.Server as RS
2021import qualified Seeded
2122import Test.Hspec
2223import Test.Hspec.Core.Spec (FailureReason (Reason), ResultStatus (Failure, Success), itemExample, mapSpecItem_, resultStatus)