From f7ef09acbf9e6927c08ed1739d40ee0a5fdd991b Mon Sep 17 00:00:00 2001 From: ALau2088 Date: Sat, 18 May 2019 18:20:43 -0700 Subject: [PATCH 001/102] add UpdateComment mutation --- backend/src/resolvers/comments.js | 96 +++++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 12 deletions(-) diff --git a/backend/src/resolvers/comments.js b/backend/src/resolvers/comments.js index d4775b235..8023fb334 100644 --- a/backend/src/resolvers/comments.js +++ b/backend/src/resolvers/comments.js @@ -16,18 +16,22 @@ export default { delete params.postId if (!params.content || content.length < COMMENT_MIN_LENGTH) { - throw new UserInputError(`Comment must be at least ${COMMENT_MIN_LENGTH} character long!`) + throw new UserInputError( + `Comment must be at least ${COMMENT_MIN_LENGTH} character long!` + ) } if (!postId.trim()) { throw new UserInputError(NO_POST_ERR_MESSAGE) } const session = context.driver.session() - const postQueryRes = await session.run(` + const postQueryRes = await session.run( + ` MATCH (post:Post {id: $postId}) - RETURN post`, { - postId - } + RETURN post`, + { + postId + } ) const [post] = postQueryRes.records.map(record => { return record.get('post') @@ -36,20 +40,88 @@ export default { if (!post) { throw new UserInputError(NO_POST_ERR_MESSAGE) } - const comment = await neo4jgraphql(object, params, context, resolveInfo, false) + const comment = await neo4jgraphql( + object, + params, + context, + resolveInfo, + false + ) - await session.run(` + await session.run( + ` MATCH (post:Post {id: $postId}), (comment:Comment {id: $commentId}), (author:User {id: $userId}) MERGE (post)<-[:COMMENTS]-(comment)<-[:WROTE]-(author) - RETURN post`, { - userId: context.user.id, - postId, - commentId: comment.id - } + RETURN post`, + { + userId: context.user.id, + postId, + commentId: comment.id + } ) session.close() return comment + }, + UpdateComment: async (object, params, context, resolveInfo) => { + // Strip element tags and remove leading/trailing white spaces from content + const content = params.content.replace(/<(?:.|\n)*?>/gm, '').trim() + const { id } = params + console.log(id) + // Check length of content + if (!params.content || content.length < COMMENT_MIN_LENGTH) { + throw new UserInputError( + `Comment must be at least ${COMMENT_MIN_LENGTH} character long!` + ) + } + + // Check if comment exists + const session = context.driver.session() + const commentQueryRes = await session.run( + ` + MATCH (comment: COMMENT) + WHERE id(comment)=$id + RETURN comment`, + { + id + } + ) + console.log(id) + console.log(commentQueryRes) + // Destructure content from session results array + const [comment] = commentQueryRes.records.map(record => { + console.log(record.get('comment')) + return record.get('comment') + }) + + // Send error message if cannot find a matching comment. + // if (!comment) { + // throw new UserInputError(NO_COMMENT_ERR_MESSAGE) + // } + + // Update comment. + // const commentRev = await neo4jgraphql( + // object, + // params, + // context, + // resolveInfo, + // false + // ) + + // await session.run( + // ` + // MATCH (comment:Comment) + // WHERE id(comment)=$id, + // SET comment.content = $content + // `, + // { + // id, + // content + // } + // ) + session.close() + + // return commentRev } } } From 7ddbab83e98e09cfac41e3ed18ea2fb5eb468faa Mon Sep 17 00:00:00 2001 From: ALau2088 Date: Sat, 18 May 2019 23:32:24 -0700 Subject: [PATCH 002/102] 552-update_comment --- backend/src/resolvers/comments.js | 54 +++++++++++++++---------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/backend/src/resolvers/comments.js b/backend/src/resolvers/comments.js index 8023fb334..72d2f96fb 100644 --- a/backend/src/resolvers/comments.js +++ b/backend/src/resolvers/comments.js @@ -40,6 +40,7 @@ export default { if (!post) { throw new UserInputError(NO_POST_ERR_MESSAGE) } + const comment = await neo4jgraphql( object, params, @@ -67,7 +68,6 @@ export default { // Strip element tags and remove leading/trailing white spaces from content const content = params.content.replace(/<(?:.|\n)*?>/gm, '').trim() const { id } = params - console.log(id) // Check length of content if (!params.content || content.length < COMMENT_MIN_LENGTH) { throw new UserInputError( @@ -79,49 +79,47 @@ export default { const session = context.driver.session() const commentQueryRes = await session.run( ` - MATCH (comment: COMMENT) - WHERE id(comment)=$id + MATCH (comment: Comment { id:$id}) RETURN comment`, { id } ) - console.log(id) - console.log(commentQueryRes) + // Destructure content from session results array const [comment] = commentQueryRes.records.map(record => { - console.log(record.get('comment')) + const a = record.get('comment') + console.log(a) return record.get('comment') }) // Send error message if cannot find a matching comment. - // if (!comment) { - // throw new UserInputError(NO_COMMENT_ERR_MESSAGE) - // } + if (!comment) { + throw new UserInputError(NO_COMMENT_ERR_MESSAGE) + } // Update comment. - // const commentRev = await neo4jgraphql( - // object, - // params, - // context, - // resolveInfo, - // false - // ) + const commentRev = await neo4jgraphql( + object, + params, + context, + resolveInfo, + false + ) - // await session.run( - // ` - // MATCH (comment:Comment) - // WHERE id(comment)=$id, - // SET comment.content = $content - // `, - // { - // id, - // content - // } - // ) + await session.run( + ` + MATCH (comment: Comment { id:$id}) + SET comment.content = 'bbb' + `, + { + id, + content + } + ) session.close() - // return commentRev + return commentRev } } } From 3fe8e9a2886e3f5a1872b4e3612b2ad70aa639de Mon Sep 17 00:00:00 2001 From: ALau2088 Date: Sat, 18 May 2019 23:42:17 -0700 Subject: [PATCH 003/102] 552-update_comment --- backend/src/resolvers/comments.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/src/resolvers/comments.js b/backend/src/resolvers/comments.js index 72d2f96fb..6dea8e51b 100644 --- a/backend/src/resolvers/comments.js +++ b/backend/src/resolvers/comments.js @@ -88,8 +88,6 @@ export default { // Destructure content from session results array const [comment] = commentQueryRes.records.map(record => { - const a = record.get('comment') - console.log(a) return record.get('comment') }) @@ -110,7 +108,7 @@ export default { await session.run( ` MATCH (comment: Comment { id:$id}) - SET comment.content = 'bbb' + SET comment.content = $content `, { id, From 8a8ed5131e010e75b156b8a80cefe1618dcd9072 Mon Sep 17 00:00:00 2001 From: ALau2088 Date: Fri, 24 May 2019 08:03:39 -0700 Subject: [PATCH 004/102] refactor backend and add UI --- backend/src/middleware/validation/index.js | 21 ++- backend/src/resolvers/comments.js | 59 --------- webapp/components/Comment.vue | 29 ++++- webapp/components/ContentMenu.vue | 2 +- .../components/comments/CommentList/index.vue | 20 +-- .../comments/EditCommentForm/index.vue | 122 ++++++++++++++++++ 6 files changed, 170 insertions(+), 83 deletions(-) create mode 100644 webapp/components/comments/EditCommentForm/index.vue diff --git a/backend/src/middleware/validation/index.js b/backend/src/middleware/validation/index.js index de9be72e9..faf95beaf 100644 --- a/backend/src/middleware/validation/index.js +++ b/backend/src/middleware/validation/index.js @@ -22,10 +22,29 @@ const validateUrl = async (resolve, root, args, context, info) => { } } +const validateComment = async (resolve, root, args, context, info) => { + const COMMENT_MIN_LENGTH = 1 + const content = args.content.replace(/<(?:.|\n)*?>/gm, '').trim() + if (!args.content || content.length < COMMENT_MIN_LENGTH) { + throw new UserInputError( + `Comment must be at least ${COMMENT_MIN_LENGTH} character long!` + ) + } + const NO_POST_ERR_MESSAGE = 'Comment cannot be created without a post!' + const { postId } = args + if (!postId) { + throw new UserInputError(NO_POST_ERR_MESSAGE) + } + + return await resolve(root, args, context, info) +} + export default { Mutation: { CreateUser: validateUsername, UpdateUser: validateUsername, - CreateSocialMedia: validateUrl + CreateSocialMedia: validateUrl, + CreateComment: validateComment, + UpdateComment: validateComment } } diff --git a/backend/src/resolvers/comments.js b/backend/src/resolvers/comments.js index 6dea8e51b..f3cfea378 100644 --- a/backend/src/resolvers/comments.js +++ b/backend/src/resolvers/comments.js @@ -1,13 +1,9 @@ import { neo4jgraphql } from 'neo4j-graphql-js' import { UserInputError } from 'apollo-server' -const COMMENT_MIN_LENGTH = 1 -const NO_POST_ERR_MESSAGE = 'Comment cannot be created without a post!' - export default { Mutation: { CreateComment: async (object, params, context, resolveInfo) => { - const content = params.content.replace(/<(?:.|\n)*?>/gm, '').trim() const { postId } = params // Adding relationship from comment to post by passing in the postId, // but we do not want to create the comment with postId as an attribute @@ -15,15 +11,6 @@ export default { // before comment creation. delete params.postId - if (!params.content || content.length < COMMENT_MIN_LENGTH) { - throw new UserInputError( - `Comment must be at least ${COMMENT_MIN_LENGTH} character long!` - ) - } - if (!postId.trim()) { - throw new UserInputError(NO_POST_ERR_MESSAGE) - } - const session = context.driver.session() const postQueryRes = await session.run( ` @@ -65,38 +52,6 @@ export default { return comment }, UpdateComment: async (object, params, context, resolveInfo) => { - // Strip element tags and remove leading/trailing white spaces from content - const content = params.content.replace(/<(?:.|\n)*?>/gm, '').trim() - const { id } = params - // Check length of content - if (!params.content || content.length < COMMENT_MIN_LENGTH) { - throw new UserInputError( - `Comment must be at least ${COMMENT_MIN_LENGTH} character long!` - ) - } - - // Check if comment exists - const session = context.driver.session() - const commentQueryRes = await session.run( - ` - MATCH (comment: Comment { id:$id}) - RETURN comment`, - { - id - } - ) - - // Destructure content from session results array - const [comment] = commentQueryRes.records.map(record => { - return record.get('comment') - }) - - // Send error message if cannot find a matching comment. - if (!comment) { - throw new UserInputError(NO_COMMENT_ERR_MESSAGE) - } - - // Update comment. const commentRev = await neo4jgraphql( object, params, @@ -104,20 +59,6 @@ export default { resolveInfo, false ) - - await session.run( - ` - MATCH (comment: Comment { id:$id}) - SET comment.content = $content - `, - { - id, - content - } - ) - session.close() - - return commentRev } } } diff --git a/webapp/components/Comment.vue b/webapp/components/Comment.vue index 13edc9c0d..906575880 100644 --- a/webapp/components/Comment.vue +++ b/webapp/components/Comment.vue @@ -22,15 +22,20 @@ :resource="comment" style="float-right" :is-owner="isAuthor(author.id)" + v-on:showEditCommentMenu="editCommentMenu" /> - -
+ +
+ +
+
@@ -39,13 +44,22 @@ import { mapGetters } from 'vuex' import HcUser from '~/components/User' import ContentMenu from '~/components/ContentMenu' +import HcEditCommentForm from '~/components/comments/EditCommentForm' +import gql from 'graphql-tag' export default { components: { HcUser, - ContentMenu + ContentMenu, + HcEditCommentForm + }, + data() { + return { + openEditCommentMenu: false + } }, props: { + post: { type: Object, default: () => {} }, comment: { type: Object, default() { @@ -69,6 +83,9 @@ export default { methods: { isAuthor(id) { return this.user.id === id + }, + editCommentMenu(showMenu) { + this.openEditCommentMenu = showMenu } } } diff --git a/webapp/components/ContentMenu.vue b/webapp/components/ContentMenu.vue index 29473d6b2..5f4cd54e7 100644 --- a/webapp/components/ContentMenu.vue +++ b/webapp/components/ContentMenu.vue @@ -90,7 +90,7 @@ export default { name: this.$t(`comment.edit`), callback: () => { /* eslint-disable-next-line no-console */ - console.log('EDIT COMMENT') + this.$emit('showEditCommentMenu', true) }, icon: 'edit' }) diff --git a/webapp/components/comments/CommentList/index.vue b/webapp/components/comments/CommentList/index.vue index 57b720087..5a682bba6 100644 --- a/webapp/components/comments/CommentList/index.vue +++ b/webapp/components/comments/CommentList/index.vue @@ -12,23 +12,11 @@ >{{ comments.length }}  Comments - -
- + +
+
- +
From 8c7009097a274c04fa22631429b1c7f57c90cad4 Mon Sep 17 00:00:00 2001 From: ALau2088 Date: Fri, 24 May 2019 08:22:56 -0700 Subject: [PATCH 005/102] 552-update_comment --- webapp/components/comments/EditCommentForm/index.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/webapp/components/comments/EditCommentForm/index.vue b/webapp/components/comments/EditCommentForm/index.vue index 216e47604..562b80b48 100644 --- a/webapp/components/comments/EditCommentForm/index.vue +++ b/webapp/components/comments/EditCommentForm/index.vue @@ -97,7 +97,6 @@ export default { this.disabled = false this.$emit('showEditCommentMenu', false) }) - //@Todo close edit comment window .catch(err => { this.$toast.error(err.message) }) From a5f6390f97a2b9e9ec614bc43e4b023b9540e35e Mon Sep 17 00:00:00 2001 From: ALau2088 Date: Thu, 30 May 2019 15:47:55 -0700 Subject: [PATCH 006/102] 552-update_comment --- backend/src/middleware/validation/index.js | 7 ++++--- backend/src/resolvers/comments.js | 10 +++------- webapp/package.json | 2 +- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/backend/src/middleware/validation/index.js b/backend/src/middleware/validation/index.js index faf95beaf..a7238ab03 100644 --- a/backend/src/middleware/validation/index.js +++ b/backend/src/middleware/validation/index.js @@ -7,7 +7,9 @@ const validateUsername = async (resolve, root, args, context, info) => { /* eslint-disable-next-line no-return-await */ return await resolve(root, args, context, info) } else { - throw new UserInputError(`Username must be at least ${USERNAME_MIN_LENGTH} characters long!`) + throw new UserInputError( + `Username must be at least ${USERNAME_MIN_LENGTH} characters long!` + ) } } @@ -35,8 +37,7 @@ const validateComment = async (resolve, root, args, context, info) => { if (!postId) { throw new UserInputError(NO_POST_ERR_MESSAGE) } - - return await resolve(root, args, context, info) + return resolve(root, args, context, info) } export default { diff --git a/backend/src/resolvers/comments.js b/backend/src/resolvers/comments.js index f3cfea378..2515ba36a 100644 --- a/backend/src/resolvers/comments.js +++ b/backend/src/resolvers/comments.js @@ -1,6 +1,8 @@ import { neo4jgraphql } from 'neo4j-graphql-js' import { UserInputError } from 'apollo-server' +const NO_POST_ERR_MESSAGE = 'Comment cannot be created without a post!' + export default { Mutation: { CreateComment: async (object, params, context, resolveInfo) => { @@ -52,13 +54,7 @@ export default { return comment }, UpdateComment: async (object, params, context, resolveInfo) => { - const commentRev = await neo4jgraphql( - object, - params, - context, - resolveInfo, - false - ) + await neo4jgraphql(object, params, context, resolveInfo, false) } } } diff --git a/webapp/package.json b/webapp/package.json index a3e8b44b9..62007112d 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -104,4 +104,4 @@ "vue-jest": "~3.0.4", "vue-svg-loader": "~0.12.0" } -} \ No newline at end of file +} From 788979eedfd82cda726ba2d69b821c43c98fd6bf Mon Sep 17 00:00:00 2001 From: ALau2088 Date: Wed, 5 Jun 2019 18:12:22 -0700 Subject: [PATCH 007/102] add update comment jest test --- backend/src/middleware/validation/index.js | 3 ++- backend/src/resolvers/comments.spec.js | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/backend/src/middleware/validation/index.js b/backend/src/middleware/validation/index.js index a7238ab03..2da25a605 100644 --- a/backend/src/middleware/validation/index.js +++ b/backend/src/middleware/validation/index.js @@ -37,7 +37,8 @@ const validateComment = async (resolve, root, args, context, info) => { if (!postId) { throw new UserInputError(NO_POST_ERR_MESSAGE) } - return resolve(root, args, context, info) + /* eslint-disable-next-line no-return-await */ + return await resolve(root, args, context, info) } export default { diff --git a/backend/src/resolvers/comments.spec.js b/backend/src/resolvers/comments.spec.js index 87a0df270..952850759 100644 --- a/backend/src/resolvers/comments.spec.js +++ b/backend/src/resolvers/comments.spec.js @@ -175,4 +175,24 @@ describe('CreateComment', () => { expect(Comment).toEqual([{ postId: null }]) }) }) + +describe('UpdateComment', () => { + const updateCommentMutation = ` + mutation($postId: ID, $content: String!, $id: ID!) { + UpdateComment(postId: $postId, content: $content, id: $id) { + id + content + } + } + ` + updateCommentVariables = { + postId: 'p1', + content: 'Comment is updated', + id: 'c8' + } + + it('updates a comment', async () = { + + }) +}) }) From 8d4ed065c7c5984e9a764ac4ac976cf2c7030751 Mon Sep 17 00:00:00 2001 From: ALau2088 Date: Wed, 5 Jun 2019 18:42:17 -0700 Subject: [PATCH 008/102] add post.comment.updated --- webapp/components/comments/EditCommentForm/index.vue | 2 +- webapp/locales/en.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/webapp/components/comments/EditCommentForm/index.vue b/webapp/components/comments/EditCommentForm/index.vue index 562b80b48..5a28062a9 100644 --- a/webapp/components/comments/EditCommentForm/index.vue +++ b/webapp/components/comments/EditCommentForm/index.vue @@ -93,7 +93,7 @@ export default { .then(res => { this.loading = false this.$root.$emit('refetchPostComments') - this.$toast.success(this.$t('post.comment.submitted')) + this.$toast.success(this.$t('post.comment.updated')) this.disabled = false this.$emit('showEditCommentMenu', false) }) diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 186b31d41..975917c78 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -145,7 +145,8 @@ }, "comment": { "submit": "Comment", - "submitted": "Comment Submitted" + "submitted": "Comment Submitted", + "updated": "Comment Updated" } }, "quotes": { From 0401e3b92947ae8319f4b6238e2da0de0edd9359 Mon Sep 17 00:00:00 2001 From: ALau2088 Date: Thu, 6 Jun 2019 15:58:32 -0700 Subject: [PATCH 009/102] add update comment to comments.spec --- backend/src/resolvers/comments.spec.js | 59 ++++++++++++++++---------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/backend/src/resolvers/comments.spec.js b/backend/src/resolvers/comments.spec.js index 952850759..7784a889f 100644 --- a/backend/src/resolvers/comments.spec.js +++ b/backend/src/resolvers/comments.spec.js @@ -20,7 +20,7 @@ afterEach(async () => { await factory.cleanDatabase() }) -describe('CreateComment', () => { +describe('Comment Functionality', () => { const createCommentMutation = ` mutation($postId: ID, $content: String!) { CreateComment(postId: $postId, content: $content) { @@ -78,7 +78,37 @@ describe('CreateComment', () => { } } - await expect(client.request(createCommentMutation, createCommentVariables)).resolves.toMatchObject(expected) + await expect( + client.request(createCommentMutation, createCommentVariables) + ).resolves.toMatchObject(expected) + }) + + it('updates a comment', async () => { + await client.request(createCommentMutation, createCommentVariables) + + const updateCommentMutation = ` + mutation($postId: ID, $content: String!, $id: ID!) { + UpdateComment(postId: $postId, content: $content, id: $id) { + id + content + } + } + ` + let updateCommentVariables = { + postId: 'p1', + content: 'Comment is updated', + id: 'c8' + } + + const expected = { + UpdateComment: { + content: 'Comment is updated' + } + } + + await expect( + client.request(updateCommentMutation, updateCommentVariables) + ).resolves.toMatchObject(expected) }) it('assigns the authenticated user as author', async () => { @@ -171,28 +201,11 @@ describe('CreateComment', () => { } await client.request(createCommentMutation, createCommentVariables) - const { Comment } = await client.request(commentQueryForPostId, commentQueryVariablesByContent) + const { Comment } = await client.request( + commentQueryForPostId, + commentQueryVariablesByContent + ) expect(Comment).toEqual([{ postId: null }]) }) }) - -describe('UpdateComment', () => { - const updateCommentMutation = ` - mutation($postId: ID, $content: String!, $id: ID!) { - UpdateComment(postId: $postId, content: $content, id: $id) { - id - content - } - } - ` - updateCommentVariables = { - postId: 'p1', - content: 'Comment is updated', - id: 'c8' - } - - it('updates a comment', async () = { - - }) -}) }) From ae9d508ada81ded368cd6adf6463b30d0ea8d410 Mon Sep 17 00:00:00 2001 From: ALau2088 Date: Sat, 8 Jun 2019 15:59:15 -0700 Subject: [PATCH 010/102] Revise "updated":"Changes Saved" --- webapp/locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 50a7b497d..521f681c0 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -154,7 +154,7 @@ "comment": { "submit": "Comment", "submitted": "Comment Submitted", - "updated": "Comment Updated" + "updated": "Changes Saved" } }, "comment": { From 3cc05c0916f66fc7290fe63c62f527e5b7db6752 Mon Sep 17 00:00:00 2001 From: ALau2088 Date: Thu, 13 Jun 2019 13:00:20 -0700 Subject: [PATCH 011/102] add backend managecomments tests --- .../src/middleware/permissionsMiddleware.js | 1 + backend/src/middleware/validation/index.js | 10 +- backend/src/schema/resolvers/comments.js | 9 + backend/src/schema/resolvers/comments.spec.js | 199 ++++++++++++------ 4 files changed, 147 insertions(+), 72 deletions(-) diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index bc9b4c525..83f5cd29b 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -113,6 +113,7 @@ const permissions = shield({ enable: isModerator, disable: isModerator, CreateComment: isAuthenticated, + UpdateComment: isAuthor, DeleteComment: isAuthor, // CreateUser: allow, }, diff --git a/backend/src/middleware/validation/index.js b/backend/src/middleware/validation/index.js index ec69b9a3a..e296bb866 100644 --- a/backend/src/middleware/validation/index.js +++ b/backend/src/middleware/validation/index.js @@ -22,17 +22,12 @@ const validateUrl = async (resolve, root, args, context, info) => { } } -const validateComment = async (resolve, root, args, context, info) => { +const validateUpdateComment = async (resolve, root, args, context, info) => { const COMMENT_MIN_LENGTH = 1 const content = args.content.replace(/<(?:.|\n)*?>/gm, '').trim() if (!args.content || content.length < COMMENT_MIN_LENGTH) { throw new UserInputError(`Comment must be at least ${COMMENT_MIN_LENGTH} character long!`) } - const NO_POST_ERR_MESSAGE = 'Comment cannot be created without a post!' - const { postId } = args - if (!postId) { - throw new UserInputError(NO_POST_ERR_MESSAGE) - } /* eslint-disable-next-line no-return-await */ return await resolve(root, args, context, info) } @@ -42,7 +37,6 @@ export default { CreateUser: validateUsername, UpdateUser: validateUsername, CreateSocialMedia: validateUrl, - CreateComment: validateComment, - UpdateComment: validateComment, + UpdateComment: validateUpdateComment, }, } diff --git a/backend/src/schema/resolvers/comments.js b/backend/src/schema/resolvers/comments.js index f28ef2792..8bca3bb98 100644 --- a/backend/src/schema/resolvers/comments.js +++ b/backend/src/schema/resolvers/comments.js @@ -1,11 +1,13 @@ import { neo4jgraphql } from 'neo4j-graphql-js' import { UserInputError } from 'apollo-server' +const COMMENT_MIN_LENGTH = 1 const NO_POST_ERR_MESSAGE = 'Comment cannot be created without a post!' export default { Mutation: { CreateComment: async (object, params, context, resolveInfo) => { + const content = params.content.replace(/<(?:.|\n)*?>/gm, '').trim() const { postId } = params // Adding relationship from comment to post by passing in the postId, // but we do not want to create the comment with postId as an attribute @@ -13,6 +15,13 @@ export default { // before comment creation. delete params.postId + if (!params.content || content.length < COMMENT_MIN_LENGTH) { + throw new UserInputError(`Comment must be at least ${COMMENT_MIN_LENGTH} character long!`) + } + if (!postId.trim()) { + throw new UserInputError(NO_POST_ERR_MESSAGE) + } + const session = context.driver.session() const postQueryRes = await session.run( ` diff --git a/backend/src/schema/resolvers/comments.spec.js b/backend/src/schema/resolvers/comments.spec.js index 55b946bb9..00d139a62 100644 --- a/backend/src/schema/resolvers/comments.spec.js +++ b/backend/src/schema/resolvers/comments.spec.js @@ -9,9 +9,10 @@ let createCommentVariables let createPostVariables let createCommentVariablesSansPostId let createCommentVariablesWithNonExistentPost +let asAuthor beforeEach(async () => { - await factory.create('User', { + asAuthor = await factory.create('User', { email: 'test@example.org', password: '1234', }) @@ -211,22 +212,9 @@ describe('CreateComment', () => { }) }) -describe('DeleteComment', () => { - const deleteCommentMutation = gql` - mutation($id: ID!) { - DeleteComment(id: $id) { - id - } - } - ` - - let deleteCommentVariables = { - id: 'c1', - } - +describe('ManageComments', () => { beforeEach(async () => { - const asAuthor = Factory() - await asAuthor.create('User', { + asAuthor = await factory.create('User', { email: 'author@example.org', password: '1234', }) @@ -245,55 +233,138 @@ describe('DeleteComment', () => { }) }) - describe('unauthenticated', () => { - it('throws authorization error', async () => { - client = new GraphQLClient(host) - await expect(client.request(deleteCommentMutation, deleteCommentVariables)).rejects.toThrow( - 'Not Authorised', - ) - }) - }) - - describe('authenticated but not the author', () => { - beforeEach(async () => { - let headers - headers = await login({ - email: 'test@example.org', - password: '1234', - }) - client = new GraphQLClient(host, { - headers, - }) - }) - - it('throws authorization error', async () => { - await expect(client.request(deleteCommentMutation, deleteCommentVariables)).rejects.toThrow( - 'Not Authorised', - ) - }) - }) - - describe('authenticated as author', () => { - beforeEach(async () => { - let headers - headers = await login({ - email: 'author@example.org', - password: '1234', - }) - client = new GraphQLClient(host, { - headers, - }) - }) - - it('deletes the comment', async () => { - const expected = { - DeleteComment: { - id: 'c1', - }, + describe('UpdateComment', () => { + const updateCommentMutation = gql` + mutation($content: String!, $id: ID!) { + UpdateComment(content: $content, id: $id) { + id + content + } } - await expect(client.request(deleteCommentMutation, deleteCommentVariables)).resolves.toEqual( - expected, - ) + ` + + let updateCommentVariables = { + id: 'c1', + content: 'The comment is updated', + } + + describe('unauthenticated', () => { + it('throws authorization error', async () => { + client = new GraphQLClient(host) + await expect(client.request(updateCommentMutation, updateCommentVariables)).rejects.toThrow( + 'Not Authorised', + ) + }) + }) + + describe('authenticated but not the author', () => { + beforeEach(async () => { + let headers + headers = await login({ + email: 'test@example.org', + password: '1234', + }) + client = new GraphQLClient(host, { + headers, + }) + }) + + it('throws authorization error', async () => { + await expect(client.request(updateCommentMutation, updateCommentVariables)).rejects.toThrow( + 'Not Authorised', + ) + }) + }) + + describe('authenticated as author', () => { + beforeEach(async () => { + let headers + headers = await login({ + email: 'author@example.org', + password: '1234', + }) + client = new GraphQLClient(host, { + headers, + }) + }) + + it('updates the comment', async () => { + const expected = { + UpdateComment: { + id: 'c1', + content: 'The comment is updated', + }, + } + await expect( + client.request(updateCommentMutation, updateCommentVariables), + ).resolves.toEqual(expected) + }) + }) + }) + + describe('DeleteComment', () => { + const deleteCommentMutation = gql` + mutation($id: ID!) { + DeleteComment(id: $id) { + id + } + } + ` + + let deleteCommentVariables = { + id: 'c1', + } + + describe('unauthenticated', () => { + it('throws authorization error', async () => { + client = new GraphQLClient(host) + await expect(client.request(deleteCommentMutation, deleteCommentVariables)).rejects.toThrow( + 'Not Authorised', + ) + }) + }) + + describe('authenticated but not the author', () => { + beforeEach(async () => { + let headers + headers = await login({ + email: 'test@example.org', + password: '1234', + }) + client = new GraphQLClient(host, { + headers, + }) + }) + + it('throws authorization error', async () => { + await expect(client.request(deleteCommentMutation, deleteCommentVariables)).rejects.toThrow( + 'Not Authorised', + ) + }) + }) + + describe('authenticated as author', () => { + beforeEach(async () => { + let headers + headers = await login({ + email: 'author@example.org', + password: '1234', + }) + client = new GraphQLClient(host, { + headers, + }) + }) + + it('deletes the comment', async () => { + const expected = { + DeleteComment: { + id: 'c1', + }, + } + await expect( + client.request(deleteCommentMutation, deleteCommentVariables), + ).resolves.toEqual(expected) + }) }) }) }) From 912b94d43c829622fb637fa0318d0675fd992fb8 Mon Sep 17 00:00:00 2001 From: ALau2088 Date: Thu, 27 Jun 2019 10:59:53 -0700 Subject: [PATCH 012/102] add EditCommentForm spec.js --- backend/src/schema/resolvers/comments.js | 3 -- webapp/components/Comment.vue | 8 +-- webapp/components/ContentMenu.vue | 4 +- .../components/comments/CommentList/index.vue | 11 ++-- .../comments/EditCommentForm/index.vue | 50 +++++++++---------- 5 files changed, 36 insertions(+), 40 deletions(-) diff --git a/backend/src/schema/resolvers/comments.js b/backend/src/schema/resolvers/comments.js index be257d912..d2e296596 100644 --- a/backend/src/schema/resolvers/comments.js +++ b/backend/src/schema/resolvers/comments.js @@ -74,9 +74,6 @@ export default { session.close() return commentReturnedWithAuthor }, - UpdateComment: async (object, params, context, resolveInfo) => { - await neo4jgraphql(object, params, context, resolveInfo, false) - }, DeleteComment: async (object, params, context, resolveInfo) => { const comment = await neo4jgraphql(object, params, context, resolveInfo, false) diff --git a/webapp/components/Comment.vue b/webapp/components/Comment.vue index a119328f1..a134787ff 100644 --- a/webapp/components/Comment.vue +++ b/webapp/components/Comment.vue @@ -1,13 +1,13 @@ diff --git a/webapp/components/ContentMenu.vue b/webapp/components/ContentMenu.vue index 8f5fe1eff..cb164678a 100644 --- a/webapp/components/ContentMenu.vue +++ b/webapp/components/ContentMenu.vue @@ -3,7 +3,7 @@ @@ -16,7 +16,7 @@ :parents="item.parents" @click.stop.prevent="openItem(item.route, toggleMenu)" > - + {{ item.route.name }} diff --git a/webapp/components/comments/CommentList/index.vue b/webapp/components/comments/CommentList/index.vue index 8f1cbc3d0..d1c91fef0 100644 --- a/webapp/components/comments/CommentList/index.vue +++ b/webapp/components/comments/CommentList/index.vue @@ -2,17 +2,20 @@

- + {{ comments.length }}  Comments + > + {{ comments.length }} + +   Comments

- +
- +
From 760f899379e074ddf84612fb34d5a3735d3ffa71 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 1 Jul 2019 16:18:14 +0200 Subject: [PATCH 013/102] import emotions --- .../migration/neo4j/emotions/delete.cql | 1 + .../migration/neo4j/emotions/emotions.cql | 57 +++++++++++++------ .../migration/neo4j/import.sh | 6 +- 3 files changed, 44 insertions(+), 20 deletions(-) diff --git a/deployment/legacy-migration/maintenance-worker/migration/neo4j/emotions/delete.cql b/deployment/legacy-migration/maintenance-worker/migration/neo4j/emotions/delete.cql index e69de29bb..18fb6699f 100644 --- a/deployment/legacy-migration/maintenance-worker/migration/neo4j/emotions/delete.cql +++ b/deployment/legacy-migration/maintenance-worker/migration/neo4j/emotions/delete.cql @@ -0,0 +1 @@ +MATCH (u:User)-[e:EMOTED]->(c:Post) DETACH DELETE e; \ No newline at end of file diff --git a/deployment/legacy-migration/maintenance-worker/migration/neo4j/emotions/emotions.cql b/deployment/legacy-migration/maintenance-worker/migration/neo4j/emotions/emotions.cql index 8aad9e923..06341f277 100644 --- a/deployment/legacy-migration/maintenance-worker/migration/neo4j/emotions/emotions.cql +++ b/deployment/legacy-migration/maintenance-worker/migration/neo4j/emotions/emotions.cql @@ -5,31 +5,54 @@ // [-] Omitted in Nitro // [?] Unclear / has work to be done for Nitro { -[ ] userId: { -[ ] type: String, -[ ] required: true, +[X] userId: { +[X] type: String, +[X] required: true, [-] index: true }, -[ ] contributionId: { -[ ] type: String, -[ ] required: true, +[X] contributionId: { +[X] type: String, +[X] required: true, [-] index: true }, -[ ] rated: { -[ ] type: String, +[?] rated: { +[X] type: String, [ ] required: true, -[ ] enum: ['funny', 'happy', 'surprised', 'cry', 'angry'] +[?] enum: ['funny', 'happy', 'surprised', 'cry', 'angry'] }, -[ ] createdAt: { -[ ] type: Date, -[ ] default: Date.now +[X] createdAt: { +[X] type: Date, +[X] default: Date.now }, -[ ] updatedAt: { -[ ] type: Date, -[ ] default: Date.now +[X] updatedAt: { +[X] type: Date, +[X] default: Date.now }, -[ ] wasSeeded: { type: Boolean } +[-] wasSeeded: { type: Boolean } } */ -CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as emotion; +CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as emotion +MATCH (u:User {id: emotion.userId}), + (c:Post {id: emotion.contributionId}) +MERGE (u)-[e:EMOTED { + id: emotion._id["$oid"], + emotion: emotion.rated, + createdAt: datetime(emotion.createdAt.`$date`), + updatedAt: datetime(emotion.updatedAt.`$date`) + }]->(c) +RETURN e; +/* + // Queries + // user sets an emotion emotion: + // MERGE (u)-[e:EMOTED {id: ..., emotion: "funny", createdAt: ..., updatedAt: ...}]->(c) + // user removes emotion + // MATCH (u)-[e:EMOTED]->(c) DELETE e + // contribution distributions over every `emotion` property value for one post + // MATCH (u:User)-[e:EMOTED]->(c:Post {id: "5a70bbc8508f5b000b443b1a"}) RETURN e.emotion,COUNT(e.emotion) + // contribution distributions over every `emotion` property value for one user (advanced - "whats the primary emotion used by the user?") + // MATCH (u:User{id:"5a663b1ac64291000bf302a1"})-[e:EMOTED]->(c:Post) RETURN e.emotion,COUNT(e.emotion) + // contribution distributions over every `emotion` property value for all posts created by one user (advanced - "how do others react to my contributions?") + // MATCH (u:User)-[e:EMOTED]->(c:Post)<-[w:WROTE]-(a:User{id:"5a663b1ac64291000bf302a1"}) RETURN e.emotion,COUNT(e.emotion) + // if we can filter the above an a variable timescale that would be great (should be possible on createdAt and updatedAt fields) +*/ \ No newline at end of file diff --git a/deployment/legacy-migration/maintenance-worker/migration/neo4j/import.sh b/deployment/legacy-migration/maintenance-worker/migration/neo4j/import.sh index 8eef68c92..cef2846a7 100755 --- a/deployment/legacy-migration/maintenance-worker/migration/neo4j/import.sh +++ b/deployment/legacy-migration/maintenance-worker/migration/neo4j/import.sh @@ -60,8 +60,8 @@ delete_collection "contributions" "contributions_post" delete_collection "contributions" "contributions_cando" delete_collection "shouts" "shouts" delete_collection "comments" "comments" +delete_collection "emotions" "emotions" -#delete_collection "emotions" #delete_collection "invites" #delete_collection "notifications" #delete_collection "organizations" @@ -82,12 +82,12 @@ import_collection "users" "users/users.cql" import_collection "follows_users" "follows/follows.cql" #import_collection "follows_organizations" "follows/follows.cql" import_collection "contributions_post" "contributions/contributions.cql" -import_collection "contributions_cando" "contributions/contributions.cql" +#import_collection "contributions_cando" "contributions/contributions.cql" #import_collection "contributions_DELETED" "contributions/contributions.cql" import_collection "shouts" "shouts/shouts.cql" import_collection "comments" "comments/comments.cql" +import_collection "emotions" "emotions/emotions.cql" -# import_collection "emotions" # import_collection "invites" # import_collection "notifications" # import_collection "organizations" From 44205e72687a48752bbf055689f32f2d25f94e23 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 1 Jul 2019 16:18:49 +0200 Subject: [PATCH 014/102] delete follows correctly --- .../maintenance-worker/migration/neo4j/follows/delete.cql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/legacy-migration/maintenance-worker/migration/neo4j/follows/delete.cql b/deployment/legacy-migration/maintenance-worker/migration/neo4j/follows/delete.cql index 3624448c3..3de01f8ea 100644 --- a/deployment/legacy-migration/maintenance-worker/migration/neo4j/follows/delete.cql +++ b/deployment/legacy-migration/maintenance-worker/migration/neo4j/follows/delete.cql @@ -1 +1 @@ -// this is just a relation between users(?) - no need to delete \ No newline at end of file +MATCH (u1:User)-[f:FOLLOWS]->(u2:User) DETACH DELETE f; \ No newline at end of file From a264ff8eee2837d5d742bdfb6893e720779efc3a Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 1 Jul 2019 16:21:11 +0200 Subject: [PATCH 015/102] emotion data modelling --- backend/src/schema/types/type/EMOTED.gql | 10 ++++++++++ backend/src/schema/types/type/Post.gql | 2 ++ backend/src/schema/types/type/User.gql | 2 ++ 3 files changed, 14 insertions(+) create mode 100644 backend/src/schema/types/type/EMOTED.gql diff --git a/backend/src/schema/types/type/EMOTED.gql b/backend/src/schema/types/type/EMOTED.gql new file mode 100644 index 000000000..4c79d88e3 --- /dev/null +++ b/backend/src/schema/types/type/EMOTED.gql @@ -0,0 +1,10 @@ +type EMOTED @relation(name: "EMOTED") { + from: User + to: Post + + emotion: String + #createdAt: DateTime + #updatedAt: DateTime + createdAt: String + updatedAt: String +} \ No newline at end of file diff --git a/backend/src/schema/types/type/Post.gql b/backend/src/schema/types/type/Post.gql index 271d92750..4b5d67572 100644 --- a/backend/src/schema/types/type/Post.gql +++ b/backend/src/schema/types/type/Post.gql @@ -48,4 +48,6 @@ type Post { RETURN COUNT(u) >= 1 """ ) + + emotions: [EMOTED] } diff --git a/backend/src/schema/types/type/User.gql b/backend/src/schema/types/type/User.gql index 6836f16fe..63151db8d 100644 --- a/backend/src/schema/types/type/User.gql +++ b/backend/src/schema/types/type/User.gql @@ -77,4 +77,6 @@ type User { badges: [Badge]! @relation(name: "REWARDED", direction: "IN") badgesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)") + + emotions: [EMOTED] } From 07b8116447c48c877aaaecd2330056fb9ce193a5 Mon Sep 17 00:00:00 2001 From: senderfm Date: Wed, 3 Jul 2019 22:51:39 +0200 Subject: [PATCH 016/102] Update of polish locales --- webapp/locales/pl.json | 295 +++++++++++++++++++++++++++++++---------- 1 file changed, 226 insertions(+), 69 deletions(-) diff --git a/webapp/locales/pl.json b/webapp/locales/pl.json index 506a04f1b..e1c825cca 100644 --- a/webapp/locales/pl.json +++ b/webapp/locales/pl.json @@ -1,13 +1,42 @@ { + "filter-menu": { + "title": "Twoja bańka filtrująca" + }, "login": { "copy": "Jeśli masz już konto Human Connection, zaloguj się tutaj.", "login": "logowanie", "logout": "Wyloguj się", "email": "Twój adres e-mail", "password": "Twoje hasło", + "forgotPassword": "Zapomniałeś hasła?", "moreInfo": "Co to jest Human Connection?", + "moreInfoURL": "https://human-connection.org/pl/", + "moreInfoHint": "na stronę prezentacji", "hello": "Cześć" }, + "password-reset": { + "title": "Zresetuj hasło", + "form": { + "description": "Na podany adres e-mail zostanie wysłany email z resetem hasła.", + "submit": "Poproś o wiadomość e-mail", + "submitted": "Na adres {email} została wysłana wiadomość z dalszymi instrukcjami." + } + }, + "verify-code": { + "form": { + "code": "Wprowadź swój kod", + "description": "Otwórz swoją skrzynkę odbiorczą i wpisz kod, który do Ciebie wysłaliśmy.", + "next": "Kontynuuj", + "change-password": { + "success": "Zmiana hasła zakończyła się sukcesem!", + "error": "Zmiana hasła nie powiodła się. Może kod bezpieczeństwa nie był poprawny?", + "help": "W przypadku problemów, zachęcamy do zwrócenia się o pomoc, wysyłając do nas wiadomość e-mail:" + } + } + }, + "editor": { + "placeholder": "Zostaw swoje inspirujące myśli...." + }, "profile": { "name": "Mój profil", "memberSince": "Członek od", @@ -16,7 +45,27 @@ "following": "Obserwowani", "shouted": "Krzyknij", "commented": "Skomentuj", - "userAnonym": "Anonymous" + "userAnonym": "Anonimowy", + "socialMedia": "Gdzie indziej mogę znaleźć", + "network": { + "title": "Sieć", + "following": "jest następująca:", + "followingNobody": "nie podąża za nikim.", + "followedBy": "po którym następuje:", + "followedByNobody": "nie jest śledzona przez nikogo.", + "and": "i", + "more": "więcej" + } + }, + "notifications": { + "menu": { + "mentioned": "wspomniała o tobie na posterunku." + } + }, + "search": { + "placeholder": "Wyszukiwanie", + "hint": "Czego szukasz?", + "failed": "Nic nie znaleziono" }, "settings": { "name": "Ustawienia", @@ -25,10 +74,28 @@ "labelName": "Twoje dane", "namePlaceholder": "Anonymous", "labelCity": "Twoje miasto lub region", - "labelBio": "O Tobie" + "labelBio": "O Tobie", + "success": "Twoje dane zostały pomyślnie zaktualizowane!" }, "security": { - "name": "Bezpieczeństwo" + "name": "Bezpieczeństwo", + "change-password": { + "button": "Zmień hasło", + "success": "Hasło zostało pomyślnie zmienione!", + "label-old-password": "Twoje stare hasło", + "label-new-password": "Twoje nowe hasło", + "label-new-password-confirm": "Potwierdź nowe hasło", + "message-old-password-required": "Wprowadź swoje stare hasło", + "message-new-password-required": "Wprowadź nowe hasło", + "message-new-password-confirm-required": "Potwierdź nowe hasło.", + "message-new-password-missmatch": "Wpisz ponownie to samo hasło.", + "passwordSecurity": "Zabezpieczenie hasłem", + "passwordStrength0": "Bardzo niepewne hasło", + "passwordStrength1": "Niepewne hasło", + "passwordStrength2": "Hasło pośredniczące", + "passwordStrength3": "Silne hasło", + "passwordStrength4": "Bardzo mocne hasło" + } }, "invites": { "name": "Zaproszenia" @@ -36,29 +103,42 @@ "download": { "name": "Pobierz dane" }, - "delete": { - "name": "Usuń konto" + "deleteUserAccount": { + "name": "Usuwanie danych", + "contributionsCount": "Usuń moje stanowiska.", + "commentsCount": "Usuń moje komentarze {liczba}.", + "accountDescription": "Bądź świadomy, że Twój post i komentarze są ważne dla naszej społeczności. Jeśli nadal chcesz je usunąć, musisz zaznaczyć je poniżej.", + "accountWarning": "Nie możesz zarządzać i Nie możesz REKOVER swoje konto, posty lub komentarze po usunięciu konta!", + "success": "Konto zostało pomyślnie usunięte", + "pleaseConfirm": "Niszczycielskie działanie! Typ {potwierdź} aby potwierdzić" }, "organizations": { - "name": "Moje organizacje" + "name": "My Organizations" }, "languages": { - "name": "Języki" + "name": "Languages" + }, + "social-media": { + "name": "Social media", + "placeholder": "Add social media url", + "submit": "Add link", + "successAdd": "Added social media. Updated user profile!", + "successDelete": "Deleted social media. Updated user profile!" } }, "admin": { - "name": "Administrator", + "name": "Admin", "dashboard": { "name": "Tablica rozdzielcza", "users": "Użytkownicy", - "posts": "Posty", + "posts": "Stanowiska", "comments": "Komentarze", "notifications": "Powiadomienia", "organizations": "Organizacje", "projects": "Projekty", - "invites": "Zaproszenia", - "follows": "Obserwowań", - "shouts": "Okrzyk" + "invites": "Zaprasza", + "follows": "Podąża za", + "shouts": "Zalecane" }, "organizations": { "name": "Organizacje" @@ -75,116 +155,193 @@ "categories": { "name": "Kategorie", "categoryName": "Nazwa", - "postCount": "Posty" + "postCount": "Stanowiska" }, "tags": { - "name": "Tagi", + "name": "Znaczniki", "tagCountUnique": "Użytkownicy", - "tagCount": "Posty" + "tagCount": "Stanowiska" }, "settings": { "name": "Ustawienia" } }, "post": { - "name": "Post", + "name": "Poczta", "moreInfo": { "name": "Więcej informacji" }, "takeAction": { - "name": "Podejmij działanie" + "name": "Podejmij działania" + }, + "menu": { + "edit": "Edytuj Post", + "delete": "Usuń wpis" + }, + "comment": { + "submit": "Komentarz", + "submitted": "Przedłożony komentarz" } }, + "comment": { + "content": { + "unavailable-placeholder": " ...ten komentarz nie jest już dostępny." + }, + "menu": { + "edit": "Edytuj komentarz", + "delete": "Usuń komentarz" + }, + "show": { + "more": "Pokaż więcej", + "less": "Pokaż mniej" + } + + }, "quotes": { "african": { - "quote": "Wielu małych ludzi w wielu małych miejscach robi wiele małych rzeczy, które mogą zmienić oblicze świata.", + "quote": "Wielu małych ludzi w wielu małych miejscowościach robi wiele małych rzeczy, które mogą zmienić oblicze świata.", "author": "Afrykańskie przysłowie" } }, "common": { - "post": "Post ::: Posty", + "post": "Poczta ::: Posty", "comment": "Komentarz ::: Komentarze", "letsTalk": "Porozmawiajmy", - "versus": "Versus", + "versus": "werset", "moreInfo": "Więcej informacji", - "takeAction": "Podejmij działanie", - "shout": "okrzyk okrzyki", + "takeAction": "Podejmij działania", + "shout": "przekazanie sprawy ::: Polecam tę stronę", "user": "Użytkownik ::: Użytkownicy", - "category": "kategoria kategorie", - "organization": "Organizacja ::: Organizacje", + "category": "Kategoria ::: Kategorie", + "organization": "Organization ::: Organizations", "project": "Projekt ::: Projekty", - "tag": "Tag ::: Tagi", - "name": "imię", - "loadMore": "załaduj więcej", - "loading": "ładowanie", - "reportContent": "Raport" + "tag": "Znacznik ::: Znaczniki", + "name": "Nazwa", + "loadMore": "Obciążenie więcej", + "loading": "załadunek", + "reportContent": "Sprawozdanie", + "validations": { + "email": "musi być ważny adres e-mail.", + "verification-code": "musi mieć długość 6 znaków." + } }, "actions": { - "loading": "ładowanie", - "loadMore": "załaduj więcej", - "create": "Stwórz", - "save": "Zapisz", - "edit": "Edytuj", + "loading": "załadunek", + "loadMore": "Obciążenie więcej", + "create": "Tworzenie", + "save": "Oszczędzaj", + "edit": "Edycja", "delete": "Usuń", - "cancel": "Anuluj" + "cancel": "Odwołaj" }, "moderation": { - "name": "Moderacja", + "name": "Umiarkowanie", "reports": { - "empty": "Gratulacje, moderacja nie jest potrzebna", - "name": "Raporty", - "reporter": "zgłoszone przez" + "empty": "Gratulacje, nic do umiarkowanego.", + "name": "Sprawozdania", + "submitter": "zgłaszane przez", + "disabledBy": "niepełnosprawni przez" } }, "disable": { + "submit": "Niepełnosprawność", + "cancel": "Odwołaj", + "success": "Niepełnosprawni skutecznie", "user": { - "title": "Ukryj użytkownika", + "title": "Wyłączenie użytkownika", "type": "Użytkownik", - "message": "Czy na pewno chcesz wyłączyć użytkownika \" {name} \"?" + "message": "Czy naprawdę chcesz wyłączyć użytkownika \"{name}\"?" }, "contribution": { - "title": "Ukryj wpis", - "type": "Wpis / Post", - "message": "Czy na pewno chcesz ukryć wpis \" tytuł} \"?" + "title": "Wyłącz Wkład", + "type": "Wkład", + "message": "Naprawdę chcesz unieszkodliwić ten wkład \"{name}\"?" }, "comment": { - "title": "Ukryj wpis", + "title": "Wyłącz komentarz", "type": "Komentarz", - "message": "Czy na pewno chcesz ukryć komentarz użytkownika\"(Imie/Avatar\"?" + "message": "Naprawdę chcesz wyłączyć komentarz \"{name}\"?" + } + }, + "delete": { + "submit": "Usuń", + "cancel": "Odwołaj", + "contribution": { + "title": "Usuń Post", + "type": "Wkład", + "message": "Naprawdę chcesz usunąć post \"{name}\"?", + "success": "Wyślij pomyślnie usunięty!" + }, + "comment": { + "title": "Usuń komentarz", + "type": "Komentarz", + "message": "Czy naprawdę chcesz usunąć komentarz \"{name}\"?", + "success": "Komentarz został pomyślnie usunięty!" } }, "report": { - "submit": "Wyślij raport", - "cancel": "Anuluj", + "submit": "Sprawozdanie", + "cancel": "Odwołaj", + "success": "Dzięki za zgłoszenie!", "user": { - "title": "Zgłoś użytkownika", + "title": "Raport Użytkownik", "type": "Użytkownik", - "message": "Czy na pewno chcesz zgłosić użytkownika \" {Imie} \"?" + "message": "Naprawdę chcesz zgłosić użytkownika \"{name}\"?", + "error": "Zgłosiłeś już użytkownika!" }, "contribution": { - "title": "Zgłoś wpis", - "type": "Wpis / Post", - "message": "Czy na pewno chcesz zgłosić ten wpis użytkownika \" {Imie} \"?" + "title": "Wkład w raport", + "type": "Wkład", + "message": "Naprawdę chcesz zgłosić wkład, jaki wniosłaś do programu \"{name}\"?", + "error": "Zgłosiłeś już ten wkład!" }, "comment": { - "title": "Zgłoś komentarz", + "title": "Sprawozdanie Komentarz", "type": "Komentarz", - "message": "Czy na pewno chcesz zgłosić komentarz użytkownika\"(Imie/Avatar\"?" + "message": "Naprawdę chcesz zgłosić komentarz od \"{name}\"?", + "error": "Zgłosiłeś już komentarz!" + } + }, + "followButton": { + "follow": "naśladować", + "following": "w skutek" + }, + "shoutButton": { + "shouted": "wykrzyczany" + }, + "release": { + "submit": "Zwolnienie", + "cancel": "Odwołaj", + "success": "Wydany z powodzeniem!", + "user": { + "title": "Zwolnienie użytkownika", + "type": "Użytkownik", + "message": "Naprawdę chcesz uwolnić użytkownika \"{name}\"?" + }, + "contribution": { + "title": "Zwolnienie Wkład", + "type": "Wkład", + "message": "Naprawdę chcesz uwolnić swój wkład \"{name}\"?" + }, + "comment": { + "title": "Zwolnienie komentarz", + "type": "komentarz", + "message": "Czy naprawdę chcesz opublikować komentarz od \"{name}\"?" + } + }, + "user": { + "avatar": { + "submitted": "Przesłanie udane" } }, "contribution": { - "edit": "Edytuj wpis", - "delete": "Usuń wpis" - }, - "comment": { - "edit": "Edytuj komentarz", - "delete": "Usuń komentarz" - }, - "followButton": { - "follow": "Obserwuj", - "following": "Obserwowani" - }, - "shoutButton": { - "shouted": "krzyczeć" + "newPost": "Utwórz nowy post", + "filterFollow": "Filtrowanie wkładu użytkowników, za którymi podążam", + "filterALL": "Wyświetl wszystkie wkłady", + "success": "Zachowany!", + "languageSelectLabel": "Język", + "categories": { + "infoSelectedNoOfMaxCategories": "{chosen} z {max} wybrane kategorie" + } } -} \ No newline at end of file +} From c6b261853f9aef26fc1d72b3f87fd62d947c6a9e Mon Sep 17 00:00:00 2001 From: Matt Rider Date: Wed, 3 Jul 2019 19:46:26 -0300 Subject: [PATCH 017/102] Add test for filter posts by category --- backend/src/schema/resolvers/posts.spec.js | 52 ++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/backend/src/schema/resolvers/posts.spec.js b/backend/src/schema/resolvers/posts.spec.js index 763945527..4c288fc25 100644 --- a/backend/src/schema/resolvers/posts.spec.js +++ b/backend/src/schema/resolvers/posts.spec.js @@ -32,6 +32,26 @@ const postQueryWithCategories = ` } } ` +const createPostWithoutCategoriesVariables = { + title: 'This is a post without categories', + content: 'I should be able to filter it out', + categoryIds: null, +} +const postQueryFilteredByCategory = ` + query($name: String) { + Post(filter: { categories_some: { name: $name } }) { + title + id + categories { + name + } + } + } +` +const postCategoriesFilterParam = 'Environment & Nature' +const postQueryFilteredByCategoryVariables = { + name: postCategoriesFilterParam, +} beforeEach(async () => { await factory.create('User', { email: 'test@example.org', @@ -124,7 +144,8 @@ describe('CreatePost', () => { }) describe('categories', () => { - it('allows a user to set the categories of the post', async () => { + let postWithCategories + beforeEach(async () => { await Promise.all([ factory.create('Category', { id: 'cat9', @@ -142,11 +163,14 @@ describe('CreatePost', () => { icon: 'shopping-cart', }), ]) - const expected = [{ id: 'cat9' }, { id: 'cat4' }, { id: 'cat15' }] - const postWithCategories = await client.request( + postWithCategories = await client.request( createPostWithCategoriesMutation, creatPostWithCategoriesVariables, ) + }) + + it('allows a user to set the categories of the post', async () => { + const expected = [{ id: 'cat9' }, { id: 'cat4' }, { id: 'cat15' }] const postQueryWithCategoriesVariables = { id: postWithCategories.CreatePost.id, } @@ -154,6 +178,28 @@ describe('CreatePost', () => { client.request(postQueryWithCategories, postQueryWithCategoriesVariables), ).resolves.toEqual({ Post: [{ categories: expect.arrayContaining(expected) }] }) }) + + it('allows a user to filter for posts by category', async () => { + await client.request(createPostWithCategoriesMutation, createPostWithoutCategoriesVariables) + const categoryNames = [ + { name: 'Democracy & Politics' }, + { name: 'Environment & Nature' }, + { name: 'Consumption & Sustainability' }, + ] + const expected = { + Post: [ + { + title: postTitle, + id: postWithCategories.CreatePost.id, + categories: expect.arrayContaining(categoryNames), + }, + ], + } + + await expect( + client.request(postQueryFilteredByCategory, postQueryFilteredByCategoryVariables), + ).resolves.toEqual(expected) + }) }) }) }) From bb93052b88da55c9c551cacd2934a91b38319e28 Mon Sep 17 00:00:00 2001 From: Matt Rider Date: Wed, 3 Jul 2019 19:46:49 -0300 Subject: [PATCH 018/102] Add FilterPosts dropdown with category names --- webapp/components/FilterPosts/FilterPosts.vue | 86 +++++++++++++++++++ webapp/layouts/default.vue | 21 ++++- webapp/store/categories.js | 42 +++++++++ 3 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 webapp/components/FilterPosts/FilterPosts.vue create mode 100644 webapp/store/categories.js diff --git a/webapp/components/FilterPosts/FilterPosts.vue b/webapp/components/FilterPosts/FilterPosts.vue new file mode 100644 index 000000000..7fff1d477 --- /dev/null +++ b/webapp/components/FilterPosts/FilterPosts.vue @@ -0,0 +1,86 @@ + + diff --git a/webapp/layouts/default.vue b/webapp/layouts/default.vue index e3fc1cccd..1c1ace9de 100644 --- a/webapp/layouts/default.vue +++ b/webapp/layouts/default.vue @@ -4,12 +4,12 @@
- + - + + + + + +