diff --git a/backend/src/middleware/validation/validationMiddleware.js b/backend/src/middleware/validation/validationMiddleware.js index 2d354ad2b..04f6b92a4 100644 --- a/backend/src/middleware/validation/validationMiddleware.js +++ b/backend/src/middleware/validation/validationMiddleware.js @@ -40,9 +40,18 @@ const validateUpdateComment = async (resolve, root, args, context, info) => { return resolve(root, args, context, info) } +const validateCreatePost = async (resolve, root, args, context, info) => { + const { categoryIds } = args + if (!Array.isArray(categoryIds) || !categoryIds.length) { + throw new UserInputError('You cannot create a post without at least one category') + } + return resolve(root, args, context, info) +} + export default { Mutation: { CreateComment: validateCommentCreation, UpdateComment: validateUpdateComment, + CreatePost: validateCreatePost, }, } diff --git a/backend/src/schema/resolvers/posts.js b/backend/src/schema/resolvers/posts.js index ea1f680bd..37710dc38 100644 --- a/backend/src/schema/resolvers/posts.js +++ b/backend/src/schema/resolvers/posts.js @@ -110,6 +110,7 @@ export default { const { categoryIds } = params delete params.categoryIds params = await fileUpload(params, { file: 'imageUpload', url: 'image' }) + params.id = params.id || uuid() let createPostCypher = `CREATE (post:Post {params}) diff --git a/backend/src/schema/resolvers/posts.spec.js b/backend/src/schema/resolvers/posts.spec.js index 15376c8a4..ab90af27e 100644 --- a/backend/src/schema/resolvers/posts.spec.js +++ b/backend/src/schema/resolvers/posts.spec.js @@ -19,20 +19,9 @@ const oldTitle = 'Old title' const oldContent = 'Old content' const newTitle = 'New title' const newContent = 'New content' -const createPostVariables = { title: postTitle, content: postContent } -const createPostWithCategoriesMutation = gql` - mutation($title: String!, $content: String!, $categoryIds: [ID]) { - CreatePost(title: $title, content: $content, categoryIds: $categoryIds) { - id - title - } - } -` -const createPostWithCategoriesVariables = { - title: postTitle, - content: postContent, - categoryIds: ['cat9', 'cat4', 'cat15'], -} +const categoryIds = ['cat9', 'cat4', 'cat15'] +let createPostVariables + const postQueryWithCategories = gql` query($id: ID) { Post(id: $id) { @@ -42,11 +31,6 @@ const postQueryWithCategories = gql` } } ` -const createPostWithoutCategoriesVariables = { - title: 'This is a post without categories', - content: 'I should be able to filter it out', - categoryIds: null, -} const postQueryFilteredByCategory = gql` query Post($filter: _PostFilter) { Post(filter: $filter) { @@ -58,14 +42,14 @@ const postQueryFilteredByCategory = gql` } } ` -const postCategoriesFilterParam = { categories_some: { id_in: ['cat4'] } } +const postCategoriesFilterParam = { categories_some: { id_in: categoryIds } } const postQueryFilteredByCategoryVariables = { filter: postCategoriesFilterParam, } const createPostMutation = gql` - mutation($title: String!, $content: String!) { - CreatePost(title: $title, content: $content) { + mutation($id: ID, $title: String!, $content: String!, $categoryIds: [ID]) { + CreatePost(id: $id, title: $title, content: $content, categoryIds: $categoryIds) { id title content @@ -88,6 +72,29 @@ beforeEach(async () => { password: '1234', } await factory.create('User', userParams) + await Promise.all([ + factory.create('Category', { + id: 'cat9', + name: 'Democracy & Politics', + icon: 'university', + }), + factory.create('Category', { + id: 'cat4', + name: 'Environment & Nature', + icon: 'tree', + }), + factory.create('Category', { + id: 'cat15', + name: 'Consumption & Sustainability', + icon: 'shopping-cart', + }), + ]) + createPostVariables = { + id: 'p3589', + title: postTitle, + content: postContent, + categoryIds, + } }) afterEach(async () => { @@ -152,8 +159,13 @@ describe('CreatePost', () => { describe('language', () => { it('allows a user to set the language of the post', async () => { const createPostWithLanguageMutation = gql` - mutation($title: String!, $content: String!, $language: String) { - CreatePost(title: $title, content: $content, language: $language) { + mutation($title: String!, $content: String!, $language: String, $categoryIds: [ID]) { + CreatePost( + title: $title + content: $content + language: $language + categoryIds: $categoryIds + ) { language } } @@ -162,6 +174,7 @@ describe('CreatePost', () => { title: postTitle, content: postContent, language: 'en', + categoryIds, } const expected = { CreatePost: { language: 'en' } } await expect( @@ -171,51 +184,29 @@ describe('CreatePost', () => { }) describe('categories', () => { - let postWithCategories - beforeEach(async () => { - await Promise.all([ - factory.create('Category', { - id: 'cat9', - name: 'Democracy & Politics', - icon: 'university', - }), - factory.create('Category', { - id: 'cat4', - name: 'Environment & Nature', - icon: 'tree', - }), - factory.create('Category', { - id: 'cat15', - name: 'Consumption & Sustainability', - icon: 'shopping-cart', - }), - ]) - postWithCategories = await client.request( - createPostWithCategoriesMutation, - createPostWithCategoriesVariables, + it('throws an error if categoryIds is not an array', async () => { + createPostVariables.categoryIds = null + await expect(client.request(createPostMutation, createPostVariables)).rejects.toThrow( + 'You cannot create a post without at least one category', ) }) - 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, - } - - await expect( - client.request(postQueryWithCategories, postQueryWithCategoriesVariables), - ).resolves.toEqual({ Post: [{ categories: expect.arrayContaining(expected) }] }) + it('requires at least one category for successful creation', async () => { + createPostVariables.categoryIds = [] + await expect(client.request(createPostMutation, createPostVariables)).rejects.toThrow( + 'You cannot create a post without at least one category', + ) }) it('allows a user to filter for posts by category', async () => { - await client.request(createPostWithCategoriesMutation, createPostWithoutCategoriesVariables) - const categoryIds = [{ id: 'cat4' }, { id: 'cat15' }, { id: 'cat9' }] + await client.request(createPostMutation, createPostVariables) + const categoryIdsArray = [{ id: 'cat4' }, { id: 'cat15' }, { id: 'cat9' }] const expected = { Post: [ { title: postTitle, - id: postWithCategories.CreatePost.id, - categories: expect.arrayContaining(categoryIds), + id: 'p3589', + categories: expect.arrayContaining(categoryIdsArray), }, ], } @@ -231,6 +222,28 @@ describe('UpdatePost', () => { let updatePostMutation let updatePostVariables beforeEach(async () => { + await Promise.all([ + factory.create('Category', { + id: 'cat9', + name: 'Democracy & Politics', + icon: 'university', + }), + factory.create('Category', { + id: 'cat4', + name: 'Environment & Nature', + icon: 'tree', + }), + factory.create('Category', { + id: 'cat15', + name: 'Consumption & Sustainability', + icon: 'shopping-cart', + }), + factory.create('Category', { + id: 'cat27', + name: 'Animal Protection', + icon: 'paw', + }), + ]) const asAuthor = Factory() await asAuthor.create('User', authorParams) await asAuthor.authenticateAs(authorParams) @@ -238,6 +251,7 @@ describe('UpdatePost', () => { id: 'p1', title: oldTitle, content: oldContent, + categoryIds, }) updatePostMutation = gql` mutation($id: ID!, $title: String!, $content: String!, $categoryIds: [ID]) { @@ -294,36 +308,10 @@ describe('UpdatePost', () => { }) describe('categories', () => { - let postWithCategories beforeEach(async () => { - await Promise.all([ - factory.create('Category', { - id: 'cat9', - name: 'Democracy & Politics', - icon: 'university', - }), - factory.create('Category', { - id: 'cat4', - name: 'Environment & Nature', - icon: 'tree', - }), - factory.create('Category', { - id: 'cat15', - name: 'Consumption & Sustainability', - icon: 'shopping-cart', - }), - factory.create('Category', { - id: 'cat27', - name: 'Animal Protection', - icon: 'paw', - }), - ]) - postWithCategories = await client.request( - createPostWithCategoriesMutation, - createPostWithCategoriesVariables, - ) + await client.request(createPostMutation, createPostVariables) updatePostVariables = { - id: postWithCategories.CreatePost.id, + id: 'p3589', title: newTitle, content: newContent, categoryIds: ['cat27'], @@ -334,7 +322,7 @@ describe('UpdatePost', () => { await client.request(updatePostMutation, updatePostVariables) const expected = [{ id: 'cat27' }] const postQueryWithCategoriesVariables = { - id: postWithCategories.CreatePost.id, + id: 'p3589', } await expect( client.request(postQueryWithCategories, postQueryWithCategoriesVariables), @@ -365,6 +353,7 @@ describe('DeletePost', () => { await asAuthor.create('Post', { id: 'p1', content: 'To be deleted', + categoryIds, }) }) diff --git a/backend/src/seed/factories/posts.js b/backend/src/seed/factories/posts.js index b8e30ee2e..f2f1432dc 100644 --- a/backend/src/seed/factories/posts.js +++ b/backend/src/seed/factories/posts.js @@ -16,6 +16,7 @@ export default function(params) { image = faker.image.unsplash.imageUrl(), visibility = 'public', deleted = false, + categoryIds, } = params return { @@ -28,6 +29,7 @@ export default function(params) { $image: String $visibility: Visibility $deleted: Boolean + $categoryIds: [ID] ) { CreatePost( id: $id @@ -37,12 +39,13 @@ export default function(params) { image: $image visibility: $visibility deleted: $deleted + categoryIds: $categoryIds ) { title content } } `, - variables: { id, slug, title, content, image, visibility, deleted }, + variables: { id, slug, title, content, image, visibility, deleted, categoryIds }, } }