Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Updated README and unit tests.

  • Loading branch information...
commit da6de5686833a3afc57f2e6dd72bb6bb15e98dd9 1 parent 6ea6413
Clint Andrew Hall authored June 20, 2011
14  lib/config-default.js
@@ -3,10 +3,22 @@ module.exports = {
3 3
     "accessTokenUrl" : "https://foursquare.com/oauth2/access_token",
4 4
     "authenticateUrl" : "https://foursquare.com/oauth2/authenticate",
5 5
     "apiUrl" : "https://api.foursquare.com/v2"
  6
+    /*
  7
+      This field will indicate which version of the Foursquare API you wish to call. If not specified or set to "LATEST",
  8
+      it will use the latest version by setting the version number to today's date.
  9
+     */
  10
+    //"version" : "LATEST",
  11
+    /*
  12
+      This field determines how this library handles endpoints that return results along with an error, (e.g. deprecations).
  13
+        - If set to "WARN" (default), log4js will write a warning to the log, (NOTE: You must raise the
  14
+          "node-foursquare.core" log4js level to WARN or lower in order to see these warnings.
  15
+        - If set to "ERROR", the library will behave as though it encountered an ERROR and not return results.
  16
+     */
  17
+    //"warnings" : "WARN"
6 18
   },
7 19
   "log4js" : {
8 20
     "levels" : {
9  
-      "node-foursquare" : "OFF",
  21
+      "node-foursquare" : "INFO",
10 22
       "node-foursquare.core" : "OFF",
11 23
       "node-foursquare.Users" : "OFF",
12 24
       "node-foursquare.Venues" : "OFF",
26  lib/core.js
@@ -64,6 +64,11 @@ module.exports = function(config) {
64 64
     else {
65 65
       parsedUrl.query.oauth_token = accessToken;
66 66
     }
  67
+
  68
+    if(config.foursquare.version) {
  69
+      parsedUrl.query.v = config.foursquare.version;
  70
+    }
  71
+
67 72
     parsedUrl.search = "?" + qs.stringify(parsedUrl.query);
68 73
     url = urlParser.format(parsedUrl);
69 74
 
@@ -79,7 +84,7 @@ module.exports = function(config) {
79 84
       });
80 85
   }
81 86
 
82  
-  function extractData(status, result, callback) {
  87
+  function extractData(url, status, result, callback) {
83 88
     var json;
84 89
     callback = callback || emptyCallback;
85 90
 
@@ -93,6 +98,19 @@ module.exports = function(config) {
93 98
       }
94 99
 
95 100
       if(json.meta && json.meta.code === 200) {
  101
+        if(json.meta.errorType) {
  102
+          var parsedUrl = urlParser.parse(url),
  103
+            message = parsedUrl.pathname + " (" + json.meta.errorType + "): " + json.meta.errorDetail || "No detail provided.";
  104
+          logger.debug("Warning level set to: " + config.foursquare.warnings);
  105
+          if(config.foursquare.warnings === "ERROR") {
  106
+            logger.error(message);
  107
+            callback(new Error(message));
  108
+            return;
  109
+          }
  110
+          else {
  111
+            logger.warn(message);
  112
+          }
  113
+        }
96 114
         if(json.response !== undefined) {
97 115
           callback(null, json.response);
98 116
         }
@@ -109,6 +127,10 @@ module.exports = function(config) {
109 127
         callback(new Error("Response had no code: " + sys.inspect(json)));
110 128
       }
111 129
     }
  130
+    else {
  131
+      logger.error("There was an unexpected, fatal error calling Foursquare: the response was undefined or had no status code.");
  132
+      callback(new Error("Foursquare had no response or status code."));
  133
+    }
112 134
   }
113 135
 
114 136
   function callApi(path, accessToken, params, callback) {
@@ -131,7 +153,7 @@ module.exports = function(config) {
131 153
       logger.trace("URL: " + url);
132 154
     }
133 155
     invokeApi(url, accessToken, function(error, status, result) {
134  
-      extractData(status, result, callback);
  156
+      extractData(url, status, result, callback);
135 157
     });
136 158
   }
137 159
   
51  lib/node-foursquare.js
@@ -8,7 +8,8 @@
8 8
  */
9 9
 var qs = require('querystring'),
10 10
   sys = require("sys"),
11  
-  defaultConfig = require("./config-default");
  11
+  defaultConfig = require("./config-default"),
  12
+  log4js = require("log4js")();
12 13
 
13 14
 function mergeDefaults(o1, o2) {
14 15
   for(var p in o2) {
@@ -26,31 +27,45 @@ function mergeDefaults(o1, o2) {
26 27
   return o1;
27 28
 }
28 29
 
29  
-function configure(config) {
30  
-  config = config || {};
31  
-  mergeDefaults(config, defaultConfig);
32  
-  return config;
33  
-}
34  
-
35 30
 module.exports = function(config) {
36  
-  config = configure(config);
37 31
 
38  
-  if(!config.foursquare.accessTokenUrl || !config.foursquare.apiUrl) {
39  
-    throw new TypeError("Supplied configuration is invalid.");
  32
+  function getLogger(config) {
  33
+    log4js.configure(config.log4js);
  34
+    return log4js.getLogger("node-foursquare");
40 35
   }
41 36
 
42  
-  var core = require("./core")(config),
43  
-    log4js = require("log4js")();
44  
-    log4js.configure(config.log4js);
  37
+  function configure(config) {
  38
+    config = config || {};
  39
+    mergeDefaults(config, defaultConfig);
  40
+    
  41
+    var logger = getLogger(config);
  42
+
  43
+    if(!config.secrets || !config.secrets.clientId || !config.secrets.clientSecret || !config.secrets.redirectUrl) {
  44
+      logger.error("Client configuration not supplied; add config.secrets information, (clientId, clientSecret, redirectUrl).");
  45
+      throw new Error("Configuration Error: Client information not supplied.");
  46
+    }
45 47
 
46  
-  var logger = log4js.getLogger("node-foursquare");
47  
-  logger.trace("Configuration: " + sys.inspect(config));
  48
+    if(!config.foursquare.accessTokenUrl || !config.foursquare.apiUrl) {
  49
+      logger.error("Foursquare configuration not supplied; add config.foursquare information, (accessTokenUrl, apiUrl)");
  50
+      throw new TypeError("Configuration Error: Foursquare information not supplied.");
  51
+    }
  52
+
  53
+    if(!config.foursquare.version || config.foursquare.version === "LATEST") {
  54
+      var d = new Date(), month = d.getMonth() + 1, date = d.getDate();
  55
+      config.foursquare.version = d.getFullYear() + ((month < 10 ? "0" : "") + month) + ((date < 10 ? "0" : "") + date);
  56
+      logger.warn("Foursquare API version not defined in configuration; defaulting to latest: " + config.foursquare.version);
  57
+    }
48 58
 
49  
-  if(!config.secrets || !config.secrets.clientId || !config.secrets.clientSecret || !config.secrets.redirectUrl) {
50  
-    logger.fatal("Client configuration not supplied; add config.secrets information, (client_id, client_secret, redirect_uri).");
51  
-    throw new Error("Client information not supplied.");
  59
+    return config;
52 60
   }
53 61
 
  62
+  config = configure(config);
  63
+
  64
+  var logger = getLogger(config),
  65
+    core = require("./core")(config);
  66
+
  67
+  logger.debug("Configuration: " + sys.inspect(config));
  68
+
54 69
   /**
55 70
    * Exchange a user authorization code for an access token.
56 71
    * @memberof module:node-foursquare
2  package.json
... ...
@@ -1,7 +1,7 @@
1 1
 {
2 2
   "name": "node-foursquare",
3 3
   "description": "Fault-tolerant Foursquare API v2 wrapper for Node JS.",
4  
-  "version": "0.1.0",
  4
+  "version": "0.1.1",
5 5
   "main": "index.js",
6 6
   "keywords": [
7 7
     "node-foursquare",
45  readme.md
Source Rendered
@@ -20,6 +20,7 @@ Version History
20 20
     * Ability to load single portions of the library, (e.g. only import Venues).
21 21
     * Users - Leaderboard, Requests
22 22
     * Venues - Categories, Explore
  23
+* v0.1.1 - Support for Foursquare API Version + Deprecation Warnings (via configuration).
23 24
 
24 25
 
25 26
 Use
@@ -62,6 +63,41 @@ in Foursquare.  Using Express, for example:
62 63
       });
63 64
     });
64 65
 
  66
+Foursquare API Version and Deprecation Warnings
  67
+-----------------------------------------------
  68
+
  69
+Foursquare allows consumers to specify a "version" of their API to invoke, based on the date that version became active.
  70
+For example, passing a version string of "20110101" uses the API as of Jan 1, 2011.  By default, this library will pass
  71
+a version of today's date.
  72
+
  73
+To enable a different version of the API, add the following to configuration.
  74
+
  75
+    var config = {
  76
+      ...
  77
+      "foursquare" : {
  78
+        ...
  79
+        "version" : "20110101",
  80
+        ...
  81
+      }
  82
+      ...
  83
+    }
  84
+
  85
+When using an older API, Foursquare will provide deprecation warnings if applicable. By default, this library will log
  86
+warnings to the log, (which will only be visible if logging for "node-foursquare" is turned on, see below).
  87
+
  88
+You can configure this library to throw an error instead:
  89
+
  90
+    var config = {
  91
+      ...
  92
+      "foursquare" : {
  93
+        ...
  94
+        "warnings" : "ERROR",
  95
+        ...
  96
+      }
  97
+      ...
  98
+    }
  99
+
  100
+
65 101
 Logging
66 102
 -------
67 103
 
@@ -77,12 +113,8 @@ INFO (and higher) messages in Venues:
77 113
         "levels" : {
78 114
           "node-foursquare.Venues" : "INFO"
79 115
         }
80  
-      },
81  
-      "secrets" : {
82  
-        "clientId" : "CLIENT_ID",
83  
-        "clientSecret" : "CLIENT_SECRET",
84  
-        "redirectUrl" : "REDIRECT_URL"
85 116
       }
  117
+      ...
86 118
     }
87 119
 
88 120
     var foursquare = require("node-foursquare")(config);
@@ -113,6 +145,9 @@ errors for protected endpoints.
113 145
 
114 146
 If you hit [http://localhost:3000](http://localhost:3000), you'll be redirected for an authentication token.
115 147
 
  148
+If you hit [http://localhost:3000/deprecations](http://localhost:3000/deprecations), you'll test an endpoint with older versions and
  149
+errors vs. warnings.
  150
+
116 151
 Testing results will be logged to the console.
117 152
 
118 153
 Documentation
2  test.js
... ...
@@ -1,2 +1,2 @@
1 1
 require("./test/node-foursquare-test");
2  
-console.log("Authenticated tests: http://localhost:3000/\nNon-authenticated tests: http://localhost:3000/test");
  2
+console.log("Authenticated tests: http://localhost:3000/\nNon-authenticated tests: http://localhost:3000/test\nDeprecation + Version tests: http://localhost:3000/deprecated");
50  test/node-foursquare-test.js
@@ -8,7 +8,6 @@ log4js.configure(config.log4js);
8 8
 
9 9
 var logger = log4js.getLogger("node-foursquare-test"),
10 10
   Foursquare = require('./../lib/node-foursquare')(config);
11  
-  core = require("./../lib/core")(config);
12 11
 
13 12
 function reportError(test, message) {
14 13
   logger.error(test + " :  \033[22;31mERROR: " + message + "\x1B[0m");
@@ -253,7 +252,7 @@ function TestSuite(accessToken) {
253 252
       else {
254 253
         try {
255 254
           logger.trace(sys.inspect(data));
256  
-          assert.ok(data.groups);
  255
+          assert.ok(data.venues);
257 256
           ok(test);
258 257
         } catch (error) {
259 258
           reportError(test, error);
@@ -586,6 +585,42 @@ function TestSuite(accessToken) {
586 585
   }
587 586
 }
588 587
 
  588
+function testDeprecated() {
  589
+  var depConfig = require("./config").config;
  590
+
  591
+  depConfig.foursquare.version = "20110101";
  592
+  depConfig.log4js.levels["node-foursquare"] = "INFO";
  593
+  depConfig.log4js.levels["node-foursquare.core"] = "WARN";
  594
+  depConfig.foursquare.warnings = "WARN";
  595
+  
  596
+  var depFoursquare =   Foursquare = require('./../lib/node-foursquare')(depConfig),
  597
+    test = "(Version 20110101, WARN) Foursquare.Venues.search(40.7, -74)";
  598
+
  599
+  function run(error, data) {
  600
+    if(error) {
  601
+      reportError(test, error);
  602
+    }
  603
+    else {
  604
+      try {
  605
+        logger.trace(sys.inspect(data));
  606
+        assert.ok(data.groups);
  607
+        ok(test);
  608
+      } catch (error) {
  609
+        reportError(test, error);
  610
+      }
  611
+    }
  612
+  }
  613
+
  614
+  depFoursquare.Venues.search("40.7", "-74", {}, null, function(error, data) {
  615
+    run(error, data);
  616
+    test = "(Version 20110101, ERROR) Foursquare.Venues.search(40.7, -74)",
  617
+    depConfig.foursquare.warnings = "ERROR";
  618
+    depFoursquare.Venues.search("40.7", "-74", {}, null, function(error, data) {
  619
+      run(error, data);
  620
+    });
  621
+  });
  622
+}
  623
+
589 624
 // Using express was just faster... *sigh*
590 625
 var app = express.createServer();
591 626
 
@@ -610,10 +645,17 @@ app.get('/callback', function (req, res) {
610 645
   });
611 646
 });
612 647
 
  648
+app.get("/deprecated", function(req, res) {
  649
+  logger.info("\n\nTesting Version + Deprecations...\n");
  650
+  testDeprecated();
  651
+  res.send('<html></html><title>Refer to Console</title><body>Testing Version + Deprecations...</body></html>');
  652
+});
  653
+
613 654
 app.get("/test", function(req, res) {
614  
-  var accessToken = req.query.token || null;
  655
+  var accessToken = req.query.token || null, type = "Testing with" + (accessToken ? "" : "out") + " Authorization";
  656
+  logger.info("\n\n" + type + "\n");
615 657
   TestSuite(accessToken).execute();
616  
-  res.send('<html></html><title>Testing...</title><body>Please check the console.</body></html>');
  658
+  res.send('<html></html><title>Refer to Console</title><body>' + type + '...</body></html>');
617 659
 });
618 660
 
619 661
 app.listen(3000);

0 notes on commit da6de56

Please sign in to comment.
Something went wrong with that request. Please try again.