diff --git a/example.js b/example.js index 684bbf6..5bfef66 100644 --- a/example.js +++ b/example.js @@ -1,28 +1,30 @@ // initialize the express application -var express = require("express"), - app = express(); +const express = require("express"); +const app = express(); // initialize the Fitbit API client -var FitbitApiClient = require("fitbit-node"), - client = new FitbitApiClient("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET"); +const FitbitApiClient = require("fitbit-node"); +const client = new FitbitApiClient({ + clientId: "YOUR_CLIENT_ID", + clientSecret: "YOUR_CLIENT_SECRET", + apiVersion: '1.2' // 1.2 is the default +}); // redirect the user to the Fitbit authorization page -app.get("/authorize", function (req, res) { - // request access to the user's activity, heartrate, location, nutrion, profile, settings, sleep, social, and weight scopes - res.redirect(client.getAuthorizeUrl('activity heartrate location nutrition profile settings sleep social weight', 'YOUR_CALLBACK_URL')); +app.get("/authorize", (req, res) => { + // request access to the user's activity, heartrate, location, nutrion, profile, settings, sleep, social, and weight scopes + res.redirect(client.getAuthorizeUrl('activity heartrate location nutrition profile settings sleep social weight', 'YOUR_CALLBACK_URL')); }); // handle the callback from the Fitbit authorization flow -app.get("/callback", function (req, res) { - // exchange the authorization code we just received for an access token - client.getAccessToken(req.query.code, 'YOUR_CALLBACK_URL').then(function (result) { - // use the access token to fetch the user's profile information - client.get("/profile.json", result.access_token).then(function (results) { - res.send(results[0]); - }); - }).catch(function (error) { - res.send(error); - }); +app.get("/callback", (req, res) => { + // exchange the authorization code we just received for an access token + client.getAccessToken(req.query.code, 'YOUR_CALLBACK_URL').then(result => { + // use the access token to fetch the user's profile information + client.get("/profile.json", result.access_token).then(results => { + res.send(results[0]); + }); + }).catch(res.send); }); // launch the server diff --git a/fitbit-api-client.js b/fitbit-api-client.js index 7749d09..6af3aac 100644 --- a/fitbit-api-client.js +++ b/fitbit-api-client.js @@ -1,199 +1,184 @@ -var OAuth2 = require('simple-oauth2'), - Q = require('q'), - Request = require('request'); - -function FitbitApiClient(clientID, clientSecret) { - this.oauth2 = OAuth2({ - clientID: clientID, - clientSecret: clientSecret, - site: 'https://api.fitbit.com/', - authorizationPath: 'oauth2/authorize', - tokenPath: 'oauth2/token', - revocationPath: 'oauth2/revoke', - useBasicAuthorizationHeader: true - }); -} - -FitbitApiClient.prototype = { - getAuthorizeUrl: function (scope, redirectUrl, prompt, state) { - return this.oauth2.authCode.authorizeURL({ - scope: scope, - redirect_uri: redirectUrl, - prompt: prompt, - state: state - }).replace('api', 'www'); - }, - - getAccessToken: function (code, redirectUrl) { - var deferred = Q.defer(); - - this.oauth2.authCode.getToken({ - code: code, - redirect_uri: redirectUrl - }, function (error, result) { - if (error) { - deferred.reject(error); - } else { - deferred.resolve(result); - } - }); - - return deferred.promise; - }, - - refreshAccessToken: function (accessToken, refreshToken, expiresIn) { - if(expiresIn === undefined) expiresIn = -1; - - var deferred = Q.defer(); - - var token = this.oauth2.accessToken.create({ - access_token: accessToken, - refresh_token: refreshToken, - expires_in: expiresIn - }); - - token.refresh(function (error, result) { - if (error) { - deferred.reject(error); - } else { - deferred.resolve(result.token); - } - }); - - return deferred.promise; - }, - - revokeAccessToken: function (accessToken) { - var deferred = Q.defer(); - - var token = this.oauth2.accessToken.create({ - access_token: accessToken, - refresh_token: '', - expires_in: '' - }); - - token.revoke('access_token', function (error, result) { - if (error) { - deferred.reject(error); - } else { - deferred.resolve(result); - } - }); - - return deferred.promise; - }, - - // extraHeaders is optional - get: function (path, accessToken, userId, extraHeaders) { - var deferred = Q.defer(); - - Request({ - url: getUrl(path, userId), - method: 'GET', - headers: mergeHeaders(accessToken, extraHeaders), - json: true - }, function(error, response, body) { - if (error) { - deferred.reject(error); - } else { - deferred.resolve([ - body, - response - ]); - } - }); - - return deferred.promise; - }, - - // extraHeaders is optional - post: function (path, accessToken, data, userId, extraHeaders) { - var deferred = Q.defer(); - - Request({ - url: getUrl(path, userId), - method: 'POST', - headers: mergeHeaders(accessToken, extraHeaders), - json: true, - form: data - }, function(error, response, body) { - if (error) { - deferred.reject(error); - } else { - deferred.resolve([ - body, - response - ]); - } - }); - - return deferred.promise; - }, - - // extraHeaders is optional - put: function (path, accessToken, data, userId, extraHeaders) { - var deferred = Q.defer(); - - Request({ - url: getUrl(path, userId), - method: 'PUT', - headers: mergeHeaders(accessToken, extraHeaders), - json: true, - form: data - }, function(error, response, body) { - if (error) { - deferred.reject(error); - } else { - deferred.resolve([ - body, - response - ]); - } - }); - - return deferred.promise; - }, - - // extraHeaders is optional - delete: function (path, accessToken, userId, extraHeaders) { - var deferred = Q.defer(); - - Request({ - url: getUrl(path, userId), - method: 'DELETE', - headers: mergeHeaders(accessToken, extraHeaders), - json: true - }, function(error, response, body) { - if (error) { - deferred.reject(error); - } else { - deferred.resolve([ - body, - response - ]); - } - }); - - return deferred.promise; - } -}; - -function getUrl(path, userId) { - return path = 'https://api.fitbit.com/1/user/' + (userId || '-') + path; -} - -function mergeHeaders(accessToken, extraHeaders) { - var headers = { - Authorization: 'Bearer ' + accessToken - }; - - if (typeof extraHeaders !== "undefined" && extraHeaders) { - for (var attrname in extraHeaders) { - headers[attrname] = extraHeaders[attrname]; - } - } - - return headers; -} - -module.exports = FitbitApiClient; +'use strict'; +const OAuth2 = require('simple-oauth2').create; +const Request = require('request'); + +module.exports = class FitbitApiClient { + constructor({clientId, clientSecret, apiVersion = '1.2'}) { + this.apiVersion = apiVersion; + this.oauth2 = OAuth2({ + client: { + id: clientId, + secret: clientSecret + }, + auth: { + tokenHost: 'https://api.fitbit.com/', + tokenPath: 'oauth2/token', + revokePath: 'oauth2/revoke', + authorizeHost: 'https://api.fitbit.com/', + authorizePath: 'oauth2/authorize' + }, + options: { + useBasicAuthorizationHeader: true + } + }); + } + + getUrl(path, userId){ + return `https://api.fitbit.com/${this.apiVersion}/user/${userId || '-'}${path}`; + } + + mergeHeaders(accessToken, extraHeaders) { + const headers = { + Authorization: 'Bearer ' + accessToken + }; + if (typeof extraHeaders !== "undefined" && extraHeaders) { + for (let attr in extraHeaders) { + if (extraHeaders.hasOwnProperty(attr)) { + headers[attr] = extraHeaders[attr]; + } + } + } + return headers; + } + + getAuthorizeUrl(scope, redirectUrl, prompt, state) { + return this.oauth2.authorizationCode.authorizeURL({ + scope: scope, + redirect_uri: redirectUrl, + prompt: prompt, + state: state + }).replace('api', 'www'); + } + + getAccessToken(code, redirectUrl) { + return new Promise((resolve, reject) => { + this.oauth2.authorizationCode.getToken({ + code: code, + redirect_uri: redirectUrl + }, (error, result) => { + if (error) { + reject(error); + } else { + resolve(result); + } + }); + }); + } + + refreshAccessToken(accessToken, refreshToken, expiresIn) { + return new Promise((resolve, reject) => { + if (expiresIn === undefined) expiresIn = -1; + const token = this.oauth2.accessToken.create({ + access_token: accessToken, + refresh_token: refreshToken, + expires_in: expiresIn + }); + token.refresh((error, result) => { + if (error) { + reject(error); + } else { + resolve(result.token); + } + }); + }); + } + + revokeAccessToken(accessToken) { + return new Promise((resolve, reject) => { + const token = this.oauth2.accessToken.create({ + access_token: accessToken, + refresh_token: '', + expires_in: '' + }); + token.revoke('access_token', (error, result) => { + if (error) { + reject(error); + } else { + resolve(result); + } + }); + }); + } + + get(path, accessToken, userId, extraHeaders) { + return new Promise((resolve, reject) => { + Request({ + url: this.getUrl(path, userId), + method: 'GET', + headers: this.mergeHeaders(accessToken, extraHeaders), + json: true + }, (error, response, body) => { + if (error) { + reject(error); + } else { + resolve([ + body, + response + ]); + } + }); + }); + } + + post(path, accessToken, data, userId, extraHeaders) { + return new Promise((resolve, reject) => { + Request({ + url: this.getUrl(path, userId), + method: 'POST', + headers: this.mergeHeaders(accessToken, extraHeaders), + json: true, + form: data + }, (error, response, body) => { + if (error) { + reject(error); + } else { + resolve([ + body, + response + ]); + } + }); + }); + } + + put(path, accessToken, data, userId, extraHeaders) { + return new Promise((resolve, reject) => { + Request({ + url: this.getUrl(path, userId), + method: 'PUT', + headers: this.mergeHeaders(accessToken, extraHeaders), + json: true, + form: data + }, (error, response, body) => { + if (error) { + reject(error); + } else { + resolve([ + body, + response + ]); + } + }); + }); + } + + delete(path, accessToken, userId, extraHeaders) { + return new Promise((resolve, reject) => { + Request({ + url: this.getUrl(path, userId), + method: 'DELETE', + headers: this.mergeHeaders(accessToken, extraHeaders), + json: true + }, (error, response, body) => { + if (error) { + reject(error); + } else { + resolve([ + body, + response + ]); + } + }); + }); + } +}; \ No newline at end of file diff --git a/package.json b/package.json index 221fc6c..766136a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fitbit-node", - "version": "2.0.5", + "version": "2.1.0", "description": "An API client library for Fitbit written in Node.js.", "main": "fitbit-api-client.js", "scripts": { @@ -20,11 +20,10 @@ "url": "https://github.com/lukasolson/fitbit-node/issues" }, "dependencies": { - "simple-oauth2": "^0.8.0", - "q": "^1.4.1", - "request": "^2.76.0" + "simple-oauth2": "^1.5.0", + "request": "^2.83.0" }, "devDependencies": { - "express": "^4.14.0" + "express": "^4.16.2" } } diff --git a/readme.md b/readme.md index ad5a399..b419c76 100644 --- a/readme.md +++ b/readme.md @@ -8,8 +8,9 @@ An API client library for Fitbit written in Node.js. ## API -#### `new FitbitApiClient(clientID, clientSecret)` -Constructor. Use the `clientID` and `clientSecret` provided to you when you registered your application on [dev.fitbit.com](http://dev.fitbit.com/). +#### `new FitbitApiClient({clientId: "YOUR_CLIENT_ID", clientSecret: "YOUR_CLIENT_SECRET", apiVersion: "1.2"})` +Constructor. Use the `clientId` and `clientSecret` provided to you when you registered your application on [dev.fitbit.com](http://dev.fitbit.com/). +The `apiVersion` is by default 1.2 #### `getAuthorizeUrl(scope, redirectUrl, [prompt], [state])` Construct the authorization URL. This is the first step of the OAuth 2.0 flow. Returns a string. When this string containing the authorization URL on the Fitbit site is returned, redirect the user to that URL for authorization. The `scope` (a string of space-delimitted scope values you wish to obtain authorization for) and the `redirectUrl` (a string for the URL where you want Fitbit to redirect the user after authorization) are required. See the [Scope](https://dev.fitbit.com/docs/oauth2/#scope) section in Fitbit's API documentation for more details about possible scope values. See the [Authorization Page](https://dev.fitbit.com/docs/oauth2/#authorization-page) section in Fitbit's API documentation for more details about possible `prompt` and `state` values.