refactor(backend): put config into context (#8603)

This is a side quest of #8558. The motivation is to be able to do dependency injection in the tests without overwriting global data. I saw the first merge conflict from #8551 and voila: It seems @Mogge could have used this already.

refactor: follow @Mogge's review

See: https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8603#pullrequestreview-2880714796

refactor: better test helper methods

wip: continue refactoring

wip: continue posts

continue

wip: continue groups

continue registration

registration

continue messages

continue observeposts

continue categories

continue posts in groups

continue invite codes

refactor: continue notificationsMiddleware

continue statistics spec

followed-users

online-status

mentions-in-groups

posts-in-groups

email spec

finish all tests

improve typescript

missed one test

remove one more reference of CONFIG

eliminate one more global import of CONFIG

fix language spec test

fix two more test suites

refactor: completely mock out 3rd part API request

refactor test

fixed user_management spec

fixed more locatoin specs

install types for jsonwebtoken

one more fetchmock

fixed one more suite

fix one more spec

yet another spec

fix spec

delete whitespaces

remove beforeAll that the same as the default

fix merge conflict

fix e2e test

refactor: use single callback function for `context` setup

refactor: display logs from backend during CI

Because why not?

fix seeds

fix login

refactor: one unnecessary naming

refactor: better editor support

refactor: fail early

Interestingly, I've had to destructure `context.user` in order to make
typescript happy. Weird.

refactor: undo changes to workflows - no effect

We're running in `--detached` mode on CI, so I guess we won't be able to
see the logs anyways.

refactor: remove fetch from context after review

See:

refactor: found an easier way for required props

Co-authored-by: Max <maxharz@gmail.com>
Co-authored-by: Ulf Gebhardt <ulf.gebhardt@webcraft-media.de>
This commit is contained in:
Robert Schäfer 2025-07-03 17:58:03 +08:00 committed by GitHub
parent 3c59e08412
commit 61813c4eb8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
60 changed files with 1246 additions and 1061 deletions

View File

@ -98,6 +98,8 @@
"@types/jest": "^29.5.14",
"@types/lodash": "^4.17.19",
"@types/node": "^24.0.10",
"@types/jsonwebtoken": "~8.5.1",
"@types/request": "^2.48.12",
"@types/slug": "^5.0.9",
"@types/uuid": "~9.0.1",
"@typescript-eslint/eslint-plugin": "^5.62.0",

View File

@ -38,6 +38,21 @@ const required = {
PRIVATE_KEY_PASSPHRASE: env.PRIVATE_KEY_PASSPHRASE,
}
// https://stackoverflow.com/a/53050575
type NoUndefinedField<T> = { [P in keyof T]-?: NoUndefinedField<NonNullable<T[P]>> }
function assertRequiredConfig(
conf: typeof required,
): asserts conf is NoUndefinedField<typeof required> {
Object.entries(conf).forEach(([key, value]) => {
if (!value) {
throw new Error(`ERROR: "${key}" env variable is missing.`)
}
})
}
assertRequiredConfig(required)
const server = {
CLIENT_URI: env.CLIENT_URI ?? 'http://localhost:3000',
GRAPHQL_URI: env.GRAPHQL_URI ?? 'http://localhost:4000',
@ -147,15 +162,7 @@ const language = {
LANGUAGE_DEFAULT: process.env.LANGUAGE_DEFAULT ?? 'en',
}
// Check if all required configs are present
Object.entries(required).map((entry) => {
if (!entry[1]) {
throw new Error(`ERROR: "${entry[0]}" env variable is missing.`)
}
return entry
})
export default {
const CONFIG = {
...environment,
...server,
...required,
@ -167,4 +174,7 @@ export default {
...language,
}
export type Config = typeof CONFIG
export default CONFIG
export { nodemailerTransportOptions }

View File

@ -0,0 +1,61 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
import databaseContext from '@context/database'
import pubsubContext from '@context/pubsub'
import CONFIG from '@src/config'
import type { DecodedUser } from '@src/jwt/decode'
import { decode } from '@src/jwt/decode'
import ocelotLogger from '@src/logger'
import type OcelotLogger from '@src/logger'
import type { ApolloServerExpressConfig } from 'apollo-server-express'
const serverDatabase = databaseContext()
const serverPubsub = pubsubContext()
export const getContext =
(opts?: {
database?: ReturnType<typeof databaseContext>
pubsub?: ReturnType<typeof pubsubContext>
authenticatedUser: DecodedUser | null | undefined
logger?: typeof OcelotLogger
config: typeof CONFIG
}) =>
async (req: { headers: { authorization?: string } }) => {
const {
database = serverDatabase,
pubsub = serverPubsub,
authenticatedUser = undefined,
logger = ocelotLogger,
config = CONFIG,
} = opts ?? {}
const { driver } = database
const user =
authenticatedUser === null
? null
: (authenticatedUser ?? (await decode({ driver, config })(req.headers.authorization)))
const result = {
database,
driver,
neode: database.neode,
pubsub,
logger,
user,
req,
cypherParams: {
currentUserId: user ? user.id : null,
},
config,
}
return result
}
export const context: ApolloServerExpressConfig['context'] = async (options) => {
const { connection, req } = options
if (connection) {
return connection.context
} else {
return getContext()(req)
}
}
export type Context = Awaited<ReturnType<ReturnType<typeof getContext>>>

View File

@ -13,7 +13,7 @@ import { v4 as uuid } from 'uuid'
import { generateInviteCode } from '@graphql/resolvers/inviteCodes'
import { isUniqueFor } from '@middleware/sluggifyMiddleware'
import uniqueSlug from '@middleware/slugify/uniqueSlug'
import { Context } from '@src/server'
import { Context } from '@src/context'
import { getDriver, getNeode } from './neo4j'

View File

@ -4,7 +4,6 @@
/* eslint-disable @typescript-eslint/no-floating-promises */
/* eslint-disable n/no-process-exit */
import { faker } from '@faker-js/faker'
import { createTestClient } from 'apollo-server-testing'
import sample from 'lodash/sample'
import CONFIG from '@config/index'
@ -16,10 +15,9 @@ import { CreateMessage } from '@graphql/queries/CreateMessage'
import { createPostMutation } from '@graphql/queries/createPostMutation'
import { createRoomMutation } from '@graphql/queries/createRoomMutation'
import { joinGroupMutation } from '@graphql/queries/joinGroupMutation'
import createServer from '@src/server'
import { createApolloTestSetup } from '@root/test/helpers'
import Factory from './factories'
import { getNeode, getDriver } from './neo4j'
import { trophies, verification } from './seed/badges'
if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) {
@ -35,22 +33,21 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
console.log('Seeded Data...')
let authenticatedUser = null
const driver = getDriver()
const neode = getNeode()
const { server } = createServer({
context: () => {
return {
driver,
neode,
user: authenticatedUser,
}
},
// locations
const context = () => ({
authenticatedUser,
config: CONFIG,
})
const { mutate } = createTestClient(server)
const apolloSetup = createApolloTestSetup({ context })
const { mutate, server, database } = apolloSetup
const { neode } = database
try {
// eslint-disable-next-line no-console
console.log('seed', 'locations')
// locations
const Hamburg = await Factory.build('location', {
id: 'region.5127278006398860',
name: 'Hamburg',
@ -1618,7 +1615,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
throw err
} finally {
await server.stop()
await driver.close()
await database.driver.close()
// eslint-disable-next-line @typescript-eslint/await-thenable
await neode.close()
process.exit(0)

View File

@ -9,15 +9,14 @@ import { Readable } from 'node:stream'
import { S3Client } from '@aws-sdk/client-s3'
import { Upload } from '@aws-sdk/lib-storage'
import { UserInputError } from 'apollo-server'
import { createTestClient } from 'apollo-server-testing'
import databaseContext from '@context/database'
import Factory, { cleanDatabase } from '@db/factories'
import File from '@db/models/File'
import { CreateMessage } from '@graphql/queries/CreateMessage'
import { createRoomMutation } from '@graphql/queries/createRoomMutation'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { S3Configured } from '@src/config'
import createServer, { getContext } from '@src/server'
import { attachments } from './attachments'
@ -47,20 +46,19 @@ const config: S3Configured = {
S3_PUBLIC_GATEWAY: undefined,
}
const database = databaseContext()
let authenticatedUser, server, mutate
let authenticatedUser
const context = () => ({ authenticatedUser, config })
let mutate: ApolloTestSetup['mutate']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
beforeAll(async () => {
await cleanDatabase()
const contextUser = async (_req) => authenticatedUser
const context = getContext({ user: contextUser, database })
server = createServer({ context }).server
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
mutate = createTestClient(server).mutate
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {
@ -115,7 +113,7 @@ describe('delete Attachment', () => {
},
})
message = m.data.CreateMessage
message = (m.data as any).CreateMessage // eslint-disable-line @typescript-eslint/no-explicit-any
await database.write({
query: `

View File

@ -1,37 +1,32 @@
/* 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 { TROPHY_BADGES_SELECTED_MAX } from '@constants/badges'
import databaseContext from '@context/database'
import Factory, { cleanDatabase } from '@db/factories'
import { rewardTrophyBadge } from '@graphql/queries/rewardTrophyBadge'
import { setTrophyBadgeSelected } from '@graphql/queries/setTrophyBadgeSelected'
import createServer, { getContext } from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
let regularUser, administrator, moderator, badge, verification
const database = databaseContext()
let server: ApolloServer
let authenticatedUser
let query, mutate
let authenticatedUser: Context['user']
const context = () => ({ authenticatedUser })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
beforeAll(async () => {
await cleanDatabase()
// 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
const createTestClientResult = createTestClient(server)
query = createTestClientResult.query
mutate = createTestClientResult.mutate
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(() => {
@ -838,7 +833,7 @@ describe('Badges', () => {
describe('check test setup', () => {
it('user has one badge and has it selected', async () => {
authenticatedUser = regularUser.toJson()
authenticatedUser = await regularUser.toJson()
const userQuery = gql`
{
User(id: "regular-user-id") {

View File

@ -7,7 +7,7 @@
import { neo4jgraphql } from 'neo4j-graphql-js'
import { TROPHY_BADGES_SELECTED_MAX } from '@constants/badges'
import { Context } from '@src/server'
import { Context } from '@src/context'
export const defaultTrophyBadge = {
id: 'default_trophy',
@ -32,7 +32,10 @@ export default {
},
Mutation: {
setVerificationBadge: async (_object, args, context, _resolveInfo) => {
setVerificationBadge: async (_object, args, context: Context, _resolveInfo) => {
if (!context.user) {
throw new Error('Missing authenticated user.')
}
const {
user: { id: currentUserId },
} = context
@ -70,11 +73,14 @@ export default {
} catch (error) {
throw new Error(error)
} finally {
session.close()
await session.close()
}
},
rewardTrophyBadge: async (_object, args, context: Context, _resolveInfo) => {
if (!context.user) {
throw new Error('Missing authenticated user.')
}
const {
user: { id: currentUserId },
} = context

View File

@ -2,29 +2,26 @@
/* 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 createServer, { getContext } from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
const database = databaseContext()
let variables, commentAuthor, newlyCreatedComment
let authenticatedUser: Context['user']
const context = () => ({ authenticatedUser })
let mutate: ApolloTestSetup['mutate']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
let variables, mutate, authenticatedUser, commentAuthor, newlyCreatedComment
let server: ApolloServer
beforeAll(async () => {
await cleanDatabase()
// 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
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {
@ -35,6 +32,7 @@ afterAll(async () => {
})
beforeEach(async () => {
authenticatedUser = null
variables = {}
await database.neode.create('Category', {
id: 'cat9',
@ -98,14 +96,14 @@ describe('CreateComment', () => {
content: "I'm not authorized to comment",
}
const { errors } = await mutate({ mutation: createCommentMutation, variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
describe('authenticated', () => {
beforeEach(async () => {
const user = await database.neode.create('User', { name: 'Author' })
authenticatedUser = await user.toJson()
authenticatedUser = (await user.toJson()) as Context['user']
})
describe('given a post', () => {
@ -157,7 +155,7 @@ describe('UpdateComment', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({ mutation: updateCommentMutation, variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -169,7 +167,7 @@ describe('UpdateComment', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({ mutation: updateCommentMutation, variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -208,7 +206,7 @@ describe('UpdateComment', () => {
newlyCreatedComment = await newlyCreatedComment.toJson()
const {
data: { UpdateComment },
} = await mutate({ mutation: updateCommentMutation, variables })
} = (await mutate({ mutation: updateCommentMutation, variables })) as any // eslint-disable-line @typescript-eslint/no-explicit-any
expect(newlyCreatedComment.updatedAt).toBeTruthy()
expect(Date.parse(newlyCreatedComment.updatedAt)).toEqual(expect.any(Number))
expect(UpdateComment.updatedAt).toBeTruthy()
@ -224,7 +222,7 @@ describe('UpdateComment', () => {
it('returns null', async () => {
const { data, errors } = await mutate({ mutation: updateCommentMutation, variables })
expect(data).toMatchObject({ UpdateComment: null })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@ -249,7 +247,7 @@ describe('DeleteComment', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const result = await mutate({ mutation: deleteCommentMutation, variables })
expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!')
expect(result.errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -261,7 +259,7 @@ describe('DeleteComment', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({ mutation: deleteCommentMutation, variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})

View File

@ -1,44 +1,37 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { createTestClient } from 'apollo-server-testing'
import CONFIG from '@config/index'
/* eslint-disable @typescript-eslint/no-explicit-any */
import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import { createPostMutation } from '@graphql/queries/createPostMutation'
import { filterPosts } from '@graphql/queries/filterPosts'
import createServer from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
CONFIG.CATEGORIES_ACTIVE = false
const driver = getDriver()
const neode = getNeode()
let query
let mutate
let authenticatedUser
let user
let authenticatedUser: Context['user']
const config = { CATEGORIES_ACTIVE: false }
const context = () => ({ authenticatedUser, config })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
beforeAll(async () => {
await cleanDatabase()
const { server } = createServer({
context: () => {
return {
driver,
neode,
user: authenticatedUser,
}
},
})
query = createTestClient(server).query
mutate = createTestClient(server).mutate
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {
await cleanDatabase()
await driver.close()
void server.stop()
void database.driver.close()
database.neode.close()
})
describe('Filter Posts', () => {
@ -99,7 +92,7 @@ describe('Filter Posts', () => {
it('finds all posts', async () => {
const {
data: { Post: result },
} = await query({ query: filterPosts() })
} = (await query({ query: filterPosts() })) as any
expect(result).toHaveLength(4)
expect(result).toEqual(
expect.arrayContaining([
@ -116,7 +109,10 @@ describe('Filter Posts', () => {
it('finds the articles', async () => {
const {
data: { Post: result },
} = await query({ query: filterPosts(), variables: { filter: { postType_in: ['Article'] } } })
} = (await query({
query: filterPosts(),
variables: { filter: { postType_in: ['Article'] } },
})) as any
expect(result).toHaveLength(2)
expect(result).toEqual(
expect.arrayContaining([
@ -131,7 +127,10 @@ describe('Filter Posts', () => {
it('finds the articles', async () => {
const {
data: { Post: result },
} = await query({ query: filterPosts(), variables: { filter: { postType_in: ['Event'] } } })
} = (await query({
query: filterPosts(),
variables: { filter: { postType_in: ['Event'] } },
})) as any
expect(result).toHaveLength(2)
expect(result).toEqual(
expect.arrayContaining([
@ -146,10 +145,10 @@ describe('Filter Posts', () => {
it('finds all posts', async () => {
const {
data: { Post: result },
} = await query({
} = (await query({
query: filterPosts(),
variables: { filter: { postType_in: ['Article', 'Event'] } },
})
})) as any
expect(result).toHaveLength(4)
expect(result).toEqual(
expect.arrayContaining([
@ -166,10 +165,10 @@ describe('Filter Posts', () => {
it('finds the events ordered accordingly', async () => {
const {
data: { Post: result },
} = await query({
} = (await query({
query: filterPosts(),
variables: { filter: { postType_in: ['Event'] }, orderBy: ['eventStart_desc'] },
})
})) as any
expect(result).toHaveLength(2)
expect(result).toEqual([
expect.objectContaining({
@ -190,10 +189,10 @@ describe('Filter Posts', () => {
it('finds the events ordered accordingly', async () => {
const {
data: { Post: result },
} = await query({
} = (await query({
query: filterPosts(),
variables: { filter: { postType_in: ['Event'] }, orderBy: ['eventStart_asc'] },
})
})) as any
expect(result).toHaveLength(2)
expect(result).toEqual([
expect.objectContaining({
@ -214,7 +213,7 @@ describe('Filter Posts', () => {
it('finds only events after given date', async () => {
const {
data: { Post: result },
} = await query({
} = (await query({
query: filterPosts(),
variables: {
filter: {
@ -226,7 +225,7 @@ describe('Filter Posts', () => {
).toISOString(),
},
},
})
})) as any
expect(result).toHaveLength(1)
expect(result).toEqual([
expect.objectContaining({

View File

@ -3,10 +3,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { createTestClient } from 'apollo-server-testing'
import CONFIG from '@config/index'
import databaseContext from '@context/database'
import Factory, { cleanDatabase } from '@db/factories'
import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation'
import { createGroupMutation } from '@graphql/queries/createGroupMutation'
@ -16,9 +12,11 @@ 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, { getContext } from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
// import CONFIG from '@src/config'
let authenticatedUser
let user
let noMemberUser
let pendingMemberUser
@ -27,18 +25,21 @@ let adminMemberUser
let ownerMemberUser
let secondOwnerMemberUser
let authenticatedUser: Context['user']
const context = () => ({ authenticatedUser, config })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
const categoryIds = ['cat9', 'cat4', 'cat15']
const descriptionAdditional100 =
' 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789'
let variables = {}
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 config = {
CATEGORIES_ACTIVE: true,
// MAPBOX_TOKEN: CONFIG.MAPBOX_TOKEN,
}
const seedBasicsAndClearAuthentication = async () => {
variables = {}
@ -230,7 +231,11 @@ const seedComplexScenarioAndClearAuthentication = async () => {
}
beforeAll(async () => {
await cleanDatabase()
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {
@ -270,7 +275,7 @@ describe('in mode', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({ mutation: createGroupMutation(), variables })
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -339,17 +344,13 @@ describe('in mode', () => {
'<a href="https://domain.org/0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789">0</a>',
},
})
expect(errors![0]).toHaveProperty('message', 'Description too short!')
expect(errors?.[0]).toHaveProperty('message', 'Description too short!')
})
})
})
})
describe('categories', () => {
beforeEach(() => {
CONFIG.CATEGORIES_ACTIVE = true
})
describe('with matching amount of categories', () => {
it('has new categories', async () => {
await expect(
@ -382,7 +383,7 @@ describe('in mode', () => {
mutation: createGroupMutation(),
variables: { ...variables, categoryIds: null },
})
expect(errors![0]).toHaveProperty('message', 'Too few categories!')
expect(errors?.[0]).toHaveProperty('message', 'Too few categories!')
})
})
@ -392,7 +393,7 @@ describe('in mode', () => {
mutation: createGroupMutation(),
variables: { ...variables, categoryIds: [] },
})
expect(errors![0]).toHaveProperty('message', 'Too few categories!')
expect(errors?.[0]).toHaveProperty('message', 'Too few categories!')
})
})
})
@ -403,7 +404,7 @@ describe('in mode', () => {
mutation: createGroupMutation(),
variables: { ...variables, categoryIds: ['cat9', 'cat4', 'cat15', 'cat27'] },
})
expect(errors![0]).toHaveProperty('message', 'Too many categories!')
expect(errors?.[0]).toHaveProperty('message', 'Too many categories!')
})
})
})
@ -581,10 +582,6 @@ describe('in mode', () => {
})
describe('categories', () => {
beforeEach(() => {
CONFIG.CATEGORIES_ACTIVE = true
})
it('has set categories', async () => {
await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject({
data: {
@ -811,7 +808,7 @@ describe('in mode', () => {
userId: 'current-user',
},
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -1566,7 +1563,7 @@ describe('in mode', () => {
roleInGroup: 'pending',
},
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -1721,7 +1718,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@ -1747,7 +1744,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -1796,7 +1793,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@ -1819,7 +1816,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@ -1842,7 +1839,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@ -1900,7 +1897,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@ -1923,7 +1920,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -1940,7 +1937,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@ -1963,7 +1960,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -1980,7 +1977,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@ -2003,7 +2000,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -2020,7 +2017,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@ -2110,7 +2107,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -2127,7 +2124,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@ -2150,7 +2147,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -2167,7 +2164,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@ -2190,7 +2187,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -2207,7 +2204,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@ -2297,7 +2294,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@ -2320,7 +2317,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@ -2343,7 +2340,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@ -2407,7 +2404,7 @@ describe('in mode', () => {
userId: 'current-user',
},
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -2524,7 +2521,7 @@ describe('in mode', () => {
userId: 'owner-member-user',
},
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -2538,7 +2535,7 @@ describe('in mode', () => {
userId: 'second-owner-member-user',
},
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -2552,7 +2549,7 @@ describe('in mode', () => {
userId: 'none-member-user',
},
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -2566,7 +2563,7 @@ describe('in mode', () => {
userId: 'usual-member-user',
},
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -2580,7 +2577,7 @@ describe('in mode', () => {
userId: 'admin-member-user',
},
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@ -2606,7 +2603,7 @@ describe('in mode', () => {
slug: 'my-best-group',
},
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -2859,17 +2856,13 @@ describe('in mode', () => {
'<a href="https://domain.org/0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789">0</a>',
},
})
expect(errors![0]).toHaveProperty('message', 'Description too short!')
expect(errors?.[0]).toHaveProperty('message', 'Description too short!')
})
})
})
})
describe('categories', () => {
beforeEach(async () => {
CONFIG.CATEGORIES_ACTIVE = true
})
describe('with matching amount of categories', () => {
it('has new categories', async () => {
await expect(
@ -2906,7 +2899,7 @@ describe('in mode', () => {
categoryIds: [],
},
})
expect(errors![0]).toHaveProperty('message', 'Too few categories!')
expect(errors?.[0]).toHaveProperty('message', 'Too few categories!')
})
})
})
@ -2920,7 +2913,7 @@ describe('in mode', () => {
categoryIds: ['cat9', 'cat4', 'cat15', 'cat27'],
},
})
expect(errors![0]).toHaveProperty('message', 'Too many categories!')
expect(errors?.[0]).toHaveProperty('message', 'Too many categories!')
})
})
})
@ -2940,7 +2933,7 @@ describe('in mode', () => {
categoryIds: ['cat4', 'cat27'],
},
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -2958,7 +2951,7 @@ describe('in mode', () => {
categoryIds: ['cat4', 'cat27'],
},
})
expect(errors![0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})

View File

@ -8,11 +8,10 @@
import { UserInputError } from 'apollo-server'
import { v4 as uuid } from 'uuid'
import CONFIG from '@config/index'
import { CATEGORIES_MIN, CATEGORIES_MAX } from '@constants/categories'
import { DESCRIPTION_WITHOUT_HTML_LENGTH_MIN } from '@constants/groups'
import { removeHtmlTags } from '@middleware/helpers/cleanHtml'
import type { Context } from '@src/server'
import type { Context } from '@src/context'
import Resolver, {
removeUndefinedNullValuesFromObject,
@ -32,6 +31,9 @@ export default {
removeUndefinedNullValuesFromObject(matchParams)
const session = context.driver.session()
const readTxResultPromise = session.readTransaction(async (txc) => {
if (!context.user) {
throw new Error('Missing authenticated user.')
}
const groupMatchParamsCypher = convertObjectToCypherMapLiteral(matchParams, true)
let groupCypher
if (isMember === true) {
@ -139,13 +141,14 @@ export default {
},
Mutation: {
CreateGroup: async (_parent, params, context: Context, _resolveInfo) => {
const { config } = context
const { categoryIds } = params
delete params.categoryIds
params.locationName = params.locationName === '' ? null : params.locationName
if (CONFIG.CATEGORIES_ACTIVE && (!categoryIds || categoryIds.length < CATEGORIES_MIN)) {
if (config.CATEGORIES_ACTIVE && (!categoryIds || categoryIds.length < CATEGORIES_MIN)) {
throw new UserInputError('Too few categories!')
}
if (CONFIG.CATEGORIES_ACTIVE && categoryIds && categoryIds.length > CATEGORIES_MAX) {
if (config.CATEGORIES_ACTIVE && categoryIds && categoryIds.length > CATEGORIES_MAX) {
throw new UserInputError('Too many categories!')
}
if (
@ -158,8 +161,11 @@ export default {
params.id = params.id || uuid()
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
if (!context.user) {
throw new Error('Missing authenticated user.')
}
const categoriesCypher =
CONFIG.CATEGORIES_ACTIVE && categoryIds
config.CATEGORIES_ACTIVE && categoryIds
? `
WITH group, membership
UNWIND $categoryIds AS categoryId
@ -194,7 +200,7 @@ export default {
try {
const group = await writeTxResultPromise
// TODO: put in a middleware, see "UpdateGroup", "UpdateUser"
await createOrUpdateLocations('Group', params.id, params.locationName, session)
await createOrUpdateLocations('Group', params.id, params.locationName, session, context)
return group
} catch (error) {
if (error.code === 'Neo.ClientError.Schema.ConstraintValidationFailed')
@ -205,13 +211,14 @@ export default {
}
},
UpdateGroup: async (_parent, params, context: Context, _resolveInfo) => {
const { config } = context
const { categoryIds } = params
delete params.categoryIds
const { id: groupId, avatar: avatarInput } = params
delete params.avatar
params.locationName = params.locationName === '' ? null : params.locationName
if (CONFIG.CATEGORIES_ACTIVE && categoryIds) {
if (config.CATEGORIES_ACTIVE && categoryIds) {
if (categoryIds.length < CATEGORIES_MIN) {
throw new UserInputError('Too few categories!')
}
@ -226,7 +233,7 @@ export default {
throw new UserInputError('Description too short!')
}
const session = context.driver.session()
if (CONFIG.CATEGORIES_ACTIVE && categoryIds && categoryIds.length) {
if (config.CATEGORIES_ACTIVE && categoryIds && categoryIds.length) {
const cypherDeletePreviousRelations = `
MATCH (group:Group {id: $groupId})-[previousRelations:CATEGORIZED]->(category:Category)
DELETE previousRelations
@ -237,13 +244,16 @@ export default {
})
}
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
if (!context.user) {
throw new Error('Missing authenticated user.')
}
let updateGroupCypher = `
MATCH (group:Group {id: $groupId})
SET group += $params
SET group.updatedAt = toString(datetime())
WITH group
`
if (CONFIG.CATEGORIES_ACTIVE && categoryIds && categoryIds.length) {
if (config.CATEGORIES_ACTIVE && categoryIds && categoryIds.length) {
updateGroupCypher += `
UNWIND $categoryIds AS categoryId
MATCH (category:Category {id: categoryId})
@ -263,14 +273,16 @@ export default {
})
const [group] = transactionResponse.records.map((record) => record.get('group'))
if (avatarInput) {
await images.mergeImage(group, 'AVATAR_IMAGE', avatarInput, { transaction })
await images(context.config).mergeImage(group, 'AVATAR_IMAGE', avatarInput, {
transaction,
})
}
return group
})
try {
const group = await writeTxResultPromise
// TODO: put in a middleware, see "CreateGroup", "UpdateUser"
await createOrUpdateLocations('Group', params.id, params.locationName, session)
await createOrUpdateLocations('Group', params.id, params.locationName, session, context)
return group
} catch (error) {
if (error.code === 'Neo.ClientError.Schema.ConstraintValidationFailed')
@ -380,10 +392,16 @@ export default {
}
},
muteGroup: async (_parent, params, context: Context, _resolveInfo) => {
if (!context.user) {
throw new Error('Missing authenticated user.')
}
const { groupId } = params
const userId = context.user.id
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
if (!context.user) {
throw new Error('Missing authenticated user.')
}
const transactionResponse = await transaction.run(
`
MATCH (group:Group { id: $groupId })
@ -409,6 +427,9 @@ export default {
}
},
unmuteGroup: async (_parent, params, context: Context, _resolveInfo) => {
if (!context.user) {
throw new Error('Missing authenticated user.')
}
const { groupId } = params
const userId = context.user.id
const session = context.driver.session()

View File

@ -1,10 +1,15 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import type { Context } from '@src/context'
import normalizeEmail from './normalizeEmail'
export default async function createPasswordReset(options) {
export default async function createPasswordReset(options: {
driver: Context['driver']
nonce: string
email: string
issuedAt?: Date
}) {
const { driver, nonce, email, issuedAt = new Date() } = options
const normalizedEmail = normalizeEmail(email)
const session = driver.session()
@ -33,6 +38,6 @@ export default async function createPasswordReset(options) {
const [records] = await createPasswordResetTxPromise
return records || {}
} finally {
session.close()
await session.close()
}
}

View File

@ -1,4 +1,5 @@
import CONFIG, { isS3configured } from '@config/index'
import { isS3configured } from '@config/index'
import type { Context } from '@src/context'
import type { FileDeleteCallback, FileUploadCallback } from '@src/uploads/types'
import { images as imagesLocal } from './imagesLocal'
@ -51,4 +52,5 @@ export interface Images {
) => Promise<any>
}
export const images = isS3configured(CONFIG) ? imagesS3(CONFIG) : imagesLocal
export const images = (config: Context['config']) =>
isS3configured(config) ? imagesS3(config) : imagesLocal

View File

@ -1,11 +1,7 @@
/* 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 { createGroupMutation } from '@graphql/queries/createGroupMutation'
import { currentUser } from '@graphql/queries/currentUser'
@ -20,26 +16,24 @@ import {
authenticatedValidateInviteCode,
unauthenticatedValidateInviteCode,
} from '@graphql/queries/validateInviteCode'
import createServer, { getContext } from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup, TEST_CONFIG } from '@root/test/helpers'
import type { Context } from '@src/context'
const database = databaseContext()
let server: ApolloServer
let authenticatedUser
let query, mutate
let authenticatedUser: Context['user']
const context = () => ({ authenticatedUser })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
beforeAll(async () => {
await cleanDatabase()
// 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
const createTestClientResult = createTestClient(server)
query = createTestClientResult.query
mutate = createTestClientResult.mutate
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(() => {
@ -479,7 +473,7 @@ describe('generatePersonalInviteCode', () => {
it('throws an error when the max amount of invite links was reached', async () => {
let lastCode
for (let i = 0; i < CONFIG.INVITE_CODES_PERSONAL_PER_USER; i++) {
for (let i = 0; i < TEST_CONFIG.INVITE_CODES_PERSONAL_PER_USER; i++) {
lastCode = await mutate({ mutation: generatePersonalInviteCode })
expect(lastCode).toMatchObject({
errors: undefined,
@ -740,7 +734,7 @@ describe('generateGroupInviteCode', () => {
it('throws an error when the max amount of invite links was reached', async () => {
let lastCode
for (let i = 0; i < CONFIG.INVITE_CODES_GROUP_PER_USER; i++) {
for (let i = 0; i < TEST_CONFIG.INVITE_CODES_GROUP_PER_USER; i++) {
lastCode = await mutate({
mutation: generateGroupInviteCode,
variables: { groupId: 'public-group' },

View File

@ -1,10 +1,8 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-return */
import CONFIG from '@config/index'
import registrationConstants from '@constants/registrationBranded'
// eslint-disable-next-line import/no-cycle
import { Context } from '@src/server'
import { Context } from '@src/context'
import Resolver from './helpers/Resolver'
@ -53,6 +51,9 @@ export const validateInviteCode = async (context: Context, inviteCode) => {
}
export const redeemInviteCode = async (context: Context, code, newUser = false) => {
if (!context.user) {
throw new Error('Missing authenticated user.')
}
const result = (
await context.database.query({
query: `
@ -159,7 +160,9 @@ export default {
})
).records[0].get('count')
if (parseInt(userInviteCodeAmount as string) >= CONFIG.INVITE_CODES_PERSONAL_PER_USER) {
if (
parseInt(userInviteCodeAmount as string) >= context.config.INVITE_CODES_PERSONAL_PER_USER
) {
throw new Error('You have reached the maximum of Invite Codes you can generate')
}
@ -198,7 +201,7 @@ export default {
})
).records[0].get('count')
if (parseInt(userInviteCodeAmount as string) >= CONFIG.INVITE_CODES_GROUP_PER_USER) {
if (parseInt(userInviteCodeAmount as string) >= context.config.INVITE_CODES_GROUP_PER_USER) {
throw new Error(
'You have reached the maximum of Invite Codes you can generate for this group',
)

View File

@ -1,10 +1,11 @@
/* 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 @typescript-eslint/no-unsafe-argument */
import { UserInputError } from 'apollo-server'
import type { Context } from '@src/context'
import Resolver from './helpers/Resolver'
import { queryLocations } from './users/location'
@ -23,7 +24,7 @@ export default {
'nameRU',
],
}),
distanceToMe: async (parent, _params, context, _resolveInfo) => {
distanceToMe: async (parent, _params, context: Context, _resolveInfo) => {
if (!parent.id) {
throw new Error('Can not identify selected Location!')
}
@ -53,9 +54,9 @@ export default {
},
},
Query: {
queryLocations: async (_object, args, _context, _resolveInfo) => {
queryLocations: async (_object, args, context: Context, _resolveInfo) => {
try {
return queryLocations(args)
return queryLocations(args, context)
} catch (e) {
throw new UserInputError(e.message)
}

View File

@ -5,11 +5,8 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { Readable } from 'node:stream'
import { ApolloServer } from 'apollo-server-express'
import { createTestClient } from 'apollo-server-testing'
import { Upload } from 'graphql-upload/public/index'
import databaseContext from '@context/database'
import pubsubContext from '@context/pubsub'
import Factory, { cleanDatabase } from '@db/factories'
import { CreateMessage } from '@graphql/queries/CreateMessage'
@ -17,29 +14,28 @@ import { createRoomMutation } from '@graphql/queries/createRoomMutation'
import { MarkMessagesAsSeen } from '@graphql/queries/MarkMessagesAsSeen'
import { Message } from '@graphql/queries/Message'
import { roomQuery } from '@graphql/queries/roomQuery'
import createServer, { getContext } from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
let query
let mutate
let authenticatedUser
let authenticatedUser: Context['user']
const context = () => ({ authenticatedUser, pubsub })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
let chattingUser, otherChattingUser, notChattingUser
const database = databaseContext()
const pubsub = pubsubContext()
const pubsubSpy = jest.spyOn(pubsub, 'publish')
let server: ApolloServer
beforeAll(async () => {
await cleanDatabase()
// 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
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
beforeEach(async () => {
@ -79,6 +75,10 @@ describe('Message', () => {
})
describe('unauthenticated', () => {
beforeAll(() => {
authenticatedUser = null
})
it('throws authorization error', async () => {
await expect(
mutate({
@ -128,7 +128,7 @@ describe('Message', () => {
userId: 'other-chatting-user',
},
})
roomId = room.data.CreateRoom.id
roomId = (room.data as any).CreateRoom.id // eslint-disable-line @typescript-eslint/no-explicit-any
})
describe('user chats in room', () => {
@ -180,7 +180,7 @@ describe('Message', () => {
lastMessageAt: expect.any(String),
unreadCount: 0,
lastMessage: expect.objectContaining({
_id: result.data.Room[0].lastMessage.id,
_id: result.data?.Room[0].lastMessage.id,
id: expect.any(String),
content: 'Some nice message to other chatting user',
senderId: 'chatting-user',
@ -410,7 +410,7 @@ describe('Message', () => {
userId: 'other-chatting-user',
},
})
roomId = room.data.CreateRoom.id
roomId = (room.data as any).CreateRoom.id // eslint-disable-line @typescript-eslint/no-explicit-any
await mutate({
mutation: CreateMessage,
@ -434,7 +434,7 @@ describe('Message', () => {
Message: [
{
id: expect.any(String),
_id: result.data.Message[0].id,
_id: result.data?.Message[0].id,
indexId: 0,
content: 'Some nice message to other chatting user',
senderId: 'chatting-user',
@ -642,7 +642,7 @@ describe('Message', () => {
userId: 'other-chatting-user',
},
})
roomId = room.data.CreateRoom.id
roomId = (room.data as any).CreateRoom.id // eslint-disable-line @typescript-eslint/no-explicit-any
await mutate({
mutation: CreateMessage,
variables: {
@ -673,7 +673,7 @@ describe('Message', () => {
roomId,
},
})
msgs.data.Message.forEach((m) => messageIds.push(m.id))
msgs.data?.Message.forEach((m) => messageIds.push(m.id))
})
it('returns true', async () => {

View File

@ -3,42 +3,40 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag'
import Factory, { cleanDatabase } from '@db/factories'
import { getDriver } from '@db/neo4j'
import { markAllAsReadMutation } from '@graphql/queries/markAllAsReadMutation'
import { markAsReadMutation } from '@graphql/queries/markAsReadMutation'
import { notificationQuery } from '@graphql/queries/notificationQuery'
import createServer from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
const driver = getDriver()
let authenticatedUser
let user
let author
let variables
let query
let mutate
let authenticatedUser: Context['user']
const context = () => ({ authenticatedUser })
let query: ApolloTestSetup['query']
let mutate: ApolloTestSetup['mutate']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
beforeAll(async () => {
await cleanDatabase()
const { server } = createServer({
context: () => {
return {
driver,
user: authenticatedUser,
}
},
})
query = createTestClient(server).query
mutate = createTestClient(server).mutate
const apolloSetup = createApolloTestSetup({ context })
query = apolloSetup.query
mutate = apolloSetup.mutate
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {
await cleanDatabase()
await driver.close()
void server.stop()
void database.driver.close()
database.neode.close()
})
beforeEach(async () => {
@ -157,7 +155,7 @@ describe('given some notifications', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await query({ query: notificationQuery() })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -241,7 +239,7 @@ describe('given some notifications', () => {
variables: { ...variables, read: false },
})
await expect(response).toMatchObject(expected)
await expect(response.data.notifications).toHaveLength(2) // double-check
await expect(response.data?.notifications).toHaveLength(2) // double-check
})
describe('if a resource gets deleted', () => {
@ -288,7 +286,7 @@ describe('given some notifications', () => {
mutation: markAsReadMutation(),
variables: { ...variables, id: 'p1' },
})
expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!')
expect(result.errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -307,7 +305,7 @@ describe('given some notifications', () => {
it('returns null', async () => {
const response = await mutate({ mutation: markAsReadMutation(), variables })
expect(response.data.markAsRead).toEqual(null)
expect(response.data?.markAsRead).toEqual(null)
expect(response.errors).toBeUndefined()
})
})
@ -344,7 +342,7 @@ describe('given some notifications', () => {
})
it('returns null', async () => {
const response = await mutate({ mutation: markAsReadMutation(), variables })
expect(response.data.markAsRead).toEqual(null)
expect(response.data?.markAsRead).toEqual(null)
expect(response.errors).toBeUndefined()
})
})
@ -382,7 +380,7 @@ describe('given some notifications', () => {
const result = await mutate({
mutation: markAllAsReadMutation(),
})
expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!')
expect(result.errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -400,7 +398,7 @@ describe('given some notifications', () => {
it('returns all as read', async () => {
const response = await mutate({ mutation: markAllAsReadMutation(), variables })
expect(response.data.markAllAsRead).toEqual(
expect(response.data?.markAllAsRead).toEqual(
expect.arrayContaining([
{
createdAt: '2019-08-30T19:33:48.651Z',

View File

@ -1,23 +1,23 @@
/* 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 { createPostMutation } from '@graphql/queries/createPostMutation'
import createServer, { getContext } from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
CONFIG.CATEGORIES_ACTIVE = false
let query
let mutate
let authenticatedUser
let user
let otherUser
let authenticatedUser: Context['user']
const config = { CATEGORIES_ACTIVE: true }
const context = () => ({ authenticatedUser, config })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
const createCommentMutation = gql`
mutation ($id: ID, $postId: ID!, $content: String!) {
@ -38,20 +38,13 @@ const postQuery = gql`
}
`
const database = databaseContext()
let server: ApolloServer
beforeAll(async () => {
await cleanDatabase()
// 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
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {

View File

@ -1,51 +1,45 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag'
import registrationConstants from '@constants/registrationBranded'
import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import createServer from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import createPasswordReset from './helpers/createPasswordReset'
const neode = getNeode()
const driver = getDriver()
let mutate
let authenticatedUser
let variables
let mutate: ApolloTestSetup['mutate']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
const getAllPasswordResets = async () => {
const passwordResetQuery = await neode.cypher(
const passwordResetQuery = await database.neode.cypher(
'MATCH (passwordReset:PasswordReset) RETURN passwordReset',
{},
)
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
const resets = passwordResetQuery.records.map((record) => record.get('passwordReset'))
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return resets
}
beforeAll(async () => {
await cleanDatabase()
const { server } = createServer({
context: () => {
return {
driver,
neode,
user: authenticatedUser,
}
},
})
mutate = createTestClient(server).mutate
const apolloSetup = createApolloTestSetup()
mutate = apolloSetup.mutate
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {
await cleanDatabase()
await driver.close()
void server.stop()
void database.driver.close()
database.neode.close()
})
beforeEach(() => {
@ -129,7 +123,7 @@ describe('resetPassword', () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const setup = async (options: any = {}) => {
const { email = 'user@example.org', issuedAt = new Date(), nonce = '12345' } = options
await createPasswordReset({ driver, email, issuedAt, nonce })
await createPasswordReset({ driver: database.driver, email, issuedAt, nonce })
}
const mutation = gql`

View File

@ -8,13 +8,15 @@ import bcrypt from 'bcryptjs'
import { v4 as uuid } from 'uuid'
import registrationConstants from '@constants/registrationBranded'
import type { Context } from '@src/context'
import createPasswordReset from './helpers/createPasswordReset'
import normalizeEmail from './helpers/normalizeEmail'
export default {
Mutation: {
requestPasswordReset: async (_parent, { email }, { driver }) => {
requestPasswordReset: async (_parent, { email }, context: Context) => {
const { driver } = context
email = normalizeEmail(email)
// TODO: why this is generated differntly from 'backend/src/schema/resolvers/helpers/generateNonce.js'?
const nonce = uuid().substring(0, registrationConstants.NONCE_LENGTH)

View File

@ -2,12 +2,8 @@
/* 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 Image from '@db/models/Image'
import { createGroupMutation } from '@graphql/queries/createGroupMutation'
@ -15,30 +11,32 @@ import { createPostMutation } from '@graphql/queries/createPostMutation'
import { Post } from '@graphql/queries/Post'
import { pushPost } from '@graphql/queries/pushPost'
import { unpushPost } from '@graphql/queries/unpushPost'
import createServer, { getContext } from '@src/server'
CONFIG.CATEGORIES_ACTIVE = true
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
let user
const database = databaseContext()
let authenticatedUser: Context['user']
const context = () => ({ authenticatedUser, config })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
let server: ApolloServer
let authenticatedUser
let query, mutate
const defaultConfig = {
CATEGORIES_ACTIVE: true,
// MAPBOX_TOKEN: CONFIG.MAPBOX_TOKEN,
}
let config: Partial<Context['config']>
beforeAll(async () => {
await cleanDatabase()
// 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
const createTestClientResult = createTestClient(server)
mutate = createTestClientResult.mutate
query = createTestClientResult.query
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(() => {
@ -51,6 +49,7 @@ const categoryIds = ['cat9', 'cat4', 'cat15']
let variables
beforeEach(async () => {
config = { ...defaultConfig }
variables = {}
user = await Factory.build(
'user',
@ -271,7 +270,7 @@ describe('CreatePost', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({ mutation: createPostMutation(), variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -708,7 +707,7 @@ describe('UpdatePost', () => {
categoryIds,
},
})
newlyCreatedPost = data.CreatePost
newlyCreatedPost = (data as any).CreatePost // eslint-disable-line @typescript-eslint/no-explicit-any
variables = {
id: newlyCreatedPost.id,
title: 'New title',
@ -733,7 +732,7 @@ describe('UpdatePost', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({ mutation: updatePostMutation, variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -771,7 +770,7 @@ describe('UpdatePost', () => {
it('updates the updatedAt attribute', async () => {
const {
data: { UpdatePost },
} = await mutate({ mutation: updatePostMutation, variables })
} = (await mutate({ mutation: updatePostMutation, variables })) as any // eslint-disable-line @typescript-eslint/no-explicit-any
expect(UpdatePost.updatedAt).toBeTruthy()
expect(Date.parse(UpdatePost.updatedAt)).toEqual(expect.any(Number))
expect(newlyCreatedPost.updatedAt).not.toEqual(UpdatePost.updatedAt)
@ -1377,7 +1376,8 @@ describe('pin posts', () => {
describe('MAX_PINNED_POSTS is 0', () => {
beforeEach(async () => {
CONFIG.MAX_PINNED_POSTS = 0
config = { ...defaultConfig, MAX_PINNED_POSTS: 0 }
await Factory.build(
'post',
{
@ -1400,7 +1400,7 @@ describe('pin posts', () => {
describe('MAX_PINNED_POSTS is 1', () => {
beforeEach(() => {
CONFIG.MAX_PINNED_POSTS = 1
config = { ...defaultConfig, MAX_PINNED_POSTS: 1 }
})
describe('are allowed to pin posts', () => {
@ -1752,7 +1752,8 @@ describe('pin posts', () => {
const postsPinnedCountsQuery = `query { PostsPinnedCounts { maxPinnedPosts, currentlyPinnedPosts } }`
beforeEach(async () => {
CONFIG.MAX_PINNED_POSTS = 3
config = { ...defaultConfig, MAX_PINNED_POSTS: 3 }
await Factory.build(
'post',
{
@ -2127,7 +2128,7 @@ describe('DeletePost', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({ mutation: deletePostMutation, variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -2138,7 +2139,7 @@ describe('DeletePost', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({ mutation: deletePostMutation, variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -2280,7 +2281,7 @@ describe('emotions', () => {
variables,
})
expect(addPostEmotions.errors[0]).toHaveProperty('message', 'Not Authorized!')
expect(addPostEmotions.errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -2401,7 +2402,7 @@ describe('emotions', () => {
mutation: removePostEmotionsMutation,
variables: removePostEmotionsVariables,
})
expect(removePostEmotions.errors[0]).toHaveProperty('message', 'Not Authorized!')
expect(removePostEmotions.errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})

View File

@ -9,8 +9,7 @@ import { isEmpty } from 'lodash'
import { neo4jgraphql } from 'neo4j-graphql-js'
import { v4 as uuid } from 'uuid'
import CONFIG from '@config/index'
import { Context } from '@src/server'
import { Context } from '@src/context'
import { validateEventParams } from './helpers/events'
import { filterForMutedUsers } from './helpers/filterForMutedUsers'
@ -41,7 +40,7 @@ const filterEventDates = (params) => {
export default {
Query: {
Post: async (object, params, context, resolveInfo) => {
Post: async (object, params, context: Context, resolveInfo) => {
params = await filterPostsOfMyGroups(params, context)
params = await filterInvisiblePosts(params, context)
params = await filterForMutedUsers(params, context)
@ -77,10 +76,13 @@ export default {
session.close()
}
},
PostsEmotionsByCurrentUser: async (_object, params, context, _resolveInfo) => {
PostsEmotionsByCurrentUser: async (_object, params, context: Context, _resolveInfo) => {
const { postId } = params
const session = context.driver.session()
const readTxResultPromise = session.readTransaction(async (transaction) => {
if (!context.user) {
throw new Error('Missing authenticated user.')
}
const emotionsTransactionResponse = await transaction.run(
`
MATCH (user:User {id: $userId})-[emoted:EMOTED]->(post:Post {id: $postId})
@ -94,23 +96,29 @@ export default {
const [emotions] = await readTxResultPromise
return emotions
} finally {
session.close()
await session.close()
}
},
PostsPinnedCounts: async (_object, params, context: Context, _resolveInfo) => {
const { config } = context
const [postsPinnedCount] = (
await context.database.query({
query: 'MATCH (p:Post { pinned: true }) RETURN COUNT (p) AS count',
})
).records.map((r) => Number(r.get('count').toString()))
return {
maxPinnedPosts: CONFIG.MAX_PINNED_POSTS,
maxPinnedPosts: config.MAX_PINNED_POSTS,
currentlyPinnedPosts: postsPinnedCount,
}
},
},
Mutation: {
CreatePost: async (_parent, params, context, _resolveInfo) => {
CreatePost: async (_parent, params, context: Context, _resolveInfo) => {
const { user } = context
if (!user) {
throw new Error('Missing authenticated user.')
}
const { config } = context
const { categoryIds, groupId } = params
const { image: imageInput } = params
@ -146,7 +154,7 @@ export default {
)`
}
const categoriesCypher =
CONFIG.CATEGORIES_ACTIVE && categoryIds
config.CATEGORIES_ACTIVE && categoryIds
? `WITH post
UNWIND $categoryIds AS categoryId
MATCH (category:Category {id: categoryId})
@ -173,18 +181,18 @@ export default {
${groupCypher}
RETURN post {.*, postType: [l IN labels(post) WHERE NOT l = 'Post'] }
`,
{ userId: context.user.id, categoryIds, groupId, params },
{ userId: user.id, categoryIds, groupId, params },
)
const [post] = createPostTransactionResponse.records.map((record) => record.get('post'))
if (imageInput) {
await images.mergeImage(post, 'HERO_IMAGE', imageInput, { transaction })
await images(context.config).mergeImage(post, 'HERO_IMAGE', imageInput, { transaction })
}
return post
})
try {
const post = await writeTxResultPromise
if (locationName) {
await createOrUpdateLocations('Post', post.id, locationName, session)
await createOrUpdateLocations('Post', post.id, locationName, session, context)
}
return post
} catch (e) {
@ -192,10 +200,11 @@ export default {
throw new UserInputError('Post with this slug already exists!')
throw new Error(e)
} finally {
session.close()
await session.close()
}
},
UpdatePost: async (_parent, params, context, _resolveInfo) => {
UpdatePost: async (_parent, params, context: Context, _resolveInfo) => {
const { config } = context
const { categoryIds } = params
const { image: imageInput } = params
@ -211,7 +220,7 @@ export default {
WITH post
`
if (CONFIG.CATEGORIES_ACTIVE && categoryIds && categoryIds.length) {
if (config.CATEGORIES_ACTIVE && categoryIds && categoryIds.length) {
const cypherDeletePreviousRelations = `
MATCH (post:Post { id: $params.id })-[previousRelations:CATEGORIZED]->(category:Category)
DELETE previousRelations
@ -248,20 +257,20 @@ export default {
updatePostVariables,
)
const [post] = updatePostTransactionResponse.records.map((record) => record.get('post'))
await images.mergeImage(post, 'HERO_IMAGE', imageInput, { transaction })
await images(context.config).mergeImage(post, 'HERO_IMAGE', imageInput, { transaction })
return post
})
const post = await writeTxResultPromise
if (locationName) {
await createOrUpdateLocations('Post', post.id, locationName, session)
await createOrUpdateLocations('Post', post.id, locationName, session, context)
}
return post
} finally {
session.close()
await session.close()
}
},
DeletePost: async (_object, args, context, _resolveInfo) => {
DeletePost: async (_object, args, context: Context, _resolveInfo) => {
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
const deletePostTransactionResponse = await transaction.run(
@ -278,17 +287,17 @@ export default {
{ postId: args.id },
)
const [post] = deletePostTransactionResponse.records.map((record) => record.get('post'))
await images.deleteImage(post, 'HERO_IMAGE', { transaction })
await images(context.config).deleteImage(post, 'HERO_IMAGE', { transaction })
return post
})
try {
const post = await writeTxResultPromise
return post
} finally {
session.close()
await session.close()
}
},
AddPostEmotions: async (_object, params, context, _resolveInfo) => {
AddPostEmotions: async (_object, params, context: Context, _resolveInfo) => {
const { to, data } = params
const { user } = context
const session = context.driver.session()
@ -312,7 +321,7 @@ export default {
const [emoted] = await writeTxResultPromise
return emoted
} finally {
session.close()
await session.close()
}
},
RemovePostEmotions: async (_object, params, context, _resolveInfo) => {
@ -344,7 +353,11 @@ export default {
}
},
pinPost: async (_parent, params, context: Context, _resolveInfo) => {
if (CONFIG.MAX_PINNED_POSTS === 0) throw new Error('Pinned posts are not allowed!')
if (!context.user) {
throw new Error('Missing authenticated user.')
}
const { config } = context
if (config.MAX_PINNED_POSTS === 0) throw new Error('Pinned posts are not allowed!')
let pinnedPostWithNestedAttributes
const { driver, user } = context
const session = driver.session()
@ -358,7 +371,7 @@ export default {
SET post.pinned = true
RETURN post, pinned.createdAt as pinnedAt`
if (CONFIG.MAX_PINNED_POSTS === 1) {
if (config.MAX_PINNED_POSTS === 1) {
let writeTxResultPromise = session.writeTransaction(async (transaction) => {
const deletePreviousRelationsResponse = await transaction.run(
`
@ -403,7 +416,7 @@ export default {
query: `MATCH (:User)-[:PINNED]->(post:Post { pinned: true }) RETURN COUNT(post) AS count`,
})
).records.map((r) => Number(r.get('count').toString()))
if (currentPinnedPostCount >= CONFIG.MAX_PINNED_POSTS) {
if (currentPinnedPostCount >= config.MAX_PINNED_POSTS) {
throw new Error('Max number of pinned posts is reached!')
}
const [pinPostResult] = (

View File

@ -2,11 +2,6 @@
/* 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 { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation'
import { createCommentMutation } from '@graphql/queries/createCommentMutation'
@ -18,9 +13,9 @@ 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, { getContext } from '@src/server'
CONFIG.CATEGORIES_ACTIVE = false
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
jest.mock('@constants/groups', () => {
return {
@ -29,30 +24,28 @@ jest.mock('@constants/groups', () => {
}
})
let query
let mutate
let anyUser
let allGroupsUser
let pendingUser
let publicUser
let closedUser
let hiddenUser
let authenticatedUser
let newUser
let authenticatedUser: Context['user']
const config = { CATEGORIES_ACTIVE: false }
const context = () => ({ authenticatedUser, config })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
const database = databaseContext()
let server: ApolloServer
beforeAll(async () => {
await cleanDatabase()
// 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
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {
@ -545,7 +538,7 @@ describe('Posts in Groups', () => {
describe('visibility of posts', () => {
describe('query post by ID', () => {
describe('without authentication', () => {
beforeAll(async () => {
beforeEach(() => {
authenticatedUser = null
})
@ -608,7 +601,7 @@ describe('Posts in Groups', () => {
termsAndConditionsAgreedVersion: '0.0.1',
},
})
newUser = result.data.SignupVerification
newUser = result.data?.SignupVerification
authenticatedUser = newUser
})
@ -802,13 +795,13 @@ describe('Posts in Groups', () => {
describe('filter posts', () => {
describe('without authentication', () => {
beforeAll(async () => {
beforeEach(() => {
authenticatedUser = null
})
it('shows the post of the public group and the post without group', async () => {
const result = await query({ query: filterPosts(), variables: {} })
expect(result.data.Post).toHaveLength(2)
expect(result.data?.Post).toHaveLength(2)
expect(result).toMatchObject({
data: {
Post: expect.arrayContaining([
@ -838,7 +831,7 @@ describe('Posts in Groups', () => {
it('shows the post of the public group and the post without group', async () => {
const result = await query({ query: filterPosts(), variables: {} })
expect(result.data.Post).toHaveLength(2)
expect(result.data?.Post).toHaveLength(2)
expect(result).toMatchObject({
data: {
Post: expect.arrayContaining([
@ -868,7 +861,7 @@ describe('Posts in Groups', () => {
it('shows the post of the public group and the post without group', async () => {
const result = await query({ query: filterPosts(), variables: {} })
expect(result.data.Post).toHaveLength(2)
expect(result.data?.Post).toHaveLength(2)
expect(result).toMatchObject({
data: {
Post: expect.arrayContaining([
@ -898,7 +891,7 @@ describe('Posts in Groups', () => {
it('shows the post of the public group and the post without group', async () => {
const result = await query({ query: filterPosts(), variables: {} })
expect(result.data.Post).toHaveLength(2)
expect(result.data?.Post).toHaveLength(2)
expect(result).toMatchObject({
data: {
Post: expect.arrayContaining([
@ -928,7 +921,7 @@ describe('Posts in Groups', () => {
it('shows all posts', async () => {
const result = await query({ query: filterPosts(), variables: {} })
expect(result.data.Post).toHaveLength(4)
expect(result.data?.Post).toHaveLength(4)
expect(result).toMatchObject({
data: {
Post: expect.arrayContaining([
@ -966,13 +959,13 @@ describe('Posts in Groups', () => {
describe('profile page posts', () => {
describe('without authentication', () => {
beforeAll(async () => {
beforeEach(() => {
authenticatedUser = null
})
it('shows the post of the public group and the post without group', async () => {
const result = await query({ query: profilePagePosts(), variables: {} })
expect(result.data.profilePagePosts).toHaveLength(2)
expect(result.data?.profilePagePosts).toHaveLength(2)
expect(result).toMatchObject({
data: {
profilePagePosts: expect.arrayContaining([
@ -1000,7 +993,7 @@ describe('Posts in Groups', () => {
it('shows the post of the public group and the post without group', async () => {
const result = await query({ query: profilePagePosts(), variables: {} })
expect(result.data.profilePagePosts).toHaveLength(2)
expect(result.data?.profilePagePosts).toHaveLength(2)
expect(result).toMatchObject({
data: {
profilePagePosts: expect.arrayContaining([
@ -1028,7 +1021,7 @@ describe('Posts in Groups', () => {
it('shows the post of the public group and the post without group', async () => {
const result = await query({ query: profilePagePosts(), variables: {} })
expect(result.data.profilePagePosts).toHaveLength(2)
expect(result.data?.profilePagePosts).toHaveLength(2)
expect(result).toMatchObject({
data: {
profilePagePosts: expect.arrayContaining([
@ -1056,7 +1049,7 @@ describe('Posts in Groups', () => {
it('shows the post of the public group and the post without group', async () => {
const result = await query({ query: profilePagePosts(), variables: {} })
expect(result.data.profilePagePosts).toHaveLength(2)
expect(result.data?.profilePagePosts).toHaveLength(2)
expect(result).toMatchObject({
data: {
profilePagePosts: expect.arrayContaining([
@ -1084,7 +1077,7 @@ describe('Posts in Groups', () => {
it('shows all posts', async () => {
const result = await query({ query: profilePagePosts(), variables: {} })
expect(result.data.profilePagePosts).toHaveLength(4)
expect(result.data?.profilePagePosts).toHaveLength(4)
expect(result).toMatchObject({
data: {
profilePagePosts: expect.arrayContaining([
@ -1118,7 +1111,7 @@ describe('Posts in Groups', () => {
describe('searchPosts', () => {
describe('without authentication', () => {
beforeAll(async () => {
beforeEach(() => {
authenticatedUser = null
})
@ -1131,7 +1124,7 @@ describe('Posts in Groups', () => {
firstPosts: 25,
},
})
expect(result.data.searchPosts.posts).toHaveLength(0)
expect(result.data?.searchPosts.posts).toHaveLength(0)
expect(result).toMatchObject({
data: {
searchPosts: {
@ -1157,7 +1150,7 @@ describe('Posts in Groups', () => {
firstPosts: 25,
},
})
expect(result.data.searchPosts.posts).toHaveLength(2)
expect(result.data?.searchPosts.posts).toHaveLength(2)
expect(result).toMatchObject({
data: {
searchPosts: {
@ -1194,7 +1187,7 @@ describe('Posts in Groups', () => {
firstPosts: 25,
},
})
expect(result.data.searchPosts.posts).toHaveLength(2)
expect(result.data?.searchPosts.posts).toHaveLength(2)
expect(result).toMatchObject({
data: {
searchPosts: {
@ -1231,7 +1224,7 @@ describe('Posts in Groups', () => {
firstPosts: 25,
},
})
expect(result.data.searchPosts.posts).toHaveLength(2)
expect(result.data?.searchPosts.posts).toHaveLength(2)
expect(result).toMatchObject({
data: {
searchPosts: {
@ -1268,7 +1261,7 @@ describe('Posts in Groups', () => {
firstPosts: 25,
},
})
expect(result.data.searchPosts.posts).toHaveLength(4)
expect(result.data?.searchPosts.posts).toHaveLength(4)
expect(result).toMatchObject({
data: {
searchPosts: {
@ -1321,7 +1314,7 @@ describe('Posts in Groups', () => {
it('shows the posts of the closed group', async () => {
const result = await query({ query: filterPosts(), variables: {} })
expect(result.data.Post).toHaveLength(3)
expect(result.data?.Post).toHaveLength(3)
expect(result).toMatchObject({
data: {
Post: expect.arrayContaining([
@ -1366,7 +1359,7 @@ describe('Posts in Groups', () => {
it('shows all the posts', async () => {
const result = await query({ query: filterPosts(), variables: {} })
expect(result.data.Post).toHaveLength(4)
expect(result.data?.Post).toHaveLength(4)
expect(result).toMatchObject({
data: {
Post: expect.arrayContaining([
@ -1419,7 +1412,7 @@ describe('Posts in Groups', () => {
it('does not show the posts of the closed group anymore', async () => {
const result = await query({ query: filterPosts(), variables: {} })
expect(result.data.Post).toHaveLength(3)
expect(result.data?.Post).toHaveLength(3)
expect(result).toMatchObject({
data: {
Post: expect.arrayContaining([
@ -1464,7 +1457,7 @@ describe('Posts in Groups', () => {
it('shows only the public posts', async () => {
const result = await query({ query: filterPosts(), variables: {} })
expect(result.data.Post).toHaveLength(2)
expect(result.data?.Post).toHaveLength(2)
expect(result).toMatchObject({
data: {
Post: expect.arrayContaining([
@ -1503,7 +1496,7 @@ describe('Posts in Groups', () => {
it('still shows the posts of the public group', async () => {
const result = await query({ query: filterPosts(), variables: {} })
expect(result.data.Post).toHaveLength(4)
expect(result.data?.Post).toHaveLength(4)
expect(result).toMatchObject({
data: {
Post: expect.arrayContaining([
@ -1552,7 +1545,7 @@ describe('Posts in Groups', () => {
it('stil shows the posts of the closed group', async () => {
const result = await query({ query: filterPosts(), variables: {} })
expect(result.data.Post).toHaveLength(4)
expect(result.data?.Post).toHaveLength(4)
expect(result).toMatchObject({
data: {
Post: expect.arrayContaining([
@ -1601,7 +1594,7 @@ describe('Posts in Groups', () => {
it('still shows the post of the hidden group', async () => {
const result = await query({ query: filterPosts(), variables: {} })
expect(result.data.Post).toHaveLength(4)
expect(result.data?.Post).toHaveLength(4)
expect(result).toMatchObject({
data: {
Post: expect.arrayContaining([
@ -1654,7 +1647,7 @@ describe('Posts in Groups', () => {
it('shows the posts of the closed group', async () => {
const result = await query({ query: filterPosts(), variables: {} })
expect(result.data.Post).toHaveLength(4)
expect(result.data?.Post).toHaveLength(4)
expect(result).toMatchObject({
data: {
Post: expect.arrayContaining([
@ -1705,7 +1698,7 @@ describe('Posts in Groups', () => {
it('shows all posts', async () => {
const result = await query({ query: filterPosts(), variables: {} })
expect(result.data.Post).toHaveLength(4)
expect(result.data?.Post).toHaveLength(4)
expect(result).toMatchObject({
data: {
Post: expect.arrayContaining([
@ -1752,7 +1745,7 @@ describe('Posts in Groups', () => {
query: filterPosts(),
variables: { filter: { postsInMyGroups: true } },
})
expect(result.data.Post).toHaveLength(0)
expect(result.data?.Post).toHaveLength(0)
expect(result).toMatchObject({
data: {
Post: [],
@ -1773,7 +1766,7 @@ describe('Posts in Groups', () => {
query: filterPosts(),
variables: { filter: { postsInMyGroups: true } },
})
expect(result.data.Post).toHaveLength(2)
expect(result.data?.Post).toHaveLength(2)
expect(result).toMatchObject({
data: {
Post: expect.arrayContaining([

View File

@ -1,36 +1,30 @@
/* 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 EmailAddress from '@db/models/EmailAddress'
import User from '@db/models/User'
import createServer, { getContext } from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
let variables
const database = databaseContext()
let server: ApolloServer
let authenticatedUser
let mutate
let authenticatedUser: Context['user']
const context = () => ({ authenticatedUser, config })
let mutate: ApolloTestSetup['mutate']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
let config: Partial<Context['config']> = {}
beforeAll(async () => {
await cleanDatabase()
// 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
const createTestClientResult = createTestClient(server)
mutate = createTestClientResult.mutate
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(() => {
@ -40,6 +34,7 @@ afterAll(() => {
})
beforeEach(() => {
config = {}
variables = {}
})
@ -62,11 +57,13 @@ describe('Signup', () => {
describe('unauthenticated', () => {
beforeEach(() => {
authenticatedUser = null
config = {
INVITE_REGISTRATION: false,
PUBLIC_REGISTRATION: false,
}
})
it('throws AuthorizationError', async () => {
CONFIG.INVITE_REGISTRATION = false
CONFIG.PUBLIC_REGISTRATION = false
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
})

View File

@ -7,7 +7,7 @@ import { UserInputError } from 'apollo-server'
import { hash } from 'bcryptjs'
import { getNeode } from '@db/neo4j'
import { Context } from '@src/server'
import { Context } from '@src/context'
import existingEmailAddress from './helpers/existingEmailAddress'
import generateNonce from './helpers/generateNonce'
@ -106,7 +106,7 @@ export default {
await redeemInviteCode(context, inviteCode, true)
}
await createOrUpdateLocations('User', user.id, locationName, session)
await createOrUpdateLocations('User', user.id, locationName, session, context)
return user
} catch (e) {
if (e.code === 'Neo.ClientError.Schema.ConstraintValidationFailed')

View File

@ -2,30 +2,21 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* 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 Factory, { cleanDatabase } from '@db/factories'
import { statistics } from '@graphql/queries/statistics'
import createServer, { getContext } from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
const database = databaseContext()
let server: ApolloServer
let query, authenticatedUser
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
let query: ApolloTestSetup['query']
beforeAll(async () => {
await cleanDatabase()
// 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
const apolloSetup = createApolloTestSetup()
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {

View File

@ -2,7 +2,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/dot-notation */
import { Context } from '@src/server'
import { Context } from '@src/context'
export default {
Query: {

View File

@ -1,31 +1,32 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable promise/prefer-await-to-callbacks */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable jest/unbound-method */
import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag'
import jwt from 'jsonwebtoken'
import { verify } from 'jsonwebtoken'
import CONFIG from '@config/index'
import { categories } from '@constants/categories'
import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import { loginMutation } from '@graphql/queries/loginMutation'
import encode from '@jwt/encode'
import createServer, { context } from '@src/server'
import { decode } from '@jwt/decode'
import { encode } from '@jwt/encode'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup, TEST_CONFIG } from '@root/test/helpers'
const neode = getNeode()
const driver = getDriver()
let query, mutate, variables, req, user
const jwt = { verify }
let variables, req, user
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
const disable = async (id) => {
const moderator = await Factory.build('user', { id: 'u2', role: 'moderator' })
const user = await neode.find('User', id)
const user = await database.neode.find('User', id)
const reportAgainstUser = await Factory.build('report')
await Promise.all([
reportAgainstUser.relateTo(moderator, 'filed', {
@ -42,23 +43,34 @@ const disable = async (id) => {
])
}
const config = {
JWT_SECRET: 'I am the JWT secret',
JWT_EXPIRES: TEST_CONFIG.JWT_EXPIRES,
CLIENT_URI: TEST_CONFIG.CLIENT_URI,
GRAPHQL_URI: TEST_CONFIG.GRAPHQL_URI,
}
const context = { config }
beforeAll(async () => {
await cleanDatabase()
const { server } = createServer({
context: () => {
// One of the rare occasions where we test
// the actual `context` implementation here
return context({ req })
},
})
query = createTestClient(server).query
mutate = createTestClient(server).mutate
const context = async () => {
const authenticatedUser = await decode({ driver: database.driver, config })(
req.headers.authorization,
)
return { authenticatedUser, config }
}
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {
await cleanDatabase()
await driver.close()
void server.stop()
void database.driver.close()
database.neode.close()
})
beforeEach(() => {
@ -120,7 +132,7 @@ describe('currentUser', () => {
avatar,
},
)
const userBearerToken = encode({ id: 'u3' })
const userBearerToken = encode(context)({ id: 'u3' })
req = { headers: { authorization: `Bearer ${userBearerToken}` } }
})
@ -203,11 +215,11 @@ describe('currentUser', () => {
it('returns only the saved active categories', async () => {
const result = await query({ query: currentUserQuery, variables })
expect(result.data.currentUser.activeCategories).toHaveLength(4)
expect(result.data.currentUser.activeCategories).toContain('cat1')
expect(result.data.currentUser.activeCategories).toContain('cat3')
expect(result.data.currentUser.activeCategories).toContain('cat5')
expect(result.data.currentUser.activeCategories).toContain('cat7')
expect(result.data?.currentUser.activeCategories).toHaveLength(4)
expect(result.data?.currentUser.activeCategories).toContain('cat1')
expect(result.data?.currentUser.activeCategories).toContain('cat3')
expect(result.data?.currentUser.activeCategories).toContain('cat5')
expect(result.data?.currentUser.activeCategories).toContain('cat7')
})
})
})
@ -236,8 +248,8 @@ describe('login', () => {
it('responds with a JWT bearer token', async () => {
const {
data: { login: token },
} = await mutate({ mutation: loginMutation, variables })
jwt.verify(token, CONFIG.JWT_SECRET, (err, data) => {
} = (await mutate({ mutation: loginMutation, variables })) as any // eslint-disable-line @typescript-eslint/no-explicit-any
jwt.verify(token, config.JWT_SECRET, (err, data) => {
expect(data).toMatchObject({
id: 'acb2d923-f3af-479e-9f00-61b12e864666',
})
@ -274,7 +286,7 @@ describe('login', () => {
describe('normalization', () => {
describe('email address is a gmail address ', () => {
beforeEach(async () => {
const email = await neode.first(
const email = await database.neode.first(
'EmailAddress',
{ email: 'test@example.org' },
undefined,
@ -354,7 +366,7 @@ describe('change password', () => {
describe('authenticated', () => {
beforeEach(async () => {
await Factory.build('user', { id: 'u3' })
const userBearerToken = encode({ id: 'u3' })
const userBearerToken = encode(context)({ id: 'u3' })
req = { headers: { authorization: `Bearer ${userBearerToken}` } }
})
describe('old password === new password', () => {

View File

@ -9,7 +9,8 @@ import bcrypt from 'bcryptjs'
import { neo4jgraphql } from 'neo4j-graphql-js'
import { getNeode } from '@db/neo4j'
import encode from '@jwt/encode'
import { encode } from '@jwt/encode'
import type { Context } from '@src/context'
import normalizeEmail from './helpers/normalizeEmail'
@ -21,7 +22,8 @@ export default {
neo4jgraphql(object, { id: context.user.id }, context, resolveInfo),
},
Mutation: {
login: async (_, { email, password }, { driver }) => {
login: async (_, { email, password }, context: Context) => {
const { driver } = context
// if (user && user.id) {
// throw new Error('Already logged in.')
// }
@ -45,17 +47,21 @@ export default {
!currentUser.disabled
) {
delete currentUser.encryptedPassword
return encode(currentUser)
return encode(context)(currentUser)
} else if (currentUser?.disabled) {
throw new AuthenticationError('Your account has been disabled.')
} else {
throw new AuthenticationError('Incorrect email address or password.')
}
} finally {
session.close()
await session.close()
}
},
changePassword: async (_, { oldPassword, newPassword }, { user }) => {
changePassword: async (_, { oldPassword, newPassword }, context: Context) => {
if (!context.user) {
throw new Error('Missing authenticated user.')
}
const { user } = context
const currentUser = await neode.find('User', user.id)
const encryptedPassword = currentUser.get<string>('encryptedPassword')
@ -73,7 +79,7 @@ export default {
updatedAt: new Date().toISOString(),
})
return encode(await currentUser.toJson())
return encode(context)(await currentUser.toJson())
},
},
}

View File

@ -3,29 +3,34 @@
/* 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 { setTrophyBadgeSelected } from '@graphql/queries/setTrophyBadgeSelected'
import createServer, { getContext } from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
import type { DecodedUser } from '@src/jwt/decode'
// import CONFIG from '@src/config'
const categoryIds = ['cat9']
let user
let admin
let authenticatedUser
let query
let mutate
let variables
const pubsub = pubsubContext()
let authenticatedUser: Context['user']
const context = () => ({ authenticatedUser, pubsub })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
const deleteUserMutation = gql`
mutation ($id: ID!, $resource: [Deletable]) {
DeleteUser(id: $id, resource: $resource) {
@ -94,21 +99,13 @@ const resetTrophyBadgesSelected = gql`
}
`
const database = databaseContext()
let server: ApolloServer
beforeAll(async () => {
await cleanDatabase()
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
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {
@ -118,6 +115,10 @@ afterAll(async () => {
database.neode.close()
})
beforeEach(async () => {
authenticatedUser = null
})
// 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
afterEach(async () => {
await cleanDatabase()
@ -128,6 +129,11 @@ describe('User', () => {
let userQuery
beforeEach(async () => {
const user = await Factory.build('user', {
id: 'user',
role: 'user',
})
authenticatedUser = await user.toJson()
userQuery = gql`
query ($email: String) {
User(email: $email) {
@ -254,7 +260,7 @@ describe('UpdateUser', () => {
it('is not allowed to change other user accounts', async () => {
const { errors } = await mutate({ mutation: updateUserMutation, variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -326,7 +332,7 @@ describe('UpdateUser', () => {
termsAndConditionsAgreedVersion: 'invalid version format',
}
const { errors } = await mutate({ mutation: updateUserMutation, variables })
expect(errors[0]).toHaveProperty('message', 'Invalid version format!')
expect(errors?.[0]).toHaveProperty('message', 'Invalid version format!')
})
describe('supports updating location', () => {
@ -684,7 +690,10 @@ describe('emailNotificationSettings', () => {
it('returns the emailNotificationSettings', async () => {
authenticatedUser = await user.toJson()
await expect(
query({ query: emailNotificationSettingsQuery, variables: { id: authenticatedUser.id } }),
query({
query: emailNotificationSettingsQuery,
variables: { id: authenticatedUser?.id },
}),
).resolves.toEqual(
expect.objectContaining({
data: {
@ -778,7 +787,7 @@ describe('emailNotificationSettings', () => {
describe('as self', () => {
it('updates the emailNotificationSettings', async () => {
authenticatedUser = await user.toJson()
authenticatedUser = (await user.toJson()) as DecodedUser
await expect(
mutate({
mutation: emailNotificationSettingsMutation,
@ -876,7 +885,7 @@ describe('save category settings', () => {
describe('not authenticated', () => {
beforeEach(async () => {
authenticatedUser = undefined
authenticatedUser = null
})
it('throws an error', async () => {
@ -921,7 +930,7 @@ describe('save category settings', () => {
it('returns the active categories when user is queried', async () => {
await expect(
query({ query: userQuery, variables: { id: authenticatedUser.id } }),
query({ query: userQuery, variables: { id: authenticatedUser?.id } }),
).resolves.toEqual(
expect.objectContaining({
data: {
@ -963,7 +972,7 @@ describe('save category settings', () => {
it('returns the new active categories when user is queried', async () => {
await expect(
query({ query: userQuery, variables: { id: authenticatedUser.id } }),
query({ query: userQuery, variables: { id: authenticatedUser?.id } }),
).resolves.toEqual(
expect.objectContaining({
data: {
@ -1000,7 +1009,7 @@ describe('updateOnlineStatus', () => {
describe('not authenticated', () => {
beforeEach(async () => {
authenticatedUser = undefined
authenticatedUser = null
})
it('throws an error', async () => {
@ -1030,7 +1039,7 @@ describe('updateOnlineStatus', () => {
)
const cypher = 'MATCH (u:User {id: $id}) RETURN u'
const result = await database.neode.cypher(cypher, { id: authenticatedUser.id })
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',
@ -1056,7 +1065,7 @@ describe('updateOnlineStatus', () => {
)
const cypher = 'MATCH (u:User {id: $id}) RETURN u'
const result = await database.neode.cypher(cypher, { id: authenticatedUser.id })
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',
@ -1072,7 +1081,7 @@ describe('updateOnlineStatus', () => {
)
const cypher = 'MATCH (u:User {id: $id}) RETURN u'
const result = await database.neode.cypher(cypher, { id: authenticatedUser.id })
const result = await database.neode.cypher(cypher, { id: authenticatedUser?.id })
const dbUser = database.neode.hydrateFirst<typeof User>(
result,
'u',
@ -1091,7 +1100,7 @@ describe('updateOnlineStatus', () => {
}),
)
const result2 = await database.neode.cypher(cypher, { id: authenticatedUser.id })
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',
@ -1133,7 +1142,7 @@ describe('setTrophyBadgeSelected', () => {
describe('not authenticated', () => {
beforeEach(async () => {
authenticatedUser = undefined
authenticatedUser = null
})
it('throws an error', async () => {
@ -1515,8 +1524,8 @@ describe('resetTrophyBadgesSelected', () => {
})
describe('not authenticated', () => {
beforeEach(async () => {
authenticatedUser = undefined
beforeEach(() => {
authenticatedUser = null
})
it('throws an error', async () => {

View File

@ -10,7 +10,7 @@ import { neo4jgraphql } from 'neo4j-graphql-js'
import { TROPHY_BADGES_SELECTED_MAX } from '@constants/badges'
import { getNeode } from '@db/neo4j'
import { Context } from '@src/server'
import { Context } from '@src/context'
import { defaultTrophyBadge, defaultVerificationBadge } from './badges'
import normalizeEmail from './helpers/normalizeEmail'
@ -168,10 +168,10 @@ export default {
} catch (error) {
throw new UserInputError(error.message)
} finally {
session.close()
await session.close()
}
},
UpdateUser: async (_parent, params, context, _resolveInfo) => {
UpdateUser: async (_parent, params, context: Context, _resolveInfo) => {
const { avatar: avatarInput } = params
delete params.avatar
params.locationName = params.locationName === '' ? null : params.locationName
@ -210,22 +210,24 @@ export default {
)
const [user] = updateUserTransactionResponse.records.map((record) => record.get('user'))
if (avatarInput) {
await images.mergeImage(user, 'AVATAR_IMAGE', avatarInput, { transaction })
await images(context.config).mergeImage(user, 'AVATAR_IMAGE', avatarInput, {
transaction,
})
}
return user
})
try {
const user = await writeTxResultPromise
// TODO: put in a middleware, see "CreateGroup", "UpdateGroup"
await createOrUpdateLocations('User', params.id, params.locationName, session)
await createOrUpdateLocations('User', params.id, params.locationName, session, context)
return user
} catch (error) {
throw new UserInputError(error.message)
} finally {
session.close()
await session.close()
}
},
DeleteUser: async (_object, params, context, _resolveInfo) => {
DeleteUser: async (_object, params, context: Context, _resolveInfo) => {
const { resource, id: userId } = params
const session = context.driver.session()
@ -253,7 +255,9 @@ export default {
return Promise.all(
txResult.records
.map((record) => record.get('resource'))
.map((resource) => images.deleteImage(resource, 'HERO_IMAGE', { transaction })),
.map((resource) =>
images(context.config).deleteImage(resource, 'HERO_IMAGE', { transaction }),
),
)
}),
)
@ -281,14 +285,14 @@ export default {
{ userId },
)
const [user] = deleteUserTransactionResponse.records.map((record) => record.get('user'))
await images.deleteImage(user, 'AVATAR_IMAGE', { transaction })
await images(context.config).deleteImage(user, 'AVATAR_IMAGE', { transaction })
return user
})
try {
const user = await deleteUserTxResultPromise
return user
} finally {
session.close()
await session.close()
}
},
switchUserRole: async (_object, args, context, _resolveInfo) => {

View File

@ -1,16 +1,22 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag'
import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import createServer from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
const neode = getNeode()
const driver = getDriver()
let authenticatedUser, mutate, query, variables
let variables
let authenticatedUser: Context['user']
const context = () => ({
authenticatedUser,
})
let mutate: ApolloTestSetup['mutate']
let query: any // eslint-disable-line @typescript-eslint/no-explicit-any
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
const updateUserMutation = gql`
mutation ($id: ID!, $name: String!, $locationName: String) {
@ -78,23 +84,19 @@ const newlyCreatedNodesWithLocales = [
beforeAll(async () => {
await cleanDatabase()
const { server } = createServer({
context: () => {
return {
user: authenticatedUser,
neode,
driver,
}
},
const apolloSetup = createApolloTestSetup({
context,
})
mutate = createTestClient(server).mutate
query = createTestClient(server).query
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {
await cleanDatabase()
await driver.close()
afterAll(() => {
void server.stop()
void database.driver.close()
database.neode.close()
})
beforeEach(() => {
@ -110,9 +112,8 @@ afterEach(async () => {
describe('Location Service', () => {
// Authentication
// TODO: unify, externalize, simplify, wtf?
let user
beforeEach(async () => {
user = await Factory.build('user', {
const user = await Factory.build('user', {
id: 'location-user',
})
authenticatedUser = await user.toJson()
@ -195,9 +196,8 @@ describe('Location Service', () => {
describe('userMiddleware', () => {
describe('UpdateUser', () => {
let user
beforeEach(async () => {
user = await Factory.build('user', {
const user = await Factory.build('user', {
id: 'updating-user',
})
authenticatedUser = await user.toJson()
@ -211,7 +211,7 @@ describe('userMiddleware', () => {
locationName: 'Welzheim, Baden-Württemberg, Germany',
}
await mutate({ mutation: updateUserMutation, variables })
const locations = await neode.cypher(
const locations = await database.neode.cypher(
`MATCH (city:Location)-[:IS_IN]->(district:Location)-[:IS_IN]->(state:Location)-[:IS_IN]->(country:Location) return city {.*}, state {.*}, country {.*}`,
{},
)

View File

@ -9,7 +9,7 @@
/* eslint-disable n/no-unsupported-features/node-builtins */
import { UserInputError } from 'apollo-server'
import CONFIG from '@config/index'
import type { Context } from '@src/context'
const locales = ['en', 'de', 'fr', 'nl', 'it', 'es', 'pt', 'pl', 'ru']
@ -61,7 +61,13 @@ const createLocation = async (session, mapboxData) => {
})
}
export const createOrUpdateLocations = async (nodeLabel, nodeId, locationName, session) => {
export const createOrUpdateLocations = async (
nodeLabel,
nodeId,
locationName,
session,
context: Context,
) => {
if (locationName === undefined) return
let locationId
@ -72,7 +78,7 @@ export const createOrUpdateLocations = async (nodeLabel, nodeId, locationName, s
`https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(
locationName,
)}.json?access_token=${
CONFIG.MAPBOX_TOKEN
context.config.MAPBOX_TOKEN
}&types=region,place,country,address&language=${locales.join(',')}`,
{
signal: AbortSignal.timeout(REQUEST_TIMEOUT),
@ -156,10 +162,10 @@ export const createOrUpdateLocations = async (nodeLabel, nodeId, locationName, s
}
}
export const queryLocations = async ({ place, lang }) => {
export const queryLocations = async ({ place, lang }, context: Context) => {
try {
const res: any = await fetch(
`https://api.mapbox.com/geocoding/v5/mapbox.places/${place}.json?access_token=${CONFIG.MAPBOX_TOKEN}&types=region,place,country&language=${lang}`,
`https://api.mapbox.com/geocoding/v5/mapbox.places/${place}.json?access_token=${context.config.MAPBOX_TOKEN}&types=region,place,country&language=${lang}`,
{
signal: AbortSignal.timeout(REQUEST_TIMEOUT),
},

View File

@ -4,12 +4,20 @@
import Factory, { cleanDatabase } from '@db/factories'
import User from '@db/models/User'
import { getDriver, getNeode } from '@db/neo4j'
import { TEST_CONFIG } from '@root/test/helpers'
import decode from './decode'
import encode from './encode'
import { decode } from './decode'
import { encode } from './encode'
const driver = getDriver()
const neode = getNeode()
const config = {
JWT_SECRET: 'supersecret',
JWT_EXPIRES: TEST_CONFIG.JWT_EXPIRES,
CLIENT_URI: TEST_CONFIG.CLIENT_URI,
GRAPHQL_URI: TEST_CONFIG.GRAPHQL_URI,
}
const context = { driver, config }
beforeAll(async () => {
await cleanDatabase()
@ -26,9 +34,9 @@ afterEach(async () => {
})
describe('decode', () => {
let authorizationHeader
let authorizationHeader: string | undefined | null
const returnsNull = async () => {
await expect(decode(driver, authorizationHeader)).resolves.toBeNull()
await expect(decode(context)(authorizationHeader)).resolves.toBeNull()
}
describe('given `null` as JWT Bearer token', () => {
@ -57,7 +65,8 @@ describe('decode', () => {
describe('given valid JWT Bearer token', () => {
describe('and corresponding user in the database', () => {
let user, validAuthorizationHeader
let user
let validAuthorizationHeader: string
beforeEach(async () => {
user = await Factory.build(
'user',
@ -74,11 +83,11 @@ describe('decode', () => {
email: 'user@example.org',
},
)
validAuthorizationHeader = encode(await user.toJson())
validAuthorizationHeader = encode(context)(await user.toJson())
})
it('returns user object without email', async () => {
await expect(decode(driver, validAuthorizationHeader)).resolves.toMatchObject({
await expect(decode(context)(validAuthorizationHeader)).resolves.toMatchObject({
role: 'user',
name: 'Jenny Rostock',
id: 'u3',
@ -89,7 +98,7 @@ describe('decode', () => {
it('sets `lastActiveAt`', async () => {
let user = await neode.first<typeof User>('User', { id: 'u3' }, undefined)
await expect(user.toJson()).resolves.not.toHaveProperty('lastActiveAt')
await decode(driver, validAuthorizationHeader)
await decode(context)(validAuthorizationHeader)
user = await neode.first<typeof User>('User', { id: 'u3' }, undefined)
await expect(user.toJson()).resolves.toMatchObject({
lastActiveAt: expect.any(String),
@ -107,7 +116,7 @@ describe('decode', () => {
await expect(user.toJson()).resolves.toMatchObject({
lastActiveAt: '2019-10-03T23:33:08.598Z',
})
await decode(driver, validAuthorizationHeader)
await decode(context)(validAuthorizationHeader)
user = await neode.first<typeof User>('User', { id: 'u3' }, undefined)
await expect(user.toJson()).resolves.toMatchObject({
// should be a different time by now ;)

View File

@ -1,44 +1,56 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import jwt from 'jsonwebtoken'
import CONFIG from '@config/index'
import { verify } from 'jsonwebtoken'
export default async (driver, authorizationHeader) => {
if (!authorizationHeader) return null
const token = authorizationHeader.replace('Bearer ', '')
let id = null
try {
const decoded = await jwt.verify(token, CONFIG.JWT_SECRET)
id = decoded.sub
// eslint-disable-next-line no-catch-all/no-catch-all
} catch (err) {
return null
}
const session = driver.session()
import type CONFIG from '@src/config'
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
const updateUserLastActiveTransactionResponse = await transaction.run(
`
import type { JwtPayload } from 'jsonwebtoken'
import type { Driver } from 'neo4j-driver'
export interface DecodedUser {
id: string
slug: string
name: string
role: string
disabled: boolean
}
const jwt = { verify }
export const decode =
(context: { config: Pick<typeof CONFIG, 'JWT_SECRET'>; driver: Driver }) =>
async (authorizationHeader: string | undefined | null) => {
if (!authorizationHeader) return null
const token = authorizationHeader.replace('Bearer ', '')
let id: null | string = null
try {
const decoded = jwt.verify(token, context.config.JWT_SECRET) as JwtPayload
id = decoded.sub ?? null
// eslint-disable-next-line no-catch-all/no-catch-all
} catch (err) {
return null
}
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction<DecodedUser[]>(async (transaction) => {
const updateUserLastActiveTransactionResponse = await transaction.run(
`
MATCH (user:User {id: $id, deleted: false, disabled: false })
SET user.lastActiveAt = toString(datetime())
RETURN user {.id, .slug, .name, .role, .disabled, .actorId}
LIMIT 1
`,
{ id },
)
return updateUserLastActiveTransactionResponse.records.map((record) => record.get('user'))
})
try {
const [currentUser] = await writeTxResultPromise
if (!currentUser) return null
return {
token,
...currentUser,
{ id },
)
return updateUserLastActiveTransactionResponse.records.map((record) => record.get('user'))
})
try {
const [currentUser] = await writeTxResultPromise
if (!currentUser) return null
return {
token,
...currentUser,
}
} finally {
await session.close()
}
} finally {
session.close()
}
}

View File

@ -1,11 +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 jwt from 'jsonwebtoken'
import { verify } from 'jsonwebtoken'
import CONFIG from '@config/index'
import { TEST_CONFIG } from '@root/test/helpers'
import encode from './encode'
import { encode } from './encode'
const jwt = { verify }
const config = {
JWT_SECRET: 'supersecret',
JWT_EXPIRES: TEST_CONFIG.JWT_EXPIRES,
CLIENT_URI: TEST_CONFIG.CLIENT_URI,
GRAPHQL_URI: TEST_CONFIG.GRAPHQL_URI,
}
const context = { config }
describe('encode', () => {
let payload
@ -18,9 +25,9 @@ describe('encode', () => {
})
it('encodes a valided JWT bearer token', () => {
const token = encode(payload)
const token = encode(context)(payload)
expect(token.split('.')).toHaveLength(3)
const decoded = jwt.verify(token, CONFIG.JWT_SECRET)
const decoded = jwt.verify(token, context.config.JWT_SECRET)
expect(decoded).toEqual({
name: 'Some body',
slug: 'some-body',
@ -43,7 +50,7 @@ describe('encode', () => {
})
it('does not encode sensitive data', () => {
const token = encode(payload)
const token = encode(context)(payload)
expect(payload).toEqual({
email: 'none-of-your-business@example.org',
password: 'topsecret',
@ -51,7 +58,7 @@ describe('encode', () => {
slug: 'some-body',
id: 'some-id',
})
const decoded = jwt.verify(token, CONFIG.JWT_SECRET)
const decoded = jwt.verify(token, context.config.JWT_SECRET)
expect(decoded).toEqual({
name: 'Some body',
slug: 'some-body',

View File

@ -1,19 +1,23 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import jwt from 'jsonwebtoken'
import { sign } from 'jsonwebtoken'
import CONFIG from '@config/index'
import type CONFIG from '@src/config'
const jwt = { sign }
// Generate an Access Token for the given User ID
export default function encode(user) {
const { id, name, slug } = user
const token = jwt.sign({ id, name, slug }, CONFIG.JWT_SECRET, {
expiresIn: CONFIG.JWT_EXPIRES,
issuer: CONFIG.GRAPHQL_URI,
audience: CONFIG.CLIENT_URI,
subject: user.id.toString(),
})
return token
}
export const encode =
(context: {
config: Pick<typeof CONFIG, 'JWT_SECRET' | 'JWT_EXPIRES' | 'GRAPHQL_URI' | 'CLIENT_URI'>
}) =>
(user) => {
const { id, name, slug } = user
const token: string = jwt.sign({ id, name, slug }, context.config.JWT_SECRET, {
expiresIn: context.config.JWT_EXPIRES,
issuer: context.config.GRAPHQL_URI,
audience: context.config.CLIENT_URI,
subject: user.id.toString(),
})
return token
}

View File

@ -1,33 +1,29 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* 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 databaseContext from '@context/database'
import Factory, { cleanDatabase } from '@db/factories'
import CONFIG from '@src/config'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import { categories } from '@src/constants/categories'
import createServer, { getContext } from '@src/server'
import type { Context } from '@src/context'
const database = databaseContext()
let config: Partial<Context['config']>
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
let server: ApolloServer
let query
beforeEach(() => {
config = {}
})
beforeAll(async () => {
await cleanDatabase()
const authenticatedUser = null
// 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
const context = () => ({ config, authenticatedUser: null })
const apolloSetup = createApolloTestSetup({ context })
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
for (const category of categories) {
await Factory.build('category', {
id: category.id,
@ -55,10 +51,10 @@ const categoriesQuery = gql`
}
`
describe('categroeis middleware', () => {
describe('categories middleware', () => {
describe('categories are active', () => {
beforeEach(() => {
CONFIG.CATEGORIES_ACTIVE = true
config = { ...config, CATEGORIES_ACTIVE: true }
})
it('returns the categories', async () => {
@ -78,7 +74,7 @@ describe('categroeis middleware', () => {
describe('categories are not active', () => {
beforeEach(() => {
CONFIG.CATEGORIES_ACTIVE = false
config = { ...config, CATEGORIES_ACTIVE: false }
})
it('returns an empty array though there are categories in the db', async () => {

View File

@ -1,9 +1,19 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import CONFIG from '@src/config'
import type { Context } from '@src/context'
const checkCategoriesActive = (resolve, root, args, context, resolveInfo) => {
if (CONFIG.CATEGORIES_ACTIVE) {
type Resolver = (
root: unknown,
args: unknown,
context: Context,
resolveInfo: unknown,
) => Promise<unknown>
const checkCategoriesActive = (
resolve: Resolver,
root: unknown,
args: unknown,
context: Context,
resolveInfo: unknown,
) => {
if (context.config.CATEGORIES_ACTIVE) {
return resolve(root, args, context, resolveInfo)
}
return []

View File

@ -1,21 +1,20 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag'
import { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import createServer from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
let server
let query
let mutate
let hashtagingUser
let authenticatedUser
const driver = getDriver()
const neode = getNeode()
let authenticatedUser: Context['user']
const context = () => ({ authenticatedUser })
let mutate: ApolloTestSetup['mutate']
let query: any // eslint-disable-line @typescript-eslint/no-explicit-any
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
const categoryIds = ['cat9']
const createPostMutation = gql`
mutation ($id: ID, $title: String!, $postContent: String!, $categoryIds: [ID]!) {
@ -37,34 +36,27 @@ const updatePostMutation = gql`
beforeAll(async () => {
await cleanDatabase()
const createServerResult = createServer({
context: () => {
return {
user: authenticatedUser,
neode,
driver,
}
},
})
server = createServerResult.server
const createTestClientResult = createTestClient(server)
query = createTestClientResult.query
mutate = createTestClientResult.mutate
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {
await cleanDatabase()
await driver.close()
void server.stop()
void database.driver.close()
database.neode.close()
})
beforeEach(async () => {
hashtagingUser = await neode.create('User', {
hashtagingUser = await database.neode.create('User', {
id: 'you',
name: 'Al Capone',
slug: 'al-capone',
})
await neode.create('Category', {
await database.neode.create('Category', {
id: 'cat9',
name: 'Democracy & Politics',
icon: 'university',

View File

@ -16,7 +16,6 @@ import languages from './languages/languages'
import login from './login/loginMiddleware'
import notifications from './notifications/notificationsMiddleware'
import orderBy from './orderByMiddleware'
// eslint-disable-next-line import/no-cycle
import permissions from './permissionsMiddleware'
import sentry from './sentryMiddleware'
import sluggify from './sluggifyMiddleware'

View File

@ -1,38 +1,33 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag'
import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import createServer from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
let mutate
let authenticatedUser
let authenticatedUser: Context['user']
const context = () => ({ authenticatedUser })
let variables
const driver = getDriver()
const neode = getNeode()
let mutate: ApolloTestSetup['mutate']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
beforeAll(async () => {
await cleanDatabase()
const { server } = createServer({
context: () => {
return {
driver,
neode,
user: authenticatedUser,
}
},
})
mutate = createTestClient(server).mutate
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {
await cleanDatabase()
await driver.close()
void server.stop()
void database.driver.close()
database.neode.close()
})
const createPostMutation = gql`

View File

@ -1,27 +1,29 @@
/* 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 { createGroupMutation } from '@graphql/queries/createGroupMutation'
import { joinGroupMutation } from '@graphql/queries/joinGroupMutation'
import CONFIG from '@src/config'
import createServer, { getContext } from '@src/server'
CONFIG.CATEGORIES_ACTIVE = false
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
const sendNotificationMailMock: (notification) => void = jest.fn()
jest.mock('@src/emails/sendEmail', () => ({
sendNotificationMail: (notification) => sendNotificationMailMock(notification),
}))
let query, mutate, authenticatedUser, emaillessMember
let emaillessMember
let authenticatedUser: Context['user']
const config = { CATEGORIES_ACTIVE: false }
const context = () => ({ authenticatedUser, config })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
let postAuthor, groupMember
@ -94,21 +96,13 @@ const markAllAsRead = async () =>
`,
})
const database = databaseContext()
let server: ApolloServer
beforeAll(async () => {
await cleanDatabase()
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
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {

View File

@ -1,25 +1,27 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* 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 { createGroupMutation } from '@graphql/queries/createGroupMutation'
import CONFIG from '@src/config'
import createServer, { getContext } from '@src/server'
CONFIG.CATEGORIES_ACTIVE = false
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
const sendNotificationMailMock: (notification) => void = jest.fn()
jest.mock('@src/emails/sendEmail', () => ({
sendNotificationMail: (notification) => sendNotificationMailMock(notification),
}))
let query, mutate, authenticatedUser
let authenticatedUser: Context['user']
const config = { CATEGORIES_ACTIVE: false }
const context = () => ({ authenticatedUser, config })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
let postAuthor, firstFollower, secondFollower, thirdFollower, emaillessFollower
@ -68,22 +70,13 @@ const followUserMutation = gql`
}
`
const database = databaseContext()
let server: ApolloServer
beforeAll(async () => {
await cleanDatabase()
// 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
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {

View File

@ -1,28 +1,29 @@
/* 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 { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation'
import { createGroupMutation } from '@graphql/queries/createGroupMutation'
import { joinGroupMutation } from '@graphql/queries/joinGroupMutation'
import CONFIG from '@src/config'
import createServer, { getContext } from '@src/server'
CONFIG.CATEGORIES_ACTIVE = false
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
const sendNotificationMailMock: (notification) => void = jest.fn()
jest.mock('@src/emails/sendEmail', () => ({
sendNotificationMail: (notification) => sendNotificationMailMock(notification),
}))
let query, mutate, authenticatedUser
let authenticatedUser: Context['user']
const config = { CATEGORIES_ACTIVE: false }
const context = () => ({ authenticatedUser, config })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
let postAuthor, groupMember, pendingMember, noMember, emaillessMember
@ -90,21 +91,13 @@ const markAllAsRead = async () =>
`,
})
const database = databaseContext()
let server: ApolloServer
beforeAll(async () => {
await cleanDatabase()
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
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {

View File

@ -1,25 +1,25 @@
/* 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-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 createServer, { getContext } from '@src/server'
CONFIG.CATEGORIES_ACTIVE = false
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
const sendNotificationMailMock: (notification) => void = jest.fn()
jest.mock('@src/emails/sendEmail', () => ({
sendNotificationMail: (notification) => sendNotificationMailMock(notification),
}))
let query, mutate, authenticatedUser
let authenticatedUser: Context['user']
const config = { CATEGORIES_ACTIVE: false }
const context = () => ({ authenticatedUser, config })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
let postAuthor, firstCommenter, secondCommenter, emaillessObserver
@ -77,21 +77,13 @@ const toggleObservePostMutation = gql`
}
}
`
const database = databaseContext()
let server: ApolloServer
beforeAll(async () => {
await cleanDatabase()
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
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {

View File

@ -2,15 +2,12 @@
/* 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 CONFIG from '@src/config'
import createServer, { getContext } from '@src/server'
CONFIG.CATEGORIES_ACTIVE = false
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
const sendNotificationMailMock: (notification) => void = jest.fn()
jest.mock('@src/emails/sendEmail', () => ({
@ -22,7 +19,12 @@ jest.mock('../helpers/isUserOnline', () => ({
isUserOnline: () => isUserOnlineMock(),
}))
let mutate, authenticatedUser
let authenticatedUser: Context['user']
const config = { CATEGORIES_ACTIVE: false }
const context = () => ({ authenticatedUser, config })
let mutate: ApolloTestSetup['mutate']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
let postAuthor
@ -36,23 +38,17 @@ const createPostMutation = gql`
}
`
const database = databaseContext()
beforeAll(async () => {
await cleanDatabase()
// 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
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {
await cleanDatabase()
void server.stop()
await database.driver.close()
})

View File

@ -1,28 +1,29 @@
/* 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 { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation'
import { createGroupMutation } from '@graphql/queries/createGroupMutation'
import { joinGroupMutation } from '@graphql/queries/joinGroupMutation'
import CONFIG from '@src/config'
import createServer, { getContext } from '@src/server'
CONFIG.CATEGORIES_ACTIVE = false
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
const sendNotificationMailMock: (notification) => void = jest.fn()
jest.mock('@src/emails/sendEmail', () => ({
sendNotificationMail: (notification) => sendNotificationMailMock(notification),
}))
let query, mutate, authenticatedUser
let authenticatedUser: Context['user']
const config = { CATEGORIES_ACTIVE: false }
const context = () => ({ authenticatedUser, config })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
let postAuthor, groupMember, pendingMember, emaillessMember
@ -92,20 +93,13 @@ const markAllAsRead = async () =>
`,
})
const database = databaseContext()
let server: ApolloServer
beforeAll(async () => {
await cleanDatabase()
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
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {

View File

@ -4,11 +4,8 @@
/* 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 { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation'
@ -18,7 +15,10 @@ 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, { getContext } from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
import type { DecodedUser } from '@src/jwt/decode'
const sendChatMessageMailMock: (notification) => void = jest.fn()
const sendNotificationMailMock: (notification) => void = jest.fn()
@ -32,11 +32,17 @@ jest.mock('../helpers/isUserOnline', () => ({
isUserOnline: () => isUserOnlineMock(),
}))
const database = databaseContext()
const pubsub = pubsubContext()
const pubsubSpy = jest.spyOn(pubsub, 'publish')
let query, mutate, notifiedUser, authenticatedUser
let notifiedUser
let authenticatedUser: Context['user']
const context = () => ({ authenticatedUser, pubsub })
let mutate: ApolloTestSetup['mutate']
let query: any // eslint-disable-line @typescript-eslint/no-explicit-any
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
const categoryIds = ['cat9']
const createPostMutation = gql`
@ -65,19 +71,13 @@ const createCommentMutation = gql`
}
`
let server: ApolloServer
beforeAll(async () => {
await cleanDatabase()
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
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {
@ -910,7 +910,7 @@ describe('notifications', () => {
userId: 'chatReceiver',
},
})
roomId = room.data.CreateRoom.id
roomId = (room.data as any).CreateRoom.id // eslint-disable-line @typescript-eslint/no-explicit-any
})
describe('if the chatReceiver is online', () => {
@ -1106,7 +1106,7 @@ describe('notifications', () => {
describe('user joins group', () => {
const joinGroupAction = async () => {
authenticatedUser = await notifiedUser.toJson()
authenticatedUser = (await notifiedUser.toJson()) as DecodedUser
await mutate({
mutation: joinGroupMutation(),
variables: {
@ -1193,7 +1193,7 @@ describe('notifications', () => {
describe('user joins and leaves group', () => {
const leaveGroupAction = async () => {
authenticatedUser = await notifiedUser.toJson()
authenticatedUser = (await notifiedUser.toJson()) as DecodedUser
await mutate({
mutation: leaveGroupMutation(),
variables: {
@ -1206,7 +1206,7 @@ describe('notifications', () => {
beforeEach(async () => {
jest.clearAllMocks()
authenticatedUser = await notifiedUser.toJson()
authenticatedUser = (await notifiedUser.toJson()) as DecodedUser
await mutate({
mutation: joinGroupMutation(),
variables: {
@ -1318,7 +1318,7 @@ describe('notifications', () => {
describe('user role in group changes', () => {
const changeGroupMemberRoleAction = async () => {
authenticatedUser = await groupOwner.toJson()
authenticatedUser = (await groupOwner.toJson()) as DecodedUser
await mutate({
mutation: changeGroupMemberRoleMutation(),
variables: {
@ -1331,7 +1331,7 @@ describe('notifications', () => {
}
beforeEach(async () => {
authenticatedUser = await notifiedUser.toJson()
authenticatedUser = (await notifiedUser.toJson()) as DecodedUser
await mutate({
mutation: joinGroupMutation(),
variables: {
@ -1427,7 +1427,7 @@ describe('notifications', () => {
}
beforeEach(async () => {
authenticatedUser = await notifiedUser.toJson()
authenticatedUser = (await notifiedUser.toJson()) as DecodedUser
await mutate({
mutation: joinGroupMutation(),
variables: {

View File

@ -1,36 +1,35 @@
/* 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 createServer, { getContext } from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
let variables
let owner, anotherRegularUser, administrator, moderator
const database = databaseContext()
let authenticatedUser: Context['user']
let config: Partial<Context['config']>
const context = () => ({ authenticatedUser, config })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
let server: ApolloServer
let authenticatedUser
let query, mutate
beforeEach(() => {
config = { CATEGORIES_ACTIVE: true }
})
beforeAll(async () => {
await cleanDatabase()
// 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
const createTestClientResult = createTestClient(server)
query = createTestClientResult.query
mutate = createTestClientResult.mutate
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(() => {
@ -194,11 +193,16 @@ describe('authorization', () => {
inviteCode: 'ABCDEF',
locale: 'de',
}
CONFIG.INVITE_REGISTRATION = false
CONFIG.PUBLIC_REGISTRATION = false
await Factory.build('inviteCode', {
code: 'ABCDEF',
})
config = {
...config,
CATEGORIES_ACTIVE: true,
INVITE_REGISTRATION: false,
PUBLIC_REGISTRATION: false,
}
})
describe('as user', () => {
@ -237,11 +241,15 @@ describe('authorization', () => {
inviteCode: 'ABCDEF',
locale: 'de',
}
CONFIG.INVITE_REGISTRATION = false
CONFIG.PUBLIC_REGISTRATION = true
await Factory.build('inviteCode', {
code: 'ABCDEF',
})
config = {
...config,
CATEGORIES_ACTIVE: true,
INVITE_REGISTRATION: false,
PUBLIC_REGISTRATION: true,
}
})
describe('as anyone', () => {
@ -262,11 +270,15 @@ describe('authorization', () => {
describe('invite registration', () => {
beforeEach(async () => {
CONFIG.INVITE_REGISTRATION = true
CONFIG.PUBLIC_REGISTRATION = false
await Factory.build('inviteCode', {
code: 'ABCDEF',
})
config = {
...config,
CATEGORIES_ACTIVE: true,
INVITE_REGISTRATION: true,
PUBLIC_REGISTRATION: false,
}
})
describe('as anyone with valid invite code', () => {

View File

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
@ -9,9 +9,8 @@ import { rule, shield, deny, allow, or, and } from 'graphql-shield'
import CONFIG from '@config/index'
import SocialMedia from '@db/models/SocialMedia'
import { getNeode } from '@db/neo4j'
// eslint-disable-next-line import/no-cycle
import { validateInviteCode } from '@graphql/resolvers/inviteCodes'
import { Context } from '@src/server'
import type { Context } from '@src/context'
const debug = !!CONFIG.DEBUG
const allowExternalErrors = true
@ -24,29 +23,29 @@ const isAuthenticated = rule({
return !!ctx?.user?.id
})
const isModerator = rule()(async (_parent, _args, { user }, _info) => {
return user && (user.role === 'moderator' || user.role === 'admin')
const isModerator = rule()(async (_parent, _args, { user }: Context, _info) => {
return !!(user && (user.role === 'moderator' || user.role === 'admin'))
})
const isAdmin = rule()(async (_parent, _args, { user }, _info) => {
return user && user.role === 'admin'
const isAdmin = rule()(async (_parent, _args, { user }: Context, _info) => {
return !!(user && user.role === 'admin')
})
const onlyYourself = rule({
cache: 'no_cache',
})(async (_parent, args, context, _info) => {
return context.user.id === args.id
})(async (_parent, args, context: Context, _info) => {
return context.user?.id === args.id
})
const isMyOwn = rule({
cache: 'no_cache',
})(async (parent, _args, { user }, _info) => {
return user && user.id === parent.id
})(async (parent, _args, { user }: Context, _info) => {
return !!(user && user.id === parent.id)
})
const isMySocialMedia = rule({
cache: 'no_cache',
})(async (_, args, { user }) => {
})(async (_, args, { user }: Context) => {
// We need a User
if (!user) {
return false
@ -65,7 +64,7 @@ const isMySocialMedia = rule({
const isAllowedToChangeGroupSettings = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
})(async (_parent, args, { user, driver }: Context) => {
if (!user?.id) return false
const ownerId = user.id
const { id: groupId } = args
@ -89,13 +88,13 @@ const isAllowedToChangeGroupSettings = rule({
} catch (error) {
throw new Error(error)
} finally {
session.close()
await session.close()
}
})
const isAllowedSeeingGroupMembers = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
})(async (_parent, args, { user, driver }: Context) => {
if (!user?.id) return false
const { id: groupId } = args
const session = driver.session()
@ -125,13 +124,13 @@ const isAllowedSeeingGroupMembers = rule({
} catch (error) {
throw new Error(error)
} finally {
session.close()
await session.close()
}
})
const isAllowedToChangeGroupMemberRole = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
})(async (_parent, args, { user, driver }: Context) => {
if (!user?.id) return false
const currentUserId = user.id
const { groupId, userId, roleInGroup } = args
@ -172,13 +171,13 @@ const isAllowedToChangeGroupMemberRole = rule({
} catch (error) {
throw new Error(error)
} finally {
session.close()
await session.close()
}
})
const isAllowedToJoinGroup = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
})(async (_parent, args, { user, driver }: Context) => {
if (!user?.id) return false
const { groupId, userId } = args
const session = driver.session()
@ -202,13 +201,13 @@ const isAllowedToJoinGroup = rule({
} catch (error) {
throw new Error(error)
} finally {
session.close()
await session.close()
}
})
const isAllowedToLeaveGroup = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
})(async (_parent, args, { user, driver }: Context) => {
if (!user?.id) return false
const { groupId, userId } = args
if (user.id !== userId) return false
@ -232,13 +231,13 @@ const isAllowedToLeaveGroup = rule({
} catch (error) {
throw new Error(error)
} finally {
session.close()
await session.close()
}
})
const isMemberOfGroup = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
})(async (_parent, args, { user, driver }: Context) => {
if (!user?.id) return false
const { groupId } = args
if (!groupId) return true
@ -260,13 +259,13 @@ const isMemberOfGroup = rule({
} catch (error) {
throw new Error(error)
} finally {
session.close()
await session.close()
}
})
const canRemoveUserFromGroup = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
})(async (_parent, args, { user, driver }: Context) => {
if (!user?.id) return false
const { groupId, userId } = args
const currentUserId = user.id
@ -296,13 +295,13 @@ const canRemoveUserFromGroup = rule({
} catch (error) {
throw new Error(error)
} finally {
session.close()
await session.close()
}
})
const canCommentPost = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
})(async (_parent, args, { user, driver }: Context) => {
if (!user?.id) return false
const { postId } = args
const userId = user.id
@ -330,13 +329,13 @@ const canCommentPost = rule({
} catch (error) {
throw new Error(error)
} finally {
session.close()
await session.close()
}
})
const isAuthor = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
})(async (_parent, args, { user, driver }: Context) => {
if (!user) return false
const { id: resourceId } = args
const session = driver.session()
@ -354,14 +353,14 @@ const isAuthor = rule({
const [author] = await authorReadTxPromise
return !!author
} finally {
session.close()
await session.close()
}
})
const isDeletingOwnAccount = rule({
cache: 'no_cache',
})(async (_parent, args, context, _info) => {
return context.user.id === args.id
})(async (_parent, args, context: Context, _info) => {
return context.user?.id === args.id
})
const noEmailFilter = rule({
@ -370,10 +369,12 @@ const noEmailFilter = rule({
return !('email' in args)
})
const publicRegistration = rule()(() => CONFIG.PUBLIC_REGISTRATION)
const publicRegistration = rule()(
async (_parent, _args, context: Context) => context.config.PUBLIC_REGISTRATION,
)
const inviteRegistration = rule()(async (_parent, args, context: Context) => {
if (!CONFIG.INVITE_REGISTRATION) return false
if (!context.config.INVITE_REGISTRATION) return false
const { inviteCode } = args
return validateInviteCode(context, inviteCode)
})

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-return */
import type { Context } from '@src/server'
import type { Context } from '@src/context'
import uniqueSlug from './slugify/uniqueSlug'

View File

@ -2,16 +2,14 @@
/* 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 Factory, { cleanDatabase } from '@db/factories'
import { createGroupMutation } from '@graphql/queries/createGroupMutation'
import { createPostMutation } from '@graphql/queries/createPostMutation'
import { signupVerificationMutation } from '@graphql/queries/signupVerificationMutation'
import { updateGroupMutation } from '@graphql/queries/updateGroupMutation'
import createServer, { getContext } from '@src/server'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
let variables
const categoryIds = ['cat9']
@ -19,23 +17,18 @@ const categoryIds = ['cat9']
const descriptionAdditional100 =
' 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789'
const database = databaseContext()
let server: ApolloServer
let authenticatedUser
let mutate
let authenticatedUser: Context['user']
const context = () => ({ authenticatedUser })
let mutate: ApolloTestSetup['mutate']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
beforeAll(async () => {
await cleanDatabase()
// 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
const createTestClientResult = createTestClient(server)
mutate = createTestClientResult.mutate
const testServer = createApolloTestSetup({ context })
mutate = testServer.mutate
database = testServer.database
server = testServer.server
})
afterAll(() => {

View File

@ -1,33 +1,29 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-return */
import { ApolloServer } from 'apollo-server-express'
import { createTestClient } from 'apollo-server-testing'
import databaseContext from '@context/database'
import { ApolloServer } from 'apollo-server-express'
import Factory, { cleanDatabase } from '@db/factories'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
import { loginMutation } from '@src/graphql/queries/loginMutation'
import ocelotLogger from '@src/logger'
import { loggerPlugin } from '@src/plugins/apolloLogger'
import createServer, { getContext } from '@src/server'
const database = databaseContext()
let server: ApolloServer
let mutate, authenticatedUser
const authenticatedUser: Context['user'] = null
let mutate: ApolloTestSetup['mutate']
let database: ApolloTestSetup['database']
const context = () => ({ authenticatedUser })
beforeAll(async () => {
await cleanDatabase()
// eslint-disable-next-line @typescript-eslint/require-await
const contextUser = async (_req) => authenticatedUser
const context = getContext({ user: contextUser, database })
server = createServer({ context, plugins: [loggerPlugin] }).server
const createTestClientResult = createTestClient(server)
mutate = createTestClientResult.mutate
const apolloSetup = createApolloTestSetup({ context, plugins: [loggerPlugin] })
mutate = apolloSetup.mutate
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {

View File

@ -1,8 +1,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable import/no-named-as-default-member */
import http from 'node:http'
@ -13,86 +12,31 @@ import express from 'express'
import { graphqlUploadExpress } from 'graphql-upload'
import helmet from 'helmet'
import databaseContext from '@context/database'
import pubsubContext from '@context/pubsub'
import CONFIG from './config'
import { context, getContext } from './context'
import schema from './graphql/schema'
import decode from './jwt/decode'
import ocelotLogger from './logger'
// eslint-disable-next-line import/no-cycle
import middleware from './middleware'
import type OcelotLogger from './logger'
import type { ApolloServerExpressConfig } from 'apollo-server-express'
const serverDatabase = databaseContext()
const serverPubsub = pubsubContext()
const databaseUser = async (req) => decode(serverDatabase.driver, req.headers.authorization)
export const getContext =
(
{
database = serverDatabase,
pubsub = serverPubsub,
user = databaseUser,
logger = ocelotLogger,
}: {
database?: ReturnType<typeof databaseContext>
pubsub?: ReturnType<typeof pubsubContext>
user?: (any) => Promise<any>
logger?: typeof OcelotLogger
} = {
database: serverDatabase,
pubsub: serverPubsub,
user: databaseUser,
logger: ocelotLogger,
},
) =>
async (req) => {
const u = await user(req)
return {
database,
driver: database.driver,
neode: database.neode,
pubsub,
logger,
user: u,
req,
cypherParams: {
currentUserId: u ? u.id : null,
},
}
}
export type Context = Awaited<ReturnType<ReturnType<typeof getContext>>>
export const context = async (options) => {
const { connection, req } = options
if (connection) {
return connection.context
} else {
return getContext()(req)
}
}
const createServer = (options?) => {
const defaults = {
const createServer = (options?: ApolloServerExpressConfig) => {
const defaults: ApolloServerExpressConfig = {
context,
schema: middleware(schema),
subscriptions: {
onConnect: (connectionParams) => getContext()(connectionParams),
onConnect: (connectionParams) =>
getContext()(connectionParams as { headers: { authorization?: string } }),
},
debug: !!CONFIG.DEBUG,
uploads: false,
tracing: !!CONFIG.DEBUG,
formatError: (error) => {
// console.log(error.originalError)
if (error.message === 'ERROR_VALIDATION') {
return new Error(error.originalError.details.map((d) => d.message))
return new Error((error.originalError as any).details.map((d) => d.message))
}
return error
},
plugins: [],
}
const server = new ApolloServer(Object.assign(defaults, options))

100
backend/test/helpers.ts Normal file
View File

@ -0,0 +1,100 @@
import { createTestClient } from 'apollo-server-testing'
import databaseContext from '@context/database'
import type CONFIG from '@src/config'
import type { Context } from '@src/context'
import { getContext } from '@src/context'
import createServer from '@src/server'
import type { ApolloServerExpressConfig } from 'apollo-server-express'
export const TEST_CONFIG = {
NODE_ENV: 'test',
DEBUG: undefined,
TEST: true,
PRODUCTION: false,
PRODUCTION_DB_CLEAN_ALLOW: false,
DISABLED_MIDDLEWARES: [],
SEND_MAIL: false,
CLIENT_URI: 'http://webapp:3000',
GRAPHQL_URI: 'http://localhost:4000',
JWT_EXPIRES: '2y',
MAPBOX_TOKEN:
'pk.eyJ1IjoiYnVzZmFrdG9yIiwiYSI6ImNraDNiM3JxcDBhaWQydG1uczhpZWtpOW4ifQ.7TNRTO-o9aK1Y6MyW_Nd4g',
JWT_SECRET: 'JWT_SECRET',
PRIVATE_KEY_PASSPHRASE: 'PRIVATE_KEY_PASSPHRASE',
NEO4J_URI: 'bolt://localhost:7687',
NEO4J_USERNAME: 'neo4j',
NEO4J_PASSWORD: 'neo4j',
SENTRY_DSN_BACKEND: undefined,
COMMIT: undefined,
REDIS_DOMAIN: undefined,
REDIS_PORT: undefined,
REDIS_PASSWORD: undefined,
AWS_ACCESS_KEY_ID: '',
AWS_SECRET_ACCESS_KEY: '',
AWS_ENDPOINT: '',
AWS_REGION: '',
AWS_BUCKET: '',
S3_PUBLIC_GATEWAY: undefined,
EMAIL_DEFAULT_SENDER: '',
SUPPORT_EMAIL: '',
SUPPORT_URL: '',
APPLICATION_NAME: '',
ORGANIZATION_URL: '',
PUBLIC_REGISTRATION: false,
INVITE_REGISTRATION: true,
INVITE_CODES_PERSONAL_PER_USER: 7,
INVITE_CODES_GROUP_PER_USER: 7,
CATEGORIES_ACTIVE: false,
MAX_PINNED_POSTS: 1,
LANGUAGE_DEFAULT: 'en',
LOG_LEVEL: 'DEBUG',
} as const satisfies typeof CONFIG
interface OverwritableContextParams {
authenticatedUser?: Context['user']
config?: Partial<typeof CONFIG>
pubsub?: Context['pubsub']
}
interface CreateTestServerOptions {
context: () => OverwritableContextParams | Promise<OverwritableContextParams>
plugins?: ApolloServerExpressConfig['plugins']
}
export const createApolloTestSetup = (opts?: CreateTestServerOptions) => {
const defaultOpts: CreateTestServerOptions = { context: () => ({ authenticatedUser: null }) }
const { context: testContext, plugins } = opts ?? defaultOpts
const database = databaseContext()
const context = async (req: { headers: { authorization?: string } }) => {
const { authenticatedUser, config = {}, pubsub } = await testContext()
return getContext({
authenticatedUser,
database,
pubsub,
config: { ...TEST_CONFIG, ...config },
})(req)
}
const server = createServer({
context,
plugins,
}).server
const { mutate, query } = createTestClient(server)
return {
server,
query,
mutate,
database,
}
}
export type ApolloTestSetup = ReturnType<typeof createApolloTestSetup>

View File

@ -2361,6 +2361,11 @@
"@types/node" "*"
"@types/responselike" "*"
"@types/caseless@*":
version "0.12.5"
resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.5.tgz#db9468cb1b1b5a925b8f34822f1669df0c5472f5"
integrity sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==
"@types/connect@*":
version "3.4.33"
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.33.tgz#31610c901eca573b8713c3330abc6e6b9f588546"
@ -2504,6 +2509,13 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
"@types/jsonwebtoken@~8.5.1":
version "8.5.9"
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz#2c064ecb0b3128d837d2764aa0b117b0ff6e4586"
integrity sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==
dependencies:
"@types/node" "*"
"@types/keygrip@*":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72"
@ -2598,6 +2610,16 @@
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c"
integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==
"@types/request@^2.48.12":
version "2.48.12"
resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.12.tgz#0f590f615a10f87da18e9790ac94c29ec4c5ef30"
integrity sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==
dependencies:
"@types/caseless" "*"
"@types/node" "*"
"@types/tough-cookie" "*"
form-data "^2.5.0"
"@types/responselike@*", "@types/responselike@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"
@ -2628,6 +2650,11 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
"@types/tough-cookie@*":
version "4.0.5"
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304"
integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==
"@types/uuid@^9.0.1", "@types/uuid@~9.0.1":
version "9.0.8"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba"
@ -4946,6 +4973,16 @@ es-set-tostringtag@^2.0.3:
has-tostringtag "^1.0.2"
hasown "^2.0.1"
es-set-tostringtag@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d"
integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==
dependencies:
es-errors "^1.3.0"
get-intrinsic "^1.2.6"
has-tostringtag "^1.0.2"
hasown "^2.0.2"
es-shim-unscopables@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241"
@ -5712,6 +5749,17 @@ foreground-child@^3.1.0:
cross-spawn "^7.0.6"
signal-exit "^4.0.1"
form-data@^2.5.0:
version "2.5.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.3.tgz#f9bcf87418ce748513c0c3494bb48ec270c97acc"
integrity sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
es-set-tostringtag "^2.1.0"
mime-types "^2.1.35"
safe-buffer "^5.2.1"
form-data@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682"
@ -5836,7 +5884,7 @@ get-intrinsic@^1.2.3, get-intrinsic@^1.2.4:
has-symbols "^1.0.3"
hasown "^2.0.0"
get-intrinsic@^1.2.5, get-intrinsic@^1.3.0:
get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
@ -8078,7 +8126,7 @@ mime-db@^1.54.0:
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5"
integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==
mime-types@^2.1.12, mime-types@~2.1.22, mime-types@~2.1.24, mime-types@~2.1.34:
mime-types@^2.1.12, mime-types@^2.1.35, mime-types@~2.1.22, mime-types@~2.1.24, mime-types@~2.1.34:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
@ -9481,7 +9529,7 @@ safe-array-concat@^1.1.2:
has-symbols "^1.0.3"
isarray "^2.0.5"
safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@~5.2.0:
safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.2.1, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==

View File

@ -1,5 +1,6 @@
import { defineStep } from '@badeball/cypress-cucumber-preprocessor'
import encode from '../../../../backend/build/src/jwt/encode'
import CONFIG from '../../../../backend/build/src/config/index'
import { encode } from '../../../../backend/build/src/jwt/encode'
defineStep('I am logged in as {string}', slug => {
cy.neode()
@ -13,6 +14,6 @@ defineStep('I am logged in as {string}', slug => {
})
})
.then(user => {
cy.setCookie('ocelot-social-token', encode(user))
cy.setCookie('ocelot-social-token', encode({ config: CONFIG })(user))
})
})