How to Capture with .ext suffix?
Hi, I have an API that I'm querying which in simplest form looks like:
/int.json
The type I tried is:
type publicAPI = Capture "index" Int :> ".json" :> Get '[JSON] PublicData
But it constructs a query with an additional slash:
/int/.json
Is there a way to remove that slash?
I'm not too good with higher haskell and tried to make a new combinator building off of the existing Capture but it doesn't compile:
data CaptureWithSuffix (sym1 :: Symbol) (a :: *) (sym2 :: Symbol)
deriving (Typeable)
type publicAPI = CaptureWithSuffix "index" Int ".json" :> Get '[JSON] PublicData
suffixToPath :: Builder -> Request -> Request -- TODO suffix
suffixToPath p req
= req { requestPath = requestPath req <> "/" <> p }
instance (KnownSymbol capture, KnownSymbol suffix, ToHttpApiData a, HasClient m api)
=> HasClient m (CaptureWithSuffix capture a suffix :> api) where
type Client m (CaptureWithSuffix capture a suffix :> api) =
a -> Client m api
clientWithRoute pm Proxy req val = -- TODO pass in suffix
clientWithRoute pm (Proxy :: Proxy api)
(suffixToPath p req)
where p = toEncodedUrlPiece val
hoistClientMonad pm _ f cl = \a ->
hoistClientMonad pm (Proxy :: Proxy api) f (cl a)
The error looks like like somewhere is missing an argument, but I don't understand it:
src/ServantClient.hs:40:3: error:
• Couldn't match type ‘Client m api0’ with ‘Client m api’
Expected type: Client m (CaptureWithSuffix capture a suffix :> api)
Actual type: a -> Client m api0
NB: ‘Client’ is a non-injective type family
The type variable ‘api0’ is ambiguous
• The equation(s) for ‘clientWithRoute’ have four arguments,
but its type ‘Proxy m
-> Proxy (CaptureWithSuffix capture a suffix :> api)
-> Request
-> Client m (CaptureWithSuffix capture a suffix :> api)’
has only three
In the instance declaration for
‘HasClient m (CaptureWithSuffix capture a suffix :> api)’
src/ServantClient.hs:46:32: error:
• Couldn't match type ‘Client mon' api1’ with ‘Client mon' api’
Expected type: Client
mon' (CaptureWithSuffix capture a suffix :> api)
Actual type: a -> Client mon' api1
NB: ‘Client’ is a non-injective type family
The type variable ‘api1’ is ambiguous
• The lambda expression ‘\ a
-> hoistClientMonad pm (Proxy :: Proxy api) f (cl a)’
has one argument,
but its type ‘Client
mon' (CaptureWithSuffix capture a suffix :> api)’
has none
In the expression:
\ a -> hoistClientMonad pm (Proxy :: Proxy api) f (cl a)
In an equation for ‘hoistClientMonad’:
hoistClientMonad pm _ f cl
= \ a -> hoistClientMonad pm (Proxy :: Proxy api) f (cl a)
How would I write this if this the way to go or is there a better way?
Hi! :)
I have a preliminary question: Would this pattern (getting a identifier without the extension) be specific to an endpoint, or is it something that many endpoints would make use of?
Many endpoints would be able to use it; in my usecase not only .json of various paths but .png is supported too, so it wouldn't be far to say other image types could be auto-generated or even some theoretical endpoint with .pdfs. This is why I was thinking of making it as general as possible, even though (I think) I can hard-code the <> ".json" in my suffixToPath function.
I figured out the compile issue! ScopedTypeVariables was missing to compile the base Capture, though I didn't figure out how to pass in the extra symbol. Instead I went with pretty much Capture except using a period in req { requestPath = requestPath req <> "." <> p } instead of a forward slash in the suffixToPath function. Is this something that would be useful as a PR to include into base servant?
@achea sorry for the late response! Sure, send us a PR. :)