mirror of
https://github.com/wassname/talk.git
synced 2026-08-10 12:41:02 +08:00
Merge branch 'master' of https://github.com/coralproject/talk into mod-queue-fix
This commit is contained in:
@@ -22,7 +22,10 @@ if (app.get('env') !== 'test') {
|
||||
//==============================================================================
|
||||
|
||||
app.set('trust proxy', 1);
|
||||
app.use(helmet());
|
||||
// We disable frameward on helmet to allow crossdomain injection of the embed
|
||||
app.use(helmet({
|
||||
frameguard: false
|
||||
}));
|
||||
app.use(bodyParser.json());
|
||||
app.use('/client', express.static(path.join(__dirname, 'dist')));
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
itemActions,
|
||||
Notification,
|
||||
notificationActions,
|
||||
authActions
|
||||
authActions,
|
||||
} from '../../coral-framework';
|
||||
import {connect} from 'react-redux';
|
||||
import CommentBox from '../../coral-plugin-commentbox/CommentBox';
|
||||
@@ -19,6 +19,8 @@ import LikeButton from '../../coral-plugin-likes/LikeButton';
|
||||
import PermalinkButton from '../../coral-plugin-permalinks/PermalinkButton';
|
||||
import SignInContainer from '../../coral-sign-in/containers/SignInContainer';
|
||||
import UserBox from '../../coral-sign-in/components/UserBox';
|
||||
import {TabBar, Tab, TabContent} from '../../coral-ui';
|
||||
import SettingsContainer from '../../coral-settings/containers/SettingsContainer';
|
||||
|
||||
const {addItem, updateItem, postItem, getStream, postAction, deleteAction, appendItemArray} = itemActions;
|
||||
const {addNotification, clearNotification} = notificationActions;
|
||||
@@ -46,11 +48,27 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
},
|
||||
appendItemArray: (item, property, value, addToFront, itemType) =>
|
||||
dispatch(appendItemArray(item, property, value, addToFront, itemType)),
|
||||
logout: () => dispatch(logout())
|
||||
logout: () => dispatch(logout()),
|
||||
});
|
||||
|
||||
class CommentStream extends Component {
|
||||
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
activeTab: 0
|
||||
};
|
||||
|
||||
this.changeTab = this.changeTab.bind(this);
|
||||
}
|
||||
|
||||
changeTab (tab) {
|
||||
this.setState({
|
||||
activeTab: tab
|
||||
});
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
items: PropTypes.object.isRequired,
|
||||
addItem: PropTypes.func.isRequired,
|
||||
@@ -90,137 +108,148 @@ class CommentStream extends Component {
|
||||
const rootItem = this.props.items.assets && this.props.items.assets[rootItemId];
|
||||
const {actions, users, comments} = this.props.items;
|
||||
const {loggedIn, user, showSignInDialog} = this.props.auth;
|
||||
const {activeTab} = this.state;
|
||||
return <div className={showSignInDialog ? 'expandForSignin' : ''}>
|
||||
{
|
||||
rootItem
|
||||
? <div>
|
||||
<div id="commentBox">
|
||||
<InfoBox
|
||||
content={this.props.config.infoBoxContent}
|
||||
enable={this.props.config.infoBoxEnable}/>
|
||||
<Count
|
||||
id={rootItemId}
|
||||
items={this.props.items}/>
|
||||
{loggedIn && <UserBox user={user} logout={this.props.logout} />}
|
||||
<CommentBox
|
||||
addNotification={this.props.addNotification}
|
||||
postItem={this.props.postItem}
|
||||
appendItemArray={this.props.appendItemArray}
|
||||
updateItem={this.props.updateItem}
|
||||
id={rootItemId}
|
||||
premod={this.props.config.moderation}
|
||||
reply={false}
|
||||
author={user}
|
||||
/>
|
||||
{!loggedIn && <SignInContainer />}
|
||||
</div>
|
||||
{
|
||||
rootItem.comments && rootItem.comments.map((commentId) => {
|
||||
const comment = comments[commentId];
|
||||
return <div className="comment" key={commentId}>
|
||||
<hr aria-hidden={true}/>
|
||||
<AuthorName author={users[comment.author_id]}/>
|
||||
<PubDate created_at={comment.created_at}/>
|
||||
<Content body={comment.body}/>
|
||||
<div className="commentActionsLeft">
|
||||
<ReplyButton
|
||||
updateItem={this.props.updateItem}
|
||||
id={commentId}
|
||||
showReply={comment.showReply}/>
|
||||
<LikeButton
|
||||
addNotification={this.props.addNotification}
|
||||
id={commentId}
|
||||
like={actions[comment.like]}
|
||||
postAction={this.props.postAction}
|
||||
deleteAction={this.props.deleteAction}
|
||||
addItem={this.props.addItem}
|
||||
updateItem={this.props.updateItem}
|
||||
currentUser={this.props.auth.user}/>
|
||||
</div>
|
||||
<div className="commentActionsRight">
|
||||
<FlagButton
|
||||
addNotification={this.props.addNotification}
|
||||
id={commentId}
|
||||
flag={actions[comment.flag]}
|
||||
postAction={this.props.postAction}
|
||||
deleteAction={this.props.deleteAction}
|
||||
addItem={this.props.addItem}
|
||||
updateItem={this.props.updateItem}
|
||||
currentUser={this.props.auth.user}/>
|
||||
<PermalinkButton
|
||||
comment_id={commentId}
|
||||
asset_id={comment.asset_id}/>
|
||||
</div>
|
||||
<ReplyBox
|
||||
addNotification={this.props.addNotification}
|
||||
postItem={this.props.postItem}
|
||||
appendItemArray={this.props.appendItemArray}
|
||||
updateItem={this.props.updateItem}
|
||||
id={rootItemId}
|
||||
author={user}
|
||||
parent_id={commentId}
|
||||
premod={this.props.config.moderation}
|
||||
showReply={comment.showReply}/>
|
||||
{
|
||||
comment.children &&
|
||||
comment.children.map((replyId) => {
|
||||
let reply = this.props.items.comments[replyId];
|
||||
return <div className="reply" key={replyId}>
|
||||
<hr aria-hidden={true}/>
|
||||
<AuthorName author={users[reply.author_id]}/>
|
||||
<PubDate created_at={reply.created_at}/>
|
||||
<Content body={reply.body}/>
|
||||
<div className="replyActionsLeft">
|
||||
<ReplyButton
|
||||
|
||||
<TabBar onChange={this.changeTab} activeTab={activeTab}>
|
||||
<Tab><Count id={rootItemId} items={this.props.items}/></Tab>
|
||||
<Tab>Settings</Tab>
|
||||
</TabBar>
|
||||
|
||||
<TabContent show={activeTab === 0}>
|
||||
<div id="commentBox">
|
||||
<InfoBox
|
||||
content={this.props.config.infoBoxContent}
|
||||
enable={this.props.config.infoBoxEnable}
|
||||
/>
|
||||
{loggedIn && <UserBox user={user} logout={this.props.logout} />}
|
||||
<CommentBox
|
||||
addNotification={this.props.addNotification}
|
||||
postItem={this.props.postItem}
|
||||
appendItemArray={this.props.appendItemArray}
|
||||
updateItem={this.props.updateItem}
|
||||
id={rootItemId}
|
||||
premod={this.props.config.moderation}
|
||||
reply={false}
|
||||
author={user}
|
||||
/>
|
||||
{!loggedIn && <SignInContainer />}
|
||||
</div>
|
||||
{
|
||||
rootItem.comments && rootItem.comments.map((commentId) => {
|
||||
const comment = comments[commentId];
|
||||
return <div className="comment" key={commentId}>
|
||||
<hr aria-hidden={true}/>
|
||||
<AuthorName author={users[comment.author_id]}/>
|
||||
<PubDate created_at={comment.created_at}/>
|
||||
<Content body={comment.body}/>
|
||||
<div className="commentActionsLeft">
|
||||
<ReplyButton
|
||||
updateItem={this.props.updateItem}
|
||||
id={commentId}
|
||||
showReply={comment.showReply}/>
|
||||
<LikeButton
|
||||
addNotification={this.props.addNotification}
|
||||
id={commentId}
|
||||
like={actions[comment.like]}
|
||||
postAction={this.props.postAction}
|
||||
deleteAction={this.props.deleteAction}
|
||||
addItem={this.props.addItem}
|
||||
updateItem={this.props.updateItem}
|
||||
currentUser={this.props.auth.user}/>
|
||||
</div>
|
||||
<div className="commentActionsRight">
|
||||
<FlagButton
|
||||
addNotification={this.props.addNotification}
|
||||
id={commentId}
|
||||
flag={actions[comment.flag]}
|
||||
postAction={this.props.postAction}
|
||||
deleteAction={this.props.deleteAction}
|
||||
addItem={this.props.addItem}
|
||||
updateItem={this.props.updateItem}
|
||||
currentUser={this.props.auth.user}/>
|
||||
<PermalinkButton
|
||||
comment_id={commentId}
|
||||
asset_id={comment.asset_id}/>
|
||||
</div>
|
||||
<ReplyBox
|
||||
addNotification={this.props.addNotification}
|
||||
postItem={this.props.postItem}
|
||||
appendItemArray={this.props.appendItemArray}
|
||||
updateItem={this.props.updateItem}
|
||||
id={rootItemId}
|
||||
author={user}
|
||||
parent_id={commentId}
|
||||
premod={this.props.config.moderation}
|
||||
showReply={comment.showReply}/>
|
||||
{
|
||||
comment.children &&
|
||||
comment.children.map((replyId) => {
|
||||
let reply = this.props.items.comments[replyId];
|
||||
return <div className="reply" key={replyId}>
|
||||
<hr aria-hidden={true}/>
|
||||
<AuthorName author={users[reply.author_id]}/>
|
||||
<PubDate created_at={reply.created_at}/>
|
||||
<Content body={reply.body}/>
|
||||
<div className="replyActionsLeft">
|
||||
<ReplyButton
|
||||
updateItem={this.props.updateItem}
|
||||
id={replyId}
|
||||
showReply={reply.showReply}/>
|
||||
<LikeButton
|
||||
addNotification={this.props.addNotification}
|
||||
id={replyId}
|
||||
like={this.props.items.actions[reply.like]}
|
||||
postAction={this.props.postAction}
|
||||
deleteAction={this.props.deleteAction}
|
||||
addItem={this.props.addItem}
|
||||
updateItem={this.props.updateItem}
|
||||
currentUser={this.props.auth.user}/>
|
||||
</div>
|
||||
<div className="replyActionsRight">
|
||||
<FlagButton
|
||||
addNotification={this.props.addNotification}
|
||||
id={replyId}
|
||||
flag={this.props.items.actions[reply.flag]}
|
||||
postAction={this.props.postAction}
|
||||
deleteAction={this.props.deleteAction}
|
||||
addItem={this.props.addItem}
|
||||
updateItem={this.props.updateItem}
|
||||
currentUser={this.props.auth.user}/>
|
||||
<PermalinkButton
|
||||
comment_id={reply.parent_id}
|
||||
asset_id={rootItemId}
|
||||
/>
|
||||
</div>
|
||||
<ReplyBox
|
||||
addNotification={this.props.addNotification}
|
||||
postItem={this.props.postItem}
|
||||
appendItemArray={this.props.appendItemArray}
|
||||
updateItem={this.props.updateItem}
|
||||
id={replyId}
|
||||
id={rootItemId}
|
||||
author={user}
|
||||
parent_id={commentId}
|
||||
child_id={replyId}
|
||||
premod={this.props.config.moderation}
|
||||
showReply={reply.showReply}/>
|
||||
<LikeButton
|
||||
addNotification={this.props.addNotification}
|
||||
id={replyId}
|
||||
like={this.props.items.actions[reply.like]}
|
||||
postAction={this.props.postAction}
|
||||
deleteAction={this.props.deleteAction}
|
||||
addItem={this.props.addItem}
|
||||
updateItem={this.props.updateItem}
|
||||
currentUser={this.props.auth.user}/>
|
||||
</div>
|
||||
<div className="replyActionsRight">
|
||||
<FlagButton
|
||||
addNotification={this.props.addNotification}
|
||||
id={replyId}
|
||||
flag={this.props.items.actions[reply.flag]}
|
||||
postAction={this.props.postAction}
|
||||
deleteAction={this.props.deleteAction}
|
||||
addItem={this.props.addItem}
|
||||
updateItem={this.props.updateItem}
|
||||
currentUser={this.props.auth.user}/>
|
||||
<PermalinkButton
|
||||
comment_id={reply.parent_id}
|
||||
asset_id={rootItemId}
|
||||
/>
|
||||
</div>
|
||||
<ReplyBox
|
||||
addNotification={this.props.addNotification}
|
||||
postItem={this.props.postItem}
|
||||
appendItemArray={this.props.appendItemArray}
|
||||
updateItem={this.props.updateItem}
|
||||
id={rootItemId}
|
||||
author={user}
|
||||
parent_id={commentId}
|
||||
child_id={replyId}
|
||||
premod={this.props.config.moderation}
|
||||
showReply={reply.showReply}/>
|
||||
</div>;
|
||||
})
|
||||
}
|
||||
</div>;
|
||||
})
|
||||
}
|
||||
<Notification
|
||||
notifLength={4500}
|
||||
clearNotification={this.props.clearNotification}
|
||||
notification={this.props.notification}/>
|
||||
</div>;
|
||||
})
|
||||
}
|
||||
</div>;
|
||||
})
|
||||
}
|
||||
<Notification
|
||||
notifLength={4500}
|
||||
clearNotification={this.props.clearNotification}
|
||||
notification={this.props.notification}
|
||||
/>
|
||||
</TabContent>
|
||||
<TabContent show={activeTab === 1}>
|
||||
<SettingsContainer/>
|
||||
</TabContent>
|
||||
</div>
|
||||
: 'Loading'
|
||||
}
|
||||
|
||||
@@ -154,11 +154,6 @@ hr {
|
||||
color: #F00;
|
||||
}
|
||||
|
||||
/* Comment count styles */
|
||||
.coral-plugin-comment-count-text {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.coral-plugin-pubdate-text {
|
||||
color: #CCC;
|
||||
display: inline-block;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {getInit, base, handleResp} from '../../coral-framework/helpers/response';
|
||||
import {getInit, base, handleResp} from '../helpers/response';
|
||||
import {fromJS} from 'immutable';
|
||||
/* Item Actions */
|
||||
|
||||
@@ -95,7 +95,7 @@ export const appendItemArray = (id, property, value, add_to_front, item_type) =>
|
||||
*/
|
||||
export function getStream (assetUrl) {
|
||||
return (dispatch) => {
|
||||
return fetch(`${base}/stream?asset_url=${encodeURIComponent(assetUrl)}`)
|
||||
return fetch(`${base}/stream?asset_url=${encodeURIComponent(assetUrl)}`, getInit('GET'))
|
||||
.then(handleResp)
|
||||
.then((json) => {
|
||||
|
||||
|
||||
@@ -11,5 +11,5 @@ export {
|
||||
itemActions,
|
||||
I18n,
|
||||
notificationActions,
|
||||
authActions
|
||||
authActions,
|
||||
};
|
||||
|
||||
@@ -14,5 +14,5 @@ export default combineReducers({
|
||||
config,
|
||||
items,
|
||||
notification,
|
||||
auth
|
||||
auth,
|
||||
});
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
.bio textarea {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border-radius: 2px;
|
||||
min-height: 100px;
|
||||
margin: 10px 0;
|
||||
border: solid 1px #d8d8d8;
|
||||
}
|
||||
|
||||
.bio h1 {
|
||||
font-size: 16px;
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.bio p {
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.actions {
|
||||
text-align: right;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import styles from './Bio.css';
|
||||
import {Button} from '../../coral-ui';
|
||||
|
||||
export default () => (
|
||||
<div className={styles.bio}>
|
||||
<h1>Bio</h1>
|
||||
<p>Tell the community about yourself</p>
|
||||
<textarea />
|
||||
<div className={styles.actions}>
|
||||
<Button cStyle='cancel' raised>Cancel</Button>
|
||||
<Button cStyle='success'>Save Changes</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import styles from './CommentHistory.css';
|
||||
|
||||
export default ({comments = []}) => (
|
||||
<div className={styles.header}>
|
||||
<h1>Comments</h1>
|
||||
<ul>
|
||||
{comments.map(() => (
|
||||
<li>
|
||||
{/* Comment Data*/}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,8 @@
|
||||
.header h1 {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.header h2 {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import styles from './SettingsHeader.css';
|
||||
|
||||
export default () => (
|
||||
<div className={styles.header}>
|
||||
<h1>Jackson</h1>
|
||||
<h2>jackson_persona@gmail.com</h2>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {TabBar, Tab} from '../../coral-ui';
|
||||
|
||||
import Bio from '../components/Bio';
|
||||
import CommentHistory from '../components/CommentHistory';
|
||||
import SettingsHeader from '../components/SettingsHeader';
|
||||
|
||||
class SignInContainer extends Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
activeTab: 0
|
||||
};
|
||||
|
||||
this.handleTabChange = this.handleTabChange.bind(this);
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
// Get Bio
|
||||
// Fetch commentHistory
|
||||
}
|
||||
|
||||
handleTabChange(tab) {
|
||||
this.setState({
|
||||
activeTab: tab
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
//const {embedStream} = this.props;
|
||||
const {activeTab} = this.state;
|
||||
return (
|
||||
<div>
|
||||
<SettingsHeader />
|
||||
<TabBar onChange={this.handleTabChange} activeTab={activeTab} cStyle='material'>
|
||||
<Tab>All Comments (120)</Tab>
|
||||
<Tab>Profile Settings</Tab>
|
||||
</TabBar>
|
||||
{ activeTab === 0 && <CommentHistory {...this.props}/> }
|
||||
{ activeTab === 1 && <Bio {...this.props}/> }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = () => ({
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
getBio: () => dispatch(),
|
||||
getHistory: () => dispatch(),
|
||||
handleSaveChanges: () => dispatch(),
|
||||
handleCancel: () => dispatch()
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(SignInContainer);
|
||||
@@ -34,7 +34,7 @@ class ForgotContent extends React.Component {
|
||||
id="email"
|
||||
name="email" />
|
||||
</div>
|
||||
<Button type="submit" cStyle="black" className={styles.signInButton}>
|
||||
<Button type="submit" cStyle="black" className={styles.signInButton} full>
|
||||
{lang.t('signIn.recoverPassword')}
|
||||
</Button>
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@ const SignInContent = ({handleChange, formData, ...props}) => (
|
||||
</h1>
|
||||
</div>
|
||||
<div className={styles.socialConnections}>
|
||||
<Button cStyle="facebook" onClick={props.fetchSignInFacebook}>
|
||||
<Button cStyle="facebook" onClick={props.fetchSignInFacebook} full>
|
||||
{lang.t('signIn.facebookSignIn')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -44,7 +44,7 @@ const SignInContent = ({handleChange, formData, ...props}) => (
|
||||
<div className={styles.action}>
|
||||
{
|
||||
!props.auth.isLoading ?
|
||||
<Button type="submit" cStyle="black" className={styles.signInButton}>
|
||||
<Button type="submit" cStyle="black" className={styles.signInButton} full>
|
||||
{lang.t('signIn.signIn')}
|
||||
</Button>
|
||||
:
|
||||
|
||||
@@ -17,7 +17,7 @@ const SignUpContent = ({handleChange, formData, ...props}) => (
|
||||
</h1>
|
||||
</div>
|
||||
<div className={styles.socialConnections}>
|
||||
<Button cStyle="facebook" onClick={props.fetchSignInFacebook}>
|
||||
<Button cStyle="facebook" onClick={props.fetchSignInFacebook} full>
|
||||
{lang.t('signIn.facebookSignUp')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -69,7 +69,7 @@ const SignUpContent = ({handleChange, formData, ...props}) => (
|
||||
/>
|
||||
<div className={styles.action}>
|
||||
{ !props.auth.isLoading && !props.auth.successSignUp && (
|
||||
<Button type="submit" cStyle="black" className={styles.signInButton}>
|
||||
<Button type="submit" cStyle="black" className={styles.signInButton} full>
|
||||
{lang.t('signIn.signUp')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -132,7 +132,7 @@ class SignInContainer extends Component {
|
||||
const {auth, showSignInDialog} = this.props;
|
||||
return (
|
||||
<div>
|
||||
<Button onClick={showSignInDialog}>
|
||||
<Button onClick={showSignInDialog} full>
|
||||
Sign in to comment
|
||||
</Button>
|
||||
<SignDialog
|
||||
|
||||
@@ -23,8 +23,7 @@
|
||||
text-align: center;
|
||||
line-height: 36px;
|
||||
vertical-align: middle;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.type--black {
|
||||
@@ -46,4 +45,34 @@
|
||||
.type--facebook:hover {
|
||||
background-color: #365899;
|
||||
border-color: #365899;
|
||||
}
|
||||
}
|
||||
|
||||
.type--success {
|
||||
color: white;
|
||||
background: #2376d8;
|
||||
}
|
||||
|
||||
.type--success:hover {
|
||||
color: white;
|
||||
background: #2782ee;
|
||||
}
|
||||
|
||||
.type--cancel {
|
||||
color: white;
|
||||
background: #2E343B;
|
||||
}
|
||||
|
||||
.type--cancel:hover {
|
||||
color: white;
|
||||
background: #4f5c67;
|
||||
}
|
||||
|
||||
.full {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.raised {
|
||||
background: rgba(158,158,158,.2);
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import React from 'react';
|
||||
import styles from './Button.css';
|
||||
|
||||
const Button = ({cStyle = 'local', children, className, ...props}) => (
|
||||
const Button = ({cStyle = 'local', children, className, raised, full, ...props}) => (
|
||||
<button
|
||||
className={`${styles.button} ${styles[`type--${cStyle}`]} ${className}`}
|
||||
className={`
|
||||
${styles.button}
|
||||
${styles[`type--${cStyle}`]}
|
||||
${className}
|
||||
${full && styles.full}
|
||||
${raised && styles.button}
|
||||
`}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
li.base--active {
|
||||
background: white;
|
||||
}
|
||||
|
||||
li.material--active {
|
||||
font-weight: bold;
|
||||
border-bottom: solid 2px black;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import styles from './Tab.css';
|
||||
|
||||
export default ({children, tabId, active, onTabClick, cStyle = 'base'}) => (
|
||||
<li
|
||||
key={tabId}
|
||||
className={active ? styles[`${cStyle}--active`] : ''}
|
||||
onClick={() => onTabClick(tabId)}
|
||||
>
|
||||
{children}
|
||||
</li>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
.base {
|
||||
list-style: none;
|
||||
border-bottom: solid 1px #D8D8D8;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.base li {
|
||||
color: #4E5259;
|
||||
border: solid 1px #D8D8D8;
|
||||
background: #F0F0F0;
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
display: inline-block;
|
||||
border-bottom: none;
|
||||
padding: 8px 10px;
|
||||
margin-right: -1px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.base li:hover {
|
||||
background: #f3f3f3;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.material {
|
||||
list-style: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.material li {
|
||||
color: black;
|
||||
border: none;
|
||||
border-bottom: solid 2px white;
|
||||
background: white;
|
||||
padding: 8px 0;
|
||||
margin-right: 40px;
|
||||
}
|
||||
|
||||
.material li:hover {
|
||||
background: white;
|
||||
border-bottom: solid 2px grey;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import styles from './TabBar.css';
|
||||
|
||||
export class TabBar extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleClickTab = this.handleClickTab.bind(this);
|
||||
}
|
||||
|
||||
handleClickTab(tabId) {
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(tabId);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {children, activeTab, cStyle = 'base'} = this.props;
|
||||
return (
|
||||
<div>
|
||||
<ul className={`${styles.base} ${cStyle ? styles[cStyle] : ''}`}>
|
||||
{React.Children.map(children, (child, tabId) =>
|
||||
React.cloneElement(child, {
|
||||
tabId,
|
||||
active: tabId === activeTab,
|
||||
onTabClick: this.handleClickTab,
|
||||
cStyle
|
||||
})
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TabBar;
|
||||
@@ -0,0 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
export default ({children, show = true}) => (
|
||||
show ? <div>{children}</div> : null
|
||||
);
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
export {default as Dialog} from './components/Dialog';
|
||||
export {default as CoralLogo} from './components/CoralLogo';
|
||||
export {default as FabButton} from './components/FabButton';
|
||||
export {default as TabBar} from './components/TabBar';
|
||||
export {default as Tab} from './components/Tab';
|
||||
export {default as TabContent} from './components/TabContent';
|
||||
export {default as Button} from './components/Button';
|
||||
|
||||
+46
-5
@@ -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;
|
||||
|
||||
@@ -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'
|
||||
];
|
||||
|
||||
+5
-1
@@ -1,7 +1,11 @@
|
||||
const mongoose = require('mongoose');
|
||||
const debug = require('debug')('talk:db');
|
||||
const enabled = require('debug').enabled;
|
||||
const url = process.env.TALK_MONGO_URL || 'mongodb://localhost';
|
||||
let url = process.env.TALK_MONGO_URL || 'mongodb://localhost';
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
url = 'mongodb://localhost/coral-test';
|
||||
}
|
||||
|
||||
// Use native promises
|
||||
mongoose.Promise = global.Promise;
|
||||
|
||||
+14
-3
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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') {
|
||||
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,16 @@ 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,
|
||||
|
||||
@@ -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
@@ -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')
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -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
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,70 +20,78 @@ beforeEach(() => {
|
||||
});
|
||||
|
||||
describe('Get moderation queues rejected, pending, flags', () => {
|
||||
let comments = [{
|
||||
id: 'abc',
|
||||
body: 'comment 10',
|
||||
asset_id: 'asset',
|
||||
status: 'rejected'
|
||||
}, {
|
||||
id: 'def',
|
||||
body: 'comment 20',
|
||||
asset_id: 'asset'
|
||||
}, {
|
||||
id: 'hij',
|
||||
body: 'comment 30',
|
||||
asset_id: '456',
|
||||
status: 'accepted'
|
||||
}];
|
||||
|
||||
const users = [{
|
||||
id: '456',
|
||||
displayName: 'Ana',
|
||||
email: 'ana@gmail.com',
|
||||
password: '123'
|
||||
}, {
|
||||
id: '123',
|
||||
displayName: 'Maria',
|
||||
email: 'maria@gmail.com',
|
||||
password: '123'
|
||||
}];
|
||||
describe('/api/v1/queue', () => {
|
||||
let comments;
|
||||
|
||||
let actions = [{
|
||||
action_type: 'flag',
|
||||
item_type: 'comment'
|
||||
}, {
|
||||
action_type: 'like',
|
||||
item_type: 'comment'
|
||||
}];
|
||||
const users = [{
|
||||
id: '456',
|
||||
displayName: 'Ana',
|
||||
email: 'ana@gmail.com',
|
||||
password: '123'
|
||||
}, {
|
||||
id: '123',
|
||||
displayName: 'Maria',
|
||||
email: 'maria@gmail.com',
|
||||
password: '123'
|
||||
}];
|
||||
|
||||
beforeEach(() => {
|
||||
return User.createLocalUsers(users)
|
||||
.then((u) => {
|
||||
comments[0].author_id = u[0].id;
|
||||
comments[1].author_id = u[1].id;
|
||||
comments[2].author_id = u[1].id;
|
||||
let actions;
|
||||
|
||||
return Comment.create(comments);
|
||||
})
|
||||
.then((c) => {
|
||||
actions[0].item_id = c[0].id;
|
||||
actions[1].item_id = c[1].id;
|
||||
beforeEach(() => {
|
||||
|
||||
return Action.create(actions);
|
||||
comments = [{
|
||||
id: 'abc',
|
||||
body: 'comment 10',
|
||||
asset_id: 'asset',
|
||||
status: 'rejected'
|
||||
}, {
|
||||
id: 'def',
|
||||
body: 'comment 20',
|
||||
asset_id: 'asset'
|
||||
}, {
|
||||
id: 'hij',
|
||||
body: 'comment 30',
|
||||
asset_id: '456',
|
||||
status: 'accepted'
|
||||
}];
|
||||
|
||||
actions = [{
|
||||
action_type: 'flag',
|
||||
item_type: 'comment'
|
||||
}, {
|
||||
action_type: 'like',
|
||||
item_type: 'comment'
|
||||
}];
|
||||
|
||||
return User.createLocalUsers(users)
|
||||
.then((u) => {
|
||||
comments[0].author_id = u[0].id;
|
||||
comments[1].author_id = u[1].id;
|
||||
comments[2].author_id = u[1].id;
|
||||
|
||||
return Comment.create(comments);
|
||||
})
|
||||
.then((c) => {
|
||||
actions[0].item_id = c[0].id;
|
||||
actions[1].item_id = c[1].id;
|
||||
|
||||
return Action.create(actions);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return all the pending comments, users and actions', 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.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');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should return all the pending comments, users and actions', 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.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');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -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: 'post'
|
||||
};
|
||||
|
||||
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: 'pre'})
|
||||
.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', 'post');
|
||||
});
|
||||
});
|
||||
|
||||
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', 'pre');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user