addCommentTag mutation works

This commit is contained in:
Benjamin Goering
2017-02-28 02:31:02 +08:00
parent fc9c67b05c
commit 6e338290a1
16 changed files with 523 additions and 379 deletions
+1
View File
@@ -3,6 +3,7 @@
"es6": true,
"node": true
},
"parser": "babel-eslint",
"extends": "eslint:recommended",
"rules": {
"indent": ["error",
+2 -2
View File
@@ -112,11 +112,11 @@ class Comment extends React.Component {
// @TODO(bengo) Would be best to only create these funcs on prop change
const addBestTag = () => addCommentTag({
comment_id: comment.id,
id: comment.id,
tag: BEST_TAG,
});
const removeBestTag = () => removeCommentTag({
comment_id: comment.id,
id: comment.id,
tag: BEST_TAG,
});
@@ -1,10 +1,11 @@
mutation AddCommentTag ($comment_id: ID!, $tag: String!) {
addCommentTag(comment_id:$comment_id, tag:$tag) {
comment {
tags {
name
}
}
mutation AddCommentTag ($id: ID!, $tag: String!) {
addCommentTag(id:$id, tag:$tag) {
comment {
id
tags {
name
}
}
errors {
translation_key
}
@@ -127,9 +127,10 @@ export const deleteAction = graphql(DELETE_ACTION, {
export const addCommentTag = graphql(ADD_COMMENT_TAG, {
props: ({mutate}) => ({
addCommentTag: (tag) => {
addCommentTag: ({id, tag}) => {
return mutate({
variables: {
id,
tag
}
});
+5 -7
View File
@@ -5,7 +5,10 @@ import classnames from 'classnames';
// tag string for best comments
export const BEST_TAG = 'BEST';
export const commentIsBest = ({tags} = {}) => Array.isArray(tags) && tags.some(t => t === BEST_TAG);
export const commentIsBest = ({tags} = {}) => {
const isBest = Array.isArray(tags) && tags.some(t => t.name === BEST_TAG);
return isBest;
};
const name = 'coral-plugin-best';
const lang = new I18n(translations);
@@ -38,10 +41,6 @@ export class BestButton extends Component {
removeBest: PropTypes.func.isRequired,
}
state = {
best: false
}
constructor(props) {
super(props);
this.onClickAddBest = this.onClickAddBest.bind(this);
@@ -75,8 +74,7 @@ export class BestButton extends Component {
// @TODO(bengo) Consider adding the comment__action classes to other buttons to add cursor:pointer and never wrap the icons
// @TODO(bengo) Should I reuse another element like coral-ui button? Just doing what LikeButton does for now
// Oh. I think that's styled for the admin. Don't use coral-ui button until the whole comment bottom bar does.
let {isBest} = this.state;
const {addBest, removeBest} = this.props;
const {isBest, addBest, removeBest} = this.props;
return <div className={classnames(`${name}-container`, `${name}-button`, 'comment__action-button--nowrap',
`e2e__${isBest ? 'unset' : 'set'}-best-comment`)}>
<button onClick={isBest ? this.onClickRemoveBest : this.onClickAddBest}
+14 -1
View File
@@ -187,11 +187,20 @@ const setCommentStatus = ({loaders: {Comments}}, {id, status}) => {
});
};
/**
* Adds a tag to a Comment
*/
const addCommentTag = ({user, loaders: {Comments}}, {id, tag}) => {
return CommentsService.addTag(id, tag, user.id)
.then(() => CommentsService.findById( id ));
};
module.exports = (context) => {
let mutators = {
Comment: {
create: () => Promise.reject(errors.ErrNotAuthorized),
setCommentStatus: () => Promise.reject(errors.ErrNotAuthorized)
setCommentStatus: () => Promise.reject(errors.ErrNotAuthorized),
addCommentTag: () => Promise.reject(errors.ErrNotAuthorized),
}
};
@@ -203,5 +212,9 @@ module.exports = (context) => {
mutators.Comment.setCommentStatus = (action) => setCommentStatus(context, action);
}
if (context.user && context.user.can('mutation:addCommentTag')) {
mutators.Comment.addCommentTag = (action) => addCommentTag(context, action);
}
return mutators;
};
+3
View File
@@ -48,6 +48,9 @@ const RootMutation = {
},
setCommentStatus(_, {id, status}, {mutators: {Comment}}) {
return wrapResponse(null)(Comment.setCommentStatus({id, status}));
},
addCommentTag(_, {id, tag}, {mutators: {Comment}}) {
return wrapResponse('comment')(Comment.addCommentTag({id, tag}));
}
};
+10
View File
@@ -631,6 +631,13 @@ type SetCommentStatusResponse implements Response {
errors: [UserError]
}
# Response to addCommentTag mutation
type AddCommentTagResponse implements Response {
# An array of errors relating to the mutation that occured.
comment: Comment
errors: [UserError]
}
# All mutations for the application are defined on this object.
type RootMutation {
@@ -654,6 +661,9 @@ type RootMutation {
# Sets Comment status. Requires the `ADMIN` role.
setCommentStatus(id: ID!, status: COMMENT_STATUS!): SetCommentStatusResponse
# Add tag to comment.
addCommentTag(id: ID!, tag: String!): AddCommentTagResponse
}
################################################################################
+2 -1
View File
@@ -156,7 +156,8 @@ const USER_GRAPH_OPERATIONS = [
'mutation:deleteAction',
'mutation:editName',
'mutation:setUserStatus',
'mutation:setCommentStatus'
'mutation:setCommentStatus',
'mutation:addCommentTag',
];
/**
+1
View File
@@ -103,6 +103,7 @@
"babel-preset-es2015": "^6.18.0",
"babel-preset-stage-0": "^6.16.0",
"chai": "^3.5.0",
"chai-as-promised": "^6.0.0",
"chai-http": "^3.0.0",
"copy-webpack-plugin": "^4.0.0",
"css-loader": "^0.25.0",
+37 -16
View File
@@ -4,7 +4,8 @@ const ActionModel = require('../models/action');
const ActionsService = require('./actions');
const ALLOWED_TAGS = [
{name: 'STAFF'}
'STAFF',
'BEST',
];
const STATUSES = [
@@ -44,29 +45,49 @@ module.exports = class CommentsService {
/**
* Adds a tag if it doesn't already exist on the comment.
* @param {String} id the id of the comment to tag
* @param {String} name the name of the tag to add
* @param {String} assigned_by the user id for the user who added the tag
*/
static addTag(id, name, assigned_by) {
if (ALLOWED_TAGS.find((t) => t.name === name) == null) {
if (ALLOWED_TAGS.find((t) => t === name) == null) {
return Promise.reject(new Error('tag not allowed'));
}
return CommentModel.update({
const filter = {
id,
tags: {
$ne: {
name
tags: {$not: {$elemMatch: {name}}}
};
const update = {
$push: {tags: {
name,
assigned_by,
created_at: new Date()
}}
};
return CommentModel.update(filter, update)
.then(({nModified}) => {
switch (nModified) {
case 0:
// either the tag was already there, or the comment doesn't exist with that id...
return this.findById(id)
.then(result => {
if ( ! result) {
throw new Error(`Can't add tag to comment. There is no comment with id ${id}`);
}
throw new Error(`Can't add tag ${name} to comment. Comment already has that tag.`);
});
case 1:
// tag added
return;
default:
console.warn('CommentsService#addTag modified multiple comments, but it should only have modified 1. '
+ `You may have bad data. Check for multiple comments with id=${id}`);
}
}
}, {
$push: {
tags: {
name,
assigned_by,
created_at: new Date()
}
}
});
});
}
/**
+4 -7
View File
@@ -16,6 +16,7 @@ module.exports = {
const setBestCommentButton = '@setBestButton';
const unsetBestCommentButton = '@unsetBestButton';
embedStreamPage
.postComment('Hi everyone. Isn\'t this the BEST comment!?')
.waitForElementVisible(setBestCommentButton, 2000)
@@ -25,13 +26,9 @@ module.exports = {
});
// on refresh, it should still be tagged as best :)
// client
// .refresh(() => {
// })
// .waitForElementVisible(unsetBestCommentButton, 2000)
// client.pause()
client.refresh();
client.page.embedStreamPage().ready()
.waitForElementVisible('.e2e__unset-best-comment', 2000);
},
after: client => {
client.end();
+42
View File
@@ -0,0 +1,42 @@
const expect = require('chai').expect;
const {graphql} = require('graphql');
const schema = require('../../../graph/schema');
const Context = require('../../../graph/context');
const UserModel = require('../../../models/user');
const SettingsService = require('../../../services/settings');
const CommentsService = require('../../../services/comments');
describe('graph.mutations.addCommentTag', () => {
let comment;
beforeEach(async () => {
SettingsService.init();
comment = await CommentsService.publicCreate({body: `hello there! ${ String(Math.random()).slice(2)}`});
});
const query = `
mutation AddCommentTag ($id: ID!, $tag: String!) {
addCommentTag(id:$id, tag:$tag) {
comment {
tags {
name
}
}
errors {
translation_key
}
}
}
`;
it('can add tags to comments', async () => {
const user = new UserModel({});
const context = new Context({user});
const response = await graphql(schema, query, {}, context, {id: comment.id, tag: 'BEST'});
if (response.errors && response.errors.length) {
console.error(response.errors);
}
expect(response.errors).to.be.empty;
expect(response.data.addCommentTag.comment.tags).to.deep.equal([{name: 'BEST'}]);
});
});
+1
View File
@@ -2,6 +2,7 @@ test/helpers/*.js
test
--compilers js:babel-core/register
--require ignore-styles
--require babel-polyfill
--recursive
--colors
--sort
+46 -1
View File
@@ -8,7 +8,7 @@ const CommentsService = require('../../services/comments');
const settings = {id: '1', moderation: 'PRE', wordlist: {banned: ['bad words'], suspect: ['suspect words']}};
const expect = require('chai').expect;
const expect = require('chai').use(require('chai-as-promised')).expect;
describe('services.CommentsService', () => {
const comments = [{
@@ -68,6 +68,7 @@ describe('services.CommentsService', () => {
}];
const users = [{
id: 'u1',
email: 'stampi@gmail.com',
username: 'Stampi',
password: '1Coral!!'
@@ -218,6 +219,50 @@ describe('services.CommentsService', () => {
});
describe('#addTag', () => {
it('adds a tag', async () => {
const commentId = comments[0].id;
const tagName = 'BEST';
const userId = users[0].id;
await CommentsService.addTag(commentId, tagName, userId);
const updatedComment = await CommentsService.findById(commentId);
expect(updatedComment.tags.length).to.equal(1);
expect(updatedComment.tags[0].name).to.equal(tagName);
expect(updatedComment.tags[0].assigned_by).to.equal(userId);
expect(updatedComment.tags[0].created_at).to.be.an.instanceof(Date);
});
it('can\'t add a tag to comment id that doesn\'t exist', async () => {
const commentId = 'fakenews';
const tagName = 'BEST';
const userId = users[0].id;
await expect(CommentsService.addTag(commentId, tagName, userId)).to.be.rejected;
});
it('can\'t add same tag.name twice', async () => {
const commentId = comments[0].id;
const tagName = 'BEST';
const userId = users[0].id;
// first time
await CommentsService.addTag(commentId, tagName, userId);
// second time should fail
await expect(CommentsService.addTag(commentId, tagName, userId)).to.be.rejected;
});
});
describe('#removeTag', () => {
it.skip('removes a tag', async () => {
const commentId = comments[0].id;
const tagName = 'BEST';
await CommentsService.addTag(commentId, tagName, users[0].id);
let updatedComment = await CommentsService.findById(commentId);
expect(updatedComment.tags.length).to.equal(1);
// ok now to remove it
await CommentsService.removeTag(commentId, tagName);
});
});
describe('#changeStatus', () => {
it('should change the status of a comment from no status', () => {
+345 -336
View File
File diff suppressed because it is too large Load Diff