diff --git a/.gitignore b/.gitignore index d52442b57..acd1ba775 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ dist .DS_Store *.iml .env +gaba.cfg diff --git a/models/action.js b/models/action.js index 581108a71..cdbf92e8f 100644 --- a/models/action.js +++ b/models/action.js @@ -1,5 +1,3 @@ -'use strict'; - const mongoose = require('../mongoose'); const uuid = require('uuid'); const Schema = mongoose.Schema; @@ -15,7 +13,6 @@ const ActionSchema = new Schema({ item_id: String, user_id: String },{ - _id: false, timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' @@ -30,6 +27,16 @@ ActionSchema.statics.findById = function(id) { return Action.findOne({id}); }; +/** + * Finds users in an array of ids. + * @param {String} ids array of user identifiers (uuid) +*/ +ActionSchema.statics.findByItemIdArray = function(item_ids) { + return Action.find({ + 'item_id': {$in: item_ids} + }); +}; + const Action = mongoose.model('Action', ActionSchema); module.exports = Action; diff --git a/models/comment.js b/models/comment.js index 107708a3b..468478810 100644 --- a/models/comment.js +++ b/models/comment.js @@ -1,5 +1,3 @@ -'use strict'; - const mongoose = require('../mongoose'); const uuid = require('uuid'); const Schema = mongoose.Schema; @@ -13,7 +11,7 @@ const CommentSchema = new Schema({ body: { type: String, required: [true, 'The body is required.'], - minlength: 50 + minlength: 2 }, asset_id: String, author_id: String, @@ -24,7 +22,6 @@ const CommentSchema = new Schema({ }, parent_id: String },{ - _id: false, timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' @@ -33,12 +30,21 @@ const CommentSchema = new Schema({ /** * Finds a comment by the id. - * @param {String} id identifier of the comment (uuid) + * @param {String} asset_id identifier of comment (uuid) */ CommentSchema.statics.findById = function(id) { return Comment.findOne({id}); }; +/** + * Finds a comment by the asset_id. + * @param {String} asset_id identifier of the asset which owns this comment (uuid) +*/ +CommentSchema.statics.findByAssetId = function(asset_id) { + return Comment.find({asset_id}); +}; + + const Comment = mongoose.model('Comment', CommentSchema); module.exports = Comment; diff --git a/models/user.js b/models/user.js index d6b174075..76635abfb 100644 --- a/models/user.js +++ b/models/user.js @@ -1,4 +1,3 @@ -'use strict'; const mongoose = require('../mongoose'); const uuid = require('uuid'); @@ -13,7 +12,6 @@ const UserProfileSchema = new Schema({ display_name: String, auth_user_id: String },{ - _id: false, timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' @@ -28,6 +26,16 @@ UserProfileSchema.statics.findById = function(id) { return UserProfile.findOne({id}); }; +/** + * Finds users in an array of idd. + * @param {String} idd array of user identifiers (uuid) +*/ +UserProfileSchema.statics.findByIdArray = function(ids) { + return UserProfile.find({ + 'id': {$in: ids} + }); +}; + // TO DO: methods // modifications to user as statics // find by auth user id diff --git a/package.json b/package.json index d0413ed89..17a6bda02 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "build": "webpack --config ./client/coral-embed-stream/webpack.config.js", "lint": "eslint .", "pretest": "npm install", - "test": "mocha tests", + "test": "mocha tests --recursive", + "test-watch": "mocha tests --recursive -w", "embed-start": "node client/coral-embed-stream/dev-server.js" }, "config": { @@ -45,6 +46,7 @@ "debug": "^2.2.0", "express": "^4.14.0", "mongoose": "^4.6.5", + "uuid": "^2.0.3", "morgan": "^1.7.0" }, "devDependencies": { @@ -60,6 +62,7 @@ "babel-preset-es2015-minimal": "^2.1.0", "babel-preset-stage-0": "^6.16.0", "chai": "^3.5.0", + "chai-http": "^1.0.0", "copy-webpack-plugin": "^3.0.1", "eslint": "^3.9.1", "exports-loader": "^0.6.3", diff --git a/routes/api/comments/index.js b/routes/api/comments/index.js index dac53b755..df9bbc456 100644 --- a/routes/api/comments/index.js +++ b/routes/api/comments/index.js @@ -1,17 +1,39 @@ const express = require('express'); +const Comment = require('../../../models/comment'); const router = express.Router(); +//============================================================================== +// Routes +//============================================================================== + + router.get('/', (req, res) => { res.send('Read all of the comments ever'); }); -router.get('/:comment_id', (req, res) => { - res.send('Read a comment'); +router.get('/:comment_id', (req, res, next) => { + Comment.findById(req.params.comment_id).then((comment) => { + res.status(200).json(comment); + }).catch(error => { + next(error); + }); }); -router.post('/', (req, res) => { - res.send('Write a comment'); +router.post('/', (req, res, next) => { + let comment = new Comment({ + body: req.query.body, + author_id: req.query.author_id, + asset_id: req.query.asset_id, + parent_id: req.query.parent_id, + status: req.query.status + }); + comment.save().then(({id}) => { + res.status(201).send(id); + }).catch(error => { + next(error); + }); + }); router.put('/:comment_id', (req, res) => { diff --git a/routes/api/stream/index.js b/routes/api/stream/index.js index b41e03dbb..c63bf871d 100644 --- a/routes/api/stream/index.js +++ b/routes/api/stream/index.js @@ -1,33 +1,26 @@ const express = require('express'); +const Comment = require('../../../models/comment'); +const User = require('../../../models/user'); +const Action = require('../../../models/action'); + const router = express.Router(); -router.get('/', (req, res) => { - console.log('Stream endpoint has been hit with asset_id ', req.query.asset_id); - res.json([ - { - 'id': 'abc', - 'type': 'comment', - 'body': 'Sample comment', - 'created_at': new Date().getTime(), - 'asset_id': 'assetTest' - }, - { - 'id': 'xyz', - 'type': 'comment', - 'body': 'Sample reply', - 'created_at': new Date().getTime() - 600000, - 'parent_id': 'abc', - 'asset_id': 'assetTest' - }, - { - 'id': 'def', - 'type': 'comment', - 'body': 'Another comment', - 'created_at': new Date().getTime() - 400000, - 'asset_id': 'assetTest' - } - ]); +router.get('/', (req, res, next) => { + + const commentsPromise = Comment.findByAssetId(req.query.asset_id); + + commentsPromise.then(comments => { + return Promise.all([ + comments, + User.findByIdArray(comments.map((comment) => comment.author_id)), + Action.findByItemIdArray(comments.map((comment) => comment.id)) + ]); + }).then(([comments, users, actions]) => { + res.json([...comments,...users,...actions]); + }).catch(error => { + next(error); + }); }); module.exports = router; diff --git a/tests/.eslintrc.json b/tests/.eslintrc.json new file mode 100644 index 000000000..97d8c197e --- /dev/null +++ b/tests/.eslintrc.json @@ -0,0 +1,10 @@ +{ + "env": { + "es6": true, + "node": true + }, + "extends": "eslint:recommended", + "rules": { + "no-undef": [0] + } +} diff --git a/tests/index.js b/tests/index.js index 0a212b7a4..51ccc54ee 100644 --- a/tests/index.js +++ b/tests/index.js @@ -1,10 +1,12 @@ /* eslint-env node, mocha */ 'use strict'; +// require('./utils/mongoose') const expect = require('chai').expect; + describe('Comment', () => { - describe.only('#add', () => { + describe('#add', () => { it('should add a comment', () => { expect(0).to.be.equal(0); }); diff --git a/tests/models/action.js b/tests/models/action.js new file mode 100644 index 000000000..8736edcab --- /dev/null +++ b/tests/models/action.js @@ -0,0 +1,40 @@ +/* eslint-env node, mocha */ + +require('../utils/mongoose'); +const Action = require('../../models/action'); +const expect = require('chai').expect; + +describe('Action: models', () => { + var mockActions; + beforeEach(() => { + return Action.create([{ + action_type: 'flag', + item_id: '123' + },{ + action_type: 'like', + item_id: '789' + },{ + action_type: 'flag', + item_id: '456' + }]).then((actions) => { + mockActions = actions; + }); + }); + + describe('#findById()', () => { + it('should find an action by id', () => { + return Action.findById(mockActions[0].id).then((result) => { + expect(result).to.have.property('action_type') + .and.to.equal('flag'); + }); + }); + }); + + describe('#findByItemIdArray()', () => { + it('should find an array of actions from an array of item_ids', () => { + return Action.findByItemIdArray(['123','456']).then((result) => { + expect(result).to.have.length(2); + }); + }); + }); +}); diff --git a/tests/models/comment.js b/tests/models/comment.js new file mode 100644 index 000000000..a8a543183 --- /dev/null +++ b/tests/models/comment.js @@ -0,0 +1,51 @@ +/* eslint-env node, mocha */ + +require('../utils/mongoose'); + +const Comment = require('../../models/comment'); +const expect = require('chai').expect; + +describe('Comment: models', () => { + var mockComments; + beforeEach(() => { + return Comment.create([{ + body: 'comment 10', + asset_id: '123' + },{ + body: 'comment 20', + asset_id: '123' + },{ + body: 'comment 30', + asset_id: '456' + }]).then((comments) => { + mockComments = comments; + }); + }); + + describe('#findById()', () => { + it('should find a comment by id', () => { + return Comment.findById(mockComments[0].id).then((result) => { + expect(result).to.have.property('body') + .and.to.equal('comment 10'); + }); + }); + }); + + describe('#findByAssetId()', () => { + it('should find an array of comments by asset id', () => { + return Comment.findByAssetId('123').then((result) => { + expect(result).to.have.length(2); + result.sort((a,b) => { + if (a.body < b.body) {return -1;} + else {return 1;} + }); + expect(result[0]).to.have.property('body') + .and.to.equal('comment 10'); + expect(result[1]).to.have.property('body') + .and.to.equal('comment 20'); + }); + }); + }); + + // }); +}); diff --git a/tests/models/user.js b/tests/models/user.js new file mode 100644 index 000000000..e1063e21c --- /dev/null +++ b/tests/models/user.js @@ -0,0 +1,40 @@ +/* eslint-env node, mocha */ + +require('../utils/mongoose'); +const User = require('../../models/user'); +const expect = require('chai').expect; + +describe('User: models', () => { + var mockUsers; + beforeEach(() => { + return User.create([{ + display_name: 'Stampi', + },{ + display_name: 'Sockmonster', + },{ + display_name: 'Marvel', + }]).then((users) => { + mockUsers = users; + }); + }); + + describe('#findById()', () => { + it('should find a user by id', () => { + return User.findById(mockUsers[0].id).then((result) => { + expect(result).to.have.property('display_name') + .and.to.equal('Stampi'); + }); + }); + }); + + describe('#findByIdArray()', () => { + it('should find an array of users from an array of ids', () => { + const ids = mockUsers.map((user) => user.id) + return User.findByIdArray(ids).then((result) => { + expect(result).to.have.length(3); + }); + }); + }); + + // }); +}); diff --git a/tests/routes/api/comments/index.js b/tests/routes/api/comments/index.js new file mode 100644 index 000000000..ad21e54ec --- /dev/null +++ b/tests/routes/api/comments/index.js @@ -0,0 +1,106 @@ +process.env.NODE_ENV = 'test'; + +require('../../../utils/mongoose'); + +const app = require('../../../../app'); +const chai = require('chai'); +const chaiHttp = require('chai-http'); +chai.use(chaiHttp); +var expect = chai.expect; + + +const Comment = require('../../../../models/comment'); +const Action = require('../../../../models/action'); +const User = require('../../../../models/user'); + +describe('Post /comments', () => { + const users = [{ + id: '123', + display_name: 'John', + },{ + id: '456', + display_name: 'Paul', + }] + + const actions = [{ + action_type: 'flag', + item_id: 'abc' + },{ + action_type: 'like', + item_id: 'hij' + }] + + beforeEach(() => { + return User.create(users).then(() => { + return Action.create(actions) + }) + }) + + it('it should create a comment', function(done) { + chai.request(app) + .post('/api/v1/comments') + .query({'body': 'Something body.', 'author_id': '123', 'asset_id': '1', 'parent_id': ''}) + .end(function(err, res){ + expect(res).to.have.status(201) + done() + }) + }) + +}) + +describe('Get /: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 = [{ + id: '123', + display_name: 'John', + },{ + id: '456', + display_name: 'Paul', + }] + + const actions = [{ + action_type: 'flag', + item_id: 'abc' + },{ + action_type: 'like', + item_id: 'hij' + }] + + beforeEach(() => { + return Comment.create(comments).then(() => { + return User.create(users) + }).then(() => { + return Action.create(actions) + }) + }) + + it('should return the right comment for the comment_id', function(done){ + chai.request(app) + .get('/api/v1/comments') + .query({'comment_id': 'abc'}) + .end(function(err, res){ + expect(err).to.be.null; + expect(res).to.have.status(200); + if (err) return done(err); + done(); + }); + }) + + + +}) diff --git a/tests/routes/api/stream/index.js b/tests/routes/api/stream/index.js new file mode 100644 index 000000000..a4ca53228 --- /dev/null +++ b/tests/routes/api/stream/index.js @@ -0,0 +1,65 @@ +require('../../../utils/mongoose'); + +const app = require('../../../../app'); +const chai = require('chai'); +const chaiHttp = require('chai-http'); +chai.use(chaiHttp); +var expect = chai.expect; + +const Action = require('../../../../models/action'); +const User = require('../../../../models/user'); +const Comment = require('../../../../models/comment'); + +describe('api/stream: routes', () => { + 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 = [{ + id: '123', + display_name: 'John', + },{ + id: '456', + display_name: 'Paul', + }] + + const actions = [{ + action_type: 'flag', + item_id: 'abc' + },{ + action_type: 'like', + item_id: 'hij' + }] + + beforeEach(() => { + return Comment.create(comments).then(() => { + return User.create(users) + }).then(() => { + return Action.create(actions) + }) + }) + + it('should return a stream with comments, users and actions', function(done){ + chai.request(app) + .get('/api/v1/stream') + .query({'asset_id': 'asset'}) + .end(function(err, res){ + expect(err).to.be.null; + expect(res).to.have.status(200); + if (err) return done(err); + done(); + }); + }) +}) diff --git a/tests/utils/mongoose.js b/tests/utils/mongoose.js new file mode 100644 index 000000000..35083a4b3 --- /dev/null +++ b/tests/utils/mongoose.js @@ -0,0 +1,31 @@ +var mongoose = require('mongoose'); + +// Ensure the NODE_ENV is set to 'test', +// this is helpful when you would like to change behavior when testing. +process.env.NODE_ENV = 'test'; + +beforeEach(function (done) { + function clearDB() { + for (var i in mongoose.connection.collections) { + mongoose.connection.collections[i].remove(function() {}); + } + return done(); + } + + + if (mongoose.connection.readyState === 0) { + mongoose.connect('coral-talk-test', function (err) { + if (err) { + throw err; + } + return clearDB(); + }); + } else { + return clearDB(); + } +}); + +after(function (done) { + mongoose.disconnect(); + return done(); +});