jsonnode.jspaginationjawbone

Jawbone API Paginated Results with 'page_token'


The Jawbone API returns paginated results of 10 json objects per result set. How does one obtain the rest of the paginated results?

The API documentation for the sleeps method indicates the existence of a page_token argument in the next object of the result set. My output below is missing this. Furthermore,the FAQ indicates this page_token takes an INT (presumably epoch) timestamp.

  • 2nd: "page_token" parameter: if the request contains the "page_token" parameter, the API will return all the workouts, in reverse order, (capped by "limit" or default of 10) that were completed before that page_token. The page_token is a timestamp, and there's a special case, when the request comes with page_token=0 which is interpreted as passing page_token = CURRENT_TIMESTAMP, ie, give all the workouts (with a limit)

I am able to authenticate with the API and return a set of 10 results (first paginated page)... but no page_token.

...snip json...
"links": {
      "next": "/nudge/api/v.1.0/users/jMdCUPXZ-InYXo1kcdOkvA/sleeps?start_time=1424699101&updated_after=0&limit=10&end_time=1438723789"
    },
    "size": 10

Have I misunderstood the documentation? Could it be the documentation is out of date (wrong)? Or more likely, I'm completely misunderstanding this and writing horrible JS for my node.js ...

Can someone set me straight and show me how I can retrieve ALL results, not just the first page?

var express     = require('express');
var app         = express();
var port        = process.env.PORT || 5000;
var passport    = require('passport');
var config      = require('./config.json');
var ejs         = require('ejs');
var https       = require('https');
var fs          = require('fs');
var bodyParser  = require('body-parser');
var jbStrategy  = require('passport-oauth').OAuth2Strategy;
var jsonfile    = require('jsonfile');
var util        = require('util');
var path        = require('path');
/* Calculate date range */
var $today      = new Date()
var $start      = new Date($today); $start.setDate($today.getDate() - 180);
var $end        = new Date($today);
var $startDate  = Math.floor(($start).getTime()/1000);
var $endDate    = Math.floor(($end).getTime()/1000);

app.use(express.logger('dev')); // log every request to the console
app.use(bodyParser.json()); // read cookies (needed for auth)
app.use(express.static(__dirname + '/public'));
app.set('view engine', 'ejs'); 
app.set('views', __dirname + '/views');
app.use(passport.initialize());

/* Default Authentication Path */
app.get('/',
    passport.authorize('jawbone', {
        scope : config.jawboneAuth.scope,
        failureRedirect: '/'
    })
);

/* oauth callback from jawbone */
app.get('/done', passport.authorize('jawbone', {
        scope : config.jawboneAuth.scope,
        failureRedirect: '/'
    }), function(req, res) {
        var result = JSON.parse(body); console.log(result);
        res.redirect('/sleeps');
    }
);

app.get('/sleeps', function(req, res) {

var options = {
    access_token    : config.jawboneAuth.accessToken,
    refresh_token   : config.jawboneAuth.refreshToken,
    client_id       : config.jawboneAuth.clientID,
    client_secret   : config.jawboneAuth.clientSecret
};

if (!config.jawboneAuth.accessToken) {
       // if there's no accessToken, go get one
       res.redirect('/');
    } else {

            var up = require('jawbone-up')(options);
            var page_token = [];

        do {

            up.sleeps.get({
                page_token :   page_token,
                start_time :   $startDate,
                end_time   :   $endDate
            }, function(err, body) {

                if (err) {
                    console.log('Error receiving Jawbone UP data');

                       res.send(err);
                } else {
                    try {
                        var result = JSON.parse(body);

                        var next_page_path = result.data.links.next;
                        //var next_page_token = next_page_path.split(path.sep);
                        //var page_token  = next_page_token[5];
                        //page_token = result.data.links.next
                        console.log(result.data);

                        res.json(result);

                    } // end try
                    catch(err) {
                        console.log(err);
                        res.render('userdata', {
                            requestTime: 0,
                            jawboneData: 'Unknown result'
                            });
                    } // end catch(err)
                    } // end else
                }  //end callback fun
            ); // end up.sleeps.get()

    } // end do
    while(page_token[0] > 1);

}   // end if
}); // end sleeps route

// Setup the passport jawbone authorization strategy
passport.use('jawbone', new jbStrategy({
    clientID        : config.jawboneAuth.clientID,
    clientSecret    : config.jawboneAuth.clientSecret,
    authorizationURL: config.jawboneAuth.authorizationURL,
    tokenURL        : config.jawboneAuth.tokenURL,
    callbackURL     : config.jawboneAuth.callbackURL,
    scope           : config.jawboneAuth.scope,
    passReqToCallback : true 
}, function(req, accessToken, refreshToken, profile, done) {
    // establish a pseudo user session.
    var user = {};
    // If there's no preexisting accessToken, 
    // write one to the config file.
    if (!config.jawboneAuth.accessToken){
        config.jawboneAuth.accessToken = accessToken;
        config.jawboneAuth.refreshToken = refreshToken;
        jsonfile.writeFile('./config.json', config, {spaces: 2},  function(err) {
        console.error(err);
        })
    }
    done(null, user);
}));

// HTTPS
var sslOptions = {
        key     : fs.readFileSync('./.server.key'),
        cert    : fs.readFileSync('./.server.crt')
    };

var secureServer = https.createServer(sslOptions, app).listen(port, function(){
    console.log('Listening on ' + port);
});

Solution

  • Turns out there is an undocumented limit parameter that has replaced the page_token.

    The Jawbone Developer documentation is currently out of date. As is their FAQ (API section Question# 12).

    A GET request like this seems to do the trick

    https://jawbone.com/nudge/api/v.1.1/users/@me/sleeps?start_time=1388603458&end_time=1420139458&limit=1000