javascriptangularjsangular-promisengresource

How to consume a RESTful API with pagination to fetch all records sequentially in AngularJS


The API I am consuming provides with the link headers as such:

</resource?page=1&limit=10>; rel="next",
</resource?page=1&limit=10>; rel="last",
</resource?page=0&limit=10>; rel="first"

And I need to consume the /resource endpoint, 10 objects at a time, in a loop, until there's no more next in link headers (last page).

I have a resource as such:

myResources.factory('MyResource', [
    '$resource',
    function($resource) {

        const ENDPOINT = '/api/resource/:id';

        return $resource(ENDPOINT, null, {
            query: {
                method: 'GET',
                isArray: true,
                interceptor: {
                    response: function(response) {
                        response.resource.headers = response.headers;
                        return response.resource;
                    }
                }
            }
        });
    }]);

and I have a service as such:

myServices.factory('MyResourceService', [
    function(MyResource) {
        return {
            findResources: function(){
                return MyResource.query().$promise;
            },
            findAllResources: function(){
                // I need to return a promise which will fetch all 
                // results from the server synchronously

                var hasNext = true;
                var params = {limit: 10, page: 0};
                var chain = $q.all();

                while(hasNext){
                    chain = chain.then(function(){
                        return MyResource.query(params).then(function(res){
                            var next = linkHeaderParser.parse(res.headers('link').next);
                            if(next) params = {limit: next.limit, page: next.page};
                            else hasNext = false;
                        }, function(){
                            hasNext = false;
                        });
                    });
                }

                return chain;
            },
            ...
        };
    }]);

Well, your eyes might hurt, because I know this is not the proper way to achieve this, since hastNext is not updated before the promise is actually executed, this leads to an infinite loop. But I couldn't get my head around it. Any help appreciated, thanks.


Solution

  • With reference to the comment by bigless, this is how I solved it:

                var getResource = function(params){
                    return MyResource
                        .query(params)
                        .$promise;
                };
    
                // Method that extracts the last page
                var pageCount = function(response){
                    var last = 
                    linkHeaderParser.parse(response.headers('link')).last;
                    return parseInt(last.page);
                };
    
                params.limit = 0;
                params.page = 0;
    
                var deferred = $q.defer();
                var resources = [];
    
                // Method that extracts the last page
                getResource(params)
                    .then(function(res){
    
                        var promises = [];
                        var lastPage = pageCount(res);
                        resources = resources.concat(res);
    
                        // Loop to get all pages
                        for(var i = params.page + 1; i <= lastPage; i++){
                            params.page = i;
                            promises.push(getResource(params));
                        }
    
                        return $q.all(promises);
                    }, function(err){
                        if(err.headers('x-pagination-count') &&
                            parseInt(err.headers('x-pagination-count')) === 0)
                            return $q.all([]);
                        }
                    })
                    .then(function(res){
    
                        // Concat results and resolve the promise
                        for (var i = 0; i < res.length; i++){
                            resources = resources.concat(res[i]);
                        }
    
                        deferred.resolve(resources);
                    });