mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into 2594-contributions-list-frontend
This commit is contained in:
commit
0f449de4a0
@ -4,6 +4,7 @@ import path from 'path'
|
|||||||
import { createTransport } from 'nodemailer'
|
import { createTransport } from 'nodemailer'
|
||||||
import Email from 'email-templates'
|
import Email from 'email-templates'
|
||||||
import i18n from 'i18n'
|
import i18n from 'i18n'
|
||||||
|
import LogError from '@/server/LogError'
|
||||||
|
|
||||||
export const sendEmailTranslated = async (params: {
|
export const sendEmailTranslated = async (params: {
|
||||||
receiver: {
|
receiver: {
|
||||||
@ -73,8 +74,7 @@ export const sendEmailTranslated = async (params: {
|
|||||||
logger.info('Result: ', result)
|
logger.info('Result: ', result)
|
||||||
})
|
})
|
||||||
.catch((error: unknown) => {
|
.catch((error: unknown) => {
|
||||||
logger.error('Error sending notification email: ', error)
|
throw new LogError('Error sending notification email', error)
|
||||||
throw new Error('Error sending notification email!')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
i18n.setLocale(rememberLocaleToRestore)
|
i18n.setLocale(rememberLocaleToRestore)
|
||||||
|
|||||||
@ -180,14 +180,14 @@ export class ContributionResolver {
|
|||||||
@Args()
|
@Args()
|
||||||
{ currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated,
|
{ currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated,
|
||||||
@Arg('statusFilter', () => [ContributionStatus], { nullable: true })
|
@Arg('statusFilter', () => [ContributionStatus], { nullable: true })
|
||||||
statusFilters?: ContributionStatus[],
|
statusFilter?: ContributionStatus[],
|
||||||
): Promise<ContributionListResult> {
|
): Promise<ContributionListResult> {
|
||||||
const where: {
|
const where: {
|
||||||
contributionStatus?: FindOperator<string> | null
|
contributionStatus?: FindOperator<string> | null
|
||||||
} = {}
|
} = {}
|
||||||
|
|
||||||
if (statusFilters && statusFilters.length) {
|
if (statusFilter && statusFilter.length) {
|
||||||
where.contributionStatus = In(statusFilters)
|
where.contributionStatus = In(statusFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
const [dbContributions, count] = await getConnection()
|
const [dbContributions, count] = await getConnection()
|
||||||
|
|||||||
@ -75,7 +75,7 @@ describe('EmailOptinCodes', () => {
|
|||||||
query({ query: queryOptIn, variables: { optIn: optinCode } }),
|
query({ query: queryOptIn, variables: { optIn: optinCode } }),
|
||||||
).resolves.toMatchObject({
|
).resolves.toMatchObject({
|
||||||
data: null,
|
data: null,
|
||||||
errors: [new GraphQLError('email was sent more than 24 hours ago')],
|
errors: [new GraphQLError('Email was sent more than 24 hours ago')],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ describe('EmailOptinCodes', () => {
|
|||||||
mutate({ mutation: setPassword, variables: { code: optinCode, password: 'Aa12345_' } }),
|
mutate({ mutation: setPassword, variables: { code: optinCode, password: 'Aa12345_' } }),
|
||||||
).resolves.toMatchObject({
|
).resolves.toMatchObject({
|
||||||
data: null,
|
data: null,
|
||||||
errors: [new GraphQLError('email was sent more than 24 hours ago')],
|
errors: [new GraphQLError('Email was sent more than 24 hours ago')],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -96,7 +96,7 @@ describe('EmailOptinCodes', () => {
|
|||||||
mutate({ mutation: forgotPassword, variables: { email: 'peter@lustig.de' } }),
|
mutate({ mutation: forgotPassword, variables: { email: 'peter@lustig.de' } }),
|
||||||
).resolves.toMatchObject({
|
).resolves.toMatchObject({
|
||||||
data: null,
|
data: null,
|
||||||
errors: [new GraphQLError('email already sent less than 10 minutes ago')],
|
errors: [new GraphQLError('Email already sent less than 10 minutes ago')],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -89,7 +89,7 @@ describe('send coins', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith(`UserContact with email=wrong@email.com does not exists`)
|
expect(logger.error).toBeCalledWith('No user with this credentials', 'wrong@email.com')
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('deleted recipient', () => {
|
describe('deleted recipient', () => {
|
||||||
|
|||||||
@ -549,7 +549,9 @@ describe('UserResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith('Password entered is lexically invalid')
|
expect(logger.error).toBeCalledWith(
|
||||||
|
'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -606,9 +608,7 @@ describe('UserResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error found', () => {
|
it('logs the error found', () => {
|
||||||
expect(logger.error).toBeCalledWith(
|
expect(logger.error).toBeCalledWith('No user with this credentials', variables.email)
|
||||||
'UserContact with email=bibi@bloxberg.de does not exists',
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -668,7 +668,112 @@ describe('UserResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith('The User has no valid credentials.')
|
expect(logger.error).toBeCalledWith('No user with this credentials', variables.email)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('user is in database but deleted', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
await userFactory(testEnv, stephenHawking)
|
||||||
|
const variables = {
|
||||||
|
email: stephenHawking.email,
|
||||||
|
password: 'Aa12345_',
|
||||||
|
publisherId: 1234,
|
||||||
|
}
|
||||||
|
result = await mutate({ mutation: login, variables })
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await cleanDB()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns an error', () => {
|
||||||
|
expect(result).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [
|
||||||
|
new GraphQLError('This user was permanently deleted. Contact support for questions'),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('logs the error thrown', () => {
|
||||||
|
expect(logger.error).toBeCalledWith(
|
||||||
|
'This user was permanently deleted. Contact support for questions',
|
||||||
|
expect.objectContaining({
|
||||||
|
firstName: stephenHawking.firstName,
|
||||||
|
lastName: stephenHawking.lastName,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('user is in database but email not confirmed', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
await userFactory(testEnv, garrickOllivander)
|
||||||
|
const variables = {
|
||||||
|
email: garrickOllivander.email,
|
||||||
|
password: 'Aa12345_',
|
||||||
|
publisherId: 1234,
|
||||||
|
}
|
||||||
|
result = await mutate({ mutation: login, variables })
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await cleanDB()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns an error', () => {
|
||||||
|
expect(result).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('The Users email is not validate yet')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('logs the error thrown', () => {
|
||||||
|
expect(logger.error).toBeCalledWith(
|
||||||
|
'The Users email is not validate yet',
|
||||||
|
expect.objectContaining({
|
||||||
|
firstName: garrickOllivander.firstName,
|
||||||
|
lastName: garrickOllivander.lastName,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe.skip('user is in database but password is not set', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
// TODO: we need an user without password set
|
||||||
|
const user = await userFactory(testEnv, bibiBloxberg)
|
||||||
|
user.password = BigInt(0)
|
||||||
|
await user.save()
|
||||||
|
result = await mutate({ mutation: login, variables })
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await cleanDB()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns an error', () => {
|
||||||
|
expect(result).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('The User has not set a password yet')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('logs the error thrown', () => {
|
||||||
|
expect(logger.error).toBeCalledWith(
|
||||||
|
'The User has not set a password yet',
|
||||||
|
expect.objectContaining({
|
||||||
|
firstName: bibiBloxberg.firstName,
|
||||||
|
lastName: bibiBloxberg.lastName,
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -828,7 +933,7 @@ describe('UserResolver', () => {
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [
|
errors: [
|
||||||
new GraphQLError(
|
new GraphQLError(
|
||||||
`email already sent less than ${printTimeDuration(
|
`Email already sent less than ${printTimeDuration(
|
||||||
CONFIG.EMAIL_CODE_REQUEST_TIME,
|
CONFIG.EMAIL_CODE_REQUEST_TIME,
|
||||||
)} ago`,
|
)} ago`,
|
||||||
),
|
),
|
||||||
@ -870,13 +975,13 @@ describe('UserResolver', () => {
|
|||||||
CONFIG.EMAIL_CODE_REQUEST_TIME = emailCodeRequestTime
|
CONFIG.EMAIL_CODE_REQUEST_TIME = emailCodeRequestTime
|
||||||
await expect(mutate({ mutation: forgotPassword, variables })).resolves.toEqual(
|
await expect(mutate({ mutation: forgotPassword, variables })).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [new GraphQLError('email already sent less than 10 minutes ago')],
|
errors: [new GraphQLError('Email already sent less than 10 minutes ago')],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error found', () => {
|
it('logs the error found', () => {
|
||||||
expect(logger.error).toBeCalledWith(`email already sent less than 10 minutes ago`)
|
expect(logger.error).toBeCalledWith(`Email already sent less than 10 minutes ago`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -1001,13 +1106,13 @@ describe('UserResolver', () => {
|
|||||||
}),
|
}),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [new GraphQLError(`"not-valid" isn't a valid language`)],
|
errors: [new GraphQLError('Given language is not a valid language')],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error found', () => {
|
it('logs the error found', () => {
|
||||||
expect(logger.error).toBeCalledWith(`"not-valid" isn't a valid language`)
|
expect(logger.error).toBeCalledWith('Given language is not a valid language', 'not-valid')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1058,7 +1163,9 @@ describe('UserResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error found', () => {
|
it('logs the error found', () => {
|
||||||
expect(logger.error).toBeCalledWith('newPassword does not fullfil the rules')
|
expect(logger.error).toBeCalledWith(
|
||||||
|
'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1116,7 +1223,9 @@ describe('UserResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith('The User has no valid credentials.')
|
expect(logger.error).toBeCalledWith(
|
||||||
|
'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -1322,13 +1431,13 @@ describe('UserResolver', () => {
|
|||||||
mutate({ mutation: setUserRole, variables: { userId: admin.id + 1, isAdmin: true } }),
|
mutate({ mutation: setUserRole, variables: { userId: admin.id + 1, isAdmin: true } }),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [new GraphQLError(`Could not find user with userId: ${admin.id + 1}`)],
|
errors: [new GraphQLError('Could not find user with given ID')],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith(`Could not find user with userId: ${admin.id + 1}`)
|
expect(logger.error).toBeCalledWith('Could not find user with given ID', admin.id + 1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1379,12 +1488,12 @@ describe('UserResolver', () => {
|
|||||||
mutate({ mutation: setUserRole, variables: { userId: admin.id, isAdmin: false } }),
|
mutate({ mutation: setUserRole, variables: { userId: admin.id, isAdmin: false } }),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [new GraphQLError('Administrator can not change his own role!')],
|
errors: [new GraphQLError('Administrator can not change his own role')],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith('Administrator can not change his own role!')
|
expect(logger.error).toBeCalledWith('Administrator can not change his own role')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1400,13 +1509,13 @@ describe('UserResolver', () => {
|
|||||||
mutate({ mutation: setUserRole, variables: { userId: user.id, isAdmin: true } }),
|
mutate({ mutation: setUserRole, variables: { userId: user.id, isAdmin: true } }),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [new GraphQLError('User is already admin!')],
|
errors: [new GraphQLError('User is already admin')],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith('User is already admin!')
|
expect(logger.error).toBeCalledWith('User is already admin')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1421,13 +1530,13 @@ describe('UserResolver', () => {
|
|||||||
mutate({ mutation: setUserRole, variables: { userId: user.id, isAdmin: false } }),
|
mutate({ mutation: setUserRole, variables: { userId: user.id, isAdmin: false } }),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [new GraphQLError('User is already a usual user!')],
|
errors: [new GraphQLError('User is already an usual user')],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith('User is already a usual user!')
|
expect(logger.error).toBeCalledWith('User is already an usual user')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -1494,13 +1603,13 @@ describe('UserResolver', () => {
|
|||||||
mutate({ mutation: deleteUser, variables: { userId: admin.id + 1 } }),
|
mutate({ mutation: deleteUser, variables: { userId: admin.id + 1 } }),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [new GraphQLError(`Could not find user with userId: ${admin.id + 1}`)],
|
errors: [new GraphQLError('Could not find user with given ID')],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith(`Could not find user with userId: ${admin.id + 1}`)
|
expect(logger.error).toBeCalledWith('Could not find user with given ID', admin.id + 1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1511,13 +1620,13 @@ describe('UserResolver', () => {
|
|||||||
mutate({ mutation: deleteUser, variables: { userId: admin.id } }),
|
mutate({ mutation: deleteUser, variables: { userId: admin.id } }),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [new GraphQLError('Moderator can not delete his own account!')],
|
errors: [new GraphQLError('Moderator can not delete his own account')],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith('Moderator can not delete his own account!')
|
expect(logger.error).toBeCalledWith('Moderator can not delete his own account')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1545,13 +1654,13 @@ describe('UserResolver', () => {
|
|||||||
mutate({ mutation: deleteUser, variables: { userId: user.id } }),
|
mutate({ mutation: deleteUser, variables: { userId: user.id } }),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [new GraphQLError(`Could not find user with userId: ${user.id}`)],
|
errors: [new GraphQLError('Could not find user with given ID')],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith(`Could not find user with userId: ${user.id}`)
|
expect(logger.error).toBeCalledWith('Could not find user with given ID', user.id)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -1617,13 +1726,13 @@ describe('UserResolver', () => {
|
|||||||
mutate({ mutation: unDeleteUser, variables: { userId: admin.id + 1 } }),
|
mutate({ mutation: unDeleteUser, variables: { userId: admin.id + 1 } }),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [new GraphQLError(`Could not find user with userId: ${admin.id + 1}`)],
|
errors: [new GraphQLError('Could not find user with given ID')],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith(`Could not find user with userId: ${admin.id + 1}`)
|
expect(logger.error).toBeCalledWith('Could not find user with given ID', admin.id + 1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -63,6 +63,7 @@ import { isValidPassword } from '@/password/EncryptorUtils'
|
|||||||
import { FULL_CREATION_AVAILABLE } from './const/const'
|
import { FULL_CREATION_AVAILABLE } from './const/const'
|
||||||
import { encryptPassword, verifyPassword } from '@/password/PasswordEncryptor'
|
import { encryptPassword, verifyPassword } from '@/password/PasswordEncryptor'
|
||||||
import { PasswordEncryptionType } from '../enum/PasswordEncryptionType'
|
import { PasswordEncryptionType } from '../enum/PasswordEncryptionType'
|
||||||
|
import LogError from '@/server/LogError'
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const sodium = require('sodium-native')
|
const sodium = require('sodium-native')
|
||||||
@ -134,22 +135,19 @@ export class UserResolver {
|
|||||||
email = email.trim().toLowerCase()
|
email = email.trim().toLowerCase()
|
||||||
const dbUser = await findUserByEmail(email)
|
const dbUser = await findUserByEmail(email)
|
||||||
if (dbUser.deletedAt) {
|
if (dbUser.deletedAt) {
|
||||||
logger.error('The User was permanently deleted in database.')
|
throw new LogError('This user was permanently deleted. Contact support for questions', dbUser)
|
||||||
throw new Error('This user was permanently deleted. Contact support for questions.')
|
|
||||||
}
|
}
|
||||||
if (!dbUser.emailContact.emailChecked) {
|
if (!dbUser.emailContact.emailChecked) {
|
||||||
logger.error('The Users email is not validate yet.')
|
throw new LogError('The Users email is not validate yet', dbUser)
|
||||||
throw new Error('User email not validated')
|
|
||||||
}
|
}
|
||||||
|
// TODO: at least in test this does not work since `dbUser.password = 0` and `BigInto(0) = 0n`
|
||||||
if (dbUser.password === BigInt(0)) {
|
if (dbUser.password === BigInt(0)) {
|
||||||
logger.error('The User has not set a password yet.')
|
|
||||||
// TODO we want to catch this on the frontend and ask the user to check his emails or resend code
|
// TODO we want to catch this on the frontend and ask the user to check his emails or resend code
|
||||||
throw new Error('User has no password set yet')
|
throw new LogError('The User has not set a password yet', dbUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!verifyPassword(dbUser, password)) {
|
if (!verifyPassword(dbUser, password)) {
|
||||||
logger.error('The User has no valid credentials.')
|
throw new LogError('No user with this credentials', dbUser)
|
||||||
throw new Error('No user with this credentials')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dbUser.passwordEncryptionType !== PasswordEncryptionType.GRADIDO_ID) {
|
if (dbUser.passwordEncryptionType !== PasswordEncryptionType.GRADIDO_ID) {
|
||||||
@ -309,30 +307,19 @@ export class UserResolver {
|
|||||||
await queryRunner.startTransaction('REPEATABLE READ')
|
await queryRunner.startTransaction('REPEATABLE READ')
|
||||||
try {
|
try {
|
||||||
dbUser = await queryRunner.manager.save(dbUser).catch((error) => {
|
dbUser = await queryRunner.manager.save(dbUser).catch((error) => {
|
||||||
logger.error('Error while saving dbUser', error)
|
throw new LogError('Error while saving dbUser', error)
|
||||||
throw new Error('error saving user')
|
|
||||||
})
|
})
|
||||||
let emailContact = newEmailContact(email, dbUser.id)
|
let emailContact = newEmailContact(email, dbUser.id)
|
||||||
emailContact = await queryRunner.manager.save(emailContact).catch((error) => {
|
emailContact = await queryRunner.manager.save(emailContact).catch((error) => {
|
||||||
logger.error('Error while saving emailContact', error)
|
throw new LogError('Error while saving user email contact', error)
|
||||||
throw new Error('error saving email user contact')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
dbUser.emailContact = emailContact
|
dbUser.emailContact = emailContact
|
||||||
dbUser.emailId = emailContact.id
|
dbUser.emailId = emailContact.id
|
||||||
await queryRunner.manager.save(dbUser).catch((error) => {
|
await queryRunner.manager.save(dbUser).catch((error) => {
|
||||||
logger.error('Error while updating dbUser', error)
|
throw new LogError('Error while updating dbUser', error)
|
||||||
throw new Error('error updating user')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
/*
|
|
||||||
const emailOptIn = newEmailOptIn(dbUser.id)
|
|
||||||
await queryRunner.manager.save(emailOptIn).catch((error) => {
|
|
||||||
logger.error('Error while saving emailOptIn', error)
|
|
||||||
throw new Error('error saving email opt in')
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
|
|
||||||
const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(
|
const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(
|
||||||
/{optin}/g,
|
/{optin}/g,
|
||||||
emailContact.emailVerificationCode.toString(),
|
emailContact.emailVerificationCode.toString(),
|
||||||
@ -358,9 +345,8 @@ export class UserResolver {
|
|||||||
await queryRunner.commitTransaction()
|
await queryRunner.commitTransaction()
|
||||||
logger.addContext('user', dbUser.id)
|
logger.addContext('user', dbUser.id)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(`error during create user with ${e}`)
|
|
||||||
await queryRunner.rollbackTransaction()
|
await queryRunner.rollbackTransaction()
|
||||||
throw e
|
throw new LogError('Error creating user', e)
|
||||||
} finally {
|
} finally {
|
||||||
await queryRunner.release()
|
await queryRunner.release()
|
||||||
}
|
}
|
||||||
@ -392,11 +378,9 @@ export class UserResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!canEmailResend(user.emailContact.updatedAt || user.emailContact.createdAt)) {
|
if (!canEmailResend(user.emailContact.updatedAt || user.emailContact.createdAt)) {
|
||||||
const errorMessage = `email already sent less than ${printTimeDuration(
|
throw new LogError(
|
||||||
CONFIG.EMAIL_CODE_REQUEST_TIME,
|
`Email already sent less than ${printTimeDuration(CONFIG.EMAIL_CODE_REQUEST_TIME)} ago`,
|
||||||
)} ago`
|
)
|
||||||
logger.error(errorMessage)
|
|
||||||
throw new Error(errorMessage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
user.emailContact.updatedAt = new Date()
|
user.emailContact.updatedAt = new Date()
|
||||||
@ -404,8 +388,7 @@ export class UserResolver {
|
|||||||
user.emailContact.emailVerificationCode = random(64)
|
user.emailContact.emailVerificationCode = random(64)
|
||||||
user.emailContact.emailOptInTypeId = OptInType.EMAIL_OPT_IN_RESET_PASSWORD
|
user.emailContact.emailOptInTypeId = OptInType.EMAIL_OPT_IN_RESET_PASSWORD
|
||||||
await user.emailContact.save().catch(() => {
|
await user.emailContact.save().catch(() => {
|
||||||
logger.error('Unable to save email verification code= ' + user.emailContact)
|
throw new LogError('Unable to save email verification code', user.emailContact)
|
||||||
throw new Error('Unable to save email verification code.')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.info(`optInCode for ${email}=${user.emailContact}`)
|
logger.info(`optInCode for ${email}=${user.emailContact}`)
|
||||||
@ -440,34 +423,23 @@ export class UserResolver {
|
|||||||
logger.info(`setPassword(${code}, ***)...`)
|
logger.info(`setPassword(${code}, ***)...`)
|
||||||
// Validate Password
|
// Validate Password
|
||||||
if (!isValidPassword(password)) {
|
if (!isValidPassword(password)) {
|
||||||
logger.error('Password entered is lexically invalid')
|
throw new LogError(
|
||||||
throw new Error(
|
|
||||||
'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!',
|
'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load code
|
// load code
|
||||||
/*
|
|
||||||
const optInCode = await LoginEmailOptIn.findOneOrFail({ verificationCode: code }).catch(() => {
|
|
||||||
logger.error('Could not login with emailVerificationCode')
|
|
||||||
throw new Error('Could not login with emailVerificationCode')
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
const userContact = await DbUserContact.findOneOrFail(
|
const userContact = await DbUserContact.findOneOrFail(
|
||||||
{ emailVerificationCode: code },
|
{ emailVerificationCode: code },
|
||||||
{ relations: ['user'] },
|
{ relations: ['user'] },
|
||||||
).catch(() => {
|
).catch(() => {
|
||||||
logger.error('Could not login with emailVerificationCode')
|
throw new LogError('Could not login with emailVerificationCode')
|
||||||
throw new Error('Could not login with emailVerificationCode')
|
|
||||||
})
|
})
|
||||||
logger.debug('userContact loaded...')
|
logger.debug('userContact loaded...')
|
||||||
// Code is only valid for `CONFIG.EMAIL_CODE_VALID_TIME` minutes
|
// Code is only valid for `CONFIG.EMAIL_CODE_VALID_TIME` minutes
|
||||||
if (!isEmailVerificationCodeValid(userContact.updatedAt || userContact.createdAt)) {
|
if (!isEmailVerificationCodeValid(userContact.updatedAt || userContact.createdAt)) {
|
||||||
logger.error(
|
throw new LogError(
|
||||||
`email was sent more than ${printTimeDuration(CONFIG.EMAIL_CODE_VALID_TIME)} ago`,
|
`Email was sent more than ${printTimeDuration(CONFIG.EMAIL_CODE_VALID_TIME)} ago`,
|
||||||
)
|
|
||||||
throw new Error(
|
|
||||||
`email was sent more than ${printTimeDuration(CONFIG.EMAIL_CODE_VALID_TIME)} ago`,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
logger.debug('EmailVerificationCode is valid...')
|
logger.debug('EmailVerificationCode is valid...')
|
||||||
@ -493,13 +465,11 @@ export class UserResolver {
|
|||||||
try {
|
try {
|
||||||
// Save user
|
// Save user
|
||||||
await queryRunner.manager.save(user).catch((error) => {
|
await queryRunner.manager.save(user).catch((error) => {
|
||||||
logger.error('error saving user: ' + error)
|
throw new LogError('Error saving user', error)
|
||||||
throw new Error('error saving user: ' + error)
|
|
||||||
})
|
})
|
||||||
// Save userContact
|
// Save userContact
|
||||||
await queryRunner.manager.save(userContact).catch((error) => {
|
await queryRunner.manager.save(userContact).catch((error) => {
|
||||||
logger.error('error saving userContact: ' + error)
|
throw new LogError('Error saving userContact', error)
|
||||||
throw new Error('error saving userContact: ' + error)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
await queryRunner.commitTransaction()
|
await queryRunner.commitTransaction()
|
||||||
@ -510,8 +480,7 @@ export class UserResolver {
|
|||||||
eventProtocol.writeEvent(event.setEventActivateAccount(eventActivateAccount))
|
eventProtocol.writeEvent(event.setEventActivateAccount(eventActivateAccount))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await queryRunner.rollbackTransaction()
|
await queryRunner.rollbackTransaction()
|
||||||
logger.error('Error on writing User and UserContact data:' + e)
|
throw new LogError('Error on writing User and User Contact data', e)
|
||||||
throw e
|
|
||||||
} finally {
|
} finally {
|
||||||
await queryRunner.release()
|
await queryRunner.release()
|
||||||
}
|
}
|
||||||
@ -525,7 +494,7 @@ export class UserResolver {
|
|||||||
`klicktippSignIn(${userContact.email}, ${user.language}, ${user.firstName}, ${user.lastName})`,
|
`klicktippSignIn(${userContact.email}, ${user.language}, ${user.firstName}, ${user.lastName})`,
|
||||||
)
|
)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Error subscribe to klicktipp:' + e)
|
logger.error('Error subscribing to klicktipp', e)
|
||||||
// TODO is this a problem?
|
// TODO is this a problem?
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
/* uncomment this, when you need the activation link on the console
|
/* uncomment this, when you need the activation link on the console
|
||||||
@ -545,11 +514,8 @@ export class UserResolver {
|
|||||||
logger.debug(`found optInCode=${userContact}`)
|
logger.debug(`found optInCode=${userContact}`)
|
||||||
// Code is only valid for `CONFIG.EMAIL_CODE_VALID_TIME` minutes
|
// Code is only valid for `CONFIG.EMAIL_CODE_VALID_TIME` minutes
|
||||||
if (!isEmailVerificationCodeValid(userContact.updatedAt || userContact.createdAt)) {
|
if (!isEmailVerificationCodeValid(userContact.updatedAt || userContact.createdAt)) {
|
||||||
logger.error(
|
throw new LogError(
|
||||||
`email was sent more than ${printTimeDuration(CONFIG.EMAIL_CODE_VALID_TIME)} ago`,
|
`Email was sent more than ${printTimeDuration(CONFIG.EMAIL_CODE_VALID_TIME)} ago`,
|
||||||
)
|
|
||||||
throw new Error(
|
|
||||||
`email was sent more than ${printTimeDuration(CONFIG.EMAIL_CODE_VALID_TIME)} ago`,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
logger.info(`queryOptIn(${optIn}) successful...`)
|
logger.info(`queryOptIn(${optIn}) successful...`)
|
||||||
@ -584,8 +550,7 @@ export class UserResolver {
|
|||||||
|
|
||||||
if (language) {
|
if (language) {
|
||||||
if (!isLanguage(language)) {
|
if (!isLanguage(language)) {
|
||||||
logger.error(`"${language}" isn't a valid language`)
|
throw new LogError('Given language is not a valid language', language)
|
||||||
throw new Error(`"${language}" isn't a valid language`)
|
|
||||||
}
|
}
|
||||||
userEntity.language = language
|
userEntity.language = language
|
||||||
i18n.setLocale(language)
|
i18n.setLocale(language)
|
||||||
@ -594,15 +559,13 @@ export class UserResolver {
|
|||||||
if (password && passwordNew) {
|
if (password && passwordNew) {
|
||||||
// Validate Password
|
// Validate Password
|
||||||
if (!isValidPassword(passwordNew)) {
|
if (!isValidPassword(passwordNew)) {
|
||||||
logger.error('newPassword does not fullfil the rules')
|
throw new LogError(
|
||||||
throw new Error(
|
|
||||||
'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!',
|
'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!verifyPassword(userEntity, password)) {
|
if (!verifyPassword(userEntity, password)) {
|
||||||
logger.error(`Old password is invalid`)
|
throw new LogError(`Old password is invalid`)
|
||||||
throw new Error(`Old password is invalid`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save new password hash and newly encrypted private key
|
// Save new password hash and newly encrypted private key
|
||||||
@ -625,16 +588,14 @@ export class UserResolver {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await queryRunner.manager.save(userEntity).catch((error) => {
|
await queryRunner.manager.save(userEntity).catch((error) => {
|
||||||
logger.error('error saving user: ' + error)
|
throw new LogError('Error saving user', error)
|
||||||
throw new Error('error saving user: ' + error)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
await queryRunner.commitTransaction()
|
await queryRunner.commitTransaction()
|
||||||
logger.debug('writing User data successful...')
|
logger.debug('writing User data successful...')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await queryRunner.rollbackTransaction()
|
await queryRunner.rollbackTransaction()
|
||||||
logger.error(`error on writing updated user data: ${e}`)
|
throw new LogError('Error on writing updated user data', e)
|
||||||
throw e
|
|
||||||
} finally {
|
} finally {
|
||||||
await queryRunner.release()
|
await queryRunner.release()
|
||||||
}
|
}
|
||||||
@ -761,14 +722,12 @@ export class UserResolver {
|
|||||||
const user = await DbUser.findOne({ id: userId })
|
const user = await DbUser.findOne({ id: userId })
|
||||||
// user exists ?
|
// user exists ?
|
||||||
if (!user) {
|
if (!user) {
|
||||||
logger.error(`Could not find user with userId: ${userId}`)
|
throw new LogError('Could not find user with given ID', userId)
|
||||||
throw new Error(`Could not find user with userId: ${userId}`)
|
|
||||||
}
|
}
|
||||||
// administrator user changes own role?
|
// administrator user changes own role?
|
||||||
const moderatorUser = getUser(context)
|
const moderatorUser = getUser(context)
|
||||||
if (moderatorUser.id === userId) {
|
if (moderatorUser.id === userId) {
|
||||||
logger.error('Administrator can not change his own role!')
|
throw new LogError('Administrator can not change his own role')
|
||||||
throw new Error('Administrator can not change his own role!')
|
|
||||||
}
|
}
|
||||||
// change isAdmin
|
// change isAdmin
|
||||||
switch (user.isAdmin) {
|
switch (user.isAdmin) {
|
||||||
@ -776,16 +735,14 @@ export class UserResolver {
|
|||||||
if (isAdmin === true) {
|
if (isAdmin === true) {
|
||||||
user.isAdmin = new Date()
|
user.isAdmin = new Date()
|
||||||
} else {
|
} else {
|
||||||
logger.error('User is already a usual user!')
|
throw new LogError('User is already an usual user')
|
||||||
throw new Error('User is already a usual user!')
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
if (isAdmin === false) {
|
if (isAdmin === false) {
|
||||||
user.isAdmin = null
|
user.isAdmin = null
|
||||||
} else {
|
} else {
|
||||||
logger.error('User is already admin!')
|
throw new LogError('User is already admin')
|
||||||
throw new Error('User is already admin!')
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -803,14 +760,12 @@ export class UserResolver {
|
|||||||
const user = await DbUser.findOne({ id: userId })
|
const user = await DbUser.findOne({ id: userId })
|
||||||
// user exists ?
|
// user exists ?
|
||||||
if (!user) {
|
if (!user) {
|
||||||
logger.error(`Could not find user with userId: ${userId}`)
|
throw new LogError('Could not find user with given ID', userId)
|
||||||
throw new Error(`Could not find user with userId: ${userId}`)
|
|
||||||
}
|
}
|
||||||
// moderator user disabled own account?
|
// moderator user disabled own account?
|
||||||
const moderatorUser = getUser(context)
|
const moderatorUser = getUser(context)
|
||||||
if (moderatorUser.id === userId) {
|
if (moderatorUser.id === userId) {
|
||||||
logger.error('Moderator can not delete his own account!')
|
throw new LogError('Moderator can not delete his own account')
|
||||||
throw new Error('Moderator can not delete his own account!')
|
|
||||||
}
|
}
|
||||||
// soft-delete user
|
// soft-delete user
|
||||||
await user.softRemove()
|
await user.softRemove()
|
||||||
@ -823,12 +778,10 @@ export class UserResolver {
|
|||||||
async unDeleteUser(@Arg('userId', () => Int) userId: number): Promise<Date | null> {
|
async unDeleteUser(@Arg('userId', () => Int) userId: number): Promise<Date | null> {
|
||||||
const user = await DbUser.findOne({ id: userId }, { withDeleted: true })
|
const user = await DbUser.findOne({ id: userId }, { withDeleted: true })
|
||||||
if (!user) {
|
if (!user) {
|
||||||
logger.error(`Could not find user with userId: ${userId}`)
|
throw new LogError('Could not find user with given ID', userId)
|
||||||
throw new Error(`Could not find user with userId: ${userId}`)
|
|
||||||
}
|
}
|
||||||
if (!user.deletedAt) {
|
if (!user.deletedAt) {
|
||||||
logger.error('User is not deleted')
|
throw new LogError('User is not deleted')
|
||||||
throw new Error('User is not deleted')
|
|
||||||
}
|
}
|
||||||
await user.recover()
|
await user.recover()
|
||||||
return null
|
return null
|
||||||
@ -841,17 +794,14 @@ export class UserResolver {
|
|||||||
// const user = await dbUser.findOne({ id: emailContact.userId })
|
// const user = await dbUser.findOne({ id: emailContact.userId })
|
||||||
const user = await findUserByEmail(email)
|
const user = await findUserByEmail(email)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
logger.error(`Could not find User to emailContact: ${email}`)
|
throw new LogError('Could not find user to given email contact', email)
|
||||||
throw new Error(`Could not find User to emailContact: ${email}`)
|
|
||||||
}
|
}
|
||||||
if (user.deletedAt) {
|
if (user.deletedAt) {
|
||||||
logger.error(`User with emailContact: ${email} is deleted.`)
|
throw new LogError('User with given email contact is deleted', email)
|
||||||
throw new Error(`User with emailContact: ${email} is deleted.`)
|
|
||||||
}
|
}
|
||||||
const emailContact = user.emailContact
|
const emailContact = user.emailContact
|
||||||
if (emailContact.deletedAt) {
|
if (emailContact.deletedAt) {
|
||||||
logger.error(`The emailContact: ${email} of this User is deleted.`)
|
throw new LogError('The given email contact for this user is deleted', email)
|
||||||
throw new Error(`The emailContact: ${email} of this User is deleted.`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emailContact.emailResendCount++
|
emailContact.emailResendCount++
|
||||||
@ -888,8 +838,7 @@ export async function findUserByEmail(email: string): Promise<DbUser> {
|
|||||||
{ email: email },
|
{ email: email },
|
||||||
{ withDeleted: true, relations: ['user'] },
|
{ withDeleted: true, relations: ['user'] },
|
||||||
).catch(() => {
|
).catch(() => {
|
||||||
logger.error(`UserContact with email=${email} does not exists`)
|
throw new LogError('No user with this credentials', email)
|
||||||
throw new Error('No user with this credentials')
|
|
||||||
})
|
})
|
||||||
const dbUser = dbUserContact.user
|
const dbUser = dbUserContact.user
|
||||||
dbUser.emailContact = dbUserContact
|
dbUser.emailContact = dbUserContact
|
||||||
@ -904,31 +853,16 @@ async function checkEmailExists(email: string): Promise<boolean> {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
const isTimeExpired = (optIn: LoginEmailOptIn, duration: number): boolean => {
|
|
||||||
const timeElapsed = Date.now() - new Date(optIn.updatedAt).getTime()
|
|
||||||
// time is given in minutes
|
|
||||||
return timeElapsed <= duration * 60 * 1000
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
const isTimeExpired = (updatedAt: Date, duration: number): boolean => {
|
const isTimeExpired = (updatedAt: Date, duration: number): boolean => {
|
||||||
const timeElapsed = Date.now() - new Date(updatedAt).getTime()
|
const timeElapsed = Date.now() - new Date(updatedAt).getTime()
|
||||||
// time is given in minutes
|
// time is given in minutes
|
||||||
return timeElapsed <= duration * 60 * 1000
|
return timeElapsed <= duration * 60 * 1000
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
const isOptInValid = (optIn: LoginEmailOptIn): boolean => {
|
|
||||||
return isTimeExpired(optIn, CONFIG.EMAIL_CODE_VALID_TIME)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
const isEmailVerificationCodeValid = (updatedAt: Date): boolean => {
|
const isEmailVerificationCodeValid = (updatedAt: Date): boolean => {
|
||||||
return isTimeExpired(updatedAt, CONFIG.EMAIL_CODE_VALID_TIME)
|
return isTimeExpired(updatedAt, CONFIG.EMAIL_CODE_VALID_TIME)
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
const canResendOptIn = (optIn: LoginEmailOptIn): boolean => {
|
|
||||||
return !isTimeExpired(optIn, CONFIG.EMAIL_CODE_REQUEST_TIME)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
const canEmailResend = (updatedAt: Date): boolean => {
|
const canEmailResend = (updatedAt: Date): boolean => {
|
||||||
return !isTimeExpired(updatedAt, CONFIG.EMAIL_CODE_REQUEST_TIME)
|
return !isTimeExpired(updatedAt, CONFIG.EMAIL_CODE_REQUEST_TIME)
|
||||||
}
|
}
|
||||||
|
|||||||
26
backend/src/server/LogError.test.ts
Normal file
26
backend/src/server/LogError.test.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { logger } from '@test/testSetup'
|
||||||
|
|
||||||
|
import LogError from './LogError'
|
||||||
|
|
||||||
|
describe('LogError', () => {
|
||||||
|
it('logs an Error when created', () => {
|
||||||
|
/* eslint-disable-next-line no-new */
|
||||||
|
new LogError('new LogError')
|
||||||
|
expect(logger.error).toBeCalledWith('new LogError')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('logs an Error including additional data when created', () => {
|
||||||
|
/* eslint-disable-next-line no-new */
|
||||||
|
new LogError('new LogError', { some: 'data' })
|
||||||
|
expect(logger.error).toBeCalledWith('new LogError', { some: 'data' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not contain additional data in Error object when thrown', () => {
|
||||||
|
try {
|
||||||
|
throw new LogError('new LogError', { someWeirdValue123: 'arbitraryData456' })
|
||||||
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||||
|
} catch (e: any) {
|
||||||
|
expect(e.stack).not.toMatch(/(someWeirdValue123|arbitraryData456)/i)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
9
backend/src/server/LogError.ts
Normal file
9
backend/src/server/LogError.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { backendLogger as logger } from './logger'
|
||||||
|
|
||||||
|
export default class LogError extends Error {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
constructor(msg: string, ...details: any[]) {
|
||||||
|
super(msg)
|
||||||
|
logger.error(msg, ...details)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@
|
|||||||
<b-form
|
<b-form
|
||||||
ref="form"
|
ref="form"
|
||||||
@submit.prevent="submit"
|
@submit.prevent="submit"
|
||||||
class="p-3 bg-white appBoxShadow gradido-border-radius"
|
class="form-style p-3 bg-white appBoxShadow gradido-border-radius"
|
||||||
>
|
>
|
||||||
<label>{{ $t('contribution.selectDate') }}</label>
|
<label>{{ $t('contribution.selectDate') }}</label>
|
||||||
<b-form-datepicker
|
<b-form-datepicker
|
||||||
@ -23,6 +23,13 @@
|
|||||||
<template #nav-next-year><span></span></template>
|
<template #nav-next-year><span></span></template>
|
||||||
</b-form-datepicker>
|
</b-form-datepicker>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="(isThisMonth && maxGddThisMonth <= 0) || (!isThisMonth && maxGddLastMonth <= 0)"
|
||||||
|
class="p-3"
|
||||||
|
>
|
||||||
|
{{ noOpenCreation }}
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
<input-textarea
|
<input-textarea
|
||||||
id="contribution-memo"
|
id="contribution-memo"
|
||||||
v-model="form.memo"
|
v-model="form.memo"
|
||||||
@ -56,17 +63,30 @@
|
|||||||
></input-amount>
|
></input-amount>
|
||||||
|
|
||||||
<b-row class="mt-5">
|
<b-row class="mt-5">
|
||||||
<b-col>
|
<b-col cols="12" lg="6">
|
||||||
<b-button type="reset" variant="secondary" @click="reset" data-test="button-cancel">
|
<b-button
|
||||||
|
block
|
||||||
|
type="reset"
|
||||||
|
variant="secondary"
|
||||||
|
@click="reset"
|
||||||
|
data-test="button-cancel"
|
||||||
|
>
|
||||||
{{ $t('form.cancel') }}
|
{{ $t('form.cancel') }}
|
||||||
</b-button>
|
</b-button>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col class="text-right">
|
<b-col cols="12" lg="6" class="text-right mt-4 mt-lg-0">
|
||||||
<b-button type="submit" variant="gradido" :disabled="disabled" data-test="button-submit">
|
<b-button
|
||||||
|
block
|
||||||
|
type="submit"
|
||||||
|
variant="gradido"
|
||||||
|
:disabled="disabled"
|
||||||
|
data-test="button-submit"
|
||||||
|
>
|
||||||
{{ form.id ? $t('form.change') : $t('contribution.submit') }}
|
{{ form.id ? $t('form.change') : $t('contribution.submit') }}
|
||||||
</b-button>
|
</b-button>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
|
</div>
|
||||||
</b-form>
|
</b-form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -133,6 +153,18 @@ export default {
|
|||||||
validMaxTime() {
|
validMaxTime() {
|
||||||
return Number(this.validMaxGDD / 20)
|
return Number(this.validMaxGDD / 20)
|
||||||
},
|
},
|
||||||
|
noOpenCreation() {
|
||||||
|
if (this.maxGddThisMonth <= 0 && this.maxGddLastMonth <= 0) {
|
||||||
|
return this.$t('contribution.noOpenCreation.allMonth')
|
||||||
|
}
|
||||||
|
if (this.isThisMonth && this.maxGddThisMonth <= 0) {
|
||||||
|
return this.$t('contribution.noOpenCreation.thisMonth')
|
||||||
|
}
|
||||||
|
if (!this.isThisMonth && this.maxGddLastMonth <= 0) {
|
||||||
|
return this.$t('contribution.noOpenCreation.lastMonth')
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
value() {
|
value() {
|
||||||
@ -142,6 +174,9 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
|
.form-style {
|
||||||
|
min-height: 410px;
|
||||||
|
}
|
||||||
span.errors {
|
span.errors {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -57,7 +57,16 @@
|
|||||||
"yourContribution": "Dein Beitrag zum Gemeinwohl"
|
"yourContribution": "Dein Beitrag zum Gemeinwohl"
|
||||||
},
|
},
|
||||||
"lastContribution": "Letzte Beiträge",
|
"lastContribution": "Letzte Beiträge",
|
||||||
|
"noContributions": {
|
||||||
|
"allContributions": "Es wurden noch keine Beiträge eingereicht.",
|
||||||
|
"myContributions": "Du hast noch keine Beiträge eingereicht."
|
||||||
|
},
|
||||||
"noDateSelected": "Wähle irgendein Datum im Monat",
|
"noDateSelected": "Wähle irgendein Datum im Monat",
|
||||||
|
"noOpenCreation": {
|
||||||
|
"allMonth": "Für alle beiden Monate ist dein Schöpfungslimit erreicht. Den Nächsten Monat kannst du wieder 1000 GDD Schöpfen.",
|
||||||
|
"lastMonth": "Für den ausgewählten Monat ist das Schöpfungslimit erreicht.",
|
||||||
|
"thisMonth": "Für den aktuellen Monat ist das Schöpfungslimit erreicht."
|
||||||
|
},
|
||||||
"selectDate": "Wann war dein Beitrag?",
|
"selectDate": "Wann war dein Beitrag?",
|
||||||
"submit": "Einreichen",
|
"submit": "Einreichen",
|
||||||
"submitted": "Der Beitrag wurde eingereicht.",
|
"submitted": "Der Beitrag wurde eingereicht.",
|
||||||
|
|||||||
@ -57,7 +57,16 @@
|
|||||||
"yourContribution": "Your Contributions to the Common Good"
|
"yourContribution": "Your Contributions to the Common Good"
|
||||||
},
|
},
|
||||||
"lastContribution": "Last Contributions",
|
"lastContribution": "Last Contributions",
|
||||||
|
"noContributions": {
|
||||||
|
"allContributions": "No contributions have been submitted yet.",
|
||||||
|
"myContributions": "You have not submitted any entries yet."
|
||||||
|
},
|
||||||
"noDateSelected": "Choose any date in the month",
|
"noDateSelected": "Choose any date in the month",
|
||||||
|
"noOpenCreation": {
|
||||||
|
"allMonth": "For all two months your creation limit is reached. The next month you can create 1000 GDD again.",
|
||||||
|
"lastMonth": "The creation limit is reached for the selected month.",
|
||||||
|
"thisMonth": "The creation limit has been reached for the current month."
|
||||||
|
},
|
||||||
"selectDate": "When was your contribution?",
|
"selectDate": "When was your contribution?",
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"submitted": "The contribution was submitted.",
|
"submitted": "The contribution was submitted.",
|
||||||
|
|||||||
@ -20,6 +20,10 @@
|
|||||||
/>
|
/>
|
||||||
</b-tab>
|
</b-tab>
|
||||||
<b-tab no-body>
|
<b-tab no-body>
|
||||||
|
<div v-if="items.length === 0">
|
||||||
|
{{ $t('contribution.noContributions.myContributions') }}
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
<contribution-list
|
<contribution-list
|
||||||
@closeAllOpenCollapse="closeAllOpenCollapse"
|
@closeAllOpenCollapse="closeAllOpenCollapse"
|
||||||
:items="items"
|
:items="items"
|
||||||
@ -31,8 +35,13 @@
|
|||||||
:showPagination="true"
|
:showPagination="true"
|
||||||
:pageSize="pageSize"
|
:pageSize="pageSize"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</b-tab>
|
</b-tab>
|
||||||
<b-tab no-body>
|
<b-tab no-body>
|
||||||
|
<div v-if="itemsAll.length === 0">
|
||||||
|
{{ $t('contribution.noContributions.allContributions') }}
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
<contribution-list
|
<contribution-list
|
||||||
:items="itemsAll"
|
:items="itemsAll"
|
||||||
@update-list-contributions="updateListAllContributions"
|
@update-list-contributions="updateListAllContributions"
|
||||||
@ -42,6 +51,7 @@
|
|||||||
:pageSize="pageSizeAll"
|
:pageSize="pageSizeAll"
|
||||||
:allContribution="true"
|
:allContribution="true"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</b-tab>
|
</b-tab>
|
||||||
</b-tabs>
|
</b-tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user