mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into 2655-Remove-Overview-Menu-entry
This commit is contained in:
commit
f584ad4e52
19
.github/workflows/test.yml
vendored
19
.github/workflows/test.yml
vendored
@ -360,6 +360,25 @@ jobs:
|
||||
- name: backend | Lint
|
||||
run: docker run --rm gradido/backend:test yarn run lint
|
||||
|
||||
##############################################################################
|
||||
# JOB: LOCALES BACKEND #######################################################
|
||||
##############################################################################
|
||||
locales_backend:
|
||||
name: Locales - Backend
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_test_backend]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
##########################################################################
|
||||
# LOCALES BACKEND #####################################################
|
||||
##########################################################################
|
||||
- name: Backend | Locales
|
||||
run: cd backend && yarn && yarn locales
|
||||
|
||||
##############################################################################
|
||||
# JOB: LINT DATABASE UP ######################################################
|
||||
##############################################################################
|
||||
|
||||
@ -4,8 +4,15 @@ All notable changes to this project will be documented in this file. Dates are d
|
||||
|
||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
|
||||
#### [1.18.2](https://github.com/gradido/gradido/compare/1.18.1...1.18.2)
|
||||
|
||||
- fix(admin): deny contribution button to left [`#2699`](https://github.com/gradido/gradido/pull/2699)
|
||||
|
||||
#### [1.18.1](https://github.com/gradido/gradido/compare/1.18.0...1.18.1)
|
||||
|
||||
> 10 February 2023
|
||||
|
||||
- chore(release): version 1.18.1 [`#2698`](https://github.com/gradido/gradido/pull/2698)
|
||||
- fix(frontend): fix is last month for empty form date [`#2697`](https://github.com/gradido/gradido/pull/2697)
|
||||
- fix(frontend): community link [`#2696`](https://github.com/gradido/gradido/pull/2696)
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
"description": "Administraion Interface for Gradido",
|
||||
"main": "index.js",
|
||||
"author": "Moriz Wahl",
|
||||
"version": "1.18.1",
|
||||
"version": "1.18.2",
|
||||
"license": "Apache-2.0",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
|
||||
@ -259,7 +259,7 @@ describe('CreationConfirm', () => {
|
||||
|
||||
describe('deny creation', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.findAll('tr').at(1).findAll('button').at(2).trigger('click')
|
||||
await wrapper.findAll('tr').at(1).findAll('button').at(1).trigger('click')
|
||||
})
|
||||
|
||||
it('opens the overlay', () => {
|
||||
|
||||
@ -129,6 +129,7 @@ export default {
|
||||
fields() {
|
||||
return [
|
||||
{ key: 'bookmark', label: this.$t('delete') },
|
||||
{ key: 'deny', label: this.$t('deny') },
|
||||
{ key: 'email', label: this.$t('e_mail') },
|
||||
{ key: 'firstName', label: this.$t('firstname') },
|
||||
{ key: 'lastName', label: this.$t('lastname') },
|
||||
@ -149,7 +150,6 @@ export default {
|
||||
},
|
||||
{ key: 'moderator', label: this.$t('moderator') },
|
||||
{ key: 'editCreation', label: this.$t('edit') },
|
||||
{ key: 'deny', label: this.$t('deny') },
|
||||
{ key: 'confirm', label: this.$t('save') },
|
||||
]
|
||||
},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gradido-backend",
|
||||
"version": "1.18.1",
|
||||
"version": "1.18.2",
|
||||
"description": "Gradido unified backend providing an API-Service for Gradido Transactions",
|
||||
"main": "src/index.ts",
|
||||
"repository": "https://github.com/gradido/gradido/backend",
|
||||
@ -15,7 +15,8 @@
|
||||
"lint": "eslint --max-warnings=0 --ext .js,.ts .",
|
||||
"test": "cross-env TZ=UTC NODE_ENV=development jest --runInBand --coverage --forceExit --detectOpenHandles",
|
||||
"seed": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/index.ts",
|
||||
"klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/klicktipp.ts"
|
||||
"klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/klicktipp.ts",
|
||||
"locales": "scripts/sort.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hyperswarm/dht": "^6.2.0",
|
||||
|
||||
25
backend/scripts/sort.sh
Executable file
25
backend/scripts/sort.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
ROOT_DIR=$(dirname "$0")/..
|
||||
|
||||
tmp=$(mktemp)
|
||||
exit_code=0
|
||||
|
||||
for locale_file in $ROOT_DIR/src/locales/*.json
|
||||
do
|
||||
jq -f $(dirname "$0")/sort_filter.jq $locale_file > "$tmp"
|
||||
if [ "$*" == "--fix" ]
|
||||
then
|
||||
mv "$tmp" $locale_file
|
||||
else
|
||||
if diff -q "$tmp" $locale_file > /dev/null ;
|
||||
then
|
||||
: # all good
|
||||
else
|
||||
exit_code=$?
|
||||
echo "$(basename -- $locale_file) is not sorted by keys"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
exit $exit_code
|
||||
13
backend/scripts/sort_filter.jq
Normal file
13
backend/scripts/sort_filter.jq
Normal file
@ -0,0 +1,13 @@
|
||||
def walk(f):
|
||||
. as $in
|
||||
| if type == "object" then
|
||||
reduce keys_unsorted[] as $key
|
||||
( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f
|
||||
elif type == "array" then map( walk(f) ) | f
|
||||
else f
|
||||
end;
|
||||
|
||||
def keys_sort_by(f):
|
||||
to_entries | sort_by(.key|f ) | from_entries;
|
||||
|
||||
walk(if type == "object" then keys_sort_by(ascii_upcase) else . end)
|
||||
@ -42,6 +42,7 @@ import { User } from '@entity/User'
|
||||
import { EventProtocolType } from '@/event/EventProtocolType'
|
||||
import { logger, i18n as localization } from '@test/testSetup'
|
||||
import { UserInputError } from 'apollo-server-express'
|
||||
import { ContributionStatus } from '../enum/ContributionStatus'
|
||||
|
||||
// mock account activation email to avoid console spam
|
||||
jest.mock('@/emails/sendEmailVariants', () => {
|
||||
@ -127,13 +128,13 @@ describe('ContributionResolver', () => {
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('memo text is too short (5 characters minimum)')],
|
||||
errors: [new GraphQLError('Memo text is too short')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith(`memo text is too short: memo.length=4 < 5`)
|
||||
expect(logger.error).toBeCalledWith('Memo text is too short', 4)
|
||||
})
|
||||
|
||||
it('throws error when memo length greater than 255 chars', async () => {
|
||||
@ -150,13 +151,13 @@ describe('ContributionResolver', () => {
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('memo text is too long (255 characters maximum)')],
|
||||
errors: [new GraphQLError('Memo text is too long')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith(`memo text is too long: memo.length=259 > 255`)
|
||||
expect(logger.error).toBeCalledWith('Memo text is too long', 259)
|
||||
})
|
||||
|
||||
it('throws error when creationDate not-valid', async () => {
|
||||
@ -417,31 +418,6 @@ describe('ContributionResolver', () => {
|
||||
resetToken()
|
||||
})
|
||||
|
||||
describe('wrong contribution id', () => {
|
||||
it('throws an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: updateContribution,
|
||||
variables: {
|
||||
contributionId: -1,
|
||||
amount: 100.0,
|
||||
memo: 'Test env contribution',
|
||||
creationDate: new Date().toString(),
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('No contribution found to given id.')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith('No contribution found to given id')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Memo length smaller than 5 chars', () => {
|
||||
it('throws error', async () => {
|
||||
jest.clearAllMocks()
|
||||
@ -458,13 +434,13 @@ describe('ContributionResolver', () => {
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('memo text is too short (5 characters minimum)')],
|
||||
errors: [new GraphQLError('Memo text is too short')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith('memo text is too short: memo.length=4 < 5')
|
||||
expect(logger.error).toBeCalledWith('Memo text is too short', 4)
|
||||
})
|
||||
})
|
||||
|
||||
@ -484,13 +460,38 @@ describe('ContributionResolver', () => {
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('memo text is too long (255 characters maximum)')],
|
||||
errors: [new GraphQLError('Memo text is too long')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith('memo text is too long: memo.length=259 > 255')
|
||||
expect(logger.error).toBeCalledWith('Memo text is too long', 259)
|
||||
})
|
||||
})
|
||||
|
||||
describe('wrong contribution id', () => {
|
||||
it('throws an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: updateContribution,
|
||||
variables: {
|
||||
contributionId: -1,
|
||||
amount: 100.0,
|
||||
memo: 'Test env contribution',
|
||||
creationDate: new Date().toString(),
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Contribution not found')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith('Contribution not found', -1)
|
||||
})
|
||||
})
|
||||
|
||||
@ -516,18 +517,16 @@ describe('ContributionResolver', () => {
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [
|
||||
new GraphQLError(
|
||||
'user of the pending contribution and send user does not correspond',
|
||||
),
|
||||
],
|
||||
errors: [new GraphQLError('Can not update contribution of another user')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'user of the pending contribution and send user does not correspond',
|
||||
'Can not update contribution of another user',
|
||||
expect.any(Object),
|
||||
expect.any(Number),
|
||||
)
|
||||
})
|
||||
})
|
||||
@ -548,12 +547,64 @@ describe('ContributionResolver', () => {
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('An admin is not allowed to update a user contribution.')],
|
||||
errors: [new GraphQLError('An admin is not allowed to update an user contribution')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
// TODO check that the error is logged (need to modify AdminResolver, avoid conflicts)
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'An admin is not allowed to update an user contribution',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('contribution has wrong status', () => {
|
||||
beforeAll(async () => {
|
||||
const contribution = await Contribution.findOneOrFail({
|
||||
id: result.data.createContribution.id,
|
||||
})
|
||||
contribution.contributionStatus = ContributionStatus.DELETED
|
||||
contribution.save()
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const contribution = await Contribution.findOneOrFail({
|
||||
id: result.data.createContribution.id,
|
||||
})
|
||||
contribution.contributionStatus = ContributionStatus.PENDING
|
||||
contribution.save()
|
||||
})
|
||||
|
||||
it('throws an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: updateContribution,
|
||||
variables: {
|
||||
contributionId: result.data.createContribution.id,
|
||||
amount: 10.0,
|
||||
memo: 'Test env contribution',
|
||||
creationDate: new Date().toString(),
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Contribution can not be updated due to status')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'Contribution can not be updated due to status',
|
||||
ContributionStatus.DELETED,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('update too much so that the limit is exceeded', () => {
|
||||
@ -610,16 +661,13 @@ describe('ContributionResolver', () => {
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Currently the month of the contribution cannot change.')],
|
||||
errors: [new GraphQLError('Month of contribution can not be changed')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it.skip('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'No information for available creations with the given creationDate=',
|
||||
'Invalid Date',
|
||||
)
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith('Month of contribution can not be changed')
|
||||
})
|
||||
})
|
||||
|
||||
@ -1153,6 +1201,7 @@ describe('ContributionResolver', () => {
|
||||
|
||||
describe('wrong contribution id', () => {
|
||||
it('returns an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: deleteContribution,
|
||||
@ -1162,18 +1211,19 @@ describe('ContributionResolver', () => {
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Contribution not found for given id.')],
|
||||
errors: [new GraphQLError('Contribution not found')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith('Contribution not found for given id')
|
||||
expect(logger.error).toBeCalledWith('Contribution not found', -1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('other user sends a deleteContribution', () => {
|
||||
it('returns an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
@ -1193,7 +1243,11 @@ describe('ContributionResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith('Can not delete contribution of another user')
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'Can not delete contribution of another user',
|
||||
expect.any(Object),
|
||||
expect.any(Number),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -1269,7 +1323,10 @@ describe('ContributionResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith('A confirmed contribution can not be deleted')
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'A confirmed contribution can not be deleted',
|
||||
expect.objectContaining({ contributionStatus: 'CONFIRMED' }),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1535,15 +1592,13 @@ describe('ContributionResolver', () => {
|
||||
mutate({ mutation: adminCreateContribution, variables }),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Could not find user with email: bibi@bloxberg.de')],
|
||||
errors: [new GraphQLError('Could not find user')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'Could not find user with email: bibi@bloxberg.de',
|
||||
)
|
||||
expect(logger.error).toBeCalledWith('Could not find user', 'bibi@bloxberg.de')
|
||||
})
|
||||
})
|
||||
|
||||
@ -1563,7 +1618,7 @@ describe('ContributionResolver', () => {
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [
|
||||
new GraphQLError('This user was deleted. Cannot create a contribution.'),
|
||||
new GraphQLError('Cannot create contribution since the user was deleted'),
|
||||
],
|
||||
}),
|
||||
)
|
||||
@ -1571,7 +1626,12 @@ describe('ContributionResolver', () => {
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'This user was deleted. Cannot create a contribution.',
|
||||
'Cannot create contribution since the user was deleted',
|
||||
expect.objectContaining({
|
||||
user: expect.objectContaining({
|
||||
deletedAt: new Date('2018-03-14T09:17:52.000Z'),
|
||||
}),
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
@ -1592,7 +1652,9 @@ describe('ContributionResolver', () => {
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [
|
||||
new GraphQLError('Contribution could not be saved, Email is not activated'),
|
||||
new GraphQLError(
|
||||
'Cannot create contribution since the users email is not activated',
|
||||
),
|
||||
],
|
||||
}),
|
||||
)
|
||||
@ -1600,7 +1662,8 @@ describe('ContributionResolver', () => {
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'Contribution could not be saved, Email is not activated',
|
||||
'Cannot create contribution since the users email is not activated',
|
||||
expect.objectContaining({ emailChecked: false }),
|
||||
)
|
||||
})
|
||||
})
|
||||
@ -1619,13 +1682,13 @@ describe('ContributionResolver', () => {
|
||||
mutate({ mutation: adminCreateContribution, variables }),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError(`invalid Date for creationDate=invalid-date`)],
|
||||
errors: [new GraphQLError('CreationDate is invalid')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(`invalid Date for creationDate=invalid-date`)
|
||||
expect(logger.error).toBeCalledWith('CreationDate is invalid', 'invalid-date')
|
||||
})
|
||||
})
|
||||
|
||||
@ -1821,17 +1884,13 @@ describe('ContributionResolver', () => {
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [
|
||||
new GraphQLError('Could not find UserContact with email: bob@baumeister.de'),
|
||||
],
|
||||
errors: [new GraphQLError('Could not find User')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'Could not find UserContact with email: bob@baumeister.de',
|
||||
)
|
||||
expect(logger.error).toBeCalledWith('Could not find User', 'bob@baumeister.de')
|
||||
})
|
||||
})
|
||||
|
||||
@ -1851,13 +1910,13 @@ describe('ContributionResolver', () => {
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('User was deleted (stephen@hawking.uk)')],
|
||||
errors: [new GraphQLError('User was deleted')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('User was deleted (stephen@hawking.uk)')
|
||||
expect(logger.error).toBeCalledWith('User was deleted', 'stephen@hawking.uk')
|
||||
})
|
||||
})
|
||||
|
||||
@ -1877,13 +1936,13 @@ describe('ContributionResolver', () => {
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('No contribution found to given id.')],
|
||||
errors: [new GraphQLError('Contribution not found')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('No contribution found to given id.')
|
||||
expect(logger.error).toBeCalledWith('Contribution not found', -1)
|
||||
})
|
||||
})
|
||||
|
||||
@ -1907,7 +1966,7 @@ describe('ContributionResolver', () => {
|
||||
expect.objectContaining({
|
||||
errors: [
|
||||
new GraphQLError(
|
||||
'user of the pending contribution and send user does not correspond',
|
||||
'User of the pending contribution and send user does not correspond',
|
||||
),
|
||||
],
|
||||
}),
|
||||
@ -1916,7 +1975,7 @@ describe('ContributionResolver', () => {
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'user of the pending contribution and send user does not correspond',
|
||||
'User of the pending contribution and send user does not correspond',
|
||||
)
|
||||
})
|
||||
})
|
||||
@ -2111,13 +2170,13 @@ describe('ContributionResolver', () => {
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Contribution not found for given id.')],
|
||||
errors: [new GraphQLError('Contribution not found')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('Contribution not found for given id: -1')
|
||||
expect(logger.error).toBeCalledWith('Contribution not found', -1)
|
||||
})
|
||||
})
|
||||
|
||||
@ -2237,13 +2296,13 @@ describe('ContributionResolver', () => {
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Contribution not found to given id.')],
|
||||
errors: [new GraphQLError('Contribution not found')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('Contribution not found for given id: -1')
|
||||
expect(logger.error).toBeCalledWith('Contribution not found', -1)
|
||||
})
|
||||
})
|
||||
|
||||
@ -2354,6 +2413,7 @@ describe('ContributionResolver', () => {
|
||||
|
||||
describe('confirm same contribution again', () => {
|
||||
it('throws an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: confirmContribution,
|
||||
@ -2363,11 +2423,18 @@ describe('ContributionResolver', () => {
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Contribution already confirmd.')],
|
||||
errors: [new GraphQLError('Contribution already confirmed')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'Contribution already confirmed',
|
||||
expect.any(Number),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('confirm two creations one after the other quickly', () => {
|
||||
|
||||
@ -55,6 +55,7 @@ import {
|
||||
sendContributionDeniedEmail,
|
||||
} from '@/emails/sendEmailVariants'
|
||||
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||
import LogError from '@/server/LogError'
|
||||
|
||||
import { getLastTransaction } from './util/getLastTransaction'
|
||||
|
||||
@ -67,14 +68,11 @@ export class ContributionResolver {
|
||||
@Ctx() context: Context,
|
||||
): Promise<UnconfirmedContribution> {
|
||||
const clientTimezoneOffset = getClientTimezoneOffset(context)
|
||||
if (memo.length > MEMO_MAX_CHARS) {
|
||||
logger.error(`memo text is too long: memo.length=${memo.length} > ${MEMO_MAX_CHARS}`)
|
||||
throw new Error(`memo text is too long (${MEMO_MAX_CHARS} characters maximum)`)
|
||||
}
|
||||
|
||||
if (memo.length < MEMO_MIN_CHARS) {
|
||||
logger.error(`memo text is too short: memo.length=${memo.length} < ${MEMO_MIN_CHARS}`)
|
||||
throw new Error(`memo text is too short (${MEMO_MIN_CHARS} characters minimum)`)
|
||||
throw new LogError('Memo text is too short', memo.length)
|
||||
}
|
||||
if (memo.length > MEMO_MAX_CHARS) {
|
||||
throw new LogError('Memo text is too long', memo.length)
|
||||
}
|
||||
|
||||
const event = new Event()
|
||||
@ -116,16 +114,13 @@ export class ContributionResolver {
|
||||
const user = getUser(context)
|
||||
const contribution = await DbContribution.findOne(id)
|
||||
if (!contribution) {
|
||||
logger.error('Contribution not found for given id')
|
||||
throw new Error('Contribution not found for given id.')
|
||||
throw new LogError('Contribution not found', id)
|
||||
}
|
||||
if (contribution.userId !== user.id) {
|
||||
logger.error('Can not delete contribution of another user')
|
||||
throw new Error('Can not delete contribution of another user')
|
||||
throw new LogError('Can not delete contribution of another user', contribution, user.id)
|
||||
}
|
||||
if (contribution.confirmedAt) {
|
||||
logger.error('A confirmed contribution can not be deleted')
|
||||
throw new Error('A confirmed contribution can not be deleted')
|
||||
throw new LogError('A confirmed contribution can not be deleted', contribution)
|
||||
}
|
||||
|
||||
contribution.contributionStatus = ContributionStatus.DELETED
|
||||
@ -219,14 +214,11 @@ export class ContributionResolver {
|
||||
@Ctx() context: Context,
|
||||
): Promise<UnconfirmedContribution> {
|
||||
const clientTimezoneOffset = getClientTimezoneOffset(context)
|
||||
if (memo.length > MEMO_MAX_CHARS) {
|
||||
logger.error(`memo text is too long: memo.length=${memo.length} > ${MEMO_MAX_CHARS}`)
|
||||
throw new Error(`memo text is too long (${MEMO_MAX_CHARS} characters maximum)`)
|
||||
}
|
||||
|
||||
if (memo.length < MEMO_MIN_CHARS) {
|
||||
logger.error(`memo text is too short: memo.length=${memo.length} < ${MEMO_MIN_CHARS}`)
|
||||
throw new Error(`memo text is too short (${MEMO_MIN_CHARS} characters minimum)`)
|
||||
throw new LogError('Memo text is too short', memo.length)
|
||||
}
|
||||
if (memo.length > MEMO_MAX_CHARS) {
|
||||
throw new LogError('Memo text is too long', memo.length)
|
||||
}
|
||||
|
||||
const user = getUser(context)
|
||||
@ -235,22 +227,22 @@ export class ContributionResolver {
|
||||
where: { id: contributionId, confirmedAt: IsNull(), deniedAt: IsNull() },
|
||||
})
|
||||
if (!contributionToUpdate) {
|
||||
logger.error('No contribution found to given id')
|
||||
throw new Error('No contribution found to given id.')
|
||||
throw new LogError('Contribution not found', contributionId)
|
||||
}
|
||||
if (contributionToUpdate.userId !== user.id) {
|
||||
logger.error('user of the pending contribution and send user does not correspond')
|
||||
throw new Error('user of the pending contribution and send user does not correspond')
|
||||
throw new LogError(
|
||||
'Can not update contribution of another user',
|
||||
contributionToUpdate,
|
||||
user.id,
|
||||
)
|
||||
}
|
||||
if (
|
||||
contributionToUpdate.contributionStatus !== ContributionStatus.IN_PROGRESS &&
|
||||
contributionToUpdate.contributionStatus !== ContributionStatus.PENDING
|
||||
) {
|
||||
logger.error(
|
||||
`Contribution can not be updated since the state is ${contributionToUpdate.contributionStatus}`,
|
||||
)
|
||||
throw new Error(
|
||||
`Contribution can not be updated since the state is ${contributionToUpdate.contributionStatus}`,
|
||||
throw new LogError(
|
||||
'Contribution can not be updated due to status',
|
||||
contributionToUpdate.contributionStatus,
|
||||
)
|
||||
}
|
||||
const creationDateObj = new Date(creationDate)
|
||||
@ -258,8 +250,7 @@ export class ContributionResolver {
|
||||
if (contributionToUpdate.contributionDate.getMonth() === creationDateObj.getMonth()) {
|
||||
creations = updateCreations(creations, contributionToUpdate, clientTimezoneOffset)
|
||||
} else {
|
||||
logger.error('Currently the month of the contribution cannot change.')
|
||||
throw new Error('Currently the month of the contribution cannot change.')
|
||||
throw new LogError('Month of contribution can not be changed')
|
||||
}
|
||||
|
||||
// all possible cases not to be true are thrown in this function
|
||||
@ -310,29 +301,24 @@ export class ContributionResolver {
|
||||
)
|
||||
const clientTimezoneOffset = getClientTimezoneOffset(context)
|
||||
if (!isValidDateString(creationDate)) {
|
||||
logger.error(`invalid Date for creationDate=${creationDate}`)
|
||||
throw new Error(`invalid Date for creationDate=${creationDate}`)
|
||||
throw new LogError('CreationDate is invalid', creationDate)
|
||||
}
|
||||
const emailContact = await UserContact.findOne({
|
||||
where: { email },
|
||||
withDeleted: true,
|
||||
relations: ['user'],
|
||||
})
|
||||
if (!emailContact) {
|
||||
logger.error(`Could not find user with email: ${email}`)
|
||||
throw new Error(`Could not find user with email: ${email}`)
|
||||
if (!emailContact || !emailContact.user) {
|
||||
throw new LogError('Could not find user', email)
|
||||
}
|
||||
if (emailContact.deletedAt) {
|
||||
logger.error('This emailContact was deleted. Cannot create a contribution.')
|
||||
throw new Error('This emailContact was deleted. Cannot create a contribution.')
|
||||
}
|
||||
if (emailContact.user.deletedAt) {
|
||||
logger.error('This user was deleted. Cannot create a contribution.')
|
||||
throw new Error('This user was deleted. Cannot create a contribution.')
|
||||
if (emailContact.deletedAt || emailContact.user.deletedAt) {
|
||||
throw new LogError('Cannot create contribution since the user was deleted', emailContact)
|
||||
}
|
||||
if (!emailContact.emailChecked) {
|
||||
logger.error('Contribution could not be saved, Email is not activated')
|
||||
throw new Error('Contribution could not be saved, Email is not activated')
|
||||
throw new LogError(
|
||||
'Cannot create contribution since the users email is not activated',
|
||||
emailContact,
|
||||
)
|
||||
}
|
||||
|
||||
const event = new Event()
|
||||
@ -405,18 +391,11 @@ export class ContributionResolver {
|
||||
withDeleted: true,
|
||||
relations: ['user'],
|
||||
})
|
||||
if (!emailContact) {
|
||||
logger.error(`Could not find UserContact with email: ${email}`)
|
||||
throw new Error(`Could not find UserContact with email: ${email}`)
|
||||
if (!emailContact || !emailContact.user) {
|
||||
throw new LogError('Could not find User', email)
|
||||
}
|
||||
const user = emailContact.user
|
||||
if (!user) {
|
||||
logger.error(`Could not find User to emailContact: ${email}`)
|
||||
throw new Error(`Could not find User to emailContact: ${email}`)
|
||||
}
|
||||
if (user.deletedAt) {
|
||||
logger.error(`User was deleted (${email})`)
|
||||
throw new Error(`User was deleted (${email})`)
|
||||
if (emailContact.deletedAt || emailContact.user.deletedAt) {
|
||||
throw new LogError('User was deleted', email)
|
||||
}
|
||||
|
||||
const moderator = getUser(context)
|
||||
@ -425,28 +404,25 @@ export class ContributionResolver {
|
||||
where: { id, confirmedAt: IsNull(), deniedAt: IsNull() },
|
||||
})
|
||||
if (!contributionToUpdate) {
|
||||
logger.error('No contribution found to given id.')
|
||||
throw new Error('No contribution found to given id.')
|
||||
throw new LogError('Contribution not found', id)
|
||||
}
|
||||
|
||||
if (contributionToUpdate.userId !== user.id) {
|
||||
logger.error('user of the pending contribution and send user does not correspond')
|
||||
throw new Error('user of the pending contribution and send user does not correspond')
|
||||
if (contributionToUpdate.userId !== emailContact.user.id) {
|
||||
throw new LogError('User of the pending contribution and send user does not correspond')
|
||||
}
|
||||
|
||||
if (contributionToUpdate.moderatorId === null) {
|
||||
logger.error('An admin is not allowed to update a user contribution.')
|
||||
throw new Error('An admin is not allowed to update a user contribution.')
|
||||
throw new LogError('An admin is not allowed to update an user contribution')
|
||||
}
|
||||
|
||||
const creationDateObj = new Date(creationDate)
|
||||
let creations = await getUserCreation(user.id, clientTimezoneOffset)
|
||||
let creations = await getUserCreation(emailContact.user.id, clientTimezoneOffset)
|
||||
|
||||
// TODO: remove this restriction
|
||||
if (contributionToUpdate.contributionDate.getMonth() === creationDateObj.getMonth()) {
|
||||
creations = updateCreations(creations, contributionToUpdate, clientTimezoneOffset)
|
||||
} else {
|
||||
logger.error('Currently the month of the contribution cannot change.')
|
||||
throw new Error('Currently the month of the contribution cannot change.')
|
||||
throw new LogError('Month of contribution can not be changed')
|
||||
}
|
||||
|
||||
// all possible cases not to be true are thrown in this function
|
||||
@ -464,11 +440,11 @@ export class ContributionResolver {
|
||||
result.memo = contributionToUpdate.memo
|
||||
result.date = contributionToUpdate.contributionDate
|
||||
|
||||
result.creation = await getUserCreation(user.id, clientTimezoneOffset)
|
||||
result.creation = await getUserCreation(emailContact.user.id, clientTimezoneOffset)
|
||||
|
||||
const event = new Event()
|
||||
const eventAdminContributionUpdate = new EventAdminContributionUpdate()
|
||||
eventAdminContributionUpdate.userId = user.id
|
||||
eventAdminContributionUpdate.userId = emailContact.user.id
|
||||
eventAdminContributionUpdate.amount = amount
|
||||
eventAdminContributionUpdate.contributionId = contributionToUpdate.id
|
||||
await writeEvent(event.setEventAdminContributionUpdate(eventAdminContributionUpdate))
|
||||
@ -521,19 +497,17 @@ export class ContributionResolver {
|
||||
): Promise<boolean> {
|
||||
const contribution = await DbContribution.findOne(id)
|
||||
if (!contribution) {
|
||||
logger.error(`Contribution not found for given id: ${id}`)
|
||||
throw new Error('Contribution not found for given id.')
|
||||
throw new LogError('Contribution not found', id)
|
||||
}
|
||||
if (contribution.confirmedAt) {
|
||||
logger.error('A confirmed contribution can not be deleted')
|
||||
throw new Error('A confirmed contribution can not be deleted')
|
||||
throw new LogError('A confirmed contribution can not be deleted')
|
||||
}
|
||||
const moderator = getUser(context)
|
||||
if (
|
||||
contribution.contributionType === ContributionType.USER &&
|
||||
contribution.userId === moderator.id
|
||||
) {
|
||||
throw new Error('Own contribution can not be deleted as admin')
|
||||
throw new LogError('Own contribution can not be deleted as admin')
|
||||
}
|
||||
const user = await DbUser.findOneOrFail(
|
||||
{ id: contribution.userId },
|
||||
@ -575,29 +549,24 @@ export class ContributionResolver {
|
||||
const clientTimezoneOffset = getClientTimezoneOffset(context)
|
||||
const contribution = await DbContribution.findOne(id)
|
||||
if (!contribution) {
|
||||
logger.error(`Contribution not found for given id: ${id}`)
|
||||
throw new Error('Contribution not found to given id.')
|
||||
throw new LogError('Contribution not found', id)
|
||||
}
|
||||
if (contribution.confirmedAt) {
|
||||
logger.error(`Contribution already confirmd: ${id}`)
|
||||
throw new Error('Contribution already confirmd.')
|
||||
throw new LogError('Contribution already confirmed', id)
|
||||
}
|
||||
if (contribution.contributionStatus === 'DENIED') {
|
||||
logger.error(`Contribution already denied: ${id}`)
|
||||
throw new Error('Contribution already denied.')
|
||||
throw new LogError('Contribution already denied', id)
|
||||
}
|
||||
const moderatorUser = getUser(context)
|
||||
if (moderatorUser.id === contribution.userId) {
|
||||
logger.error('Moderator can not confirm own contribution')
|
||||
throw new Error('Moderator can not confirm own contribution')
|
||||
throw new LogError('Moderator can not confirm own contribution')
|
||||
}
|
||||
const user = await DbUser.findOneOrFail(
|
||||
{ id: contribution.userId },
|
||||
{ withDeleted: true, relations: ['emailContact'] },
|
||||
)
|
||||
if (user.deletedAt) {
|
||||
logger.error('This user was deleted. Cannot confirm a contribution.')
|
||||
throw new Error('This user was deleted. Cannot confirm a contribution.')
|
||||
throw new LogError('Can not confirm contribution since the user was deleted')
|
||||
}
|
||||
const creations = await getUserCreation(contribution.userId, clientTimezoneOffset, false)
|
||||
validateContribution(
|
||||
@ -661,8 +630,7 @@ export class ContributionResolver {
|
||||
})
|
||||
} catch (e) {
|
||||
await queryRunner.rollbackTransaction()
|
||||
logger.error('Creation was not successful', e)
|
||||
throw new Error('Creation was not successful.')
|
||||
throw new LogError('Creation was not successful', e)
|
||||
} finally {
|
||||
await queryRunner.release()
|
||||
}
|
||||
@ -737,17 +705,16 @@ export class ContributionResolver {
|
||||
deniedBy: IsNull(),
|
||||
})
|
||||
if (!contributionToUpdate) {
|
||||
logger.error(`Contribution not found for given id: ${id}`)
|
||||
throw new Error(`Contribution not found for given id.`)
|
||||
throw new LogError('Contribution not found', id)
|
||||
}
|
||||
if (
|
||||
contributionToUpdate.contributionStatus !== ContributionStatus.IN_PROGRESS &&
|
||||
contributionToUpdate.contributionStatus !== ContributionStatus.PENDING
|
||||
) {
|
||||
logger.error(
|
||||
`Contribution state (${contributionToUpdate.contributionStatus}) is not allowed.`,
|
||||
throw new LogError(
|
||||
'Status of the contribution is not allowed',
|
||||
contributionToUpdate.contributionStatus,
|
||||
)
|
||||
throw new Error(`State of the contribution is not allowed.`)
|
||||
}
|
||||
const moderator = getUser(context)
|
||||
const user = await DbUser.findOne(
|
||||
@ -755,10 +722,7 @@ export class ContributionResolver {
|
||||
{ relations: ['emailContact'] },
|
||||
)
|
||||
if (!user) {
|
||||
logger.error(
|
||||
`Could not find User for the Contribution (userId: ${contributionToUpdate.userId}).`,
|
||||
)
|
||||
throw new Error('Could not find User for the Contribution.')
|
||||
throw new LogError('Could not find User of the Contribution', contributionToUpdate.userId)
|
||||
}
|
||||
|
||||
contributionToUpdate.contributionStatus = ContributionStatus.DENIED
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
{
|
||||
"emails": {
|
||||
"addedContributionMessage": {
|
||||
"commonGoodContributionMessage": "du hast zu deinem Gemeinwohl-Beitrag „{contributionMemo}“ eine Nachricht von {senderFirstName} {senderLastName} erhalten.",
|
||||
"subject": "Gradido: Nachricht zu deinem Gemeinwohl-Beitrag",
|
||||
"toSeeAndAnswerMessage": "Um die Nachricht zu sehen und darauf zu antworten, gehe in deinem Gradido-Konto ins Menü „Gemeinschaft“ auf den Tab „Meine Beiträge zum Gemeinwohl“!"
|
||||
},
|
||||
"accountActivation": {
|
||||
"duration": "Der Link hat eine Gültigkeit von {hours} Stunden und {minutes} Minuten. Sollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen:",
|
||||
"emailRegistered": "deine E-Mail-Adresse wurde soeben bei Gradido registriert.",
|
||||
@ -19,6 +14,11 @@
|
||||
"onForgottenPasswordCopyLink": "oder kopiere den obigen Link in dein Browserfenster.",
|
||||
"subject": "Gradido: Erneuter Registrierungsversuch mit deiner E-Mail"
|
||||
},
|
||||
"addedContributionMessage": {
|
||||
"commonGoodContributionMessage": "du hast zu deinem Gemeinwohl-Beitrag „{contributionMemo}“ eine Nachricht von {senderFirstName} {senderLastName} erhalten.",
|
||||
"subject": "Gradido: Nachricht zu deinem Gemeinwohl-Beitrag",
|
||||
"toSeeAndAnswerMessage": "Um die Nachricht zu sehen und darauf zu antworten, gehe in deinem Gradido-Konto ins Menü „Gemeinschaft“ auf den Tab „Meine Beiträge zum Gemeinwohl“!"
|
||||
},
|
||||
"contributionConfirmed": {
|
||||
"commonGoodContributionConfirmed": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde soeben von {senderFirstName} {senderLastName} bestätigt und in deinem Gradido-Konto gutgeschrieben.",
|
||||
"subject": "Gradido: Dein Gemeinwohl-Beitrag wurde bestätigt"
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
{
|
||||
"emails": {
|
||||
"addedContributionMessage": {
|
||||
"commonGoodContributionMessage": "you have received a message from {senderFirstName} {senderLastName} regarding your common good contribution “{contributionMemo}”.",
|
||||
"subject": "Gradido: Message about your common good contribution",
|
||||
"toSeeAndAnswerMessage": "To view and reply to the message, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!"
|
||||
},
|
||||
"accountActivation": {
|
||||
"duration": "The link has a validity of {hours} hours and {minutes} minutes. If the validity of the link has already expired, you can have a new link sent to you here:",
|
||||
"emailRegistered": "Your email address has just been registered with Gradido.",
|
||||
@ -19,6 +14,11 @@
|
||||
"onForgottenPasswordCopyLink": "or copy the link above into your browser window.",
|
||||
"subject": "Gradido: Try To Register Again With Your Email"
|
||||
},
|
||||
"addedContributionMessage": {
|
||||
"commonGoodContributionMessage": "you have received a message from {senderFirstName} {senderLastName} regarding your common good contribution “{contributionMemo}”.",
|
||||
"subject": "Gradido: Message about your common good contribution",
|
||||
"toSeeAndAnswerMessage": "To view and reply to the message, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!"
|
||||
},
|
||||
"contributionConfirmed": {
|
||||
"commonGoodContributionConfirmed": "Your public good contribution “{contributionMemo}” has just been confirmed by {senderFirstName} {senderLastName} and credited to your Gradido account.",
|
||||
"subject": "Gradido: Your contribution to the common good was confirmed"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gradido-database",
|
||||
"version": "1.18.1",
|
||||
"version": "1.18.2",
|
||||
"description": "Gradido Database Tool to execute database migrations",
|
||||
"main": "src/index.ts",
|
||||
"repository": "https://github.com/gradido/gradido/database",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bootstrap-vue-gradido-wallet",
|
||||
"version": "1.18.1",
|
||||
"version": "1.18.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node run/server.js",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gradido",
|
||||
"version": "1.18.1",
|
||||
"version": "1.18.2",
|
||||
"description": "Gradido",
|
||||
"main": "index.js",
|
||||
"repository": "git@github.com:gradido/gradido.git",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user