From 66f068359fa774ba630d821998539b9158c5c256 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 4 Jan 2018 14:14:27 -0700 Subject: [PATCH 1/9] Added index for `item_id` on Actions. --- models/action.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/models/action.js b/models/action.js index 8aa7322e3..e378eec18 100644 --- a/models/action.js +++ b/models/action.js @@ -21,8 +21,8 @@ const ActionSchema = new Schema({ item_id: String, user_id: String, - // The element that summaries will additionally group on in addtion to their action_type, item_type, and - // item_id. + // The element that summaries will additionally group on in addition to their + // action_type, item_type, and item_id. group_id: String, // Additional metadata stored on the field. @@ -34,6 +34,14 @@ const ActionSchema = new Schema({ } }); +// Create an index on the `item_id` field so that queries looking for +// actions based on the item id can resolve faster. +ActionSchema.index({ + 'item_id': 1 +}, { + background: true +}); + const Action = mongoose.model('Action', ActionSchema); module.exports = Action; From e256b0da8a61b0111d69fd6641f3b7bbc8033324 Mon Sep 17 00:00:00 2001 From: okbel Date: Fri, 12 Jan 2018 15:47:09 -0300 Subject: [PATCH 2/9] Adding format to admin comment - CommentFormatter --- ...BodyHighlighter.js => CommentFormatter.js} | 50 +++++++++++++++---- .../routes/Moderation/components/Comment.js | 13 +++-- 2 files changed, 47 insertions(+), 16 deletions(-) rename client/coral-admin/src/components/{CommentBodyHighlighter.js => CommentFormatter.js} (63%) diff --git a/client/coral-admin/src/components/CommentBodyHighlighter.js b/client/coral-admin/src/components/CommentFormatter.js similarity index 63% rename from client/coral-admin/src/components/CommentBodyHighlighter.js rename to client/coral-admin/src/components/CommentFormatter.js index 9e381bf0b..6c086b6aa 100644 --- a/client/coral-admin/src/components/CommentBodyHighlighter.js +++ b/client/coral-admin/src/components/CommentFormatter.js @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { matchLinks } from '../utils'; import memoize from 'lodash/memoize'; @@ -62,16 +63,43 @@ function markLinks(body) { return content; } -export default ({ suspectWords, bannedWords, body, ...rest }) => { - // First highlight links. - const content = markLinks(body).map((element, index) => { - // Keep highlighted links. - if (typeof element !== 'string') { - return element; - } +function format(body, { suspectWords, bannedWords, className = 'comment' }) { + // Breaking the body by line break + const textbreaks = body.split('\n'); - // Highlight suspect and banned phrase inside this part of text. - return markPhrases(element, suspectWords, bannedWords, index); - }); - return
{content}
; + return ( + + {textbreaks.map((line, i) => { + const content = markLinks(line).map((element, index) => { + // Keep highlighted links. + if (typeof element !== 'string') { + return element; + } + + // Highlight suspect and banned phrase inside this part of text. + return markPhrases(element, suspectWords, bannedWords, index); + }); + + return ( + + {content} + {i !== textbreaks.length - 1 && ( +
+ )} +
+ ); + })} +
+ ); +} + +const CommentFormatter = ({ body, settings, ...rest }) => { + return
{format(body, settings)}
; }; + +CommentFormatter.propTypes = { + settings: PropTypes.object, + body: PropTypes.string, +}; + +export default CommentFormatter; diff --git a/client/coral-admin/src/routes/Moderation/components/Comment.js b/client/coral-admin/src/routes/Moderation/components/Comment.js index fc6894a46..504c3e04d 100644 --- a/client/coral-admin/src/routes/Moderation/components/Comment.js +++ b/client/coral-admin/src/routes/Moderation/components/Comment.js @@ -8,7 +8,7 @@ import styles from './Comment.css'; import CommentLabels from 'coral-admin/src/components/CommentLabels'; import CommentAnimatedEdit from 'coral-admin/src/components/CommentAnimatedEdit'; import Slot from 'coral-framework/components/Slot'; -import CommentBodyHighlighter from 'coral-admin/src/components/CommentBodyHighlighter'; +import CommentFormatter from 'coral-admin/src/components/CommentFormatter'; import IfHasLink from 'coral-admin/src/components/IfHasLink'; import cn from 'classnames'; import ApproveButton from 'coral-admin/src/components/ApproveButton'; @@ -126,11 +126,14 @@ class Comment extends React.Component {
- {' '} + /> Date: Fri, 12 Jan 2018 15:51:49 -0300 Subject: [PATCH 3/9] Adding changes to the comments on the User Drawer Detail --- client/coral-admin/src/components/UserDetailComment.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/coral-admin/src/components/UserDetailComment.js b/client/coral-admin/src/components/UserDetailComment.js index b1cc486ec..669f035fe 100644 --- a/client/coral-admin/src/components/UserDetailComment.js +++ b/client/coral-admin/src/components/UserDetailComment.js @@ -5,7 +5,7 @@ import { Link } from 'react-router'; import { Icon } from 'coral-ui'; import CommentDetails from './CommentDetails'; import styles from './UserDetailComment.css'; -import CommentBodyHighlighter from 'coral-admin/src/components/CommentBodyHighlighter'; +import CommentFormatter from 'coral-admin/src/components/CommentFormatter'; import IfHasLink from 'coral-admin/src/components/IfHasLink'; import cn from 'classnames'; import CommentAnimatedEdit from './CommentAnimatedEdit'; @@ -78,9 +78,11 @@ class UserDetailComment extends React.Component {
- {' '} Date: Fri, 12 Jan 2018 15:53:32 -0300 Subject: [PATCH 4/9] Wrong name --- client/coral-admin/src/components/CommentAnimatedEdit.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/coral-admin/src/components/CommentAnimatedEdit.js b/client/coral-admin/src/components/CommentAnimatedEdit.js index 43fbf9fbf..12280b204 100644 --- a/client/coral-admin/src/components/CommentAnimatedEdit.js +++ b/client/coral-admin/src/components/CommentAnimatedEdit.js @@ -4,7 +4,7 @@ import { CSSTransitionGroup } from 'react-transition-group'; import styles from './CommentAnimatedEdit.css'; import PropTypes from 'prop-types'; -const CommentBodyHighlighter = ({ children, body }) => { +const CommentAnimatedEdit = ({ children, body }) => { return ( { ); }; -CommentBodyHighlighter.propTypes = { +CommentAnimatedEdit.propTypes = { children: PropTypes.node, body: PropTypes.string, }; -export default CommentBodyHighlighter; +export default CommentAnimatedEdit; From bfa2f6edb958df93e5a042b6123d384cbb229c73 Mon Sep 17 00:00:00 2001 From: okbel Date: Mon, 15 Jan 2018 12:59:39 -0300 Subject: [PATCH 5/9] Updates --- .../src/components/CommentFormatter.js | 18 +++++++++++------- .../src/components/UserDetailComment.js | 9 ++++----- .../routes/Moderation/components/Comment.js | 8 +++----- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/client/coral-admin/src/components/CommentFormatter.js b/client/coral-admin/src/components/CommentFormatter.js index 6c086b6aa..152ae8547 100644 --- a/client/coral-admin/src/components/CommentFormatter.js +++ b/client/coral-admin/src/components/CommentFormatter.js @@ -63,12 +63,18 @@ function markLinks(body) { return content; } -function format(body, { suspectWords, bannedWords, className = 'comment' }) { +const CommentFormatter = ( + body, + suspectWords, + bannedWords, + className = 'comment', + ...rest +) => { // Breaking the body by line break const textbreaks = body.split('\n'); return ( - + {textbreaks.map((line, i) => { const content = markLinks(line).map((element, index) => { // Keep highlighted links. @@ -91,14 +97,12 @@ function format(body, { suspectWords, bannedWords, className = 'comment' }) { })} ); -} - -const CommentFormatter = ({ body, settings, ...rest }) => { - return
{format(body, settings)}
; }; CommentFormatter.propTypes = { - settings: PropTypes.object, + className: PropTypes.string, + bannedWords: PropTypes.array, + suspectWords: PropTypes.array, body: PropTypes.string, }; diff --git a/client/coral-admin/src/components/UserDetailComment.js b/client/coral-admin/src/components/UserDetailComment.js index 669f035fe..2d98edcb3 100644 --- a/client/coral-admin/src/components/UserDetailComment.js +++ b/client/coral-admin/src/components/UserDetailComment.js @@ -79,12 +79,11 @@ class UserDetailComment extends React.Component {
{' '} + className="talk-admin-user-detail-comment" + />
Date: Mon, 15 Jan 2018 13:14:24 -0300 Subject: [PATCH 6/9] missing brackets --- client/coral-admin/src/components/CommentFormatter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/coral-admin/src/components/CommentFormatter.js b/client/coral-admin/src/components/CommentFormatter.js index 152ae8547..8874d123f 100644 --- a/client/coral-admin/src/components/CommentFormatter.js +++ b/client/coral-admin/src/components/CommentFormatter.js @@ -63,13 +63,13 @@ function markLinks(body) { return content; } -const CommentFormatter = ( +const CommentFormatter = ({ body, suspectWords, bannedWords, className = 'comment', ...rest -) => { +}) => { // Breaking the body by line break const textbreaks = body.split('\n'); From 45d4c79c265fe8f8290142a29a6d1db2791fd275 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 16 Jan 2018 12:30:38 -0700 Subject: [PATCH 7/9] added reply bug --- errors.js | 16 +++++- locales/en.yml | 1 + locales/es.yml | 1 + services/comments.js | 36 ++++++++---- test/.eslintrc.json | 2 +- test/server/graph/mutations/createComment.js | 2 + test/server/services/comments.js | 58 ++++++++++++++++++++ 7 files changed, 100 insertions(+), 16 deletions(-) diff --git a/errors.js b/errors.js index 868be0488..a19afe3c6 100644 --- a/errors.js +++ b/errors.js @@ -265,6 +265,15 @@ const ErrCannotIgnoreStaff = new APIError('Cannot ignore staff members.', { status: 400, }); +// ErrParentDoesNotVisible is returned when the user tries to reply to a comment +// that isn't visible. +const ErrParentDoesNotVisible = new APIError( + 'Cannot reply to a comment that is not visible', + { + translation_key: 'COMMENT_PARENT_NOT_VISIBLE', + } +); + module.exports = { APIError, ErrAlreadyExists, @@ -276,24 +285,25 @@ module.exports = { ErrContainsProfanity, ErrEditWindowHasEnded, ErrEmailTaken, + ErrEmailVerificationToken, ErrInstallLock, ErrInvalidAssetURL, ErrLoginAttemptMaximumExceeded, ErrMaxRateLimit, ErrMissingEmail, ErrMissingPassword, - ErrEmailVerificationToken, - ErrPasswordResetToken, ErrMissingUsername, ErrNotAuthorized, ErrNotFound, ErrNotVerified, + ErrParentDoesNotVisible, + ErrPasswordResetToken, ErrPasswordTooShort, ErrPermissionUpdateUsername, + ErrSameUsernameProvided, ErrSettingsInit, ErrSettingsNotInit, ErrSpecialChars, ErrUsernameTaken, - ErrSameUsernameProvided, ExtendableError, }; diff --git a/locales/en.yml b/locales/en.yml index 0da121202..39cd3f0d9 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -198,6 +198,7 @@ en: embedlink: copy: "Copy to Clipboard" error: + COMMENT_PARENT_NOT_VISIBLE: "The comment that you're replying to has been removed or doesn't exist." EMAIL_VERIFICATION_TOKEN_INVALID: "Email verification token is invalid." PASSWORD_RESET_TOKEN_INVALID: "Your password reset link is invalid." COMMENT_TOO_SHORT: "Comments should be more than one character, please revise your comment and try again." diff --git a/locales/es.yml b/locales/es.yml index 584e83f7b..010119728 100644 --- a/locales/es.yml +++ b/locales/es.yml @@ -182,6 +182,7 @@ es: embedlink: copy: "Copiar al portapapeles" error: + COMMENT_PARENT_NOT_VISIBLE: "El comentario a la que estás contestando ha sido eliminado o no existe." COMMENT_TOO_SHORT: "Tu comentario debe tener algo escrito" COMMENTING_CLOSED: "Los comentarios ya estan cerrados" confirm_password: "Las contraseñas no coinciden. Inténtelo nuevamente" diff --git a/services/comments.js b/services/comments.js index 344115054..d82f5046d 100644 --- a/services/comments.js +++ b/services/comments.js @@ -7,24 +7,35 @@ const SettingsService = require('./settings'); const cloneDeep = require('lodash/cloneDeep'); const errors = require('../errors'); const events = require('./events'); +const merge = require('lodash/merge'); const { COMMENTS_NEW, COMMENTS_EDIT } = require('./events/constants'); module.exports = class CommentsService { /** * Creates a new Comment that came from a public source. - * @param {Mixed} comment either a single comment or an array of comments. + * @param {Mixed} input either a single comment or an array of comments. * @return {Promise} */ - static async publicCreate(comment) { + static async publicCreate(input) { // Check to see if this is an array of comments, if so map it out. - if (Array.isArray(comment)) { - return Promise.all(comment.map(CommentsService.publicCreate)); + if (Array.isArray(input)) { + return Promise.all(input.map(CommentsService.publicCreate)); } - const { status = 'NONE' } = comment; + const { status = 'NONE', parent_id = null } = input; - const commentModel = new CommentModel( - Object.assign( + // Check to see if we are replying to a comment, and if that comment is + // visible. + if (parent_id !== null) { + const parent = await CommentModel.findOne({ id: parent_id }); + if (parent === null || !parent.visible) { + throw errors.ErrParentDoesNotVisible; + } + } + + // Turn the comment into a new object. + const comment = new CommentModel( + merge( { status_history: status ? [ @@ -36,21 +47,22 @@ module.exports = class CommentsService { : [], body_history: [ { - body: comment.body, + body: input.body, created_at: new Date(), }, ], }, - comment + input ) ); - const savedCommentModel = await commentModel.save(); + // Save the comment to the database. + await comment.save(); // Emit that the comment was created! - await events.emitAsync(COMMENTS_NEW, savedCommentModel); + await events.emitAsync(COMMENTS_NEW, comment); - return savedCommentModel; + return comment; } /** diff --git a/test/.eslintrc.json b/test/.eslintrc.json index 027872c7d..36b8a3078 100644 --- a/test/.eslintrc.json +++ b/test/.eslintrc.json @@ -7,6 +7,6 @@ ], "extends": "../.eslintrc.json", "rules": { - "mocha/no-exclusive-tests": "warn" + "mocha/no-exclusive-tests": "error" } } diff --git a/test/server/graph/mutations/createComment.js b/test/server/graph/mutations/createComment.js index 88ca3415a..8a5a98aa1 100644 --- a/test/server/graph/mutations/createComment.js +++ b/test/server/graph/mutations/createComment.js @@ -116,6 +116,8 @@ describe('graph.mutations.createComment', () => { } expect(data.createComment).to.have.property('errors').null; expect(data.createComment).to.have.property('comment').not.null; + expect(data.createComment.comment).to.have.property('id').not + .null; } } ); diff --git a/test/server/services/comments.js b/test/server/services/comments.js index c9ec1315d..b0af0b0e0 100644 --- a/test/server/services/comments.js +++ b/test/server/services/comments.js @@ -131,6 +131,64 @@ describe('services.CommentsService', () => { }); describe('#publicCreate()', () => { + describe('does not allow replies to comments that are not visible', () => { + it('parent not found', async () => { + try { + await CommentsService.publicCreate({ + body: 'This is a comment!', + status: 'ACCEPTED', + parent_id: 'does not exist', + }); + throw new Error('comment should not have been created'); + } catch (err) { + expect(err).to.have.property( + 'translation_key', + 'COMMENT_PARENT_NOT_VISIBLE' + ); + } + }); + + it('parent REJECTED', async () => { + try { + const parent = await CommentsService.publicCreate({ + body: 'This is a comment!', + status: 'REJECTED', + }); + await CommentsService.publicCreate({ + body: 'This is a comment!', + status: 'ACCEPTED', + parent_id: parent.id, + }); + throw new Error('comment should not have been created'); + } catch (err) { + expect(err).to.have.property( + 'translation_key', + 'COMMENT_PARENT_NOT_VISIBLE' + ); + } + }); + + it('parent SYSTEM_WITHHELD', async () => { + try { + const parent = await CommentsService.publicCreate({ + body: 'This is a comment!', + status: 'SYSTEM_WITHHELD', + }); + await CommentsService.publicCreate({ + body: 'This is a comment!', + status: 'ACCEPTED', + parent_id: parent.id, + }); + throw new Error('comment should not have been created'); + } catch (err) { + expect(err).to.have.property( + 'translation_key', + 'COMMENT_PARENT_NOT_VISIBLE' + ); + } + }); + }); + it('creates a new comment', async () => { const c = await CommentsService.publicCreate({ body: 'This is a comment!', From b709cb485e3ceec5e3cdca030ff1524b685e6848 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 16 Jan 2018 14:11:24 -0700 Subject: [PATCH 8/9] refactored publicCreate --- services/comments.js | 15 +++------- test/server/graph/queries/asset.js | 14 +++++---- test/server/services/assets.js | 46 ++++++++++++++++-------------- test/server/services/comments.js | 25 ---------------- 4 files changed, 36 insertions(+), 64 deletions(-) diff --git a/services/comments.js b/services/comments.js index d82f5046d..e18e04970 100644 --- a/services/comments.js +++ b/services/comments.js @@ -13,15 +13,11 @@ const { COMMENTS_NEW, COMMENTS_EDIT } = require('./events/constants'); module.exports = class CommentsService { /** * Creates a new Comment that came from a public source. - * @param {Mixed} input either a single comment or an array of comments. + * @param {Object} input either a single comment or an array of comments. * @return {Promise} */ static async publicCreate(input) { - // Check to see if this is an array of comments, if so map it out. - if (Array.isArray(input)) { - return Promise.all(input.map(CommentsService.publicCreate)); - } - + // Extract the parent_id from the comment, if there is one. const { status = 'NONE', parent_id = null } = input; // Check to see if we are replying to a comment, and if that comment is @@ -33,8 +29,8 @@ module.exports = class CommentsService { } } - // Turn the comment into a new object. - const comment = new CommentModel( + // Create the comment in the database. + const comment = await CommentModel.create( merge( { status_history: status @@ -56,9 +52,6 @@ module.exports = class CommentsService { ) ); - // Save the comment to the database. - await comment.save(); - // Emit that the comment was created! await events.emitAsync(COMMENTS_NEW, comment); diff --git a/test/server/graph/queries/asset.js b/test/server/graph/queries/asset.js index af19e4f69..f976bd37d 100644 --- a/test/server/graph/queries/asset.js +++ b/test/server/graph/queries/asset.js @@ -34,12 +34,14 @@ describe('graph.queries.asset', () => { username: 'usernameC', }, ]); - comments = await CommentsService.publicCreate( - [0, 0, 1, 1].map(idx => ({ - author_id: users[idx].id, - asset_id: assets[idx].id, - body: `hello there! ${String(Math.random()).slice(2)}`, - })) + comments = await Promise.all( + [0, 0, 1, 1].map(idx => + CommentsService.publicCreate({ + author_id: users[idx].id, + asset_id: assets[idx].id, + body: `hello there! ${String(Math.random()).slice(2)}`, + }) + ) ); }); diff --git a/test/server/services/assets.js b/test/server/services/assets.js index c071b5634..923f28751 100644 --- a/test/server/services/assets.js +++ b/test/server/services/assets.js @@ -176,28 +176,30 @@ describe('services.AssetsService', () => { ); // Create some comments on both assets. - await CommentsService.publicCreate([ - { - asset_id: '1', - body: 'This is a comment!', - status: 'ACCEPTED', - }, - { - asset_id: '1', - body: 'This is a comment!', - status: 'ACCEPTED', - }, - { - asset_id: '2', - body: 'This is a comment!', - status: 'ACCEPTED', - }, - { - asset_id: '2', - body: 'This is a comment!', - status: 'ACCEPTED', - }, - ]); + await Promise.all( + [ + { + asset_id: '1', + body: 'This is a comment!', + status: 'ACCEPTED', + }, + { + asset_id: '1', + body: 'This is a comment!', + status: 'ACCEPTED', + }, + { + asset_id: '2', + body: 'This is a comment!', + status: 'ACCEPTED', + }, + { + asset_id: '2', + body: 'This is a comment!', + status: 'ACCEPTED', + }, + ].map(comment => CommentsService.publicCreate(comment)) + ); // Merge all the comments from asset 1 into asset 2, followed by deleting // asset 1. diff --git a/test/server/services/comments.js b/test/server/services/comments.js index b0af0b0e0..2d0b37163 100644 --- a/test/server/services/comments.js +++ b/test/server/services/comments.js @@ -200,31 +200,6 @@ describe('services.CommentsService', () => { expect(c.id).to.be.uuid; expect(c.status).to.be.equal('ACCEPTED'); }); - - it('creates many new comments', async () => { - const [c1, c2, c3] = await CommentsService.publicCreate([ - { - body: 'This is a comment!', - status: 'ACCEPTED', - }, - { - body: 'This is another comment!', - }, - { - body: 'This is a rejected comment!', - status: 'REJECTED', - }, - ]); - - expect(c1).to.not.be.null; - expect(c1.status).to.be.equal('ACCEPTED'); - - expect(c2).to.not.be.null; - expect(c2.status).to.be.equal('NONE'); - - expect(c3).to.not.be.null; - expect(c3.status).to.be.equal('REJECTED'); - }); }); describe('#edit', () => { From 4960b34748d2e33f1e67b5d6e41a3ff83acc0f8a Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 17 Jan 2018 14:56:36 -0700 Subject: [PATCH 9/9] fixed deploy script --- scripts/docker.sh | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/scripts/docker.sh b/scripts/docker.sh index e1e3f9725..06f0cb279 100755 --- a/scripts/docker.sh +++ b/scripts/docker.sh @@ -68,17 +68,16 @@ then docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS fi - if [ "$CIRCLE_BRANCH" = "master" ] + # deploy based on the env + if [ -n "$CIRCLE_TAG" ] then - - # deploy based on the env - if [ -n "$CIRCLE_TAG" ] - then - deploy_tag - else - deploy_latest - fi + deploy_tag else - deploy_branch + if [ "$CIRCLE_BRANCH" = "master" ] + then + deploy_latest + else + deploy_branch + fi fi fi \ No newline at end of file