mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge branch 'master' into 375-Sorting-II
This commit is contained in:
commit
4fe3c08402
@ -89,7 +89,7 @@
|
||||
"@babel/plugin-proposal-throw-expressions": "^7.2.0",
|
||||
"@babel/preset-env": "~7.5.4",
|
||||
"@babel/register": "~7.4.4",
|
||||
"apollo-server-testing": "~2.6.9",
|
||||
"apollo-server-testing": "~2.7.0",
|
||||
"babel-core": "~7.0.0-0",
|
||||
"babel-eslint": "~10.0.2",
|
||||
"babel-jest": "~24.8.0",
|
||||
@ -99,7 +99,7 @@
|
||||
"eslint-config-prettier": "~6.0.0",
|
||||
"eslint-config-standard": "~12.0.0",
|
||||
"eslint-plugin-import": "~2.18.0",
|
||||
"eslint-plugin-jest": "~22.8.0",
|
||||
"eslint-plugin-jest": "~22.9.0",
|
||||
"eslint-plugin-node": "~9.1.0",
|
||||
"eslint-plugin-prettier": "~3.1.0",
|
||||
"eslint-plugin-promise": "~4.2.1",
|
||||
|
||||
@ -176,6 +176,7 @@ const permissions = shield(
|
||||
enable: isModerator,
|
||||
disable: isModerator,
|
||||
CreateComment: isAuthenticated,
|
||||
UpdateComment: isAuthor,
|
||||
DeleteComment: isAuthor,
|
||||
DeleteUser: isDeletingOwnAccount,
|
||||
requestPasswordReset: allow,
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
import { UserInputError } from 'apollo-server'
|
||||
|
||||
const validateUrl = async (resolve, root, args, context, info) => {
|
||||
const { url } = args
|
||||
const isValid = url.match(/^(?:https?:\/\/)(?:[^@\n])?(?:www\.)?([^:/\n?]+)/g)
|
||||
if (isValid) {
|
||||
/* eslint-disable-next-line no-return-await */
|
||||
return await resolve(root, args, context, info)
|
||||
} else {
|
||||
throw new UserInputError('Input is not a URL')
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
Mutation: {
|
||||
CreateSocialMedia: validateUrl,
|
||||
},
|
||||
}
|
||||
@ -45,9 +45,20 @@ const validateCommentCreation = 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!`)
|
||||
}
|
||||
|
||||
return resolve(root, args, context, info)
|
||||
}
|
||||
|
||||
export default {
|
||||
Mutation: {
|
||||
CreateSocialMedia: validate(socialMediaSchema),
|
||||
CreateComment: validateCommentCreation,
|
||||
UpdateComment: validateUpdateComment,
|
||||
},
|
||||
}
|
||||
|
||||
@ -9,7 +9,28 @@ let createPostVariables
|
||||
let createCommentVariablesSansPostId
|
||||
let createCommentVariablesWithNonExistentPost
|
||||
let userParams
|
||||
let authorParams
|
||||
let headers
|
||||
|
||||
const createPostMutation = gql`
|
||||
mutation($id: ID!, $title: String!, $content: String!) {
|
||||
CreatePost(id: $id, title: $title, content: $content) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
const createCommentMutation = gql`
|
||||
mutation($id: ID, $postId: ID!, $content: String!) {
|
||||
CreateComment(id: $id, postId: $postId, content: $content) {
|
||||
id
|
||||
content
|
||||
}
|
||||
}
|
||||
`
|
||||
createPostVariables = {
|
||||
id: 'p1',
|
||||
title: 'post to comment on',
|
||||
content: 'please comment on me',
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
userParams = {
|
||||
@ -25,21 +46,6 @@ afterEach(async () => {
|
||||
})
|
||||
|
||||
describe('CreateComment', () => {
|
||||
const createCommentMutation = gql`
|
||||
mutation($postId: ID!, $content: String!) {
|
||||
CreateComment(postId: $postId, content: $content) {
|
||||
id
|
||||
content
|
||||
}
|
||||
}
|
||||
`
|
||||
const createPostMutation = gql`
|
||||
mutation($id: ID!, $title: String!, $content: String!) {
|
||||
CreatePost(id: $id, title: $title, content: $content) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
createCommentVariables = {
|
||||
@ -54,7 +60,6 @@ describe('CreateComment', () => {
|
||||
})
|
||||
|
||||
describe('authenticated', () => {
|
||||
let headers
|
||||
beforeEach(async () => {
|
||||
headers = await login(userParams)
|
||||
client = new GraphQLClient(host, {
|
||||
@ -64,11 +69,6 @@ describe('CreateComment', () => {
|
||||
postId: 'p1',
|
||||
content: "I'm authorised to comment",
|
||||
}
|
||||
createPostVariables = {
|
||||
id: 'p1',
|
||||
title: 'post to comment on',
|
||||
content: 'please comment on me',
|
||||
}
|
||||
await client.request(createPostMutation, createPostVariables)
|
||||
})
|
||||
|
||||
@ -187,19 +187,8 @@ describe('CreateComment', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('DeleteComment', () => {
|
||||
const deleteCommentMutation = gql`
|
||||
mutation($id: ID!) {
|
||||
DeleteComment(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
let deleteCommentVariables = {
|
||||
id: 'c1',
|
||||
}
|
||||
|
||||
describe('ManageComments', () => {
|
||||
let authorParams
|
||||
beforeEach(async () => {
|
||||
authorParams = {
|
||||
email: 'author@example.org',
|
||||
@ -213,51 +202,178 @@ describe('DeleteComment', () => {
|
||||
content: 'Post to be commented',
|
||||
})
|
||||
await asAuthor.create('Comment', {
|
||||
id: 'c1',
|
||||
id: 'c456',
|
||||
postId: 'p1',
|
||||
content: 'Comment to be deleted',
|
||||
})
|
||||
})
|
||||
|
||||
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(userParams)
|
||||
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(authorParams)
|
||||
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: 'c456',
|
||||
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 () => {
|
||||
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 () => {
|
||||
headers = await login(authorParams)
|
||||
client = new GraphQLClient(host, {
|
||||
headers,
|
||||
})
|
||||
})
|
||||
|
||||
it('updates the comment', async () => {
|
||||
const expected = {
|
||||
UpdateComment: {
|
||||
id: 'c456',
|
||||
content: 'The comment is updated',
|
||||
},
|
||||
}
|
||||
await expect(
|
||||
client.request(updateCommentMutation, updateCommentVariables),
|
||||
).resolves.toEqual(expected)
|
||||
})
|
||||
|
||||
it('throw an error if an empty string is sent from the editor as content', async () => {
|
||||
updateCommentVariables = {
|
||||
id: 'c456',
|
||||
content: '<p></p>',
|
||||
}
|
||||
|
||||
await expect(client.request(updateCommentMutation, updateCommentVariables)).rejects.toThrow(
|
||||
'Comment must be at least 1 character long!',
|
||||
)
|
||||
})
|
||||
|
||||
it('throws an error if a comment sent from the editor does not contain a single letter character', async () => {
|
||||
updateCommentVariables = {
|
||||
id: 'c456',
|
||||
content: '<p> </p>',
|
||||
}
|
||||
|
||||
await expect(client.request(updateCommentMutation, updateCommentVariables)).rejects.toThrow(
|
||||
'Comment must be at least 1 character long!',
|
||||
)
|
||||
})
|
||||
|
||||
it('throws an error if commentId is sent as an empty string', async () => {
|
||||
updateCommentVariables = {
|
||||
id: '',
|
||||
content: '<p>Hello</p>',
|
||||
}
|
||||
|
||||
await expect(client.request(updateCommentMutation, updateCommentVariables)).rejects.toThrow(
|
||||
'Not Authorised!',
|
||||
)
|
||||
})
|
||||
|
||||
it('throws an error if the comment does not exist in the database', async () => {
|
||||
updateCommentVariables = {
|
||||
id: 'c1000',
|
||||
content: '<p>Hello</p>',
|
||||
}
|
||||
|
||||
await expect(client.request(updateCommentMutation, updateCommentVariables)).rejects.toThrow(
|
||||
'Not Authorised!',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('DeleteComment', () => {
|
||||
const deleteCommentMutation = gql`
|
||||
mutation($id: ID!) {
|
||||
DeleteComment(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
let deleteCommentVariables = {
|
||||
id: 'c456',
|
||||
}
|
||||
|
||||
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 () => {
|
||||
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 () => {
|
||||
headers = await login(authorParams)
|
||||
client = new GraphQLClient(host, {
|
||||
headers,
|
||||
})
|
||||
})
|
||||
|
||||
it('deletes the comment', async () => {
|
||||
const expected = {
|
||||
DeleteComment: {
|
||||
id: 'c456',
|
||||
},
|
||||
}
|
||||
await expect(
|
||||
client.request(deleteCommentMutation, deleteCommentVariables),
|
||||
).resolves.toEqual(expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -24,7 +24,7 @@ type Mutation {
|
||||
): Comment
|
||||
UpdateComment(
|
||||
id: ID!
|
||||
content: String
|
||||
content: String!
|
||||
contentExcerpt: String
|
||||
deleted: Boolean
|
||||
disabled: Boolean
|
||||
|
||||
@ -1336,6 +1336,14 @@ apollo-cache-control@0.7.5:
|
||||
apollo-server-env "2.4.0"
|
||||
graphql-extensions "0.7.7"
|
||||
|
||||
apollo-cache-control@0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.8.0.tgz#08b157e5f8cd86f63608b05d45222de0725ebd5a"
|
||||
integrity sha512-BBnfUmSWRws5dRSDD+R56RLJCE9v6xQuob+i/1Ju9EX4LZszU5JKVmxEvnkJ1bk/BkihjoQXTnP6fJCnt6fCmA==
|
||||
dependencies:
|
||||
apollo-server-env "2.4.0"
|
||||
graphql-extensions "0.8.0"
|
||||
|
||||
apollo-cache-inmemory@~1.6.2:
|
||||
version "1.6.2"
|
||||
resolved "https://registry.yarnpkg.com/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.2.tgz#bbf2e4e1eacdf82b2d526f5c2f3b37e5acee3c5e"
|
||||
@ -1377,6 +1385,14 @@ apollo-datasource@0.5.0:
|
||||
apollo-server-caching "0.4.0"
|
||||
apollo-server-env "2.4.0"
|
||||
|
||||
apollo-datasource@0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/apollo-datasource/-/apollo-datasource-0.6.0.tgz#823d6be8a3804613b5c56d2972c07db662293fc6"
|
||||
integrity sha512-DOzzYWEOReYRu2vWPKEulqlTb9Xjg67sjVCzve5MXa7GUXjfr8IKioljvfoBMlqm/PpbJVk2ci4n5NIFqoYsrQ==
|
||||
dependencies:
|
||||
apollo-server-caching "0.5.0"
|
||||
apollo-server-env "2.4.0"
|
||||
|
||||
apollo-engine-reporting-protobuf@0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-engine-reporting-protobuf/-/apollo-engine-reporting-protobuf-0.3.1.tgz#a581257fa8e3bb115ce38bf1b22e052d1475ad69"
|
||||
@ -1384,6 +1400,13 @@ apollo-engine-reporting-protobuf@0.3.1:
|
||||
dependencies:
|
||||
protobufjs "^6.8.6"
|
||||
|
||||
apollo-engine-reporting-protobuf@0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/apollo-engine-reporting-protobuf/-/apollo-engine-reporting-protobuf-0.4.0.tgz#e34c192d86493b33a73181fd6be75721559111ec"
|
||||
integrity sha512-cXHZSienkis8v4RhqB3YG3DkaksqLpcxApRLTpRMs7IXNozgV7CUPYGFyFBEra1ZFgUyHXx4G9MpelV+n2cCfA==
|
||||
dependencies:
|
||||
protobufjs "^6.8.6"
|
||||
|
||||
apollo-engine-reporting@1.3.6:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.3.6.tgz#579ba2da85ff848bd92be1b0f1ad61f0c57e3585"
|
||||
@ -1396,6 +1419,18 @@ apollo-engine-reporting@1.3.6:
|
||||
async-retry "^1.2.1"
|
||||
graphql-extensions "0.7.7"
|
||||
|
||||
apollo-engine-reporting@1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.4.0.tgz#3a9bd011b271593e16d7057044898d0a817b197d"
|
||||
integrity sha512-NMiO3h1cuEBt6QZNGHxivwuyZQnoU/2MMx0gUA8Gyy1ERBhK6P235qoMnvoi34rLmqJuyGPX6tXcab8MpMIzYQ==
|
||||
dependencies:
|
||||
apollo-engine-reporting-protobuf "0.4.0"
|
||||
apollo-graphql "^0.3.3"
|
||||
apollo-server-env "2.4.0"
|
||||
apollo-server-types "0.2.0"
|
||||
async-retry "^1.2.1"
|
||||
graphql-extensions "0.8.0"
|
||||
|
||||
apollo-env@0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-env/-/apollo-env-0.5.1.tgz#b9b0195c16feadf0fe9fd5563edb0b9b7d9e97d3"
|
||||
@ -1464,6 +1499,13 @@ apollo-server-caching@0.4.0:
|
||||
dependencies:
|
||||
lru-cache "^5.0.0"
|
||||
|
||||
apollo-server-caching@0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-caching/-/apollo-server-caching-0.5.0.tgz#446a37ce2d4e24c81833e276638330a634f7bd46"
|
||||
integrity sha512-l7ieNCGxUaUAVAAp600HjbUJxVaxjJygtPV0tPTe1Q3HkPy6LEWoY6mNHV7T268g1hxtPTxcdRu7WLsJrg7ufw==
|
||||
dependencies:
|
||||
lru-cache "^5.0.0"
|
||||
|
||||
apollo-server-core@2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.6.9.tgz#75542ad206782e5c31a023b54962e9fdc6404a91"
|
||||
@ -1490,6 +1532,34 @@ apollo-server-core@2.6.9:
|
||||
subscriptions-transport-ws "^0.9.11"
|
||||
ws "^6.0.0"
|
||||
|
||||
apollo-server-core@2.7.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.7.0.tgz#c444347dea11149b5b453890506e43dc7e711257"
|
||||
integrity sha512-CXjXAkgcMBCJZpsZgfAY5W7f5thdxUhn75UgzeH28RTUZ2aKi/LjoCixPWRSF1lU4vuEWneAnM8Vg/KCD+29lQ==
|
||||
dependencies:
|
||||
"@apollographql/apollo-tools" "^0.3.6"
|
||||
"@apollographql/graphql-playground-html" "1.6.24"
|
||||
"@types/ws" "^6.0.0"
|
||||
apollo-cache-control "0.8.0"
|
||||
apollo-datasource "0.6.0"
|
||||
apollo-engine-reporting "1.4.0"
|
||||
apollo-engine-reporting-protobuf "0.4.0"
|
||||
apollo-server-caching "0.5.0"
|
||||
apollo-server-env "2.4.0"
|
||||
apollo-server-errors "2.3.1"
|
||||
apollo-server-plugin-base "0.6.0"
|
||||
apollo-server-types "0.2.0"
|
||||
apollo-tracing "0.8.0"
|
||||
fast-json-stable-stringify "^2.0.0"
|
||||
graphql-extensions "0.8.0"
|
||||
graphql-subscriptions "^1.0.0"
|
||||
graphql-tag "^2.9.2"
|
||||
graphql-tools "^4.0.0"
|
||||
graphql-upload "^8.0.2"
|
||||
sha.js "^2.4.11"
|
||||
subscriptions-transport-ws "^0.9.11"
|
||||
ws "^6.0.0"
|
||||
|
||||
apollo-server-env@2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-env/-/apollo-server-env-2.4.0.tgz#6611556c6b627a1636eed31317d4f7ea30705872"
|
||||
@ -1526,12 +1596,28 @@ apollo-server-plugin-base@0.5.8:
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.5.8.tgz#77b4127aff4e3514a9d49e3cc61256aee4d9422e"
|
||||
integrity sha512-ICbaXr0ycQZL5llbtZhg8zyHbxuZ4khdAJsJgiZaUXXP6+F47XfDQ5uwnl/4Sq9fvkpwS0ctvfZ1D+Ks4NvUzA==
|
||||
|
||||
apollo-server-testing@~2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-testing/-/apollo-server-testing-2.6.9.tgz#6c1d20a89c0676bf32714405d729c302d62adfb1"
|
||||
integrity sha512-MQfXAjNsI63O9sY60tQnGy102sqJSr++Yzm+IR44WrK3Z7FHUDisoh6UATly04EDGtO034xtqukzdUNQCK7+rw==
|
||||
apollo-server-plugin-base@0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.6.0.tgz#4186296ea5d52cfe613961d252a8a2f9e13e6ba6"
|
||||
integrity sha512-BjfyWpHyKwHOe819gk3wEFwbnVp9Xvos03lkkYTTcXS/8G7xO78aUcE65mmyAC56/ZQ0aodNFkFrhwNtWBQWUQ==
|
||||
dependencies:
|
||||
apollo-server-core "2.6.9"
|
||||
apollo-server-types "0.2.0"
|
||||
|
||||
apollo-server-testing@~2.7.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-testing/-/apollo-server-testing-2.7.0.tgz#df8f8fb0df85f9781b6822fc92ee36fdc039b024"
|
||||
integrity sha512-geBTK5T8mqZ2UOscC3oHQ/QoKMINLK+Mmq5ZfZe9UhmdXi+WqQ3/6B+3HMoQ7eQ7D6bNILe8b7N2EKuyUkePdg==
|
||||
dependencies:
|
||||
apollo-server-core "2.7.0"
|
||||
|
||||
apollo-server-types@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-types/-/apollo-server-types-0.2.0.tgz#270d7298f709fd8237ebfa48753249e5286df5f2"
|
||||
integrity sha512-5dgiyXsM90vnfmdXO1ixHvsLn0d9NP4tWufmr3ZmjKv00r4JAQNUaUdgOSGbRIKoHELQGwxUuTySTZ/tYfGaNQ==
|
||||
dependencies:
|
||||
apollo-engine-reporting-protobuf "0.4.0"
|
||||
apollo-server-caching "0.5.0"
|
||||
apollo-server-env "2.4.0"
|
||||
|
||||
apollo-server@~2.6.9:
|
||||
version "2.6.9"
|
||||
@ -1552,6 +1638,14 @@ apollo-tracing@0.7.4:
|
||||
apollo-server-env "2.4.0"
|
||||
graphql-extensions "0.7.7"
|
||||
|
||||
apollo-tracing@0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.8.0.tgz#28cd9c61a4db12b2c24dad67fdedd309806c1650"
|
||||
integrity sha512-cNOtOlyZ56iJRsCjnxjM1V0SnQ2ZZttuyoeOejdat6llPfk5bfYTVOKMjdbSfDvU33LS9g9sqNJCT0MwrEPFKQ==
|
||||
dependencies:
|
||||
apollo-server-env "2.4.0"
|
||||
graphql-extensions "0.8.0"
|
||||
|
||||
apollo-utilities@1.3.2, apollo-utilities@^1.0.1, apollo-utilities@^1.3.0, apollo-utilities@^1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.2.tgz#8cbdcf8b012f664cd6cb5767f6130f5aed9115c9"
|
||||
@ -2989,10 +3083,10 @@ eslint-plugin-import@~2.18.0:
|
||||
read-pkg-up "^2.0.0"
|
||||
resolve "^1.11.0"
|
||||
|
||||
eslint-plugin-jest@~22.8.0:
|
||||
version "22.8.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.8.0.tgz#242ef5459e8da25d2c41438e95eb546e03d7fae1"
|
||||
integrity sha512-2VftZMfILmlhL3VMq5ptHRIuyyXb3ShDEDb1J1UjvWNzm4l+UK/YmwNuTuJcM0gv8pJuOfiR/8ZptJ8Ou68pFw==
|
||||
eslint-plugin-jest@~22.9.0:
|
||||
version "22.9.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.9.0.tgz#2573dbcb4f1066b96a6e6d3b9aa439c80b28975a"
|
||||
integrity sha512-V89BUiwf76FHlhj1mlNhNyvpzTy8VbWCh2RZpKYz/XDSl/pcuwFiE/LMt7r3q1sRKygzEMjbYeDob8MMuvakXg==
|
||||
|
||||
eslint-plugin-node@~9.1.0:
|
||||
version "9.1.0"
|
||||
@ -3649,6 +3743,15 @@ graphql-extensions@0.7.7:
|
||||
"@apollographql/apollo-tools" "^0.3.6"
|
||||
apollo-server-env "2.4.0"
|
||||
|
||||
graphql-extensions@0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.8.0.tgz#b3fe7915aa84eef5a39135840840cc4d2e700c46"
|
||||
integrity sha512-zV9RefkusIXqi9ZJtl7IJ5ecjDKdb7PLAb5E3CmxX3OK1GwNCIubp0vE7Fp4fXlCUKgTB1Woubs0zj71JT8o0A==
|
||||
dependencies:
|
||||
"@apollographql/apollo-tools" "^0.3.6"
|
||||
apollo-server-env "2.4.0"
|
||||
apollo-server-types "0.2.0"
|
||||
|
||||
graphql-iso-date@~3.6.1:
|
||||
version "3.6.1"
|
||||
resolved "https://registry.yarnpkg.com/graphql-iso-date/-/graphql-iso-date-3.6.1.tgz#bd2d0dc886e0f954cbbbc496bbf1d480b57ffa96"
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
"cross-env": "^5.2.0",
|
||||
"cypress": "^3.4.0",
|
||||
"cypress-cucumber-preprocessor": "^1.12.0",
|
||||
"cypress-file-upload": "^3.3.1",
|
||||
"cypress-file-upload": "^3.3.2",
|
||||
"cypress-plugin-retries": "^1.2.2",
|
||||
"dotenv": "^8.0.0",
|
||||
"faker": "Marak/faker.js#master",
|
||||
|
||||
@ -23,49 +23,61 @@
|
||||
:modalsData="menuModalsData"
|
||||
style="float-right"
|
||||
:is-owner="isAuthor(author.id)"
|
||||
@showEditCommentMenu="editCommentMenu"
|
||||
/>
|
||||
</no-ssr>
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<!-- TODO: replace editor content with tiptap render view -->
|
||||
|
||||
<div v-if="isCollapsed" v-html="comment.contentExcerpt" style="padding-left: 40px;" />
|
||||
<div
|
||||
v-show="comment.content !== comment.contentExcerpt"
|
||||
style="text-align: right; margin-right: 20px; margin-top: -12px;"
|
||||
>
|
||||
<a v-if="isCollapsed" style="padding-left: 40px;" @click="isCollapsed = !isCollapsed">
|
||||
{{ $t('comment.show.more') }}
|
||||
</a>
|
||||
<ds-space margin-bottom="small" />
|
||||
<div v-if="openEditCommentMenu">
|
||||
<hc-edit-comment-form
|
||||
:comment="comment"
|
||||
:post="post"
|
||||
@showEditCommentMenu="editCommentMenu"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="!isCollapsed" v-html="comment.content" style="padding-left: 40px;" />
|
||||
<div style="text-align: right; margin-right: 20px; margin-top: -12px;">
|
||||
<a v-if="!isCollapsed" @click="isCollapsed = !isCollapsed" style="padding-left: 40px; ">
|
||||
{{ $t('comment.show.less') }}
|
||||
</a>
|
||||
<div v-show="!openEditCommentMenu">
|
||||
<div v-if="isCollapsed" v-html="comment.contentExcerpt" style="padding-left: 40px;" />
|
||||
<div
|
||||
v-show="comment.content !== comment.contentExcerpt"
|
||||
style="text-align: right; margin-right: 20px; margin-top: -12px;"
|
||||
>
|
||||
<a v-if="isCollapsed" style="padding-left: 40px;" @click="isCollapsed = !isCollapsed">
|
||||
{{ $t('comment.show.more') }}
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="!isCollapsed" v-html="comment.content" style="padding-left: 40px;" />
|
||||
<div style="text-align: right; margin-right: 20px; margin-top: -12px;">
|
||||
<a v-if="!isCollapsed" @click="isCollapsed = !isCollapsed" style="padding-left: 40px; ">
|
||||
{{ $t('comment.show.less') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<ds-space margin-bottom="small" />
|
||||
</ds-card>
|
||||
</div>
|
||||
</template>
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
|
||||
<script>
|
||||
import gql from 'graphql-tag'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { mapGetters, mapMutations } from 'vuex'
|
||||
import HcUser from '~/components/User'
|
||||
import ContentMenu from '~/components/ContentMenu'
|
||||
import HcEditCommentForm from '~/components/comments/EditCommentForm/EditCommentForm'
|
||||
|
||||
export default {
|
||||
data: function() {
|
||||
return {
|
||||
isCollapsed: true,
|
||||
openEditCommentMenu: false,
|
||||
}
|
||||
},
|
||||
components: {
|
||||
HcUser,
|
||||
ContentMenu,
|
||||
HcEditCommentForm,
|
||||
},
|
||||
props: {
|
||||
post: { type: Object, default: () => {} },
|
||||
comment: {
|
||||
type: Object,
|
||||
default() {
|
||||
@ -112,9 +124,16 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapMutations({
|
||||
setEditPending: 'editor/SET_EDIT_PENDING',
|
||||
}),
|
||||
isAuthor(id) {
|
||||
return this.user.id === id
|
||||
},
|
||||
editCommentMenu(showMenu) {
|
||||
this.openEditCommentMenu = showMenu
|
||||
this.setEditPending(showMenu)
|
||||
},
|
||||
async deleteCommentCallback() {
|
||||
try {
|
||||
var gqlMutation = gql`
|
||||
|
||||
@ -76,14 +76,13 @@ export default {
|
||||
}
|
||||
|
||||
if (this.isOwner && this.resourceType === 'comment') {
|
||||
// routes.push({
|
||||
// name: this.$t(`comment.menu.edit`),
|
||||
// callback: () => {
|
||||
// /* eslint-disable-next-line no-console */
|
||||
// console.log('EDIT COMMENT')
|
||||
// },
|
||||
// icon: 'edit'
|
||||
// })
|
||||
routes.push({
|
||||
name: this.$t(`comment.menu.edit`),
|
||||
callback: () => {
|
||||
this.$emit('showEditCommentMenu', true)
|
||||
},
|
||||
icon: 'edit',
|
||||
})
|
||||
routes.push({
|
||||
name: this.$t(`comment.menu.delete`),
|
||||
callback: () => {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<ds-form v-model="form" @submit="handleSubmit">
|
||||
<ds-form v-show="!editPending" v-model="form" @submit="handleSubmit">
|
||||
<template slot-scope="{ errors }">
|
||||
<ds-card>
|
||||
<hc-editor ref="editor" :users="users" :value="form.content" @input="updateEditorContent" />
|
||||
@ -27,6 +27,7 @@ import gql from 'graphql-tag'
|
||||
import HcEditor from '~/components/Editor/Editor'
|
||||
import PostCommentsQuery from '~/graphql/PostCommentsQuery.js'
|
||||
import CommentMutations from '~/graphql/CommentMutations.js'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -46,6 +47,11 @@ export default {
|
||||
users: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
editPending: 'editor/editPending',
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
updateEditorContent(value) {
|
||||
const content = value.replace(/<(?:.|\n)*?>/gm, '').trim()
|
||||
|
||||
@ -40,6 +40,7 @@ describe('CommentForm.vue', () => {
|
||||
'editor/placeholder': () => {
|
||||
return 'some cool placeholder'
|
||||
},
|
||||
'editor/editPending': () => false,
|
||||
}
|
||||
const store = new Vuex.Store({
|
||||
getters,
|
||||
|
||||
@ -16,11 +16,12 @@
|
||||
</span>
|
||||
</h3>
|
||||
<ds-space margin-bottom="large" />
|
||||
<div v-if="comments && comments.length" class="comments">
|
||||
<div v-if="comments && comments.length" id="comments" class="comments">
|
||||
<comment
|
||||
v-for="(comment, index) in comments"
|
||||
:key="comment.id"
|
||||
:comment="comment"
|
||||
:post="post"
|
||||
@deleteComment="comments.splice(index, 1)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
108
webapp/components/comments/EditCommentForm/EditCommentForm.vue
Normal file
108
webapp/components/comments/EditCommentForm/EditCommentForm.vue
Normal file
@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<ds-form v-model="form" @submit="handleSubmit">
|
||||
<template slot-scope="{ errors }">
|
||||
<ds-card>
|
||||
<!-- with no-ssr the content is not shown -->
|
||||
<hc-editor ref="editor" :users="users" :value="form.content" @input="updateEditorContent" />
|
||||
<ds-space />
|
||||
<ds-flex :gutter="{ base: 'small', md: 'small', sm: 'x-large', xs: 'x-large' }">
|
||||
<ds-flex-item :width="{ base: '0%', md: '50%', sm: '0%', xs: '0%' }" />
|
||||
<ds-flex-item :width="{ base: '40%', md: '20%', sm: '30%', xs: '30%' }">
|
||||
<ds-button ghost class="cancelBtn" @click.prevent="closeEditWindow">
|
||||
{{ $t('actions.cancel') }}
|
||||
</ds-button>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '40%', md: '20%', sm: '40%', xs: '40%' }">
|
||||
<ds-button type="submit" :loading="loading" :disabled="disabled || errors" primary>
|
||||
{{ $t('post.comment.submit') }}
|
||||
</ds-button>
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
</ds-card>
|
||||
</template>
|
||||
</ds-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import gql from 'graphql-tag'
|
||||
import HcEditor from '~/components/Editor/Editor'
|
||||
import { mapMutations } from 'vuex'
|
||||
import CommentMutations from '~/graphql/CommentMutations.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HcEditor,
|
||||
},
|
||||
props: {
|
||||
comment: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
disabled: true,
|
||||
loading: false,
|
||||
form: {
|
||||
content: this.comment.content,
|
||||
},
|
||||
users: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapMutations({
|
||||
setEditPending: 'editor/SET_EDIT_PENDING',
|
||||
}),
|
||||
updateEditorContent(value) {
|
||||
const sanitizedContent = value.replace(/<(?:.|\n)*?>/gm, '').trim()
|
||||
this.disabled = value === this.comment.content || sanitizedContent.length < 1
|
||||
this.form.content = value
|
||||
},
|
||||
closeEditWindow() {
|
||||
this.$emit('showEditCommentMenu', false)
|
||||
},
|
||||
handleSubmit() {
|
||||
this.loading = true
|
||||
this.disabled = true
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: CommentMutations().UpdateComment,
|
||||
variables: {
|
||||
content: this.form.content,
|
||||
id: this.comment.id,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.loading = false
|
||||
|
||||
this.$toast.success(this.$t('post.comment.updated'))
|
||||
this.disabled = false
|
||||
this.$emit('showEditCommentMenu', false)
|
||||
this.setEditPending(false)
|
||||
})
|
||||
.catch(err => {
|
||||
this.$toast.error(err.message)
|
||||
})
|
||||
},
|
||||
},
|
||||
apollo: {
|
||||
User: {
|
||||
query() {
|
||||
return gql`
|
||||
{
|
||||
User(orderBy: slug_asc) {
|
||||
id
|
||||
slug
|
||||
}
|
||||
}
|
||||
`
|
||||
},
|
||||
result({ data: { User } }) {
|
||||
this.users = User
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -20,5 +20,14 @@ export default () => {
|
||||
}
|
||||
}
|
||||
`,
|
||||
UpdateComment: gql`
|
||||
mutation($content: String!, $id: ID!) {
|
||||
UpdateComment(content: $content, id: $id) {
|
||||
id
|
||||
content
|
||||
contentExcerpt
|
||||
}
|
||||
}
|
||||
`,
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import gql from 'graphql-tag'
|
||||
|
||||
export default app => {
|
||||
const lang = app.$i18n.locale().toUpperCase()
|
||||
return gql(`
|
||||
return gql`
|
||||
query Comment($postId: ID) {
|
||||
Comment(postId: $postId) {
|
||||
id
|
||||
@ -30,5 +30,5 @@ export default app => {
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
`
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import gql from 'graphql-tag'
|
||||
|
||||
export default i18n => {
|
||||
const lang = i18n.locale().toUpperCase()
|
||||
return gql(`
|
||||
return gql`
|
||||
query Post($slug: String!) {
|
||||
Post(slug: $slug) {
|
||||
id
|
||||
@ -73,7 +73,7 @@ export default i18n => {
|
||||
shoutedByCurrentUser
|
||||
}
|
||||
}
|
||||
`)
|
||||
`
|
||||
}
|
||||
|
||||
export const filterPosts = i18n => {
|
||||
@ -118,5 +118,5 @@ export const filterPosts = i18n => {
|
||||
shoutedCount
|
||||
}
|
||||
}
|
||||
`)
|
||||
`
|
||||
}
|
||||
|
||||
@ -252,7 +252,8 @@
|
||||
},
|
||||
"comment": {
|
||||
"submit": "Comment",
|
||||
"submitted": "Comment Submitted"
|
||||
"submitted": "Comment Submitted",
|
||||
"updated": "Changes Saved"
|
||||
}
|
||||
},
|
||||
"comment": {
|
||||
|
||||
@ -95,7 +95,7 @@
|
||||
"eslint-config-standard": "~12.0.0",
|
||||
"eslint-loader": "~2.2.1",
|
||||
"eslint-plugin-import": "~2.18.0",
|
||||
"eslint-plugin-jest": "~22.8.0",
|
||||
"eslint-plugin-jest": "~22.9.0",
|
||||
"eslint-plugin-node": "~9.1.0",
|
||||
"eslint-plugin-prettier": "~3.1.0",
|
||||
"eslint-plugin-promise": "~4.2.1",
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
export const state = () => {
|
||||
return {
|
||||
placeholder: null,
|
||||
editPending: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,10 +9,16 @@ export const getters = {
|
||||
placeholder(state) {
|
||||
return state.placeholder
|
||||
},
|
||||
editPending(state) {
|
||||
return state.editPending
|
||||
},
|
||||
}
|
||||
|
||||
export const mutations = {
|
||||
SET_PLACEHOLDER_TEXT(state, text) {
|
||||
state.placeholder = text
|
||||
},
|
||||
SET_EDIT_PENDING(state, boolean) {
|
||||
state.editPending = boolean
|
||||
},
|
||||
}
|
||||
|
||||
@ -4320,10 +4320,10 @@ eslint-plugin-import@~2.18.0:
|
||||
read-pkg-up "^2.0.0"
|
||||
resolve "^1.11.0"
|
||||
|
||||
eslint-plugin-jest@~22.8.0:
|
||||
version "22.8.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.8.0.tgz#242ef5459e8da25d2c41438e95eb546e03d7fae1"
|
||||
integrity sha512-2VftZMfILmlhL3VMq5ptHRIuyyXb3ShDEDb1J1UjvWNzm4l+UK/YmwNuTuJcM0gv8pJuOfiR/8ZptJ8Ou68pFw==
|
||||
eslint-plugin-jest@~22.9.0:
|
||||
version "22.9.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.9.0.tgz#2573dbcb4f1066b96a6e6d3b9aa439c80b28975a"
|
||||
integrity sha512-V89BUiwf76FHlhj1mlNhNyvpzTy8VbWCh2RZpKYz/XDSl/pcuwFiE/LMt7r3q1sRKygzEMjbYeDob8MMuvakXg==
|
||||
|
||||
eslint-plugin-node@~9.1.0:
|
||||
version "9.1.0"
|
||||
|
||||
@ -1827,10 +1827,10 @@ cypress-cucumber-preprocessor@^1.12.0:
|
||||
glob "^7.1.2"
|
||||
through "^2.3.8"
|
||||
|
||||
cypress-file-upload@^3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-3.3.1.tgz#19bb6c296ffc492dbfae8a7511c94d6b4d0ad4d5"
|
||||
integrity sha512-iUtq/a30i73JXx9sUj5HhmuEV9pHMV2/7C06H8/zFDSgFweFSwKL0SSprQu8Ewf7cAEsExBKigwlLQYFdTW8PA==
|
||||
cypress-file-upload@^3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-3.3.2.tgz#d9bb00cac756fd4b5f68079f19745fe40d97fdd4"
|
||||
integrity sha512-39CW6/rTmn7AGa+ZrD37R+ANnnWAesbUj2RpBfbpRzV+KTAlF+m9Djbd2f325w0JGY0A2eO0w6TYYo+NvBTnoA==
|
||||
|
||||
cypress-plugin-retries@^1.2.2:
|
||||
version "1.2.2"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user