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

Suggestion: Paginated Field #11

Open
grvhi opened this issue Aug 5, 2019 · 1 comment
Open

Suggestion: Paginated Field #11

grvhi opened this issue Aug 5, 2019 · 1 comment
Labels
question Further information is requested

Comments

@grvhi
Copy link
Contributor

grvhi commented Aug 5, 2019

I'm not sure if this will be useful to the library, but I've created a paginated_field decorator for my project to make it easier to create fields which support pagination in a consistent yet DRY manner. Essentially, the decorator wraps an Object inside a dynamically-generated Object class; the original Object becomes the data field and a page_info field is added.

Before:

{"myQuery": {"someField": "Foo", "someOtherField": "Bar"}}

After:

{"myQuery": {"data": {"someField": "Foo", "someOtherField": "Bar"}, "pageInfo": {"cursor": "some-cursor"}}}

Usage:

@paginated_field(MyObjectSchema)
def pwnd_emails(self, count: int = 10, cursor: str = None) -> Tuple[List[MyObjectSchema], str]:
        user_id = context.get().variables['user_id']
        data, cursor = get_my_objects(
            user_id, as_dicts=True, 
            limit=count, cursor=cursor
        )
        return [MyObjectSchema(**d) for d in data], cursor

Note that the method's return annotation is Tuple[List[MyObjectSchema], str]: this allows IDEs to correctly type hint and accept the method's return type. The paginated_field decorator (see below) changes the field's return type to reflect the correct GraphQL schema when the schema is generated.

So far, this is working well for me (although it is currently un-tested). If @ethe thinks it would be a useful addition to the library, I can update the code below to be more generic and to add support for more fields on PageInfo.

class PageInfo(Object):
    cursor: Optional[str]


class PaginatedObject(Object):
    page_info: PageInfo


PAGINATED_CLASSES = {}


def paginated_field(schema_type):
    new_name = f'Paginated{schema_type.__name__}'
    new_class = PAGINATED_CLASSES.get(new_name)
    if not new_class:
        new_class = make_dataclass(
            new_name,
            fields=[('data', List[schema_type])],
            bases=(PaginatedObject, )
        )
        PAGINATED_CLASSES[new_name] = new_class

    def decorator(f):
        f.__is_field__ = True
        f.__annotations__['return'] = PaginatedObject

        @wraps(f)
        def wrapper(*args, **kwargs):
            data, cursor = f(*args, **kwargs)
            # noinspection PyArgumentList
            return new_class(data=data, page_info=PageInfo(cursor=cursor))

        return wrapper

    return decorator

Suggestions welcome!

@ethe
Copy link
Owner

ethe commented Aug 5, 2019

GraphQL provides pagination support in Relay but does not give an independent component. Pygraphy may take a long time to support Relay, and I also do not want to extend GraphQL. So it is sorry for supporting the pagination query now, you can still make some custom tools to support it.

@ethe ethe added the question Further information is requested label Sep 17, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants