servant icon indicating copy to clipboard operation
servant copied to clipboard

How to define a type for polymorphic endpoint?

Open yaitskov opened this issue 2 years ago • 1 comments

Hi,

I suspect the following usecase is not supported by servant well. I define Servant API type to talk to Gitlab API. E.g. List merge request endpoint returns MRs in different states so there are different fields. I would like to define independent Haskell datas. MRs can be filtered by state through QueryParam.

  • I cannot set value for QueryParam in types.
  • I cannot append unescaped string ("?state=merged") to generated URL
  • I cannot define endpoint returning values of different types depending on QueryParam value

Workaround is to define 2 Types for the same endpoint and take care of proper values for "synthetic" argument to make its value consistent with result type.

data MrState = MrStateMerged | MrStateClosed deriving (Show, Read, Eq, Ord)
type PidCap = Capture "pid" GitLabProjectId
type MrStateParam = QueryParam "state" MrState
type ListMergedMrs = "projects" :> PidCap :> "merge_requests" :> MrStateParam :> Get '[JSON] [GlMergedMr]

type ListClosedMrs = "projects" :> PidCap :> "merge_requests" :> MrStateParam :> Get '[JSON] [GlClosedMr]

api :: Proxy GitLabApiV4
api = Proxy

-- generated API is unsafe 
listMergedMrs' :: GitLabProjectId -> Maybe MrState -> ClientM [GlMergedMr]
listClosedMrs' :: GitLabProjectId -> Maybe MrState ->  ClientM [GlClosedMr]
listMergedMrs' :<|> listClosedMrs' = client api

-- safe wrappers
listMergedMrs pid = listMergedMrs' pid (Just MrStateMerged)
listClosedMrs pid = listClosedMrs' pid (Just MrStateClosed)

yaitskov avatar Feb 10 '22 01:02 yaitskov

@yaitskov Some folks looked into this kind of problem (server side, but same idea) a while back: https://www.well-typed.com/blog/2015/12/dependently-typed-servers/

The internals have changed but maybe this kind of trick could still be applied.

alpmestan avatar Feb 11 '22 18:02 alpmestan