Some refactoring

This commit is contained in:
goferito 2015-05-24 00:50:48 +02:00
parent 94398b9d45
commit 79098f0c93
3 changed files with 282 additions and 232 deletions

View file

@ -16,81 +16,94 @@ var request = require('request'),
*/ */
var Bing = function (options) { var Bing = function (options) {
if (!(this instanceof Bing)) return new Bing(options); if (!(this instanceof Bing)) return new Bing(options);
var defaults = { var defaults = {
//Bing Search API URI //Bing Search API URI
rootUri: "https://api.datamarket.azure.com/Bing/Search/v1/", rootUri: "https://api.datamarket.azure.com/Bing/Search/v1/",
//Account Key //Account Key
accKey: null, accKey: null,
//Bing UserAgent //Bing UserAgent
userAgent: 'Bing Search Client for Node.js', userAgent: 'Bing Search Client for Node.js',
//Request Timeout //Request Timeout
reqTimeout: 5000, reqTimeout: 5000,
// Number of results (limited to 50 by API) // Number of results (limited to 50 by API)
top: 50, top: 50,
// Number of skipped results (pagination) // Number of skipped results (pagination)
skip: 0 skip: 0
}; };
//merge options passed in with defaults //merge options passed in with defaults
this.options = _.extend(defaults, options); this.options = _.extend(defaults, options);
this.searchVertical = function (query, vertical, options, callback) { this.searchVertical = function (query, vertical, options, callback) {
if (typeof options === 'function') {
callback = options;
}
if (typeof callback != 'function') {
throw "Error: Callback function required!";
}
// Create a copy of the options, to avoid permanent overwrites if (typeof options === 'function') {
var opts = JSON.parse(JSON.stringify(this.options)); callback = options;
}
if (typeof callback != 'function') {
throw "Error: Callback function required!";
}
if (typeof options === 'object') { // Create a copy of the options, to avoid permanent overwrites
_.extend(opts, options); var opts = JSON.parse(JSON.stringify(this.options));
}
var reqUri = opts.rootUri + vertical + "?$format=json&" + qs.stringify({ if (typeof options === 'object') {
"Query": "'" + query + "'" _.extend(opts, options);
}) + "&$top=" + opts.top + "&$skip=" + opts.skip + (opts.sources ? "&Sources=%27" + opts.sources + "%27" : '') + (opts.newssortby ? "&NewsSortBy=%27" + opts.newssortby + "%27" : '') + (opts.newscategory ? "&NewsCategory=%27" + opts.newscategory + "%27" : '') + (opts.newslocationoverride ? "&NewsLocationOverride=%27" + opts.newslocationoverride + "%27" : '') + (opts.market ? "&Market=%27" + opts.market + "%27" : '') + (opts.adult ? "&Adult=%27" + opts.adult + "%27" : '') + (opts.imagefilters ? '&' + qs.stringify({ }
"ImageFilters": "'" + opts.imagefilters + "'"
}) : '') + (opts.videofilters ? '&' + qs.stringify({
"VideoFilters": "'" + opts.videofilters + "'"
}) : '');
request({ var reqUri = opts.rootUri
uri: reqUri, + vertical
method: opts.method || "GET", + "?$format=json&" + qs.stringify({ "Query": "'" + query + "'" })
headers: { + "&$top=" + opts.top
"User-Agent": opts.userAgent + "&$skip=" + opts.skip
}, + (opts.sources ? "&Sources=%27" + opts.sources + "%27" : '')
auth: { + (opts.newssortby ? "&NewsSortBy=%27" + opts.newssortby + "%27" : '')
user: opts.accKey, + (opts.newscategory ? "&NewsCategory=%27" + opts.newscategory + "%27" : '')
pass: opts.accKey + (opts.newslocationoverride
}, ? "&NewsLocationOverride=%27" + opts.newslocationoverride + "%27"
timeout: opts.reqTimeout : '')
+ (opts.market ? "&Market=%27" + opts.market + "%27" : '')
+ (opts.adult ? "&Adult=%27" + opts.adult + "%27" : '')
+ (opts.imagefilters
? '&' + qs.stringify({ "ImageFilters": "'" + opts.imagefilters + "'" })
: '')
+ (opts.videofilters
? '&' + qs.stringify({ "VideoFilters": "'" + opts.videofilters + "'" })
: '');
}, function (err, res, body) { request({
uri: reqUri,
method: opts.method || "GET",
headers: {
"User-Agent": opts.userAgent
},
auth: {
user: opts.accKey,
pass: opts.accKey
},
timeout: opts.reqTimeout
if (res && res.statusCode !== 200) { }, function (err, res, body) {
err = new Error(body);
} else {
// Parse body, if body if (res && res.statusCode !== 200) {
body = typeof body === 'string' ? JSON.parse(body) : body; err = new Error(body);
} } else {
callback(err, res, body); // Parse body, if body
}); body = typeof body === 'string' ? JSON.parse(body) : body;
}; }
callback(err, res, body);
});
};
}; };
@ -118,7 +131,7 @@ var Bing = function (options) {
* @function * @function
*/ */
Bing.prototype.web = function (query, options, callback) { Bing.prototype.web = function (query, options, callback) {
this.searchVertical(query, "Web", options, callback); this.searchVertical(query, "Web", options, callback);
}; };
// Alias Bing.search to Bing.web // Alias Bing.search to Bing.web
@ -140,7 +153,7 @@ Bing.prototype.search = Bing.prototype.web;
* @function * @function
*/ */
Bing.prototype.composite = function (query, options, callback) { Bing.prototype.composite = function (query, options, callback) {
this.searchVertical(query, "Composite", options, callback); this.searchVertical(query, "Composite", options, callback);
}; };
/** /**
@ -157,7 +170,7 @@ Bing.prototype.composite = function (query, options, callback) {
* @function * @function
*/ */
Bing.prototype.news = function (query, options, callback) { Bing.prototype.news = function (query, options, callback) {
this.searchVertical(query, "News", options, callback); this.searchVertical(query, "News", options, callback);
}; };
/** /**
@ -174,18 +187,21 @@ Bing.prototype.news = function (query, options, callback) {
* @function * @function
*/ */
Bing.prototype.video = function (query, options, callback) { Bing.prototype.video = function (query, options, callback) {
if (options && typeof options === 'object' && options.videofilters && typeof options.videofilters === 'object') { if (options
var filterQuery = ''; && typeof options === 'object'
var filters = Object.keys(options.videofilters); && options.videofilters
filters.map(function (key, i) { && typeof options.videofilters === 'object') {
filterQuery += capitalizeFirstLetter(key) + ':';
filterQuery += capitalizeFirstLetter(options.videofilters[key]); var filterQuery = Object.keys(options.videofilters)
if (i < filters.length - 1) .map(function(key){
filterQuery += '+'; return capitalise(key) + ':'
}); + capitalise(options.videofilters[key]);
options.videofilters = filterQuery; })
} .join('+');
this.searchVertical(query, "Video", options, callback);
options.videofilters = filterQuery;
}
this.searchVertical(query, "Video", options, callback);
}; };
@ -205,22 +221,35 @@ Bing.prototype.video = function (query, options, callback) {
* @function * @function
*/ */
Bing.prototype.images = function (query, options, callback) { Bing.prototype.images = function (query, options, callback) {
if (options && typeof options === 'object' && options.imagefilters && typeof options.imagefilters === 'object') { if (options
var filterQuery = ''; && typeof options === 'object'
var filters = Object.keys(options.imagefilters); && options.imagefilters
filters.map(function (key, i) { && typeof options.imagefilters === 'object') {
filterQuery += capitalizeFirstLetter(key) + ':';
filterQuery += capitalizeFirstLetter(options.imagefilters[key]); var filterQuery = Object.keys(options.imagefilters)
if (i < filters.length - 1) .map(function(key){
filterQuery += '+'; return capitalise(key) + ':'
}); + capitalise(options.imagefilters[key]);
options.imagefilters = filterQuery; })
} .join('+');
this.searchVertical(query, "Image", options, callback);
options.imagefilters = filterQuery;
}
this.searchVertical(query, "Image", options, callback);
}; };
function capitalizeFirstLetter(s) {
return s.charAt(0).toUpperCase() + s.slice(1); /**
* Capitalises the first word of the passed string
*
* @param {String} s String to be capitalised
*
* @funtion
*/
function capitalise(s) {
return s.charAt(0).toUpperCase() + s.slice(1);
} }
module.exports = Bing; module.exports = Bing;

View file

@ -1,28 +1,28 @@
var validWebResponse = { var validWebResponse = {
"d": { "d": {
"results": [{ "results": [{
"__metadata": { "__metadata": {
"uri": "https://api.datamarket.azure.com/Data.ashx/Bing/Search/Web?Query='xbox'&$skip=0&$top=1", "uri": "https://api.datamarket.azure.com/Data.ashx/Bing/Search/Web?Query='xbox'&$skip=0&$top=1",
"type": "WebResult" "type": "WebResult"
}, },
"ID": "26888c2a-d245-47dc-87de-dc3551249de7", "ID": "26888c2a-d245-47dc-87de-dc3551249de7",
"Title": "Xbox | Games and Entertainment on All Your Devices", "Title": "Xbox | Games and Entertainment on All Your Devices",
"Description": "Experience the new generation of games and entertainment with Xbox. Play Xbox games and stream video on all your devices.", "Description": "Experience the new generation of games and entertainment with Xbox. Play Xbox games and stream video on all your devices.",
"DisplayUrl": "www.xbox.com", "DisplayUrl": "www.xbox.com",
"Url": "http://www.xbox.com/" "Url": "http://www.xbox.com/"
}, { }, {
"__metadata": { "__metadata": {
"uri": "https://api.datamarket.azure.com/Data.ashx/Bing/Search/Web?Query='xbox'&$skip=1&$top=1", "uri": "https://api.datamarket.azure.com/Data.ashx/Bing/Search/Web?Query='xbox'&$skip=1&$top=1",
"type": "WebResult" "type": "WebResult"
}, },
"ID": "ff23e110-31c2-44f9-be21-f213bcd4c654", "ID": "ff23e110-31c2-44f9-be21-f213bcd4c654",
"Title": "Amazon.com: Xbox - More Systems: Video Games: Games ...", "Title": "Amazon.com: Xbox - More Systems: Video Games: Games ...",
"Description": "Online shopping for Video Games from a great selection of Games, Hardware, Computer And Console Video Game Products & more at everyday low prices.", "Description": "Online shopping for Video Games from a great selection of Games, Hardware, Computer And Console Video Game Products & more at everyday low prices.",
"DisplayUrl": "www.amazon.com/Xbox-Games/b?ie=UTF8&node=537504", "DisplayUrl": "www.amazon.com/Xbox-Games/b?ie=UTF8&node=537504",
"Url": "http://www.amazon.com/Xbox-Games/b?ie=UTF8&node=537504" "Url": "http://www.amazon.com/Xbox-Games/b?ie=UTF8&node=537504"
}], }],
"__next": "https://api.datamarket.azure.com/Data.ashx/Bing/Search/Web?Query='xbox'&$skip=2" "__next": "https://api.datamarket.azure.com/Data.ashx/Bing/Search/Web?Query='xbox'&$skip=2"
} }
}; };
var should = require('should'), var should = require('should'),
@ -32,62 +32,69 @@ var should = require('should'),
bing = require('../lib/bing'); bing = require('../lib/bing');
describe('Bing', function () { describe('Bing', function () {
var server; var server;
var app; var app;
var port = 4321; var port = 4321;
before(function (done) { before(function (done) {
app = express(); app = express();
server = http.createServer(app); server = http.createServer(app);
server.listen.apply(server, [port, server.listen.apply(server, [port,
function (err, result) { function (err, result) {
if (err) { if (err) {
done(err); done(err);
} else { } else {
done(); done();
} }
} }
]); ]);
});
after(function (done) {
server.close();
app = null;
server = null;
done();
});
it('should cope with valid responses', function (done) {
app.get('/hello/Web', function (req, res) {
res.status(200).send(JSON.stringify(validWebResponse));
}); });
after(function (done) { var bingClient = bing({
server.close(); rootUri: 'http://localhost:' + port + '/hello/',
app = null; accKey: '123'
server = null;
done();
}); });
it('should cope with valid responses', function (done) { bingClient.search('xbox', function (error, response, body) {
app.get('/hello/Web', function (req, res) { response.statusCode.should.eql(200);
res.status(200).send(JSON.stringify(validWebResponse)); body.should.eql(validWebResponse);
}); done();
var bingClient = bing({ });
rootUri: 'http://localhost:' + port + '/hello/', });
accKey: '123'
}); it('should cope with errors', function (done) {
bingClient.search('xbox', function (error, response, body) {
response.statusCode.should.eql(200); // No actual data on what the failure looks like.
body.should.eql(validWebResponse); var failure = {
done(); message: 'Failed request'
}); };
app.get('/hello/Image', function (req, res) {
res.status(500).send(failure);
});
var bingClient = bing({
rootUri: 'http://localhost:' + port + '/hello/',
accKey: '123'
}); });
it('should cope with errors', function (done) { bingClient.images('xbox', function (error, response, body) {
// No actual data on what the failure looks like. response.statusCode.should.eql(500);
var failure = { body.should.eql(JSON.stringify(failure));
message: 'Failed request' done();
};
app.get('/hello/Image', function (req, res) {
res.status(500).send(failure);
});
var bingClient = bing({
rootUri: 'http://localhost:' + port + '/hello/',
accKey: '123'
});
bingClient.images('xbox', function (error, response, body) {
response.statusCode.should.eql(500);
body.should.eql(JSON.stringify(failure));
done();
});
}); });
});
}); });

View file

@ -3,131 +3,145 @@
// should still run the basic tests; thus throwing an exception must // should still run the basic tests; thus throwing an exception must
// be avoided. // be avoided.
try { try {
var accKey = require('./secrets').accKey; var accKey = require('./secrets').accKey;
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
if (!accKey) { if (!accKey) {
return console.error("Need to include an access key in your secrets.js"); return console.error("Need to include an access key in your secrets.js");
} }
var Bing = require('../')({ var Bing = require('../')({ accKey: accKey })
accKey: accKey , should = require('should')
});
var should = require('should');
describe("Bing Search", function () { describe("Bing Search", function () {
this.timeout(1000 * 10); this.timeout(1000 * 10);
it('works without options', function (done) { it('works without options', function (done) {
Bing.search('monkey vs frog', function (err, res, body) { Bing.search('monkey vs frog', function (err, res, body) {
should.not.exist(err); should.not.exist(err);
should.exist(res); should.exist(res);
should.exist(body); should.exist(body);
body.d.results.should.have.length(50); body.d.results.should.have.length(50);
//TODO check it contains the right fields //TODO check it contains the right fields
done(); done();
});
}); });
});
it('finds only 5 results', function (done) { it('finds only 5 results', function (done) {
Bing.search('monkey vs frog', { Bing.search('monkey vs frog',
top: 5, {
market: 'en-US', top: 5,
adult: 'Strict' market: 'en-US',
}, function (err, res, body) { adult: 'Strict'
should.not.exist(err); },
should.exist(res); function (err, res, body) {
should.exist(body);
body.d.results.should.have.length(5); should.not.exist(err);
should.exist(res);
should.exist(body);
done(); body.d.results.should.have.length(5);
});
done();
}); });
});
}); });
describe("Bing Images", function () { describe("Bing Images", function () {
this.timeout(1000 * 10); this.timeout(1000 * 10);
it('finds images with specific options', function (done) { it('finds images with specific options', function (done) {
Bing.images('pizza', { Bing.images('pizza',
top: 3, {
adult: 'Off', top: 3,
imagefilters: { adult: 'Off',
size: 'small', imagefilters: {
color: 'monochrome' size: 'small',
} color: 'monochrome'
}, function (err, res, body) { }
should.not.exist(err); },
should.exist(res); function (err, res, body) {
should.exist(body);
body.d.results.should.have.length(3); should.not.exist(err);
should.exist(res);
should.exist(body);
done(); body.d.results.should.have.length(3);
});
done();
}); });
});
}); });
describe("Bing News", function () { describe("Bing News", function () {
this.timeout(1000 * 10); this.timeout(1000 * 10);
it('finds news with specific options', function (done) { it('finds news with specific options', function (done) {
Bing.news('ps4', { Bing.news('ps4',
top: 10, {
skip: 1, top: 10,
newsortby: 'Date' skip: 1,
}, function (err, res, body) { newsortby: 'Date'
//TODO try unaccepted options like imagefilters },
function (err, res, body) {
should.not.exist(err); //TODO try unaccepted options like imagefilters
should.exist(res);
should.exist(body);
body.d.results.should.have.length(10); should.not.exist(err);
should.exist(res);
should.exist(body);
done(); body.d.results.should.have.length(10);
});
done();
}); });
});
}); });
describe("Bing Video", function () { describe("Bing Video", function () {
this.timeout(1000 * 10); this.timeout(1000 * 10);
it('finds videos with specific options', function (done) { it('finds videos with specific options', function (done) {
Bing.video('monkey vs frog', { Bing.video('monkey vs frog',
top: 10 {
}, function (err, res, body) { top: 10,
should.not.exist(err); videofilters: {
should.exist(res); duration: 'short',
should.exist(body); resolution: 'high'
}
},
function (err, res, body) {
body.d.results.should.have.length(10); should.not.exist(err);
should.exist(res);
should.exist(body);
//TODO try here unaccepted options like imagefilters body.d.results.should.have.length(10);
done(); //TODO try here unaccepted options like imagefilters
});
done();
}); });
});
}); });