diff --git a/lib/config-default.js b/lib/config-default.js index ab0705a..ea4fd76 100644 --- a/lib/config-default.js +++ b/lib/config-default.js @@ -3,10 +3,22 @@ module.exports = { "accessTokenUrl" : "https://foursquare.com/oauth2/access_token", "authenticateUrl" : "https://foursquare.com/oauth2/authenticate", "apiUrl" : "https://api.foursquare.com/v2" + /* + This field will indicate which version of the Foursquare API you wish to call. If not specified or set to "LATEST", + it will use the latest version by setting the version number to today's date. + */ + //"version" : "LATEST", + /* + This field determines how this library handles endpoints that return results along with an error, (e.g. deprecations). + - If set to "WARN" (default), log4js will write a warning to the log, (NOTE: You must raise the + "node-foursquare.core" log4js level to WARN or lower in order to see these warnings. + - If set to "ERROR", the library will behave as though it encountered an ERROR and not return results. + */ + //"warnings" : "WARN" }, "log4js" : { "levels" : { - "node-foursquare" : "OFF", + "node-foursquare" : "INFO", "node-foursquare.core" : "OFF", "node-foursquare.Users" : "OFF", "node-foursquare.Venues" : "OFF", diff --git a/lib/core.js b/lib/core.js index 43fe45a..45f3408 100644 --- a/lib/core.js +++ b/lib/core.js @@ -64,6 +64,11 @@ module.exports = function(config) { else { parsedUrl.query.oauth_token = accessToken; } + + if(config.foursquare.version) { + parsedUrl.query.v = config.foursquare.version; + } + parsedUrl.search = "?" + qs.stringify(parsedUrl.query); url = urlParser.format(parsedUrl); @@ -79,7 +84,7 @@ module.exports = function(config) { }); } - function extractData(status, result, callback) { + function extractData(url, status, result, callback) { var json; callback = callback || emptyCallback; @@ -93,6 +98,19 @@ module.exports = function(config) { } if(json.meta && json.meta.code === 200) { + if(json.meta.errorType) { + var parsedUrl = urlParser.parse(url), + message = parsedUrl.pathname + " (" + json.meta.errorType + "): " + json.meta.errorDetail || "No detail provided."; + logger.debug("Warning level set to: " + config.foursquare.warnings); + if(config.foursquare.warnings === "ERROR") { + logger.error(message); + callback(new Error(message)); + return; + } + else { + logger.warn(message); + } + } if(json.response !== undefined) { callback(null, json.response); } @@ -109,6 +127,10 @@ module.exports = function(config) { callback(new Error("Response had no code: " + sys.inspect(json))); } } + else { + logger.error("There was an unexpected, fatal error calling Foursquare: the response was undefined or had no status code."); + callback(new Error("Foursquare had no response or status code.")); + } } function callApi(path, accessToken, params, callback) { @@ -131,7 +153,7 @@ module.exports = function(config) { logger.trace("URL: " + url); } invokeApi(url, accessToken, function(error, status, result) { - extractData(status, result, callback); + extractData(url, status, result, callback); }); } diff --git a/lib/node-foursquare.js b/lib/node-foursquare.js index 3dc35e7..3236620 100644 --- a/lib/node-foursquare.js +++ b/lib/node-foursquare.js @@ -8,7 +8,8 @@ */ var qs = require('querystring'), sys = require("sys"), - defaultConfig = require("./config-default"); + defaultConfig = require("./config-default"), + log4js = require("log4js")(); function mergeDefaults(o1, o2) { for(var p in o2) { @@ -26,31 +27,45 @@ function mergeDefaults(o1, o2) { return o1; } -function configure(config) { - config = config || {}; - mergeDefaults(config, defaultConfig); - return config; -} - module.exports = function(config) { - config = configure(config); - if(!config.foursquare.accessTokenUrl || !config.foursquare.apiUrl) { - throw new TypeError("Supplied configuration is invalid."); + function getLogger(config) { + log4js.configure(config.log4js); + return log4js.getLogger("node-foursquare"); } - var core = require("./core")(config), - log4js = require("log4js")(); - log4js.configure(config.log4js); + function configure(config) { + config = config || {}; + mergeDefaults(config, defaultConfig); + + var logger = getLogger(config); + + if(!config.secrets || !config.secrets.clientId || !config.secrets.clientSecret || !config.secrets.redirectUrl) { + logger.error("Client configuration not supplied; add config.secrets information, (clientId, clientSecret, redirectUrl)."); + throw new Error("Configuration Error: Client information not supplied."); + } - var logger = log4js.getLogger("node-foursquare"); - logger.trace("Configuration: " + sys.inspect(config)); + if(!config.foursquare.accessTokenUrl || !config.foursquare.apiUrl) { + logger.error("Foursquare configuration not supplied; add config.foursquare information, (accessTokenUrl, apiUrl)"); + throw new TypeError("Configuration Error: Foursquare information not supplied."); + } + + if(!config.foursquare.version || config.foursquare.version === "LATEST") { + var d = new Date(), month = d.getMonth() + 1, date = d.getDate(); + config.foursquare.version = d.getFullYear() + ((month < 10 ? "0" : "") + month) + ((date < 10 ? "0" : "") + date); + logger.warn("Foursquare API version not defined in configuration; defaulting to latest: " + config.foursquare.version); + } - if(!config.secrets || !config.secrets.clientId || !config.secrets.clientSecret || !config.secrets.redirectUrl) { - logger.fatal("Client configuration not supplied; add config.secrets information, (client_id, client_secret, redirect_uri)."); - throw new Error("Client information not supplied."); + return config; } + config = configure(config); + + var logger = getLogger(config), + core = require("./core")(config); + + logger.debug("Configuration: " + sys.inspect(config)); + /** * Exchange a user authorization code for an access token. * @memberof module:node-foursquare diff --git a/package.json b/package.json index 56191a4..1d362ca 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "node-foursquare", "description": "Fault-tolerant Foursquare API v2 wrapper for Node JS.", - "version": "0.1.0", + "version": "0.1.1", "main": "index.js", "keywords": [ "node-foursquare", diff --git a/readme.md b/readme.md index fe814c8..0c7c601 100644 --- a/readme.md +++ b/readme.md @@ -20,6 +20,7 @@ Version History * Ability to load single portions of the library, (e.g. only import Venues). * Users - Leaderboard, Requests * Venues - Categories, Explore +* v0.1.1 - Support for Foursquare API Version + Deprecation Warnings (via configuration). Use @@ -62,6 +63,41 @@ in Foursquare. Using Express, for example: }); }); +Foursquare API Version and Deprecation Warnings +----------------------------------------------- + +Foursquare allows consumers to specify a "version" of their API to invoke, based on the date that version became active. +For example, passing a version string of "20110101" uses the API as of Jan 1, 2011. By default, this library will pass +a version of today's date. + +To enable a different version of the API, add the following to configuration. + + var config = { + ... + "foursquare" : { + ... + "version" : "20110101", + ... + } + ... + } + +When using an older API, Foursquare will provide deprecation warnings if applicable. By default, this library will log +warnings to the log, (which will only be visible if logging for "node-foursquare" is turned on, see below). + +You can configure this library to throw an error instead: + + var config = { + ... + "foursquare" : { + ... + "warnings" : "ERROR", + ... + } + ... + } + + Logging ------- @@ -77,12 +113,8 @@ INFO (and higher) messages in Venues: "levels" : { "node-foursquare.Venues" : "INFO" } - }, - "secrets" : { - "clientId" : "CLIENT_ID", - "clientSecret" : "CLIENT_SECRET", - "redirectUrl" : "REDIRECT_URL" } + ... } var foursquare = require("node-foursquare")(config); @@ -113,6 +145,9 @@ errors for protected endpoints. If you hit [http://localhost:3000](http://localhost:3000), you'll be redirected for an authentication token. +If you hit [http://localhost:3000/deprecations](http://localhost:3000/deprecations), you'll test an endpoint with older versions and +errors vs. warnings. + Testing results will be logged to the console. Documentation diff --git a/test.js b/test.js index c713b71..f602c72 100644 --- a/test.js +++ b/test.js @@ -1,2 +1,2 @@ require("./test/node-foursquare-test"); -console.log("Authenticated tests: http://localhost:3000/\nNon-authenticated tests: http://localhost:3000/test"); \ No newline at end of file +console.log("Authenticated tests: http://localhost:3000/\nNon-authenticated tests: http://localhost:3000/test\nDeprecation + Version tests: http://localhost:3000/deprecated"); \ No newline at end of file diff --git a/test/node-foursquare-test.js b/test/node-foursquare-test.js index 5d6fdcf..04cb18f 100644 --- a/test/node-foursquare-test.js +++ b/test/node-foursquare-test.js @@ -8,7 +8,6 @@ log4js.configure(config.log4js); var logger = log4js.getLogger("node-foursquare-test"), Foursquare = require('./../lib/node-foursquare')(config); - core = require("./../lib/core")(config); function reportError(test, message) { logger.error(test + " : \033[22;31mERROR: " + message + "\x1B[0m"); @@ -253,7 +252,7 @@ function TestSuite(accessToken) { else { try { logger.trace(sys.inspect(data)); - assert.ok(data.groups); + assert.ok(data.venues); ok(test); } catch (error) { reportError(test, error); @@ -586,6 +585,42 @@ function TestSuite(accessToken) { } } +function testDeprecated() { + var depConfig = require("./config").config; + + depConfig.foursquare.version = "20110101"; + depConfig.log4js.levels["node-foursquare"] = "INFO"; + depConfig.log4js.levels["node-foursquare.core"] = "WARN"; + depConfig.foursquare.warnings = "WARN"; + + var depFoursquare = Foursquare = require('./../lib/node-foursquare')(depConfig), + test = "(Version 20110101, WARN) Foursquare.Venues.search(40.7, -74)"; + + function run(error, data) { + if(error) { + reportError(test, error); + } + else { + try { + logger.trace(sys.inspect(data)); + assert.ok(data.groups); + ok(test); + } catch (error) { + reportError(test, error); + } + } + } + + depFoursquare.Venues.search("40.7", "-74", {}, null, function(error, data) { + run(error, data); + test = "(Version 20110101, ERROR) Foursquare.Venues.search(40.7, -74)", + depConfig.foursquare.warnings = "ERROR"; + depFoursquare.Venues.search("40.7", "-74", {}, null, function(error, data) { + run(error, data); + }); + }); +} + // Using express was just faster... *sigh* var app = express.createServer(); @@ -610,10 +645,17 @@ app.get('/callback', function (req, res) { }); }); +app.get("/deprecated", function(req, res) { + logger.info("\n\nTesting Version + Deprecations...\n"); + testDeprecated(); + res.send('Refer to ConsoleTesting Version + Deprecations...'); +}); + app.get("/test", function(req, res) { - var accessToken = req.query.token || null; + var accessToken = req.query.token || null, type = "Testing with" + (accessToken ? "" : "out") + " Authorization"; + logger.info("\n\n" + type + "\n"); TestSuite(accessToken).execute(); - res.send('Testing...Please check the console.'); + res.send('Refer to Console' + type + '...'); }); app.listen(3000); \ No newline at end of file