Yelp / bravado

Bravado is a python client library for Swagger 2.0 services
Other
605 stars 117 forks source link

Handling next and previous pagination of results #352

Open bdowling opened 6 years ago

bdowling commented 6 years ago

I'm looking for an example of dealing with pagination in the case where the API provides a next/previous URL for you to follow.

It is not clear to me if there is a streamlined way of handling this in Bravado already, but I was attempting to craft a wrapper class that would handle this in my schema. If there is and someone can point me to the light I'd be happy to write some additional docs around this useful case.

I have done it initially for a single operation, but I am trying so see if there is an easy way to do this for all operations more generically.

Is there a way to pass back the URL and have it process it per the spec/model as defined? What I did in my test was parse out the params (cursor) in the URL and craft a new operation request, but it feels like there should be a simpler shortcut here.

I'm sure this has been encountered, so any input you can provide would be helpful.

The latest 3.0 OpenAPI specs try to address this with links, wondering if a x-links feature may make this suit the generic case (where other URLs to other Models in the API also are returned).

bdowling commented 6 years ago

Thoughts on this?

bdowling commented 6 years ago

@macisamuele @sjaensch have any input on this? Seems like a common need, lots of APIs are designed to return URL refs back into the API, next/prev is just another case of that. How can one generically map those URLs back into the bravado client so you can fetch the model they point to? There is a convert_path_to_resource method, but this doesn't go deeper into the operation or path parameters embedded in the URL.

This seems common enough that their could be generic support for handling this?

bdowling commented 6 years ago

I came up with this, feels like I'm doing something that there should already be an easier way to do. Ideally I was thinking that with the format: url and x-operationId: some_op that I added into my schema, I could return back a custom type for the URLs in the response that could have a next.fetch() function. Looks like currently unmarshall_schema_object isn't passed enough info though to re-implement what I did here in that context.

    def fetch_op_from_url(self, data, key, req, res):
        # lookup the last op response_spec
        # check the content_spec for x-operationId on key
        # call the model_op on the url
        url = data[key]
        op = req.operation
        response_spec = get_response_spec(status_code=res.status_code, op=op)
        content_spec = op.swagger_spec.deref(response_spec['schema'])

        model_op_id = content_spec['properties'][key].get('x-operationId', op.operation_id)
        if model_op_id == op.operation_id:
            # short circuit, common for next/prev links, etc
            model_op = op
        else:
            # Find it on another resource, surprised there is no single dict
            # for operationId since it is required to be unique across spec
            for resource in self.swagger_spec.resources.values():
                model_op = resource.operations.get(model_op_id)
                if model_op:
                    break

        http_client = op.swagger_spec.http_client
        return http_client.request({'method': 'GET', 'url': url},
                                   operation=model_op,
                                   also_return_response=True)
sjaensch commented 6 years ago

@bdowling I guess the main reason there isn't a single dict to look up operation IDs is that up to this point bravado doesn't need it. :) This could be an interesting feature, and if it doesn't cause too many issues in terms of code architecture, I could see bravado adopting this feature from OpenAPI 3.0. Pull requests always welcome. ;)