Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to Capture with .ext suffix? #1647

Open
achea opened this issue Feb 2, 2023 · 4 comments
Open

How to Capture with .ext suffix? #1647

achea opened this issue Feb 2, 2023 · 4 comments

Comments

@achea
Copy link

achea commented Feb 2, 2023

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?

@tchoutri
Copy link
Contributor

tchoutri commented Feb 2, 2023

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?

@achea
Copy link
Author

achea commented Feb 2, 2023

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.

@achea
Copy link
Author

achea commented Feb 5, 2023

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?

@tchoutri
Copy link
Contributor

@achea sorry for the late response! Sure, send us a PR. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants