From 543e5b12588b3692449dc26e6fefece566f65801 Mon Sep 17 00:00:00 2001 From: dsuket Date: Wed, 3 Jun 2015 15:46:32 +0900 Subject: [PATCH] add tags api --- lib/methods/tags.js | 269 +++++++++++++++++++++++++++++++++++++++++++ lib/stackexchange.js | 4 +- test/tags-test.js | 201 ++++++++++++++++++++++++++++++++ 3 files changed, 473 insertions(+), 1 deletion(-) create mode 100644 lib/methods/tags.js create mode 100644 test/tags-test.js diff --git a/lib/methods/tags.js b/lib/methods/tags.js new file mode 100644 index 0000000..c4d3ae1 --- /dev/null +++ b/lib/methods/tags.js @@ -0,0 +1,269 @@ +'use strict'; + +/** + * Required modules. + */ +var query = require('../query'); + +function tagsValidator(tags) { + return function() { + if (!tags || !tags.length) { + return 'tags is required'; + } + return false; + } +} + +var sortPattern1 = /^popular$|^activity$|^name$/; +function sortValidator1(sort) { + return function() { + if (!sort) { + return 'sort is required'; + } + if (!sort.match(sortPattern1)) { + return 'sort is invalid. [popular|activity|name]'; + } + return false; + } +} +var sortPattern2 = /^creation$|^applied$|^activity$/; +function sortValidator2(sort) { + return function() { + if (!sort) { + return 'sort is required'; + } + if (!sort.match(sortPattern2)) { + return 'sort is invalid. [creation|applied|activity]'; + } + return false; + } +} + +var periodPattern = /^all_time$|^month$/; +function periodValidator(period) { + return function() { + if (!period) { + return 'period is required'; + } + if (!period.match(periodPattern)) { + return 'sort is invalid. [all_time|month]'; + } + return false; + } +} + +function validate(validators) { + var errors = []; + validators = validators || []; + if (!(validators instanceof Array)) { + validators = [validators]; + } + validators.forEach(function(validator) { + var err = validator(); + if (err) { + errors.push(err); + } + }); + if (errors.length === 0) { + return false; + } + return errors; +} + +/** + * Get the tags on the site. + * + * @param {Object} criteria + * @param {Function} callback return results + * @api public + */ +function tags (criteria, callback) { + query('tags', criteria, callback); +} + +/** + * Get tags on the site by their names. + * + * @param {Object} criteria + * @param {Function} callback return results + * @param {Array} tags collection of Tag + * @api public + */ +function info (criteria, callback, tags) { + var errors = validate([tagsValidator(tags), sortValidator1(criteria.sort)]); + if (errors) { + callback(new Error(errors.join(', '))); + return; + } + query('tags/' + tags.join(';') + '/info', criteria, callback); +} + +/** + * Get the tags on the site that only moderators can use. + * + * @param {Object} criteria + * @param {Function} callback return results + * @api public + */ +function moderatorOnly (criteria, callback) { + var errors = validate([sortValidator1(criteria.sort)]); + if (errors) { + callback(new Error(errors.join(', '))); + return; + } + query('tags/moderator-only', criteria, callback); +} + +/** + * Get the tags on the site that fulfill required tag constraints. + * + * @param {Object} criteria + * @param {Function} callback return results + * @api public + */ +function required (criteria, callback) { + var errors = validate([sortValidator1(criteria.sort)]); + if (errors) { + callback(new Error(errors.join(', '))); + return; + } + query('tags/required', criteria, callback); +} + +/** + * Get all the tag synonyms on the site. + * + * @param {Object} criteria + * @param {Function} callback return results + * @api public + */ +function synonyms (criteria, callback) { + var errors = validate([sortValidator2(criteria.sort)]); + if (errors) { + callback(new Error(errors.join(', '))); + return; + } + query('tags/synonyms', criteria, callback); +} + +/** + * Get frequently asked questions in a set of tags. + * + * @param {Object} criteria + * @param {Function} callback return results + * @param {Array} tags collection of Tag + * @api public + */ +function faq (criteria, callback, tags) { + var errors = validate(tagsValidator(tags)); + if (errors) { + callback(new Error(errors.join(', '))); + return; + } + query('tags/' + tags.join(';') + '/faq', criteria, callback); +} + +/** + * Get related tags, based on common tag pairings. + * + * @param {Object} criteria + * @param {Function} callback return results + * @param {Array} tags collection of Tag + * @api public + */ +function related (criteria, callback, tags) { + var errors = validate(tagsValidator(tags)); + if (errors) { + callback(new Error(errors.join(', '))); + return; + } + query('tags/' + tags.join(';') + '/related', criteria, callback); +} + +/** + * Get the synonyms for a specific set of tags. + * + * @param {Object} criteria + * @param {Function} callback return results + * @param {Array} tags collection of Tag + * @api public + */ +function tagsSynonyms (criteria, callback, tags) { + var errors = validate([tagsValidator(tags), sortValidator2(criteria.sort)]); + if (errors) { + callback(new Error(errors.join(', '))); + return; + } + query('tags/' + tags.join(';') + '/synonyms', criteria, callback); +} + +/** + * Get the top answer posters in a specific tag, either in the last month or for all time. + * + * @param {Object} criteria + * @param {Function} callback return results + * @param {Array} tags collection of Tag + * @param {String} all_time or month + * @api public + */ +function topAnswerers (criteria, callback, tags, period) { + period = period || 'all_time'; + var errors = validate([tagsValidator(tags), periodValidator(period)]); + if (errors) { + callback(new Error(errors.join(', '))); + return; + } + query('tags/' + tags.join(';') + '/top-answerers/' + period, + criteria, callback); +} + +/** + * Get the top question askers in a specific tag, either in the last month or for all time. + * + * @param {Object} criteria + * @param {Function} callback return results + * @param {Array} tags collection of Tag + * @param {String} all_time or month + * @api public + */ +function topAskers (criteria, callback, tags, period) { + period = period || 'all_time'; + var errors = validate([tagsValidator(tags), periodValidator(period)]); + if (errors) { + callback(new Error(errors.join(', '))); + return; + } + query('tags/' + tags.join(';') + '/top-askers/' + period, + criteria, callback); +} + +/** + * Get the wiki entries for a set of tags. + * + * @param {Object} criteria + * @param {Function} callback return results + * @param {Array} tags collection of Tag + * @api public + */ +function wiki (criteria, callback, tags) { + var errors = validate(tagsValidator(tags)); + if (errors) { + callback(new Error(errors.join(', '))); + return; + } + query('tags/' + tags.join(';') + '/wikis', criteria, callback); +} + + +// Expose commands. +module.exports.tags = tags; +module.exports.info = info; +module.exports.moderatorOnly = moderatorOnly; +module.exports.required = required; +module.exports.synonyms = synonyms; +module.exports.faq = faq; +module.exports.related = related; +module.exports.tagsSynonyms = tagsSynonyms; +module.exports.topAnswerers = topAnswerers; +module.exports.topAskers = topAskers; +module.exports.wiki = wiki; diff --git a/lib/stackexchange.js b/lib/stackexchange.js index ec124cf..f7d2191 100644 --- a/lib/stackexchange.js +++ b/lib/stackexchange.js @@ -5,7 +5,8 @@ var util = require('utile') , search = require('./methods/search') , questions = require('./methods/questions') , answers = require('./methods/answers') - , users = require('./methods/users'); + , users = require('./methods/users') + , tags = require('./methods/tags'); /** * Initialize StackExchange API. @@ -26,4 +27,5 @@ module.exports = function StackExchange(options) { this.questions = questions; this.answers = answers; this.users = users; + this.tags = tags; }; diff --git a/test/tags-test.js b/test/tags-test.js new file mode 100644 index 0000000..f1b7103 --- /dev/null +++ b/test/tags-test.js @@ -0,0 +1,201 @@ +/*tags expect*/ + +var stackexchange = require('../lib/stackexchange'); + +describe('Tags', function () { + 'use strict'; + + var options, context, filter; + + beforeEach(function() { + options = { version: 2.2 }; + context = new stackexchange(options); + filter = { + pagesize: 10, + sort: 'popular', + order: 'desc' + }; + }); + + function expectTagProperty(item) { + expect(item).to.have.property('has_synonyms'); + expect(item).to.have.property('is_moderator_only'); + expect(item).to.have.property('is_required'); + expect(item).to.have.property('count'); + expect(item).to.have.property('name'); + } + function expectSynonymsProperty(item) { + expect(item).to.have.property('creation_date'); + expect(item).to.have.property('applied_count'); + expect(item).to.have.property('to_tag'); + expect(item).to.have.property('from_tag'); + } + + it('get tags', function(done) { + context.tags.tags(filter, function(err, results){ + if (err) throw err; + // console.log('results: ', results); + + expect(results.items).to.have.length(10); + expectTagProperty(results.items[0]); + expect(results.has_more).to.be.true; + done(); + }); + }); + it('get tags illegal sort option', function(done) { + filter.sort = 'creation'; + context.tags.tags(filter, function(err, results){ + expect(err).to.be.instanceof(Error); + done(); + }); + }); + + it('get tags info', function(done) { + var tags = ['javascript', 'ruby']; + context.tags.info(filter, function(err, results){ + if (err) throw err; + // console.log('results: ', results); + + expect(results.items).to.have.length(2); + expectTagProperty(results.items[0]); + expect(results.has_more).to.be.false; + done(); + }, tags); + }); + + it('get tags info has error with empty tags', function(done) { + context.tags.info(filter, function(err, results){ + expect(err).to.be.instanceof(Error); + done(); + }, []); + }); + + it('get tags moderatorOnly return empty', function(done) { + context.tags.moderatorOnly(filter, function(err, results){ + if (err) throw err; + // console.log('results: ', results); + + expect(results.items).to.have.length(0); + expect(results.has_more).to.be.false; + done(); + }); + }); + + it('get tags required return empty', function(done) { + context.tags.required(filter, function(err, results){ + if (err) throw err; + // console.log('results: ', results); + + expect(results.items).to.have.length(0); + expect(results.has_more).to.be.false; + done(); + }); + }); + + it('get tags synonyms', function(done) { + filter.sort = 'creation'; + context.tags.synonyms(filter, function(err, results){ + if (err) throw err; + // console.log('results: ', results); + + expect(results.items).to.have.length(10); + expectSynonymsProperty(results.items[0]); + expect(results.has_more).to.be.true; + done(); + }); + }); + it('get tags synonyms illegal sort option', function(done) { + context.tags.synonyms(filter, function(err, results){ + expect(err).to.be.instanceof(Error); + done(); + }); + }); + + it('get tags faq', function(done) { + context.tags.faq(filter, function(err, results) { + if (err) throw err; + // console.log('results: ', results); + + expect(results.items).to.have.length(10); + expect(results.has_more).to.be.true; + done(); + }, ['java', 'c']); + }); + + it('get tags related', function(done) { + context.tags.related(filter, function(err, results) { + if (err) throw err; + // console.log('results: ', results); + + expect(results.items).to.have.length(10); + expectTagProperty(results.items[0]); + expect(results.has_more).to.be.true; + done(); + }, ['java', 'javascript']); + }); + + it('get tags tagsSynonyms', function(done) { + filter.sort = 'creation'; + context.tags.tagsSynonyms(filter, function(err, results) { + if (err) throw err; + // console.log('results: ', results); + + // expect(results.items).to.have.length(10); + expectSynonymsProperty(results.items[0]); + // expect(results.has_more).to.be.true; + done(); + }, ['javascript']); + }); + + it('get tags topAnswerers', function(done) { + context.tags.topAnswerers(filter, function(err, results) { + if (err) throw err; + // console.log('results: ', results); + + expect(results.items).to.have.length(10); + expect(results.has_more).to.be.true; + done(); + }, ['javascript'], 'all_time'); + }); + + it('get tags topAnswerers illegal period', function(done) { + context.tags.topAnswerers(filter, function(err, results) { + expect(err).to.be.instanceof(Error); + done(); + }, ['javascript'], 'all_days'); + }); + + it('get tags topAskers', function(done) { + context.tags.topAskers(filter, function(err, results) { + if (err) throw err; + // console.log('results: ', results); + + expect(results.items).to.have.length(10); + expect(results.has_more).to.be.true; + done(); + }, ['javascript']); + }); + + it('get tags topAskers illegal period', function(done) { + context.tags.topAskers(filter, function(err, results) { + expect(err).to.be.instanceof(Error); + done(); + }, ['javascript'], 'all_days'); + }); + + it('get tags wiki', function(done) { + context.tags.wiki(filter, function(err, results) { + if (err) throw err; + console.log('results: ', results); + + expect(results.items).to.have.length(1); + expect(results.items[0]).to.have.property('excerpt_last_edit_date'); + expect(results.items[0]).to.have.property('body_last_edit_date'); + expect(results.items[0]).to.have.property('excerpt'); + expect(results.items[0]).to.have.property('tag_name'); + expect(results.has_more).to.be.false; + done(); + }, ['javascript']); + }); + +});