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

Remove hard-limit on collection size #119

Open
Philonous opened this issue May 4, 2015 · 12 comments
Open

Remove hard-limit on collection size #119

Philonous opened this issue May 4, 2015 · 12 comments

Comments

@Philonous
Copy link

List Handlers using the default Range parameter have a build in upper limit of 1000 items returned. I've tried working around it with genHandler and a custom parameter, but it seems that rest-core enforces a hard-limit of 1000 on items returned and will clip the list if it is longer. This limit seems arbitrary and makes it harder to retrieve large collections wholesale when no pagination is needed (e.g. for statistical analysis).

I suggest either removing the list clipping behaviour or restricting it to cases where an explicit upper limit is given.

The offending code seems to be here:

return (List f (min c (length xs)) (take c xs))

@jonkri
Copy link

jonkri commented May 4, 2015

+1

@hesselink
Copy link
Member

I agree that this is rather arbitrary. However, you can easily work around it, as this is just the default list handler, you can easily make your own for the time being. The relevant code is actually here. If you create your own parameter parser that has different bounds or none, everything else should still work.

In the long term, we could either remove the bounds completely, make them configurable, or offer two list handlers. @bergmark What do you think?

@Philonous
Copy link
Author

I tried that and the 1000-item hard-limit still seems to be enforced.

Skimming the code it seems that a ListHandler is converted to a Handler internally (using the defaul Range parameter parsing, even when I use a customer parameter ), clipping the list in the process.

@Philonous
Copy link
Author

More specifically, my custom parameter doesn't set default values (rather, it leaves the values to be Nothing if unset, in which case all items are returned), and the internal conversion limits the number of items to 100 in this case.

@hesselink
Copy link
Member

Hmm, it seems you're right. It looks like the routing code is adding the default parameters to the list handler for some reason, probably to be able to do the slicing. I'll think about how to fix this, it doesn't look as straightforward as I initially thought.

@bergmark
Copy link
Member

bergmark commented May 5, 2015

I agree that the bound is arbitrary and shouldn't be implicit. I actually ran across this the other day as well and was confused.

I see these options for bounded listhandlers

  1. Let users define it themselves
  2. Add a api-global setting for the limit
  3. Add a mkBoundedListing :: Int -> ...
  4. Add a mk1000BoundedListing

Option 4 Is still arbitrary so no thanks. 1 will always be possible. I'm fine with either 2 or 3, we could also do both.

@bergmark
Copy link
Member

bergmark commented Jul 3, 2015

Another limitation of listings is that you have to use the predefined query parameters. offset is not always possible to respond to. In one case I want to use before or after dates instead.

@hesselink
Copy link
Member

Isn't it possible to do that using a custom handler (using mkGenHandler)?

@bergmark
Copy link
Member

bergmark commented Jul 3, 2015

Hmm yeah I think so

@jonkri
Copy link

jonkri commented Jul 28, 2015

It would also be nice if the library could support a way to inform how many items there are in total. This would make pagination easier, since the list call would include all the information necessary to figure out how many pages there are.

@bergmark
Copy link
Member

I ended up here again...

It's not possible to use mkGenHandler to define a ListHandler with an increased limit, it will still be truncated.

I think the only way to work around this is to skip the list field for the resource and use statics instead.

@bergmark
Copy link
Member

I ended up using this for now

#!/usr/bin/env stack
-- stack --resolver lts-6.10 --install-ghc runghc
{-# LANGUAGE DataKinds #-}
module UnlimitedListing where

import           Control.Monad.Except (ExceptT)
import           Rest                 (DataError (ParseError), Handler,
                                       Range (..), Reason, Void, mkHandler,
                                       mkPar, param)
import           Rest.Dictionary      (Dict, FromMaybe, Param (Param))
import           Text.Read            (readMaybe)

-- | A version of 'Rest.Handler.mkListing' with no upper limit for the
-- @count@ parameter.
--
-- Be careful not to use this for listings that are expensive to
-- compute (and if you use mkListing, enforce a maximum yourself).
mkUnlimitedListing :: Monad m
  => (Dict () () 'Nothing 'Nothing 'Nothing
  -> Dict h x i' o' e')
  -> (Range -> ExceptT (Reason (FromMaybe Void e')) m (FromMaybe () o'))
  -> Handler m
mkUnlimitedListing d a = mkHandler (mkPar unlimitedRange . d) (a . param)
  where
    unlimitedRange :: Param Range
    unlimitedRange = Param ["offset", "count"] $ \xs ->
      maybe (Left (ParseError "range"))
            (Right . normalize)
        $ case xs of
            [Just o, Just c] -> Range         <$> readMaybe o <*> readMaybe c
            [_     , Just c] -> Range 0       <$> readMaybe c
            [Just o, _     ] -> (`Range` 100) <$> readMaybe o
            _                -> Just $ Range 0 100
      where
        normalize r = Range
          { offset = max 0 . offset $ r
          , count  = max 0 . count $ r
          }

main :: IO ()
main = return ()

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

No branches or pull requests

4 participants