Permalink
Browse files

Added request signing for Google Maps API for Business users, see str…

…eetview-test.js for an example on how to set the business specific parameters
  • Loading branch information...
1 parent e7cd094 commit 500a3920665a2250e6c00e3ac59b82d42309e785 @spatical spatical committed Mar 12, 2012
Showing with 85 additions and 33 deletions.
  1. +59 −32 lib/googlemaps.js
  2. +14 −1 test/geocode-test.js
  3. +12 −0 test/streetview-test.js
View
91 lib/googlemaps.js
@@ -1,10 +1,18 @@
var qs = require('querystring'),
- request = require('request');
+ request = require('request'),
+ crypto = require("crypto"),
+ url = require('url');
var proxy;
+var google_client_id;
+var google_private_key;
exports.setProxy = function(uri) {
proxy = uri;
};
+exports.setBusinessSpecificParameters = function(client_id, private_key) {
+ google_client_id = client_id;
+ google_private_key = private_key;
+};
// http://code.google.com/apis/maps/documentation/places/
exports.places = function(latlng, radius, key, callback, sensor, types, lang, name) {
@@ -19,7 +27,7 @@ exports.places = function(latlng, radius, key, callback, sensor, types, lang, na
args.sensor = sensor || 'false';
var path = '/maps/api/place/search/json?' + qs.stringify(args);
- makeRequest(path, true, returnObjectFromJSON(callback));
+ return makeRequest(path, true, returnObjectFromJSON(callback));
};
exports.placeDetails = function(referenceId, key, callback, sensor, lang) {
@@ -31,7 +39,7 @@ exports.placeDetails = function(referenceId, key, callback, sensor, lang) {
args.sensor = sensor || 'false';
var path = '/maps/api/place/details/json?' + qs.stringify(args);
- makeRequest(path, true, returnObjectFromJSON(callback));
+ return makeRequest(path, true, returnObjectFromJSON(callback));
};
// http://code.google.com/apis/maps/documentation/geocoding/
@@ -46,7 +54,7 @@ exports.geocode = function(address , callback , sensor , bounds , region , langu
var path = '/maps/api/geocode/json?' + qs.stringify(args);
- makeRequest(path, false, returnObjectFromJSON(callback));
+ return makeRequest(path, false, returnObjectFromJSON(callback));
};
// http://code.google.com/apis/maps/documentation/geocoding/#ReverseGeocoding
@@ -59,7 +67,7 @@ exports.reverseGeocode = function(latlng , callback , sensor , language ){
var path = '/maps/api/geocode/json?' + qs.stringify(args);
- makeRequest(path, false, returnObjectFromJSON(callback));
+ return makeRequest(path, false, returnObjectFromJSON(callback));
};
// http://code.google.com/apis/maps/documentation/distancematrix/
@@ -74,7 +82,7 @@ exports.distance = function(origins, destinations, callback, sensor, mode, alter
if(units){ args.units = units; }
if(language){ args.language = language; }
var path = '/maps/api/distancematrix/json?' + qs.stringify(args);
- makeRequest(path, false, returnObjectFromJSON(callback));
+ return makeRequest(path, false, returnObjectFromJSON(callback));
};
@@ -94,7 +102,7 @@ exports.directions = function(origin , destination , callback , sensor , mode ,
var path = '/maps/api/directions/json?' + qs.stringify(args);
- makeRequest(path, false, returnObjectFromJSON(callback));
+ return makeRequest(path, false, returnObjectFromJSON(callback));
};
// http://code.google.com/apis/maps/documentation/elevation/
@@ -107,7 +115,7 @@ exports.elevationFromLocations = function(locations , callback , sensor){
var path = '/maps/api/elevation/json?' + qs.stringify(args);
- makeRequest(path, false, returnObjectFromJSON(callback));
+ return makeRequest(path, false, returnObjectFromJSON(callback));
};
// http://code.google.com/apis/maps/documentation/elevation/#Paths
@@ -120,7 +128,7 @@ exports.elevationFromPath = function(path , samples , callback , sensor){
var reqPath = '/maps/api/elevation/json?' + qs.stringify(args);
- makeRequest(reqPath, false, returnObjectFromJSON(callback));
+ return makeRequest(reqPath, false, returnObjectFromJSON(callback));
};
// http://code.google.com/apis/maps/documentation/staticmaps
@@ -187,11 +195,7 @@ exports.staticMap = function(center , zoom , size , callback , sensor ,
var path = '/maps/api/staticmap?' + qs.stringify(args);
- if( typeof( callback ) === 'function' ){
- makeRequest( path , false , callback , 'binary' );
- }
-
- return 'http://maps.googleapis.com' + path;
+ return makeRequest( path , false , callback , 'binary' );
};
// http://code.google.com/apis/maps/documentation/streetview
@@ -220,11 +224,7 @@ exports.streetView = function(size , location , callback , sensor ,
var path = '/maps/api/streetview?' + qs.stringify(args);
- if( typeof( callback ) === 'function' ){
- makeRequest( path , false , callback , 'binary' );
- }
-
- return 'http://maps.googleapis.com' + path;
+ return makeRequest( path , false , callback , 'binary' );
};
// Helper function to check and convert an array of points, be it strings/numbers/etc
@@ -263,31 +263,58 @@ exports.checkAndConvertPoint = function(input){
// Wraps the callback function to convert the output to a javascript object
var returnObjectFromJSON = function(callback){
- return function(err, jsonString){
- callback(err , JSON.parse(jsonString));
- };
+ if( typeof( callback ) === 'function' ){
+ return function(err, jsonString){
+ callback(err , JSON.parse(jsonString));
+ };
+ }
+ return false;
};
+// Sign any google request with Business/Premier signature
+var signGoogleRestApiRequest = function(urlToParse){
+ // Parse url so we can get just the pathname + search portion of the url /p/a/t/h?query=string also append client id
+ var urlObj = url.parse(urlToParse + "&client="+google_client_id);
+ // Google private keys are URL friendly base64, needs to be replaced with base64 valid characters
+ var private_key = google_private_key.replace(/-/g,'+').replace(/_/g,'/');
+ // Create signer object passing in the key, telling it the key is in base64 format
+ var signer = crypto.createHmac('sha1', new Buffer(private_key, 'base64'));
+ // Get the signature, telling it to return the sig in base64 format
+ var signature = signer.update(urlObj.path).digest('base64');
+ // Format the base64 sig in the URL friendly format required by Google
+ signature = signature.replace(/\+/g,'-').replace(/\//g,'_');
+ // Add the signature to the url (there should already always be at least the client id)
+ urlObj.search = urlObj.search + "&signature=" + signature;
+
+ return url.format(urlObj);
+}
+
// Makes the request to Google Maps API.
// If secure is true, uses https. Otherwise http is used.
var makeRequest = function(path, secure, callback, encoding){
var options = {
uri: (secure ? 'https' : 'http') + '://maps.googleapis.com' + path
};
+ if( google_client_id && google_private_key ) {
+ options.uri = signGoogleRestApiRequest(options.uri);
+ }
if( encoding ) {
options.encoding = encoding;
}
if( proxy ) {
options.proxy = proxy;
}
-
- request(options, function (error, res, data) {
- if( error ) {
- return callback(error);
- }
- if (res.statusCode === 200) {
- return callback(null, data);
- }
- return callback(new Error("Response status code: " + res.statusCode), data);
- });
+ if( typeof( callback ) === 'function' ){
+ request(options, function (error, res, data) {
+ if( error ) {
+ return callback(error);
+ }
+ if (res.statusCode === 200) {
+ return callback(null, data);
+ }
+ return callback(new Error("Response status code: " + res.statusCode), data);
+ });
+ }
+
+ return options.uri;
};
View
15 test/geocode-test.js
@@ -13,7 +13,20 @@ vows.describe('geocode').addBatch({
'returns the expected lat/lng for Chicago': function(err, result){
assert.equal(result.results[0].geometry.location.lat , 41.8781136);
assert.equal(result.results[0].geometry.location.lng , -87.6297982);
- }
+ },
+
+ 'Business Parameters URL': {
+ topic: function(options){
+ // Using the signature example clientID and private key for testing,
+ // http://code.google.com/apis/maps/documentation/business/webservices.html#signature_examples
+ gm.setBusinessSpecificParameters('clientID','vNIXE0xscrmjlyV-12Nj_BvUPaw=');
+ return gm.geocode('Chicago , Il , USA', false, false);
+ },
+ 'returns the expected street view URL': function(result){
+ assert.equal(result , "http://maps.googleapis.com/maps/api/geocode/json?address=Chicago%20%2C%20Il%20%2C%20USA&sensor=false&client=clientID&signature=m9bKYBws8BKuAO2mRf0sZWKlyPQ=");
+ }
+ }
+
}
}).export(module);
View
12 test/streetview-test.js
@@ -64,6 +64,18 @@ vows.describe('streetview').addBatch({
md5.update(data);
assert.equal(md5.digest('hex') , 'a355992522bc7d640ba605268e703e37');
}
+ },
+
+ 'Business Parameters URL': {
+ topic: function(options){
+ // Using the signature example clientID and private key for testing,
+ // http://code.google.com/apis/maps/documentation/business/webservices.html#signature_examples
+ gm.setBusinessSpecificParameters('clientID','vNIXE0xscrmjlyV-12Nj_BvUPaw=');
+ return gm.streetView('600x300', '56.960654,-2.201815', false);
+ },
+ 'returns the expected street view URL': function(result){
+ assert.equal(result , "http://maps.googleapis.com/maps/api/streetview?size=600x300&location=56.960654%2C-2.201815&sensor=false&client=clientID&signature=W-iU4lapSK7yN2qDCDXwW-GKoIo=");
+ }
}
}
}).export(module);

0 comments on commit 500a392

Please sign in to comment.