diff --git a/backend/src/db/graphql/posts.js b/backend/src/db/graphql/posts.js index e758c523e..2669d6f24 100644 --- a/backend/src/db/graphql/posts.js +++ b/backend/src/db/graphql/posts.js @@ -71,3 +71,18 @@ export const profilePagePosts = () => { } ` } + +export const searchPosts = () => { + return gql` + query ($query: String!, $firstPosts: Int, $postsOffset: Int) { + searchPosts(query: $query, firstPosts: $firstPosts, postsOffset: $postsOffset) { + postCount + posts { + id + title + content + } + } + } + ` +} diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index 96bffbedb..d77363c29 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -299,10 +299,10 @@ export default shield( { Query: { '*': deny, - searchResults: isAuthenticated, - searchPosts: isAuthenticated, - searchUsers: isAuthenticated, - searchHashtags: isAuthenticated, + searchResults: allow, + searchPosts: allow, + searchUsers: allow, + searchHashtags: allow, embed: allow, Category: allow, Tag: allow, diff --git a/backend/src/schema/resolvers/postsInGroups.spec.js b/backend/src/schema/resolvers/postsInGroups.spec.js index 5617a13a4..48178ae93 100644 --- a/backend/src/schema/resolvers/postsInGroups.spec.js +++ b/backend/src/schema/resolvers/postsInGroups.spec.js @@ -12,6 +12,7 @@ import { postQuery, filterPosts, profilePagePosts, + searchPosts, } from '../../db/graphql/posts' // eslint-disable-next-line no-unused-vars import { DESCRIPTION_WITHOUT_HTML_LENGTH_MIN } from '../../constants/groups' @@ -943,6 +944,192 @@ describe('Posts in Groups', () => { }) }) }) + + describe('searchPosts', () => { + describe('without authentication', () => { + beforeEach(async () => { + authenticatedUser = null + }) + + it('finds nothing', async () => { + const result = await query({ + query: searchPosts(), + variables: { + query: 'post', + postsOffset: 0, + firstPosts: 25, + }, + }) + expect(result.data.searchPosts.posts).toHaveLength(0) + expect(result).toMatchObject({ + data: { + searchPosts: { + postCount: 0, + posts: [], + }, + }, + }) + }) + }) + + describe('as new user', () => { + beforeEach(async () => { + authenticatedUser = newUser + }) + + it('finds the post of the public group and the post without group', async () => { + const result = await query({ + query: searchPosts(), + variables: { + query: 'post', + postsOffset: 0, + firstPosts: 25, + }, + }) + expect(result.data.searchPosts.posts).toHaveLength(2) + expect(result).toMatchObject({ + data: { + searchPosts: { + postCount: 2, + posts: expect.arrayContaining([ + { + id: 'post-to-public-group', + title: 'A post to a public group', + content: 'I am posting into a public group as a member of the group', + }, + { + id: 'post-without-group', + title: 'A post without a group', + content: 'As a new user, I do not belong to a group yet.', + }, + ]), + }, + }, + }) + }) + }) + + describe('without membership of group', () => { + beforeEach(async () => { + authenticatedUser = await anyUser.toJson() + }) + + it('finds the post of the public group and the post without group', async () => { + const result = await query({ + query: searchPosts(), + variables: { + query: 'post', + postsOffset: 0, + firstPosts: 25, + }, + }) + expect(result.data.searchPosts.posts).toHaveLength(2) + expect(result).toMatchObject({ + data: { + searchPosts: { + postCount: 2, + posts: expect.arrayContaining([ + { + id: 'post-to-public-group', + title: 'A post to a public group', + content: 'I am posting into a public group as a member of the group', + }, + { + id: 'post-without-group', + title: 'A post without a group', + content: 'As a new user, I do not belong to a group yet.', + }, + ]), + }, + }, + }) + }) + }) + + describe('with pending membership of group', () => { + beforeEach(async () => { + authenticatedUser = await pendingUser.toJson() + }) + + it('finds the post of the public group and the post without group', async () => { + const result = await query({ + query: searchPosts(), + variables: { + query: 'post', + postsOffset: 0, + firstPosts: 25, + }, + }) + expect(result.data.searchPosts.posts).toHaveLength(2) + expect(result).toMatchObject({ + data: { + searchPosts: { + postCount: 2, + posts: expect.arrayContaining([ + { + id: 'post-to-public-group', + title: 'A post to a public group', + content: 'I am posting into a public group as a member of the group', + }, + { + id: 'post-without-group', + title: 'A post without a group', + content: 'As a new user, I do not belong to a group yet.', + }, + ]), + }, + }, + }) + }) + }) + + describe('as member of group', () => { + beforeEach(async () => { + authenticatedUser = await allGroupsUser.toJson() + }) + + it('finds all posts', async () => { + const result = await query({ + query: searchPosts(), + variables: { + query: 'post', + postsOffset: 0, + firstPosts: 25, + }, + }) + expect(result.data.searchPosts.posts).toHaveLength(4) + expect(result).toMatchObject({ + data: { + searchPosts: { + postCount: 4, + posts: expect.arrayContaining([ + { + id: 'post-to-public-group', + title: 'A post to a public group', + content: 'I am posting into a public group as a member of the group', + }, + { + id: 'post-without-group', + title: 'A post without a group', + content: 'As a new user, I do not belong to a group yet.', + }, + { + id: 'post-to-closed-group', + title: 'A post to a closed group', + content: 'I am posting into a closed group as a member of the group', + }, + { + id: 'post-to-hidden-group', + title: 'A post to a hidden group', + content: 'I am posting into a hidden group as a member of the group', + }, + ]), + }, + }, + }) + }) + }) + }) }) }) diff --git a/backend/src/schema/resolvers/searches.js b/backend/src/schema/resolvers/searches.js index aba89fd4c..63279b4bf 100644 --- a/backend/src/schema/resolvers/searches.js +++ b/backend/src/schema/resolvers/searches.js @@ -119,7 +119,8 @@ export default { Query: { searchPosts: async (_parent, args, context, _resolveInfo) => { const { query, postsOffset, firstPosts } = args - const { id: userId } = context.user + let userId = null + if (context.user) userId = context.user.id return { postCount: getSearchResults( context, @@ -179,7 +180,8 @@ export default { }, searchResults: async (_parent, args, context, _resolveInfo) => { const { query, limit } = args - const { id: userId } = context.user + let userId = null + if (context.user) userId = context.user.id const searchType = query.replace(/^([!@#]?).*$/, '$1') const searchString = query.replace(/^([!@#])/, '')