diff --git a/client/coral-framework/graphql/fragments/commentView.graphql b/client/coral-framework/graphql/fragments/commentView.graphql
index 575434a31..123ce9a09 100644
--- a/client/coral-framework/graphql/fragments/commentView.graphql
+++ b/client/coral-framework/graphql/fragments/commentView.graphql
@@ -5,6 +5,9 @@ fragment commentView on Comment {
body
created_at
status
+ tags {
+ name
+ }
user {
id
name: displayName
diff --git a/client/coral-plugin-stream/Stream.js b/client/coral-plugin-stream/Stream.js
index 633a98502..c62ef7081 100644
--- a/client/coral-plugin-stream/Stream.js
+++ b/client/coral-plugin-stream/Stream.js
@@ -58,6 +58,9 @@ const StreamQuery = gql`fragment commentView on Comment {
user {
name: displayName
}
+ tags {
+ name
+ }
actions {
type: action_type
count
diff --git a/client/coral-plugin-tag-label/TagLabel.js b/client/coral-plugin-tag-label/TagLabel.js
new file mode 100644
index 000000000..0bceca225
--- /dev/null
+++ b/client/coral-plugin-tag-label/TagLabel.js
@@ -0,0 +1,9 @@
+import React from 'react';
+
+import styles from './styles.css';
+
+const TagLabel = ({isStaff}) =>
+ {isStaff ? 'Staff' : ''}
+
;
+
+export default TagLabel;
diff --git a/client/coral-plugin-tag-label/styles.css b/client/coral-plugin-tag-label/styles.css
new file mode 100644
index 000000000..03dc3dbc8
--- /dev/null
+++ b/client/coral-plugin-tag-label/styles.css
@@ -0,0 +1,7 @@
+.staff {
+ background-color: #4C1066;
+ color: white;
+ display: inline-block;
+ margin: 10px 10px;
+ padding: 5px 5px;
+}
diff --git a/graph/mutators/comment.js b/graph/mutators/comment.js
index 2f18ae135..6214c9df8 100644
--- a/graph/mutators/comment.js
+++ b/graph/mutators/comment.js
@@ -36,6 +36,12 @@ const createComment = ({user, loaders: {Comments}}, {body, asset_id, parent_id =
}
}
+ if (user.hasRoles('ADMIN')) {
+ return CommentsService
+ .addTag(comment.id, 'STAFF', user.id)
+ .then(() => comment);
+ }
+
return comment;
});
};
diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql
index e231bc0c3..ddffcd708 100644
--- a/graph/typeDefs.graphql
+++ b/graph/typeDefs.graphql
@@ -5,6 +5,7 @@
# Date represented as an ISO8601 string.
scalar Date
+
################################################################################
## Users
################################################################################
@@ -45,6 +46,17 @@ type User {
comments(query: CommentsQuery): [Comment]
}
+type Tag {
+ # the actual tag for the comment.
+ name: String!
+
+ # the user that assigned the tag. If NULL then the system automatically tagged it.
+ assigned_by: String
+
+ # the time when the tag was assigned.
+ created_at: Date!
+}
+
################################################################################
## Comments
################################################################################
@@ -96,6 +108,9 @@ input CommentsQuery {
# skip results from the last created_at timestamp.
cursor: Date
+ # filter by a specific tag name.
+ tag: [String]
+
# sort the results by created_at.
sort: SORT_ORDER = REVERSE_CHRONOLOGICAL
}
@@ -109,7 +124,10 @@ type Comment {
# The actual comment data.
body: String!
- # The user who authored the comment.
+ # the tags on the comment
+ tags: [Tag]
+
+ # the user who authored the comment.
user: User
# the recent replies made against this comment.
diff --git a/models/comment.js b/models/comment.js
index b656f9238..ac44d904e 100644
--- a/models/comment.js
+++ b/models/comment.js
@@ -30,6 +30,24 @@ const StatusSchema = new Schema({
_id: false
});
+/**
+ * The Mongo schema for a Comment Tag.
+ * @type {Schema}
+ */
+const TagSchema = new Schema({
+ name: String,
+
+ // The User ID of the user that assigned the status.
+ assigned_by: {
+ type: String,
+ default: null
+ },
+
+ created_at: Date
+}, {
+ _id: false
+});
+
/**
* The Mongo schema for a Comment.
* @type {Schema}
@@ -49,6 +67,7 @@ const CommentSchema = new Schema({
author_id: String,
status_history: [StatusSchema],
status: {type: String, default: null},
+ tags: [TagSchema],
parent_id: String
}, {
timestamps: {
diff --git a/services/comments.js b/services/comments.js
index 5d39a129e..fc05341cb 100644
--- a/services/comments.js
+++ b/services/comments.js
@@ -3,6 +3,10 @@ const CommentModel = require('../models/comment');
const ActionModel = require('../models/action');
const ActionsService = require('./actions');
+const ALLOWED_TAGS = [
+ {name: 'STAFF'}
+];
+
module.exports = class CommentsService {
/**
@@ -33,6 +37,7 @@ module.exports = class CommentsService {
type: status,
created_at: new Date()
}] : [],
+ tags: [],
status,
author_id
});
@@ -40,6 +45,33 @@ module.exports = class CommentsService {
return comment.save();
}
+ /**
+ * Adds a tag if it doesn't already exist on the comment.
+ */
+ static addTag(id, name, assigned_by) {
+
+ if (ALLOWED_TAGS.find((t) => t.name === name) == null) {
+ return Promise.reject(new Error('tag not allowed'));
+ }
+
+ return CommentModel.update({
+ id,
+ tags: {
+ $ne: {
+ name
+ }
+ }
+ }, {
+ $push: {
+ tags: {
+ name,
+ assigned_by,
+ created_at: new Date()
+ }
+ }
+ });
+ }
+
/**
* Finds a comment by the id.
* @param {String} id identifier of comment (uuid)
diff --git a/test/services/comments.js b/test/services/comments.js
index fc2105cb1..c27050ae7 100644
--- a/test/services/comments.js
+++ b/test/services/comments.js
@@ -70,11 +70,14 @@ describe('services.CommentsService', () => {
const users = [{
email: 'stampi@gmail.com',
displayName: 'Stampi',
- password: '1Coral!!'
+ password: '1Coral!!',
+ roles: ['ADMIN'],
+ _id: '1'
}, {
email: 'sockmonster@gmail.com',
displayName: 'Sockmonster',
- password: '2Coral!!'
+ password: '2Coral!!',
+ _id : '2'
}];
const actions = [{
@@ -256,4 +259,40 @@ describe('services.CommentsService', () => {
});
});
+
+ describe('#tagByStaff()', () => {
+
+ it('creates a new comment by admin', () => {
+ return UsersService.findLocalUser('stampi@gmail.com', '1Coral!!').then((user) => {
+ return UsersService.addRoleToUser(user.id, 'ADMIN').then(() => {
+ UsersService.findById(user.id).then((u) => {
+ return CommentsService
+ .publicCreate({
+ body: 'This is a comment!',
+ status: 'ACCEPTED',
+ author_id: u.id
+ }).then((c) => {
+ expect(c).to.not.be.null;
+ expect(c.tags).to.not.have.length(0);
+ expect(c.tags[0].name).to.be.equal('STAFF');
+ });
+ });
+ });
+ });
+ });
+
+ it('creates a new comment by non admin', () => {
+ return UsersService.findLocalUser('sockmonster@gmail.com', '2Coral!!').then((user) => {
+ return CommentsService
+ .publicCreate({
+ body: 'This is a comment!',
+ status: 'ACCEPTED',
+ author_id: user.id
+ }).then((c) => {
+ expect(c).to.not.be.null;
+ expect(c.tags).to.have.length(0);
+ });
+ });
+ });
+ });
});