├── .gitignore ├── Makefile ├── config.js ├── .travis.yml ├── package.json ├── LICENSE.md ├── index.js ├── README.md ├── v3.js ├── server.js └── test └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @./node_modules/.bin/mocha --reporter=spec 3 | 4 | .PHONY: test -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | module.exports = exports = { 2 | port: 9130, // Port that the webserver will run on 3 | address: '127.0.0.1', // Host that the webserver will bind to 4 | 5 | database: { 6 | host: 'mongodb://127.0.0.1/', // MongoDB host 7 | name: 'bukget', // MongoDB database name 8 | test_name: 'bukget_test' // MongoDB database name used for tests 9 | } 10 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "0.12" 5 | - "0.11" 6 | - "0.10" 7 | services: 8 | - mongodb 9 | notifications: 10 | irc: 11 | channels: 12 | - "irc.esper.net#bukget" 13 | skip_join: true 14 | on_success: change 15 | on_failure: always 16 | email: 17 | recipients: 18 | - david@dmarby.se 19 | on_success: change 20 | on_failure: always 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bukget-api", 3 | "version": "0.0.1", 4 | "main": "index.js", 5 | "private": true, 6 | "scripts": { 7 | "test": "make test", 8 | "start": "node index.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/BukGet/api.git" 13 | }, 14 | "homepage": "http://bukget.org", 15 | "author": "David Marby http://dmarby.se", 16 | "dependencies": { 17 | "cors": "^2.5.3", 18 | "miniops": "^0.1.0", 19 | "monk": "^1.0.1", 20 | "restify": "^3.0.1" 21 | }, 22 | "devDependencies": { 23 | "mocha": "~2.2.1", 24 | "should": "~5.2.0", 25 | "supertest": "^0.15.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 David Marby & Steve McGrath 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | //Include the cluster module 2 | var cluster = require('cluster'); 3 | var config = require('./config'); 4 | 5 | // Code to run if we're in the master process 6 | if (cluster.isMaster) { 7 | var MiniOps = require('miniops'); 8 | var restify = require('restify'); 9 | var miniOps = new MiniOps(24); 10 | 11 | var stats = restify.createServer(); 12 | stats.use(restify.jsonp()); 13 | stats.get('/ops', miniOps.dataHub()); 14 | stats.listen(9133); 15 | // Count the machine's CPU cores 16 | var cpuCount = require('os').cpus().length - 1; 17 | if (cpuCount < 2) { 18 | cpuCount = 2; 19 | } 20 | 21 | // Create a worker for each cpu core - 1 22 | for (var i = 0, il=cpuCount; i < il; i++) { 23 | var worker = cluster.fork(); 24 | worker.on('message', function (msg) { 25 | miniOps.recorder()(msg.req, msg.res, msg.route, msg.error); 26 | }); 27 | } 28 | 29 | // Listen for dying workers 30 | cluster.on('exit', function (worker) { 31 | // Replace the dead worker, we're not sentimental 32 | console.log('Worker ' + worker.id + ' died'); 33 | 34 | var worker = cluster.fork(); 35 | worker.on('message', function (msg) { 36 | miniOps.recorder()(msg.req, msg.res, msg.route, msg.error); 37 | }); 38 | }); 39 | 40 | } else { 41 | require('./server')(config.database.host + config.database.name, function (callback) { 42 | //Start webserver 43 | callback.listen(config.port, config.address); 44 | }); 45 | console.log('Worker ' + cluster.worker.id + ' running!'); 46 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## BukGet [![Build Status](https://travis-ci.org/BukGet/api.svg?branch=master)](https://travis-ci.org/BukGet/api) 2 | BukGet is a JSON API into dev.bukkit.org. The idea is to provide some sort of 3 | an interface into DBO's contents as Curse doesn't natively have one for us to 4 | interact with. 5 | 6 | ### What does it do? 7 | BukGet is a API into [dev.bukkit.org][dbo] that 8 | is trying to parse out the relevant details needed to perform package 9 | management for Bukkit. We do not have a client that interacts with our API, 10 | however instead offer the API to anyone wishing to use it to build their own 11 | package management system for Bukkit. We do our best to try to parse out all 12 | of the relevant data, including dependencies, CB build numbers, etc. to help 13 | aide developers along in getting the information they need. 14 | 15 | ### How do I use BukGet? 16 | We have a JSON API that you can interact with in order to pull the information 17 | you need. Details on how to interact with the API are explained later on in 18 | this document. 19 | 20 | ### How do I get my plugin included in BukGet? 21 | Is your plugin in [dev.bukkit.org][dbo]? If it is, then your plugin is 22 | already included. 23 | 24 | 25 | ### Where can I get more information? 26 | 27 | Visit the Projects main website [bukget.org][bukget] 28 | 29 | [bukget]: http://bukget.org 30 | [dbo]: http://dev.bukkit.org 31 | 32 | ## Arch Overview 33 | 34 | BukGet utilizes MongoDB as the back-end database and is coded using the bottlepy web framework. For scalability, we use Paste as the application server and front-end that with Nginx for URL routing (in-case we need to stand up a dev instance) as well a logging all of the requests. This implementation framework has been able to handle 20M requests a month without showing any significant impact on the hardware (~10% CPU Utilization and ~1-2% I/OWait as of the writing of this document). 35 | 36 | __Application List__ 37 | 38 | * MongoDB 39 | * Python 2.6/7 40 | * Bottle 41 | * Tornado 42 | * Nginx 43 | 44 | __Script List__ 45 | 46 | * bukgen_bukkit 47 | * bukgen_manual 48 | * bukget 49 | * force_update.py 50 | * logreader.py 51 | 52 | __Services List__ 53 | 54 | * nginx - sysv 55 | * mongod - sysv 56 | * bukget - upstart 57 | 58 | 59 | __Services Layout (By Server)__ 60 | 61 | * dallas.api.bukget.org 62 | * BukGet API (Nginx, Mongo, BukGet) 63 | * logreader script (cronjob) 64 | * BukGen Generator (bukgen_* applications) 65 | * Maintinence Functions (force_update.py, bukgen_manual) 66 | * paris.api.bukget.org 67 | * BukGet API (Nginx, Mongo, BukGet) 68 | * dev.vpn.bukget.org 69 | * Development Services (Independent of Prod) 70 | * BukGet API (Nginx, Mongo, BukGet) 71 | * Production Database Nightly Backup 72 | 73 | 74 | ### MongoDB 75 | 76 | The Mongo Database has not had any customization that needs to be documented at this time. The Dallas server is the primary database engine, with the Paris server acting as the secondary. This means that all Database writes has to go through the Dallas Mongo instance. 77 | 78 | 79 | ### Python 2.6/7 80 | 81 | The Python Installation on the server(s) is the default installation that comes with CentOS 6.3 x64. Pip has also been installed on these hosts manually and is being used to help manage the installation of any needed dependencies as well as the installation of the bukget and bukgen packages themselves. These packages are being pulled directly from the git repository and then being updated using the following command in the appropriate directory: 82 | 83 | `pip install --upgrade ./` 84 | 85 | This will install/upgrade any dependencies for the package as well as install the latest version of the package itself. 86 | 87 | 88 | ### Nginx 89 | 90 | Below is the configuration for the VHost that we use for BukGet. Keep in mind that minor modifications may exist to also listen for the hostname as well as api.bukget.org 91 | 92 | server { 93 | listen 80; 94 | server_name api.bukget.org XXX.api.bukget.org; 95 | 96 | access_log /var/log/bukget/api-access.log; 97 | error_log /var/log/bukget/api-error.log; 98 | 99 | location / { 100 | proxy_pass http://127.0.0.1:9132/; 101 | proxy_redirect off; 102 | 103 | proxy_set_header Host $host; 104 | proxy_set_header X-Real-IP $remote_addr; 105 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 106 | proxy_max_temp_file_size 0; 107 | 108 | client_max_body_size 10m; 109 | client_body_buffer_size 128k; 110 | 111 | proxy_connect_timeout 90; 112 | proxy_send_timeout 90; 113 | proxy_read_timeout 90; 114 | 115 | proxy_buffer_size 4k; 116 | proxy_buffers 4 32k; 117 | proxy_busy_buffers_size 64k; 118 | proxy_temp_file_write_size 64k; 119 | } 120 | } 121 | 122 | 123 | ### bukgen_bukkit Script 124 | 125 | This script is the main script that is being run to keep bukget up to date. This script runs every 6 hours. It can also be run manually as needed (uncommon). The script has a self-imposed 2 second delay for each URL request so not to overwhelm BukkitDev. It's still worth noting that if a generation is run outside of the normal window, to check to make sure nothing else is currently talking to BukkitDev so now to overwhelm the service we're pulling from. 126 | 127 | BukGen also has a number of built-in safeties aside from the 2 second delay. These mechanisms have evolved over the life of the generation script and may change: 128 | 129 | * __2 Second Delay Timer__: Delays the amount of time globally for each request to BukkitDev. This is tunable. 130 | * __Re-pull URL on Failure__: The system will automatically dwell on URLs that have failed to pull (i.e. timeout has been reached). This allows the generation script to effectively pull the required data. 131 | * __Stop Pulling Bad Data After 3 Re-trys__: If the generator receives an HTTP code other than a 200-OK (timeouts excluded) then it will attempt another 2 times. If a 200-OK is not returned, then it will fail the URL out and continue on. This prevents the generator from going into an infinite request loop. 132 | 133 | __Options__ 134 | 135 | * __speedy__: This is the normal run behavior. It will run through BukkitDev until it no longer finds any updates to process, then terminates. 136 | * __full__: This process will run through all of BukkitDev, ignoring any existing data and will update everything. This is generally only used when API changes of DB re-population is needed. This will take several days to complete. 137 | * __speedy_full__: Forces bukgen to look at every plugin, however will still honor any existing data for each plugin. This function effectively replaces the stage_update function and is designed to be run monthly to update any stale information about the plugin itself. 138 | * __stage_update__: Function to only update the stage value for the plugin. Rotates through all plugins in the database and will mark any deleted plugins as well as update the stage of the plugin. 139 | 140 | 141 | ### bukgen_manual Script 142 | 143 | This is a script to manually update the document to one or many plugins. There may be many reasons for doing this, however this generally relates to the generator script not being able to pull the right data (this is most common with plugins that are bundled in zip files). To run the script, first you will need to create a json file with the corrected plugin definition and place it into the directory `/tmp/bukget/fixed_json`. You may need to create this directory if it doesn't exist. The filename of the json files are not important, however they must have the .json extension and be only 1 plugin per json file. When ready, just run the command `bukkit_manual`. The script should import/update the given plugins then remove the .json files as it runs. 144 | 145 | 146 | ### bukget Script 147 | 148 | This is the bukget api launcher script. This is generally run from the upstart job, however can be manually run when debugging code to see what the errors are. 149 | 150 | 151 | ### force_update.py script 152 | 153 | This script will update the plugins specified as arguments out-of-band of the normal update cycle. This is useful when a plugin either was never updated, or the update never completed for this plugin. These occurrences are rare, however do happen from time to time. 154 | 155 | #### Usage: 156 | 157 | `/opt/bukget/devfiles/force_update.py plugin_name_1 plugin_name_2` 158 | 159 | 160 | ### logreader.py Script 161 | 162 | This script is designed to run nightly from a cronjob. It walks through the previous days nginx log, determines how many calls to each plugin and from each API were performed, then also updates the popularity scoring based on those. It is not recommended to run this script outside the normal schedule unless it fails as it will modify the popularity scoring for all plugins in the system. -------------------------------------------------------------------------------- /v3.js: -------------------------------------------------------------------------------- 1 | module.exports = function (app, common) { 2 | 3 | // This could become a middleware in an ideal reality. 4 | // 5 | // Ecmascript 6 would make this easier... 6 | // let [ fields, start, size, sort ] = handle_parameters(req); 7 | function handle_parameters (req, full, callback) { 8 | if (req.params.server == null) { 9 | req.params.server = undefined; 10 | } 11 | 12 | if (req.params.version == null) { 13 | req.params.version = undefined; 14 | } 15 | 16 | var fields; 17 | 18 | if (full) { 19 | fields = ((req.query.fields == null ? '' : req.query.fields).split(',')); 20 | } else { 21 | fields = ((req.query.fields == null ? 'slug,plugin_name,description' : req.query.fields).split(',')); 22 | } 23 | 24 | var start = req.query.start == null ? undefined : parseInt(req.query.start); 25 | var size = req.query.size == null ? undefined : parseInt(req.query.size); 26 | var sort = req.query.sort == null ? 'slug' : req.query.sort; 27 | 28 | return callback(fields, start, size, sort); 29 | } 30 | 31 | function geninfo (req, res, next) { 32 | handle_parameters(req, false, function (fields, start, size, sort) { 33 | common.list_geninfo(size, function (callback) { 34 | res.send(callback); 35 | next(); 36 | }); 37 | }); 38 | } 39 | 40 | function plugin_list (req, res, next) { 41 | handle_parameters(req, false, function (fields, start, size, sort) { 42 | common.list_plugins(req.params.server, fields, sort, start, size, function (callback) { 43 | if (callback == null) { 44 | res.send(400, { 45 | 'error': 'invalid params' 46 | }); 47 | return next(); 48 | } 49 | res.send(callback); 50 | next(); 51 | }); 52 | }); 53 | } 54 | 55 | function plugin_details (req, res, next) { 56 | handle_parameters(req, true, function (fields, start, size, sort) { 57 | common.plugin_details(req.params.server, req.params.slug, req.params.version, fields, function (data) { 58 | if (data == null) { 59 | res.send(404, { 'error' : 'Plugin Does Not Exist' }); 60 | return next(); 61 | } 62 | 63 | if (size != undefined && data['versions'] != null) { 64 | data['versions'].length = size; 65 | } 66 | 67 | res.send(data); 68 | next(); 69 | }); 70 | }); 71 | } 72 | 73 | function author_plugins (req, res, next) { 74 | handle_parameters(req, false, function (fields, start, size, sort) { 75 | common.list_author_plugins(req.params.server, req.params.name, fields, sort, start, size, function (callback) { 76 | if (callback == null) { 77 | res.send(400, { 78 | 'error': 'invalid author' 79 | }); 80 | return next(); 81 | } 82 | res.send(callback); 83 | next(); 84 | }); 85 | }); 86 | } 87 | 88 | function category_plugins (req, res, next) { 89 | handle_parameters(req, false, function (fields, start, size, sort) { 90 | common.list_category_plugins(req.params.server, req.params.name, fields, sort, start, size, function (callback) { 91 | if (callback == null) { 92 | res.send(400, { 93 | 'error': 'invalid category' 94 | }); 95 | return next(); 96 | } 97 | res.send(callback); 98 | next(); 99 | }); 100 | }); 101 | } 102 | 103 | app.get('/3/', function (req, res, next) { 104 | geninfo(req, res, next); 105 | }); 106 | 107 | app.get('/3/geninfo', function (req, res, next) { 108 | geninfo(req, res, next); 109 | }); 110 | 111 | app.get('/3/geninfo/:idnum', function (req, res, next) { 112 | common.get_geninfo(req.params.idnum, function (callback) { 113 | res.send(callback); 114 | next(); 115 | }); 116 | }); 117 | 118 | app.get('/3/plugins', function (req, res, next) { 119 | plugin_list(req, res, next); 120 | }); 121 | 122 | app.get('/3/plugins/:server', function (req, res, next) { 123 | plugin_list(req, res, next); 124 | }); 125 | 126 | app.get('/3/plugins/:server/:slug', function (req, res, next) { 127 | plugin_details(req, res, next); 128 | }); 129 | 130 | app.get(/^\/3\/plugins\/(.*)\/(.*)\/(.*)\/download/, function (req, res, next) { 131 | common.plugin_details(req.params[0], req.params[1], req.params[2], [], function (data) { 132 | if (data == null) { 133 | res.send(404, { 'error' : 'Plugin Does Not Exist' }); 134 | return next(); 135 | } 136 | 137 | if (req.params[2].toLowerCase() == 'latest') { 138 | res.header('Location', data['versions'][0]['download']); 139 | res.send(302); 140 | return next(); 141 | } else { 142 | for (var i = 0, il = data['versions'].length; i < il; i++) { 143 | if (data['versions'][i]['version'] == req.params[2]) { 144 | res.header('Location', data['versions'][i]['download']); 145 | res.send(302); 146 | return next(); 147 | } 148 | } 149 | } 150 | 151 | res.send(404, {'error': 'could not find version'}); 152 | next(); 153 | }); 154 | }); 155 | 156 | app.get(/^\/3\/plugins\/(.*)\/(.*)\/(.*)/, function (req, res, next) { 157 | req.params.server = req.params[0]; 158 | req.params.slug = req.params[1]; 159 | req.params.version = req.params[2]; 160 | plugin_details(req, res, next); 161 | }); 162 | 163 | 164 | app.get('/3/authors', function (req, res, next) { 165 | common.list_authors(function (callback) { 166 | res.send(callback); 167 | next(); 168 | }); 169 | }); 170 | 171 | app.get('/3/authors/:name', function (req, res, next) { 172 | author_plugins(req, res, next); 173 | }); 174 | 175 | app.get('/3/authors/:server/:name', function (req, res, next) { 176 | author_plugins(req, res, next); 177 | }); 178 | 179 | app.get('/3/categories', function (req, res, next) { 180 | common.list_categories(function (callback) { 181 | res.send(callback); 182 | next(); 183 | }); 184 | }); 185 | 186 | app.get('/3/categories/:name', function (req, res, next) { 187 | category_plugins(req, res, next); 188 | }); 189 | 190 | app.get('/3/categories/:server/:name', function (req, res, next) { 191 | category_plugins(req, res, next); 192 | }); 193 | 194 | app.post('/3/updates', function (req, res, next) { 195 | var slugs = (req.params.slugs == null ? '' : req.params.slugs).split(','); 196 | var server = req.params.server == null ? 'bukkit' : req.params.server; 197 | var hashes = (req.params.hashes == null ? '' : req.params.hashes).split(','); 198 | var filenames = (req.params.filenames == null ? '' : req.params.filenames).split(','); 199 | var extra_fields = ((req.params.extra_fields == null ? '' : req.params.extra_fields.split(','))); 200 | var extra_version_fields = ((req.query.extra_version_fields == null ? '' : req.query.extra_version_fields.split(','))); 201 | common.plugins_up_to_date(slugs, hashes, filenames, server, extra_fields, extra_version_fields, function (callback) { 202 | res.send(callback); 203 | next(); 204 | }); 205 | }); 206 | 207 | app.get('/3/updates', function (req, res, next) { 208 | var slugs = (req.query.slugs == null ? '' : req.query.slugs).split(','); 209 | var server = req.query.server == null ? 'bukkit' : req.query.server; 210 | var hashes = (req.query.hashes == null ? '' : req.query.hashes).split(','); 211 | var filenames = (req.query.filenames == null ? '' : req.query.filenames).split(','); 212 | var extra_fields = ((req.query.extra_fields == null ? '' : req.query.extra_fields.split(','))); 213 | var extra_version_fields = ((req.query.extra_version_fields == null ? '' : req.query.extra_version_fields.split(','))); 214 | common.plugins_up_to_date(slugs, hashes, filenames, server, extra_fields, extra_version_fields, function (callback) { 215 | res.send(callback); 216 | next(); 217 | }); 218 | }); 219 | 220 | function search (req, res, next) { 221 | var filters = [] 222 | 223 | if (req.method == 'GET') { 224 | var fields = ((req.query.fields == null ? 'slug,plugin_name,description' : req.query.fields).split(',')); 225 | var start = req.query.start == null ? undefined : parseInt(req.query.start); 226 | var size = req.query.size == null ? undefined : parseInt(req.query.size); 227 | var sort = req.query.sort == null ? 'slug' : req.query.sort; 228 | var field = req.params[0]; 229 | var value = req.params[2]; 230 | filters = [{ 231 | 'field': field, 232 | 'action': req.params[1], 233 | 'value': value 234 | }]; 235 | } else { 236 | var filters 237 | 238 | if (req.params.filters === null) { 239 | filters = [] 240 | } else { 241 | try { 242 | filters = JSON.parse(req.params.filters) 243 | } catch (error) { 244 | filters = req.params.filters 245 | } 246 | } 247 | 248 | filters = req.params.filters == null ? [] : filters 249 | var fields = ((req.params.fields == null ? 'slug,plugin_name,description' : req.params.fields).split(',')); 250 | var start = req.params.start == null ? undefined : parseInt(req.params.start); 251 | var size = req.params.size == null ? undefined : parseInt(req.params.size); 252 | var sort = (req.params.sort == null || typeof req.params.sort === 'function') ? 'slug' : req.params.sort; 253 | } 254 | 255 | common.plugin_search(filters, fields, sort, start, size, false, function (callback) { 256 | if (callback == null) { 257 | res.send(400, { 258 | 'error': 'invalid search' 259 | }); 260 | return next(); 261 | } 262 | 263 | res.send(callback); 264 | next(); 265 | }); 266 | } 267 | 268 | app.post('/3/search', function (req, res, next) { 269 | search(req, res, next); 270 | }); 271 | 272 | app.get(/^\/3\/search\/([a-zA-Z0-9_\.~-]+)\/(\=|\!\=|\<|\<\=|\>|\>\=|[a-z]+)\/([a-zA-Z0-9_\.~-]+)/, function (req, res, next) { 273 | search(req, res, next); 274 | }); 275 | } -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | module.exports = function (database, callback) { 2 | //Imports 3 | var restify = require('restify'); 4 | var cors = require('cors'); 5 | 6 | //Connect to database 7 | var db = require('monk')(database); 8 | var plugins = db.get('plugins'); 9 | var webstats = db.get('webstats'); 10 | var geninfo = db.get('geninfo'); 11 | var authors = db.get('authors'); 12 | var categories = db.get('categories'); 13 | 14 | // Search Type Map 15 | var types = { 16 | '=': function (item, sub, reference) { 17 | reference[item['field']] = item['value']; 18 | }, 19 | 20 | '!=': function (item, sub, reference) { 21 | reference[item['field']] = { 22 | '$ne': item['value'] 23 | }; 24 | }, 25 | 26 | '<': function (item, sub, reference) { 27 | reference[item['field']] = { 28 | '$lt': item['value'] 29 | }; 30 | }, 31 | 32 | '<=': function (item, sub, reference) { 33 | reference[item['field']] = { 34 | '$lte': item['value'] 35 | }; 36 | }, 37 | 38 | '>': function (item, sub, reference) { 39 | reference[item['field']] = { 40 | '$gt': item['value'] 41 | }; 42 | }, 43 | 44 | '>=': function (item, sub, reference) { 45 | reference[item['field']] = { 46 | '$gte': item['value'] 47 | }; 48 | }, 49 | 50 | 'like': function (item, sub, reference) { 51 | reference[item['field']] = new RegExp(item['value'], 'i'); 52 | }, 53 | 54 | 'exists': function (item, sub, reference) { 55 | reference[item['field']] = { 56 | '$exists': Boolean(item['value']) 57 | }; 58 | }, 59 | 60 | 'in': function (item, sub, reference) { 61 | var list = []; 62 | for (i in item['value']) { 63 | list.push(new RegExp('^' + item['value'][i] + '$', 'i')); 64 | } 65 | if (Object.prototype.toString.call(item['value']) === '[object Array]') { 66 | reference[item['field']] = { 67 | '$in': list 68 | } 69 | } 70 | }, 71 | 72 | 'not in': function (item, sub, reference) { 73 | var list = []; 74 | for (i in item['value']) { 75 | list.push(new RegExp('^' + item['value'][i] + '$', 'i')); 76 | } 77 | if (Object.prototype.toString.call(item['value']) === '[object Array]') { 78 | reference[item['field']] = { 79 | '$nin': list 80 | } 81 | } 82 | }, 83 | 84 | 'all': function (item, sub, reference) { 85 | var list = []; 86 | for (i in item['value']) { 87 | list.push(new RegExp('^' + item['value'][i] + '$', 'i')); 88 | } 89 | if (Object.prototype.toString.call(item['value']) === '[object Array]') { 90 | reference[item['field']] = { 91 | '$all': list 92 | } 93 | } 94 | }, 95 | 96 | 'and': function (item, sub, reference) { 97 | if (Object.prototype.toString.call(item['value']) === '[object Array]' && item['field'] == '') { 98 | reference['$and'] = (item['value'] == null ? sub : item['value']); 99 | } 100 | }, 101 | 102 | 'or': function (item, sub, reference) { 103 | if (Object.prototype.toString.call(item['value']) === '[object Array]' && item['field'] == '') { 104 | reference['$or'] = (item['value'] == null ? sub : item['value']); 105 | } 106 | }, 107 | 108 | 'likeor': function (item, sub, reference) { 109 | reference['$or'] = []; 110 | for (i in item['value']) { 111 | var key = Object.keys(item['value'][i])[0]; 112 | reference['$or'][i] = {}; 113 | reference['$or'][i][key] = new RegExp(item['value'][i][key], 'i'); 114 | } 115 | }, 116 | 117 | 'nor': function (item, sub, reference) { 118 | if (Object.prototype.toString.call(item['value']) === '[object Array]' && item['field'] == '') { 119 | reference['$nor'] = (item['value'] == null ? sub : item['value']); 120 | } 121 | }, 122 | 123 | 'not': function (item, sub, reference) { 124 | if (Object.prototype.toString.call(item['value']) === '[object Object]') { 125 | reference[item['field']] = { '$not' : (item['value'] == null ? sub : item['value']) }; 126 | } 127 | } 128 | }; 129 | 130 | // Aliases 131 | types['equals'] = types['=']; 132 | types['not-equals'] = types['!=']; 133 | types['less'] = types['<']; 134 | types['less-equal'] = types['<=']; 135 | types['more'] = types['>']; 136 | types['more-equal'] = types['>=']; 137 | 138 | //Common methods 139 | var common = { 140 | fieldgen: function (fields, callback) { 141 | // Generates the field listing based on the include and exclude lists. 142 | var f = [ 143 | '-_id' 144 | ] 145 | 146 | fields.forEach(function (field) { 147 | if (field != '' && f.indexOf(field) === -1) { 148 | f.push(field) 149 | } 150 | }) 151 | 152 | callback(f); 153 | }, 154 | 155 | list_geninfo: function (size, callback) { 156 | // Retrieves the last X number of generations based on the value of the size variable. 157 | if (size == undefined) { 158 | size = 1; 159 | } 160 | 161 | geninfo.find({}, { limit: 5, sort: { '_id': -1 } }, function (error, docs) { 162 | for (var i in docs) { 163 | docs[i]['id'] = docs[i]['_id']; 164 | delete docs[i]['_id']; 165 | } 166 | 167 | callback(docs); 168 | }); 169 | }, 170 | 171 | get_geninfo: function (idnum, callback) { 172 | // Returns a specific generation ID's information. 173 | geninfo.findOne({ 174 | '_id': geninfo.id(idnum) 175 | }, function (error, document) { 176 | if (document != null) { 177 | document['id'] = document['_id']; 178 | delete document['_id']; 179 | } else { 180 | document = {}; 181 | } 182 | 183 | callback(document); 184 | }) 185 | }, 186 | 187 | query: function (filters, fields, sort, start, size, callback) { 188 | // Generic query function to centralize querying the database. 189 | var d = 1; 190 | 191 | if (sort.charAt(0) == '-') { 192 | sort = sort.substr(1); 193 | d = -1; 194 | } 195 | 196 | common.fieldgen(fields, function (generated_fields) { 197 | var the_sort = {} 198 | the_sort[sort] = d 199 | 200 | var options = { 201 | fields: generated_fields, 202 | sort: the_sort 203 | } 204 | 205 | if (size != undefined) { 206 | if (start == undefined) { 207 | start = 0; 208 | } 209 | 210 | options.skip = start; 211 | options.limit = size; 212 | 213 | plugins.find(filters, options, function (error, docs) { 214 | callback(docs); 215 | }); 216 | } else { 217 | plugins.find(filters, options, function (error, docs) { 218 | if (error || docs == null) { 219 | return callback(null); 220 | } 221 | 222 | callback(docs); 223 | }); 224 | } 225 | }); 226 | }, 227 | 228 | list_plugins: function (server, fields, sort, start, size, callback) { 229 | // Returns a list of plugins with the field specified, the list can be narrowed down to specific server binary compatability by setting it to other than undefined. 230 | var filters = { 231 | 'deleted': { 232 | '$exists': false 233 | } 234 | }; 235 | 236 | if (server != undefined) { 237 | filters['server'] = server 238 | } 239 | 240 | common.query(filters, fields, sort, start, size, function (the_callback) { 241 | callback(the_callback); 242 | }) 243 | }, 244 | 245 | list_author_plugins: function (server, author, fields, sort, start, size, callback) { 246 | // Returns the plugin list for a given author. Can be filtered by server binary compatability using the server variable. 247 | var filters = { 248 | 'authors': author 249 | }; 250 | 251 | if (server != undefined) { 252 | filters['server'] = server 253 | } 254 | 255 | common.query(filters, fields, sort, start, size, function (the_callback) { 256 | callback(the_callback) 257 | }) 258 | }, 259 | 260 | list_category_plugins: function (server, category, fields, sort, start, size, callback) { 261 | // Returns the plugin list for a given category. Can be filtered by server binary compatability using the server variable. 262 | var filters = { 263 | 'categories': category 264 | }; 265 | 266 | if (server != undefined) { 267 | filters['server'] = server 268 | } 269 | 270 | common.query(filters, fields, sort, start, size, function (the_callback) { 271 | callback(the_callback) 272 | }) 273 | }, 274 | 275 | ca_convert: function (data, callback) { 276 | // Reformats the data to what the API should be returning. 277 | var dset = []; 278 | var item; 279 | 280 | for (var i = 0, dlen = data.length; i < dlen; i++) { 281 | item = data[i]; 282 | 283 | dset.push({ 284 | 'name': item['_id'], 285 | 'count': item['value'] 286 | }); 287 | } 288 | 289 | return callback(dset); 290 | }, 291 | 292 | list_authors: function (callback) { 293 | // Returns a list of plugin authors and the number of plugins each one has created/worked on. 294 | authors.find({}, { sort: '_id' }, function (error, docs) { 295 | common.ca_convert(docs, function (the_callback) { 296 | callback(the_callback); 297 | }) 298 | }) 299 | }, 300 | 301 | list_categories: function (callback) { 302 | // Returns a list of plugin categories and the count of plugins that fall under each category. 303 | categories.find({}, { sort: '_id' }, function (error, docs) { 304 | common.ca_convert(docs, function (the_callback) { 305 | callback(the_callback); 306 | }) 307 | }) 308 | }, 309 | 310 | plugin_details: function (server, plugin, version, fields, callback) { 311 | // Returns the plugin details for a given plugin. Optionally will also return a specific version of the plugin in the versions list if something other than undefined is specified in the version variable. 312 | var filters = { 313 | 'slug': plugin, 314 | 'server': server 315 | }; 316 | 317 | if (version != undefined && (version == 'release' || version == 'alpha' || version == 'beta')) { 318 | filters['versions.type'] = (version.charAt(0).toUpperCase() + version.slice(1)); 319 | 320 | if (fields != '' && fields != null && fields.length != 0) { 321 | fields.push('versions.type'); 322 | } 323 | } 324 | 325 | common.fieldgen(fields, function (callback) { 326 | fields = callback; 327 | }); 328 | 329 | plugins.findOne(filters, fields, function (error, p) { 330 | if (error || p == null) { 331 | return callback(null); 332 | } 333 | var found = false; 334 | var the_versions = p['versions']; 335 | 336 | if (version != undefined && p['versions'] != null) { 337 | if (version.toLowerCase() == 'latest') { 338 | the_versions = [p['versions'][0]]; 339 | } else if (version.toLowerCase() == 'alpha' || version.toLowerCase() == 'beta' || version.toLowerCase() == 'release') { 340 | for (var i = 0, versionsLen = p['versions'].length; i < versionsLen; i++) { 341 | if (p['versions'][i]['type'].toLowerCase() == version.toLowerCase()) { 342 | the_versions = [p['versions'][i]]; 343 | found = true; 344 | break; 345 | } 346 | } 347 | 348 | if (!found) { 349 | the_versions = []; 350 | } 351 | } else { 352 | for (var i = 0, versionsLen = p['versions'].length; i < versionsLen; i++) { 353 | if (p['versions'][i]['version'] == version) { 354 | the_versions = [p['versions'][i]]; 355 | found = true; 356 | break; 357 | } 358 | } 359 | 360 | if (!found) { 361 | the_versions = []; 362 | } 363 | } 364 | } 365 | 366 | p['versions'] = the_versions; 367 | callback(p); 368 | }); 369 | }, 370 | 371 | plugins_up_to_date: function (plugins_list, hash_list, file_list, server, extra_fields, extra_version_fields, callback) { 372 | // Takes a list of plugin slugs and returns an array of objects with the plugin and most recent version. 373 | var data = []; 374 | var slugs = []; 375 | 376 | if (plugins_list != '') { 377 | for (var i = 0; i < plugins_list.length; i++) { 378 | slugs.push({ 379 | 'slug': plugins_list[i] 380 | }); 381 | } 382 | } 383 | 384 | if (hash_list != '') { 385 | for (var i = 0; i < hash_list.length; i++) { 386 | slugs.push({ 'versions' : { '$elemMatch': { 'md5': hash_list[i] } } }); 387 | } 388 | } 389 | 390 | if (file_list != '') { 391 | for (var i = 0; i < file_list.length; i++) { 392 | slugs.push({ 'versions' : { '$elemMatch': { 'filename': file_list[i] } } }); 393 | } 394 | } 395 | 396 | plugins.find({ 397 | '$or': slugs, 398 | 'server': server 399 | }, function (error, docs) { 400 | var doc, versions, version; 401 | 402 | if (docs == null) { 403 | return callback([]); 404 | } 405 | for (var i = 0, docLen = docs.length; i < docLen; i++) { 406 | doc = docs[i]; 407 | versions = doc['versions']; 408 | 409 | var entry = { 410 | 'slug': doc['slug'], 411 | 'plugin_name': doc['plugin_name'], 412 | 'versions': { 413 | 'latest': {'version': versions[0]['version'], 'download': versions[0]['download'], 'md5': versions[0]['md5'] } 414 | }, 415 | } 416 | 417 | for (var i in extra_fields) { 418 | entry[extra_fields[i]] = doc[extra_fields[i]]; 419 | } 420 | 421 | for (var i in extra_version_fields) { 422 | entry['versions']['latest'][extra_version_fields[i]] = versions[0][extra_version_fields[i]]; 423 | } 424 | 425 | if (hash_list && hash_list[i]) { 426 | entry.hash = hash_list[i]; 427 | } 428 | if (file_list && file_list[i]) { 429 | entry.file = file_list[i]; 430 | } 431 | 432 | for (var x = 0, versionLen = versions.length; x < versionLen; x++) { 433 | version = versions[x]; 434 | 435 | if (version['type'] == 'Release' || version['type'] == 'Beta' || version['type'] == 'Alpha') { 436 | if (entry['versions'][version['type'].toLowerCase()] == null) { 437 | entry['versions'][version['type'].toLowerCase()] = { 'version': version['version'], 'download': version['download'], 'md5': version['md5'] }; 438 | for (var i in extra_version_fields) { 439 | entry['versions'][version['type'].toLowerCase()][extra_version_fields[i]] = version[extra_version_fields[i]]; 440 | } 441 | } 442 | } 443 | if (hash_list && hash_list[i] && hash_list[i] == version['md5']) { 444 | entry['versions']['current'] = { 'version': version['version'], 'download': version['download'], 'md5': version['md5'] }; 445 | for (var i in extra_version_fields) { 446 | entry['versions']['current'][extra_version_fields[i]] = version[extra_version_fields[i]]; 447 | } 448 | } 449 | } 450 | 451 | data.push(entry); 452 | } 453 | 454 | callback(data); 455 | }) 456 | }, 457 | 458 | plugin_search: function (filters, fields, sort, start, size, sub, callback) { 459 | // A generalized sort function for the database. 460 | // Returns a list of plugins with the fields specified in the inclusion and exclusion variables. 461 | 462 | var f = {}; 463 | var item; 464 | var action; 465 | 466 | if (sub == undefined) { 467 | sub = false; 468 | } 469 | 470 | for (var i = 0, filterLen = filters.length; i < filterLen; i++) { 471 | item = filters[i]; 472 | action = item['action']; 473 | 474 | if (types[action]) { 475 | types[action](item, sub, f); 476 | } 477 | } 478 | 479 | if (sub) { 480 | return callback(f); 481 | } 482 | 483 | common.query(f, fields, sort, start, size, function (the_callback) { 484 | callback(the_callback); 485 | }); 486 | } 487 | }; 488 | 489 | //Initialize express app 490 | var app = restify.createServer(); 491 | 492 | app.pre(restify.pre.userAgentConnection()); 493 | app.pre(restify.pre.sanitizePath()); 494 | 495 | //Middleware 496 | app.use(cors()); 497 | app.use(restify.queryParser()); 498 | app.use(restify.bodyParser()) 499 | app.use(restify.jsonp()); 500 | app.use(restify.gzipResponse()); 501 | 502 | //Include api handlers 503 | require('./v3')(app, common); 504 | 505 | //Redirect 506 | app.get('/', function (req, res, next) { 507 | res.header('Location', '/3'); 508 | res.send(302); 509 | next(); 510 | }); 511 | 512 | //Handle stats requests 513 | app.get('/stats/naughty_list', function (req, res, next) { 514 | plugins.find({ '_use_dbo': { '$exists': true } }, { 515 | fields: [ 516 | '-_id', 517 | 'slug', 518 | 'plugin_name', 519 | 'authors', 520 | ] 521 | }, function (error, docs) { 522 | if (error) { 523 | res.send(500); 524 | return next(); 525 | } 526 | 527 | res.send(docs); 528 | next(); 529 | }); 530 | }); 531 | 532 | app.get('/stats/todays_trends', function (req, res, next) { 533 | plugins.find({}, { 534 | fields: [ 535 | 'slug', 536 | 'versions.version' 537 | ] 538 | }, function (error, plugins) { 539 | if (error) { 540 | res.send(500); 541 | return next(); 542 | } 543 | 544 | var pcount = plugins.length; 545 | var vcount = 0; 546 | 547 | for (plugin in plugins) { 548 | vcount += plugins[plugin]['versions'].length; 549 | } 550 | 551 | res.send({ 552 | 'plugin_count': pcount, 553 | 'version_count': vcount 554 | }); 555 | next(); 556 | }); 557 | }); 558 | 559 | app.get('/stats/trend/:days', function (req, res, next) { 560 | var fields = [ 561 | '-_id', 562 | '-plugins' 563 | ] 564 | 565 | if (req.query.plugins) { 566 | if (req.query.plugins == 'all') { 567 | fields = ['-_id'] 568 | } else { 569 | fields = ['-_id', 'timestamp'] 570 | var plugins = req.query.plugins.split(','); 571 | 572 | for (var index = 0, pluginsLen = plugins.length; index < pluginsLen; index++) { 573 | fields.push('plugins.' + plugins[index]) 574 | } 575 | } 576 | } 577 | 578 | var days = (new Date().getTime() / 1000) - (86400 * req.params.days); 579 | var options = { 580 | fields: fields, 581 | limit: parseInt(req.params.days), 582 | sort: { '_id': -1 } 583 | } 584 | 585 | webstats.find({ timestamp: { $gte: days } }, options, function (error, docs) { 586 | if (error) { 587 | res.send(500); 588 | return next(); 589 | } 590 | 591 | res.send(docs); 592 | next(); 593 | }); 594 | }); 595 | 596 | //Deprecation stuff 597 | app.get('/2/bukkit/plugins', function (req, res, next) { 598 | res.send(['API', 'Deprecated', 'Please', 'update', 'your', 'software']); 599 | next(); 600 | }); 601 | 602 | app.get('/2/authors', function (req, res, next) { 603 | res.send(['API', 'Deprecated', 'Please', 'update', 'your', 'software']); 604 | next(); 605 | }); 606 | 607 | app.get('/2/categories', function (req, res, next) { 608 | res.send(['API', 'Deprecated', 'Please', 'update', 'your', 'software']); 609 | next(); 610 | }); 611 | 612 | app.get('/api2/bukkit/plugins', function (req, res, next) { 613 | res.send(['API', 'Deprecated', 'Please', 'update', 'your', 'software']); 614 | next(); 615 | }); 616 | 617 | app.get('/api2/authors', function (req, res, next) { 618 | res.send(['API', 'Deprecated', 'Please', 'update', 'your', 'software']); 619 | next(); 620 | }); 621 | 622 | app.get('/api2/categories', function (req, res, next) { 623 | res.send(['API', 'Deprecated', 'Please', 'update', 'your', 'software']); 624 | next(); 625 | }); 626 | 627 | app.get('/favicon.ico', function (req, res, next) { 628 | res.writeHead(204); 629 | res.end(); 630 | }); 631 | 632 | app.on('NotFound', function (req, res, next) { 633 | res.send(404, { error: 'Invalid route' }); 634 | }); 635 | 636 | if (process.send != null) { 637 | app.on('after', function (req, res, route, error) { 638 | process.send({ res: { statusCode : res.statusCode }, req: { url: req.url }, route: route, error: error }); 639 | }); 640 | } 641 | 642 | app.on('uncaughtException', function (req, res, route, error) { 643 | if (process.env.NODE_ENV === 'development') { 644 | console.log(error.stack) 645 | console.log(route) 646 | } 647 | 648 | res.send(500, { error: 'Internal server error' }) 649 | }) 650 | 651 | callback(app); 652 | } 653 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var should = require('should'); 2 | var request = require('supertest'); 3 | var config = require('../config'); 4 | 5 | var instance; 6 | var db = require('monk')(config.database.host + config.database.test_name); 7 | var plugins = db.get('plugins'); 8 | var webstats = db.get('webstats'); 9 | var geninfo = db.get('geninfo'); 10 | var authors = db.get('authors'); 11 | var categories = db.get('categories'); 12 | //Cleanup when testing locally to make sure it's not DB related 13 | plugins.remove({}, function callback (err, res) {}); 14 | webstats.remove({}, function callback (err, res) {}); 15 | geninfo.remove({}, function callback (err, res) {}); 16 | authors.remove({}, function callback (err, res) {}); 17 | categories.remove({}, function callback (err, res) {}); 18 | 19 | authors.id = function (str) { return str; }; 20 | categories.id = function (str) { return str; }; 21 | 22 | require('../server')(config.database.host + config.database.test_name, function (callback) { instance = callback; }); 23 | 24 | var webstat = { 'bukkitdev': 1409, 'unique': 8731, 'api1': 40189, 'api2': 139654, 'api3': 24332, 'timestamp': Math.round(new Date().getTime() / 1000), 'total': 384421, 'plugins': { 'monsterflight': { 'total': 1, 'unique': 1 }, 'dynmap': { 'total': 1, 'unique': 1 } } } 25 | var webstat_no_plugins = { 'bukkitdev': webstat.bukkitdev, 'unique': webstat.unique, 'api1': webstat.api1, 'api2': webstat.api2, 'api3': webstat.api3, 'timestamp': webstat.timestamp, 'total': webstat.total } 26 | var webstat_specific_plugin = { 'timestamp': webstat.timestamp, 'plugins': { 'dynmap' : webstat.plugins['dynmap'] } } 27 | var webstat_multiple_plugins = { 'timestamp': webstat.timestamp, 'plugins': webstat.plugins } 28 | 29 | var plugin = { 'website': 'http://dev.bukkit.org/bukkit-plugins/clearthechat', 'dbo_page': 'http://dev.bukkit.org/bukkit-plugins/clearthechat', 'main': 'com.iAlexak.ClearTheChat.ClearTheChat', 'description': '', 'curse_id': 48244, 'versions': [ { 'status': 'Semi-normal', 'commands': [ { 'usage': '/', 'permission': '', 'command': 'clearchat', 'permission-message': '', 'aliases': [ ] } ], 'changelog': 'PGRpdiBjbGFzcz0iY29udGVudC1ib3gtaW5uZXIiPgo8aDM+CiBDaGFuZ2UgbG9nCjwvaDM+Cjxz\nZWN0aW9uIGNsYXNzPSJ0b2MgZXhwYW5kaW5nLW1vZHVsZSIgZGF0YS1leHBhbmQtYnk9InRvYyIg\nZGF0YS1leHBhbmQtZGVmYXVsdD0ib3BlbiI+CiA8ZGl2IGNsYXNzPSJjb250ZW50LWJveCI+CiAg\nPGRpdiBjbGFzcz0iY29udGVudC1ib3gtaW5uZXIiPgogICA8aDM+CiAgICBUYWJsZSBvZiBjb250\nZW50cwogICA8L2gzPgogICA8b2wgY2xhc3M9InRvYy1sZXZlbCB0b2MtbGV2ZWwtMSI+CiAgICA8\nbGk+CiAgICAgPGEgaHJlZj0iI3ctdmVyc2lvbi0xLTAiPgogICAgICA8c3BhbiBjbGFzcz0idG9j\nLW51bWJlciI+CiAgICAgICAxCiAgICAgIDwvc3Bhbj4KICAgICAgPHNwYW4gY2xhc3M9InRvYy10\nZXh0Ij4KICAgICAgIFZlcnNpb24gMS4wCiAgICAgIDwvc3Bhbj4KICAgICA8L2E+CiAgICAgPG9s\nIGNsYXNzPSJ0b2MtbGV2ZWwgdG9jLWxldmVsLTIiPgogICAgICA8bGk+CiAgICAgICA8YSBocmVm\nPSIjdy1yZWxlYXNlIj4KICAgICAgICA8c3BhbiBjbGFzcz0idG9jLW51bWJlciI+CiAgICAgICAg\nIDEuMQogICAgICAgIDwvc3Bhbj4KICAgICAgICA8c3BhbiBjbGFzcz0idG9jLXRleHQiPgogICAg\nICAgICArIFJlbGVhc2UKICAgICAgICA8L3NwYW4+CiAgICAgICA8L2E+CiAgICAgIDwvbGk+CiAg\nICAgPC9vbD4KICAgIDwvbGk+CiAgIDwvb2w+CiAgPC9kaXY+CiA8L2Rpdj4KPC9zZWN0aW9uPgo8\naDIgaWQ9InctdmVyc2lvbi0xLTAiPgogVmVyc2lvbiAxLjAKPC9oMj4KPGg0IGlkPSJ3LXJlbGVh\nc2UiPgogKyBSZWxlYXNlCjwvaDQ+CjwvZGl2Pg==\n', 'game_versions': [ 'CB 1.4.5-R0.2' ], 'filename': 'ClearTheChat_-_Version_1.0__GER_.jar', 'hard_dependencies': [ ], 'date': 1355009738, 'version': '1.0', 'link': 'http://dev.bukkit.org/bukkit-plugins/clearthechat/files/2-clear-the-chat-version-1-0-deutsche-version/', 'file_id': 655365, 'md5': '4bbe4c806f5ffc9547d2db95f76175f6', 'download': 'http://dev.bukkit.org/media/files/655/365/ClearTheChat_-_Version_1.0__GER_.jar', 'dbo_version': '1.0', 'type': 'Release', 'slug': '2-clear-the-chat-version-1-0-deutsche-version', 'soft_dependencies': [ ], 'permissions': [ ] }, { 'status': 'Semi-normal', 'commands': [ { 'usage': '/', 'permission': '', 'command': 'clearchat', 'permission-message': '', 'aliases': [ ] } ], 'changelog': 'PGRpdiBjbGFzcz0iY29udGVudC1ib3gtaW5uZXIiPgo8aDM+CiBDaGFuZ2UgbG9nCjwvaDM+Cjxz\nZWN0aW9uIGNsYXNzPSJ0b2MgZXhwYW5kaW5nLW1vZHVsZSIgZGF0YS1leHBhbmQtYnk9InRvYyIg\nZGF0YS1leHBhbmQtZGVmYXVsdD0ib3BlbiI+CiA8ZGl2IGNsYXNzPSJjb250ZW50LWJveCI+CiAg\nPGRpdiBjbGFzcz0iY29udGVudC1ib3gtaW5uZXIiPgogICA8aDM+CiAgICBUYWJsZSBvZiBjb250\nZW50cwogICA8L2gzPgogICA8b2wgY2xhc3M9InRvYy1sZXZlbCB0b2MtbGV2ZWwtMSI+CiAgICA8\nbGk+CiAgICAgPGEgaHJlZj0iI3ctdmVyc2lvbi0xLTAiPgogICAgICA8c3BhbiBjbGFzcz0idG9j\nLW51bWJlciI+CiAgICAgICAxCiAgICAgIDwvc3Bhbj4KICAgICAgPHNwYW4gY2xhc3M9InRvYy10\nZXh0Ij4KICAgICAgIFZlcnNpb24gMS4wCiAgICAgIDwvc3Bhbj4KICAgICA8L2E+CiAgICAgPG9s\nIGNsYXNzPSJ0b2MtbGV2ZWwgdG9jLWxldmVsLTIiPgogICAgICA8bGk+CiAgICAgICA8YSBocmVm\nPSIjdy1yZWxlYXNlIj4KICAgICAgICA8c3BhbiBjbGFzcz0idG9jLW51bWJlciI+CiAgICAgICAg\nIDEuMQogICAgICAgIDwvc3Bhbj4KICAgICAgICA8c3BhbiBjbGFzcz0idG9jLXRleHQiPgogICAg\nICAgICArIFJlbGVhc2UKICAgICAgICA8L3NwYW4+CiAgICAgICA8L2E+CiAgICAgIDwvbGk+CiAg\nICAgPC9vbD4KICAgIDwvbGk+CiAgIDwvb2w+CiAgPC9kaXY+CiA8L2Rpdj4KPC9zZWN0aW9uPgo8\naDIgaWQ9InctdmVyc2lvbi0xLTAiPgogVmVyc2lvbiAxLjAKPC9oMj4KPGg0IGlkPSJ3LXJlbGVh\nc2UiPgogKyBSZWxlYXNlCjwvaDQ+CjwvZGl2Pg==\n', 'game_versions': [ 'CB 1.4.5-R0.2' ], 'filename': 'ClearTheChat_-_Version_1.0__ENG_.jar', 'hard_dependencies': [ ], 'date': 1355009707, 'version': '1.0', 'link': 'http://dev.bukkit.org/bukkit-plugins/clearthechat/files/1-clear-the-chat-version-1-0-english-version/', 'file_id': 655364, 'md5': '018cb6d144fa1e2cb98600b3ad75ed0d', 'download': 'http://dev.bukkit.org/media/files/655/364/ClearTheChat_-_Version_1.0__ENG_.jar', 'dbo_version': '1.0', 'type': 'Release', 'slug': '1-clear-the-chat-version-1-0-english-version', 'soft_dependencies': [ ], 'permissions': [ ] } ], 'popularity': { 'monthly': 0, 'daily': 0, 'weekly': 0 }, 'plugin_name': 'ClearTheChat', 'server': 'bukkit', 'curse_link': 'http://www.curse.com/bukkit-plugins/minecraft/clearthechat', 'logo_full': 'http://dev.bukkit.org/media/images/48/819/Paper.png', 'authors': [ 'iAlexak' ], 'logo': 'http://dev.bukkit.org/thumbman/images/48/819/100x100/Paper.png.-m1.png', 'slug': 'clearthechat', 'categories': [ 'Chat Related', 'Admin Tools' ], 'stage': 'Release' } 30 | var plugin_two = { 'website': 'http://dev.bukkit.org/bukkit-plugins/abitofrealism', 'dbo_page': 'http://dev.bukkit.org/bukkit-plugins/abitofrealism', 'main': 'com.mcheaven.abitofrealism.AbOR_Main', 'description': '', 'curse_id': 40285, 'versions': [ { 'status': 'Semi-normal', 'commands': [], 'changelog': 'PGRpdiBjbGFzcz0iY29udGVudC1ib3gtaW5uZXIiPgo8aDM+CiBDaGFuZ2UgbG9nCjwvaDM+Cjxw\nPgogPHN0cm9uZz4KICAwLjMuMQogPC9zdHJvbmc+CjwvcD4KPHVsPgogPGxpPgogIFJlbW92ZWQg\nRGVidWctTWVzc2FnZQogPC9saT4KPC91bD4KPHA+CiA8c3Ryb25nPgogIDAuMwogPC9zdHJvbmc+\nCjwvcD4KPHVsPgogPGxpPgogIEtlZXAgZWF0aW5nIFN1Z2FyIGFuZCBnZXQgZmFzdGVyISAoaXRz\nIGFkZGluZyB0aGUgdGltZSB5b3Ugc3RheSBmYXN0IHRvbykKIDwvbGk+CjwvdWw+CjxwPgogPHN0\ncm9uZz4KICAwLjIKIDwvc3Ryb25nPgo8L3A+Cjx1bD4KIDxsaT4KICBIZWFkc2hvdCBGZWF0dXJl\nIChBcnJvd3MpIGlmIGEgcGxheWVycyBzaG9vdHMgYW5vdGhlciBwbGF5ZXIgaW4gdGhlIGhlYWQs\nIHNlcnZlciBzYXlzIEhFQURTSE9UISBhbmQgaXQgZG9lcyBkb3VibGUgZGFtYWdlCiAgPHNwYW4g\nY2xhc3M9ImVtb3RlIGVtb3RlLXNtaWxlIiB0aXRsZT0iU21pbGUiPgogICA6KQogIDwvc3Bhbj4K\nICBQbGVhc2UgdGVzdCBpdCBvdXQgb2YgY291cnNlIEknbGwgY2hhbmdlIGRhbWFnZSBhbmQgbWVz\nc2FnZSBsYXRlci4uCiA8L2xpPgo8L3VsPgo8cD4KIDxzdHJvbmc+CiAgMC4xCiA8L3N0cm9uZz4K\nPC9wPgo8dWw+CiA8bGk+CiAgRmFsbCBEYW1hZ2UgJmd0OyBTbG93bmVzcwogIDxlbT4KICAgVGVz\ndCBpdCBvdXQhCiAgPC9lbT4KIDwvbGk+CiA8bGk+CiAgRWF0IFN1Z2FyICZndDsgU3BlZWQKICA8\nZW0+CiAgIFRlc3QgaXQgb3V0IQogIDwvZW0+CiA8L2xpPgo8L3VsPgo8L2Rpdj4=\n', 'game_versions': [ '1.2.5' ], 'filename': 'AbitOfRealism.jar', 'hard_dependencies': [], 'date': 1340493789, 'version': '0.3.1', 'link': 'http://dev.bukkit.org/bukkit-plugins/abitofrealism/files/4-abit-of-realism-0-3-1/', 'file_id': 599604, 'md5': '236c18df1d15e149fe91675c08efa8b5', 'download': 'http://dev.bukkit.org/media/files/599/604/AbitOfRealism.jar', 'dbo_version': '0.3.1', 'type': 'None', 'slug': '4-abit-of-realism-0-3-1', 'soft_dependencies': [], 'permissions': [] }, { 'status': 'Semi-normal', 'commands': [], 'changelog': 'PGRpdiBjbGFzcz0iY29udGVudC1ib3gtaW5uZXIiPgo8aDM+CiBDaGFuZ2UgbG9nCjwvaDM+Cjxw\nPgogPHN0cm9uZz4KICAwLjMKIDwvc3Ryb25nPgo8L3A+Cjx1bD4KIDxsaT4KICBLZWVwIGVhdGlu\nZyBTdWdhciBhbmQgZ2V0IGZhc3RlciEgKGl0cyBhZGRpbmcgdGhlIHRpbWUgeW91IHN0YXkgZmFz\ndCB0b28pCiA8L2xpPgo8L3VsPgo8cD4KIDxzdHJvbmc+CiAgMC4yCiA8L3N0cm9uZz4KPC9wPgo8\ndWw+CiA8bGk+CiAgSGVhZHNob3QgRmVhdHVyZSAoQXJyb3dzKSBpZiBhIHBsYXllcnMgc2hvb3Rz\nIGFub3RoZXIgcGxheWVyIGluIHRoZSBoZWFkLCBzZXJ2ZXIgc2F5cyBIRUFEU0hPVCEgYW5kIGl0\nIGRvZXMgZG91YmxlIGRhbWFnZQogIDxzcGFuIGNsYXNzPSJlbW90ZSBlbW90ZS1zbWlsZSIgdGl0\nbGU9IlNtaWxlIj4KICAgOikKICA8L3NwYW4+CiAgUGxlYXNlIHRlc3QgaXQgb3V0IG9mIGNvdXJz\nZSBJJ2xsIGNoYW5nZSBkYW1hZ2UgYW5kIG1lc3NhZ2UgbGF0ZXIuLgogPC9saT4KPC91bD4KPHA+\nCiA8c3Ryb25nPgogIDAuMQogPC9zdHJvbmc+CjwvcD4KPHVsPgogPGxpPgogIEZhbGwgRGFtYWdl\nICZndDsgU2xvd25lc3MKICA8ZW0+CiAgIFRlc3QgaXQgb3V0IQogIDwvZW0+CiA8L2xpPgogPGxp\nPgogIEVhdCBTdWdhciAmZ3Q7IFNwZWVkCiAgPGVtPgogICBUZXN0IGl0IG91dCEKICA8L2VtPgog\nPC9saT4KPC91bD4KPC9kaXY+\n', 'game_versions': [ '1.2.5' ], 'filename': 'AbitOfRealism.jar', 'hard_dependencies': [], 'date': 1339605762, 'version': '0.3', 'link': 'http://dev.bukkit.org/bukkit-plugins/abitofrealism/files/3-abit-of-realism-0-3/', 'file_id': 597975, 'md5': '49ab15446ae1bfce8801433cd75f8fc9', 'download': 'http://dev.bukkit.org/media/files/597/975/AbitOfRealism.jar', 'dbo_version': '0.3', 'type': 'Alpha', 'slug': '3-abit-of-realism-0-3', 'soft_dependencies': [], 'permissions': [] }, { 'status': 'Semi-normal', 'commands': [], 'changelog': 'PGRpdiBjbGFzcz0iY29udGVudC1ib3gtaW5uZXIiPgo8aDM+CiBDaGFuZ2UgbG9nCjwvaDM+Cjxw\nPgogPHN0cm9uZz4KICAwLjIKIDwvc3Ryb25nPgo8L3A+Cjx1bD4KIDxsaT4KICBIZWFkc2hvdCBG\nZWF0dXJlIChBcnJvd3MpIGlmIGEgcGxheWVycyBzaG9vdHMgYW5vdGhlciBwbGF5ZXIgaW4gdGhl\nIGhlYWQsIHNlcnZlciBzYXlzIEhFQURTSE9UISBhbmQgaXQgZG9lcyBkb3VibGUgZGFtYWdlCiAg\nPHNwYW4gY2xhc3M9ImVtb3RlIGVtb3RlLXNtaWxlIiB0aXRsZT0iU21pbGUiPgogICA6KQogIDwv\nc3Bhbj4KICBQbGVhc2UgdGVzdCBpdCBvdXQgb2YgY291cnNlIEknbGwgY2hhbmdlIGRhbWFnZSBh\nbmQgbWVzc2FnZSBsYXRlci4uCiA8L2xpPgo8L3VsPgo8cD4KIDxzdHJvbmc+CiAgMC4xCiA8L3N0\ncm9uZz4KPC9wPgo8dWw+CiA8bGk+CiAgRmFsbCBEYW1hZ2UgJmd0OyBTbG93bmVzcwogIDxlbT4K\nICAgVGVzdCBpdCBvdXQhCiAgPC9lbT4KIDwvbGk+CiA8bGk+CiAgRWF0IFN1Z2FyICZndDsgU3Bl\nZWQKICA8ZW0+CiAgIFRlc3QgaXQgb3V0IQogIDwvZW0+CiA8L2xpPgo8L3VsPgo8L2Rpdj4=\n', 'game_versions': [ '1.2.5' ], 'filename': 'AbitOfRealism.jar', 'hard_dependencies': [], 'date': 1338732054, 'version': '0.2', 'link': 'http://dev.bukkit.org/bukkit-plugins/abitofrealism/files/2-abit-of-realism-0-2/', 'file_id': 596426, 'md5': '9ab16c40a6cbff566965a37f137c25bf', 'download': 'http://dev.bukkit.org/media/files/596/426/AbitOfRealism.jar', 'dbo_version': '0.2', 'type': 'Beta', 'slug': '2-abit-of-realism-0-2', 'soft_dependencies': [], 'permissions': [] }, { 'status': 'Semi-normal', 'commands': [], 'changelog': 'PGRpdiBjbGFzcz0iY29udGVudC1ib3gtaW5uZXIiPgo8aDM+CiBDaGFuZ2UgbG9nCjwvaDM+Cjxw\nPgogPHN0cm9uZz4KICAwLjEKIDwvc3Ryb25nPgo8L3A+Cjx1bD4KIDxsaT4KICBGYWxsIERhbWFn\nZSAmZ3Q7IFNsb3duZXNzCiAgPGVtPgogICBUZXN0IGl0IG91dCEKICA8L2VtPgogPC9saT4KIDxs\naT4KICBFYXQgU3VnYXIgJmd0OyBTcGVlZAogIDxlbT4KICAgVGVzdCBpdCBvdXQhCiAgPC9lbT4K\nIDwvbGk+CjwvdWw+CjwvZGl2Pg==\n', 'game_versions': [ '1.2.5' ], 'filename': 'AbitOfRealism.jar', 'hard_dependencies': [], 'date': 1338663342, 'version': '0.1', 'link': 'http://dev.bukkit.org/bukkit-plugins/abitofrealism/files/1-abit-of-realism-0-1/', 'file_id': 596293, 'md5': '2458133b2813b66e2c162ce7bbdf5c18', 'download': 'http://dev.bukkit.org/media/files/596/293/AbitOfRealism.jar', 'dbo_version': '0.1', 'type': 'Release', 'slug': '1-abit-of-realism-0-1', 'soft_dependencies': [], 'permissions': [] } ], 'popularity': { 'monthly': 19, 'daily': 33, 'weekly': 9 }, 'plugin_name': 'AbitOfRealism', 'server': 'bukkit', 'curse_link': 'http://www.curse.com/bukkit-plugins/minecraft/abitofrealism', 'logo_full': '', 'authors': [ 'mcheaven' ], '_use_dbo': true, 'logo': '', 'slug': 'abitofrealism', 'categories': [ 'Fixes', 'Fun', 'General' ], 'stage': 'Release' } 31 | var plugin_version_latest = { 'website': plugin_two.website, 'dbo_page': plugin_two.dbo_page, 'main': plugin_two.main, 'description': plugin_two.description, 'curse_id': plugin_two.curse_id, 'versions': [ plugin_two.versions[0] ], 'popularity': plugin_two.popularity, 'plugin_name': plugin_two.plugin_name, 'server': plugin_two.server, 'curse_link': plugin_two.curse_link, 'logo_full': plugin_two.logo_full, 'authors': plugin_two.authors, '_use_dbo': plugin_two._use_dbo, 'logo': plugin_two.logo, 'slug': plugin_two.slug, 'categories': plugin_two.categories, 'stage': plugin_two.stage } 32 | var plugin_version_alpha = { 'website': plugin_two.website, 'dbo_page': plugin_two.dbo_page, 'main': plugin_two.main, 'description': plugin_two.description, 'curse_id': plugin_two.curse_id, 'versions': [ plugin_two.versions[1] ], 'popularity': plugin_two.popularity, 'plugin_name': plugin_two.plugin_name, 'server': plugin_two.server, 'curse_link': plugin_two.curse_link, 'logo_full': plugin_two.logo_full, 'authors': plugin_two.authors, '_use_dbo': plugin_two._use_dbo, 'logo': plugin_two.logo, 'slug': plugin_two.slug, 'categories': plugin_two.categories, 'stage': plugin_two.stage } 33 | var plugin_version_beta = { 'website': plugin_two.website, 'dbo_page': plugin_two.dbo_page, 'main': plugin_two.main, 'description': plugin_two.description, 'curse_id': plugin_two.curse_id, 'versions': [ plugin_two.versions[2] ], 'popularity': plugin_two.popularity, 'plugin_name': plugin_two.plugin_name, 'server': plugin_two.server, 'curse_link': plugin_two.curse_link, 'logo_full': plugin_two.logo_full, 'authors': plugin_two.authors, '_use_dbo': plugin_two._use_dbo, 'logo': plugin_two.logo, 'slug': plugin_two.slug, 'categories': plugin_two.categories, 'stage': plugin_two.stage } 34 | var plugin_version_release = { 'website': plugin_two.website, 'dbo_page': plugin_two.dbo_page, 'main': plugin_two.main, 'description': plugin_two.description, 'curse_id': plugin_two.curse_id, 'versions': [ plugin_two.versions[3] ], 'popularity': plugin_two.popularity, 'plugin_name': plugin_two.plugin_name, 'server': plugin_two.server, 'curse_link': plugin_two.curse_link, 'logo_full': plugin_two.logo_full, 'authors': plugin_two.authors, '_use_dbo': plugin_two._use_dbo, 'logo': plugin_two.logo, 'slug': plugin_two.slug, 'categories': plugin_two.categories, 'stage': plugin_two.stage } 35 | 36 | var update_versions = { 'current': { 'version': plugin_two.versions[1]['version'], 'download': plugin_two.versions[1]['download'],'md5': plugin_two.versions[1]['md5'] }, 'latest': { 'version': plugin_two.versions[0]['version'], 'download': plugin_two.versions[0]['download'], 'md5': plugin_two.versions[0]['md5'] }}; 37 | 38 | var author = { '_id': 'iAlexak', 'value': 5 } 39 | var category = { '_id': 'General', 'value': 1645 } 40 | 41 | describe('Stats', function() { 42 | var naughty_plugin; 43 | var webstat_test; 44 | before(function (done) { 45 | plugins.insert(plugin_two, {safe: true}, function (err, records) { 46 | webstats.insert(webstat, {safe: true}, function (err, records) { 47 | naughty_plugin = [{ 48 | 'plugin_name': plugin_two.plugin_name, 49 | 'authors': plugin_two.authors, 50 | 'slug': plugin_two.slug 51 | }] 52 | delete webstat['_id']; 53 | done(); 54 | }); 55 | }); 56 | }); 57 | it('returns proper naughty list', function (done) { 58 | request(instance) 59 | .get('/stats/naughty_list') 60 | .set('Accept', 'application/json') 61 | .expect('Content-Type', /json/) 62 | .expect(200) 63 | .end(function (err,res) { 64 | if (err) { 65 | throw err; 66 | } 67 | JSON.stringify(res.res.body).should.equal(JSON.stringify(naughty_plugin)); 68 | done(); 69 | }); 70 | }); 71 | it('returns todays_trends', function (done) { 72 | request(instance) 73 | .get('/stats/todays_trends') 74 | .set('Accept', 'application/json') 75 | .expect('Content-Type', /json/) 76 | .expect(200) 77 | .end(function (err,res) { 78 | if (err) { 79 | throw err; 80 | } 81 | JSON.stringify(res.res.body).should.equal(JSON.stringify({'plugin_count': 1, 'version_count': plugin_two.versions.length})); 82 | done(); 83 | }); 84 | }); 85 | it('returns webstats within date range', function (done) { 86 | request(instance) 87 | .get('/stats/trend/1') 88 | .set('Accept', 'application/json') 89 | .expect('Content-Type', /json/) 90 | .expect(200) 91 | .end(function (err,res) { 92 | if (err) { 93 | throw err; 94 | } 95 | JSON.stringify(res.res.body).should.equal(JSON.stringify([webstat_no_plugins])); 96 | done(); 97 | }); 98 | }); 99 | it('returns webstats within date range with all plugins', function (done) { 100 | request(instance) 101 | .get('/stats/trend/1?plugins=all') 102 | .set('Accept', 'application/json') 103 | .expect('Content-Type', /json/) 104 | .expect(200) 105 | .end(function (err,res) { 106 | if (err) { 107 | throw err; 108 | } 109 | JSON.stringify(res.res.body).should.equal(JSON.stringify([webstat])); 110 | done(); 111 | }); 112 | }); 113 | it('returns webstats within date range with specific plugin', function (done) { 114 | request(instance) 115 | .get('/stats/trend/1?plugins=dynmap') 116 | .set('Accept', 'application/json') 117 | .expect('Content-Type', /json/) 118 | .expect(200) 119 | .end(function (err,res) { 120 | if (err) { 121 | throw err; 122 | } 123 | JSON.stringify(res.res.body).should.equal(JSON.stringify([webstat_specific_plugin])); 124 | done(); 125 | }); 126 | }); 127 | it('returns webstats within date range with multiple plugins', function (done) { 128 | request(instance) 129 | .get('/stats/trend/1?plugins=monsterflight,dynmap') 130 | .set('Accept', 'application/json') 131 | .expect('Content-Type', /json/) 132 | .expect(200) 133 | .end(function (err,res) { 134 | if (err) { 135 | throw err; 136 | } 137 | JSON.stringify(res.res.body).should.equal(JSON.stringify([webstat_multiple_plugins])); 138 | done(); 139 | }); 140 | }); 141 | after(function (done) { 142 | plugins.remove({}, function callback (err, res) { 143 | webstats.remove({}, function callback (err, res) { done(); }); 144 | }); 145 | }); 146 | }); 147 | 148 | describe('Misc', function() { 149 | var plugin_list; 150 | before(function (done) { 151 | plugins.insert(plugin, {safe: true}, function (err, records) { 152 | plugins.insert(plugin_two, {safe: true}, function (err, records) { 153 | plugin_list = [{ 154 | 'description': plugin_two.description, 155 | 'plugin_name': plugin_two.plugin_name, 156 | 'slug': plugin_two.slug 157 | },{ 158 | 'description': plugin.description, 159 | 'plugin_name': plugin.plugin_name, 160 | 'slug': plugin.slug 161 | }] 162 | done(); 163 | }); 164 | }); 165 | }); 166 | it('handle_parameters works properly', function (done) { 167 | request(instance) 168 | .get('/3/plugins/bukkit?size=1&start=1&sort=-slug&fields=slug,plugin_name') 169 | .set('Accept', 'application/json') 170 | .expect('Content-Type', /json/) 171 | .expect(200) 172 | .end(function (err,res) { 173 | if (err) { 174 | throw err; 175 | } 176 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'plugin_name': plugin_list[0].plugin_name, 'slug': plugin_list[0].slug }])); 177 | done(); 178 | }); 179 | }); 180 | after(function (done) { 181 | plugins.remove({}, function callback(err, res) { done(); }); 182 | }); 183 | }); 184 | 185 | describe('Geninfo', function() { 186 | var test_data = [{'timestamp':1391688444,'parser':'bukkit','changes':[{'version':'2.4.8','plugin':'notebook'},{'version':'0.3','plugin':'playerbar'},{'version':'0.4','plugin':'pvpessentials'},{'version':'0.0.4','plugin':'saveonempty'},{'version':'1.1.6','plugin':'contrabanner'},{'version':'3.8.2','plugin':'craftbook'},{'version':'1.2','plugin':'exp-2-money'},{'version':'1.1','plugin':'monitorfishing'},{'version':'1.0','plugin':'googlesave'}],'duration':441,'type':'speedy','_id': geninfo.id('52f37afcaab9e60332667aa2') }]; 187 | before(function (done) { 188 | geninfo.insert(test_data, {safe: true}, function (err, records) { 189 | test_data[0]['id'] = test_data[0]['_id']; 190 | delete test_data[0]['_id']; 191 | done(); 192 | }); 193 | }); 194 | it('returns list of geninfo', function (done) { 195 | request(instance) 196 | .get('/3/geninfo') 197 | .set('Accept', 'application/json') 198 | .expect('Content-Type', /json/) 199 | .expect(200) 200 | .end(function (err,res) { 201 | if (err) { 202 | throw err; 203 | } 204 | JSON.stringify(res.res.body).should.equal(JSON.stringify(test_data)); 205 | done(); 206 | }); 207 | }); 208 | it('returns specific geninfo', function (done) { 209 | request(instance) 210 | .get('/3/geninfo/' + test_data[0].id.toString()) 211 | .set('Accept', 'application/json') 212 | .expect('Content-Type', /json/) 213 | .expect(200) 214 | .end(function (err,res) { 215 | if (err) { 216 | throw err; 217 | } 218 | JSON.stringify(res.res.body).should.equal(JSON.stringify(test_data[0])); 219 | done(); 220 | }); 221 | }); 222 | after(function (done) { 223 | geninfo.remove({}, function callback(err, res) { done(); }); 224 | }); 225 | }); 226 | 227 | describe('Plugins', function() { 228 | var plugin_list; 229 | before(function (done) { 230 | plugins.insert(plugin, {safe: true}, function (err, records) { 231 | delete plugin['_id']; 232 | plugins.insert(plugin_two, {safe: true}, function (err, records) { 233 | delete plugin_two['_id']; 234 | plugin_list = [{ 235 | 'description': plugin_two.description, 236 | 'plugin_name': plugin_two.plugin_name, 237 | 'slug': plugin_two.slug 238 | },{ 239 | 'description': plugin.description, 240 | 'plugin_name': plugin.plugin_name, 241 | 'slug': plugin.slug 242 | }] 243 | done(); 244 | }); 245 | }); 246 | }); 247 | it('returns list of plugins', function (done) { 248 | request(instance) 249 | .get('/3/plugins') 250 | .set('Accept', 'application/json') 251 | .expect('Content-Type', /json/) 252 | .expect(200) 253 | .end(function (err,res) { 254 | if (err) { 255 | throw err; 256 | } 257 | JSON.stringify(res.res.body).should.equal(JSON.stringify(plugin_list)); 258 | done(); 259 | }); 260 | }); 261 | it('returns list of plugins with specific server', function (done) { 262 | request(instance) 263 | .get('/3/plugins/bukkit') 264 | .set('Accept', 'application/json') 265 | .expect('Content-Type', /json/) 266 | .expect(200) 267 | .end(function (err,res) { 268 | if (err) { 269 | throw err; 270 | } 271 | JSON.stringify(res.res.body).should.equal(JSON.stringify(plugin_list)); 272 | done(); 273 | }); 274 | }); 275 | it('returns specific plugin', function (done) { 276 | request(instance) 277 | .get('/3/plugins/bukkit/abitofrealism') 278 | .set('Accept', 'application/json') 279 | .expect('Content-Type', /json/) 280 | .expect(200) 281 | .end(function (err,res) { 282 | if (err) { 283 | throw err; 284 | } 285 | JSON.stringify(res.res.body).should.equal(JSON.stringify(plugin_two)); 286 | done(); 287 | }); 288 | }); 289 | it('returns specific version of specific plugin', function (done) { 290 | request(instance) 291 | .get('/3/plugins/bukkit/abitofrealism/0.3.1') 292 | .set('Accept', 'application/json') 293 | .expect('Content-Type', /json/) 294 | .expect(200) 295 | .end(function (err,res) { 296 | if (err) { 297 | throw err; 298 | } 299 | JSON.stringify(res.res.body).should.equal(JSON.stringify(plugin_version_latest)); 300 | done(); 301 | }); 302 | }); 303 | it('returns latest version of specific plugin', function (done) { 304 | request(instance) 305 | .get('/3/plugins/bukkit/abitofrealism/latest') 306 | .set('Accept', 'application/json') 307 | .expect('Content-Type', /json/) 308 | .expect(200) 309 | .end(function (err,res) { 310 | if (err) { 311 | throw err; 312 | } 313 | JSON.stringify(res.res.body).should.equal(JSON.stringify(plugin_version_latest)); 314 | done(); 315 | }); 316 | }); 317 | it('returns alpha version of specific plugin', function (done) { 318 | request(instance) 319 | .get('/3/plugins/bukkit/abitofrealism/alpha') 320 | .set('Accept', 'application/json') 321 | .expect('Content-Type', /json/) 322 | .expect(200) 323 | .end(function (err,res) { 324 | if (err) { 325 | throw err; 326 | } 327 | JSON.stringify(res.res.body).should.equal(JSON.stringify(plugin_version_alpha)); 328 | done(); 329 | }); 330 | }); 331 | it('returns beta version of specific plugin', function (done) { 332 | request(instance) 333 | .get('/3/plugins/bukkit/abitofrealism/beta') 334 | .set('Accept', 'application/json') 335 | .expect('Content-Type', /json/) 336 | .expect(200) 337 | .end(function (err,res) { 338 | if (err) { 339 | throw err; 340 | } 341 | JSON.stringify(res.res.body).should.equal(JSON.stringify(plugin_version_beta)); 342 | done(); 343 | }); 344 | }); 345 | it('returns release version of specific plugin', function (done) { 346 | request(instance) 347 | .get('/3/plugins/bukkit/abitofrealism/release') 348 | .set('Accept', 'application/json') 349 | .expect('Content-Type', /json/) 350 | .expect(200) 351 | .end(function (err,res) { 352 | if (err) { 353 | throw err; 354 | } 355 | JSON.stringify(res.res.body).should.equal(JSON.stringify(plugin_version_release)); 356 | done(); 357 | }); 358 | }); 359 | it('redirects correctly for specific version download for specific plugin', function (done) { 360 | request(instance) 361 | .get('/3/plugins/bukkit/abitofrealism/0.3.1/download') 362 | .expect(302) 363 | .end(function (err,res) { 364 | if (err) { 365 | throw err; 366 | } 367 | res.header['location'].should.equal(plugin_two.versions[0].download); 368 | done(); 369 | }); 370 | }); 371 | it('redirects correctly for latest version download for specific plugin', function (done) { 372 | request(instance) 373 | .get('/3/plugins/bukkit/abitofrealism/latest/download') 374 | .expect(302) 375 | .end(function (err,res) { 376 | if (err) { 377 | throw err; 378 | } 379 | res.header['location'].should.equal(plugin_two.versions[0].download); 380 | done(); 381 | }); 382 | }); 383 | after(function (done) { 384 | plugins.remove({}, function callback(err, res) { done(); }); 385 | }); 386 | }); 387 | 388 | describe('Authors', function() { 389 | before(function (done) { 390 | plugins.insert(plugin, {safe: true}, function (err, records) { 391 | authors.insert(author, {safe: true}, function (err, records) { 392 | done(); 393 | }); 394 | }); 395 | }); 396 | it('returns list of authors', function (done) { 397 | request(instance) 398 | .get('/3/authors') 399 | .set('Accept', 'application/json') 400 | .expect('Content-Type', /json/) 401 | .expect(200) 402 | .end(function (err,res) { 403 | if (err) { 404 | throw err; 405 | } 406 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'name': author._id, 'count': author.value }])); 407 | done(); 408 | }); 409 | }); 410 | it('returns list of plugins made by author', function (done) { 411 | request(instance) 412 | .get('/3/authors/' + author._id) 413 | .set('Accept', 'application/json') 414 | .expect('Content-Type', /json/) 415 | .expect(200) 416 | .end(function (err,res) { 417 | if (err) { 418 | throw err; 419 | } 420 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin.description, 'plugin_name': plugin.plugin_name, 'slug': plugin.slug }])); 421 | done(); 422 | }); 423 | }); 424 | it('returns list of plugins made by author for specific server', function (done) { 425 | request(instance) 426 | .get('/3/authors/bukkit/' + author._id) 427 | .set('Accept', 'application/json') 428 | .expect('Content-Type', /json/) 429 | .expect(200) 430 | .end(function (err,res) { 431 | if (err) { 432 | throw err; 433 | } 434 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin.description, 'plugin_name': plugin.plugin_name, 'slug': plugin.slug }])); 435 | done(); 436 | }); 437 | }); 438 | after(function (done) { 439 | authors.remove({}, function callback(err, res) { 440 | plugins.remove({}, function callback(err, res) { done(); }); 441 | }); 442 | }); 443 | }); 444 | 445 | describe('Categories', function() { 446 | before(function (done) { 447 | plugins.insert(plugin_two, {safe: true}, function (err, records) { 448 | categories.insert(category, {safe: true}, function (err, records) { 449 | done(); 450 | }); 451 | }); 452 | }); 453 | it('returns list of categories', function (done) { 454 | request(instance) 455 | .get('/3/categories') 456 | .set('Accept', 'application/json') 457 | .expect('Content-Type', /json/) 458 | .expect(200) 459 | .end(function (err,res) { 460 | if (err) { 461 | throw err; 462 | } 463 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'name': category._id, 'count': category.value }])); 464 | done(); 465 | }); 466 | }); 467 | it('returns list of plugins in specific category', function (done) { 468 | request(instance) 469 | .get('/3/categories/' + category._id) 470 | .set('Accept', 'application/json') 471 | .expect('Content-Type', /json/) 472 | .expect(200) 473 | .end(function (err,res) { 474 | if (err) { 475 | throw err; 476 | } 477 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin_two.description, 'plugin_name': plugin_two.plugin_name, 'slug': plugin_two.slug }])); 478 | done(); 479 | }); 480 | }); 481 | it('returns list of plugins in specific category for specific server', function (done) { 482 | request(instance) 483 | .get('/3/categories/bukkit/' + category._id) 484 | .set('Accept', 'application/json') 485 | .expect('Content-Type', /json/) 486 | .expect(200) 487 | .end(function (err,res) { 488 | if (err) { 489 | throw err; 490 | } 491 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin_two.description, 'plugin_name': plugin_two.plugin_name, 'slug': plugin_two.slug }])); 492 | done(); 493 | }); 494 | }); 495 | after(function (done) { 496 | categories.remove({}, function callback(err, res) { 497 | plugins.remove({}, function callback(err, res) { done(); }); 498 | }); 499 | }); 500 | }); 501 | 502 | describe('Updates', function() { 503 | before(function (done) { 504 | plugins.insert(plugin_two, {safe: true}, function (err, records) { 505 | var versions = plugin_two.versions; 506 | for (var x = 0, versionLen = versions.length; x < versionLen; x++) { 507 | var version = versions[x]; 508 | 509 | if (version['type'] == 'Release' || version['type'] == 'Beta' || version['type'] == 'Alpha') { 510 | if (update_versions[version['type'].toLowerCase()] == null) { 511 | update_versions[version['type'].toLowerCase()] = { 'version': version['version'], 'download' : version['download'], 'md5': version['md5'] }; 512 | } 513 | } 514 | } 515 | done(); 516 | }); 517 | }); 518 | it('returns list of latest versions by md5', function (done) { 519 | request(instance) 520 | .get('/3/updates?hashes=49ab15446ae1bfce8801433cd75f8fc9') 521 | .set('Accept', 'application/json') 522 | .expect('Content-Type', /json/) 523 | .expect(200) 524 | .end(function (err,res) { 525 | if (err) { 526 | throw err; 527 | } 528 | (res.res.body).should.eql([{ 'slug': plugin_two.slug, 'plugin_name': plugin_two.plugin_name, 'versions': update_versions, 'hash': plugin_two.versions[1]['md5'] }]); 529 | done(); 530 | }); 531 | }); 532 | it('returns list of latest versions via get', function (done) { 533 | request(instance) 534 | .get('/3/updates?slugs=abitofrealism') 535 | .set('Accept', 'application/json') 536 | .expect('Content-Type', /json/) 537 | .expect(200) 538 | .end(function (err,res) { 539 | if (err) { 540 | throw err; 541 | } 542 | if (update_versions.current) delete update_versions.current; 543 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'slug': plugin_two.slug, 'plugin_name': plugin_two.plugin_name, 'versions': update_versions }])); 544 | done(); 545 | }); 546 | }); 547 | it('returns list of latest versions via post', function (done) { 548 | request(instance) 549 | .post('/3/updates?slugs=abitofrealism') 550 | .set('Accept', 'application/json') 551 | .expect('Content-Type', /json/) 552 | .expect(200) 553 | .end(function (err,res) { 554 | if (err) { 555 | throw err; 556 | } 557 | if (update_versions.current) delete update_versions.current; 558 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'slug': plugin_two.slug, 'plugin_name': plugin_two.plugin_name, 'versions': update_versions }])); 559 | done(); 560 | }); 561 | }); 562 | it('returns list of latest versions for specific server', function (done) { 563 | request(instance) 564 | .get('/3/updates?slugs=abitofrealism&server=bukkit') 565 | .set('Accept', 'application/json') 566 | .expect('Content-Type', /json/) 567 | .expect(200) 568 | .end(function (err,res) { 569 | if (err) { 570 | throw err; 571 | } 572 | if (update_versions.current) delete update_versions.current; 573 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'slug': plugin_two.slug, 'plugin_name': plugin_two.plugin_name, 'versions': update_versions }])); 574 | done(); 575 | }); 576 | }); 577 | it('returns list of latest versions by filename', function (done) { 578 | request(instance) 579 | .get('/3/updates?filenames=AbitOfRealism.jar') 580 | .set('Accept', 'application/json') 581 | .expect('Content-Type', /json/) 582 | .expect(200) 583 | .end(function (err,res) { 584 | if (err) { 585 | throw err; 586 | } 587 | if (update_versions.current) delete update_versions.current; 588 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'slug': plugin_two.slug, 'plugin_name': plugin_two.plugin_name, 'versions': update_versions, 'file': plugin_two.versions[0].filename }])); 589 | done(); 590 | }); 591 | }); 592 | it('returns extra fields correctly', function (done) { 593 | request(instance) 594 | .get('/3/updates?extra_fields=website&filenames=AbitOfRealism.jar') 595 | .set('Accept', 'application/json') 596 | .expect('Content-Type', /json/) 597 | .expect(200) 598 | .end(function (err,res) { 599 | if (err) { 600 | throw err; 601 | } 602 | if (update_versions.current) delete update_versions.current; 603 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'slug': plugin_two.slug, 'plugin_name': plugin_two.plugin_name, 'versions': update_versions, 'website': plugin_two.website, 'file': plugin_two.versions[0].filename }])); 604 | done(); 605 | }); 606 | }); 607 | it('returns extra version fields correctly', function (done) { 608 | request(instance) 609 | .get('/3/updates?extra_version_fields=status&filenames=AbitOfRealism.jar') 610 | .set('Accept', 'application/json') 611 | .expect('Content-Type', /json/) 612 | .expect(200) 613 | .end(function (err,res) { 614 | if (err) { 615 | throw err; 616 | } 617 | if (update_versions.current) delete update_versions.current; 618 | for (i in update_versions) { 619 | update_versions[i]['status'] = plugin_two.versions[0]['status']; 620 | } 621 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'slug': plugin_two.slug, 'plugin_name': plugin_two.plugin_name, 'versions': update_versions, 'file': plugin_two.versions[0].filename }])); 622 | done(); 623 | }); 624 | }); 625 | after(function (done) { 626 | plugins.remove({}, function callback(err, res) { done(); }); 627 | }); 628 | }); 629 | 630 | describe('Search', function() { 631 | before(function (done) { 632 | plugins.insert(plugin_two, {safe: true}, function (err, records) { 633 | plugins.insert(plugin, {safe: true}, function (err, records) { 634 | done(); 635 | }); 636 | }); 637 | }); 638 | it('basic get search', function (done) { 639 | request(instance) 640 | .get('/3/search/slug/equals/abitofrealism') 641 | .set('Accept', 'application/json') 642 | .expect('Content-Type', /json/) 643 | .expect(200) 644 | .end(function (err,res) { 645 | if (err) { 646 | throw err; 647 | } 648 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin_two.description, 'plugin_name': plugin_two.plugin_name, 'slug': plugin_two.slug }])); 649 | done(); 650 | }); 651 | }); 652 | it('basic post search', function (done) { 653 | request(instance) 654 | .post('/3/search') 655 | .send({ 'filters': JSON.stringify([{'field': 'slug', 'action': 'equals', 'value': 'abitofrealism' }]) }) 656 | .set('Accept', 'application/json') 657 | .expect('Content-Type', /json/) 658 | .expect(200) 659 | .end(function (err,res) { 660 | if (err) { 661 | throw err; 662 | } 663 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin_two.description, 'plugin_name': plugin_two.plugin_name, 'slug': plugin_two.slug }])); 664 | done(); 665 | }); 666 | }); 667 | it('equals', function (done) { 668 | request(instance) 669 | .post('/3/search') 670 | .send({ 'filters': JSON.stringify([{'field': 'slug', 'action': 'equals', 'value': 'abitofrealism' }]) }) 671 | .set('Accept', 'application/json') 672 | .expect('Content-Type', /json/) 673 | .expect(200) 674 | .end(function (err,res) { 675 | if (err) { 676 | throw err; 677 | } 678 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin_two.description, 'plugin_name': plugin_two.plugin_name, 'slug': plugin_two.slug }])); 679 | done(); 680 | }); 681 | }); 682 | it('does not equal', function (done) { 683 | request(instance) 684 | .post('/3/search') 685 | .send({ 'filters': JSON.stringify([{'field': 'slug', 'action': 'not-equals', 'value': 'abitofrealism' }]) }) 686 | .set('Accept', 'application/json') 687 | .expect('Content-Type', /json/) 688 | .expect(200) 689 | .end(function (err,res) { 690 | if (err) { 691 | throw err; 692 | } 693 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin.description, 'plugin_name': plugin.plugin_name, 'slug': plugin.slug }])); 694 | done(); 695 | }); 696 | }); 697 | it('less than', function (done) { 698 | request(instance) 699 | .post('/3/search') 700 | .send({ 'filters': JSON.stringify([{'field': 'curse_id', 'action': 'less', 'value': 40286 }]) }) 701 | .set('Accept', 'application/json') 702 | .expect('Content-Type', /json/) 703 | .expect(200) 704 | .end(function (err,res) { 705 | if (err) { 706 | throw err; 707 | } 708 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin_two.description, 'plugin_name': plugin_two.plugin_name, 'slug': plugin_two.slug }])); 709 | done(); 710 | }); 711 | }); 712 | it('less than or equal to', function (done) { 713 | request(instance) 714 | .post('/3/search') 715 | .send({ 'filters': JSON.stringify([{'field': 'curse_id', 'action': 'less-equal', 'value': 40285 }]) }) 716 | .set('Accept', 'application/json') 717 | .expect('Content-Type', /json/) 718 | .expect(200) 719 | .end(function (err,res) { 720 | if (err) { 721 | throw err; 722 | } 723 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin_two.description, 'plugin_name': plugin_two.plugin_name, 'slug': plugin_two.slug }])); 724 | done(); 725 | }); 726 | }); 727 | it('more than', function (done) { 728 | request(instance) 729 | .post('/3/search') 730 | .send({ 'filters': JSON.stringify([{'field': 'curse_id', 'action': 'more', 'value': 40285 }]) }) 731 | .set('Accept', 'application/json') 732 | .expect('Content-Type', /json/) 733 | .expect(200) 734 | .end(function (err,res) { 735 | if (err) { 736 | throw err; 737 | } 738 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin.description, 'plugin_name': plugin.plugin_name, 'slug': plugin.slug }])); 739 | done(); 740 | }); 741 | }); 742 | it('more than or equal to', function (done) { 743 | request(instance) 744 | .post('/3/search') 745 | .send({ 'filters': JSON.stringify([{'field': 'curse_id', 'action': 'more-equal', 'value': 40285 }]) }) 746 | .set('Accept', 'application/json') 747 | .expect('Content-Type', /json/) 748 | .expect(200) 749 | .end(function (err,res) { 750 | if (err) { 751 | throw err; 752 | } 753 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin_two.description, 'plugin_name': plugin_two.plugin_name, 'slug': plugin_two.slug }, { 'description': plugin.description, 'plugin_name': plugin.plugin_name, 'slug': plugin.slug }])); 754 | done(); 755 | }); 756 | }); 757 | it('like', function (done) { 758 | request(instance) 759 | .post('/3/search') 760 | .send({ 'filters': JSON.stringify([{'field': 'slug', 'action': 'like', 'value': 'abitof' }]) }) 761 | .set('Accept', 'application/json') 762 | .expect('Content-Type', /json/) 763 | .expect(200) 764 | .end(function (err,res) { 765 | if (err) { 766 | throw err; 767 | } 768 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin_two.description, 'plugin_name': plugin_two.plugin_name, 'slug': plugin_two.slug }])); 769 | done(); 770 | }); 771 | }); 772 | it('exists', function (done) { 773 | request(instance) 774 | .post('/3/search') 775 | .send({ 'filters': JSON.stringify([{'field': '_use_dbo', 'action': 'exists', 'value': true }]) }) 776 | .set('Accept', 'application/json') 777 | .expect('Content-Type', /json/) 778 | .expect(200) 779 | .end(function (err,res) { 780 | if (err) { 781 | throw err; 782 | } 783 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin_two.description, 'plugin_name': plugin_two.plugin_name, 'slug': plugin_two.slug }])); 784 | done(); 785 | }); 786 | }); 787 | it('doesn\'t exist', function (done) { 788 | request(instance) 789 | .post('/3/search') 790 | .send({ 'filters': JSON.stringify([{'field': '_use_dbo', 'action': 'exists', 'value': false }]) }) 791 | .set('Accept', 'application/json') 792 | .expect('Content-Type', /json/) 793 | .expect(200) 794 | .end(function (err,res) { 795 | if (err) { 796 | throw err; 797 | } 798 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin.description, 'plugin_name': plugin.plugin_name, 'slug': plugin.slug }])); 799 | done(); 800 | }); 801 | }); 802 | it('in', function (done) { 803 | request(instance) 804 | .post('/3/search') 805 | .send({ 'filters': JSON.stringify([{'field': 'slug', 'action': 'in', 'value': ['clearthechat'] }]) }) 806 | .set('Accept', 'application/json') 807 | .expect('Content-Type', /json/) 808 | .expect(200) 809 | .end(function (err,res) { 810 | if (err) { 811 | throw err; 812 | } 813 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin.description, 'plugin_name': plugin.plugin_name, 'slug': plugin.slug }])); 814 | done(); 815 | }); 816 | }); 817 | it('not in', function (done) { 818 | request(instance) 819 | .post('/3/search') 820 | .send({ 'filters': JSON.stringify([{'field': 'slug', 'action': 'not in', 'value': ['clearthechat'] }]) }) 821 | .set('Accept', 'application/json') 822 | .expect('Content-Type', /json/) 823 | .expect(200) 824 | .end(function (err,res) { 825 | if (err) { 826 | throw err; 827 | } 828 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin_two.description, 'plugin_name': plugin_two.plugin_name, 'slug': plugin_two.slug }])); 829 | done(); 830 | }); 831 | }); 832 | it('all', function (done) { 833 | request(instance) 834 | .post('/3/search') 835 | .send({ 'filters': JSON.stringify([{'field': 'categories', 'action': 'all', 'value': [ 'Fixes', 'Fun', 'General' ] }]) }) 836 | .set('Accept', 'application/json') 837 | .expect('Content-Type', /json/) 838 | .expect(200) 839 | .end(function (err,res) { 840 | if (err) { 841 | throw err; 842 | } 843 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin_two.description, 'plugin_name': plugin_two.plugin_name, 'slug': plugin_two.slug }])); 844 | done(); 845 | }); 846 | }); 847 | it('and', function (done) { 848 | request(instance) 849 | .post('/3/search') 850 | .send({ 'filters': JSON.stringify([{'field': '', 'action': 'and', 'value': [{ 'stage': 'Release'}, {'slug': 'abitofrealism' }] }]) }) 851 | .set('Accept', 'application/json') 852 | .expect('Content-Type', /json/) 853 | .expect(200) 854 | .end(function (err,res) { 855 | if (err) { 856 | throw err; 857 | } 858 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin_two.description, 'plugin_name': plugin_two.plugin_name, 'slug': plugin_two.slug }])); 859 | done(); 860 | }); 861 | }); 862 | it('or', function (done) { 863 | request(instance) 864 | .post('/3/search') 865 | .send({ 'filters': JSON.stringify([{'field': '', 'action': 'or', 'value': [{ 'slug': 'abitofrealism' }, { 'slug': 'clearthechat'}] }]) }) 866 | .set('Accept', 'application/json') 867 | .expect('Content-Type', /json/) 868 | .expect(200) 869 | .end(function (err,res) { 870 | if (err) { 871 | throw err; 872 | } 873 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin_two.description, 'plugin_name': plugin_two.plugin_name, 'slug': plugin_two.slug }, { 'description': plugin.description, 'plugin_name': plugin.plugin_name, 'slug': plugin.slug }])); 874 | done(); 875 | }); 876 | }); 877 | it('likeor', function (done) { 878 | request(instance) 879 | .post('/3/search') 880 | .send({ 'filters': JSON.stringify([{'field': '', 'action': 'likeor', 'value': [{ 'slug': 'freal' }, { 'main': 'exak.ClearTheChat.Cl'}] }]) }) 881 | .set('Accept', 'application/json') 882 | .expect('Content-Type', /json/) 883 | .expect(200) 884 | .end(function (err,res) { 885 | if (err) { 886 | throw err; 887 | } 888 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin_two.description, 'plugin_name': plugin_two.plugin_name, 'slug': plugin_two.slug }, { 'description': plugin.description, 'plugin_name': plugin.plugin_name, 'slug': plugin.slug }])); 889 | done(); 890 | }); 891 | }); 892 | it('nor', function (done) { 893 | request(instance) 894 | .post('/3/search') 895 | .send({ 'filters': JSON.stringify([{'field': '', 'action': 'nor', 'value': [{ 'slug': 'clearthechat' }, { '_use_dbo': { '$exists': false } }] }]) }) 896 | .set('Accept', 'application/json') 897 | .expect('Content-Type', /json/) 898 | .expect(200) 899 | .end(function (err,res) { 900 | if (err) { 901 | throw err; 902 | } 903 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin_two.description, 'plugin_name': plugin_two.plugin_name, 'slug': plugin_two.slug }])); 904 | done(); 905 | }); 906 | }); 907 | it('not', function (done) { 908 | request(instance) 909 | .post('/3/search') 910 | .send({ 'filters': JSON.stringify([{'field': 'curse_id', 'action': 'not', 'value': { '$lt' : 48244 } }]) }) 911 | .set('Accept', 'application/json') 912 | .expect('Content-Type', /json/) 913 | .expect(200) 914 | .end(function (err,res) { 915 | if (err) { 916 | throw err; 917 | } 918 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin.description, 'plugin_name': plugin.plugin_name, 'slug': plugin.slug }])); 919 | done(); 920 | }); 921 | }); 922 | it('multiple filters', function (done) { 923 | request(instance) 924 | .post('/3/search') 925 | .send({ 'filters': JSON.stringify([{'field': 'slug', 'action': 'equals', 'value': 'clearthechat' }, {'field': 'curse_id', 'action': 'equals', 'value': 48244 }]) }) 926 | .set('Accept', 'application/json') 927 | .expect('Content-Type', /json/) 928 | .expect(200) 929 | .end(function (err,res) { 930 | if (err) { 931 | throw err; 932 | } 933 | JSON.stringify(res.res.body).should.equal(JSON.stringify([{ 'description': plugin.description, 'plugin_name': plugin.plugin_name, 'slug': plugin.slug }])); 934 | done(); 935 | }); 936 | }); 937 | it('doesn\'t break when invalid filter is specified', function (done) { 938 | request(instance) 939 | .post('/3/search') 940 | .send([ { field: 'plugin_name', action: 'like', value: 'mc' }, { field: 'versions.game_version', action: 'like', value: '1.4.5' } ]) 941 | .set('Accept', 'application/json') 942 | .expect('Content-Type', /json/) 943 | .expect(200) 944 | .end(function (err,res) { 945 | if (err) { 946 | throw err; 947 | } 948 | 949 | done(); 950 | }); 951 | }); 952 | it('doesn\t break on search for some reason', function (done) { 953 | request(instance) 954 | .post('/3/search') 955 | .send({ filters: [ { field: 'plugin_name', action: 'like', value: 'mc' }, { field: 'versions.game_version', action: 'like', value: '1.4.5' } ] }) 956 | .set('Accept', 'application/json') 957 | .expect('Content-Type', /json/) 958 | .expect(200) 959 | .end(function (err,res) { 960 | if (err) { 961 | throw err; 962 | } 963 | 964 | done(); 965 | }); 966 | }); 967 | after(function (done) { 968 | plugins.remove({}, function callback(err, res) { done(); }); 969 | }); 970 | }); 971 | --------------------------------------------------------------------------------