Merge pull request #19 from coralproject/asset

This commit is contained in:
Wyatt Johnson
2016-11-07 12:46:05 -07:00
committed by GitHub
22 changed files with 401 additions and 98 deletions
+1
View File
@@ -1 +1,2 @@
client
dist
+14 -1
View File
@@ -45,6 +45,19 @@
"space-infix-ops": ["error"],
"no-const-assign": [2],
"no-duplicate-imports": [2],
"prefer-template": [1]
"prefer-template": [1],
"comma-spacing": [
"error",
{
"after": true
}
],
"no-var": [2],
"no-lonely-if": [2],
"curly": [2],
"no-multiple-empty-lines": [
"error",
{"max": 1}
]
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ const ActionSchema = new Schema({
item_type: String,
item_id: String,
user_id: String
},{
}, {
timestamps: {
createdAt: 'created_at',
updatedAt: 'updated_at'
+106
View File
@@ -0,0 +1,106 @@
const mongoose = require('../mongoose');
const uuid = require('uuid');
const Schema = mongoose.Schema;
const AssetSchema = new Schema({
id: {
type: String,
default: uuid.v4,
unique: true,
index: true
},
url: {
type: String,
unique: true,
index: true
},
type: {
type: String,
default: 'article'
},
headline: String,
summary: String,
section: String,
subsection: String,
authors: [String],
publication_date: Date
}, {
versionKey: false,
timestamps: {
createdAt: 'created_at',
updatedAt: 'updated_at'
}
});
/**
* Search for assets. Currently only returns all.
*/
AssetSchema.statics.search = function(query) {
return Asset.find(query).exec();
};
/**
* Finds an asset by its id.
* @param {String} id identifier of the asset (uuid).
*/
AssetSchema.statics.findById = function(id) {
return Asset.findOne({id}).exec();
};
/**
* Finds a asset by its url.
* @param {String} url identifier of the asset (uuid).
*/
AssetSchema.statics.findByUrl = function(url) {
return Asset.findOne({'url': url}).exec();
};
/**
* Upserts an asset.
*/
AssetSchema.statics.upsert = function(data) {
// If an id is not sent, create one.
if (typeof data.id === 'undefined') {
data.id = uuid.v4();
}
// Perform the upsert against the id field.
let updatePromise = Asset.update({id: data.id}, data, {upsert: true}).exec()
.then(() => {
// Pull the freshly minted asset out and return.
return Asset.findById(data.id);
})
.catch((err) => {
console.error('Error upserting asset.', err);
//return new Promise(); // ??? what do we return on error?
});
return updatePromise;
};
/**
* Remove assets from the db.
* @param {String} query bson query to identify assets to be removed.
*/
AssetSchema.statics.removeAll = function(query) {
return Asset.remove(query).exec();
};
const Asset = mongoose.model('Asset', AssetSchema);
module.exports = Asset;
+1 -2
View File
@@ -21,7 +21,7 @@ const CommentSchema = new Schema({
default: ''
},
parent_id: String
},{
}, {
timestamps: {
createdAt: 'created_at',
updatedAt: 'updated_at'
@@ -44,7 +44,6 @@ CommentSchema.statics.findByAssetId = function(asset_id) {
return Comment.find({asset_id});
};
const Comment = mongoose.model('Comment', CommentSchema);
module.exports = Comment;
+1 -1
View File
@@ -11,7 +11,7 @@ const UserProfileSchema = new Schema({
},
display_name: String,
auth_user_id: String
},{
}, {
timestamps: {
createdAt: 'created_at',
updatedAt: 'updated_at'
+1 -1
View File
@@ -11,7 +11,7 @@ if (enabled('talk:db')) {
try {
mongoose.connect(url, (err) => {
if (err) throw err;
if (err) {throw err;}
console.log('Connected to MongoDB!');
});
} catch (err) {
+1
View File
@@ -43,6 +43,7 @@
"homepage": "https://github.com/coralproject/talk#readme",
"dependencies": {
"body-parser": "^1.15.2",
"chai-http": "^3.0.0",
"debug": "^2.2.0",
"express": "^4.14.0",
"mongoose": "^4.6.5",
+44
View File
@@ -0,0 +1,44 @@
const express = require('express');
const router = express.Router();
const Asset = require('../../../models/asset');
// Search assets.
router.get('/', (req, res, next) => {
let query = {};
if (typeof req.query.url !== 'undefined') {
query.url = req.query.url;
}
Asset.search(query)
.then((asset) => {
res.json(asset);
})
.catch(next);
});
// Get an asset by id
router.get('/:id', (req, res, next) => {
Asset.findById(req.params.id)
.then((asset) => {
res.json(asset);
})
.catch(next);
});
// Upsert an asset and return the affected document.
router.put('/', (req, res, next) => {
Asset.upsert(req.body)
.then((asset) => {
res.json(asset);
})
.catch(next);
});
module.exports = router;
-1
View File
@@ -7,7 +7,6 @@ const router = express.Router();
// Routes
//==============================================================================
router.get('/', (req, res) => {
res.send('Read all of the comments ever');
});
+3 -2
View File
@@ -2,9 +2,10 @@ const express = require('express');
const router = express.Router();
router.use('/asset', require('./asset'));
router.use('/comments', require('./comments'));
router.use('/stream', require('./stream'));
router.use('/settings', require('./settings'));
router.use('/queue', require('./queue'));
router.use('/settings', require('./settings'));
router.use('/stream', require('./stream'));
module.exports = router;
+1 -1
View File
@@ -17,7 +17,7 @@ router.get('/', (req, res, next) => {
Action.findByItemIdArray(comments.map((comment) => comment.id))
]);
}).then(([comments, users, actions]) => {
res.json([...comments,...users,...actions]);
res.json([...comments, ...users, ...actions]);
}).catch(error => {
next(error);
});
+62 -8
View File
@@ -3,10 +3,10 @@ info:
title: Talk API
description: A commenting platform from The Coral Project. https://coralproject.net
version: "0.0.1"
host: api.talk-coralproject.net
host: talk-stg.coralproject.net
schemes:
- https
basePath: /v1
basePath: /api/v1
produces:
- application/json
paths:
@@ -161,7 +161,35 @@ paths:
description: Settings
responses:
204:
description: Updated Setting
description: OK
/asset:
get:
tags:
- Asset
description: Get an asset by id.
responses:
200:
description: OK
put:
tags:
- Asset
description: Upsert an asset.
responses:
204:
description: OK
/asset?url={url}:
get:
tags:
- Asset
parameters:
- name: url
in: query
description: The url of the asset.
required: true
description: Get an asset by its url.
responses:
200:
description: OK
definitions:
Item:
@@ -214,11 +242,6 @@ definitions:
type: string
user_id:
type: string
Setting:
type: object
properties:
id:
type: string
moderation:
type: string
enum:
@@ -232,3 +255,34 @@ definitions:
type: string
format: date-time
description: Updated Date-Time
Asset:
type: object
properties:
id:
type: string
description: The uuid.v4 id of the asset.
url:
type: string
description: The url where the asset is found.
type:
type: string
description: What kind of asset it is.
default: article
headline:
type: string
description: The headline of the asset, inferred on initial load.
summary:
type: string
description: A summary of the asset, inferred on initial load.
section:
type: string
description: The section the asset is in.
subsection:
type: string
description: The subsection that the asset is in.
authors:
type: string
description: An array of the authors for this asset.
publication_date:
type: date
desctipion: When this asset was published.
+3 -2
View File
@@ -1,9 +1,10 @@
{
"env": {
"es6": true,
"node": true
"node": true,
"mocha": true
},
"extends": "eslint:recommended",
"extends": "../.eslintrc.json",
"rules": {
"no-undef": [0]
}
-5
View File
@@ -1,10 +1,5 @@
/* eslint-env node, mocha */
'use strict';
// require('./utils/mongoose')
const expect = require('chai').expect;
describe('Comment', () => {
describe('#add', () => {
it('should add a comment', () => {
+5 -6
View File
@@ -1,19 +1,18 @@
/* eslint-env node, mocha */
require('../utils/mongoose');
const Action = require('../../models/action');
const expect = require('chai').expect;
describe('Action: models', () => {
var mockActions;
let 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) => {
@@ -32,7 +31,7 @@ describe('Action: models', () => {
describe('#findByItemIdArray()', () => {
it('should find an array of actions from an array of item_ids', () => {
return Action.findByItemIdArray(['123','456']).then((result) => {
return Action.findByItemIdArray(['123', '456']).then((result) => {
expect(result).to.have.length(2);
});
});
+95
View File
@@ -0,0 +1,95 @@
require('../utils/mongoose');
const chai = require('chai');
const expect = chai.expect;
const server = require('../../app');
// Setup chai.
chai.should();
chai.use(require('chai-http'));
let fixture = {
'url': 'http://hhgg.com/total-perspective-vortex',
'type': 'article',
'headline': 'The Total Perspective Vortex',
'summary': 'You are an insignificant dot on an insignificant dot.',
'section': 'Everything',
'authors': ['Ford Prefect']
};
describe('Asset: models', () => {
describe('/GET Asset', () => {
describe('#get', () => {
it('It should get an empty array when there are no assets.', (done) => {
chai.request(server)
.get('/api/v1/asset')
.end((err, res) => {
if (err) {
throw new Error(err);
}
res.should.have.status(200);
res.body.should.be.a('array');
res.body.length.should.be.eql(0);
done();
});
});
});
});
// This test checks PUT and read
describe('/PUT Asset', () => {
describe('#put', () => {
it('It should save an asset and load it again.', (done) => {
chai.request(server)
.put('/api/v1/asset')
.send(fixture)
.end((err, res) => {
if (err) {
throw new Error(err);
}
res.should.have.status(200);
res.body.should.be.a('object');
// Id should be generated by the model if absent.
res.body.should.have.property('id');
// Save the asset id to compare with GET result.
let assetId = res.body.id;
// Load the asset to make sure it's really there.
chai.request(server)
.get(`/api/v1/asset?url=${encodeURIComponent(fixture.url)}`)
.end((err, res) => {
if (err) {
throw new Error(err);
}
res.should.have.status(200);
res.body.should.be.an('array');
let asset = res.body[0];
expect(asset).to.have.property('id');
// Ensure the asset has the same id as above.
// This tests the single url per Id concept.
expect(assetId).to.equal(asset.id);
done();
});
});
});
});
}); // End describe /PUT Asset
});
+4 -6
View File
@@ -1,20 +1,18 @@
/* eslint-env node, mocha */
require('../utils/mongoose');
const Comment = require('../../models/comment');
const expect = require('chai').expect;
describe('Comment: models', () => {
var mockComments;
let 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) => {
@@ -35,7 +33,7 @@ describe('Comment: models', () => {
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) => {
result.sort((a, b) => {
if (a.body < b.body) {return -1;}
else {return 1;}
});
+5 -8
View File
@@ -1,17 +1,16 @@
/* eslint-env node, mocha */
require('../utils/mongoose');
const User = require('../../models/user');
const expect = require('chai').expect;
describe('User: models', () => {
var mockUsers;
let mockUsers;
beforeEach(() => {
return User.create([{
display_name: 'Stampi',
},{
}, {
display_name: 'Sockmonster',
},{
}, {
display_name: 'Marvel',
}]).then((users) => {
mockUsers = users;
@@ -29,12 +28,10 @@ describe('User: models', () => {
describe('#findByIdArray()', () => {
it('should find an array of users from an array of ids', () => {
const ids = mockUsers.map((user) => user.id)
const ids = mockUsers.map((user) => user.id);
return User.findByIdArray(ids).then((result) => {
expect(result).to.have.length(3);
});
});
});
// });
});
+30 -32
View File
@@ -4,10 +4,11 @@ require('../../../utils/mongoose');
const app = require('../../../../app');
const chai = require('chai');
const chaiHttp = require('chai-http');
chai.use(chaiHttp);
var expect = chai.expect;
const expect = chai.expect;
// Setup chai.
chai.should();
chai.use(require('chai-http'));
const Comment = require('../../../../models/comment');
const Action = require('../../../../models/action');
@@ -17,36 +18,36 @@ 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)
})
})
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()
})
})
expect(res).to.have.status(201);
done();
});
});
})
});
describe('Get /:comment_id', () => {
const comments = [{
@@ -54,40 +55,40 @@ describe('Get /:comment_id', () => {
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)
return User.create(users);
}).then(() => {
return Action.create(actions)
})
})
return Action.create(actions);
});
});
it('should return the right comment for the comment_id', function(done){
chai.request(app)
@@ -96,11 +97,8 @@ describe('Get /:comment_id', () => {
.end(function(err, res){
expect(err).to.be.null;
expect(res).to.have.status(200);
if (err) return done(err);
if (err) {return done(err);}
done();
});
})
})
});
});
+19 -17
View File
@@ -2,9 +2,11 @@ require('../../../utils/mongoose');
const app = require('../../../../app');
const chai = require('chai');
const chaiHttp = require('chai-http');
chai.use(chaiHttp);
var expect = chai.expect;
const expect = chai.expect;
// Setup chai.
chai.should();
chai.use(require('chai-http'));
const Action = require('../../../../models/action');
const User = require('../../../../models/user');
@@ -16,40 +18,40 @@ describe('api/stream: routes', () => {
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)
return User.create(users);
}).then(() => {
return Action.create(actions)
})
})
return Action.create(actions);
});
});
it('should return a stream with comments, users and actions', function(done){
chai.request(app)
@@ -58,8 +60,8 @@ describe('api/stream: routes', () => {
.end(function(err, res){
expect(err).to.be.null;
expect(res).to.have.status(200);
if (err) return done(err);
if (err) {return done(err);}
done();
});
})
})
});
});
+4 -4
View File
@@ -1,4 +1,4 @@
var mongoose = require('mongoose');
const mongoose = require('../../mongoose');
// Ensure the NODE_ENV is set to 'test',
// this is helpful when you would like to change behavior when testing.
@@ -6,18 +6,18 @@ process.env.NODE_ENV = 'test';
beforeEach(function (done) {
function clearDB() {
for (var i in mongoose.connection.collections) {
for (let 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) {
mongoose.on('open', function() {
if (err) {
throw err;
}
return clearDB();
});
} else {