diff --git a/backend/src/schema/resolvers/posts.spec.js b/backend/src/schema/resolvers/posts.spec.js index 7c5e88d69..af2d8089c 100644 --- a/backend/src/schema/resolvers/posts.spec.js +++ b/backend/src/schema/resolvers/posts.spec.js @@ -91,7 +91,7 @@ afterEach(async () => { }) describe('Post', () => { - const postQuery = gql` + const postQueryFilteredByCategories = gql` query Post($filter: _PostFilter) { Post(filter: $filter) { id @@ -102,13 +102,28 @@ describe('Post', () => { } ` + const postQueryFilteredByEmotions = gql` + query Post($filter: _PostFilter) { + Post(filter: $filter) { + id + emotions { + emotion + } + } + } + ` + describe('can be filtered', () => { - it('by categories', async () => { - await Promise.all([ + let post31, post32 + beforeEach(async () => { + ;[post31, post32] = await Promise.all([ factory.create('Post', { id: 'p31', categoryIds: ['cat4'] }), factory.create('Post', { id: 'p32', categoryIds: ['cat15'] }), factory.create('Post', { id: 'p33', categoryIds: ['cat9'] }), ]) + }) + + it('by categories', async () => { const expected = { data: { Post: [ @@ -120,7 +135,50 @@ describe('Post', () => { }, } variables = { ...variables, filter: { categories_some: { id_in: ['cat9'] } } } - await expect(query({ query: postQuery, variables })).resolves.toMatchObject(expected) + await expect( + query({ query: postQueryFilteredByCategories, variables }), + ).resolves.toMatchObject(expected) + }) + + it('by emotions', async () => { + const expected = { + data: { + Post: [ + { + id: 'p31', + emotions: [{ emotion: 'happy' }], + }, + ], + }, + } + await user.relateTo(post31, 'emoted', { emotion: 'happy' }) + variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy'] } } } + await expect(query({ query: postQueryFilteredByEmotions, variables })).resolves.toMatchObject( + expected, + ) + }) + + it('supports filtering by multiple emotions', async () => { + const expected = [ + { + id: 'p31', + emotions: [{ emotion: 'happy' }], + }, + { + id: 'p32', + emotions: [{ emotion: 'cry' }], + }, + ] + await user.relateTo(post31, 'emoted', { emotion: 'happy' }) + await user.relateTo(post32, 'emoted', { emotion: 'cry' }) + variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy', 'cry'] } } } + await expect(query({ query: postQueryFilteredByEmotions, variables })).resolves.toMatchObject( + { + data: { + Post: expect.arrayContaining(expected), + }, + }, + ) }) }) }) diff --git a/backend/src/schema/types/type/Post.gql b/backend/src/schema/types/type/Post.gql index 5f0aeea7a..5b11757d3 100644 --- a/backend/src/schema/types/type/Post.gql +++ b/backend/src/schema/types/type/Post.gql @@ -1,92 +1,92 @@ type Post { - id: ID! - activityId: String - objectId: String - author: User @relation(name: "WROTE", direction: "IN") - title: String! - slug: String - content: String! - contentExcerpt: String - image: String - imageUpload: Upload - visibility: Visibility - deleted: Boolean - disabled: Boolean - disabledBy: User @relation(name: "DISABLED", direction: "IN") - createdAt: String - updatedAt: String - language: String - relatedContributions: [Post]! - @cypher( - statement: """ - MATCH (this)-[: TAGGED|CATEGORIZED]->(categoryOrTag)<-[: TAGGED|CATEGORIZED]-(post: Post) - WHERE NOT post.deleted AND NOT post.disabled - RETURN DISTINCT post - LIMIT 10 - """ - ) + id: ID! + activityId: String + objectId: String + author: User @relation(name: "WROTE", direction: "IN") + title: String! + slug: String + content: String! + contentExcerpt: String + image: String + imageUpload: Upload + visibility: Visibility + deleted: Boolean + disabled: Boolean + disabledBy: User @relation(name: "DISABLED", direction: "IN") + createdAt: String + updatedAt: String + language: String + relatedContributions: [Post]! + @cypher( + statement: """ + MATCH (this)-[:TAGGED|CATEGORIZED]->(categoryOrTag)<-[:TAGGED|CATEGORIZED]-(post:Post) + WHERE NOT post.deleted AND NOT post.disabled + RETURN DISTINCT post + LIMIT 10 + """ + ) - tags: [Tag]! @relation(name: "TAGGED", direction: "OUT") - categories: [Category]! @relation(name: "CATEGORIZED", direction: "OUT") + tags: [Tag]! @relation(name: "TAGGED", direction: "OUT") + categories: [Category]! @relation(name: "CATEGORIZED", direction: "OUT") - comments: [Comment]! @relation(name: "COMMENTS", direction: "IN") - commentsCount: Int! - @cypher( - statement: "MATCH (this)<-[: COMMENTS]-(r: Comment) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)" - ) + comments: [Comment]! @relation(name: "COMMENTS", direction: "IN") + commentsCount: Int! + @cypher( + statement: "MATCH (this)<-[:COMMENTS]-(r:Comment) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)" + ) - shoutedBy: [User]! @relation(name: "SHOUTED", direction: "IN") - shoutedCount: Int! - @cypher( - statement: "MATCH (this)<-[: SHOUTED]-(r: User) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)" - ) + shoutedBy: [User]! @relation(name: "SHOUTED", direction: "IN") + shoutedCount: Int! + @cypher( + statement: "MATCH (this)<-[:SHOUTED]-(r:User) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)" + ) - # Has the currently logged in user shouted that post? - shoutedByCurrentUser: Boolean! - @cypher( - statement: "MATCH (this)<-[: SHOUTED]-(u: User { id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1" - ) + # Has the currently logged in user shouted that post? + shoutedByCurrentUser: Boolean! + @cypher( + statement: "MATCH (this)<-[:SHOUTED]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1" + ) - emotions: [EMOTED] - emotionsCount: Int! - @cypher(statement: "MATCH (this)<-[emoted: EMOTED]-(: User) RETURN COUNT(DISTINCT emoted)") + emotions: [EMOTED] + emotionsCount: Int! + @cypher(statement: "MATCH (this)<-[emoted:EMOTED]-(:User) RETURN COUNT(DISTINCT emoted)") } input _PostInput { - id: ID! + id: ID! } type Mutation { - CreatePost( - id: ID - title: String! - slug: String - content: String! - image: String - imageUpload: Upload - visibility: Visibility - language: String - categoryIds: [ID] - contentExcerpt: String - ): Post - UpdatePost( - id: ID! - title: String! - slug: String - content: String! - contentExcerpt: String - image: String - imageUpload: Upload - visibility: Visibility - language: String - categoryIds: [ID] - ): Post - DeletePost(id: ID!): Post - AddPostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED - RemovePostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED + CreatePost( + id: ID + title: String! + slug: String + content: String! + image: String + imageUpload: Upload + visibility: Visibility + language: String + categoryIds: [ID] + contentExcerpt: String + ): Post + UpdatePost( + id: ID! + title: String! + slug: String + content: String! + contentExcerpt: String + image: String + imageUpload: Upload + visibility: Visibility + language: String + categoryIds: [ID] + ): Post + DeletePost(id: ID!): Post + AddPostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED + RemovePostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED } type Query { - PostsEmotionsCountByEmotion(postId: ID!, data: _EMOTEDInput!): Int! - PostsEmotionsByCurrentUser(postId: ID!): [String] -} \ No newline at end of file + PostsEmotionsCountByEmotion(postId: ID!, data: _EMOTEDInput!): Int! + PostsEmotionsByCurrentUser(postId: ID!): [String] +} diff --git a/backend/src/seed/factories/posts.js b/backend/src/seed/factories/posts.js index e81251c53..3058204a1 100644 --- a/backend/src/seed/factories/posts.js +++ b/backend/src/seed/factories/posts.js @@ -30,7 +30,7 @@ export default function create() { let { categories, categoryIds } = args delete args.categories delete args.categoryIds - if (categories && categoryIds) throw new Error('You provided both category and categoryIds') + if (categories && categoryIds) throw new Error('You provided both categories and categoryIds') if (categoryIds) categories = await Promise.all(categoryIds.map(id => neodeInstance.find('Category', id))) categories = categories || (await Promise.all([factoryInstance.create('Category')]))