Merge pull request #114 from coralproject/asset-settings

Asset settings
This commit is contained in:
David Jay
2016-11-29 11:37:08 -05:00
committed by GitHub
17 changed files with 634 additions and 421 deletions
+38
View File
@@ -12,9 +12,11 @@ process.env.DEBUG = process.env.TALK_DEBUG;
const program = require('commander');
const pkg = require('../package.json');
const parseDuration = require('parse-duration');
const Table = require('cli-table');
const Asset = require('../models/asset');
const mongoose = require('../mongoose');
const scraper = require('../services/scraper');
const util = require('../util');
// Register the shutdown criteria.
@@ -55,6 +57,37 @@ function listAssets() {
});
}
function refreshAssets(ageString) {
const now = new Date().getTime();
const ageMs = parseDuration(ageString);
const age = new Date(now - ageMs);
Asset.find({
$or: [
{
scraped: {
$lte: age
}
},
{
scraped: null
}
]
})
// Queue all the assets for scraping.
.then((assets) => Promise.all(assets.map(scraper.create)))
.then(() => {
console.log('Assets were queued to be scraped');
util.shutdown();
})
.catch((err) => {
console.error(err);
util.shutdown(1);
});
}
//==============================================================================
// Setting up the program command line arguments.
//==============================================================================
@@ -67,6 +100,11 @@ program
.description('list all the assets in the database')
.action(listAssets);
program
.command('refresh <age>')
.description('queues the assets that exceed the age requested')
.action(refreshAssets);
program.parse(process.argv);
// If there is no command listed, output help.
+69
View File
@@ -14,11 +14,15 @@ const program = require('commander');
const scraper = require('../services/scraper');
const util = require('../util');
const mongoose = require('../mongoose');
const kue = require('../kue');
util.onshutdown([
() => mongoose.disconnect()
]);
/**
* Starts the job processor.
*/
function processJobs() {
// Start the processor.
@@ -31,6 +35,65 @@ function processJobs() {
]);
}
/**
* Removes a single job.
* @param {Object} job the job to be removed
* @return {Promise}
*/
function removeJob(job) {
return new Promise((resolve, reject) => job.remove((err) => {
if (err) {
return reject(err);
}
return resolve(job);
}));
}
/**
* Removes the jobs passed in and returns a promise.
* @param {Array} jobs array of jobs
* @return {Promise}
*/
function removeJobs(jobs) {
return Promise.all(jobs.map(removeJob));
}
/**
* Get the top n jobs with a specific state.
* @param {String} [state='complete'] state to list jobs by
* @param {Number} limit limit of jobs to load
* @return {Promise}
*/
function rangeJobsByState(state = 'complete', limit) {
return new Promise((resolve, reject) => {
kue.Job.rangeByState(state, 0, limit, 'asc', (err, jobs) => {
if (err) {
return reject(err);
}
resolve(jobs);
});
});
}
/**
* Cleans up the jobs that are in the queue.
*/
function cleanupJobs(options) {
const n = 100;
Promise.all([
rangeJobsByState('complete', n),
options.stuck ? rangeJobsByState('failed', n) : false
])
.then((joblists) => joblists.filter((jobs) => jobs).map(removeJobs))
.then(() => {
util.shutdown();
console.log('Removed old jobs');
});
}
//==============================================================================
// Setting up the program command line arguments.
//==============================================================================
@@ -40,6 +103,12 @@ program
.description('starts job processing')
.action(processJobs);
program
.command('cleanup')
.option('-s, --stuck', 'cleans up jobs that have been stuck', false)
.description('cleans up inactive jobs')
.action(cleanupJobs);
program.parse(process.argv);
// If there is no command listed, output help.
+46 -5
View File
@@ -1,7 +1,8 @@
const mongoose = require('../mongoose');
const uuid = require('uuid');
const Schema = mongoose.Schema;
const uuid = require('uuid');
const AssetSchema = new Schema({
id: {
type: String,
@@ -22,6 +23,10 @@ const AssetSchema = new Schema({
type: Date,
default: null
},
settings: {
type: Schema.Types.Mixed,
default: null
},
title: String,
description: String,
image: String,
@@ -38,21 +43,32 @@ const AssetSchema = new Schema({
}
});
AssetSchema.index({
title: 'text',
url: 'text',
description: 'text',
section: 'text',
subsection: 'text',
author: 'text'
}, {
background: true
});
/**
* Search for assets. Currently only returns all.
*/
*/
AssetSchema.statics.search = (query) => Asset.find(query);
/**
* Finds an asset by its id.
* @param {String} id identifier of the asset (uuid).
*/
*/
AssetSchema.statics.findById = (id) => Asset.findOne({id});
/**
* Finds a asset by its url.
* @param {String} url identifier of the asset (uuid).
*/
*/
AssetSchema.statics.findByUrl = (url) => Asset.findOne({url});
/**
@@ -65,7 +81,8 @@ AssetSchema.statics.findByUrl = (url) => Asset.findOne({url});
* is not possible with the mongoose driver.
*
* @param {String} url identifier of the asset (uuid).
*/
* @return {Promise}
*/
AssetSchema.statics.findOrCreateByUrl = (url) => Asset.findOneAndUpdate({url}, {url}, {
// Ensure that if it's new, we return the new object created.
@@ -78,6 +95,30 @@ AssetSchema.statics.findOrCreateByUrl = (url) => Asset.findOneAndUpdate({url}, {
setDefaultsOnInsert: true
});
/**
* Updates the settings for the asset.
* @param {[type]} id [description]
* @param {[type]} settings [description]
* @return {[type]} [description]
*/
AssetSchema.statics.overrideSettings = (id, settings) => Asset.update({id}, {
$set: {
settings
}
});
/**
* Finds assets matching keywords on the model. If `value` is an empty string,
* then it will not even perform a text search query.
* @param {String} value string to search by.
* @return {Promise}
*/
AssetSchema.statics.search = (value) => value.length === 0 ? Asset.find({}) : Asset.find({
$text: {
$search: value
}
});
const Asset = mongoose.model('Asset', AssetSchema);
module.exports = Asset;
+1
View File
@@ -9,6 +9,7 @@ const SALT_ROUNDS = 10;
// USER_ROLES is the array of roles that is permissible as a user role.
const USER_ROLES = [
'',
'admin',
'moderator'
];
+14 -3
View File
@@ -16,8 +16,13 @@
"config": {
"pre-git": {
"commit-msg": [],
"pre-commit": ["npm run lint", "npm test"],
"pre-push": ["npm test"],
"pre-commit": [
"npm run lint",
"npm test"
],
"pre-push": [
"npm test"
],
"post-commit": [],
"post-merge": []
}
@@ -26,7 +31,12 @@
"type": "git",
"url": "git+https://github.com/coralproject/talk.git"
},
"keywords": ["talk", "coral", "coralproject", "ask"],
"keywords": [
"talk",
"coral",
"coralproject",
"ask"
],
"author": "",
"license": "Apache-2.0",
"bugs": {
@@ -52,6 +62,7 @@
"morgan": "^1.7.0",
"natural": "^0.4.0",
"nodemailer": "^2.6.4",
"parse-duration": "^0.1.1",
"passport": "^0.3.2",
"passport-facebook": "^2.1.1",
"passport-local": "^1.0.0",
+21 -3
View File
@@ -11,17 +11,20 @@ router.get('/', (req, res, next) => {
limit = 20,
skip = 0,
sort = 'asc',
field = 'created_at'
field = 'created_at',
search = ''
} = req.query;
// Find all the assets.
Promise.all([
Asset
.find({})
.search(search)
.sort({[field]: (sort === 'asc') ? 1 : -1})
.skip(skip)
.limit(limit),
Asset.count()
Asset
.search(search)
.count()
])
.then(([result, count]) => {
@@ -78,4 +81,19 @@ router.post('/:asset_id/scrape', (req, res, next) => {
});
});
router.put('/:asset_id/settings', (req, res, next) => {
// Override the settings for the asset.
Asset
.overrideSettings(req.params.asset_id, req.body)
.then(() => {
res.status(204).end();
})
.catch((err) => {
next(err);
});
});
module.exports = router;
+26 -5
View File
@@ -30,16 +30,36 @@ router.get('/', (req, res, next) => {
Setting.getModerationSetting()
])
.then(([asset, settings]) => {
// Get the sitewide moderation setting and return the appropriate comments
// Merge the asset specific settings with the returned settings object in
// the event that the asset that was returned also had settings.
if (asset.settings) {
settings = Object.assign(settings, asset.settings);
}
// Fetch the appropriate comments stream.
let comments;
if (settings.moderation === 'pre') {
if (settings.moderation === 'post') {
comments = Comment.findAcceptedByAssetId(asset.id);
} else {
comments = Comment.findAcceptedAndNewByAssetId(asset.id);
}
return Promise.all([comments, asset, settings]);
return Promise.all([
// This is the promised component... Fetch the comments based on the
// moderation settings.
comments,
// Send back the reference to the asset.
asset,
// Send back the settings to the stream.
settings
]);
})
// Get all the users and actions for those comments.
.then(([comments, asset, settings]) => {
// Get the user id's from the author id's as a unique array that gets
@@ -73,14 +93,15 @@ router.get('/', (req, res, next) => {
// The users who wrote those comments
users,
// The actions on the above items
// And all actions about the asset, comments, and users.
actions,
// And the relevant settings
// Pass back the settings that we loaded.
settings
]);
})
.then(([asset, comments, users, actions, settings]) => {
// Send back the payload containing all this data.
res.json({
assets: [asset],
comments,
+22
View File
@@ -335,6 +335,28 @@ paths:
schema:
$ref: '#/definitions/Error'
/asset/{asset_id}/settings:
put:
parameters:
- name: asset_id
in: path
required: true
type: string
format: uuid
- name: body
in: body
required: true
schema:
$ref: '#/definitions/Settings'
responses:
204:
description: The asset settings were updated.
404:
description: The asset was not found.
500:
description: An error occured.
schema:
$ref: '#/definitions/Error'
/stream:
get:
+27 -1
View File
@@ -1,5 +1,10 @@
const Asset = require('../../models/asset');
const expect = require('chai').expect;
const chai = require('chai');
const expect = chai.expect;
// Use the chai should.
chai.should();
describe('Asset: model', () => {
@@ -53,6 +58,27 @@ describe('Asset: model', () => {
});
});
describe('#overrideSettings', () => {
it('should update the settings', () => {
return Asset
.findOrCreateByUrl('https://override.test.com/asset')
.then((asset) => {
expect(asset).to.have.property('settings');
expect(asset.settings).to.be.null;
return Asset.overrideSettings(asset.id, {moderation: 'pre'});
})
.then(() => {
return Asset.findOrCreateByUrl('https://override.test.com/asset');
})
.then((asset) => {
expect(asset).to.have.property('settings');
expect(asset.settings).is.an('object');
expect(asset.settings).to.have.property('moderation', 'pre');
});
});
});
describe('#findOrCreateByUrl', ()=> {
it('should find an asset by a url', () => {
return Asset.findOrCreateByUrl('http://test.com')
+2
View File
@@ -14,6 +14,7 @@ describe('Setting: model', () => {
expect(settings).to.have.property('moderation').and.to.equal('pre');
});
});
it('should have two infoBox fields defined', () => {
return Setting.getSettings().then(settings => {
expect(settings).to.have.property('infoBoxEnable').and.to.equal(false);
@@ -26,6 +27,7 @@ describe('Setting: model', () => {
it('should update the settings with a passed object', () => {
const mockSettings = {moderation: 'post', infoBoxEnable: true, infoBoxContent: 'yeah'};
return Setting.updateSettings(mockSettings).then(updatedSettings => {
expect(updatedSettings).to.be.an('object');
expect(updatedSettings).to.have.property('moderation').and.to.equal('post');
expect(updatedSettings).to.have.property('infoBoxEnable', true);
expect(updatedSettings).to.have.property('infoBoxContent', 'yeah');
+2 -2
View File
@@ -2,8 +2,8 @@ const mongoose = require('../mongoose');
beforeEach(function (done) {
function clearDB() {
for (let i in mongoose.connection.collections) {
mongoose.connection.collections[i].remove(function() {});
for (let collection in mongoose.connection.collections) {
mongoose.connection.collections[collection].remove(function() {});
}
return done();
}
+79 -4
View File
@@ -1,9 +1,84 @@
describe('/assets', () => {
const passport = require('../../../passport');
describe('GET', () => {
const app = require('../../../../app');
const chai = require('chai');
const expect = chai.expect;
it('should return assets that we search for');
it('should not return assets that we do not search for');
// Setup chai.
chai.should();
chai.use(require('chai-http'));
const Asset = require('../../../../models/asset');
describe('/api/v1/assets', () => {
beforeEach(() => {
return Asset.create([
{
url: 'https://coralproject.net/news/asset1',
title: 'Asset 1',
description: 'term1'
},
{
url: 'https://coralproject.net/news/asset2',
title: 'Asset 2',
description: 'term2'
}
]);
});
describe('#get', () => {
it('should return all assets without a search query', () => {
return chai.request(app)
.get('/api/v1/asset')
.set(passport.inject({roles: ['admin']}))
.then((res) => {
const body = res.body;
expect(body).to.have.property('count', 2);
expect(body).to.have.property('result');
const assets = body.result;
expect(assets).to.have.length(2);
});
});
it('should return assets that we search for', () => {
return chai.request(app)
.get('/api/v1/asset?search=term2')
.set(passport.inject({roles: ['admin']}))
.then((res) => {
const body = res.body;
expect(body).to.have.property('count', 1);
expect(body).to.have.property('result');
const assets = body.result;
expect(assets).to.have.length(1);
const asset = assets[0];
expect(asset).to.have.property('url', 'https://coralproject.net/news/asset2');
expect(asset).to.have.property('title', 'Asset 2');
});
});
it('should not return assets that we do not search for', () => {
return chai.request(app)
.get('/api/v1/asset?search=term3')
.set(passport.inject({roles: ['admin']}))
.then((res) => {
const body = res.body;
expect(body).to.have.property('count', 0);
expect(body).to.have.property('result');
expect(body.result).to.be.empty;
});
});
});
+36 -21
View File
@@ -6,32 +6,47 @@ chai.use(require('chai-http'));
const User = require('../../../../models/user');
describe('POST /auth/local', () => {
describe('/api/v1/auth', () => {
describe('#get', () => {
it('should return nothing when no user is logged in', () => {
return chai.request(app)
.get('/api/v1/auth')
.then((res) => {
expect(res.status).to.be.equal(204);
expect(res.body).to.be.empty;
});
});
});
});
describe('/api/v1/auth/local', () => {
beforeEach(() => {
return User.createLocalUser('maria@gmail.com', 'password!', 'Maria');
});
it('should send back the user on a successful login', () => {
return chai.request(app)
.post('/api/v1/auth/local')
.send({email: 'maria@gmail.com', password: 'password!'})
.catch((res) => {
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body).to.have.property('user');
expect(res.body.user).to.have.property('displayName', 'Maria');
});
});
describe('#post', () => {
it('should send back the user on a successful login', () => {
return chai.request(app)
.post('/api/v1/auth/local')
.send({email: 'maria@gmail.com', password: 'password!'})
.catch((res) => {
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body).to.have.property('user');
expect(res.body.user).to.have.property('displayName', 'Maria');
});
});
it('should not send back the user on a unsuccessful login', () => {
return chai.request(app)
.post('/api/v1/auth/local')
.send({email: 'maria@gmail.com', password: 'password!3'})
.catch((err) => {
expect(err).to.not.be.null;
expect(err.response).to.have.status(401);
expect(err.response.body).to.have.property('message', 'not authorized');
});
it('should not send back the user on a unsuccessful login', () => {
return chai.request(app)
.post('/api/v1/auth/local')
.send({email: 'maria@gmail.com', password: 'password!3'})
.catch((err) => {
expect(err).to.not.be.null;
expect(err.response).to.have.status(401);
expect(err.response.body).to.have.property('message', 'not authorized');
});
});
});
});
+156 -303
View File
@@ -16,11 +16,7 @@ const User = require('../../../../models/user');
const Setting = require('../../../../models/setting');
const settings = {id: '1', moderation: 'pre'};
beforeEach(() => {
return Setting.create(settings);
});
describe('Get /comments', () => {
describe('/api/v1/comments', () => {
const comments = [{
id: 'abc',
body: 'comment 10',
@@ -32,61 +28,11 @@ describe('Get /comments', () => {
asset_id: 'asset',
author_id: '456'
}, {
id: 'hij',
body: 'comment 30',
asset_id: '456'
}];
const users = [{
displayName: 'Ana',
email: 'ana@gmail.com',
password: '123'
}, {
displayName: 'Maria',
email: 'maria@gmail.com',
password: '123'
}];
const actions = [{
action_type: 'flag',
item_id: 'abc'
}, {
action_type: 'like',
item_id: 'hij'
}];
beforeEach(() => {
return Promise.all([
Comment.create(comments),
User.createLocalUsers(users),
Action.create(actions)
]);
});
it('should return all the comments', () => {
return chai.request(app)
.get('/api/v1/comments')
.set(passport.inject({roles: ['admin']}))
.then((res) => {
expect(res).to.have.status(200);
});
});
});
describe('Get comments by status and action', () => {
const comments = [{
id: 'abc',
body: 'comment 10',
asset_id: 'asset',
author_id: '123',
status: 'rejected'
}, {
id: 'def',
id: 'def-rejected',
body: 'comment 20',
asset_id: 'asset',
author_id: '456'
author_id: '456',
status: 'rejected'
}, {
id: 'hij',
body: 'comment 30',
@@ -117,109 +63,100 @@ describe('Get comments by status and action', () => {
beforeEach(() => {
return Promise.all([
Comment.create(comments),
User.createLocalUsers(users),
Action.create(actions)
]);
});
it('should return all the rejected comments', () => {
return chai.request(app)
.get('/api/v1/comments?status=rejected')
.set(passport.inject({roles: ['admin']}))
.then((res) => {
expect(res).to.have.status(200);
expect(res.body[0]).to.have.property('id', 'abc');
});
});
it('should return all the approved comments', () => {
return chai.request(app)
.get('/api/v1/comments?status=accepted')
.set(passport.inject({roles: ['admin']}))
.then((res) => {
expect(res).to.have.status(200);
expect(res.body[0]).to.have.property('id', 'hij');
});
});
it('should return all the new comments', () => {
return chai.request(app)
.get('/api/v1/comments?status=new')
.set(passport.inject({roles: ['admin']}))
.then((res) => {
expect(res).to.have.status(200);
expect(res.body[0]).to.have.property('id', 'def');
});
});
it('should return all the flagged comments', () => {
return chai.request(app)
.get('/api/v1/comments?action_type=flag')
.set(passport.inject({roles: ['admin']}))
.then((res) => {
expect(res).to.have.status(200);
expect(res.body.length).to.equal(1);
expect(res.body[0]).to.have.property('id', 'abc');
});
});
});
describe('Post /comments', () => {
const users = [{
displayName: 'Ana',
email: 'ana@gmail.com',
password: '123'
}, {
displayName: 'Maria',
email: 'maria@gmail.com',
password: '123'
}];
const actions = [{
action_type: 'flag',
item_id: 'abc'
}, {
action_type: 'like',
item_id: 'hij'
}];
beforeEach(() => {
return Promise.all([
User.createLocalUsers(users),
Action.create(actions),
wordlist.insert([
'bad words'
])
]),
Setting.create(settings)
]);
});
it('should create a comment', () => {
return chai.request(app)
.post('/api/v1/comments')
.set(passport.inject({roles: []}))
.send({'body': 'Something body.', 'author_id': '123', 'asset_id': '1', 'parent_id': ''})
.then((res) => {
expect(res).to.have.status(201);
expect(res.body).to.have.property('id');
});
describe('#get', () => {
it('should return all the comments', () => {
return chai.request(app)
.get('/api/v1/comments')
.set(passport.inject({roles: ['admin']}))
.then((res) => {
expect(res).to.have.status(200);
});
});
it('should return all the rejected comments', () => {
return chai.request(app)
.get('/api/v1/comments?status=rejected')
.set(passport.inject({roles: ['admin']}))
.then((res) => {
expect(res).to.have.status(200);
expect(res.body[0]).to.have.property('id', 'def-rejected');
});
});
it('should return all the approved comments', () => {
return chai.request(app)
.get('/api/v1/comments?status=accepted')
.set(passport.inject({roles: ['admin']}))
.then((res) => {
expect(res).to.have.status(200);
expect(res.body).to.have.length(1);
expect(res.body[0]).to.have.property('id', 'hij');
});
});
it('should return all the new comments', () => {
return chai.request(app)
.get('/api/v1/comments?status=new')
.set(passport.inject({roles: ['admin']}))
.then((res) => {
expect(res).to.have.status(200);
expect(res.body).to.have.length(2);
});
});
it('should return all the flagged comments', () => {
return chai.request(app)
.get('/api/v1/comments?action_type=flag')
.set(passport.inject({roles: ['admin']}))
.then((res) => {
expect(res).to.have.status(200);
expect(res.body).to.have.length(1);
expect(res.body[0]).to.have.property('id', 'abc');
});
});
});
it('should create a comment with a rejected status if it contains a bad word', () => {
return chai.request(app)
.post('/api/v1/comments')
.set(passport.inject({roles: []}))
.send({'body': 'bad words are the baddest', 'author_id': '123', 'asset_id': '1', 'parent_id': ''})
.then((res) => {
expect(res).to.have.status(201);
expect(res.body).to.have.property('id');
expect(res.body).to.have.property('status', 'rejected');
});
describe('#post', () => {
it('should create a comment', () => {
return chai.request(app)
.post('/api/v1/comments')
.set(passport.inject({roles: []}))
.send({'body': 'Something body.', 'author_id': '123', 'asset_id': '1', 'parent_id': ''})
.then((res) => {
expect(res).to.have.status(201);
expect(res.body).to.have.property('id');
});
});
it('should create a comment with a rejected status if it contains a bad word', () => {
return chai.request(app)
.post('/api/v1/comments')
.set(passport.inject({roles: []}))
.send({'body': 'bad words are the baddest', 'author_id': '123', 'asset_id': '1', 'parent_id': ''})
.then((res) => {
expect(res).to.have.status(201);
expect(res.body).to.have.property('id');
expect(res.body).to.have.property('status', 'rejected');
});
});
});
});
describe('Get /:comment_id', () => {
describe('/api/v1/comments/:comment_id', () => {
const comments = [{
id: 'abc',
body: 'comment 10',
@@ -264,79 +201,65 @@ describe('Get /:comment_id', () => {
]);
});
it('should return the right comment for the comment_id', () => {
return chai.request(app)
.get('/api/v1/comments/abc')
.set(passport.inject({roles: ['admin']}))
.then((res) => {
expect(res).to.have.status(200);
expect(res).to.have.property('body');
expect(res.body).to.have.property('body', 'comment 10');
describe('#get', () => {
});
it('should return the right comment for the comment_id', () => {
return chai.request(app)
.get('/api/v1/comments/abc')
.set(passport.inject({roles: ['admin']}))
.then((res) => {
expect(res).to.have.status(200);
expect(res).to.have.property('body');
expect(res.body).to.have.property('body', 'comment 10');
});
});
});
describe('#delete', () => {
it('it should remove comment', () => {
return chai.request(app)
.delete('/api/v1/comments/abc')
.set(passport.inject({roles: ['admin']}))
.then((res) => {
expect(res).to.have.status(204);
return Comment.findById('abc');
})
.then((comment) => {
expect(comment).to.be.null;
});
});
});
describe('#put', () => {
it('it should update status', function() {
return chai.request(app)
.put('/api/v1/comments/abc/status')
.set(passport.inject({roles: ['admin']}))
.send({status: 'accepted'})
.then((res) => {
expect(res).to.have.status(204);
expect(res.body).to.be.empty;
});
});
it('it should not allow a non-admin to update status', () => {
return chai.request(app)
.put('/api/v1/comments/abc/status')
.set(passport.inject({roles: []}))
.send({status: 'accepted'})
.then((res) => {
expect(res).to.be.empty;
})
.catch((err) => {
expect(err).to.have.property('status', 401);
});
});
});
});
describe('Remove /:comment_id', () => {
const comments = [{
id: 'abc',
body: 'comment 10',
asset_id: 'asset',
author_id: '123'
}, {
id: 'def',
body: 'comment 20',
asset_id: 'asset',
author_id: '456'
}, {
id: 'hij',
body: 'comment 30',
asset_id: '456'
}];
const users = [{
displayName: 'Ana',
email: 'ana@gmail.com',
password: '123'
}, {
displayName: 'Maria',
email: 'maria@gmail.com',
password: '123'
}];
const actions = [{
action_type: 'flag',
item_id: 'abc'
}, {
action_type: 'like',
item_id: 'hij'
}];
beforeEach(() => {
return Promise.all([
Comment.create(comments),
User.createLocalUsers(users),
Action.create(actions)
]);
});
it('it should remove comment', () => {
return chai.request(app)
.delete('/api/v1/comments/abc')
.set(passport.inject({roles: ['admin']}))
.then((res) => {
expect(res).to.have.status(204);
return Comment.findById('abc');
})
.then((comment) => {
expect(comment).to.be.null;
});
});
});
describe('Put /:comment_id/status', () => {
describe('/api/v1/comments/:comment_id/actions', () => {
const comments = [{
id: 'abc',
@@ -383,90 +306,20 @@ describe('Put /:comment_id/status', () => {
]);
});
it('it should update status', function() {
return chai.request(app)
.put('/api/v1/comments/abc/status')
.set(passport.inject({roles: ['admin']}))
.send({status: 'accepted'})
.then((res) => {
expect(res).to.have.status(204);
expect(res.body).to.be.empty;
});
});
it('it should not allow a non-admin to update status', () => {
return chai.request(app)
.put('/api/v1/comments/abc/status')
.set(passport.inject({roles: []}))
.send({status: 'accepted'})
.then((res) => {
expect(res).to.be.empty;
})
.catch((err) => {
expect(err).to.have.property('status', 401);
});
});
});
describe('Post /:comment_id/actions', () => {
const comments = [{
id: 'abc',
body: 'comment 10',
asset_id: 'asset',
author_id: '123',
status: ''
}, {
id: 'def',
body: 'comment 20',
asset_id: 'asset',
author_id: '456',
status: 'rejected'
}, {
id: 'hij',
body: 'comment 30',
asset_id: '456',
status: 'accepted'
}];
const users = [{
displayName: 'Ana',
email: 'ana@gmail.com',
password: '123'
}, {
displayName: 'Maria',
email: 'maria@gmail.com',
password: '123'
}];
const actions = [{
action_type: 'flag',
item_id: 'abc'
}, {
action_type: 'like',
item_id: 'hij'
}];
beforeEach(() => {
return Promise.all([
Comment.create(comments),
User.createLocalUsers(users),
Action.create(actions)
]);
});
it('it should update actions', () => {
return chai.request(app)
.post('/api/v1/comments/abc/actions')
.set(passport.inject({id: '456', roles: ['admin']}))
.send({'user_id': '456', 'action_type': 'flag'})
.then((res) => {
expect(res).to.have.status(201);
expect(res).to.have.body;
expect(res.body).to.have.property('item_type', 'comment');
expect(res.body).to.have.property('action_type', 'flag');
expect(res.body).to.have.property('item_id', 'abc');
expect(res.body).to.have.property('user_id', '456');
});
describe('#post', () => {
it('it should update actions', () => {
return chai.request(app)
.post('/api/v1/comments/abc/actions')
.set(passport.inject({id: '456', roles: ['admin']}))
.send({'user_id': '456', 'action_type': 'flag'})
.then((res) => {
expect(res).to.have.status(201);
expect(res).to.have.body;
expect(res.body).to.have.property('item_type', 'comment');
expect(res.body).to.have.property('action_type', 'flag');
expect(res.body).to.have.property('item_id', 'abc');
expect(res.body).to.have.property('user_id', '456');
});
});
});
});
+15 -16
View File
@@ -15,11 +15,7 @@ const User = require('../../../../models/user');
const Setting = require('../../../../models/setting');
const settings = {id: '1', moderation: 'pre'};
beforeEach(() => {
return Setting.create(settings);
});
describe('Get moderation queues rejected, pending, flags', () => {
describe('/api/v1/queue', () => {
const comments = [{
id: 'abc',
body: 'comment 10',
@@ -62,19 +58,22 @@ describe('Get moderation queues rejected, pending, flags', () => {
return Promise.all([
Comment.create(comments),
User.createLocalUsers(users),
Action.create(actions)
Action.create(actions),
Setting.create(settings)
]);
});
it('should return all the pending comments', function(done){
chai.request(app)
.get('/api/v1/queue/comments/pending')
.set(passport.inject({roles: ['admin']}))
.end(function(err, res){
expect(err).to.be.null;
expect(res).to.have.status(200);
expect(res.body[0]).to.have.property('id', 'def');
done();
});
describe('#get', () => {
it('should return all the pending comments', function(done){
chai.request(app)
.get('/api/v1/queue/comments/pending')
.set(passport.inject({roles: ['admin']}))
.end(function(err, res){
expect(err).to.be.null;
expect(res).to.have.status(200);
expect(res.body[0]).to.have.property('id', 'def');
done();
});
});
});
});
+32 -39
View File
@@ -10,49 +10,42 @@ chai.use(require('chai-http'));
const Setting = require('../../../../models/setting');
const defaults = {id: '1', moderation: 'pre'};
describe('GET /settings', () => {
describe('/api/v1/settings', () => {
beforeEach(() => {
return Setting.update({id: '1'}, {$setOnInsert: defaults}, {upsert: true});
beforeEach(() => Setting.create(defaults));
describe('#get', () => {
it('should return a settings object', () => {
return chai.request(app)
.get('/api/v1/settings')
.set(passport.inject({
roles: ['admin']
}))
.then((res) => {
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body).to.have.property('moderation', 'pre');
});
});
});
it('should return a settings object', () => {
return chai.request(app)
.get('/api/v1/settings')
.set(passport.inject({
roles: ['admin']
}))
.then((res) => {
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body).to.have.property('moderation', 'pre');
});
});
});
describe('#put', () => {
// update the settings.
describe('update settings', () => {
it('should respond ok to a PUT', () => {
return Setting
.update({id: '1'}, {$setOnInsert: defaults}, {upsert: true})
.then(() => {
return chai.request(app)
.put('/api/v1/settings')
.set(passport.inject({
roles: ['admin']
}))
.send({moderation: 'post'});
})
.then(res => {
expect(res).to.have.status(204);
it('should update the settings', () => {
return chai.request(app)
.put('/api/v1/settings')
.set(passport.inject({roles: ['admin']}))
.send({moderation: 'post'})
.then((res) => {
expect(res).to.have.status(204);
return Setting.getSettings();
})
.then(settings => {
// confirm updated settings in db
expect(settings).to.have.property('moderation');
expect(settings.moderation).to.equal('post');
});
return Setting.getSettings();
})
.then((settings) => {
expect(settings).to.have.property('moderation', 'post');
});
});
});
});
+48 -19
View File
@@ -13,9 +13,12 @@ const Asset = require('../../../../models/asset');
const Setting = require('../../../../models/setting');
describe('api/stream: routes', () => {
describe('/api/v1/stream', () => {
const settings = {id: '1', moderation: 'pre'};
const settings = {
id: '1',
moderation: 'pre'
};
const comments = [{
id: 'abc',
@@ -35,7 +38,7 @@ describe('api/stream: routes', () => {
asset_id: 'asset',
author_id: '456',
parent_id: '',
status: ''
status: 'accepted'
}, {
id: 'hij',
body: 'comment 40',
@@ -65,15 +68,26 @@ describe('api/stream: routes', () => {
return Promise.all([
User.createLocalUsers(users),
Asset.findOrCreateByUrl('http://test.com')
Asset.findOrCreateByUrl('http://test.com'),
Asset
.findOrCreateByUrl('http://coralproject.net/asset2')
.then((asset) => {
return Asset
.overrideSettings(asset.id, {moderation: 'post'})
.then(() => asset);
})
])
.then(([users, asset]) => {
.then(([users, asset1, asset2]) => {
comments[0].author_id = users[0].id;
comments[1].author_id = users[1].id;
comments[2].author_id = users[0].id;
comments[3].author_id = users[1].id;
comments[0].asset_id = asset.id;
comments[1].asset_id = asset.id;
comments[0].asset_id = asset1.id;
comments[1].asset_id = asset1.id;
comments[2].asset_id = asset2.id;
comments[3].asset_id = asset2.id;
return Promise.all([
Comment.create(comments),
@@ -83,17 +97,32 @@ describe('api/stream: routes', () => {
});
});
it('should return a stream with comments, users and actions for an existing asset', () => {
return chai.request(app)
.get('/api/v1/stream')
.query({'asset_url': 'http://test.com'})
.then(res => {
expect(res).to.have.status(200);
expect(res.body.assets[0]).to.have.property('url');
expect(res.body.comments[0]).to.have.property('body');
expect(res.body.users[0]).to.have.property('displayName');
expect(res.body.actions[0]).to.have.property('action_type');
expect(res.body.settings).to.have.property('moderation');
});
describe('#get', () => {
it('should return a stream with comments, users and actions for an existing asset', () => {
return chai.request(app)
.get('/api/v1/stream')
.query({'asset_url': 'http://test.com'})
.then(res => {
expect(res).to.have.status(200);
expect(res.body.assets.length).to.equal(1);
expect(res.body.comments.length).to.equal(2);
expect(res.body.users.length).to.equal(2);
expect(res.body.actions.length).to.equal(1);
expect(res.body.settings).to.have.property('moderation', 'pre');
});
});
it('should merge the settings when the asset contains settings to override it with', () => {
return chai.request(app)
.get('/api/v1/stream')
.query({'asset_url': 'http://coralproject.net/asset2'})
.then((res) => {
expect(res).to.have.status(200);
expect(res.body.assets.length).to.equal(1);
expect(res.body.comments.length).to.equal(1);
expect(res.body.users.length).to.equal(1);
expect(res.body.settings).to.have.property('moderation', 'post');
});
});
});
});