refactor(backend): refactor context (#8434)

* type for neo4j and neode

* fix build

* remove flakyness

* wait for neode to install schema

* remove flakyness

* explain why we wait for a non-promise

* refactor context

missing change

missing change

* adjust test setup

proper cleanup after test

* lint fixes

* fix failing test to use new context
This commit is contained in:
Ulf Gebhardt 2025-05-03 11:43:08 +02:00 committed by GitHub
parent 913cc2f06d
commit c69cef47a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 359 additions and 331 deletions

View File

@ -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'

View File

@ -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),
}
}

View File

@ -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),
})
}

View File

@ -2,40 +2,41 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { ApolloServer } from 'apollo-server-express'
import { createTestClient } from 'apollo-server-testing' import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import databaseContext from '@context/database'
import Factory, { cleanDatabase } from '@db/factories' import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j' import createServer, { getContext } from '@src/server'
import createServer from '@src/server'
const driver = getDriver() const database = databaseContext()
const neode = getNeode()
let variables, mutate, authenticatedUser, commentAuthor, newlyCreatedComment let variables, mutate, authenticatedUser, commentAuthor, newlyCreatedComment
let server: ApolloServer
beforeAll(async () => { beforeAll(async () => {
await cleanDatabase() await cleanDatabase()
const { server } = createServer({ // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/require-await
context: () => { const contextUser = async (_req) => authenticatedUser
return { const context = getContext({ user: contextUser, database })
driver,
user: authenticatedUser, server = createServer({ context }).server
}
},
})
mutate = createTestClient(server).mutate mutate = createTestClient(server).mutate
}) })
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
await driver.close() void server.stop()
void database.driver.close()
database.neode.close()
}) })
beforeEach(async () => { beforeEach(async () => {
variables = {} variables = {}
await neode.create('Category', { await database.neode.create('Category', {
id: 'cat9', id: 'cat9',
name: 'Democracy & Politics', name: 'Democracy & Politics',
icon: 'university', icon: 'university',
@ -103,7 +104,7 @@ describe('CreateComment', () => {
describe('authenticated', () => { describe('authenticated', () => {
beforeEach(async () => { beforeEach(async () => {
const user = await neode.create('User', { name: 'Author' }) const user = await database.neode.create('User', { name: 'Author' })
authenticatedUser = await user.toJson() authenticatedUser = await user.toJson()
}) })

View File

@ -1,7 +1,5 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { getNeode } from '@db/neo4j' import { getNeode } from '@db/neo4j'

View File

@ -6,8 +6,8 @@
import { createTestClient } from 'apollo-server-testing' import { createTestClient } from 'apollo-server-testing'
import CONFIG from '@config/index' import CONFIG from '@config/index'
import databaseContext from '@context/database'
import Factory, { cleanDatabase } from '@db/factories' import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation' import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation'
import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation'
import { groupMembersQuery } from '@graphql/queries/groupMembersQuery' import { groupMembersQuery } from '@graphql/queries/groupMembersQuery'
@ -16,10 +16,7 @@ import { joinGroupMutation } from '@graphql/queries/joinGroupMutation'
import { leaveGroupMutation } from '@graphql/queries/leaveGroupMutation' import { leaveGroupMutation } from '@graphql/queries/leaveGroupMutation'
import { removeUserFromGroupMutation } from '@graphql/queries/removeUserFromGroupMutation' import { removeUserFromGroupMutation } from '@graphql/queries/removeUserFromGroupMutation'
import { updateGroupMutation } from '@graphql/queries/updateGroupMutation' import { updateGroupMutation } from '@graphql/queries/updateGroupMutation'
import createServer from '@src/server' import createServer, { getContext } from '@src/server'
const driver = getDriver()
const neode = getNeode()
let authenticatedUser let authenticatedUser
let user let user
@ -35,15 +32,12 @@ const descriptionAdditional100 =
' 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789' ' 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789'
let variables = {} let variables = {}
const { server } = createServer({ const database = databaseContext()
context: () => { // eslint-disable-next-line @typescript-eslint/no-unsafe-return
return { const contextUser = async (_req) => authenticatedUser
driver, const context = getContext({ user: contextUser, database })
neode,
user: authenticatedUser, const { server } = createServer({ context })
}
},
})
const { mutate, query } = createTestClient(server) const { mutate, query } = createTestClient(server)
const seedBasicsAndClearAuthentication = async () => { const seedBasicsAndClearAuthentication = async () => {
@ -60,25 +54,25 @@ const seedBasicsAndClearAuthentication = async () => {
}, },
) )
await Promise.all([ await Promise.all([
neode.create('Category', { database.neode.create('Category', {
id: 'cat4', id: 'cat4',
name: 'Environment & Nature', name: 'Environment & Nature',
slug: 'environment-nature', slug: 'environment-nature',
icon: 'tree', icon: 'tree',
}), }),
neode.create('Category', { database.neode.create('Category', {
id: 'cat9', id: 'cat9',
name: 'Democracy & Politics', name: 'Democracy & Politics',
slug: 'democracy-politics', slug: 'democracy-politics',
icon: 'university', icon: 'university',
}), }),
neode.create('Category', { database.neode.create('Category', {
id: 'cat15', id: 'cat15',
name: 'Consumption & Sustainability', name: 'Consumption & Sustainability',
slug: 'consumption-sustainability', slug: 'consumption-sustainability',
icon: 'shopping-cart', icon: 'shopping-cart',
}), }),
neode.create('Category', { database.neode.create('Category', {
id: 'cat27', id: 'cat27',
name: 'Animal Protection', name: 'Animal Protection',
slug: 'animal-protection', slug: 'animal-protection',
@ -241,7 +235,9 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
await driver.close() void server.stop()
void database.driver.close()
database.neode.close()
}) })
describe('in mode', () => { describe('in mode', () => {

View File

@ -3,49 +3,47 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { ApolloServer } from 'apollo-server-express'
import { createTestClient } from 'apollo-server-testing' import { createTestClient } from 'apollo-server-testing'
import databaseContext from '@context/database'
import pubsubContext from '@context/pubsub'
import Factory, { cleanDatabase } from '@db/factories' import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import { createMessageMutation } from '@graphql/queries/createMessageMutation' import { createMessageMutation } from '@graphql/queries/createMessageMutation'
import { createRoomMutation } from '@graphql/queries/createRoomMutation' import { createRoomMutation } from '@graphql/queries/createRoomMutation'
import { markMessagesAsSeen } from '@graphql/queries/markMessagesAsSeen' import { markMessagesAsSeen } from '@graphql/queries/markMessagesAsSeen'
import { messageQuery } from '@graphql/queries/messageQuery' import { messageQuery } from '@graphql/queries/messageQuery'
import { roomQuery } from '@graphql/queries/roomQuery' import { roomQuery } from '@graphql/queries/roomQuery'
import createServer, { pubsub } from '@src/server' import createServer, { getContext } from '@src/server'
const driver = getDriver()
const neode = getNeode()
let query let query
let mutate let mutate
let authenticatedUser let authenticatedUser
let chattingUser, otherChattingUser, notChattingUser let chattingUser, otherChattingUser, notChattingUser
const database = databaseContext()
const pubsub = pubsubContext()
const pubsubSpy = jest.spyOn(pubsub, 'publish') const pubsubSpy = jest.spyOn(pubsub, 'publish')
let server: ApolloServer
beforeAll(async () => { beforeAll(async () => {
await cleanDatabase() await cleanDatabase()
const { server } = createServer({ // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/require-await
context: () => { const contextUser = async (_req) => authenticatedUser
return { const context = getContext({ user: contextUser, database, pubsub })
driver,
neode, server = createServer({ context }).server
user: authenticatedUser,
cypherParams: {
currentUserId: authenticatedUser ? authenticatedUser.id : null,
},
}
},
})
query = createTestClient(server).query query = createTestClient(server).query
mutate = createTestClient(server).mutate mutate = createTestClient(server).mutate
}) })
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
await driver.close() void server.stop()
void database.driver.close()
database.neode.close()
}) })
describe('Message', () => { describe('Message', () => {

View File

@ -7,7 +7,7 @@
import { withFilter } from 'graphql-subscriptions' import { withFilter } from 'graphql-subscriptions'
import { neo4jgraphql } from 'neo4j-graphql-js' 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' import Resolver from './helpers/Resolver'
@ -30,7 +30,7 @@ export default {
Subscription: { Subscription: {
chatMessageAdded: { chatMessageAdded: {
subscribe: withFilter( subscribe: withFilter(
() => pubsub.asyncIterator(CHAT_MESSAGE_ADDED), (_, __, context) => context.pubsub.asyncIterator(CHAT_MESSAGE_ADDED),
(payload, variables, context) => { (payload, variables, context) => {
return payload.userId === context.user?.id return payload.userId === context.user?.id
}, },

View File

@ -6,13 +6,13 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { withFilter } from 'graphql-subscriptions' import { withFilter } from 'graphql-subscriptions'
import { pubsub, NOTIFICATION_ADDED } from '@src/server' import { NOTIFICATION_ADDED } from '@constants/subscriptions'
export default { export default {
Subscription: { Subscription: {
notificationAdded: { notificationAdded: {
subscribe: withFilter( subscribe: withFilter(
() => pubsub.asyncIterator(NOTIFICATION_ADDED), (_, __, context) => context.pubsub.asyncIterator(NOTIFICATION_ADDED),
(payload, variables, context) => { (payload, variables, context) => {
return payload.notificationAdded.to.id === context.user?.id return payload.notificationAdded.to.id === context.user?.id
}, },

View File

@ -1,20 +1,18 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { ApolloServer } from 'apollo-server-express'
import { createTestClient } from 'apollo-server-testing' import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import CONFIG from '@config/index' import CONFIG from '@config/index'
import databaseContext from '@context/database'
import Factory, { cleanDatabase } from '@db/factories' import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import { createPostMutation } from '@graphql/queries/createPostMutation' import { createPostMutation } from '@graphql/queries/createPostMutation'
import createServer from '@src/server' import createServer, { getContext } from '@src/server'
CONFIG.CATEGORIES_ACTIVE = false CONFIG.CATEGORIES_ACTIVE = false
const driver = getDriver()
const neode = getNeode()
let query let query
let mutate let mutate
let authenticatedUser let authenticatedUser
@ -40,28 +38,27 @@ const postQuery = gql`
} }
` `
const database = databaseContext()
let server: ApolloServer
beforeAll(async () => { beforeAll(async () => {
await cleanDatabase() await cleanDatabase()
const { server } = createServer({ // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/require-await
context: () => { const contextUser = async (_req) => authenticatedUser
return { const context = getContext({ user: contextUser, database })
driver,
neode, server = createServer({ context }).server
user: authenticatedUser,
cypherParams: {
currentUserId: authenticatedUser ? authenticatedUser.id : null,
},
}
},
})
query = createTestClient(server).query query = createTestClient(server).query
mutate = createTestClient(server).mutate mutate = createTestClient(server).mutate
}) })
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
await driver.close() void server.stop()
void database.driver.close()
database.neode.close()
}) })
describe('observing posts', () => { describe('observing posts', () => {

View File

@ -2,11 +2,12 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { ApolloServer } from 'apollo-server-express'
import { createTestClient } from 'apollo-server-testing' import { createTestClient } from 'apollo-server-testing'
import CONFIG from '@config/index' import CONFIG from '@config/index'
import databaseContext from '@context/database'
import Factory, { cleanDatabase } from '@db/factories' import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation' import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation'
import { createCommentMutation } from '@graphql/queries/createCommentMutation' import { createCommentMutation } from '@graphql/queries/createCommentMutation'
import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation'
@ -17,7 +18,7 @@ import { postQuery } from '@graphql/queries/postQuery'
import { profilePagePosts } from '@graphql/queries/profilePagePosts' import { profilePagePosts } from '@graphql/queries/profilePagePosts'
import { searchPosts } from '@graphql/queries/searchPosts' import { searchPosts } from '@graphql/queries/searchPosts'
import { signupVerificationMutation } from '@graphql/queries/signupVerificationMutation' import { signupVerificationMutation } from '@graphql/queries/signupVerificationMutation'
import createServer from '@src/server' import createServer, { getContext } from '@src/server'
CONFIG.CATEGORIES_ACTIVE = false CONFIG.CATEGORIES_ACTIVE = false
@ -28,9 +29,6 @@ jest.mock('@constants/groups', () => {
} }
}) })
const driver = getDriver()
const neode = getNeode()
let query let query
let mutate let mutate
let anyUser let anyUser
@ -42,28 +40,26 @@ let hiddenUser
let authenticatedUser let authenticatedUser
let newUser let newUser
const database = databaseContext()
let server: ApolloServer
beforeAll(async () => { beforeAll(async () => {
await cleanDatabase() await cleanDatabase()
const { server } = createServer({ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
context: () => { const contextUser = async (_req) => authenticatedUser
return { const context = getContext({ user: contextUser, database })
driver,
neode, server = createServer({ context }).server
user: authenticatedUser,
cypherParams: {
currentUserId: authenticatedUser ? authenticatedUser.id : null,
},
}
},
})
query = createTestClient(server).query query = createTestClient(server).query
mutate = createTestClient(server).mutate mutate = createTestClient(server).mutate
}) })
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
await driver.close() void server.stop()
void database.driver.close()
database.neode.close()
}) })
describe('Posts in Groups', () => { describe('Posts in Groups', () => {

View File

@ -7,8 +7,7 @@
import { withFilter } from 'graphql-subscriptions' import { withFilter } from 'graphql-subscriptions'
import { neo4jgraphql } from 'neo4j-graphql-js' import { neo4jgraphql } from 'neo4j-graphql-js'
// eslint-disable-next-line import/no-cycle import { ROOM_COUNT_UPDATED } from '@constants/subscriptions'
import { pubsub, ROOM_COUNT_UPDATED } from '@src/server'
import Resolver from './helpers/Resolver' import Resolver from './helpers/Resolver'
@ -30,7 +29,7 @@ export default {
Subscription: { Subscription: {
roomCountUpdated: { roomCountUpdated: {
subscribe: withFilter( subscribe: withFilter(
() => pubsub.asyncIterator(ROOM_COUNT_UPDATED), (_, __, context) => context.pubsub.asyncIterator(ROOM_COUNT_UPDATED),
(payload, variables, context) => { (payload, variables, context) => {
return payload.userId === context.user?.id return payload.userId === context.user?.id
}, },

View File

@ -1,7 +1,5 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { getNeode } from '@db/neo4j' import { getNeode } from '@db/neo4j'

View File

@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */

View File

@ -3,14 +3,16 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
import { ApolloServer } from 'apollo-server-express'
import { createTestClient } from 'apollo-server-testing' import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import { categories } from '@constants/categories' import { categories } from '@constants/categories'
import databaseContext from '@context/database'
import pubsubContext from '@context/pubsub'
import Factory, { cleanDatabase } from '@db/factories' import Factory, { cleanDatabase } from '@db/factories'
import User from '@db/models/User' import User from '@db/models/User'
import { getNeode, getDriver } from '@db/neo4j' import createServer, { getContext } from '@src/server'
import createServer from '@src/server'
const categoryIds = ['cat9'] const categoryIds = ['cat9']
let user let user
@ -21,8 +23,7 @@ let query
let mutate let mutate
let variables let variables
const driver = getDriver() const pubsub = pubsubContext()
const neode = getNeode()
const deleteUserMutation = gql` const deleteUserMutation = gql`
mutation ($id: ID!, $resource: [Deletable]) { mutation ($id: ID!, $resource: [Deletable]) {
@ -108,25 +109,28 @@ const resetTrophyBadgesSelected = gql`
} }
` `
const database = databaseContext()
let server: ApolloServer
beforeAll(async () => { beforeAll(async () => {
await cleanDatabase() await cleanDatabase()
const { server } = createServer({ const contextUser = async (_req) => authenticatedUser
context: () => { const context = getContext({ user: contextUser, database, pubsub })
return {
driver, server = createServer({ context }).server
neode,
user: authenticatedUser, const createTestClientResult = createTestClient(server)
} query = createTestClientResult.query
}, mutate = createTestClientResult.mutate
})
query = createTestClient(server).query
mutate = createTestClient(server).mutate
}) })
afterAll(async () => { afterAll(async () => {
await cleanDatabase() 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 // 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', () => { describe('connected `EmailAddress` nodes', () => {
it('will be removed completely', async () => { 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 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 () => { 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 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 cypher = 'MATCH (u:User {id: $id}) RETURN u'
const result = await neode.cypher(cypher, { id: authenticatedUser.id }) const result = await database.neode.cypher(cypher, { id: authenticatedUser.id })
const dbUser = neode.hydrateFirst(result, 'u', neode.model('User')) const dbUser = database.neode.hydrateFirst(result, 'u', database.neode.model('User'))
await expect(dbUser.toJson()).resolves.toMatchObject({ await expect(dbUser.toJson()).resolves.toMatchObject({
lastOnlineStatus: 'online', lastOnlineStatus: 'online',
}) })
@ -1067,8 +1071,8 @@ describe('updateOnlineStatus', () => {
) )
const cypher = 'MATCH (u:User {id: $id}) RETURN u' const cypher = 'MATCH (u:User {id: $id}) RETURN u'
const result = await neode.cypher(cypher, { id: authenticatedUser.id }) const result = await database.neode.cypher(cypher, { id: authenticatedUser.id })
const dbUser = neode.hydrateFirst(result, 'u', neode.model('User')) const dbUser = database.neode.hydrateFirst(result, 'u', database.neode.model('User'))
await expect(dbUser.toJson()).resolves.toMatchObject({ await expect(dbUser.toJson()).resolves.toMatchObject({
lastOnlineStatus: 'away', lastOnlineStatus: 'away',
awaySince: expect.any(String), awaySince: expect.any(String),
@ -1083,8 +1087,12 @@ describe('updateOnlineStatus', () => {
) )
const cypher = 'MATCH (u:User {id: $id}) RETURN u' const cypher = 'MATCH (u:User {id: $id}) RETURN u'
const result = await neode.cypher(cypher, { id: authenticatedUser.id }) const result = await database.neode.cypher(cypher, { id: authenticatedUser.id })
const dbUser = neode.hydrateFirst<typeof User>(result, 'u', neode.model('User')) const dbUser = database.neode.hydrateFirst<typeof User>(
result,
'u',
database.neode.model('User'),
)
await expect(dbUser.toJson()).resolves.toMatchObject({ await expect(dbUser.toJson()).resolves.toMatchObject({
lastOnlineStatus: 'away', lastOnlineStatus: 'away',
awaySince: expect.any(String), awaySince: expect.any(String),
@ -1098,8 +1106,8 @@ describe('updateOnlineStatus', () => {
}), }),
) )
const result2 = await neode.cypher(cypher, { id: authenticatedUser.id }) const result2 = await database.neode.cypher(cypher, { id: authenticatedUser.id })
const dbUser2 = neode.hydrateFirst(result2, 'u', neode.model('User')) const dbUser2 = database.neode.hydrateFirst(result2, 'u', database.neode.model('User'))
await expect(dbUser2.toJson()).resolves.toMatchObject({ await expect(dbUser2.toJson()).resolves.toMatchObject({
lastOnlineStatus: 'away', lastOnlineStatus: 'away',
awaySince, awaySince,

View File

@ -4,7 +4,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
import nodemailer from 'nodemailer' import { createTransport } from 'nodemailer'
import { htmlToText } from 'nodemailer-html-to-text' import { htmlToText } from 'nodemailer-html-to-text'
import CONFIG from '@config/index' import CONFIG from '@config/index'
@ -15,7 +15,7 @@ const hasAuthData = CONFIG.SMTP_USERNAME && CONFIG.SMTP_PASSWORD
const hasDKIMData = const hasDKIMData =
CONFIG.SMTP_DKIM_DOMAINNAME && CONFIG.SMTP_DKIM_KEYSELECTOR && CONFIG.SMTP_DKIM_PRIVATKEY CONFIG.SMTP_DKIM_DOMAINNAME && CONFIG.SMTP_DKIM_KEYSELECTOR && CONFIG.SMTP_DKIM_PRIVATKEY
const transporter = nodemailer.createTransport({ const transporter = createTransport({
host: CONFIG.SMTP_HOST, host: CONFIG.SMTP_HOST,
port: CONFIG.SMTP_PORT, port: CONFIG.SMTP_PORT,
ignoreTLS: CONFIG.SMTP_IGNORE_TLS, ignoreTLS: CONFIG.SMTP_IGNORE_TLS,

View File

@ -15,7 +15,6 @@ import hashtags from './hashtags/hashtagsMiddleware'
import includedFields from './includedFieldsMiddleware' import includedFields from './includedFieldsMiddleware'
import languages from './languages/languages' import languages from './languages/languages'
import login from './login/loginMiddleware' import login from './login/loginMiddleware'
// eslint-disable-next-line import/no-cycle
import notifications from './notifications/notificationsMiddleware' import notifications from './notifications/notificationsMiddleware'
import orderBy from './orderByMiddleware' import orderBy from './orderByMiddleware'
import permissions from './permissionsMiddleware' import permissions from './permissionsMiddleware'

View File

@ -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-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
import { ApolloServer } from 'apollo-server-express'
import { createTestClient } from 'apollo-server-testing' import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import databaseContext from '@context/database'
import Factory, { cleanDatabase } from '@db/factories' import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation'
import { joinGroupMutation } from '@graphql/queries/joinGroupMutation' import { joinGroupMutation } from '@graphql/queries/joinGroupMutation'
import CONFIG from '@src/config' import CONFIG from '@src/config'
import createServer from '@src/server' import createServer, { getContext } from '@src/server'
CONFIG.CATEGORIES_ACTIVE = false CONFIG.CATEGORIES_ACTIVE = false
@ -21,13 +21,10 @@ jest.mock('@middleware/helpers/email/sendMail', () => ({
sendMail: (notification) => sendMailMock(notification), sendMail: (notification) => sendMailMock(notification),
})) }))
let server, query, mutate, authenticatedUser, emaillessMember let query, mutate, authenticatedUser, emaillessMember
let postAuthor, groupMember let postAuthor, groupMember
const driver = getDriver()
const neode = getNeode()
const mentionString = ` const mentionString = `
<a class="mention" data-mention-id="group-member" href="/profile/group-member/group-member">@group-member</a> <a class="mention" data-mention-id="group-member" href="/profile/group-member/group-member">@group-member</a>
<a class="mention" data-mention-id="email-less-member" href="/profile/email-less-member/email-less-member">@email-less-member</a>` <a class="mention" data-mention-id="email-less-member" href="/profile/email-less-member/email-less-member">@email-less-member</a>`
@ -97,22 +94,18 @@ const markAllAsRead = async () =>
`, `,
}) })
const database = databaseContext()
let server: ApolloServer
beforeAll(async () => { beforeAll(async () => {
await cleanDatabase() await cleanDatabase()
const createServerResult = createServer({ const contextUser = async (_req) => authenticatedUser
context: () => { const context = getContext({ user: contextUser, database })
return {
user: authenticatedUser, server = createServer({ context }).server
neode,
driver,
cypherParams: {
currentUserId: authenticatedUser ? authenticatedUser.id : null,
},
}
},
})
server = createServerResult.server
const createTestClientResult = createTestClient(server) const createTestClientResult = createTestClient(server)
query = createTestClientResult.query query = createTestClientResult.query
mutate = createTestClientResult.mutate mutate = createTestClientResult.mutate
@ -120,7 +113,9 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
await driver.close() void server.stop()
void database.driver.close()
database.neode.close()
}) })
describe('emails sent for notifications', () => { describe('emails sent for notifications', () => {
@ -149,7 +144,7 @@ describe('emails sent for notifications', () => {
password: '1234', password: '1234',
}, },
) )
emaillessMember = await neode.create('User', { emaillessMember = await database.neode.create('User', {
id: 'email-less-member', id: 'email-less-member',
name: 'Email-less Member', name: 'Email-less Member',
slug: 'email-less-member', slug: 'email-less-member',

View File

@ -1,16 +1,16 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* 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-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* 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 { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import databaseContext from '@context/database'
import Factory, { cleanDatabase } from '@db/factories' import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation'
import CONFIG from '@src/config' import CONFIG from '@src/config'
import createServer from '@src/server' import createServer, { getContext } from '@src/server'
CONFIG.CATEGORIES_ACTIVE = false CONFIG.CATEGORIES_ACTIVE = false
@ -19,13 +19,10 @@ jest.mock('@middleware/helpers/email/sendMail', () => ({
sendMail: (notification) => sendMailMock(notification), sendMail: (notification) => sendMailMock(notification),
})) }))
let server, query, mutate, authenticatedUser let query, mutate, authenticatedUser
let postAuthor, firstFollower, secondFollower, thirdFollower, emaillessFollower let postAuthor, firstFollower, secondFollower, thirdFollower, emaillessFollower
const driver = getDriver()
const neode = getNeode()
const createPostMutation = gql` const createPostMutation = gql`
mutation ($id: ID, $title: String!, $content: String!, $groupId: ID) { mutation ($id: ID, $title: String!, $content: String!, $groupId: ID) {
CreatePost(id: $id, title: $title, content: $content, groupId: $groupId) { CreatePost(id: $id, title: $title, content: $content, groupId: $groupId) {
@ -71,22 +68,19 @@ const followUserMutation = gql`
} }
` `
const database = databaseContext()
let server: ApolloServer
beforeAll(async () => { beforeAll(async () => {
await cleanDatabase() await cleanDatabase()
const createServerResult = createServer({ // eslint-disable-next-line @typescript-eslint/require-await
context: () => { const contextUser = async (_req) => authenticatedUser
return { const context = getContext({ user: contextUser, database })
user: authenticatedUser,
neode, server = createServer({ context }).server
driver,
cypherParams: {
currentUserId: authenticatedUser ? authenticatedUser.id : null,
},
}
},
})
server = createServerResult.server
const createTestClientResult = createTestClient(server) const createTestClientResult = createTestClient(server)
query = createTestClientResult.query query = createTestClientResult.query
mutate = createTestClientResult.mutate mutate = createTestClientResult.mutate
@ -94,7 +88,9 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
await driver.close() void server.stop()
void database.driver.close()
database.neode.close()
}) })
describe('following users notifications', () => { describe('following users notifications', () => {
@ -147,7 +143,7 @@ describe('following users notifications', () => {
password: '1234', password: '1234',
}, },
) )
emaillessFollower = await neode.create('User', { emaillessFollower = await database.neode.create('User', {
id: 'email-less-follower', id: 'email-less-follower',
name: 'Email-less Follower', name: 'Email-less Follower',
slug: 'email-less-follower', slug: 'email-less-follower',

View File

@ -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-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
import { ApolloServer } from 'apollo-server-express'
import { createTestClient } from 'apollo-server-testing' import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import databaseContext from '@context/database'
import Factory, { cleanDatabase } from '@db/factories' import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation' import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation'
import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation'
import { joinGroupMutation } from '@graphql/queries/joinGroupMutation' import { joinGroupMutation } from '@graphql/queries/joinGroupMutation'
import CONFIG from '@src/config' import CONFIG from '@src/config'
import createServer from '@src/server' import createServer, { getContext } from '@src/server'
CONFIG.CATEGORIES_ACTIVE = false CONFIG.CATEGORIES_ACTIVE = false
@ -22,13 +22,10 @@ jest.mock('@middleware/helpers/email/sendMail', () => ({
sendMail: (notification) => sendMailMock(notification), sendMail: (notification) => sendMailMock(notification),
})) }))
let server, query, mutate, authenticatedUser let query, mutate, authenticatedUser
let postAuthor, groupMember, pendingMember, noMember, emaillessMember let postAuthor, groupMember, pendingMember, noMember, emaillessMember
const driver = getDriver()
const neode = getNeode()
const mentionString = ` const mentionString = `
<a class="mention" data-mention-id="no-member" href="/profile/no-member/no-member">@no-member</a> <a class="mention" data-mention-id="no-member" href="/profile/no-member/no-member">@no-member</a>
<a class="mention" data-mention-id="pending-member" href="/profile/pending-member/pending-member">@pending-member</a> <a class="mention" data-mention-id="pending-member" href="/profile/pending-member/pending-member">@pending-member</a>
@ -93,22 +90,18 @@ const markAllAsRead = async () =>
`, `,
}) })
const database = databaseContext()
let server: ApolloServer
beforeAll(async () => { beforeAll(async () => {
await cleanDatabase() await cleanDatabase()
const createServerResult = createServer({ const contextUser = async (_req) => authenticatedUser
context: () => { const context = getContext({ user: contextUser, database })
return {
user: authenticatedUser, server = createServer({ context }).server
neode,
driver,
cypherParams: {
currentUserId: authenticatedUser ? authenticatedUser.id : null,
},
}
},
})
server = createServerResult.server
const createTestClientResult = createTestClient(server) const createTestClientResult = createTestClient(server)
query = createTestClientResult.query query = createTestClientResult.query
mutate = createTestClientResult.mutate mutate = createTestClientResult.mutate
@ -116,7 +109,9 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
await driver.close() void server.stop()
void database.driver.close()
database.neode.close()
}) })
describe('mentions in groups', () => { describe('mentions in groups', () => {
@ -169,7 +164,7 @@ describe('mentions in groups', () => {
password: '1234', password: '1234',
}, },
) )
emaillessMember = await neode.create('User', { emaillessMember = await database.neode.create('User', {
id: 'email-less-member', id: 'email-less-member',
name: 'Email-less Member', name: 'Email-less Member',
slug: 'email-less-member', slug: 'email-less-member',

View File

@ -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-call */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { ApolloServer } from 'apollo-server-express'
import { createTestClient } from 'apollo-server-testing' import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import CONFIG from '@config/index' import CONFIG from '@config/index'
import databaseContext from '@context/database'
import Factory, { cleanDatabase } from '@db/factories' import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j' import createServer, { getContext } from '@src/server'
import createServer from '@src/server'
CONFIG.CATEGORIES_ACTIVE = false CONFIG.CATEGORIES_ACTIVE = false
@ -17,13 +19,10 @@ jest.mock('@middleware/helpers/email/sendMail', () => ({
sendMail: (notification) => sendMailMock(notification), sendMail: (notification) => sendMailMock(notification),
})) }))
let server, query, mutate, authenticatedUser let query, mutate, authenticatedUser
let postAuthor, firstCommenter, secondCommenter, emaillessObserver let postAuthor, firstCommenter, secondCommenter, emaillessObserver
const driver = getDriver()
const neode = getNeode()
const createPostMutation = gql` const createPostMutation = gql`
mutation ($id: ID, $title: String!, $content: String!) { mutation ($id: ID, $title: String!, $content: String!) {
CreatePost(id: $id, title: $title, content: $content) { CreatePost(id: $id, title: $title, content: $content) {
@ -78,23 +77,18 @@ const toggleObservePostMutation = gql`
} }
} }
` `
const database = databaseContext()
let server: ApolloServer
beforeAll(async () => { beforeAll(async () => {
await cleanDatabase() await cleanDatabase()
const createServerResult = createServer({ const contextUser = async (_req) => authenticatedUser
context: () => { const context = getContext({ user: contextUser, database })
return {
user: authenticatedUser, server = createServer({ context }).server
neode,
driver,
cypherParams: {
currentUserId: authenticatedUser ? authenticatedUser.id : null,
},
}
},
})
server = createServerResult.server
const createTestClientResult = createTestClient(server) const createTestClientResult = createTestClient(server)
query = createTestClientResult.query query = createTestClientResult.query
mutate = createTestClientResult.mutate mutate = createTestClientResult.mutate
@ -102,7 +96,9 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
await driver.close() void server.stop()
void database.driver.close()
database.neode.close()
}) })
describe('notifications for users that observe a post', () => { describe('notifications for users that observe a post', () => {
@ -143,7 +139,7 @@ describe('notifications for users that observe a post', () => {
password: '1234', password: '1234',
}, },
) )
emaillessObserver = await neode.create('User', { emaillessObserver = await database.neode.create('User', {
id: 'email-less-observer', id: 'email-less-observer',
name: 'Email-less Observer', name: 'Email-less Observer',
slug: 'email-less-observer', slug: 'email-less-observer',

View File

@ -1,15 +1,14 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* 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-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
import { createTestClient } from 'apollo-server-testing' import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import databaseContext from '@context/database'
import Factory, { cleanDatabase } from '@db/factories' import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import CONFIG from '@src/config' import CONFIG from '@src/config'
import createServer from '@src/server' import createServer, { getContext } from '@src/server'
CONFIG.CATEGORIES_ACTIVE = false CONFIG.CATEGORIES_ACTIVE = false
@ -23,13 +22,10 @@ jest.mock('../helpers/isUserOnline', () => ({
isUserOnline: () => isUserOnlineMock(), isUserOnline: () => isUserOnlineMock(),
})) }))
let server, mutate, authenticatedUser let mutate, authenticatedUser
let postAuthor let postAuthor
const driver = getDriver()
const neode = getNeode()
const createPostMutation = gql` const createPostMutation = gql`
mutation ($id: ID, $title: String!, $content: String!, $groupId: ID) { mutation ($id: ID, $title: String!, $content: String!, $groupId: ID) {
CreatePost(id: $id, title: $title, content: $content, groupId: $groupId) { CreatePost(id: $id, title: $title, content: $content, groupId: $groupId) {
@ -40,29 +36,24 @@ const createPostMutation = gql`
} }
` `
const database = databaseContext()
beforeAll(async () => { beforeAll(async () => {
await cleanDatabase() await cleanDatabase()
const createServerResult = createServer({ // eslint-disable-next-line @typescript-eslint/require-await
context: () => { const contextUser = async (_req) => authenticatedUser
return { const context = getContext({ user: contextUser, database })
user: authenticatedUser,
neode, const { server } = createServer({ context })
driver,
cypherParams: {
currentUserId: authenticatedUser ? authenticatedUser.id : null,
},
}
},
})
server = createServerResult.server
const createTestClientResult = createTestClient(server) const createTestClientResult = createTestClient(server)
mutate = createTestClientResult.mutate mutate = createTestClientResult.mutate
}) })
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
await driver.close() await database.driver.close()
}) })
afterEach(async () => { afterEach(async () => {

View File

@ -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-member-access */
/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
import { ApolloServer } from 'apollo-server-express'
import { createTestClient } from 'apollo-server-testing' import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import databaseContext from '@context/database'
import Factory, { cleanDatabase } from '@db/factories' import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation' import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation'
import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation'
import { joinGroupMutation } from '@graphql/queries/joinGroupMutation' import { joinGroupMutation } from '@graphql/queries/joinGroupMutation'
import CONFIG from '@src/config' import CONFIG from '@src/config'
import createServer from '@src/server' import createServer, { getContext } from '@src/server'
CONFIG.CATEGORIES_ACTIVE = false CONFIG.CATEGORIES_ACTIVE = false
@ -22,13 +22,10 @@ jest.mock('@middleware/helpers/email/sendMail', () => ({
sendMail: (notification) => sendMailMock(notification), sendMail: (notification) => sendMailMock(notification),
})) }))
let server, query, mutate, authenticatedUser let query, mutate, authenticatedUser
let postAuthor, groupMember, pendingMember, emaillessMember let postAuthor, groupMember, pendingMember, emaillessMember
const driver = getDriver()
const neode = getNeode()
const createPostMutation = gql` const createPostMutation = gql`
mutation ($id: ID, $title: String!, $content: String!, $groupId: ID) { mutation ($id: ID, $title: String!, $content: String!, $groupId: ID) {
CreatePost(id: $id, title: $title, content: $content, groupId: $groupId) { CreatePost(id: $id, title: $title, content: $content, groupId: $groupId) {
@ -95,22 +92,17 @@ const markAllAsRead = async () =>
`, `,
}) })
const database = databaseContext()
let server: ApolloServer
beforeAll(async () => { beforeAll(async () => {
await cleanDatabase() await cleanDatabase()
const createServerResult = createServer({ const contextUser = async (_req) => authenticatedUser
context: () => { const context = getContext({ user: contextUser, database })
return {
user: authenticatedUser, server = createServer({ context }).server
neode,
driver,
cypherParams: {
currentUserId: authenticatedUser ? authenticatedUser.id : null,
},
}
},
})
server = createServerResult.server
const createTestClientResult = createTestClient(server) const createTestClientResult = createTestClient(server)
query = createTestClientResult.query query = createTestClientResult.query
mutate = createTestClientResult.mutate mutate = createTestClientResult.mutate
@ -118,7 +110,9 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
await driver.close() void server.stop()
void database.driver.close()
database.neode.close()
}) })
describe('notify group members of new posts in group', () => { describe('notify group members of new posts in group', () => {
@ -159,7 +153,7 @@ describe('notify group members of new posts in group', () => {
password: '1234', password: '1234',
}, },
) )
emaillessMember = await neode.create('User', { emaillessMember = await database.neode.create('User', {
id: 'email-less-member', id: 'email-less-member',
name: 'Email-less Member', name: 'Email-less Member',
slug: 'email-less-member', slug: 'email-less-member',

View File

@ -4,11 +4,13 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
import { ApolloServer } from 'apollo-server-express'
import { createTestClient } from 'apollo-server-testing' import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import databaseContext from '@context/database'
import pubsubContext from '@context/pubsub'
import Factory, { cleanDatabase } from '@db/factories' import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation' import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation'
import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation'
import { createMessageMutation } from '@graphql/queries/createMessageMutation' import { createMessageMutation } from '@graphql/queries/createMessageMutation'
@ -16,7 +18,7 @@ import { createRoomMutation } from '@graphql/queries/createRoomMutation'
import { joinGroupMutation } from '@graphql/queries/joinGroupMutation' import { joinGroupMutation } from '@graphql/queries/joinGroupMutation'
import { leaveGroupMutation } from '@graphql/queries/leaveGroupMutation' import { leaveGroupMutation } from '@graphql/queries/leaveGroupMutation'
import { removeUserFromGroupMutation } from '@graphql/queries/removeUserFromGroupMutation' import { removeUserFromGroupMutation } from '@graphql/queries/removeUserFromGroupMutation'
import createServer, { pubsub } from '@src/server' import createServer, { getContext } from '@src/server'
const sendMailMock: (notification) => void = jest.fn() const sendMailMock: (notification) => void = jest.fn()
jest.mock('@middleware/helpers/email/sendMail', () => ({ jest.mock('@middleware/helpers/email/sendMail', () => ({
@ -35,12 +37,12 @@ jest.mock('../helpers/isUserOnline', () => ({
isUserOnline: () => isUserOnlineMock(), isUserOnline: () => isUserOnlineMock(),
})) }))
const database = databaseContext()
const pubsub = pubsubContext()
const pubsubSpy = jest.spyOn(pubsub, 'publish') 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 categoryIds = ['cat9']
const createPostMutation = gql` const createPostMutation = gql`
mutation ($id: ID, $title: String!, $postContent: String!, $categoryIds: [ID]!) { mutation ($id: ID, $title: String!, $postContent: String!, $categoryIds: [ID]!) {
@ -68,19 +70,16 @@ const createCommentMutation = gql`
} }
` `
let server: ApolloServer
beforeAll(async () => { beforeAll(async () => {
await cleanDatabase() await cleanDatabase()
const createServerResult = createServer({ const contextUser = async (_req) => authenticatedUser
context: () => { const context = getContext({ user: contextUser, database, pubsub })
return {
user: authenticatedUser, server = createServer({ context }).server
neode,
driver,
}
},
})
server = createServerResult.server
const createTestClientResult = createTestClient(server) const createTestClientResult = createTestClient(server)
query = createTestClientResult.query query = createTestClientResult.query
mutate = createTestClientResult.mutate mutate = createTestClientResult.mutate
@ -88,7 +87,9 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
await driver.close() void server.stop()
void database.driver.close()
database.neode.close()
}) })
beforeEach(async () => { beforeEach(async () => {
@ -104,7 +105,7 @@ beforeEach(async () => {
password: '1234', password: '1234',
}, },
) )
await neode.create('Category', { await database.neode.create('Category', {
id: 'cat9', id: 'cat9',
name: 'Democracy & Politics', name: 'Democracy & Politics',
icon: 'university', icon: 'university',

View File

@ -1,10 +1,14 @@
/* eslint-disable import/no-cycle */
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable security/detect-object-injection */ /* 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 { getUnreadRoomsCount } from '@graphql/resolvers/rooms'
import { sendMail } from '@middleware/helpers/email/sendMail' import { sendMail } from '@middleware/helpers/email/sendMail'
import { import {
@ -13,7 +17,6 @@ import {
} from '@middleware/helpers/email/templateBuilder' } from '@middleware/helpers/email/templateBuilder'
import { isUserOnline } from '@middleware/helpers/isUserOnline' import { isUserOnline } from '@middleware/helpers/isUserOnline'
import { validateNotifyUsers } from '@middleware/validation/validationMiddleware' import { validateNotifyUsers } from '@middleware/validation/validationMiddleware'
import { pubsub, NOTIFICATION_ADDED, ROOM_COUNT_UPDATED, CHAT_MESSAGE_ADDED } from '@src/server'
import extractMentionedUsers from './mentions/extractMentionedUsers' import extractMentionedUsers from './mentions/extractMentionedUsers'
@ -25,7 +28,7 @@ const publishNotifications = async (
): Promise<string[]> => { ): Promise<string[]> => {
const notifications = await notificationsPromise const notifications = await notificationsPromise
notifications.forEach((notificationAdded) => { notifications.forEach((notificationAdded) => {
pubsub.publish(NOTIFICATION_ADDED, { notificationAdded }) context.pubsub.publish(NOTIFICATION_ADDED, { notificationAdded })
if ( if (
notificationAdded.email && // no primary email was found notificationAdded.email && // no primary email was found
(notificationAdded.to[emailNotificationSetting] ?? true) && (notificationAdded.to[emailNotificationSetting] ?? true) &&
@ -482,11 +485,11 @@ const handleCreateMessage = async (resolve, root, args, context, resolveInfo) =>
// send subscriptions // send subscriptions
const roomCountUpdated = await getUnreadRoomsCount(recipientUser.id, session) const roomCountUpdated = await getUnreadRoomsCount(recipientUser.id, session)
void pubsub.publish(ROOM_COUNT_UPDATED, { void context.pubsub.publish(ROOM_COUNT_UPDATED, {
roomCountUpdated, roomCountUpdated,
userId: recipientUser.id, userId: recipientUser.id,
}) })
void pubsub.publish(CHAT_MESSAGE_ADDED, { void context.pubsub.publish(CHAT_MESSAGE_ADDED, {
chatMessageAdded: message, chatMessageAdded: message,
userId: recipientUser.id, userId: recipientUser.id,
}) })

View File

@ -10,62 +10,55 @@ import http from 'node:http'
import { ApolloServer } from 'apollo-server-express' import { ApolloServer } from 'apollo-server-express'
import bodyParser from 'body-parser' import bodyParser from 'body-parser'
import express from 'express' import express from 'express'
import { RedisPubSub } from 'graphql-redis-subscriptions'
import { PubSub } from 'graphql-subscriptions'
import { graphqlUploadExpress } from 'graphql-upload' import { graphqlUploadExpress } from 'graphql-upload'
import helmet from 'helmet' import helmet from 'helmet'
import Redis from 'ioredis'
import databaseContext from '@context/database'
import pubsubContext from '@context/pubsub'
import CONFIG from './config' import CONFIG from './config'
import { getNeode, getDriver } from './db/neo4j'
import schema from './graphql/schema' import schema from './graphql/schema'
import decode from './jwt/decode' import decode from './jwt/decode'
// eslint-disable-next-line import/no-cycle
import middleware from './middleware' import middleware from './middleware'
export const NOTIFICATION_ADDED = 'NOTIFICATION_ADDED' const serverDatabase = databaseContext()
export const CHAT_MESSAGE_ADDED = 'CHAT_MESSAGE_ADDED' const serverPubsub = pubsubContext()
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 getContext = async (req) => { const databaseUser = async (req) => decode(serverDatabase.driver, req.headers.authorization)
const user = await decode(driver, req.headers.authorization)
return { export const getContext =
driver, (
neode, {
user, database = serverDatabase,
req, pubsub = serverPubsub,
cypherParams: { user = databaseUser,
currentUserId: user ? user.id : null, }: {
}, database?: ReturnType<typeof databaseContext>
pubsub?: ReturnType<typeof pubsubContext>
user?: (any) => Promise<any>
} = { 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) => { export const context = async (options) => {
const { connection, req } = options const { connection, req } = options
if (connection) { if (connection) {
return connection.context return connection.context
} else { } else {
return getContext(req) return getContext()(req)
} }
} }
@ -74,9 +67,7 @@ const createServer = (options?) => {
context, context,
schema: middleware(schema), schema: middleware(schema),
subscriptions: { subscriptions: {
onConnect: (connectionParams, _webSocket) => { onConnect: (connectionParams) => getContext()(connectionParams),
return getContext(connectionParams)
},
}, },
debug: !!CONFIG.DEBUG, debug: !!CONFIG.DEBUG,
uploads: false, uploads: false,
@ -88,11 +79,10 @@ const createServer = (options?) => {
return error return error
}, },
} }
const server = new ApolloServer(Object.assign({}, defaults, options)) const server = new ApolloServer(Object.assign(defaults, options))
const app = express() const app = express()
app.set('driver', driver)
// TODO: this exception is required for the graphql playground, since the playground loads external resources // TODO: this exception is required for the graphql playground, since the playground loads external resources
// See: https://github.com/graphql/graphql-playground/issues/1283 // See: https://github.com/graphql/graphql-playground/issues/1283
app.use( app.use(

View File

@ -32,6 +32,7 @@
"paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */ "paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */
"@config/*": ["./src/config/*"], "@config/*": ["./src/config/*"],
"@constants/*": ["./src/constants/*"], "@constants/*": ["./src/constants/*"],
"@context/*": ["./src/context/*"],
"@db/*": ["./src/db/*"], "@db/*": ["./src/db/*"],
"@graphql/*": ["./src/graphql/*"], "@graphql/*": ["./src/graphql/*"],
"@helpers/*": ["./src/helpers/*"], "@helpers/*": ["./src/helpers/*"],