From b8ac4cc3ca33653fd529ae7b637418d4e06d76ef Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 5 Sep 2022 17:41:21 +0200 Subject: [PATCH 1/5] add saveCategoruSettings mutation to schema --- backend/src/middleware/permissionsMiddleware.js | 1 + backend/src/schema/types/type/User.gql | 2 ++ 2 files changed, 3 insertions(+) diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index b5ce4fe07..71a44f225 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -178,6 +178,7 @@ export default shield( GenerateInviteCode: isAuthenticated, switchUserRole: isAdmin, markTeaserAsViewed: allow, + saveCategorySettings: isAuthenticated, }, User: { email: or(isMyOwn, isAdmin), diff --git a/backend/src/schema/types/type/User.gql b/backend/src/schema/types/type/User.gql index 871e73ad8..b84606ff7 100644 --- a/backend/src/schema/types/type/User.gql +++ b/backend/src/schema/types/type/User.gql @@ -220,4 +220,6 @@ type Mutation { unblockUser(id: ID!): User switchUserRole(role: UserRole!, id: ID!): User + + saveCategorySettings(activeCategories: [String]): Boolean } From 572377acfde8e6a07f3fb32b598b4cf9b15b6969 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 5 Sep 2022 20:06:10 +0200 Subject: [PATCH 2/5] add active categories to user --- backend/src/schema/types/type/User.gql | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/backend/src/schema/types/type/User.gql b/backend/src/schema/types/type/User.gql index b84606ff7..d2ac79037 100644 --- a/backend/src/schema/types/type/User.gql +++ b/backend/src/schema/types/type/User.gql @@ -114,6 +114,14 @@ type User { badgesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)") emotions: [EMOTED] + + activeCategories: [Category] @cypher( + statement: """ + MATCH (category:Category) + WHERE NOT ((this)-[:NOT_INTERESTED_IN]->(category)) + RETURN category + """ + ) } From fc6d544ea7399c711ba7ce5c6134cf0de94714ab Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 5 Sep 2022 20:07:14 +0200 Subject: [PATCH 3/5] add resolver for save active categories --- backend/src/schema/resolvers/users.js | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/backend/src/schema/resolvers/users.js b/backend/src/schema/resolvers/users.js index 5dc78c5e1..4ab721b06 100644 --- a/backend/src/schema/resolvers/users.js +++ b/backend/src/schema/resolvers/users.js @@ -269,6 +269,46 @@ export default { session.close() } }, + saveCategorySettings: async (object, args, context, resolveInfo) => { + const { activeCategories } = args + const { + user: { id }, + } = context + + const session = context.driver.session() + await session.writeTransaction((transaction) => { + return transaction.run( + ` + MATCH (user:User { id: $id })-[previousCategories:NOT_INTERESTED_IN]->(category:Category) + DELETE previousCategories + RETURN user, category + `, + { id }, + ) + }) + + const writeTxResultPromise = session.writeTransaction(async (transaction) => { + const saveCategorySettingsResponse = await transaction.run( + ` + MATCH (category:Category) WHERE NOT category.id IN $activeCategories + MATCH (user:User { id: $id }) + MERGE (user)-[r:NOT_INTERESTED_IN]->(category) + RETURN user, r, category + `, + { id, activeCategories }, + ) + const [user] = await saveCategorySettingsResponse.records.map((record) => + record.get('user'), + ) + return user + }) + try { + await writeTxResultPromise + return true + } finally { + session.close() + } + }, }, User: { email: async (parent, params, context, resolveInfo) => { From 4e5d4cb8734a8f5c581e78cd7b4717d82fc27a8d Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 5 Sep 2022 20:51:48 +0200 Subject: [PATCH 4/5] basic tests for save category settings --- backend/src/schema/resolvers/users.js | 1 - backend/src/schema/resolvers/users.spec.js | 102 +++++++++++++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/backend/src/schema/resolvers/users.js b/backend/src/schema/resolvers/users.js index 4ab721b06..23a39a2a1 100644 --- a/backend/src/schema/resolvers/users.js +++ b/backend/src/schema/resolvers/users.js @@ -286,7 +286,6 @@ export default { { id }, ) }) - const writeTxResultPromise = session.writeTransaction(async (transaction) => { const saveCategorySettingsResponse = await transaction.run( ` diff --git a/backend/src/schema/resolvers/users.spec.js b/backend/src/schema/resolvers/users.spec.js index 920ef52ea..e655bdf28 100644 --- a/backend/src/schema/resolvers/users.spec.js +++ b/backend/src/schema/resolvers/users.spec.js @@ -3,6 +3,7 @@ import { gql } from '../../helpers/jest' import { getNeode, getDriver } from '../../db/neo4j' import createServer from '../../server' import { createTestClient } from 'apollo-server-testing' +import { categories } from '../../constants/categories' const categoryIds = ['cat9'] let user @@ -56,6 +57,12 @@ const switchUserRoleMutation = gql` } ` +const saveCategorySettings = gql` + mutation ($activeCategories: [String]) { + saveCategorySettings(activeCategories: $activeCategories) + } +` + beforeAll(async () => { await cleanDatabase() @@ -544,3 +551,98 @@ describe('switch user role', () => { }) }) }) + +describe('save category settings', () => { + beforeAll(async () => { + await Promise.all( + categories.map(({ icon, name }, index) => { + Factory.build('category', { + id: `cat${index + 1}`, + slug: name, + name, + icon, + }) + }), + ) + }) + + beforeEach(async () => { + user = await Factory.build('user', { + id: 'user', + role: 'user', + }) + variables = { + activeCategories: ['cat1', 'cat3', 'cat5'], + } + }) + + describe('not authenticated', () => { + beforeEach(async () => { + authenticatedUser = undefined + }) + + it('throws an error', async () => { + await expect(mutate({ mutation: saveCategorySettings, variables })).resolves.toEqual( + expect.objectContaining({ + errors: [ + expect.objectContaining({ + message: 'Not Authorized!', + }), + ], + }), + ) + }) + }) + + describe('authenticated', () => { + beforeEach(async () => { + authenticatedUser = await user.toJson() + }) + + describe('no categories saved', () => { + it('returns true for active categories mutation', async () => { + await expect(mutate({ mutation: saveCategorySettings, variables })).resolves.toEqual( + expect.objectContaining({ + data: { saveCategorySettings: true }, + }), + ) + }) + + describe('query for user', () => { + beforeEach(async () => { + await mutate({ mutation: saveCategorySettings, variables }) + }) + + it.skip('returns the active categories when user is queried', async () => { + const userQuery = gql` + query ($id: ID) { + User(id: $id) { + activeCategories { + id + } + } + } + ` + + await expect( + query({ query: userQuery, variables: { id: authenticatedUser.id } }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + User: [ + { + activeCategories: expect.arrayContaining([ + { id: 'cat1' }, + { id: 'cat3' }, + { id: 'cat5' }, + ]), + }, + ], + }, + }), + ) + }) + }) + }) + }) +}) From 8a688ca78be0663527336f9b2502a3d796749308 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 6 Sep 2022 15:14:31 +0200 Subject: [PATCH 5/5] improve tests --- backend/src/schema/resolvers/users.spec.js | 72 ++++++++++++++++++---- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/backend/src/schema/resolvers/users.spec.js b/backend/src/schema/resolvers/users.spec.js index e655bdf28..7116d2201 100644 --- a/backend/src/schema/resolvers/users.spec.js +++ b/backend/src/schema/resolvers/users.spec.js @@ -553,7 +553,7 @@ describe('switch user role', () => { }) describe('save category settings', () => { - beforeAll(async () => { + beforeEach(async () => { await Promise.all( categories.map(({ icon, name }, index) => { Factory.build('category', { @@ -599,6 +599,16 @@ describe('save category settings', () => { authenticatedUser = await user.toJson() }) + const userQuery = gql` + query ($id: ID) { + User(id: $id) { + activeCategories { + id + } + } + } + ` + describe('no categories saved', () => { it('returns true for active categories mutation', async () => { await expect(mutate({ mutation: saveCategorySettings, variables })).resolves.toEqual( @@ -613,17 +623,7 @@ describe('save category settings', () => { await mutate({ mutation: saveCategorySettings, variables }) }) - it.skip('returns the active categories when user is queried', async () => { - const userQuery = gql` - query ($id: ID) { - User(id: $id) { - activeCategories { - id - } - } - } - ` - + it('returns the active categories when user is queried', async () => { await expect( query({ query: userQuery, variables: { id: authenticatedUser.id } }), ).resolves.toEqual( @@ -644,5 +644,53 @@ describe('save category settings', () => { }) }) }) + + describe('categories already saved', () => { + beforeEach(async () => { + variables = { + activeCategories: ['cat1', 'cat3', 'cat5'], + } + await mutate({ mutation: saveCategorySettings, variables }) + variables = { + activeCategories: ['cat10', 'cat11', 'cat12', 'cat8', 'cat9'], + } + }) + + it('returns true', async () => { + await expect(mutate({ mutation: saveCategorySettings, variables })).resolves.toEqual( + expect.objectContaining({ + data: { saveCategorySettings: true }, + }), + ) + }) + + describe('query for user', () => { + beforeEach(async () => { + await mutate({ mutation: saveCategorySettings, variables }) + }) + + it('returns the new active categories when user is queried', async () => { + await expect( + query({ query: userQuery, variables: { id: authenticatedUser.id } }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + User: [ + { + activeCategories: expect.arrayContaining([ + { id: 'cat10' }, + { id: 'cat11' }, + { id: 'cat12' }, + { id: 'cat8' }, + { id: 'cat9' }, + ]), + }, + ], + }, + }), + ) + }) + }) + }) }) })