diff --git a/backend/src/constants/subscriptions.ts b/backend/src/constants/subscriptions.ts new file mode 100644 index 000000000..ec3e79e63 --- /dev/null +++ b/backend/src/constants/subscriptions.ts @@ -0,0 +1,3 @@ +export const NOTIFICATION_ADDED = 'NOTIFICATION_ADDED' +export const CHAT_MESSAGE_ADDED = 'CHAT_MESSAGE_ADDED' +export const ROOM_COUNT_UPDATED = 'ROOM_COUNT_UPDATED' diff --git a/backend/src/context/database.ts b/backend/src/context/database.ts new file mode 100644 index 000000000..f6ccdc9ca --- /dev/null +++ b/backend/src/context/database.ts @@ -0,0 +1,49 @@ +import { getDriver, getNeode } from '@db/neo4j' + +import type { Driver } from 'neo4j-driver' + +export const query = + (driver: Driver) => + async ({ query, variables = {} }: { driver; query: string; variables: object }) => { + const session = driver.session() + + const result = session.readTransaction(async (transaction) => { + const response = await transaction.run(query, variables) + return response + }) + + try { + return await result + } finally { + await session.close() + } + } + +export const mutate = + (driver: Driver) => + async ({ query, variables = {} }: { driver; query: string; variables: object }) => { + const session = driver.session() + + const result = session.writeTransaction(async (transaction) => { + const response = await transaction.run(query, variables) + return response + }) + + try { + return await result + } finally { + await session.close() + } + } + +export default () => { + const driver = getDriver() + const neode = getNeode() + + return { + driver, + neode, + query: query(driver), + mutate: mutate(driver), + } +} diff --git a/backend/src/context/pubsub.ts b/backend/src/context/pubsub.ts new file mode 100644 index 000000000..003347b16 --- /dev/null +++ b/backend/src/context/pubsub.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { RedisPubSub } from 'graphql-redis-subscriptions' +import { PubSub } from 'graphql-subscriptions' +import Redis from 'ioredis' + +import CONFIG from '@config/index' + +export default () => { + if (!CONFIG.REDIS_DOMAIN || CONFIG.REDIS_PORT || CONFIG.REDIS_PASSWORD) { + return new PubSub() + } + + const options = { + host: CONFIG.REDIS_DOMAIN, + port: CONFIG.REDIS_PORT, + password: CONFIG.REDIS_PASSWORD, + retryStrategy: (times) => { + return Math.min(times * 50, 2000) + }, + } + return new RedisPubSub({ + publisher: new Redis(options), + subscriber: new Redis(options), + }) +} diff --git a/backend/src/graphql/resolvers/comments.spec.ts b/backend/src/graphql/resolvers/comments.spec.ts index a7177d754..9681abe9a 100644 --- a/backend/src/graphql/resolvers/comments.spec.ts +++ b/backend/src/graphql/resolvers/comments.spec.ts @@ -2,40 +2,41 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' -const driver = getDriver() -const neode = getNeode() +const database = databaseContext() let variables, mutate, authenticatedUser, commentAuthor, newlyCreatedComment +let server: ApolloServer beforeAll(async () => { await cleanDatabase() - const { server } = createServer({ - context: () => { - return { - driver, - user: authenticatedUser, - } - }, - }) + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/require-await + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + server = createServer({ context }).server + mutate = createTestClient(server).mutate }) afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) beforeEach(async () => { variables = {} - await neode.create('Category', { + await database.neode.create('Category', { id: 'cat9', name: 'Democracy & Politics', icon: 'university', @@ -103,7 +104,7 @@ describe('CreateComment', () => { describe('authenticated', () => { beforeEach(async () => { - const user = await neode.create('User', { name: 'Author' }) + const user = await database.neode.create('User', { name: 'Author' }) authenticatedUser = await user.toJson() }) diff --git a/backend/src/graphql/resolvers/follow.ts b/backend/src/graphql/resolvers/follow.ts index d08f243b1..8d69a7d5b 100644 --- a/backend/src/graphql/resolvers/follow.ts +++ b/backend/src/graphql/resolvers/follow.ts @@ -1,7 +1,5 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ - /* eslint-disable @typescript-eslint/no-unsafe-assignment */ - /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { getNeode } from '@db/neo4j' diff --git a/backend/src/graphql/resolvers/groups.spec.ts b/backend/src/graphql/resolvers/groups.spec.ts index 664f57397..545865c20 100644 --- a/backend/src/graphql/resolvers/groups.spec.ts +++ b/backend/src/graphql/resolvers/groups.spec.ts @@ -6,8 +6,8 @@ import { createTestClient } from 'apollo-server-testing' import CONFIG from '@config/index' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { groupMembersQuery } from '@graphql/queries/groupMembersQuery' @@ -16,10 +16,7 @@ import { joinGroupMutation } from '@graphql/queries/joinGroupMutation' import { leaveGroupMutation } from '@graphql/queries/leaveGroupMutation' import { removeUserFromGroupMutation } from '@graphql/queries/removeUserFromGroupMutation' import { updateGroupMutation } from '@graphql/queries/updateGroupMutation' -import createServer from '@src/server' - -const driver = getDriver() -const neode = getNeode() +import createServer, { getContext } from '@src/server' let authenticatedUser let user @@ -35,15 +32,12 @@ const descriptionAdditional100 = ' 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789' let variables = {} -const { server } = createServer({ - context: () => { - return { - driver, - neode, - user: authenticatedUser, - } - }, -}) +const database = databaseContext() +// eslint-disable-next-line @typescript-eslint/no-unsafe-return +const contextUser = async (_req) => authenticatedUser +const context = getContext({ user: contextUser, database }) + +const { server } = createServer({ context }) const { mutate, query } = createTestClient(server) const seedBasicsAndClearAuthentication = async () => { @@ -60,25 +54,25 @@ const seedBasicsAndClearAuthentication = async () => { }, ) await Promise.all([ - neode.create('Category', { + database.neode.create('Category', { id: 'cat4', name: 'Environment & Nature', slug: 'environment-nature', icon: 'tree', }), - neode.create('Category', { + database.neode.create('Category', { id: 'cat9', name: 'Democracy & Politics', slug: 'democracy-politics', icon: 'university', }), - neode.create('Category', { + database.neode.create('Category', { id: 'cat15', name: 'Consumption & Sustainability', slug: 'consumption-sustainability', icon: 'shopping-cart', }), - neode.create('Category', { + database.neode.create('Category', { id: 'cat27', name: 'Animal Protection', slug: 'animal-protection', @@ -241,7 +235,9 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('in mode', () => { diff --git a/backend/src/graphql/resolvers/messages.spec.ts b/backend/src/graphql/resolvers/messages.spec.ts index 8061cf460..81799fdf1 100644 --- a/backend/src/graphql/resolvers/messages.spec.ts +++ b/backend/src/graphql/resolvers/messages.spec.ts @@ -3,49 +3,47 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' +import databaseContext from '@context/database' +import pubsubContext from '@context/pubsub' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { createMessageMutation } from '@graphql/queries/createMessageMutation' import { createRoomMutation } from '@graphql/queries/createRoomMutation' import { markMessagesAsSeen } from '@graphql/queries/markMessagesAsSeen' import { messageQuery } from '@graphql/queries/messageQuery' import { roomQuery } from '@graphql/queries/roomQuery' -import createServer, { pubsub } from '@src/server' - -const driver = getDriver() -const neode = getNeode() +import createServer, { getContext } from '@src/server' let query let mutate let authenticatedUser let chattingUser, otherChattingUser, notChattingUser +const database = databaseContext() +const pubsub = pubsubContext() const pubsubSpy = jest.spyOn(pubsub, 'publish') +let server: ApolloServer beforeAll(async () => { await cleanDatabase() - const { server } = createServer({ - context: () => { - return { - driver, - neode, - user: authenticatedUser, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/require-await + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database, pubsub }) + + server = createServer({ context }).server + query = createTestClient(server).query mutate = createTestClient(server).mutate }) afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('Message', () => { diff --git a/backend/src/graphql/resolvers/messages.ts b/backend/src/graphql/resolvers/messages.ts index c3f362660..6a5a59d27 100644 --- a/backend/src/graphql/resolvers/messages.ts +++ b/backend/src/graphql/resolvers/messages.ts @@ -7,7 +7,7 @@ import { withFilter } from 'graphql-subscriptions' import { neo4jgraphql } from 'neo4j-graphql-js' -import { pubsub, CHAT_MESSAGE_ADDED } from '@src/server' +import { CHAT_MESSAGE_ADDED } from '@constants/subscriptions' import Resolver from './helpers/Resolver' @@ -30,7 +30,7 @@ export default { Subscription: { chatMessageAdded: { subscribe: withFilter( - () => pubsub.asyncIterator(CHAT_MESSAGE_ADDED), + (_, __, context) => context.pubsub.asyncIterator(CHAT_MESSAGE_ADDED), (payload, variables, context) => { return payload.userId === context.user?.id }, diff --git a/backend/src/graphql/resolvers/notifications.ts b/backend/src/graphql/resolvers/notifications.ts index 0168558c3..08a7c48f5 100644 --- a/backend/src/graphql/resolvers/notifications.ts +++ b/backend/src/graphql/resolvers/notifications.ts @@ -6,13 +6,13 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { withFilter } from 'graphql-subscriptions' -import { pubsub, NOTIFICATION_ADDED } from '@src/server' +import { NOTIFICATION_ADDED } from '@constants/subscriptions' export default { Subscription: { notificationAdded: { subscribe: withFilter( - () => pubsub.asyncIterator(NOTIFICATION_ADDED), + (_, __, context) => context.pubsub.asyncIterator(NOTIFICATION_ADDED), (payload, variables, context) => { return payload.notificationAdded.to.id === context.user?.id }, diff --git a/backend/src/graphql/resolvers/observePosts.spec.ts b/backend/src/graphql/resolvers/observePosts.spec.ts index 76ad5b058..fd2786fc9 100644 --- a/backend/src/graphql/resolvers/observePosts.spec.ts +++ b/backend/src/graphql/resolvers/observePosts.spec.ts @@ -1,20 +1,18 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' import CONFIG from '@config/index' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { createPostMutation } from '@graphql/queries/createPostMutation' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const driver = getDriver() -const neode = getNeode() - let query let mutate let authenticatedUser @@ -40,28 +38,27 @@ const postQuery = gql` } ` +const database = databaseContext() + +let server: ApolloServer beforeAll(async () => { await cleanDatabase() - const { server } = createServer({ - context: () => { - return { - driver, - neode, - user: authenticatedUser, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/require-await + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + server = createServer({ context }).server + query = createTestClient(server).query mutate = createTestClient(server).mutate }) afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('observing posts', () => { diff --git a/backend/src/graphql/resolvers/postsInGroups.spec.ts b/backend/src/graphql/resolvers/postsInGroups.spec.ts index 7cb0bdc76..d50451468 100644 --- a/backend/src/graphql/resolvers/postsInGroups.spec.ts +++ b/backend/src/graphql/resolvers/postsInGroups.spec.ts @@ -2,11 +2,12 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import CONFIG from '@config/index' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation' import { createCommentMutation } from '@graphql/queries/createCommentMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation' @@ -17,7 +18,7 @@ import { postQuery } from '@graphql/queries/postQuery' import { profilePagePosts } from '@graphql/queries/profilePagePosts' import { searchPosts } from '@graphql/queries/searchPosts' import { signupVerificationMutation } from '@graphql/queries/signupVerificationMutation' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false @@ -28,9 +29,6 @@ jest.mock('@constants/groups', () => { } }) -const driver = getDriver() -const neode = getNeode() - let query let mutate let anyUser @@ -42,28 +40,26 @@ let hiddenUser let authenticatedUser let newUser +const database = databaseContext() + +let server: ApolloServer beforeAll(async () => { await cleanDatabase() - const { server } = createServer({ - context: () => { - return { - driver, - neode, - user: authenticatedUser, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + server = createServer({ context }).server query = createTestClient(server).query mutate = createTestClient(server).mutate }) afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('Posts in Groups', () => { diff --git a/backend/src/graphql/resolvers/rooms.ts b/backend/src/graphql/resolvers/rooms.ts index 9c6751695..e3422a5ce 100644 --- a/backend/src/graphql/resolvers/rooms.ts +++ b/backend/src/graphql/resolvers/rooms.ts @@ -7,8 +7,7 @@ import { withFilter } from 'graphql-subscriptions' import { neo4jgraphql } from 'neo4j-graphql-js' -// eslint-disable-next-line import/no-cycle -import { pubsub, ROOM_COUNT_UPDATED } from '@src/server' +import { ROOM_COUNT_UPDATED } from '@constants/subscriptions' import Resolver from './helpers/Resolver' @@ -30,7 +29,7 @@ export default { Subscription: { roomCountUpdated: { subscribe: withFilter( - () => pubsub.asyncIterator(ROOM_COUNT_UPDATED), + (_, __, context) => context.pubsub.asyncIterator(ROOM_COUNT_UPDATED), (payload, variables, context) => { return payload.userId === context.user?.id }, diff --git a/backend/src/graphql/resolvers/socialMedia.ts b/backend/src/graphql/resolvers/socialMedia.ts index 952e4a27e..2c0cd4c94 100644 --- a/backend/src/graphql/resolvers/socialMedia.ts +++ b/backend/src/graphql/resolvers/socialMedia.ts @@ -1,7 +1,5 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ - /* eslint-disable @typescript-eslint/no-unsafe-member-access */ - /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { getNeode } from '@db/neo4j' diff --git a/backend/src/graphql/resolvers/user_management.ts b/backend/src/graphql/resolvers/user_management.ts index 7bea1f53c..140a8d53c 100644 --- a/backend/src/graphql/resolvers/user_management.ts +++ b/backend/src/graphql/resolvers/user_management.ts @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ - /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ diff --git a/backend/src/graphql/resolvers/users.spec.ts b/backend/src/graphql/resolvers/users.spec.ts index 4fc1c3efd..f65f9eae2 100644 --- a/backend/src/graphql/resolvers/users.spec.ts +++ b/backend/src/graphql/resolvers/users.spec.ts @@ -3,14 +3,16 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-call */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' import { categories } from '@constants/categories' +import databaseContext from '@context/database' +import pubsubContext from '@context/pubsub' import Factory, { cleanDatabase } from '@db/factories' import User from '@db/models/User' -import { getNeode, getDriver } from '@db/neo4j' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' const categoryIds = ['cat9'] let user @@ -21,8 +23,7 @@ let query let mutate let variables -const driver = getDriver() -const neode = getNeode() +const pubsub = pubsubContext() const deleteUserMutation = gql` mutation ($id: ID!, $resource: [Deletable]) { @@ -108,25 +109,28 @@ const resetTrophyBadgesSelected = gql` } ` +const database = databaseContext() + +let server: ApolloServer + beforeAll(async () => { await cleanDatabase() - const { server } = createServer({ - context: () => { - return { - driver, - neode, - user: authenticatedUser, - } - }, - }) - query = createTestClient(server).query - mutate = createTestClient(server).mutate + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database, pubsub }) + + server = createServer({ context }).server + + const createTestClientResult = createTestClient(server) + query = createTestClientResult.query + mutate = createTestClientResult.mutate }) afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 @@ -540,10 +544,10 @@ describe('Delete a User as admin', () => { describe('connected `EmailAddress` nodes', () => { it('will be removed completely', async () => { - await expect(neode.all('EmailAddress')).resolves.toHaveLength(2) + await expect(database.neode.all('EmailAddress')).resolves.toHaveLength(2) await mutate({ mutation: deleteUserMutation, variables }) - await expect(neode.all('EmailAddress')).resolves.toHaveLength(1) + await expect(database.neode.all('EmailAddress')).resolves.toHaveLength(1) }) }) @@ -554,9 +558,9 @@ describe('Delete a User as admin', () => { }) it('will be removed completely', async () => { - await expect(neode.all('SocialMedia')).resolves.toHaveLength(1) + await expect(database.neode.all('SocialMedia')).resolves.toHaveLength(1) await mutate({ mutation: deleteUserMutation, variables }) - await expect(neode.all('SocialMedia')).resolves.toHaveLength(0) + await expect(database.neode.all('SocialMedia')).resolves.toHaveLength(0) }) }) }) @@ -1041,8 +1045,8 @@ describe('updateOnlineStatus', () => { ) const cypher = 'MATCH (u:User {id: $id}) RETURN u' - const result = await neode.cypher(cypher, { id: authenticatedUser.id }) - const dbUser = neode.hydrateFirst(result, 'u', neode.model('User')) + const result = await database.neode.cypher(cypher, { id: authenticatedUser.id }) + const dbUser = database.neode.hydrateFirst(result, 'u', database.neode.model('User')) await expect(dbUser.toJson()).resolves.toMatchObject({ lastOnlineStatus: 'online', }) @@ -1067,8 +1071,8 @@ describe('updateOnlineStatus', () => { ) const cypher = 'MATCH (u:User {id: $id}) RETURN u' - const result = await neode.cypher(cypher, { id: authenticatedUser.id }) - const dbUser = neode.hydrateFirst(result, 'u', neode.model('User')) + const result = await database.neode.cypher(cypher, { id: authenticatedUser.id }) + const dbUser = database.neode.hydrateFirst(result, 'u', database.neode.model('User')) await expect(dbUser.toJson()).resolves.toMatchObject({ lastOnlineStatus: 'away', awaySince: expect.any(String), @@ -1083,8 +1087,12 @@ describe('updateOnlineStatus', () => { ) const cypher = 'MATCH (u:User {id: $id}) RETURN u' - const result = await neode.cypher(cypher, { id: authenticatedUser.id }) - const dbUser = neode.hydrateFirst(result, 'u', neode.model('User')) + const result = await database.neode.cypher(cypher, { id: authenticatedUser.id }) + const dbUser = database.neode.hydrateFirst( + result, + 'u', + database.neode.model('User'), + ) await expect(dbUser.toJson()).resolves.toMatchObject({ lastOnlineStatus: 'away', awaySince: expect.any(String), @@ -1098,8 +1106,8 @@ describe('updateOnlineStatus', () => { }), ) - const result2 = await neode.cypher(cypher, { id: authenticatedUser.id }) - const dbUser2 = neode.hydrateFirst(result2, 'u', neode.model('User')) + const result2 = await database.neode.cypher(cypher, { id: authenticatedUser.id }) + const dbUser2 = database.neode.hydrateFirst(result2, 'u', database.neode.model('User')) await expect(dbUser2.toJson()).resolves.toMatchObject({ lastOnlineStatus: 'away', awaySince, diff --git a/backend/src/middleware/helpers/email/sendMail.ts b/backend/src/middleware/helpers/email/sendMail.ts index a7d223f1c..fc50107fb 100644 --- a/backend/src/middleware/helpers/email/sendMail.ts +++ b/backend/src/middleware/helpers/email/sendMail.ts @@ -4,7 +4,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import nodemailer from 'nodemailer' +import { createTransport } from 'nodemailer' import { htmlToText } from 'nodemailer-html-to-text' import CONFIG from '@config/index' @@ -15,7 +15,7 @@ const hasAuthData = CONFIG.SMTP_USERNAME && CONFIG.SMTP_PASSWORD const hasDKIMData = CONFIG.SMTP_DKIM_DOMAINNAME && CONFIG.SMTP_DKIM_KEYSELECTOR && CONFIG.SMTP_DKIM_PRIVATKEY -const transporter = nodemailer.createTransport({ +const transporter = createTransport({ host: CONFIG.SMTP_HOST, port: CONFIG.SMTP_PORT, ignoreTLS: CONFIG.SMTP_IGNORE_TLS, diff --git a/backend/src/middleware/index.ts b/backend/src/middleware/index.ts index 37fd33ef9..896e5b33b 100644 --- a/backend/src/middleware/index.ts +++ b/backend/src/middleware/index.ts @@ -15,7 +15,6 @@ import hashtags from './hashtags/hashtagsMiddleware' import includedFields from './includedFieldsMiddleware' import languages from './languages/languages' import login from './login/loginMiddleware' -// eslint-disable-next-line import/no-cycle import notifications from './notifications/notificationsMiddleware' import orderBy from './orderByMiddleware' import permissions from './permissionsMiddleware' diff --git a/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts index 79d95e43e..8b41498ab 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts @@ -1,18 +1,18 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-return */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { joinGroupMutation } from '@graphql/queries/joinGroupMutation' import CONFIG from '@src/config' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false @@ -21,13 +21,10 @@ jest.mock('@middleware/helpers/email/sendMail', () => ({ sendMail: (notification) => sendMailMock(notification), })) -let server, query, mutate, authenticatedUser, emaillessMember +let query, mutate, authenticatedUser, emaillessMember let postAuthor, groupMember -const driver = getDriver() -const neode = getNeode() - const mentionString = ` @group-member @email-less-member` @@ -97,22 +94,18 @@ const markAllAsRead = async () => `, }) +const database = databaseContext() + +let server: ApolloServer + beforeAll(async () => { await cleanDatabase() - const createServerResult = createServer({ - context: () => { - return { - user: authenticatedUser, - neode, - driver, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) - server = createServerResult.server + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + server = createServer({ context }).server + const createTestClientResult = createTestClient(server) query = createTestClientResult.query mutate = createTestClientResult.mutate @@ -120,7 +113,9 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('emails sent for notifications', () => { @@ -149,7 +144,7 @@ describe('emails sent for notifications', () => { password: '1234', }, ) - emaillessMember = await neode.create('User', { + emaillessMember = await database.neode.create('User', { id: 'email-less-member', name: 'Email-less Member', slug: 'email-less-member', diff --git a/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts index 21d4a14a0..f595f441e 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts @@ -1,16 +1,16 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ - +/* eslint-disable @typescript-eslint/no-unsafe-return */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { createGroupMutation } from '@graphql/queries/createGroupMutation' import CONFIG from '@src/config' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false @@ -19,13 +19,10 @@ jest.mock('@middleware/helpers/email/sendMail', () => ({ sendMail: (notification) => sendMailMock(notification), })) -let server, query, mutate, authenticatedUser +let query, mutate, authenticatedUser let postAuthor, firstFollower, secondFollower, thirdFollower, emaillessFollower -const driver = getDriver() -const neode = getNeode() - const createPostMutation = gql` mutation ($id: ID, $title: String!, $content: String!, $groupId: ID) { CreatePost(id: $id, title: $title, content: $content, groupId: $groupId) { @@ -71,22 +68,19 @@ const followUserMutation = gql` } ` +const database = databaseContext() + +let server: ApolloServer + beforeAll(async () => { await cleanDatabase() - const createServerResult = createServer({ - context: () => { - return { - user: authenticatedUser, - neode, - driver, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) - server = createServerResult.server + // eslint-disable-next-line @typescript-eslint/require-await + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + server = createServer({ context }).server + const createTestClientResult = createTestClient(server) query = createTestClientResult.query mutate = createTestClientResult.mutate @@ -94,7 +88,9 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('following users notifications', () => { @@ -147,7 +143,7 @@ describe('following users notifications', () => { password: '1234', }, ) - emaillessFollower = await neode.create('User', { + emaillessFollower = await database.neode.create('User', { id: 'email-less-follower', name: 'Email-less Follower', slug: 'email-less-follower', diff --git a/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts index 96c7e9d18..539022262 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts @@ -1,19 +1,19 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-return */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { joinGroupMutation } from '@graphql/queries/joinGroupMutation' import CONFIG from '@src/config' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false @@ -22,13 +22,10 @@ jest.mock('@middleware/helpers/email/sendMail', () => ({ sendMail: (notification) => sendMailMock(notification), })) -let server, query, mutate, authenticatedUser +let query, mutate, authenticatedUser let postAuthor, groupMember, pendingMember, noMember, emaillessMember -const driver = getDriver() -const neode = getNeode() - const mentionString = ` @no-member @pending-member @@ -93,22 +90,18 @@ const markAllAsRead = async () => `, }) +const database = databaseContext() + +let server: ApolloServer + beforeAll(async () => { await cleanDatabase() - const createServerResult = createServer({ - context: () => { - return { - user: authenticatedUser, - neode, - driver, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) - server = createServerResult.server + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + server = createServer({ context }).server + const createTestClientResult = createTestClient(server) query = createTestClientResult.query mutate = createTestClientResult.mutate @@ -116,7 +109,9 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('mentions in groups', () => { @@ -169,7 +164,7 @@ describe('mentions in groups', () => { password: '1234', }, ) - emaillessMember = await neode.create('User', { + emaillessMember = await database.neode.create('User', { id: 'email-less-member', name: 'Email-less Member', slug: 'email-less-member', diff --git a/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts index a0864fe07..2fff0195d 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts @@ -1,14 +1,16 @@ +/* eslint-disable @typescript-eslint/require-await */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' import CONFIG from '@config/index' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false @@ -17,13 +19,10 @@ jest.mock('@middleware/helpers/email/sendMail', () => ({ sendMail: (notification) => sendMailMock(notification), })) -let server, query, mutate, authenticatedUser +let query, mutate, authenticatedUser let postAuthor, firstCommenter, secondCommenter, emaillessObserver -const driver = getDriver() -const neode = getNeode() - const createPostMutation = gql` mutation ($id: ID, $title: String!, $content: String!) { CreatePost(id: $id, title: $title, content: $content) { @@ -78,23 +77,18 @@ const toggleObservePostMutation = gql` } } ` +const database = databaseContext() + +let server: ApolloServer beforeAll(async () => { await cleanDatabase() - const createServerResult = createServer({ - context: () => { - return { - user: authenticatedUser, - neode, - driver, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) - server = createServerResult.server + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + server = createServer({ context }).server + const createTestClientResult = createTestClient(server) query = createTestClientResult.query mutate = createTestClientResult.mutate @@ -102,7 +96,9 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('notifications for users that observe a post', () => { @@ -143,7 +139,7 @@ describe('notifications for users that observe a post', () => { password: '1234', }, ) - emaillessObserver = await neode.create('User', { + emaillessObserver = await database.neode.create('User', { id: 'email-less-observer', name: 'Email-less Observer', slug: 'email-less-observer', diff --git a/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts index 3a47d376d..8d06396ce 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts @@ -1,15 +1,14 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-return */ import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import CONFIG from '@src/config' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false @@ -23,13 +22,10 @@ jest.mock('../helpers/isUserOnline', () => ({ isUserOnline: () => isUserOnlineMock(), })) -let server, mutate, authenticatedUser +let mutate, authenticatedUser let postAuthor -const driver = getDriver() -const neode = getNeode() - const createPostMutation = gql` mutation ($id: ID, $title: String!, $content: String!, $groupId: ID) { CreatePost(id: $id, title: $title, content: $content, groupId: $groupId) { @@ -40,29 +36,24 @@ const createPostMutation = gql` } ` +const database = databaseContext() + beforeAll(async () => { await cleanDatabase() - const createServerResult = createServer({ - context: () => { - return { - user: authenticatedUser, - neode, - driver, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) - server = createServerResult.server + // eslint-disable-next-line @typescript-eslint/require-await + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + const { server } = createServer({ context }) + const createTestClientResult = createTestClient(server) mutate = createTestClientResult.mutate }) afterAll(async () => { await cleanDatabase() - await driver.close() + await database.driver.close() }) afterEach(async () => { diff --git a/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts index 25aef2e2b..461fa6996 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts @@ -1,19 +1,19 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-return */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { joinGroupMutation } from '@graphql/queries/joinGroupMutation' import CONFIG from '@src/config' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false @@ -22,13 +22,10 @@ jest.mock('@middleware/helpers/email/sendMail', () => ({ sendMail: (notification) => sendMailMock(notification), })) -let server, query, mutate, authenticatedUser +let query, mutate, authenticatedUser let postAuthor, groupMember, pendingMember, emaillessMember -const driver = getDriver() -const neode = getNeode() - const createPostMutation = gql` mutation ($id: ID, $title: String!, $content: String!, $groupId: ID) { CreatePost(id: $id, title: $title, content: $content, groupId: $groupId) { @@ -95,22 +92,17 @@ const markAllAsRead = async () => `, }) +const database = databaseContext() + +let server: ApolloServer beforeAll(async () => { await cleanDatabase() - const createServerResult = createServer({ - context: () => { - return { - user: authenticatedUser, - neode, - driver, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) - server = createServerResult.server + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + server = createServer({ context }).server + const createTestClientResult = createTestClient(server) query = createTestClientResult.query mutate = createTestClientResult.mutate @@ -118,7 +110,9 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('notify group members of new posts in group', () => { @@ -159,7 +153,7 @@ describe('notify group members of new posts in group', () => { password: '1234', }, ) - emaillessMember = await neode.create('User', { + emaillessMember = await database.neode.create('User', { id: 'email-less-member', name: 'Email-less Member', slug: 'email-less-member', diff --git a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts index ab0a6a5b2..985a19193 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts @@ -4,11 +4,13 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-return */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' +import databaseContext from '@context/database' +import pubsubContext from '@context/pubsub' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { createMessageMutation } from '@graphql/queries/createMessageMutation' @@ -16,7 +18,7 @@ import { createRoomMutation } from '@graphql/queries/createRoomMutation' import { joinGroupMutation } from '@graphql/queries/joinGroupMutation' import { leaveGroupMutation } from '@graphql/queries/leaveGroupMutation' import { removeUserFromGroupMutation } from '@graphql/queries/removeUserFromGroupMutation' -import createServer, { pubsub } from '@src/server' +import createServer, { getContext } from '@src/server' const sendMailMock: (notification) => void = jest.fn() jest.mock('@middleware/helpers/email/sendMail', () => ({ @@ -35,12 +37,12 @@ jest.mock('../helpers/isUserOnline', () => ({ isUserOnline: () => isUserOnlineMock(), })) +const database = databaseContext() +const pubsub = pubsubContext() const pubsubSpy = jest.spyOn(pubsub, 'publish') -let server, query, mutate, notifiedUser, authenticatedUser +let query, mutate, notifiedUser, authenticatedUser -const driver = getDriver() -const neode = getNeode() const categoryIds = ['cat9'] const createPostMutation = gql` mutation ($id: ID, $title: String!, $postContent: String!, $categoryIds: [ID]!) { @@ -68,19 +70,16 @@ const createCommentMutation = gql` } ` +let server: ApolloServer + beforeAll(async () => { await cleanDatabase() - const createServerResult = createServer({ - context: () => { - return { - user: authenticatedUser, - neode, - driver, - } - }, - }) - server = createServerResult.server + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database, pubsub }) + + server = createServer({ context }).server + const createTestClientResult = createTestClient(server) query = createTestClientResult.query mutate = createTestClientResult.mutate @@ -88,7 +87,9 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) beforeEach(async () => { @@ -104,7 +105,7 @@ beforeEach(async () => { password: '1234', }, ) - await neode.create('Category', { + await database.neode.create('Category', { id: 'cat9', name: 'Democracy & Politics', icon: 'university', diff --git a/backend/src/middleware/notifications/notificationsMiddleware.ts b/backend/src/middleware/notifications/notificationsMiddleware.ts index 4459d23b8..5737a6587 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.ts @@ -1,10 +1,14 @@ -/* eslint-disable import/no-cycle */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable security/detect-object-injection */ +import { + NOTIFICATION_ADDED, + ROOM_COUNT_UPDATED, + CHAT_MESSAGE_ADDED, +} from '@constants/subscriptions' import { getUnreadRoomsCount } from '@graphql/resolvers/rooms' import { sendMail } from '@middleware/helpers/email/sendMail' import { @@ -13,7 +17,6 @@ import { } from '@middleware/helpers/email/templateBuilder' import { isUserOnline } from '@middleware/helpers/isUserOnline' import { validateNotifyUsers } from '@middleware/validation/validationMiddleware' -import { pubsub, NOTIFICATION_ADDED, ROOM_COUNT_UPDATED, CHAT_MESSAGE_ADDED } from '@src/server' import extractMentionedUsers from './mentions/extractMentionedUsers' @@ -25,7 +28,7 @@ const publishNotifications = async ( ): Promise => { const notifications = await notificationsPromise notifications.forEach((notificationAdded) => { - pubsub.publish(NOTIFICATION_ADDED, { notificationAdded }) + context.pubsub.publish(NOTIFICATION_ADDED, { notificationAdded }) if ( notificationAdded.email && // no primary email was found (notificationAdded.to[emailNotificationSetting] ?? true) && @@ -482,11 +485,11 @@ const handleCreateMessage = async (resolve, root, args, context, resolveInfo) => // send subscriptions const roomCountUpdated = await getUnreadRoomsCount(recipientUser.id, session) - void pubsub.publish(ROOM_COUNT_UPDATED, { + void context.pubsub.publish(ROOM_COUNT_UPDATED, { roomCountUpdated, userId: recipientUser.id, }) - void pubsub.publish(CHAT_MESSAGE_ADDED, { + void context.pubsub.publish(CHAT_MESSAGE_ADDED, { chatMessageAdded: message, userId: recipientUser.id, }) diff --git a/backend/src/server.ts b/backend/src/server.ts index a9fc43a0e..457ea3684 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -10,62 +10,55 @@ import http from 'node:http' import { ApolloServer } from 'apollo-server-express' import bodyParser from 'body-parser' import express from 'express' -import { RedisPubSub } from 'graphql-redis-subscriptions' -import { PubSub } from 'graphql-subscriptions' import { graphqlUploadExpress } from 'graphql-upload' import helmet from 'helmet' -import Redis from 'ioredis' + +import databaseContext from '@context/database' +import pubsubContext from '@context/pubsub' import CONFIG from './config' -import { getNeode, getDriver } from './db/neo4j' import schema from './graphql/schema' import decode from './jwt/decode' -// eslint-disable-next-line import/no-cycle import middleware from './middleware' -export const NOTIFICATION_ADDED = 'NOTIFICATION_ADDED' -export const CHAT_MESSAGE_ADDED = 'CHAT_MESSAGE_ADDED' -export const ROOM_COUNT_UPDATED = 'ROOM_COUNT_UPDATED' -const { REDIS_DOMAIN, REDIS_PORT, REDIS_PASSWORD } = CONFIG -let prodPubsub, devPubsub -const options = { - host: REDIS_DOMAIN, - port: REDIS_PORT, - password: REDIS_PASSWORD, - retryStrategy: (times) => { - return Math.min(times * 50, 2000) - }, -} -if (options.host && options.port && options.password) { - prodPubsub = new RedisPubSub({ - publisher: new Redis(options), - subscriber: new Redis(options), - }) -} else { - devPubsub = new PubSub() -} -export const pubsub = prodPubsub || devPubsub -const driver = getDriver() -const neode = getNeode() +const serverDatabase = databaseContext() +const serverPubsub = pubsubContext() -const getContext = async (req) => { - const user = await decode(driver, req.headers.authorization) - return { - driver, - neode, - user, - req, - cypherParams: { - currentUserId: user ? user.id : null, - }, +const databaseUser = async (req) => decode(serverDatabase.driver, req.headers.authorization) + +export const getContext = + ( + { + database = serverDatabase, + pubsub = serverPubsub, + user = databaseUser, + }: { + database?: ReturnType + pubsub?: ReturnType + user?: (any) => Promise + } = { database: serverDatabase, pubsub: serverPubsub, user: databaseUser }, + ) => + async (req) => { + const u = await user(req) + return { + database, + driver: database.driver, + neode: database.neode, + pubsub, + user: u, + req, + cypherParams: { + currentUserId: u ? u.id : null, + }, + } } -} + export const context = async (options) => { const { connection, req } = options if (connection) { return connection.context } else { - return getContext(req) + return getContext()(req) } } @@ -74,9 +67,7 @@ const createServer = (options?) => { context, schema: middleware(schema), subscriptions: { - onConnect: (connectionParams, _webSocket) => { - return getContext(connectionParams) - }, + onConnect: (connectionParams) => getContext()(connectionParams), }, debug: !!CONFIG.DEBUG, uploads: false, @@ -88,11 +79,10 @@ const createServer = (options?) => { return error }, } - const server = new ApolloServer(Object.assign({}, defaults, options)) + const server = new ApolloServer(Object.assign(defaults, options)) const app = express() - app.set('driver', driver) // TODO: this exception is required for the graphql playground, since the playground loads external resources // See: https://github.com/graphql/graphql-playground/issues/1283 app.use( diff --git a/backend/tsconfig.json b/backend/tsconfig.json index e34f920be..7ef3f47b0 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -32,6 +32,7 @@ "paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */ "@config/*": ["./src/config/*"], "@constants/*": ["./src/constants/*"], + "@context/*": ["./src/context/*"], "@db/*": ["./src/db/*"], "@graphql/*": ["./src/graphql/*"], "@helpers/*": ["./src/helpers/*"],