From 574906eb7602afcd75d4aef11291f6ed053feb76 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 24 Jan 2023 10:27:46 +0100 Subject: [PATCH 01/91] Add filterState parameter to listAllContributions query, can be a string or null and checks in the DB for the ContributionStatus. --- .../graphql/resolver/ContributionResolver.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index a71e4767e..e9c949dba 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -1,6 +1,6 @@ import Decimal from 'decimal.js-light' import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql' -import { FindOperator, IsNull, In, getConnection } from '@dbTools/typeorm' +import { FindOperator, IsNull, In, getConnection, Equal } from '@dbTools/typeorm' import { Contribution as DbContribution } from '@entity/Contribution' import { ContributionMessage } from '@entity/ContributionMessage' @@ -179,12 +179,27 @@ export class ContributionResolver { async listAllContributions( @Args() { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, + @Arg('filterState', () => String, { nullable: true }) + filterState: string | null, ): Promise { + const where: { + contributionStatus?: FindOperator | null + } = {} + const typeStatus = Object.values(ContributionStatus) + if (filterState !== null) { + // Asked ChatGBT for explanation => filterState converted to ContributionStatus + const contributionStatus = ContributionStatus[filterState as keyof typeof ContributionStatus] + if (typeStatus.includes(contributionStatus)) { + where.contributionStatus = Equal(filterState) + } + } + const [dbContributions, count] = await getConnection() .createQueryBuilder() .select('c') .from(DbContribution, 'c') .innerJoinAndSelect('c.user', 'u') + .where(where) .orderBy('c.createdAt', order) .limit(pageSize) .offset((currentPage - 1) * pageSize) From f4c743f7b4e30118cbe35e2faec55becf117e3df Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 24 Jan 2023 11:33:29 +0100 Subject: [PATCH 02/91] Add localization, change filterState from string to array of string. --- .../graphql/resolver/ContributionResolver.ts | 38 ++++++++++++++++--- backend/src/locales/de.json | 5 +++ backend/src/locales/en.json | 6 +++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index e9c949dba..759d3b1a1 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -1,4 +1,5 @@ import Decimal from 'decimal.js-light' +import i18n from 'i18n' import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql' import { FindOperator, IsNull, In, getConnection, Equal } from '@dbTools/typeorm' @@ -179,18 +180,43 @@ export class ContributionResolver { async listAllContributions( @Args() { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, - @Arg('filterState', () => String, { nullable: true }) - filterState: string | null, + @Arg('filterState', () => [String], { nullable: true }) + filterStates: string[] | null, + @Ctx() context: Context, ): Promise { const where: { contributionStatus?: FindOperator | null } = {} const typeStatus = Object.values(ContributionStatus) - if (filterState !== null) { + const filterStateArray = [] + const user = getUser(context) + i18n.setLocale(user.language) + if (filterStates !== null) { // Asked ChatGBT for explanation => filterState converted to ContributionStatus - const contributionStatus = ContributionStatus[filterState as keyof typeof ContributionStatus] - if (typeStatus.includes(contributionStatus)) { - where.contributionStatus = Equal(filterState) + const length = filterStates.length + let i = 0 + for (i; i < length; i++) { + const filterState = filterStates[i].toUpperCase() + logger.info('filterState', filterState) + const contributionStatus = + ContributionStatus[filterState as keyof typeof ContributionStatus] + logger.info('contributionStatus', contributionStatus) + if (!typeStatus.includes(contributionStatus)) { + logger.error( + `${i18n.__('error.contributions.wrongFilterState', { + contributionState: filterStates[i], + })}`, + ) + throw new Error( + `${i18n.__('error.contributions.wrongFilterState', { + contributionState: filterStates[i], + })}`, + ) + } + filterStateArray.push(contributionStatus) + } + if (filterStateArray.length > 0) { + where.contributionStatus = In(filterStateArray) } } diff --git a/backend/src/locales/de.json b/backend/src/locales/de.json index 38b53508b..d547fd951 100644 --- a/backend/src/locales/de.json +++ b/backend/src/locales/de.json @@ -55,6 +55,11 @@ "subject": "Gradido: {senderFirstName} {senderLastName} hat dir {transactionAmount} Gradido gesendet" } }, + "error": { + "contributions": { + "wrongFilterState": "Nicht definierter Status wurde gesendet: {contributionState}" + } + }, "general": { "decimalSeparator": ",", "imprint": "Gradido-Akademie\nInstitut für Wirtschaftsbionik\nPfarrweg 2\n74653 Künzelsau\nDeutschland", diff --git a/backend/src/locales/en.json b/backend/src/locales/en.json index ac1ad380f..f6ada3716 100644 --- a/backend/src/locales/en.json +++ b/backend/src/locales/en.json @@ -1,4 +1,5 @@ { + "contributions": {}, "emails": { "addedContributionMessage": { "commonGoodContributionMessage": "you have received a message from {senderFirstName} {senderLastName} regarding your common good contribution “{contributionMemo}”.", @@ -55,6 +56,11 @@ "subject": "Gradido: {senderFirstName} {senderLastName} has sent you {transactionAmount} Gradido" } }, + "error": { + "contributions": { + "wrongFilterState": "Send a state that isn't defined: {contributionState}" + } + }, "general": { "decimalSeparator": ".", "imprint": "Gradido-Akademie\nInstitut für Wirtschaftsbionik\nPfarrweg 2\n74653 Künzelsau\nDeutschland", From 1554a7d454957eae0fdee7b0415f8401fef4f0ea Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 24 Jan 2023 12:04:09 +0100 Subject: [PATCH 03/91] Remove test loggings. --- backend/src/graphql/resolver/ContributionResolver.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 759d3b1a1..68bfddeb9 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -197,10 +197,8 @@ export class ContributionResolver { let i = 0 for (i; i < length; i++) { const filterState = filterStates[i].toUpperCase() - logger.info('filterState', filterState) const contributionStatus = ContributionStatus[filterState as keyof typeof ContributionStatus] - logger.info('contributionStatus', contributionStatus) if (!typeStatus.includes(contributionStatus)) { logger.error( `${i18n.__('error.contributions.wrongFilterState', { From d42804acfb4900c9b7914aad42e819a5db6eaa89 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 24 Jan 2023 12:10:53 +0100 Subject: [PATCH 04/91] Change type of filterState from string[] to ContributionStatus[]. --- .../src/graphql/resolver/ContributionResolver.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 68bfddeb9..bc1706e0c 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -180,26 +180,23 @@ export class ContributionResolver { async listAllContributions( @Args() { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, - @Arg('filterState', () => [String], { nullable: true }) - filterStates: string[] | null, + @Arg('filterState', () => [ContributionStatus], { nullable: true }) + filterStates: ContributionStatus[] | null, @Ctx() context: Context, ): Promise { const where: { contributionStatus?: FindOperator | null } = {} const typeStatus = Object.values(ContributionStatus) - const filterStateArray = [] const user = getUser(context) i18n.setLocale(user.language) if (filterStates !== null) { - // Asked ChatGBT for explanation => filterState converted to ContributionStatus + const filterStateArray = [] const length = filterStates.length let i = 0 for (i; i < length; i++) { - const filterState = filterStates[i].toUpperCase() - const contributionStatus = - ContributionStatus[filterState as keyof typeof ContributionStatus] - if (!typeStatus.includes(contributionStatus)) { + const filterState = filterStates[i] + if (!typeStatus.includes(filterState)) { logger.error( `${i18n.__('error.contributions.wrongFilterState', { contributionState: filterStates[i], @@ -211,7 +208,7 @@ export class ContributionResolver { })}`, ) } - filterStateArray.push(contributionStatus) + filterStateArray.push(filterState) } if (filterStateArray.length > 0) { where.contributionStatus = In(filterStateArray) From b940400e5b4e0716ed00128885fa32e95d2ce879 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 24 Jan 2023 14:26:43 +0100 Subject: [PATCH 05/91] Remove unused import. --- backend/src/graphql/resolver/ContributionResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index bc1706e0c..148cbceb8 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -1,7 +1,7 @@ import Decimal from 'decimal.js-light' import i18n from 'i18n' import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql' -import { FindOperator, IsNull, In, getConnection, Equal } from '@dbTools/typeorm' +import { FindOperator, IsNull, In, getConnection } from '@dbTools/typeorm' import { Contribution as DbContribution } from '@entity/Contribution' import { ContributionMessage } from '@entity/ContributionMessage' From 2cecadded222b8f06739c198f9bb53df478b0870 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 24 Jan 2023 17:02:08 +0100 Subject: [PATCH 06/91] Add filterState to the backend test listAllContributions query. --- backend/src/graphql/resolver/ContributionResolver.test.ts | 4 ++-- backend/src/seeds/graphql/queries.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index abae8e446..7b4186fb4 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -681,7 +681,7 @@ describe('ContributionResolver', () => { currentPage: 1, pageSize: 25, order: 'DESC', - filterConfirmed: false, + filterState: null, }, }), ).resolves.toEqual( @@ -726,7 +726,7 @@ describe('ContributionResolver', () => { currentPage: 1, pageSize: 25, order: 'DESC', - filterConfirmed: false, + filterState: null, }, }), ).resolves.toEqual( diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index 97f871235..b99b39b40 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -172,8 +172,8 @@ export const listContributions = gql` ` export const listAllContributions = ` -query ($currentPage: Int = 1, $pageSize: Int = 5, $order: Order = DESC) { - listAllContributions(currentPage: $currentPage, pageSize: $pageSize, order: $order) { +query ($currentPage: Int = 1, $pageSize: Int = 5, $order: Order = DESC, $filterState: [ContributionStatus!] = null) { + listAllContributions(currentPage: $currentPage, pageSize: $pageSize, order: $order, filterState: $filterState) { contributionCount contributionList { id From cc0fe5968d5eddc048991a318aef3dd0c79a8fcd Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 25 Jan 2023 07:54:17 +0100 Subject: [PATCH 07/91] Tests that the filterState parameter in listAllContributions works as expected. --- .../resolver/ContributionResolver.test.ts | 111 ++++++++++++++++++ backend/src/seeds/graphql/queries.ts | 19 +-- 2 files changed, 123 insertions(+), 7 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 7b4186fb4..04749d8f0 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -45,6 +45,7 @@ import { Transaction as DbTransaction } from '@entity/Transaction' import { User } from '@entity/User' import { EventProtocolType } from '@/event/EventProtocolType' import { logger, i18n as localization } from '@test/testSetup' +import { ContributionStatus } from '@enum/ContributionStatus' // mock account activation email to avoid console spam // mock account activation email to avoid console spam @@ -737,11 +738,121 @@ describe('ContributionResolver', () => { contributionList: expect.arrayContaining([ expect.objectContaining({ id: expect.any(Number), + state: 'CONFIRMED', memo: 'Herzlich Willkommen bei Gradido!', amount: '1000', }), expect.objectContaining({ id: expect.any(Number), + state: 'PENDING', + memo: 'Test env contribution', + amount: '100', + }), + ]), + }, + }, + }), + ) + }) + + it('returns all CONFIRMED Creation', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterState: ['CONFIRMED'], + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listAllContributions: { + contributionCount: 1, + contributionList: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + state: 'CONFIRMED', + memo: 'Herzlich Willkommen bei Gradido!', + amount: '1000', + }), + expect.not.objectContaining({ + id: expect.any(Number), + state: 'PENDING', + memo: 'Test env contribution', + amount: '100', + }), + ]), + }, + }, + }), + ) + }) + + it('returns all PENDING Creation', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterState: ['PENDING'], + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listAllContributions: { + contributionCount: 1, + contributionList: expect.arrayContaining([ + expect.not.objectContaining({ + id: expect.any(Number), + state: 'CONFIRMED', + memo: 'Herzlich Willkommen bei Gradido!', + amount: '1000', + }), + expect.objectContaining({ + id: expect.any(Number), + state: 'PENDING', + memo: 'Test env contribution', + amount: '100', + }), + ]), + }, + }, + }), + ) + }) + + it('returns all DENIED Creation', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterState: ['DENIED'], + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listAllContributions: { + contributionCount: 0, + contributionList: expect.not.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + state: 'CONFIRMED', + memo: 'Herzlich Willkommen bei Gradido!', + amount: '1000', + }), + expect.objectContaining({ + id: expect.any(Number), + state: 'PENDING', memo: 'Test env contribution', amount: '100', }), diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index b99b39b40..17a66951a 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -177,13 +177,18 @@ query ($currentPage: Int = 1, $pageSize: Int = 5, $order: Order = DESC, $filterS contributionCount contributionList { id - firstName - lastName - amount - memo - createdAt - confirmedAt - confirmedBy + firstName + lastName + amount + memo + createdAt + confirmedAt + confirmedBy + contributionDate + state + messagesCount + deniedAt + deniedBy } } } From 47eea57c0943729bf235633d5746c1fa6efce0d5 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 25 Jan 2023 08:00:34 +0100 Subject: [PATCH 08/91] Remove unused import. --- backend/src/graphql/resolver/ContributionResolver.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 04749d8f0..3d251ced1 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -45,7 +45,6 @@ import { Transaction as DbTransaction } from '@entity/Transaction' import { User } from '@entity/User' import { EventProtocolType } from '@/event/EventProtocolType' import { logger, i18n as localization } from '@test/testSetup' -import { ContributionStatus } from '@enum/ContributionStatus' // mock account activation email to avoid console spam // mock account activation email to avoid console spam From 9e7da0f451fd18920880d589baffee2fc0a73a5d Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 25 Jan 2023 08:11:47 +0100 Subject: [PATCH 09/91] Add tests for filterState parameter in listAllContributions query. --- .../resolver/ContributionResolver.test.ts | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 3d251ced1..f65a8d5a5 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -754,6 +754,42 @@ describe('ContributionResolver', () => { ) }) + it('returns allCreation', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterState: [], + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listAllContributions: { + contributionCount: 2, + contributionList: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + state: 'CONFIRMED', + memo: 'Herzlich Willkommen bei Gradido!', + amount: '1000', + }), + expect.objectContaining({ + id: expect.any(Number), + state: 'PENDING', + memo: 'Test env contribution', + amount: '100', + }), + ]), + }, + }, + }), + ) + }) + it('returns all CONFIRMED Creation', async () => { await expect( query({ @@ -826,6 +862,42 @@ describe('ContributionResolver', () => { ) }) + it('returns all IN_PROGRESS Creation', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterState: ['IN_PROGRESS'], + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listAllContributions: { + contributionCount: 0, + contributionList: expect.not.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + state: 'CONFIRMED', + memo: 'Herzlich Willkommen bei Gradido!', + amount: '1000', + }), + expect.objectContaining({ + id: expect.any(Number), + state: 'PENDING', + memo: 'Test env contribution', + amount: '100', + }), + ]), + }, + }, + }), + ) + }) + it('returns all DENIED Creation', async () => { await expect( query({ @@ -861,6 +933,78 @@ describe('ContributionResolver', () => { }), ) }) + + it('returns all DELETED Creation', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterState: ['DELETED'], + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listAllContributions: { + contributionCount: 0, + contributionList: expect.not.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + state: 'CONFIRMED', + memo: 'Herzlich Willkommen bei Gradido!', + amount: '1000', + }), + expect.objectContaining({ + id: expect.any(Number), + state: 'PENDING', + memo: 'Test env contribution', + amount: '100', + }), + ]), + }, + }, + }), + ) + }) + + it('returns all CONFIRMED and PENDING Creation', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterState: ['CONFIRMED', 'PENDING'], + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listAllContributions: { + contributionCount: 2, + contributionList: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + state: 'CONFIRMED', + memo: 'Herzlich Willkommen bei Gradido!', + amount: '1000', + }), + expect.objectContaining({ + id: expect.any(Number), + state: 'PENDING', + memo: 'Test env contribution', + amount: '100', + }), + ]), + }, + }, + }), + ) + }) }) }) From 8b3423b91b2cd632befe4adb5d2b9ed2d0b87e16 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 25 Jan 2023 16:40:32 +0100 Subject: [PATCH 10/91] Remove i18n, change state to status. --- .../graphql/resolver/ContributionResolver.ts | 35 +++++++------------ backend/src/locales/de.json | 5 --- backend/src/locales/en.json | 5 --- 3 files changed, 12 insertions(+), 33 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 36d7c2237..799747013 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -180,38 +180,27 @@ export class ContributionResolver { async listAllContributions( @Args() { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, - @Arg('filterState', () => [ContributionStatus], { nullable: true }) - filterStates: ContributionStatus[] | null, - @Ctx() context: Context, + @Arg('statusFilter', () => [ContributionStatus], { nullable: true }) + statusFilters: ContributionStatus[] | null, ): Promise { const where: { contributionStatus?: FindOperator | null } = {} const typeStatus = Object.values(ContributionStatus) - const user = getUser(context) - i18n.setLocale(user.language) - if (filterStates !== null) { - const filterStateArray = [] - const length = filterStates.length + if (statusFilters !== null) { + const statusFilterArray = [] + const length = statusFilters.length let i = 0 for (i; i < length; i++) { - const filterState = filterStates[i] - if (!typeStatus.includes(filterState)) { - logger.error( - `${i18n.__('error.contributions.wrongFilterState', { - contributionState: filterStates[i], - })}`, - ) - throw new Error( - `${i18n.__('error.contributions.wrongFilterState', { - contributionState: filterStates[i], - })}`, - ) + const statusFilter = statusFilters[i] + if (!typeStatus.includes(statusFilter)) { + logger.error(`Nicht definierter Status wurde gesendet: ${statusFilters[i]}`) + throw new Error(`Nicht definierter Status wurde gesendet: ${statusFilters[i]}`) } - filterStateArray.push(filterState) + statusFilterArray.push(statusFilter) } - if (filterStateArray.length > 0) { - where.contributionStatus = In(filterStateArray) + if (statusFilterArray.length > 0) { + where.contributionStatus = In(statusFilterArray) } } diff --git a/backend/src/locales/de.json b/backend/src/locales/de.json index d547fd951..38b53508b 100644 --- a/backend/src/locales/de.json +++ b/backend/src/locales/de.json @@ -55,11 +55,6 @@ "subject": "Gradido: {senderFirstName} {senderLastName} hat dir {transactionAmount} Gradido gesendet" } }, - "error": { - "contributions": { - "wrongFilterState": "Nicht definierter Status wurde gesendet: {contributionState}" - } - }, "general": { "decimalSeparator": ",", "imprint": "Gradido-Akademie\nInstitut für Wirtschaftsbionik\nPfarrweg 2\n74653 Künzelsau\nDeutschland", diff --git a/backend/src/locales/en.json b/backend/src/locales/en.json index be26a8467..fa45c6635 100644 --- a/backend/src/locales/en.json +++ b/backend/src/locales/en.json @@ -56,11 +56,6 @@ "subject": "Gradido: {senderFirstName} {senderLastName} has sent you {transactionAmount} Gradido" } }, - "error": { - "contributions": { - "wrongFilterState": "Send a state that isn't defined: {contributionState}" - } - }, "general": { "decimalSeparator": ".", "imprint": "Gradido-Akademie\nInstitut für Wirtschaftsbionik\nPfarrweg 2\n74653 Künzelsau\nDeutschland", From 0bd3ce27dfdc277edc3007e3b840a45b2d2291ef Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 25 Jan 2023 16:48:49 +0100 Subject: [PATCH 11/91] Change Tests to statusFilter. --- .../resolver/ContributionResolver.test.ts | 18 +++++++++--------- backend/src/seeds/graphql/queries.ts | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index f65a8d5a5..7958fd0cb 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -681,7 +681,7 @@ describe('ContributionResolver', () => { currentPage: 1, pageSize: 25, order: 'DESC', - filterState: null, + statusFilter: null, }, }), ).resolves.toEqual( @@ -726,7 +726,7 @@ describe('ContributionResolver', () => { currentPage: 1, pageSize: 25, order: 'DESC', - filterState: null, + statusFilter: null, }, }), ).resolves.toEqual( @@ -762,7 +762,7 @@ describe('ContributionResolver', () => { currentPage: 1, pageSize: 25, order: 'DESC', - filterState: [], + statusFilter: [], }, }), ).resolves.toEqual( @@ -798,7 +798,7 @@ describe('ContributionResolver', () => { currentPage: 1, pageSize: 25, order: 'DESC', - filterState: ['CONFIRMED'], + statusFilter: ['CONFIRMED'], }, }), ).resolves.toEqual( @@ -834,7 +834,7 @@ describe('ContributionResolver', () => { currentPage: 1, pageSize: 25, order: 'DESC', - filterState: ['PENDING'], + statusFilter: ['PENDING'], }, }), ).resolves.toEqual( @@ -870,7 +870,7 @@ describe('ContributionResolver', () => { currentPage: 1, pageSize: 25, order: 'DESC', - filterState: ['IN_PROGRESS'], + statusFilter: ['IN_PROGRESS'], }, }), ).resolves.toEqual( @@ -906,7 +906,7 @@ describe('ContributionResolver', () => { currentPage: 1, pageSize: 25, order: 'DESC', - filterState: ['DENIED'], + statusFilter: ['DENIED'], }, }), ).resolves.toEqual( @@ -942,7 +942,7 @@ describe('ContributionResolver', () => { currentPage: 1, pageSize: 25, order: 'DESC', - filterState: ['DELETED'], + statusFilter: ['DELETED'], }, }), ).resolves.toEqual( @@ -978,7 +978,7 @@ describe('ContributionResolver', () => { currentPage: 1, pageSize: 25, order: 'DESC', - filterState: ['CONFIRMED', 'PENDING'], + statusFilter: ['CONFIRMED', 'PENDING'], }, }), ).resolves.toEqual( diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index 17a66951a..9ee04254d 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -172,8 +172,8 @@ export const listContributions = gql` ` export const listAllContributions = ` -query ($currentPage: Int = 1, $pageSize: Int = 5, $order: Order = DESC, $filterState: [ContributionStatus!] = null) { - listAllContributions(currentPage: $currentPage, pageSize: $pageSize, order: $order, filterState: $filterState) { +query ($currentPage: Int = 1, $pageSize: Int = 5, $order: Order = DESC, $statusFilter: [ContributionStatus!] = null) { + listAllContributions(currentPage: $currentPage, pageSize: $pageSize, order: $order, statusFilter: $statusFilter) { contributionCount contributionList { id From 12703a33f9eaf0d8e0ed857c9abbff6986e08a90 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 26 Jan 2023 07:06:57 +0100 Subject: [PATCH 12/91] Remove unused i18n import. --- backend/src/graphql/resolver/ContributionResolver.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 799747013..56f389b08 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -1,5 +1,4 @@ import Decimal from 'decimal.js-light' -import i18n from 'i18n' import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql' import { FindOperator, IsNull, In, getConnection } from '@dbTools/typeorm' From 311fb974679706c2f8639f4a77372707ea9b044b Mon Sep 17 00:00:00 2001 From: elweyn Date: Sat, 28 Jan 2023 08:10:50 +0100 Subject: [PATCH 13/91] Refactor filter method. --- .../graphql/resolver/ContributionResolver.ts | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 56f389b08..728079bab 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -185,22 +185,9 @@ export class ContributionResolver { const where: { contributionStatus?: FindOperator | null } = {} - const typeStatus = Object.values(ContributionStatus) - if (statusFilters !== null) { - const statusFilterArray = [] - const length = statusFilters.length - let i = 0 - for (i; i < length; i++) { - const statusFilter = statusFilters[i] - if (!typeStatus.includes(statusFilter)) { - logger.error(`Nicht definierter Status wurde gesendet: ${statusFilters[i]}`) - throw new Error(`Nicht definierter Status wurde gesendet: ${statusFilters[i]}`) - } - statusFilterArray.push(statusFilter) - } - if (statusFilterArray.length > 0) { - where.contributionStatus = In(statusFilterArray) - } + + if (statusFilters !== null && statusFilters.length) { + where.contributionStatus = In(statusFilters) } const [dbContributions, count] = await getConnection() From e5a86cd12505b70f28bacf07517c07f37fab84b6 Mon Sep 17 00:00:00 2001 From: elweyn Date: Sat, 28 Jan 2023 08:12:26 +0100 Subject: [PATCH 14/91] Correct en local file. --- backend/src/locales/en.json | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/locales/en.json b/backend/src/locales/en.json index fa45c6635..5cde70d26 100644 --- a/backend/src/locales/en.json +++ b/backend/src/locales/en.json @@ -1,5 +1,4 @@ { - "contributions": {}, "emails": { "addedContributionMessage": { "commonGoodContributionMessage": "you have received a message from {senderFirstName} {senderLastName} regarding your common good contribution “{contributionMemo}”.", From d5605d3afb4b2cf83dcc534b085ad43ee32a3ef9 Mon Sep 17 00:00:00 2001 From: elweyn Date: Sat, 28 Jan 2023 08:36:46 +0100 Subject: [PATCH 15/91] Add test for error case wrong statusFilter value. --- .../resolver/ContributionResolver.test.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 7958fd0cb..738c6fc25 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -45,6 +45,7 @@ import { Transaction as DbTransaction } from '@entity/Transaction' import { User } from '@entity/User' import { EventProtocolType } from '@/event/EventProtocolType' import { logger, i18n as localization } from '@test/testSetup' +import { UserInputError } from 'apollo-server-express' // mock account activation email to avoid console spam // mock account activation email to avoid console spam @@ -718,6 +719,28 @@ describe('ContributionResolver', () => { resetToken() }) + it('throws an error with non existing filter', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + statusFilter: ['CONFIRMD'], + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + new UserInputError( + 'Variable "$statusFilter" got invalid value "CONFIRMD" at "statusFilter[0]"; Value "CONFIRMD" does not exist in "ContributionStatus" enum. Did you mean the enum value "CONFIRMED"?', + ), + ], + }), + ) + }) + it('returns allCreation', async () => { await expect( query({ From c63e6fd474ba80dc9383190a18e148df231d42db Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 31 Jan 2023 11:10:02 +0100 Subject: [PATCH 16/91] Add tests for listAllContributions statusFilter parameter. --- .../resolver/ContributionResolver.test.ts | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 738c6fc25..be4b91713 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -727,21 +727,56 @@ describe('ContributionResolver', () => { currentPage: 1, pageSize: 25, order: 'DESC', - statusFilter: ['CONFIRMD'], + statusFilter: ['NOT_VALID'], }, }), ).resolves.toEqual( expect.objectContaining({ errors: [ new UserInputError( - 'Variable "$statusFilter" got invalid value "CONFIRMD" at "statusFilter[0]"; Value "CONFIRMD" does not exist in "ContributionStatus" enum. Did you mean the enum value "CONFIRMED"?', + 'Variable "$statusFilter" got invalid value "NOT_VALID" at "statusFilter[0]"; Value "NOT_VALID" does not exist in "ContributionStatus" enum.', ), ], }), ) }) - it('returns allCreation', async () => { + it('returns allCreation without statusFilter', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listAllContributions: { + contributionCount: 2, + contributionList: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + state: 'CONFIRMED', + memo: 'Herzlich Willkommen bei Gradido!', + amount: '1000', + }), + expect.objectContaining({ + id: expect.any(Number), + state: 'PENDING', + memo: 'Test env contribution', + amount: '100', + }), + ]), + }, + }, + }), + ) + }) + + it('returns allCreation for statusFilter = null', async () => { await expect( query({ query: listAllContributions, @@ -777,7 +812,7 @@ describe('ContributionResolver', () => { ) }) - it('returns allCreation', async () => { + it('returns allCreation for statusFilter = []', async () => { await expect( query({ query: listAllContributions, From 70649b968f22a7149227297e6d679e3339593e20 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 31 Jan 2023 11:11:18 +0100 Subject: [PATCH 17/91] Change statusFilter from default null to optional. --- backend/src/graphql/resolver/ContributionResolver.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 728079bab..6a5240cd1 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -180,13 +180,13 @@ export class ContributionResolver { @Args() { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, @Arg('statusFilter', () => [ContributionStatus], { nullable: true }) - statusFilters: ContributionStatus[] | null, + statusFilters?: ContributionStatus[], ): Promise { const where: { contributionStatus?: FindOperator | null } = {} - if (statusFilters !== null && statusFilters.length) { + if (statusFilters && statusFilters.length) { where.contributionStatus = In(statusFilters) } From 75d47d016d1edb83f1f1c2d4b29bac0deee77816 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 31 Jan 2023 11:19:43 +0100 Subject: [PATCH 18/91] Remove the default null of statusFilter or the test query. --- backend/src/seeds/graphql/queries.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index 9ee04254d..385a69479 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -172,7 +172,7 @@ export const listContributions = gql` ` export const listAllContributions = ` -query ($currentPage: Int = 1, $pageSize: Int = 5, $order: Order = DESC, $statusFilter: [ContributionStatus!] = null) { +query ($currentPage: Int = 1, $pageSize: Int = 5, $order: Order = DESC, $statusFilter: [ContributionStatus!]) { listAllContributions(currentPage: $currentPage, pageSize: $pageSize, order: $order, statusFilter: $statusFilter) { contributionCount contributionList { From 787b2b95529c9a2c3d44f6f9678abb5a4f083151 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 31 Jan 2023 11:38:37 +0100 Subject: [PATCH 19/91] Add test for value null in statusFilter array. --- .../resolver/ContributionResolver.test.ts | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index be4b91713..6c9c1136b 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -719,7 +719,7 @@ describe('ContributionResolver', () => { resetToken() }) - it('throws an error with non existing filter', async () => { + it('throws an error with "NOT_VALID" in statusFilter', async () => { await expect( query({ query: listAllContributions, @@ -741,6 +741,53 @@ describe('ContributionResolver', () => { ) }) + it('throws an error with a null in statusFilter', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + statusFilter: [null], + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + new UserInputError( + 'Variable "$statusFilter" got invalid value null at "statusFilter[0]"; Expected non-nullable type "ContributionStatus!" not to be null.', + ), + ], + }), + ) + }) + + it('throws an error with null and "NOT_VALID" in statusFilter', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + statusFilter: [null, 'NOT_VALID'], + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + new UserInputError( + 'Variable "$statusFilter" got invalid value null at "statusFilter[0]"; Expected non-nullable type "ContributionStatus!" not to be null.', + ), + new UserInputError( + 'Variable "$statusFilter" got invalid value "NOT_VALID" at "statusFilter[1]"; Value "NOT_VALID" does not exist in "ContributionStatus" enum.', + ), + ], + }), + ) + }) + it('returns allCreation without statusFilter', async () => { await expect( query({ From a1ece2f171b9efaf1c1a8eb762600435743ef206 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 31 Jan 2023 11:39:44 +0100 Subject: [PATCH 20/91] Change definition of the parameter statusFilter so that the ContributionStatus item can't be null. --- backend/src/graphql/resolver/ContributionResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 6a5240cd1..623ee19f0 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -179,7 +179,7 @@ export class ContributionResolver { async listAllContributions( @Args() { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, - @Arg('statusFilter', () => [ContributionStatus], { nullable: true }) + @Arg('statusFilter', () => [ContributionStatus!], { nullable: true }) statusFilters?: ContributionStatus[], ): Promise { const where: { From 681a23a1132c8fab50f8fee17ce5530ecdc1fef2 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 31 Jan 2023 11:50:25 +0100 Subject: [PATCH 21/91] Change the definition of statusFilter remove --- backend/src/graphql/resolver/ContributionResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 623ee19f0..6a5240cd1 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -179,7 +179,7 @@ export class ContributionResolver { async listAllContributions( @Args() { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, - @Arg('statusFilter', () => [ContributionStatus!], { nullable: true }) + @Arg('statusFilter', () => [ContributionStatus], { nullable: true }) statusFilters?: ContributionStatus[], ): Promise { const where: { From 9c4de72be6ea761c22d3a1c2dd3c40a3d53ce79e Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 31 Jan 2023 13:30:45 +0100 Subject: [PATCH 22/91] LogError class --- backend/src/server/LogError.ts | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 backend/src/server/LogError.ts diff --git a/backend/src/server/LogError.ts b/backend/src/server/LogError.ts new file mode 100644 index 000000000..b753d204e --- /dev/null +++ b/backend/src/server/LogError.ts @@ -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) + } +} From 25e03c35b2a610946c592707ad3b75ce9b4b48c5 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 31 Jan 2023 13:31:10 +0100 Subject: [PATCH 23/91] refactor UserResolver to use LogError, clean comments --- backend/src/graphql/resolver/UserResolver.ts | 131 ++++++------------- 1 file changed, 38 insertions(+), 93 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index c630c240a..773e1bac1 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -63,6 +63,7 @@ import { isValidPassword } from '@/password/EncryptorUtils' import { FULL_CREATION_AVAILABLE } from './const/const' import { encryptPassword, verifyPassword } from '@/password/PasswordEncryptor' import { PasswordEncryptionType } from '../enum/PasswordEncryptionType' +import LogError from '@/server/LogError' // eslint-disable-next-line @typescript-eslint/no-var-requires const sodium = require('sodium-native') @@ -134,22 +135,18 @@ export class UserResolver { email = email.trim().toLowerCase() const dbUser = await findUserByEmail(email) if (dbUser.deletedAt) { - logger.error('The User was permanently deleted in database.') - throw new Error('This user was permanently deleted. Contact support for questions.') + throw new LogError('This user was permanently deleted. Contact support for questions', dbUser) } if (!dbUser.emailContact.emailChecked) { - logger.error('The Users email is not validate yet.') - throw new Error('User email not validated') + throw new LogError('The Users email is not validate yet', dbUser) } 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 - 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)) { - logger.error('The User has no valid credentials.') - throw new Error('No user with this credentials') + throw new LogError('No user with this credentials', dbUser) } if (dbUser.passwordEncryptionType !== PasswordEncryptionType.GRADIDO_ID) { @@ -309,30 +306,19 @@ export class UserResolver { await queryRunner.startTransaction('REPEATABLE READ') try { dbUser = await queryRunner.manager.save(dbUser).catch((error) => { - logger.error('Error while saving dbUser', error) - throw new Error('error saving user') + throw new LogError('Error while saving dbUser', error) }) let emailContact = newEmailContact(email, dbUser.id) emailContact = await queryRunner.manager.save(emailContact).catch((error) => { - logger.error('Error while saving emailContact', error) - throw new Error('error saving email user contact') + throw new LogError('Error while saving user email contact', error) }) dbUser.emailContact = emailContact dbUser.emailId = emailContact.id await queryRunner.manager.save(dbUser).catch((error) => { - logger.error('Error while updating dbUser', error) - throw new Error('error updating user') + throw new LogError('Error while updating dbUser', error) }) - /* - 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( /{optin}/g, emailContact.emailVerificationCode.toString(), @@ -358,9 +344,8 @@ export class UserResolver { await queryRunner.commitTransaction() logger.addContext('user', dbUser.id) } catch (e) { - logger.error(`error during create user with ${e}`) await queryRunner.rollbackTransaction() - throw e + throw new LogError(`Error during create user with error: ${e}`, e) } finally { await queryRunner.release() } @@ -392,13 +377,8 @@ export class UserResolver { } if (!canEmailResend(user.emailContact.updatedAt || user.emailContact.createdAt)) { - logger.error( - `email already sent less than ${printTimeDuration( - CONFIG.EMAIL_CODE_REQUEST_TIME, - )} minutes ago`, - ) - throw new Error( - `email already sent less than ${printTimeDuration( + throw new LogError( + `Email already sent less than ${printTimeDuration( CONFIG.EMAIL_CODE_REQUEST_TIME, )} minutes ago`, ) @@ -409,8 +389,7 @@ export class UserResolver { user.emailContact.emailVerificationCode = random(64) user.emailContact.emailOptInTypeId = OptInType.EMAIL_OPT_IN_RESET_PASSWORD await user.emailContact.save().catch(() => { - logger.error('Unable to save email verification code= ' + user.emailContact) - throw new Error('Unable to save email verification code.') + throw new LogError('Unable to save email verification code', user.emailContact) }) logger.info(`optInCode for ${email}=${user.emailContact}`) @@ -445,33 +424,22 @@ export class UserResolver { logger.info(`setPassword(${code}, ***)...`) // Validate Password if (!isValidPassword(password)) { - logger.error('Password entered is lexically invalid') - throw new Error( + throw new LogError( '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 - /* - const optInCode = await LoginEmailOptIn.findOneOrFail({ verificationCode: code }).catch(() => { - logger.error('Could not login with emailVerificationCode') - throw new Error('Could not login with emailVerificationCode') - }) - */ + // load code const userContact = await DbUserContact.findOneOrFail( { emailVerificationCode: code }, { relations: ['user'] }, ).catch(() => { - logger.error('Could not login with emailVerificationCode') - throw new Error('Could not login with emailVerificationCode') + throw new LogError('Could not login with emailVerificationCode') }) logger.debug('userContact loaded...') // Code is only valid for `CONFIG.EMAIL_CODE_VALID_TIME` minutes if (!isEmailVerificationCodeValid(userContact.updatedAt || userContact.createdAt)) { - logger.error( - `email was sent more than ${printTimeDuration(CONFIG.EMAIL_CODE_VALID_TIME)} ago`, - ) - throw new Error( + throw new LogError( `email was sent more than ${printTimeDuration(CONFIG.EMAIL_CODE_VALID_TIME)} ago`, ) } @@ -498,13 +466,11 @@ export class UserResolver { try { // Save user await queryRunner.manager.save(user).catch((error) => { - logger.error('error saving user: ' + error) - throw new Error('error saving user: ' + error) + throw new LogError(`Error saving user: ${error}`, error) }) // Save userContact await queryRunner.manager.save(userContact).catch((error) => { - logger.error('error saving userContact: ' + error) - throw new Error('error saving userContact: ' + error) + throw new LogError(`error saving userContact: ${error}`, error) }) await queryRunner.commitTransaction() @@ -515,8 +481,7 @@ export class UserResolver { eventProtocol.writeEvent(event.setEventActivateAccount(eventActivateAccount)) } catch (e) { await queryRunner.rollbackTransaction() - logger.error('Error on writing User and UserContact data:' + e) - throw e + throw new LogError(`Error on writing User and UserContact data: ${e}`, e) } finally { await queryRunner.release() } @@ -530,7 +495,7 @@ export class UserResolver { `klicktippSignIn(${userContact.email}, ${user.language}, ${user.firstName}, ${user.lastName})`, ) } catch (e) { - logger.error('Error subscribe to klicktipp:' + e) + logger.error(`Error subscribe to klicktipp: ${e}`, e) // TODO is this a problem? // eslint-disable-next-line no-console /* uncomment this, when you need the activation link on the console @@ -550,10 +515,7 @@ export class UserResolver { logger.debug(`found optInCode=${userContact}`) // Code is only valid for `CONFIG.EMAIL_CODE_VALID_TIME` minutes if (!isEmailVerificationCodeValid(userContact.updatedAt || userContact.createdAt)) { - logger.error( - `email was sent more than ${printTimeDuration(CONFIG.EMAIL_CODE_VALID_TIME)} ago`, - ) - throw new Error( + throw new LogError( `email was sent more than ${printTimeDuration(CONFIG.EMAIL_CODE_VALID_TIME)} ago`, ) } @@ -589,8 +551,7 @@ export class UserResolver { if (language) { if (!isLanguage(language)) { - logger.error(`"${language}" isn't a valid language`) - throw new Error(`"${language}" isn't a valid language`) + throw new LogError(`"${language}" isn't a valid language`, language) } userEntity.language = language i18n.setLocale(language) @@ -599,15 +560,13 @@ export class UserResolver { if (password && passwordNew) { // Validate Password if (!isValidPassword(passwordNew)) { - logger.error('newPassword does not fullfil the rules') - throw new Error( + throw new LogError( '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)) { - logger.error(`Old password is invalid`) - throw new Error(`Old password is invalid`) + throw new LogError(`Old password is invalid`) } // Save new password hash and newly encrypted private key @@ -630,16 +589,14 @@ export class UserResolver { try { await queryRunner.manager.save(userEntity).catch((error) => { - logger.error('error saving user: ' + error) - throw new Error('error saving user: ' + error) + throw new LogError(`error saving user: ${error}`, error) }) await queryRunner.commitTransaction() logger.debug('writing User data successful...') } catch (e) { await queryRunner.rollbackTransaction() - logger.error(`error on writing updated user data: ${e}`) - throw e + throw new LogError(`error on writing updated user data: ${e}`, e) } finally { await queryRunner.release() } @@ -766,14 +723,12 @@ export class UserResolver { const user = await DbUser.findOne({ id: userId }) // user exists ? if (!user) { - logger.error(`Could not find user with userId: ${userId}`) - throw new Error(`Could not find user with userId: ${userId}`) + throw new LogError(`Could not find user with userId: ${userId}`, userId) } // administrator user changes own role? const moderatorUser = getUser(context) if (moderatorUser.id === userId) { - logger.error('Administrator can not change his own role!') - throw new Error('Administrator can not change his own role!') + throw new LogError('Administrator can not change his own role') } // change isAdmin switch (user.isAdmin) { @@ -781,16 +736,14 @@ export class UserResolver { if (isAdmin === true) { user.isAdmin = new Date() } else { - logger.error('User is already a usual user!') - throw new Error('User is already a usual user!') + throw new LogError('User is already an usual user') } break default: if (isAdmin === false) { user.isAdmin = null } else { - logger.error('User is already admin!') - throw new Error('User is already admin!') + throw new LogError('User is already admin') } break } @@ -808,14 +761,12 @@ export class UserResolver { const user = await DbUser.findOne({ id: userId }) // user exists ? if (!user) { - logger.error(`Could not find user with userId: ${userId}`) - throw new Error(`Could not find user with userId: ${userId}`) + throw new LogError(`Could not find user with userId: ${userId}`, userId) } // moderator user disabled own account? const moderatorUser = getUser(context) if (moderatorUser.id === userId) { - logger.error('Moderator can not delete his own account!') - throw new Error('Moderator can not delete his own account!') + throw new LogError('Moderator can not delete his own account') } // soft-delete user await user.softRemove() @@ -828,12 +779,10 @@ export class UserResolver { async unDeleteUser(@Arg('userId', () => Int) userId: number): Promise { const user = await DbUser.findOne({ id: userId }, { withDeleted: true }) if (!user) { - logger.error(`Could not find user with userId: ${userId}`) - throw new Error(`Could not find user with userId: ${userId}`) + throw new LogError(`Could not find user with userId: ${userId}`, userId) } if (!user.deletedAt) { - logger.error('User is not deleted') - throw new Error('User is not deleted') + throw new LogError('User is not deleted') } await user.recover() return null @@ -846,17 +795,14 @@ export class UserResolver { // const user = await dbUser.findOne({ id: emailContact.userId }) const user = await findUserByEmail(email) if (!user) { - logger.error(`Could not find User to emailContact: ${email}`) - throw new Error(`Could not find User to emailContact: ${email}`) + throw new LogError(`Could not find User to emailContact: ${email}`, email) } if (user.deletedAt) { - logger.error(`User with emailContact: ${email} is deleted.`) - throw new Error(`User with emailContact: ${email} is deleted.`) + throw new LogError(`User with emailContact: ${email} is deleted`, email) } const emailContact = user.emailContact if (emailContact.deletedAt) { - logger.error(`The emailContact: ${email} of this User is deleted.`) - throw new Error(`The emailContact: ${email} of this User is deleted.`) + throw new LogError(`The emailContact: ${email} of this User is deleted`, email) } emailContact.emailResendCount++ @@ -893,8 +839,7 @@ export async function findUserByEmail(email: string): Promise { { email: email }, { withDeleted: true, relations: ['user'] }, ).catch(() => { - logger.error(`UserContact with email=${email} does not exists`) - throw new Error('No user with this credentials') + throw new LogError('No user with this credentials', email) }) const dbUser = dbUserContact.user dbUser.emailContact = dbUserContact From 412499d5f2d3d3200ce743df44c7c002a739c26f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 31 Jan 2023 21:04:39 +0100 Subject: [PATCH 24/91] fix unit tests for UserResolver --- .../src/graphql/resolver/UserResolver.test.ts | 60 ++++++++++++------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 073bad04d..3b2ff0a73 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -549,7 +549,9 @@ describe('UserResolver', () => { }) 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', () => { - expect(logger.error).toBeCalledWith( - 'UserContact with email=bibi@bloxberg.de does not exists', - ) + expect(logger.error).toBeCalledWith('No user with this credentials', variables.email) }) }) @@ -668,7 +668,7 @@ describe('UserResolver', () => { }) 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) }) }) }) @@ -828,7 +828,7 @@ describe('UserResolver', () => { expect.objectContaining({ errors: [ new GraphQLError( - `email already sent less than ${printTimeDuration( + `Email already sent less than ${printTimeDuration( CONFIG.EMAIL_CODE_REQUEST_TIME, )} minutes ago`, ), @@ -870,13 +870,13 @@ describe('UserResolver', () => { CONFIG.EMAIL_CODE_REQUEST_TIME = emailCodeRequestTime await expect(mutate({ mutation: forgotPassword, variables })).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('email already sent less than 10 minutes minutes ago')], + errors: [new GraphQLError('Email already sent less than 10 minutes minutes ago')], }), ) }) it('logs the error found', () => { - expect(logger.error).toBeCalledWith(`email already sent less than 10 minutes minutes ago`) + expect(logger.error).toBeCalledWith(`Email already sent less than 10 minutes minutes ago`) }) }) }) @@ -1007,7 +1007,7 @@ describe('UserResolver', () => { }) it('logs the error found', () => { - expect(logger.error).toBeCalledWith(`"not-valid" isn't a valid language`) + expect(logger.error).toBeCalledWith(`"not-valid" isn't a valid language`, 'not-valid') }) }) @@ -1058,7 +1058,9 @@ describe('UserResolver', () => { }) 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 +1118,7 @@ describe('UserResolver', () => { }) 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!') }) }) }) @@ -1328,7 +1330,10 @@ describe('UserResolver', () => { }) 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 userId: ${admin.id + 1}`, + admin.id + 1, + ) }) }) @@ -1379,12 +1384,12 @@ describe('UserResolver', () => { mutate({ mutation: setUserRole, variables: { userId: admin.id, isAdmin: false } }), ).resolves.toEqual( 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', () => { - expect(logger.error).toBeCalledWith('Administrator can not change his own role!') + expect(logger.error).toBeCalledWith('Administrator can not change his own role') }) }) @@ -1400,13 +1405,13 @@ describe('UserResolver', () => { mutate({ mutation: setUserRole, variables: { userId: user.id, isAdmin: true } }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('User is already admin!')], + errors: [new GraphQLError('User is already admin')], }), ) }) it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith('User is already admin!') + expect(logger.error).toBeCalledWith('User is already admin') }) }) @@ -1421,13 +1426,13 @@ describe('UserResolver', () => { mutate({ mutation: setUserRole, variables: { userId: user.id, isAdmin: false } }), ).resolves.toEqual( 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', () => { - expect(logger.error).toBeCalledWith('User is already a usual user!') + expect(logger.error).toBeCalledWith('User is already an usual user') }) }) }) @@ -1500,7 +1505,10 @@ describe('UserResolver', () => { }) 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 userId: ${admin.id + 1}`, + admin.id + 1, + ) }) }) @@ -1511,13 +1519,13 @@ describe('UserResolver', () => { mutate({ mutation: deleteUser, variables: { userId: admin.id } }), ).resolves.toEqual( 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', () => { - expect(logger.error).toBeCalledWith('Moderator can not delete his own account!') + expect(logger.error).toBeCalledWith('Moderator can not delete his own account') }) }) @@ -1551,7 +1559,10 @@ describe('UserResolver', () => { }) 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 userId: ${user.id}`, + user.id, + ) }) }) }) @@ -1623,7 +1634,10 @@ describe('UserResolver', () => { }) 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 userId: ${admin.id + 1}`, + admin.id + 1, + ) }) }) From 45a92e637a190753a81d36d3e021df5758528d56 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 31 Jan 2023 21:07:07 +0100 Subject: [PATCH 25/91] deleted old commented code --- backend/src/graphql/resolver/UserResolver.ts | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 773e1bac1..fe33d9287 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -854,31 +854,16 @@ async function checkEmailExists(email: string): Promise { 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 timeElapsed = Date.now() - new Date(updatedAt).getTime() // time is given in minutes return timeElapsed <= duration * 60 * 1000 } -/* -const isOptInValid = (optIn: LoginEmailOptIn): boolean => { - return isTimeExpired(optIn, CONFIG.EMAIL_CODE_VALID_TIME) -} -*/ + const isEmailVerificationCodeValid = (updatedAt: Date): boolean => { 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 => { return !isTimeExpired(updatedAt, CONFIG.EMAIL_CODE_REQUEST_TIME) } From 702a39af9ae9ffa179d7760dc677d00997d7b6bf Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 31 Jan 2023 21:34:30 +0100 Subject: [PATCH 26/91] test case deleted User --- .../src/graphql/resolver/UserResolver.test.ts | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 3b2ff0a73..b1b1b015f 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -671,6 +671,40 @@ describe('UserResolver', () => { expect(logger.error).toBeCalledWith('No user with this credentials', variables.email) }) }) + + describe('user is in database but deleted', () => { + beforeAll(async () => { + jest.clearAllMocks() + const user = await userFactory(testEnv, bibiBloxberg) + // Hint: softRemove does not soft-delete the email contact of this user + await user.softRemove() + 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: bibiBloxberg.firstName, + lastName: bibiBloxberg.lastName, + }), + ) + }) + }) }) describe('logout', () => { From d0d45aaf6a67e91ed8ddaa8cb8f47f05393a4313 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 31 Jan 2023 22:11:58 +0100 Subject: [PATCH 27/91] more tests and a potential bug --- .../src/graphql/resolver/UserResolver.test.ts | 77 ++++++++++++++++++- backend/src/graphql/resolver/UserResolver.ts | 1 + 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index b1b1b015f..8f8c5757e 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -675,9 +675,82 @@ describe('UserResolver', () => { 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) - // Hint: softRemove does not soft-delete the email contact of this user - await user.softRemove() + user.password = BigInt(0) + await user.save() result = await mutate({ mutation: login, variables }) }) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index fe33d9287..b50cf3af5 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -140,6 +140,7 @@ export class UserResolver { if (!dbUser.emailContact.emailChecked) { throw new LogError('The Users email is not validate yet', dbUser) } + // TODO: at least in test this does not work since `dbUser.password = 0` and `BigInto(0) = 0n` if (dbUser.password === BigInt(0)) { // TODO we want to catch this on the frontend and ask the user to check his emails or resend code throw new LogError('The User has not set a password yet', dbUser) From 6e663f609329f8d507a6607a7844069e78955708 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 31 Jan 2023 22:13:13 +0100 Subject: [PATCH 28/91] even tho skipped have the correct error message in test --- backend/src/graphql/resolver/UserResolver.test.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 8f8c5757e..28f5d816d 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -761,16 +761,14 @@ describe('UserResolver', () => { it('returns an error', () => { expect(result).toEqual( expect.objectContaining({ - errors: [ - new GraphQLError('This user was permanently deleted. Contact support for questions'), - ], + errors: [new GraphQLError('The User has not set a password yet')], }), ) }) it('logs the error thrown', () => { expect(logger.error).toBeCalledWith( - 'This user was permanently deleted. Contact support for questions', + 'The User has not set a password yet', expect.objectContaining({ firstName: bibiBloxberg.firstName, lastName: bibiBloxberg.lastName, From 945295bb410a72379523b342510bd1c6724c1ea2 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 31 Jan 2023 22:18:43 +0100 Subject: [PATCH 29/91] lint fixes --- backend/src/graphql/resolver/UserResolver.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 28f5d816d..fba468217 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -1223,7 +1223,9 @@ describe('UserResolver', () => { }) it('logs the error thrown', () => { - 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!') + 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!', + ) }) }) }) From 7d304593f66668d7d65be2e3502518e407219fc7 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 31 Jan 2023 22:44:31 +0100 Subject: [PATCH 30/91] LogError test --- backend/src/server/LogError.test.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 backend/src/server/LogError.test.ts diff --git a/backend/src/server/LogError.test.ts b/backend/src/server/LogError.test.ts new file mode 100644 index 000000000..ef8d6a23c --- /dev/null +++ b/backend/src/server/LogError.test.ts @@ -0,0 +1,23 @@ +import { logger } from '@test/testSetup' + +import LogError from './LogError' + +describe('LogError', () => { + it('logs an Error when created', () => { + new LogError('new LogError') + expect(logger.error).toBeCalledWith('new LogError') + }) + + it('logs an Error including additional data when created', () => { + 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' }) + } catch (e: any) { + expect(e.stack).not.toMatch(/(someWeirdValue123|arbitraryData456)/i) + } + }) +}) From 5de22d46e6451e30c68e114fc092c0227b8b1f96 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 31 Jan 2023 22:58:14 +0100 Subject: [PATCH 31/91] fix another test --- backend/src/graphql/resolver/EmailOptinCodes.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/EmailOptinCodes.test.ts b/backend/src/graphql/resolver/EmailOptinCodes.test.ts index d7c0b9bd6..e4e96e6a1 100644 --- a/backend/src/graphql/resolver/EmailOptinCodes.test.ts +++ b/backend/src/graphql/resolver/EmailOptinCodes.test.ts @@ -96,7 +96,7 @@ describe('EmailOptinCodes', () => { mutate({ mutation: forgotPassword, variables: { email: 'peter@lustig.de' } }), ).resolves.toMatchObject({ data: null, - errors: [new GraphQLError('email already sent less than 10 minutes minutes ago')], + errors: [new GraphQLError('Email already sent less than 10 minutes minutes ago')], }) }) From 7845ffd8e45055d2ec0ebec7db3d81c14a4d7a07 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 31 Jan 2023 23:02:17 +0100 Subject: [PATCH 32/91] fix another test --- backend/src/graphql/resolver/TransactionResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index 6115ef846..50b2b3690 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -89,7 +89,7 @@ describe('send coins', () => { }) 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', () => { From be902ae07e6683a72371740ddf011ab3f9c3fe24 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 31 Jan 2023 23:06:57 +0100 Subject: [PATCH 33/91] lint fixes --- backend/src/server/LogError.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/src/server/LogError.test.ts b/backend/src/server/LogError.test.ts index ef8d6a23c..6654c42f3 100644 --- a/backend/src/server/LogError.test.ts +++ b/backend/src/server/LogError.test.ts @@ -4,11 +4,13 @@ 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' }) }) @@ -16,6 +18,7 @@ describe('LogError', () => { 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) } From d1f62efeda6b7653748e1846f61f0ea4015a5626 Mon Sep 17 00:00:00 2001 From: ogerly Date: Wed, 1 Feb 2023 14:53:54 +0100 Subject: [PATCH 34/91] sidebar becomes smaller when critical phase --- frontend/src/components/Menu/Sidebar.vue | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Menu/Sidebar.vue b/frontend/src/components/Menu/Sidebar.vue index 9388b0e64..0875c0c7e 100644 --- a/frontend/src/components/Menu/Sidebar.vue +++ b/frontend/src/components/Menu/Sidebar.vue @@ -78,7 +78,24 @@ export default { min-width: 200px; } -@media screen and (max-width: 1024px) { +@media screen and (min-width: 1025px) { + #side-menu { + max-width: 180px; + } + #component-sidebar { + min-width: 180px; + } +} + +@media screen and (min-width: 1075px) { + #side-menu { + max-width: 200px; + } + #component-sidebar { + min-width: 200px; + } +} +@media screen and (max-width: 1108px) { #side-menu { max-width: 100%; } From fbec0d44b53939ce509dc3219cc4418e0d74d6df Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 01:51:17 +0100 Subject: [PATCH 35/91] corrected error message create user --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 9fe4b9320..b907bea20 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -346,7 +346,7 @@ export class UserResolver { logger.addContext('user', dbUser.id) } catch (e) { await queryRunner.rollbackTransaction() - throw new LogError(`Error during create user with error: ${e}`, e) + throw new LogError('Error creating user', e) } finally { await queryRunner.release() } From 0f7f38e2cdb087f77832c8da4dab09a787371aa5 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 01:52:58 +0100 Subject: [PATCH 36/91] corrected error message to not expose too much info --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index b907bea20..3494db003 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -465,7 +465,7 @@ export class UserResolver { try { // Save user await queryRunner.manager.save(user).catch((error) => { - throw new LogError(`Error saving user: ${error}`, error) + throw new LogError('Error saving user', error) }) // Save userContact await queryRunner.manager.save(userContact).catch((error) => { From 9fa9bb127b06f8852a71dd4b31d5e5eb93409ab0 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 01:54:35 +0100 Subject: [PATCH 37/91] error message start with upper case consistency --- backend/src/graphql/resolver/EmailOptinCodes.test.ts | 4 ++-- backend/src/graphql/resolver/UserResolver.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/graphql/resolver/EmailOptinCodes.test.ts b/backend/src/graphql/resolver/EmailOptinCodes.test.ts index 92f2ee9d0..988595723 100644 --- a/backend/src/graphql/resolver/EmailOptinCodes.test.ts +++ b/backend/src/graphql/resolver/EmailOptinCodes.test.ts @@ -75,7 +75,7 @@ describe('EmailOptinCodes', () => { query({ query: queryOptIn, variables: { optIn: optinCode } }), ).resolves.toMatchObject({ 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_' } }), ).resolves.toMatchObject({ data: null, - errors: [new GraphQLError('email was sent more than 24 hours ago')], + errors: [new GraphQLError('Email was sent more than 24 hours ago')], }) }) }) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 3494db003..3fa148b88 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -439,7 +439,7 @@ export class UserResolver { // Code is only valid for `CONFIG.EMAIL_CODE_VALID_TIME` minutes if (!isEmailVerificationCodeValid(userContact.updatedAt || userContact.createdAt)) { 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`, ) } logger.debug('EmailVerificationCode is valid...') @@ -515,7 +515,7 @@ export class UserResolver { // Code is only valid for `CONFIG.EMAIL_CODE_VALID_TIME` minutes if (!isEmailVerificationCodeValid(userContact.updatedAt || userContact.createdAt)) { 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`, ) } logger.info(`queryOptIn(${optIn}) successful...`) From de4ee4f6147455ddac9c0ade1f733bcfddcd2ab9 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 01:56:39 +0100 Subject: [PATCH 38/91] Update backend/src/graphql/resolver/UserResolver.ts Co-authored-by: Moriz Wahl --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 3fa148b88..0e09cfac3 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -480,7 +480,7 @@ export class UserResolver { eventProtocol.writeEvent(event.setEventActivateAccount(eventActivateAccount)) } catch (e) { await queryRunner.rollbackTransaction() - throw new LogError(`Error on writing User and UserContact data: ${e}`, e) + throw new LogError('Error on writing User and User Contact data', e) } finally { await queryRunner.release() } From 4300b10df70b97fae6f146b8590f16c06cc6b5a5 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 01:59:34 +0100 Subject: [PATCH 39/91] Update backend/src/graphql/resolver/UserResolver.ts Co-authored-by: Moriz Wahl --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 0e09cfac3..d63f05108 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -588,7 +588,7 @@ export class UserResolver { try { await queryRunner.manager.save(userEntity).catch((error) => { - throw new LogError(`error saving user: ${error}`, error) + throw new LogError('Error saving user', error) }) await queryRunner.commitTransaction() From 09ac97faba07873bd5ffa8f9cfec21c92f54cb5e Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 01:56:07 +0100 Subject: [PATCH 40/91] do not expose so much error information --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index d63f05108..4128cee1f 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -469,7 +469,7 @@ export class UserResolver { }) // Save userContact await queryRunner.manager.save(userContact).catch((error) => { - throw new LogError(`error saving userContact: ${error}`, error) + throw new LogError('Error saving userContact', error) }) await queryRunner.commitTransaction() From be53d6bbe0194af0c027b80c3abcd45a1085b470 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 01:58:52 +0100 Subject: [PATCH 41/91] don't expose klicktipp errors --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 4128cee1f..6f7e0177a 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -494,7 +494,7 @@ export class UserResolver { `klicktippSignIn(${userContact.email}, ${user.language}, ${user.firstName}, ${user.lastName})`, ) } catch (e) { - logger.error(`Error subscribe to klicktipp: ${e}`, e) + logger.error('Error subscribing to klicktipp', e) // TODO is this a problem? // eslint-disable-next-line no-console /* uncomment this, when you need the activation link on the console From 46eb45a14e6ada4e5f45b73a72543b3249ab3a8d Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 02:01:45 +0100 Subject: [PATCH 42/91] Update backend/src/graphql/resolver/UserResolver.ts Co-authored-by: Moriz Wahl --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 6f7e0177a..f51d4d32a 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -595,7 +595,7 @@ export class UserResolver { logger.debug('writing User data successful...') } catch (e) { await queryRunner.rollbackTransaction() - throw new LogError(`error on writing updated user data: ${e}`, e) + throw new LogError(`Error on writing updated user data', e) } finally { await queryRunner.release() } From 064e3221bd2e5e2bdf01323ffec5fd5cecf356fb Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 02:02:41 +0100 Subject: [PATCH 43/91] Update backend/src/graphql/resolver/UserResolver.ts Co-authored-by: Moriz Wahl --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index f51d4d32a..d5fedbfcf 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -722,7 +722,7 @@ export class UserResolver { const user = await DbUser.findOne({ id: userId }) // user exists ? if (!user) { - throw new LogError(`Could not find user with userId: ${userId}`, userId) + throw new LogError('Could not find user with given ID', userId) } // administrator user changes own role? const moderatorUser = getUser(context) From dce2abf1c36601db6a71e94d7f4dab2ec1ca2b1d Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 02:03:29 +0100 Subject: [PATCH 44/91] Update backend/src/graphql/resolver/UserResolver.ts Co-authored-by: Moriz Wahl --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index d5fedbfcf..cdf03362c 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -760,7 +760,7 @@ export class UserResolver { const user = await DbUser.findOne({ id: userId }) // user exists ? if (!user) { - throw new LogError(`Could not find user with userId: ${userId}`, userId) + throw new LogError('Could not find user with given ID', userId) } // moderator user disabled own account? const moderatorUser = getUser(context) From 2cbf01b8dfe25f6159a40b32ecd5b68853d75257 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 02:03:55 +0100 Subject: [PATCH 45/91] Update backend/src/graphql/resolver/UserResolver.ts Co-authored-by: Moriz Wahl --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index cdf03362c..0c0e3623a 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -778,7 +778,7 @@ export class UserResolver { async unDeleteUser(@Arg('userId', () => Int) userId: number): Promise { const user = await DbUser.findOne({ id: userId }, { withDeleted: true }) if (!user) { - throw new LogError(`Could not find user with userId: ${userId}`, userId) + throw new LogError('Could not find user with given ID', userId) } if (!user.deletedAt) { throw new LogError('User is not deleted') From 42e2490d848063e36b2db77c2a67a1a9f9ec463e Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 02:04:25 +0100 Subject: [PATCH 46/91] Update backend/src/graphql/resolver/UserResolver.ts Co-authored-by: Moriz Wahl --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 0c0e3623a..bfa3d7cdd 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -794,7 +794,7 @@ export class UserResolver { // const user = await dbUser.findOne({ id: emailContact.userId }) const user = await findUserByEmail(email) if (!user) { - throw new LogError(`Could not find User to emailContact: ${email}`, email) + throw new LogError('Could not find user to given email contact', email) } if (user.deletedAt) { throw new LogError(`User with emailContact: ${email} is deleted`, email) From cd2eeee40d82cb03ab5749e48c55b08ac55b9b78 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 02:04:38 +0100 Subject: [PATCH 47/91] Update backend/src/graphql/resolver/UserResolver.ts Co-authored-by: Moriz Wahl --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index bfa3d7cdd..d8f1ab299 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -797,7 +797,7 @@ export class UserResolver { throw new LogError('Could not find user to given email contact', email) } if (user.deletedAt) { - throw new LogError(`User with emailContact: ${email} is deleted`, email) + throw new LogError('User with given email contact is deleted', email) } const emailContact = user.emailContact if (emailContact.deletedAt) { From 3c3ccdb133c4b4867e38c03692214c464d4c056f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 02:06:39 +0100 Subject: [PATCH 48/91] fix suggestion typo --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index d8f1ab299..1710bb7e9 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -595,7 +595,7 @@ export class UserResolver { logger.debug('writing User data successful...') } catch (e) { await queryRunner.rollbackTransaction() - throw new LogError(`Error on writing updated user data', e) + throw new LogError('Error on writing updated user data', e) } finally { await queryRunner.release() } From 80ff425e3966259bc34ea4d1a4f72ced91781e5e Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 02:07:30 +0100 Subject: [PATCH 49/91] do not expose email in error --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 1710bb7e9..b14a5e50d 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -801,7 +801,7 @@ export class UserResolver { } const emailContact = user.emailContact if (emailContact.deletedAt) { - throw new LogError(`The emailContact: ${email} of this User is deleted`, email) + throw new LogError('The given email contact for this user is deleted', email) } emailContact.emailResendCount++ From 1c7a2267c0495c86763f63aaddb55b34ec8d9452 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 02:12:12 +0100 Subject: [PATCH 50/91] dont reference the language on error --- backend/src/graphql/resolver/UserResolver.test.ts | 4 ++-- backend/src/graphql/resolver/UserResolver.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index cadd77c92..b26d92d76 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -1106,13 +1106,13 @@ describe('UserResolver', () => { }), ).resolves.toEqual( 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', () => { - expect(logger.error).toBeCalledWith(`"not-valid" isn't a valid language`, 'not-valid') + expect(logger.error).toBeCalledWith('Given language is not a valid language', 'not-valid') }) }) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index b14a5e50d..d92958009 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -550,7 +550,7 @@ export class UserResolver { if (language) { if (!isLanguage(language)) { - throw new LogError(`"${language}" isn't a valid language`, language) + throw new LogError('Given language is not a valid language', language) } userEntity.language = language i18n.setLocale(language) From b89509149605f8cfc71874d3c35423ba06030c14 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 02:47:46 +0100 Subject: [PATCH 51/91] fix error messages expected in tests --- .../src/graphql/resolver/UserResolver.test.ts | 28 ++++++------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index b26d92d76..17eddca94 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -1431,16 +1431,13 @@ describe('UserResolver', () => { mutate({ mutation: setUserRole, variables: { userId: admin.id + 1, isAdmin: true } }), ).resolves.toEqual( 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', () => { - expect(logger.error).toBeCalledWith( - `Could not find user with userId: ${admin.id + 1}`, - admin.id + 1, - ) + expect(logger.error).toBeCalledWith('Could not find user with given ID', admin.id + 1) }) }) @@ -1606,16 +1603,13 @@ describe('UserResolver', () => { mutate({ mutation: deleteUser, variables: { userId: admin.id + 1 } }), ).resolves.toEqual( 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', () => { - expect(logger.error).toBeCalledWith( - `Could not find user with userId: ${admin.id + 1}`, - admin.id + 1, - ) + expect(logger.error).toBeCalledWith('Could not find user with given ID', admin.id + 1) }) }) @@ -1660,16 +1654,13 @@ describe('UserResolver', () => { mutate({ mutation: deleteUser, variables: { userId: user.id } }), ).resolves.toEqual( 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', () => { - expect(logger.error).toBeCalledWith( - `Could not find user with userId: ${user.id}`, - user.id, - ) + expect(logger.error).toBeCalledWith('Could not find user with given ID', user.id) }) }) }) @@ -1735,16 +1726,13 @@ describe('UserResolver', () => { mutate({ mutation: unDeleteUser, variables: { userId: admin.id + 1 } }), ).resolves.toEqual( 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', () => { - expect(logger.error).toBeCalledWith( - `Could not find user with userId: ${admin.id + 1}`, - admin.id + 1, - ) + expect(logger.error).toBeCalledWith('Could not find user with given ID', admin.id + 1) }) }) From 214fbe0a5c73cdec97d75d4e1d271cd42155fda9 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 03:55:01 +0100 Subject: [PATCH 52/91] remove unused constructor --- backend/src/event/Event.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts index 09a31d4e0..6a1233224 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -1,4 +1,3 @@ -import { EventProtocol } from '@entity/EventProtocol' import decimal from 'decimal.js-light' import { EventProtocolType } from './EventProtocolType' @@ -87,21 +86,6 @@ export class EventDeleteContributionLink extends EventBasicCt {} export class EventUpdateContributionLink extends EventBasicCt {} export class Event { - constructor() - constructor(event?: EventProtocol) { - if (event) { - this.id = event.id - this.type = event.type - this.createdAt = event.createdAt - this.userId = event.userId - this.xUserId = event.xUserId - this.xCommunityId = event.xCommunityId - this.transactionId = event.transactionId - this.contributionId = event.contributionId - this.amount = event.amount - } - } - public setEventBasic(): Event { this.type = EventProtocolType.BASIC this.createdAt = new Date() From 2c7e36d7e223b526dba0dd940c6737673d07315b Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 03:55:26 +0100 Subject: [PATCH 53/91] fix nullable fields in database entity --- .../EventProtocol.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/database/entity/0050-add_messageId_to_event_protocol/EventProtocol.ts b/database/entity/0050-add_messageId_to_event_protocol/EventProtocol.ts index d4dbc526f..a61895502 100644 --- a/database/entity/0050-add_messageId_to_event_protocol/EventProtocol.ts +++ b/database/entity/0050-add_messageId_to_event_protocol/EventProtocol.ts @@ -17,16 +17,16 @@ export class EventProtocol extends BaseEntity { userId: number @Column({ name: 'x_user_id', unsigned: true, nullable: true }) - xUserId: number + xUserId: number | null @Column({ name: 'x_community_id', unsigned: true, nullable: true }) - xCommunityId: number + xCommunityId: number | null @Column({ name: 'transaction_id', unsigned: true, nullable: true }) - transactionId: number + transactionId: number | null @Column({ name: 'contribution_id', unsigned: true, nullable: true }) - contributionId: number + contributionId: number | null @Column({ type: 'decimal', @@ -35,8 +35,8 @@ export class EventProtocol extends BaseEntity { nullable: true, transformer: DecimalTransformer, }) - amount: Decimal + amount: Decimal | null @Column({ name: 'message_id', unsigned: true, nullable: true }) - messageId: number + messageId: number | null } From ab2535abea04e9126c296848d2351ceeaf653a4b Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 03:55:47 +0100 Subject: [PATCH 54/91] simplify writeEvent --- backend/src/event/EventProtocolEmitter.ts | 48 ++++++------------- .../graphql/resolver/ContributionResolver.ts | 22 ++++----- .../graphql/resolver/TransactionResolver.ts | 8 ++-- backend/src/graphql/resolver/UserResolver.ts | 20 ++++---- 4 files changed, 34 insertions(+), 64 deletions(-) diff --git a/backend/src/event/EventProtocolEmitter.ts b/backend/src/event/EventProtocolEmitter.ts index b4b22bce1..e22e6218b 100644 --- a/backend/src/event/EventProtocolEmitter.ts +++ b/backend/src/event/EventProtocolEmitter.ts @@ -3,39 +3,21 @@ import { backendLogger as logger } from '@/server/logger' import { EventProtocol } from '@entity/EventProtocol' import CONFIG from '@/config' -class EventProtocolEmitter { - /* }extends EventEmitter { */ - private events: Event[] - - /* - public addEvent(event: Event) { - this.events.push(event) +export const writeEvent = async (event: Event): Promise => { + if (CONFIG.EVENT_PROTOCOL_DISABLED) { + logger.info('EventProtocol is disabled', CONFIG.EVENT_PROTOCOL_DISABLED) + return null } - public getEvents(): Event[] { - return this.events - } - */ - - public isDisabled() { - logger.info(`EventProtocol - isDisabled=${CONFIG.EVENT_PROTOCOL_DISABLED}`) - return CONFIG.EVENT_PROTOCOL_DISABLED === true - } - - public async writeEvent(event: Event): Promise { - if (!eventProtocol.isDisabled()) { - logger.info(`writeEvent(${JSON.stringify(event)})`) - const dbEvent = new EventProtocol() - dbEvent.type = event.type - dbEvent.createdAt = event.createdAt - dbEvent.userId = event.userId - if (event.xUserId) dbEvent.xUserId = event.xUserId - if (event.xCommunityId) dbEvent.xCommunityId = event.xCommunityId - if (event.contributionId) dbEvent.contributionId = event.contributionId - if (event.transactionId) dbEvent.transactionId = event.transactionId - if (event.amount) dbEvent.amount = event.amount - await dbEvent.save() - } - } + logger.info(`writeEvent(${JSON.stringify(event)})`) + const dbEvent = new EventProtocol() + dbEvent.type = event.type + dbEvent.createdAt = event.createdAt + dbEvent.userId = event.userId + dbEvent.xUserId = event.xUserId || null + dbEvent.xCommunityId = event.xCommunityId || null + dbEvent.contributionId = event.contributionId || null + dbEvent.transactionId = event.transactionId || null + dbEvent.amount = event.amount || null + return dbEvent.save() } -export const eventProtocol = new EventProtocolEmitter() diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 0c758b52b..b5357709c 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -46,7 +46,7 @@ import { EventAdminContributionDelete, EventAdminContributionUpdate, } from '@/event/Event' -import { eventProtocol } from '@/event/EventProtocolEmitter' +import { writeEvent } from '@/event/EventProtocolEmitter' import { calculateDecay } from '@/util/decay' import { sendContributionConfirmedEmail, @@ -97,7 +97,7 @@ export class ContributionResolver { eventCreateContribution.userId = user.id eventCreateContribution.amount = amount eventCreateContribution.contributionId = contribution.id - await eventProtocol.writeEvent(event.setEventContributionCreate(eventCreateContribution)) + await writeEvent(event.setEventContributionCreate(eventCreateContribution)) return new UnconfirmedContribution(contribution, user, creations) } @@ -133,7 +133,7 @@ export class ContributionResolver { eventDeleteContribution.userId = user.id eventDeleteContribution.contributionId = contribution.id eventDeleteContribution.amount = contribution.amount - await eventProtocol.writeEvent(event.setEventContributionDelete(eventDeleteContribution)) + await writeEvent(event.setEventContributionDelete(eventDeleteContribution)) const res = await contribution.softRemove() return !!res @@ -279,7 +279,7 @@ export class ContributionResolver { eventUpdateContribution.userId = user.id eventUpdateContribution.contributionId = contributionId eventUpdateContribution.amount = amount - await eventProtocol.writeEvent(event.setEventContributionUpdate(eventUpdateContribution)) + await writeEvent(event.setEventContributionUpdate(eventUpdateContribution)) return new UnconfirmedContribution(contributionToUpdate, user, creations) } @@ -346,9 +346,7 @@ export class ContributionResolver { eventAdminCreateContribution.userId = moderator.id eventAdminCreateContribution.amount = amount eventAdminCreateContribution.contributionId = contribution.id - await eventProtocol.writeEvent( - event.setEventAdminContributionCreate(eventAdminCreateContribution), - ) + await writeEvent(event.setEventAdminContributionCreate(eventAdminCreateContribution)) return getUserCreation(emailContact.userId, clientTimezoneOffset) } @@ -458,9 +456,7 @@ export class ContributionResolver { eventAdminContributionUpdate.userId = user.id eventAdminContributionUpdate.amount = amount eventAdminContributionUpdate.contributionId = contributionToUpdate.id - await eventProtocol.writeEvent( - event.setEventAdminContributionUpdate(eventAdminContributionUpdate), - ) + await writeEvent(event.setEventAdminContributionUpdate(eventAdminContributionUpdate)) return result } @@ -538,9 +534,7 @@ export class ContributionResolver { eventAdminContributionDelete.userId = contribution.userId eventAdminContributionDelete.amount = contribution.amount eventAdminContributionDelete.contributionId = contribution.id - await eventProtocol.writeEvent( - event.setEventAdminContributionDelete(eventAdminContributionDelete), - ) + await writeEvent(event.setEventAdminContributionDelete(eventAdminContributionDelete)) sendContributionDeniedEmail({ firstName: user.firstName, lastName: user.lastName, @@ -668,7 +662,7 @@ export class ContributionResolver { eventContributionConfirm.userId = user.id eventContributionConfirm.amount = contribution.amount eventContributionConfirm.contributionId = contribution.id - await eventProtocol.writeEvent(event.setEventContributionConfirm(eventContributionConfirm)) + await writeEvent(event.setEventContributionConfirm(eventContributionConfirm)) } finally { releaseLock() } diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 2f97596b2..b75782abf 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -30,7 +30,7 @@ import { sendTransactionReceivedEmail, } from '@/emails/sendEmailVariants' import { Event, EventTransactionReceive, EventTransactionSend } from '@/event/Event' -import { eventProtocol } from '@/event/EventProtocolEmitter' +import { writeEvent } from '@/event/EventProtocolEmitter' import { BalanceResolver } from './BalanceResolver' import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const' @@ -144,16 +144,14 @@ export const executeTransaction = async ( eventTransactionSend.xUserId = transactionSend.linkedUserId eventTransactionSend.transactionId = transactionSend.id eventTransactionSend.amount = transactionSend.amount.mul(-1) - await eventProtocol.writeEvent(new Event().setEventTransactionSend(eventTransactionSend)) + await writeEvent(new Event().setEventTransactionSend(eventTransactionSend)) const eventTransactionReceive = new EventTransactionReceive() eventTransactionReceive.userId = transactionReceive.userId eventTransactionReceive.xUserId = transactionReceive.linkedUserId eventTransactionReceive.transactionId = transactionReceive.id eventTransactionReceive.amount = transactionReceive.amount - await eventProtocol.writeEvent( - new Event().setEventTransactionReceive(eventTransactionReceive), - ) + await writeEvent(new Event().setEventTransactionReceive(eventTransactionReceive)) } catch (e) { await queryRunner.rollbackTransaction() logger.error(`Transaction was not successful: ${e}`) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 94b9265b3..aa56e81bc 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -48,7 +48,7 @@ import { klicktippNewsletterStateMiddleware } from '@/middleware/klicktippMiddle import { klicktippSignIn } from '@/apis/KlicktippController' import { RIGHTS } from '@/auth/RIGHTS' import { hasElopageBuys } from '@/util/hasElopageBuys' -import { eventProtocol } from '@/event/EventProtocolEmitter' +import { writeEvent } from '@/event/EventProtocolEmitter' import { Event, EventLogin, @@ -181,7 +181,7 @@ export class UserResolver { }) const ev = new EventLogin() ev.userId = user.id - eventProtocol.writeEvent(new Event().setEventLogin(ev)) + writeEvent(new Event().setEventLogin(ev)) logger.info(`successful Login: ${JSON.stringify(user, null, 2)}`) return user } @@ -253,9 +253,7 @@ export class UserResolver { }) const eventSendAccountMultiRegistrationEmail = new EventSendAccountMultiRegistrationEmail() eventSendAccountMultiRegistrationEmail.userId = foundUser.id - eventProtocol.writeEvent( - event.setEventSendConfirmationEmail(eventSendAccountMultiRegistrationEmail), - ) + writeEvent(event.setEventSendConfirmationEmail(eventSendAccountMultiRegistrationEmail)) logger.info( `sendAccountMultiRegistrationEmail by ${firstName} ${lastName} to ${foundUser.firstName} ${foundUser.lastName} <${email}>`, ) @@ -349,7 +347,7 @@ export class UserResolver { }) logger.info(`sendAccountActivationEmail of ${firstName}.${lastName} to ${email}`) eventSendConfirmEmail.userId = dbUser.id - eventProtocol.writeEvent(event.setEventSendConfirmationEmail(eventSendConfirmEmail)) + writeEvent(event.setEventSendConfirmationEmail(eventSendConfirmEmail)) if (!emailSent) { logger.debug(`Account confirmation link: ${activationLink}`) @@ -368,10 +366,10 @@ export class UserResolver { if (redeemCode) { eventRedeemRegister.userId = dbUser.id - await eventProtocol.writeEvent(event.setEventRedeemRegister(eventRedeemRegister)) + await writeEvent(event.setEventRedeemRegister(eventRedeemRegister)) } else { eventRegister.userId = dbUser.id - await eventProtocol.writeEvent(event.setEventRegister(eventRegister)) + await writeEvent(event.setEventRegister(eventRegister)) } return new User(dbUser) @@ -507,7 +505,7 @@ export class UserResolver { const eventActivateAccount = new EventActivateAccount() eventActivateAccount.userId = user.id - eventProtocol.writeEvent(event.setEventActivateAccount(eventActivateAccount)) + writeEvent(event.setEventActivateAccount(eventActivateAccount)) } catch (e) { await queryRunner.rollbackTransaction() logger.error('Error on writing User and UserContact data:' + e) @@ -874,9 +872,7 @@ export class UserResolver { const event = new Event() const eventSendConfirmationEmail = new EventSendConfirmationEmail() eventSendConfirmationEmail.userId = user.id - await eventProtocol.writeEvent( - event.setEventSendConfirmationEmail(eventSendConfirmationEmail), - ) + await writeEvent(event.setEventSendConfirmationEmail(eventSendConfirmationEmail)) } return true From f577e3258a1957e7312d16a8bdd5a60f97dc22d6 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 04:04:05 +0100 Subject: [PATCH 55/91] also make older entity models nullable --- .../0043-add_event_protocol_table/EventProtocol.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/database/entity/0043-add_event_protocol_table/EventProtocol.ts b/database/entity/0043-add_event_protocol_table/EventProtocol.ts index 72470d2ed..c05651df2 100644 --- a/database/entity/0043-add_event_protocol_table/EventProtocol.ts +++ b/database/entity/0043-add_event_protocol_table/EventProtocol.ts @@ -14,19 +14,19 @@ export class EventProtocol extends BaseEntity { createdAt: Date @Column({ name: 'user_id', unsigned: true, nullable: false }) - userId: number + userId: number | null @Column({ name: 'x_user_id', unsigned: true, nullable: true }) - xUserId: number + xUserId: number | null @Column({ name: 'x_community_id', unsigned: true, nullable: true }) - xCommunityId: number + xCommunityId: number | null @Column({ name: 'transaction_id', unsigned: true, nullable: true }) - transactionId: number + transactionId: number | null @Column({ name: 'contribution_id', unsigned: true, nullable: true }) - contributionId: number + contributionId: number | null @Column({ type: 'decimal', @@ -35,5 +35,5 @@ export class EventProtocol extends BaseEntity { nullable: true, transformer: DecimalTransformer, }) - amount: Decimal + amount: Decimal | null } From 357588a6808a1d2309046d3906186e2683191998 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 04:08:07 +0100 Subject: [PATCH 56/91] define type properly --- .../0043-add_event_protocol_table/EventProtocol.ts | 12 ++++++------ .../EventProtocol.ts | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/database/entity/0043-add_event_protocol_table/EventProtocol.ts b/database/entity/0043-add_event_protocol_table/EventProtocol.ts index c05651df2..72470d2ed 100644 --- a/database/entity/0043-add_event_protocol_table/EventProtocol.ts +++ b/database/entity/0043-add_event_protocol_table/EventProtocol.ts @@ -14,19 +14,19 @@ export class EventProtocol extends BaseEntity { createdAt: Date @Column({ name: 'user_id', unsigned: true, nullable: false }) - userId: number | null + userId: number @Column({ name: 'x_user_id', unsigned: true, nullable: true }) - xUserId: number | null + xUserId: number @Column({ name: 'x_community_id', unsigned: true, nullable: true }) - xCommunityId: number | null + xCommunityId: number @Column({ name: 'transaction_id', unsigned: true, nullable: true }) - transactionId: number | null + transactionId: number @Column({ name: 'contribution_id', unsigned: true, nullable: true }) - contributionId: number | null + contributionId: number @Column({ type: 'decimal', @@ -35,5 +35,5 @@ export class EventProtocol extends BaseEntity { nullable: true, transformer: DecimalTransformer, }) - amount: Decimal | null + amount: Decimal } diff --git a/database/entity/0050-add_messageId_to_event_protocol/EventProtocol.ts b/database/entity/0050-add_messageId_to_event_protocol/EventProtocol.ts index a61895502..e457cc0a3 100644 --- a/database/entity/0050-add_messageId_to_event_protocol/EventProtocol.ts +++ b/database/entity/0050-add_messageId_to_event_protocol/EventProtocol.ts @@ -16,16 +16,16 @@ export class EventProtocol extends BaseEntity { @Column({ name: 'user_id', unsigned: true, nullable: false }) userId: number - @Column({ name: 'x_user_id', unsigned: true, nullable: true }) + @Column({ name: 'x_user_id', type: 'int', unsigned: true, nullable: true }) xUserId: number | null - @Column({ name: 'x_community_id', unsigned: true, nullable: true }) + @Column({ name: 'x_community_id', type: 'int', unsigned: true, nullable: true }) xCommunityId: number | null - @Column({ name: 'transaction_id', unsigned: true, nullable: true }) + @Column({ name: 'transaction_id', type: 'int', unsigned: true, nullable: true }) transactionId: number | null - @Column({ name: 'contribution_id', unsigned: true, nullable: true }) + @Column({ name: 'contribution_id', type: 'int', unsigned: true, nullable: true }) contributionId: number | null @Column({ @@ -37,6 +37,6 @@ export class EventProtocol extends BaseEntity { }) amount: Decimal | null - @Column({ name: 'message_id', unsigned: true, nullable: true }) + @Column({ name: 'message_id', type: 'int', unsigned: true, nullable: true }) messageId: number | null } From 7288bfc7dc8e9c1fa6bee0d8549f70e3683991e4 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 04:20:36 +0100 Subject: [PATCH 57/91] properly use logger --- backend/src/event/EventProtocolEmitter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/event/EventProtocolEmitter.ts b/backend/src/event/EventProtocolEmitter.ts index e22e6218b..9cde4939f 100644 --- a/backend/src/event/EventProtocolEmitter.ts +++ b/backend/src/event/EventProtocolEmitter.ts @@ -9,7 +9,7 @@ export const writeEvent = async (event: Event): Promise => return null } - logger.info(`writeEvent(${JSON.stringify(event)})`) + logger.info('writeEvent', event) const dbEvent = new EventProtocol() dbEvent.type = event.type dbEvent.createdAt = event.createdAt From 55e830ee8d6b101b0de5363f4adca6dff223fb29 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Thu, 2 Feb 2023 09:14:04 +0100 Subject: [PATCH 58/91] Update backend/src/graphql/resolver/ContributionResolver.test.ts Co-authored-by: Moriz Wahl --- backend/src/graphql/resolver/ContributionResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 6c9c1136b..a5c52e58b 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -788,7 +788,7 @@ describe('ContributionResolver', () => { ) }) - it('returns allCreation without statusFilter', async () => { + it('returns all contributions without statusFilter', async () => { await expect( query({ query: listAllContributions, From 44dabe04862e676ab17f6eef7c3fb59a430b820d Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Thu, 2 Feb 2023 09:14:24 +0100 Subject: [PATCH 59/91] Update backend/src/graphql/resolver/ContributionResolver.test.ts Co-authored-by: Moriz Wahl --- backend/src/graphql/resolver/ContributionResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index a5c52e58b..76bf900e4 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -823,7 +823,7 @@ describe('ContributionResolver', () => { ) }) - it('returns allCreation for statusFilter = null', async () => { + it('returns all contributions for statusFilter = null', async () => { await expect( query({ query: listAllContributions, From 27b54555eadd95d58eb96f5ee51d9a24c2500641 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Thu, 2 Feb 2023 09:14:37 +0100 Subject: [PATCH 60/91] Update backend/src/graphql/resolver/ContributionResolver.test.ts Co-authored-by: Moriz Wahl --- backend/src/graphql/resolver/ContributionResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 76bf900e4..6dfb4234d 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -859,7 +859,7 @@ describe('ContributionResolver', () => { ) }) - it('returns allCreation for statusFilter = []', async () => { + it('returns all contributions for statusFilter = []', async () => { await expect( query({ query: listAllContributions, From 676c6b2e7e2eba946b81cca15f3a50ca86f3bc53 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Thu, 2 Feb 2023 09:14:48 +0100 Subject: [PATCH 61/91] Update backend/src/graphql/resolver/ContributionResolver.test.ts Co-authored-by: Moriz Wahl --- backend/src/graphql/resolver/ContributionResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 6dfb4234d..d4bd753de 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -895,7 +895,7 @@ describe('ContributionResolver', () => { ) }) - it('returns all CONFIRMED Creation', async () => { + it('returns all CONFIRMED contributions', async () => { await expect( query({ query: listAllContributions, From c659264e8264a10a884e0a72eeb8f9533b8b5b27 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Thu, 2 Feb 2023 09:18:16 +0100 Subject: [PATCH 62/91] Update backend/src/graphql/resolver/ContributionResolver.test.ts Co-authored-by: Moriz Wahl --- backend/src/graphql/resolver/ContributionResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index d4bd753de..d48502c69 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -931,7 +931,7 @@ describe('ContributionResolver', () => { ) }) - it('returns all PENDING Creation', async () => { + it('returns all PENDING contributions', async () => { await expect( query({ query: listAllContributions, From 471685bbd7a04d4ff946ee7d509b0cde0caae87d Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 2 Feb 2023 11:22:47 +0100 Subject: [PATCH 63/91] Change statusFilters to statusFilter. --- backend/src/graphql/resolver/ContributionResolver.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 6a5240cd1..c7946d2c8 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -180,14 +180,14 @@ export class ContributionResolver { @Args() { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, @Arg('statusFilter', () => [ContributionStatus], { nullable: true }) - statusFilters?: ContributionStatus[], + statusFilter?: ContributionStatus[], ): Promise { const where: { contributionStatus?: FindOperator | null } = {} - if (statusFilters && statusFilters.length) { - where.contributionStatus = In(statusFilters) + if (statusFilter && statusFilter.length) { + where.contributionStatus = In(statusFilter) } const [dbContributions, count] = await getConnection() From fdf22daec78305d5072d19963984ebf44bee2a46 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 13:04:47 +0100 Subject: [PATCH 64/91] use LogError --- backend/src/emails/sendEmailTranslated.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/emails/sendEmailTranslated.ts b/backend/src/emails/sendEmailTranslated.ts index 5652b3424..9fa8af73f 100644 --- a/backend/src/emails/sendEmailTranslated.ts +++ b/backend/src/emails/sendEmailTranslated.ts @@ -4,6 +4,7 @@ import path from 'path' import { createTransport } from 'nodemailer' import Email from 'email-templates' import i18n from 'i18n' +import LogError from '@/server/LogError' export const sendEmailTranslated = async (params: { receiver: { @@ -73,8 +74,7 @@ export const sendEmailTranslated = async (params: { logger.info('Result: ', result) }) .catch((error: unknown) => { - logger.error('Error sending notification email: ', error) - throw new Error('Error sending notification email!') + throw new LogError('Error sending notification email', error) }) i18n.setLocale(rememberLocaleToRestore) From ee8a338ec4e927932a64d233364f06e2dd37592f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 2 Feb 2023 23:49:35 +0100 Subject: [PATCH 65/91] fix logger on contributionLinkResolver --- .../resolver/ContributionLinkResolver.test.ts | 72 ++++++++----------- .../resolver/ContributionLinkResolver.ts | 42 +++++------ 2 files changed, 46 insertions(+), 68 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionLinkResolver.test.ts b/backend/src/graphql/resolver/ContributionLinkResolver.test.ts index 0cf27bf33..46296e009 100644 --- a/backend/src/graphql/resolver/ContributionLinkResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionLinkResolver.test.ts @@ -246,6 +246,7 @@ describe('Contribution Links', () => { }) it('returns an error if missing startDate', async () => { + jest.clearAllMocks() await expect( mutate({ mutation: createContributionLink, @@ -270,6 +271,7 @@ describe('Contribution Links', () => { }) it('returns an error if missing endDate', async () => { + jest.clearAllMocks() await expect( mutate({ mutation: createContributionLink, @@ -292,6 +294,7 @@ describe('Contribution Links', () => { }) it('returns an error if endDate is before startDate', async () => { + jest.clearAllMocks() await expect( mutate({ mutation: createContributionLink, @@ -317,6 +320,7 @@ describe('Contribution Links', () => { }) it('returns an error if name is an empty string', async () => { + jest.clearAllMocks() await expect( mutate({ mutation: createContributionLink, @@ -327,16 +331,17 @@ describe('Contribution Links', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('The name must be initialized!')], + errors: [new GraphQLError('The name must be initialized')], }), ) }) it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith('The name must be initialized!') + expect(logger.error).toBeCalledWith('The name must be initialized') }) it('returns an error if name is shorter than 5 characters', async () => { + jest.clearAllMocks() await expect( mutate({ mutation: createContributionLink, @@ -347,22 +352,17 @@ describe('Contribution Links', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [ - new GraphQLError( - `The value of 'name' with a length of 3 did not fulfill the requested bounderies min=5 and max=100`, - ), - ], + errors: [new GraphQLError('The value of name is too short')], }), ) }) it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith( - `The value of 'name' with a length of 3 did not fulfill the requested bounderies min=5 and max=100`, - ) + expect(logger.error).toBeCalledWith('The value of name is too short', 3) }) it('returns an error if name is longer than 100 characters', async () => { + jest.clearAllMocks() await expect( mutate({ mutation: createContributionLink, @@ -373,22 +373,17 @@ describe('Contribution Links', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [ - new GraphQLError( - `The value of 'name' with a length of 101 did not fulfill the requested bounderies min=5 and max=100`, - ), - ], + errors: [new GraphQLError('The value of name is too long')], }), ) }) it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith( - `The value of 'name' with a length of 101 did not fulfill the requested bounderies min=5 and max=100`, - ) + expect(logger.error).toBeCalledWith('The value of name is too long', 101) }) it('returns an error if memo is an empty string', async () => { + jest.clearAllMocks() await expect( mutate({ mutation: createContributionLink, @@ -399,16 +394,17 @@ describe('Contribution Links', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('The memo must be initialized!')], + errors: [new GraphQLError('The memo must be initialized')], }), ) }) it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith('The memo must be initialized!') + expect(logger.error).toBeCalledWith('The memo must be initialized') }) it('returns an error if memo is shorter than 5 characters', async () => { + jest.clearAllMocks() await expect( mutate({ mutation: createContributionLink, @@ -419,22 +415,17 @@ describe('Contribution Links', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [ - new GraphQLError( - `The value of 'memo' with a length of 3 did not fulfill the requested bounderies min=5 and max=255`, - ), - ], + errors: [new GraphQLError('The value of memo is too short')], }), ) }) it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith( - `The value of 'memo' with a length of 3 did not fulfill the requested bounderies min=5 and max=255`, - ) + expect(logger.error).toBeCalledWith('The value of memo is too short', 3) }) it('returns an error if memo is longer than 255 characters', async () => { + jest.clearAllMocks() await expect( mutate({ mutation: createContributionLink, @@ -445,22 +436,17 @@ describe('Contribution Links', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [ - new GraphQLError( - `The value of 'memo' with a length of 256 did not fulfill the requested bounderies min=5 and max=255`, - ), - ], + errors: [new GraphQLError('The value of memo is too long')], }), ) }) it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith( - `The value of 'memo' with a length of 256 did not fulfill the requested bounderies min=5 and max=255`, - ) + expect(logger.error).toBeCalledWith('The value of memo is too long', 256) }) it('returns an error if amount is not positive', async () => { + jest.clearAllMocks() await expect( mutate({ mutation: createContributionLink, @@ -471,15 +457,13 @@ describe('Contribution Links', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('The amount=0 must be initialized with a positiv value!')], + errors: [new GraphQLError('The amount must be a positiv value')], }), ) }) it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith( - 'The amount=0 must be initialized with a positiv value!', - ) + expect(logger.error).toBeCalledWith('The amount must be a positiv value', new Decimal(0)) }) }) @@ -530,14 +514,14 @@ describe('Contribution Links', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('Contribution Link not found to given id.')], + errors: [new GraphQLError('Contribution Link not found')], }), ) }) }) it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith('Contribution Link not found to given id: -1') + expect(logger.error).toBeCalledWith('Contribution Link not found', -1) }) describe('valid id', () => { @@ -601,13 +585,13 @@ describe('Contribution Links', () => { mutate({ mutation: deleteContributionLink, variables: { id: -1 } }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('Contribution Link not found to given id.')], + errors: [new GraphQLError('Contribution Link not found')], }), ) }) it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith('Contribution Link not found to given id: -1') + expect(logger.error).toBeCalledWith('Contribution Link not found', -1) }) }) diff --git a/backend/src/graphql/resolver/ContributionLinkResolver.ts b/backend/src/graphql/resolver/ContributionLinkResolver.ts index 0a6bb971c..6a7a71391 100644 --- a/backend/src/graphql/resolver/ContributionLinkResolver.ts +++ b/backend/src/graphql/resolver/ContributionLinkResolver.ts @@ -20,6 +20,7 @@ import Paginated from '@arg/Paginated' // TODO: this is a strange construct import { transactionLinkCode as contributionLinkCode } from './TransactionLinkResolver' +import LogError from '@/server/LogError' @Resolver() export class ContributionLinkResolver { @@ -39,35 +40,30 @@ export class ContributionLinkResolver { }: ContributionLinkArgs, ): Promise { isStartEndDateValid(validFrom, validTo) + // TODO: this should be enforced by the schema. if (!name) { - logger.error(`The name must be initialized!`) - throw new Error(`The name must be initialized!`) + throw new LogError('The name must be initialized') } - if ( - name.length < CONTRIBUTIONLINK_NAME_MIN_CHARS || - name.length > CONTRIBUTIONLINK_NAME_MAX_CHARS - ) { - const msg = `The value of 'name' with a length of ${name.length} did not fulfill the requested bounderies min=${CONTRIBUTIONLINK_NAME_MIN_CHARS} and max=${CONTRIBUTIONLINK_NAME_MAX_CHARS}` - logger.error(`${msg}`) - throw new Error(`${msg}`) + if (name.length < CONTRIBUTIONLINK_NAME_MIN_CHARS) { + throw new LogError('The value of name is too short', name.length) } + if (name.length > CONTRIBUTIONLINK_NAME_MAX_CHARS) { + throw new LogError('The value of name is too long', name.length) + } + // TODO: this should be enforced by the schema. if (!memo) { - logger.error(`The memo must be initialized!`) - throw new Error(`The memo must be initialized!`) + throw new LogError('The memo must be initialized') } - if (memo.length < MEMO_MIN_CHARS || memo.length > MEMO_MAX_CHARS) { - const msg = `The value of 'memo' with a length of ${memo.length} did not fulfill the requested bounderies min=${MEMO_MIN_CHARS} and max=${MEMO_MAX_CHARS}` - logger.error(`${msg}`) - throw new Error(`${msg}`) + if (memo.length < MEMO_MIN_CHARS) { + throw new LogError('The value of memo is too short', memo.length) } - if (!amount) { - logger.error(`The amount must be initialized!`) - throw new Error('The amount must be initialized!') + if (memo.length > MEMO_MAX_CHARS) { + throw new LogError('The value of memo is too long', memo.length) } if (!new Decimal(amount).isPositive()) { - logger.error(`The amount=${amount} must be initialized with a positiv value!`) - throw new Error(`The amount=${amount} must be initialized with a positiv value!`) + throw new LogError('The amount must be a positiv value', amount) } + const dbContributionLink = new DbContributionLink() dbContributionLink.amount = amount dbContributionLink.name = name @@ -107,8 +103,7 @@ export class ContributionLinkResolver { async deleteContributionLink(@Arg('id', () => Int) id: number): Promise { const contributionLink = await DbContributionLink.findOne(id) if (!contributionLink) { - logger.error(`Contribution Link not found to given id: ${id}`) - throw new Error('Contribution Link not found to given id.') + throw new LogError('Contribution Link not found', id) } await contributionLink.softRemove() logger.debug(`deleteContributionLink successful!`) @@ -134,8 +129,7 @@ export class ContributionLinkResolver { ): Promise { const dbContributionLink = await DbContributionLink.findOne(id) if (!dbContributionLink) { - logger.error(`Contribution Link not found to given id: ${id}`) - throw new Error('Contribution Link not found to given id.') + throw new LogError('Contribution Link not found', id) } dbContributionLink.amount = amount dbContributionLink.name = name From 33e87ddd92b6316ed3faefe4a9a00efb74ca0bc5 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 3 Feb 2023 00:13:25 +0100 Subject: [PATCH 66/91] fix logger on contributionMessageResolver --- .../ContributionMessageResolver.test.ts | 56 ++++++++++++------- .../resolver/ContributionMessageResolver.ts | 17 ++---- 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index 436830c2c..36d78c382 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -88,6 +88,7 @@ describe('ContributionMessageResolver', () => { describe('input not valid', () => { it('throws error when contribution does not exist', async () => { + jest.clearAllMocks() await expect( mutate({ mutation: adminCreateContributionMessage, @@ -98,16 +99,20 @@ describe('ContributionMessageResolver', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [ - new GraphQLError( - 'ContributionMessage was not successful: Error: Contribution not found', - ), - ], + errors: [new GraphQLError('ContributionMessage was not sent successfully')], }), ) }) + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith( + 'ContributionMessage was not sent successfully', + new Error('Contribution not found'), + ) + }) + it('throws error when contribution.userId equals user.id', async () => { + jest.clearAllMocks() await mutate({ mutation: login, variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, @@ -130,14 +135,17 @@ describe('ContributionMessageResolver', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [ - new GraphQLError( - 'ContributionMessage was not successful: Error: Admin can not answer on own contribution', - ), - ], + errors: [new GraphQLError('ContributionMessage was not sent successfully')], }), ) }) + + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith( + 'ContributionMessage was not sent successfully', + new Error('Admin can not answer on his own contribution'), + ) + }) }) describe('valid input', () => { @@ -210,6 +218,7 @@ describe('ContributionMessageResolver', () => { describe('input not valid', () => { it('throws error when contribution does not exist', async () => { + jest.clearAllMocks() await expect( mutate({ mutation: createContributionMessage, @@ -220,16 +229,20 @@ describe('ContributionMessageResolver', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [ - new GraphQLError( - 'ContributionMessage was not successful: Error: Contribution not found', - ), - ], + errors: [new GraphQLError('ContributionMessage was not sent successfully')], }), ) }) + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith( + 'ContributionMessage was not sent successfully', + new Error('Contribution not found'), + ) + }) + it('throws error when other user tries to send createContributionMessage', async () => { + jest.clearAllMocks() await mutate({ mutation: login, variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, @@ -244,14 +257,17 @@ describe('ContributionMessageResolver', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [ - new GraphQLError( - 'ContributionMessage was not successful: Error: Can not send message to contribution of another user', - ), - ], + errors: [new GraphQLError('ContributionMessage was not sent successfully')], }), ) }) + + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith( + 'ContributionMessage was not sent successfully', + new Error('Can not send message to contribution of another user'), + ) + }) }) describe('valid input', () => { diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index 38bea804e..9544f81c4 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -16,6 +16,7 @@ import { backendLogger as logger } from '@/server/logger' import { RIGHTS } from '@/auth/RIGHTS' import { Context, getUser } from '@/server/context' import { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants' +import LogError from '@/server/LogError' @Resolver() export class ContributionMessageResolver { @@ -54,8 +55,7 @@ export class ContributionMessageResolver { await queryRunner.commitTransaction() } catch (e) { await queryRunner.rollbackTransaction() - logger.error(`ContributionMessage was not successful: ${e}`) - throw new Error(`ContributionMessage was not successful: ${e}`) + throw new LogError('ContributionMessage was not sent successfully', e) } finally { await queryRunner.release() } @@ -95,9 +95,7 @@ export class ContributionMessageResolver { @Ctx() context: Context, ): Promise { const user = getUser(context) - if (!user.emailContact) { - user.emailContact = await UserContact.findOneOrFail({ where: { id: user.emailId } }) - } + const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() await queryRunner.startTransaction('REPEATABLE READ') @@ -108,12 +106,10 @@ export class ContributionMessageResolver { relations: ['user'], }) if (!contribution) { - logger.error('Contribution not found') - throw new Error('Contribution not found') + throw new LogError('Contribution not found', contributionId) } if (contribution.userId === user.id) { - logger.error('Admin can not answer on own contribution') - throw new Error('Admin can not answer on own contribution') + throw new LogError('Admin can not answer on his own contribution', contributionId) } if (!contribution.user.emailContact) { contribution.user.emailContact = await UserContact.findOneOrFail({ @@ -149,8 +145,7 @@ export class ContributionMessageResolver { await queryRunner.commitTransaction() } catch (e) { await queryRunner.rollbackTransaction() - logger.error(`ContributionMessage was not successful: ${e}`) - throw new Error(`ContributionMessage was not successful: ${e}`) + throw new LogError('ContributionMessage was not sent successfully', e) } finally { await queryRunner.release() } From ab96a38dd38b3b2d9b1ebc216966a2121d7d2a4e Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 3 Feb 2023 01:25:24 +0100 Subject: [PATCH 67/91] remove unused import --- backend/src/graphql/resolver/ContributionMessageResolver.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index 9544f81c4..3e6f86e53 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -12,7 +12,6 @@ import { ContributionStatus } from '@enum/ContributionStatus' import { Order } from '@enum/Order' import Paginated from '@arg/Paginated' -import { backendLogger as logger } from '@/server/logger' import { RIGHTS } from '@/auth/RIGHTS' import { Context, getUser } from '@/server/context' import { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants' From 8b502c940cf94ff4d343b22367d9d4768ae963dc Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 6 Feb 2023 15:35:11 +0100 Subject: [PATCH 68/91] Remove rebuild of admin, backend and frontend in case there tests files have been changed. --- admin/package.json | 5 ++++- backend/package.json | 3 +++ frontend/package.json | 5 ++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/admin/package.json b/admin/package.json index 8270c4da6..30e93239b 100644 --- a/admin/package.json +++ b/admin/package.json @@ -86,5 +86,8 @@ "> 1%", "last 2 versions", "not ie <= 10" - ] + ], + "nodemonConfig": { + "ignore": ["**/*.spec.js"] + } } diff --git a/backend/package.json b/backend/package.json index bfcd61d5b..9a36c2ff8 100644 --- a/backend/package.json +++ b/backend/package.json @@ -72,5 +72,8 @@ "ts-node": "^10.0.0", "tsconfig-paths": "^3.14.0", "typescript": "^4.3.4" + }, + "nodemonConfig": { + "ignore": ["**/*.test.ts"] } } diff --git a/frontend/package.json b/frontend/package.json index 29c440988..73651327f 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -104,5 +104,8 @@ ], "author": "Gradido-Akademie - https://www.gradido.net/", "license": "Apache-2.0", - "description": "Gradido, the Natural Economy of Life, is a way to worldwide prosperity and peace in harmony with nature. - Gradido, die Natürliche Ökonomie des lebens, ist ein Weg zu weltweitem Wohlstand und Frieden in Harmonie mit der Natur." + "description": "Gradido, the Natural Economy of Life, is a way to worldwide prosperity and peace in harmony with nature. - Gradido, die Natürliche Ökonomie des lebens, ist ein Weg zu weltweitem Wohlstand und Frieden in Harmonie mit der Natur.", + "nodemonConfig": { + "ignore": ["**/*.spec.js"] + } } From cb748d8c503ba8e5a99453c31e3ee6d8cb560dc1 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 6 Feb 2023 18:47:58 +0100 Subject: [PATCH 69/91] refactor(backend): get last transaction by only one function --- backend/src/graphql/resolver/BalanceResolver.ts | 4 +++- .../src/graphql/resolver/ContributionResolver.ts | 15 ++++++--------- .../graphql/resolver/TransactionLinkResolver.ts | 10 +++------- .../src/graphql/resolver/TransactionResolver.ts | 7 +++---- .../resolver/accessLayer/getLastTransaction.ts | 14 ++++++++++++++ backend/src/util/validate.ts | 4 ++-- 6 files changed, 31 insertions(+), 23 deletions(-) create mode 100644 backend/src/graphql/resolver/accessLayer/getLastTransaction.ts diff --git a/backend/src/graphql/resolver/BalanceResolver.ts b/backend/src/graphql/resolver/BalanceResolver.ts index 26f9cd656..ae099bcde 100644 --- a/backend/src/graphql/resolver/BalanceResolver.ts +++ b/backend/src/graphql/resolver/BalanceResolver.ts @@ -15,6 +15,8 @@ import { calculateDecay } from '@/util/decay' import { RIGHTS } from '@/auth/RIGHTS' import { GdtResolver } from './GdtResolver' +import { getLastTransaction } from './accessLayer/getLastTransaction' + @Resolver() export class BalanceResolver { @Authorized([RIGHTS.BALANCE]) @@ -32,7 +34,7 @@ export class BalanceResolver { const lastTransaction = context.lastTransaction ? context.lastTransaction - : await dbTransaction.findOne({ userId: user.id }, { order: { id: 'DESC' } }) + : await getLastTransaction(user.id) logger.debug(`lastTransaction=${lastTransaction}`) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index c7946d2c8..a03453c9a 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -54,6 +54,8 @@ import { } from '@/emails/sendEmailVariants' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' +import { getLastTransaction } from './accessLayer/getLastTransaction' + @Resolver() export class ContributionResolver { @Authorized([RIGHTS.CREATE_CONTRIBUTION]) @@ -613,16 +615,11 @@ export class ContributionResolver { const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() await queryRunner.startTransaction('REPEATABLE READ') // 'READ COMMITTED') - try { - const lastTransaction = await queryRunner.manager - .createQueryBuilder() - .select('transaction') - .from(DbTransaction, 'transaction') - .where('transaction.userId = :id', { id: contribution.userId }) - .orderBy('transaction.id', 'DESC') - .getOne() - logger.info('lastTransaction ID', lastTransaction ? lastTransaction.id : 'undefined') + const lastTransaction = await getLastTransaction(contribution.userId) + logger.info('lastTransaction ID', lastTransaction ? lastTransaction.id : 'undefined') + + try { let newBalance = new Decimal(0) let decay: Decay | null = null if (lastTransaction) { diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.ts b/backend/src/graphql/resolver/TransactionLinkResolver.ts index df70b4bc9..f8f61686e 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.ts @@ -33,6 +33,8 @@ import { executeTransaction } from './TransactionResolver' import QueryLinkResult from '@union/QueryLinkResult' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' +import { getLastTransaction } from './accessLayer/getLastTransaction' + // TODO: do not export, test it inside the resolver export const transactionLinkCode = (date: Date): string => { const time = date.getTime().toString(16) @@ -275,13 +277,7 @@ export class TransactionLinkResolver { await queryRunner.manager.insert(DbContribution, contribution) - const lastTransaction = await queryRunner.manager - .createQueryBuilder() - .select('transaction') - .from(DbTransaction, 'transaction') - .where('transaction.userId = :id', { id: user.id }) - .orderBy('transaction.id', 'DESC') - .getOne() + const lastTransaction = await getLastTransaction(user.id) let newBalance = new Decimal(0) let decay: Decay | null = null diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 2f97596b2..2a160a1ee 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -38,6 +38,8 @@ import { findUserByEmail } from './UserResolver' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' +import { getLastTransaction } from './accessLayer/getLastTransaction' + export const executeTransaction = async ( amount: Decimal, memo: string, @@ -208,10 +210,7 @@ export class TransactionResolver { logger.info(`transactionList(user=${user.firstName}.${user.lastName}, ${user.emailId})`) // find current balance - const lastTransaction = await dbTransaction.findOne( - { userId: user.id }, - { order: { id: 'DESC' }, relations: ['contribution'] }, - ) + const lastTransaction = await getLastTransaction(user.id, ['contribution']) logger.debug(`lastTransaction=${lastTransaction}`) const balanceResolver = new BalanceResolver() diff --git a/backend/src/graphql/resolver/accessLayer/getLastTransaction.ts b/backend/src/graphql/resolver/accessLayer/getLastTransaction.ts new file mode 100644 index 000000000..5b3e862c2 --- /dev/null +++ b/backend/src/graphql/resolver/accessLayer/getLastTransaction.ts @@ -0,0 +1,14 @@ +import { Transaction as DbTransaction } from '@entity/Transaction' + +export const getLastTransaction = async ( + userId: number, + relations?: string[], +): Promise => { + return DbTransaction.findOne( + { userId }, + { + order: { balanceDate: 'DESC', id: 'DESC' }, + relations, + }, + ) +} diff --git a/backend/src/util/validate.ts b/backend/src/util/validate.ts index 397a38730..a5d7c4e5f 100644 --- a/backend/src/util/validate.ts +++ b/backend/src/util/validate.ts @@ -1,10 +1,10 @@ import { calculateDecay } from './decay' import Decimal from 'decimal.js-light' -import { Transaction } from '@entity/Transaction' import { Decay } from '@model/Decay' import { getCustomRepository } from '@dbTools/typeorm' import { TransactionLinkRepository } from '@repository/TransactionLink' import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' +import { getLastTransaction } from '../graphql/resolver/accessLayer/getLastTransaction' function isStringBoolean(value: string): boolean { const lowerValue = value.toLowerCase() @@ -20,7 +20,7 @@ async function calculateBalance( time: Date, transactionLink?: dbTransactionLink | null, ): Promise<{ balance: Decimal; decay: Decay; lastTransactionId: number } | null> { - const lastTransaction = await Transaction.findOne({ userId }, { order: { id: 'DESC' } }) + const lastTransaction = await getLastTransaction(userId) if (!lastTransaction) return null const decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time) From 5a78228af31269de4b6a9818cbf699689d6b427f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 7 Feb 2023 00:16:53 +0100 Subject: [PATCH 70/91] remove EVENT_PROTOCOL_DISABLED from backend & bare_metal deployment --- backend/.env.dist | 3 --- backend/.env.template | 3 --- backend/src/config/index.ts | 7 +------ backend/src/event/EventProtocolEmitter.ts | 6 ------ deployment/bare_metal/.env.dist | 5 +---- 5 files changed, 2 insertions(+), 22 deletions(-) diff --git a/backend/.env.dist b/backend/.env.dist index ba5b89fa9..3e54b0566 100644 --- a/backend/.env.dist +++ b/backend/.env.dist @@ -55,9 +55,6 @@ EMAIL_CODE_REQUEST_TIME=10 # Webhook WEBHOOK_ELOPAGE_SECRET=secret -# EventProtocol -EVENT_PROTOCOL_DISABLED=false - # SET LOG LEVEL AS NEEDED IN YOUR .ENV # POSSIBLE VALUES: all | trace | debug | info | warn | error | fatal # LOG_LEVEL=info diff --git a/backend/.env.template b/backend/.env.template index f73b87353..e75798325 100644 --- a/backend/.env.template +++ b/backend/.env.template @@ -54,9 +54,6 @@ EMAIL_CODE_REQUEST_TIME=$EMAIL_CODE_REQUEST_TIME # Webhook WEBHOOK_ELOPAGE_SECRET=$WEBHOOK_ELOPAGE_SECRET -# EventProtocol -EVENT_PROTOCOL_DISABLED=$EVENT_PROTOCOL_DISABLED - # Federation FEDERATION_DHT_TOPIC=$FEDERATION_DHT_TOPIC FEDERATION_DHT_SEED=$FEDERATION_DHT_SEED diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index d010b4ab0..63ea9c5b6 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -17,7 +17,7 @@ const constants = { LOG_LEVEL: process.env.LOG_LEVEL || 'info', CONFIG_VERSION: { DEFAULT: 'DEFAULT', - EXPECTED: 'v14.2022-12-22', + EXPECTED: 'v15.2023-02-07', CURRENT: '', }, } @@ -99,11 +99,6 @@ const webhook = { WEBHOOK_ELOPAGE_SECRET: process.env.WEBHOOK_ELOPAGE_SECRET || 'secret', } -const eventProtocol = { - // global switch to enable writing of EventProtocol-Entries - EVENT_PROTOCOL_DISABLED: process.env.EVENT_PROTOCOL_DISABLED === 'true' || false, -} - // This is needed by graphql-directive-auth process.env.APP_SECRET = server.JWT_SECRET diff --git a/backend/src/event/EventProtocolEmitter.ts b/backend/src/event/EventProtocolEmitter.ts index 9cde4939f..a87e8a256 100644 --- a/backend/src/event/EventProtocolEmitter.ts +++ b/backend/src/event/EventProtocolEmitter.ts @@ -1,14 +1,8 @@ import { Event } from '@/event/Event' import { backendLogger as logger } from '@/server/logger' import { EventProtocol } from '@entity/EventProtocol' -import CONFIG from '@/config' export const writeEvent = async (event: Event): Promise => { - if (CONFIG.EVENT_PROTOCOL_DISABLED) { - logger.info('EventProtocol is disabled', CONFIG.EVENT_PROTOCOL_DISABLED) - return null - } - logger.info('writeEvent', event) const dbEvent = new EventProtocol() dbEvent.type = event.type diff --git a/deployment/bare_metal/.env.dist b/deployment/bare_metal/.env.dist index e816c9236..9c9c7ac82 100644 --- a/deployment/bare_metal/.env.dist +++ b/deployment/bare_metal/.env.dist @@ -27,7 +27,7 @@ COMMUNITY_DESCRIPTION="Gradido Development Stage1 Test Community" COMMUNITY_SUPPORT_MAIL=support@supportmail.com # backend -BACKEND_CONFIG_VERSION=v14.2022-12-22 +BACKEND_CONFIG_VERSION=v15.2023-02-07 JWT_EXPIRES_IN=10m GDT_API_URL=https://gdt.gradido.net @@ -56,9 +56,6 @@ EMAIL_CODE_REQUEST_TIME=10 WEBHOOK_ELOPAGE_SECRET=secret -# EventProtocol -EVENT_PROTOCOL_DISABLED=false - # Federation # if you set the value of FEDERATION_DHT_TOPIC, the DHT hyperswarm will start to announce and listen # on an hash created from this topic From 24ca1563b898c338b4870cc50489d7ed753c5f20 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 7 Feb 2023 00:17:37 +0100 Subject: [PATCH 71/91] remove EVENT_PROTOCOL_DISABLED from dht-node --- dht-node/.env.dist | 3 --- dht-node/.env.template | 3 --- dht-node/src/config/index.ts | 7 +------ 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/dht-node/.env.dist b/dht-node/.env.dist index c1641ea98..09e25ccb6 100644 --- a/dht-node/.env.dist +++ b/dht-node/.env.dist @@ -8,9 +8,6 @@ DB_PASSWORD= DB_DATABASE=gradido_community TYPEORM_LOGGING_RELATIVE_PATH=typeorm.dht-node.log -# EventProtocol -EVENT_PROTOCOL_DISABLED=false - # SET LOG LEVEL AS NEEDED IN YOUR .ENV # POSSIBLE VALUES: all | trace | debug | info | warn | error | fatal # LOG_LEVEL=info diff --git a/dht-node/.env.template b/dht-node/.env.template index eca7cb277..b31667fdb 100644 --- a/dht-node/.env.template +++ b/dht-node/.env.template @@ -8,9 +8,6 @@ DB_PASSWORD=$DB_PASSWORD DB_DATABASE=gradido_community TYPEORM_LOGGING_RELATIVE_PATH=$TYPEORM_LOGGING_RELATIVE_PATH -# EventProtocol -EVENT_PROTOCOL_DISABLED=$EVENT_PROTOCOL_DISABLED - # Federation FEDERATION_DHT_TOPIC=$FEDERATION_DHT_TOPIC FEDERATION_DHT_SEED=$FEDERATION_DHT_SEED diff --git a/dht-node/src/config/index.ts b/dht-node/src/config/index.ts index 02cbb20e9..cb6fe7c77 100644 --- a/dht-node/src/config/index.ts +++ b/dht-node/src/config/index.ts @@ -9,7 +9,7 @@ const constants = { LOG_LEVEL: process.env.LOG_LEVEL || 'info', CONFIG_VERSION: { DEFAULT: 'DEFAULT', - EXPECTED: 'v1.2023-01-01', + EXPECTED: 'v2.2023-02-07', CURRENT: '', }, } @@ -28,11 +28,6 @@ const database = { process.env.TYPEORM_LOGGING_RELATIVE_PATH || 'typeorm.dht-node.log', } -const eventProtocol = { - // global switch to enable writing of EventProtocol-Entries - EVENT_PROTOCOL_DISABLED: process.env.EVENT_PROTOCOL_DISABLED === 'true' || false, -} - const federation = { FEDERATION_DHT_TOPIC: process.env.FEDERATION_DHT_TOPIC || 'GRADIDO_HUB', FEDERATION_DHT_SEED: process.env.FEDERATION_DHT_SEED || null, From 4d3ea26365ef873292207b4b10166abc969af850 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 7 Feb 2023 00:17:49 +0100 Subject: [PATCH 72/91] remove comment on federation --- federation/src/config/index.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/federation/src/config/index.ts b/federation/src/config/index.ts index c2d81d2c8..588f52c60 100644 --- a/federation/src/config/index.ts +++ b/federation/src/config/index.ts @@ -52,10 +52,6 @@ const community = { process.env.COMMUNITY_DESCRIPTION || 'Die lokale Entwicklungsumgebung von Gradido.', } */ -// const eventProtocol = { -// global switch to enable writing of EventProtocol-Entries -// EVENT_PROTOCOL_DISABLED: process.env.EVENT_PROTOCOL_DISABLED === 'true' || false, -// } // This is needed by graphql-directive-auth // process.env.APP_SECRET = server.JWT_SECRET From 151f381d9405a03f4bdb196ccff61621f99404ee Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 7 Feb 2023 00:29:07 +0100 Subject: [PATCH 73/91] fix config aggregation on dht-node & backend --- backend/src/config/index.ts | 1 - dht-node/src/config/index.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 63ea9c5b6..4cca9e0e2 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -134,7 +134,6 @@ const CONFIG = { ...email, ...loginServer, ...webhook, - ...eventProtocol, ...federation, } diff --git a/dht-node/src/config/index.ts b/dht-node/src/config/index.ts index cb6fe7c77..795925ee3 100644 --- a/dht-node/src/config/index.ts +++ b/dht-node/src/config/index.ts @@ -50,7 +50,6 @@ const CONFIG = { ...constants, ...server, ...database, - ...eventProtocol, ...federation, } From 8544eb51e9f0e1c8ca22e4fec5719cbd0b493f10 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 7 Feb 2023 07:10:01 +0100 Subject: [PATCH 74/91] Fix some lint problems. --- admin/package.json | 4 +++- frontend/package.json | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/admin/package.json b/admin/package.json index 30e93239b..5bb3d1cd3 100644 --- a/admin/package.json +++ b/admin/package.json @@ -88,6 +88,8 @@ "not ie <= 10" ], "nodemonConfig": { - "ignore": ["**/*.spec.js"] + "ignore": [ + "**/*.spec.js" + ] } } diff --git a/frontend/package.json b/frontend/package.json index 73651327f..bf26c7529 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -106,6 +106,8 @@ "license": "Apache-2.0", "description": "Gradido, the Natural Economy of Life, is a way to worldwide prosperity and peace in harmony with nature. - Gradido, die Natürliche Ökonomie des lebens, ist ein Weg zu weltweitem Wohlstand und Frieden in Harmonie mit der Natur.", "nodemonConfig": { - "ignore": ["**/*.spec.js"] + "ignore": [ + "**/*.spec.js" + ] } } From a04d4d39855b42200b201081d9fc4ffe027d1f03 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 7 Feb 2023 11:01:35 +0100 Subject: [PATCH 75/91] remove empty string check since we do a length check on memeo & name --- .../resolver/ContributionLinkResolver.test.ts | 42 ------------------- .../resolver/ContributionLinkResolver.ts | 8 ---- 2 files changed, 50 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionLinkResolver.test.ts b/backend/src/graphql/resolver/ContributionLinkResolver.test.ts index 46296e009..49bca2c42 100644 --- a/backend/src/graphql/resolver/ContributionLinkResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionLinkResolver.test.ts @@ -319,27 +319,6 @@ describe('Contribution Links', () => { ) }) - it('returns an error if name is an empty string', async () => { - jest.clearAllMocks() - await expect( - mutate({ - mutation: createContributionLink, - variables: { - ...variables, - name: '', - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - errors: [new GraphQLError('The name must be initialized')], - }), - ) - }) - - it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith('The name must be initialized') - }) - it('returns an error if name is shorter than 5 characters', async () => { jest.clearAllMocks() await expect( @@ -382,27 +361,6 @@ describe('Contribution Links', () => { expect(logger.error).toBeCalledWith('The value of name is too long', 101) }) - it('returns an error if memo is an empty string', async () => { - jest.clearAllMocks() - await expect( - mutate({ - mutation: createContributionLink, - variables: { - ...variables, - memo: '', - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - errors: [new GraphQLError('The memo must be initialized')], - }), - ) - }) - - it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith('The memo must be initialized') - }) - it('returns an error if memo is shorter than 5 characters', async () => { jest.clearAllMocks() await expect( diff --git a/backend/src/graphql/resolver/ContributionLinkResolver.ts b/backend/src/graphql/resolver/ContributionLinkResolver.ts index 6a7a71391..39f202848 100644 --- a/backend/src/graphql/resolver/ContributionLinkResolver.ts +++ b/backend/src/graphql/resolver/ContributionLinkResolver.ts @@ -40,20 +40,12 @@ export class ContributionLinkResolver { }: ContributionLinkArgs, ): Promise { isStartEndDateValid(validFrom, validTo) - // TODO: this should be enforced by the schema. - if (!name) { - throw new LogError('The name must be initialized') - } if (name.length < CONTRIBUTIONLINK_NAME_MIN_CHARS) { throw new LogError('The value of name is too short', name.length) } if (name.length > CONTRIBUTIONLINK_NAME_MAX_CHARS) { throw new LogError('The value of name is too long', name.length) } - // TODO: this should be enforced by the schema. - if (!memo) { - throw new LogError('The memo must be initialized') - } if (memo.length < MEMO_MIN_CHARS) { throw new LogError('The value of memo is too short', memo.length) } From 0b8ac928cf38e2f8ef66996efa3c68c92048bebd Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 7 Feb 2023 18:24:20 +0100 Subject: [PATCH 76/91] more detailed error messages --- .../ContributionMessageResolver.test.ts | 32 ++++++++++++++----- .../resolver/ContributionMessageResolver.ts | 4 +-- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index 36d78c382..f3e5e865d 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -99,14 +99,18 @@ describe('ContributionMessageResolver', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('ContributionMessage was not sent successfully')], + errors: [ + new GraphQLError( + 'ContributionMessage was not sent successfully: Error: Contribution not found', + ), + ], }), ) }) it('logs the error thrown', () => { expect(logger.error).toBeCalledWith( - 'ContributionMessage was not sent successfully', + 'ContributionMessage was not sent successfully: Error: Contribution not found', new Error('Contribution not found'), ) }) @@ -135,14 +139,18 @@ describe('ContributionMessageResolver', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('ContributionMessage was not sent successfully')], + errors: [ + new GraphQLError( + 'ContributionMessage was not sent successfully: Error: Admin can not answer on his own contribution', + ), + ], }), ) }) it('logs the error thrown', () => { expect(logger.error).toBeCalledWith( - 'ContributionMessage was not sent successfully', + 'ContributionMessage was not sent successfully: Error: Admin can not answer on his own contribution', new Error('Admin can not answer on his own contribution'), ) }) @@ -229,14 +237,18 @@ describe('ContributionMessageResolver', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('ContributionMessage was not sent successfully')], + errors: [ + new GraphQLError( + 'ContributionMessage was not sent successfully: Error: Contribution not found', + ), + ], }), ) }) it('logs the error thrown', () => { expect(logger.error).toBeCalledWith( - 'ContributionMessage was not sent successfully', + 'ContributionMessage was not sent successfully: Error: Contribution not found', new Error('Contribution not found'), ) }) @@ -257,14 +269,18 @@ describe('ContributionMessageResolver', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('ContributionMessage was not sent successfully')], + errors: [ + new GraphQLError( + 'ContributionMessage was not sent successfully: Error: Can not send message to contribution of another user', + ), + ], }), ) }) it('logs the error thrown', () => { expect(logger.error).toBeCalledWith( - 'ContributionMessage was not sent successfully', + 'ContributionMessage was not sent successfully: Error: Can not send message to contribution of another user', new Error('Can not send message to contribution of another user'), ) }) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index 3e6f86e53..4248946b1 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -54,7 +54,7 @@ export class ContributionMessageResolver { await queryRunner.commitTransaction() } catch (e) { await queryRunner.rollbackTransaction() - throw new LogError('ContributionMessage was not sent successfully', e) + throw new LogError(`ContributionMessage was not sent successfully: ${e}`, e) } finally { await queryRunner.release() } @@ -144,7 +144,7 @@ export class ContributionMessageResolver { await queryRunner.commitTransaction() } catch (e) { await queryRunner.rollbackTransaction() - throw new LogError('ContributionMessage was not sent successfully', e) + throw new LogError(`ContributionMessage was not sent successfully: ${e}`, e) } finally { await queryRunner.release() } From 9c282c0142ab41b486801eeb8de93a3abb03f217 Mon Sep 17 00:00:00 2001 From: mahula Date: Tue, 7 Feb 2023 21:09:16 +0100 Subject: [PATCH 77/91] add email template for deleted contributions --- .../templates/contributionDeleted/html.pug | 16 ++++++++++++++++ .../templates/contributionDeleted/subject.pug | 1 + 2 files changed, 17 insertions(+) create mode 100644 backend/src/emails/templates/contributionDeleted/html.pug create mode 100644 backend/src/emails/templates/contributionDeleted/subject.pug diff --git a/backend/src/emails/templates/contributionDeleted/html.pug b/backend/src/emails/templates/contributionDeleted/html.pug new file mode 100644 index 000000000..5343a9f10 --- /dev/null +++ b/backend/src/emails/templates/contributionDeleted/html.pug @@ -0,0 +1,16 @@ +doctype html +html(lang=locale) + head + title= t('emails.contributionDeleted.subject') + body + h1(style='margin-bottom: 24px;')= t('emails.contributionDeleted.subject') + #container.col + include ../hello.pug + p= t('emails.contributionDeleted.contributionDeleted', { senderFirstName, senderLastName, contributionMemo }) + p= t('emails.contributionDeleted.toSeeContributionsAndMessages') + p + = t('emails.general.linkToYourAccount') + = " " + a(href=overviewURL) #{overviewURL} + p= t('emails.general.pleaseDoNotReply') + include ../greatingFormularImprint.pug diff --git a/backend/src/emails/templates/contributionDeleted/subject.pug b/backend/src/emails/templates/contributionDeleted/subject.pug new file mode 100644 index 000000000..024588472 --- /dev/null +++ b/backend/src/emails/templates/contributionDeleted/subject.pug @@ -0,0 +1 @@ += t('emails.contributionDeleted.subject') From bd9307612f1a0be7b38dbae61bc9f0ffeb358df6 Mon Sep 17 00:00:00 2001 From: mahula Date: Tue, 7 Feb 2023 21:10:35 +0100 Subject: [PATCH 78/91] adapt backend locales to changes regarding denying and deleting contributuins --- backend/src/locales/de.json | 9 +++++++-- backend/src/locales/en.json | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/backend/src/locales/de.json b/backend/src/locales/de.json index 38b53508b..304ae2adc 100644 --- a/backend/src/locales/de.json +++ b/backend/src/locales/de.json @@ -23,8 +23,13 @@ "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" }, - "contributionRejected": { - "commonGoodContributionRejected": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde von {senderFirstName} {senderLastName} abgelehnt.", + "contributionDeleted": { + "commonGoodContributionDeleted": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde von {senderFirstName} {senderLastName} gelöscht.", + "subject": "Gradido: Dein Gemeinwohl-Beitrag wurde gelöscht", + "toSeeContributionsAndMessages": "Um deine Gemeinwohl-Beiträge und dazugehörige Nachrichten zu sehen, gehe in deinem Gradido-Konto ins Menü „Gemeinschaft“ auf den Tab „Meine Beiträge zum Gemeinwohl“!" + }, + "contributionDenied": { + "commonGoodContributionDenied": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde von {senderFirstName} {senderLastName} abgelehnt.", "subject": "Gradido: Dein Gemeinwohl-Beitrag wurde abgelehnt", "toSeeContributionsAndMessages": "Um deine Gemeinwohl-Beiträge und dazugehörige Nachrichten zu sehen, gehe in deinem Gradido-Konto ins Menü „Gemeinschaft“ auf den Tab „Meine Beiträge zum Gemeinwohl“!" }, diff --git a/backend/src/locales/en.json b/backend/src/locales/en.json index 5cde70d26..bdc92b2cf 100644 --- a/backend/src/locales/en.json +++ b/backend/src/locales/en.json @@ -23,6 +23,11 @@ "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" }, + "contributionDeleted": { + "commonGoodContributionDeleted": "Your public good contribution “{contributionMemo}” was deleted by {senderFirstName} {senderLastName}.", + "subject": "Gradido: Your common good contribution was deleted", + "toSeeContributionsAndMessages": "To see your common good contributions and related messages, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!" + }, "contributionDenied": { "commonGoodContributionDenied": "Your public good contribution “{contributionMemo}” was rejected by {senderFirstName} {senderLastName}.", "subject": "Gradido: Your common good contribution was rejected", From f980ca20b1e8be677a9e2bb81f210ee4c77df135 Mon Sep 17 00:00:00 2001 From: mahula Date: Tue, 7 Feb 2023 21:11:34 +0100 Subject: [PATCH 79/91] adapt backend's email sending to added deletion email --- backend/src/emails/sendEmailVariants.ts | 26 +++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/backend/src/emails/sendEmailVariants.ts b/backend/src/emails/sendEmailVariants.ts index 681ee56af..4e3881829 100644 --- a/backend/src/emails/sendEmailVariants.ts +++ b/backend/src/emails/sendEmailVariants.ts @@ -103,6 +103,32 @@ export const sendContributionConfirmedEmail = (data: { }) } +export const sendContributionDeletedEmail = (data: { + firstName: string + lastName: string + email: string + language: string + senderFirstName: string + senderLastName: string + contributionMemo: string +}): Promise | null> => { + return sendEmailTranslated({ + receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, + template: 'contributionDeleted', + locals: { + firstName: data.firstName, + lastName: data.lastName, + locale: data.language, + senderFirstName: data.senderFirstName, + senderLastName: data.senderLastName, + contributionMemo: data.contributionMemo, + overviewURL: CONFIG.EMAIL_LINK_OVERVIEW, + supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, + communityURL: CONFIG.COMMUNITY_URL, + }, + }) +} + export const sendContributionDeniedEmail = (data: { firstName: string lastName: string From 5c42ec828a6d55d1107d6a0d19dd1c7427776ea7 Mon Sep 17 00:00:00 2001 From: mahula Date: Tue, 7 Feb 2023 21:40:02 +0100 Subject: [PATCH 80/91] fix admin right to deny confirmation --- backend/src/auth/RIGHTS.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index 98f6cf118..2d836dad5 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -45,6 +45,7 @@ export enum RIGHTS { ADMIN_CREATE_CONTRIBUTIONS = 'ADMIN_CREATE_CONTRIBUTIONS', ADMIN_UPDATE_CONTRIBUTION = 'ADMIN_UPDATE_CONTRIBUTION', ADMIN_DELETE_CONTRIBUTION = 'ADMIN_DELETE_CONTRIBUTION', + ADMIN_DENY_CONTRIBUTION = 'DENY_CONTRIBUTION', LIST_UNCONFIRMED_CONTRIBUTIONS = 'LIST_UNCONFIRMED_CONTRIBUTIONS', CONFIRM_CONTRIBUTION = 'CONFIRM_CONTRIBUTION', SEND_ACTIVATION_EMAIL = 'SEND_ACTIVATION_EMAIL', @@ -54,5 +55,4 @@ export enum RIGHTS { DELETE_CONTRIBUTION_LINK = 'DELETE_CONTRIBUTION_LINK', UPDATE_CONTRIBUTION_LINK = 'UPDATE_CONTRIBUTION_LINK', ADMIN_CREATE_CONTRIBUTION_MESSAGE = 'ADMIN_CREATE_CONTRIBUTION_MESSAGE', - DENY_CONTRIBUTION = 'DENY_CONTRIBUTION', } From 2fc2ecf5af37ed421f4721765cd1e98fd921f281 Mon Sep 17 00:00:00 2001 From: mahula Date: Tue, 7 Feb 2023 21:46:44 +0100 Subject: [PATCH 81/91] WIP add elements to get contribution denying working in backend --- backend/src/event/Event.ts | 7 +++ .../graphql/resolver/ContributionResolver.ts | 51 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts index 6a1233224..786d48eed 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -298,6 +298,13 @@ export class Event { return this } + public setEventAdminContributionDeny(ev: EventAdminContributionDeny): Event { + this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) + this.type = EventProtocolType.ADMIN_CONTRIBUTION_DENY + + return this + } + public setEventAdminContributionUpdate(ev: EventAdminContributionUpdate): Event { this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) this.type = EventProtocolType.ADMIN_CONTRIBUTION_UPDATE diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index bf80bcb4d..01a681e52 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -44,6 +44,7 @@ import { EventContributionConfirm, EventAdminContributionCreate, EventAdminContributionDelete, + EventAdminContributionDeny, EventAdminContributionUpdate, } from '@/event/Event' import { writeEvent } from '@/event/EventProtocolEmitter' @@ -559,6 +560,56 @@ export class ContributionResolver { return !!res } + @Authorized([RIGHTS.ADMIN_DENY_CONTRIBUTION]) + @Mutation(() => Boolean) + async adminDenyContribution( + @Arg('id', () => Int) id: number, + @Ctx() context: Context, + ): Promise { + 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.') + } + if (contribution.confirmedAt) { + logger.error('A confirmed contribution can not be denied') + throw new Error('A confirmed contribution can not be denied') + } + const moderator = getUser(context) + if ( + contribution.contributionType === ContributionType.USER && + contribution.userId === moderator.id + ) { + throw new Error('Own contribution can not be denied as admin') + } + const user = await DbUser.findOneOrFail( + { id: contribution.userId }, + { relations: ['emailContact'] }, + ) + contribution.contributionStatus = ContributionStatus.DENIED + contribution.deniedBy = moderator.id + await contribution.save() + const res = await contribution.softRemove() + + const event = new Event() + const eventAdminContributionDeny = new EventAdminContributionDeny() + eventAdminContributionDeny.userId = contribution.userId + eventAdminContributionDeny.amount = contribution.amount + eventAdminContributionDeny.contributionId = contribution.id + await writeEvent(event.setEventAdminContributionDeny(eventAdminContributionDeny)) + sendContributionDeniedEmail({ + firstName: user.firstName, + lastName: user.lastName, + email: user.emailContact.email, + language: user.language, + senderFirstName: moderator.firstName, + senderLastName: moderator.lastName, + contributionMemo: contribution.memo, + }) + + return !!res + } + @Authorized([RIGHTS.CONFIRM_CONTRIBUTION]) @Mutation(() => Boolean) async confirmContribution( From 0b1d6b5237dc883ac0bd39f049c053256492c13f Mon Sep 17 00:00:00 2001 From: mahula Date: Tue, 7 Feb 2023 21:56:32 +0100 Subject: [PATCH 82/91] add deying to events and event protocol --- backend/src/event/Event.ts | 1 + backend/src/event/EventProtocolType.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts index 786d48eed..77e86ad46 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -67,6 +67,7 @@ export class EventTransactionReceiveRedeem extends EventBasicTxX {} export class EventContributionCreate extends EventBasicCt {} export class EventAdminContributionCreate extends EventBasicCt {} export class EventAdminContributionDelete extends EventBasicCt {} +export class EventAdminContributionDeny extends EventBasicCt {} export class EventAdminContributionUpdate extends EventBasicCt {} export class EventUserCreateContributionMessage extends EventBasicCtMsg {} export class EventAdminCreateContributionMessage extends EventBasicCtMsg {} diff --git a/backend/src/event/EventProtocolType.ts b/backend/src/event/EventProtocolType.ts index b7c2f0151..ccd15d238 100644 --- a/backend/src/event/EventProtocolType.ts +++ b/backend/src/event/EventProtocolType.ts @@ -35,6 +35,7 @@ export enum EventProtocolType { CONTRIBUTION_UPDATE = 'CONTRIBUTION_UPDATE', ADMIN_CONTRIBUTION_CREATE = 'ADMIN_CONTRIBUTION_CREATE', ADMIN_CONTRIBUTION_DELETE = 'ADMIN_CONTRIBUTION_DELETE', + ADMIN_CONTRIBUTION_DENY = 'ADMIN_CONTRIBUTION_DENY', ADMIN_CONTRIBUTION_UPDATE = 'ADMIN_CONTRIBUTION_UPDATE', USER_CREATE_CONTRIBUTION_MESSAGE = 'USER_CREATE_CONTRIBUTION_MESSAGE', ADMIN_CREATE_CONTRIBUTION_MESSAGE = 'ADMIN_CREATE_CONTRIBUTION_MESSAGE', From cf9b06824b1324d30f972968aaab96cfa7643953 Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 8 Feb 2023 08:37:49 +0100 Subject: [PATCH 83/91] set right to deny a contribution --- backend/src/auth/RIGHTS.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index 2d836dad5..98f6cf118 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -45,7 +45,6 @@ export enum RIGHTS { ADMIN_CREATE_CONTRIBUTIONS = 'ADMIN_CREATE_CONTRIBUTIONS', ADMIN_UPDATE_CONTRIBUTION = 'ADMIN_UPDATE_CONTRIBUTION', ADMIN_DELETE_CONTRIBUTION = 'ADMIN_DELETE_CONTRIBUTION', - ADMIN_DENY_CONTRIBUTION = 'DENY_CONTRIBUTION', LIST_UNCONFIRMED_CONTRIBUTIONS = 'LIST_UNCONFIRMED_CONTRIBUTIONS', CONFIRM_CONTRIBUTION = 'CONFIRM_CONTRIBUTION', SEND_ACTIVATION_EMAIL = 'SEND_ACTIVATION_EMAIL', @@ -55,4 +54,5 @@ export enum RIGHTS { DELETE_CONTRIBUTION_LINK = 'DELETE_CONTRIBUTION_LINK', UPDATE_CONTRIBUTION_LINK = 'UPDATE_CONTRIBUTION_LINK', ADMIN_CREATE_CONTRIBUTION_MESSAGE = 'ADMIN_CREATE_CONTRIBUTION_MESSAGE', + DENY_CONTRIBUTION = 'DENY_CONTRIBUTION', } From 274bd8b86413686b3e10398f5510a4e4f33bcade Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 8 Feb 2023 08:39:11 +0100 Subject: [PATCH 84/91] set correct locale key in contribution deleted email template --- backend/src/emails/templates/contributionDeleted/html.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/emails/templates/contributionDeleted/html.pug b/backend/src/emails/templates/contributionDeleted/html.pug index 5343a9f10..d6b3ea207 100644 --- a/backend/src/emails/templates/contributionDeleted/html.pug +++ b/backend/src/emails/templates/contributionDeleted/html.pug @@ -6,7 +6,7 @@ html(lang=locale) h1(style='margin-bottom: 24px;')= t('emails.contributionDeleted.subject') #container.col include ../hello.pug - p= t('emails.contributionDeleted.contributionDeleted', { senderFirstName, senderLastName, contributionMemo }) + p= t('emails.contributionDeleted.commonGoodContributionDeleted', { senderFirstName, senderLastName, contributionMemo }) p= t('emails.contributionDeleted.toSeeContributionsAndMessages') p = t('emails.general.linkToYourAccount') From 01cca65dea9534a10840568b22965fe8e8f8cf49 Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 8 Feb 2023 08:46:36 +0100 Subject: [PATCH 85/91] finish denyContribution method in contribution resolver --- .../graphql/resolver/ContributionResolver.ts | 60 +++---------------- 1 file changed, 9 insertions(+), 51 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 01a681e52..1ed424301 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -51,6 +51,7 @@ import { writeEvent } from '@/event/EventProtocolEmitter' import { calculateDecay } from '@/util/decay' import { sendContributionConfirmedEmail, + sendContributionDeletedEmail, sendContributionDeniedEmail, } from '@/emails/sendEmailVariants' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' @@ -547,57 +548,7 @@ export class ContributionResolver { eventAdminContributionDelete.amount = contribution.amount eventAdminContributionDelete.contributionId = contribution.id await writeEvent(event.setEventAdminContributionDelete(eventAdminContributionDelete)) - sendContributionDeniedEmail({ - firstName: user.firstName, - lastName: user.lastName, - email: user.emailContact.email, - language: user.language, - senderFirstName: moderator.firstName, - senderLastName: moderator.lastName, - contributionMemo: contribution.memo, - }) - - return !!res - } - - @Authorized([RIGHTS.ADMIN_DENY_CONTRIBUTION]) - @Mutation(() => Boolean) - async adminDenyContribution( - @Arg('id', () => Int) id: number, - @Ctx() context: Context, - ): Promise { - 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.') - } - if (contribution.confirmedAt) { - logger.error('A confirmed contribution can not be denied') - throw new Error('A confirmed contribution can not be denied') - } - const moderator = getUser(context) - if ( - contribution.contributionType === ContributionType.USER && - contribution.userId === moderator.id - ) { - throw new Error('Own contribution can not be denied as admin') - } - const user = await DbUser.findOneOrFail( - { id: contribution.userId }, - { relations: ['emailContact'] }, - ) - contribution.contributionStatus = ContributionStatus.DENIED - contribution.deniedBy = moderator.id - await contribution.save() - const res = await contribution.softRemove() - - const event = new Event() - const eventAdminContributionDeny = new EventAdminContributionDeny() - eventAdminContributionDeny.userId = contribution.userId - eventAdminContributionDeny.amount = contribution.amount - eventAdminContributionDeny.contributionId = contribution.id - await writeEvent(event.setEventAdminContributionDeny(eventAdminContributionDeny)) - sendContributionDeniedEmail({ + sendContributionDeletedEmail({ firstName: user.firstName, lastName: user.lastName, email: user.emailContact.email, @@ -818,6 +769,13 @@ export class ContributionResolver { contributionToUpdate.deniedAt = new Date() const res = await contributionToUpdate.save() + const event = new Event() + const eventAdminContributionDeny = new EventAdminContributionDeny() + eventAdminContributionDeny.userId = contributionToUpdate.userId + eventAdminContributionDeny.amount = contributionToUpdate.amount + eventAdminContributionDeny.contributionId = contributionToUpdate.id + await writeEvent(event.setEventAdminContributionDeny(eventAdminContributionDeny)) + sendContributionDeniedEmail({ firstName: user.firstName, lastName: user.lastName, From f5d986664478ec6389a61a6ee2b9d847778901d1 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 8 Feb 2023 10:33:53 +0100 Subject: [PATCH 86/91] remove accessLayer folder --- backend/src/graphql/resolver/BalanceResolver.ts | 2 +- backend/src/graphql/resolver/ContributionResolver.ts | 2 +- backend/src/graphql/resolver/TransactionLinkResolver.ts | 2 +- backend/src/graphql/resolver/TransactionResolver.ts | 2 +- .../resolver/{accessLayer => util}/getLastTransaction.ts | 0 backend/src/util/validate.ts | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename backend/src/graphql/resolver/{accessLayer => util}/getLastTransaction.ts (100%) diff --git a/backend/src/graphql/resolver/BalanceResolver.ts b/backend/src/graphql/resolver/BalanceResolver.ts index ae099bcde..65cccf4d4 100644 --- a/backend/src/graphql/resolver/BalanceResolver.ts +++ b/backend/src/graphql/resolver/BalanceResolver.ts @@ -15,7 +15,7 @@ import { calculateDecay } from '@/util/decay' import { RIGHTS } from '@/auth/RIGHTS' import { GdtResolver } from './GdtResolver' -import { getLastTransaction } from './accessLayer/getLastTransaction' +import { getLastTransaction } from './util/getLastTransaction' @Resolver() export class BalanceResolver { diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index a03453c9a..474e38cf9 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -54,7 +54,7 @@ import { } from '@/emails/sendEmailVariants' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' -import { getLastTransaction } from './accessLayer/getLastTransaction' +import { getLastTransaction } from './util/getLastTransaction' @Resolver() export class ContributionResolver { diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.ts b/backend/src/graphql/resolver/TransactionLinkResolver.ts index f8f61686e..b3376c65f 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.ts @@ -33,7 +33,7 @@ import { executeTransaction } from './TransactionResolver' import QueryLinkResult from '@union/QueryLinkResult' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' -import { getLastTransaction } from './accessLayer/getLastTransaction' +import { getLastTransaction } from './util/getLastTransaction' // TODO: do not export, test it inside the resolver export const transactionLinkCode = (date: Date): string => { diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 2a160a1ee..bc3ce4880 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -38,7 +38,7 @@ import { findUserByEmail } from './UserResolver' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' -import { getLastTransaction } from './accessLayer/getLastTransaction' +import { getLastTransaction } from './util/getLastTransaction' export const executeTransaction = async ( amount: Decimal, diff --git a/backend/src/graphql/resolver/accessLayer/getLastTransaction.ts b/backend/src/graphql/resolver/util/getLastTransaction.ts similarity index 100% rename from backend/src/graphql/resolver/accessLayer/getLastTransaction.ts rename to backend/src/graphql/resolver/util/getLastTransaction.ts diff --git a/backend/src/util/validate.ts b/backend/src/util/validate.ts index a5d7c4e5f..482e9eb50 100644 --- a/backend/src/util/validate.ts +++ b/backend/src/util/validate.ts @@ -4,7 +4,7 @@ import { Decay } from '@model/Decay' import { getCustomRepository } from '@dbTools/typeorm' import { TransactionLinkRepository } from '@repository/TransactionLink' import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' -import { getLastTransaction } from '../graphql/resolver/accessLayer/getLastTransaction' +import { getLastTransaction } from '../graphql/resolver/util/getLastTransaction' function isStringBoolean(value: string): boolean { const lowerValue = value.toLowerCase() From 687ae6e877b0cb72a3c1849e051a56c749222b63 Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 8 Feb 2023 13:22:44 +0100 Subject: [PATCH 87/91] remove commented unused code --- backend/src/graphql/resolver/ContributionResolver.test.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index d48502c69..efea67506 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -22,11 +22,7 @@ import { listContributions, listUnconfirmedContributions, } from '@/seeds/graphql/queries' -import { - // sendAccountActivationEmail, - sendContributionConfirmedEmail, - // sendContributionRejectedEmail, -} from '@/emails/sendEmailVariants' +import { sendContributionConfirmedEmail } from '@/emails/sendEmailVariants' import { cleanDB, resetToken, From b4b180a1d1ec7d0e680dfceec8760722dd7e9a88 Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 8 Feb 2023 13:22:44 +0100 Subject: [PATCH 88/91] remove commented unused code --- backend/src/graphql/resolver/ContributionResolver.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index efea67506..03ad002ad 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -43,7 +43,6 @@ import { EventProtocolType } from '@/event/EventProtocolType' import { logger, i18n as localization } from '@test/testSetup' import { UserInputError } from 'apollo-server-express' -// mock account activation email to avoid console spam // mock account activation email to avoid console spam jest.mock('@/emails/sendEmailVariants', () => { const originalModule = jest.requireActual('@/emails/sendEmailVariants') From 7fa34ab56a9d7993cb67bdaa86b9812038e67d90 Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 8 Feb 2023 18:17:14 +0100 Subject: [PATCH 89/91] adapt unit tests tu changes on email variants in backend --- backend/src/emails/sendEmailVariants.test.ts | 79 ++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/backend/src/emails/sendEmailVariants.test.ts b/backend/src/emails/sendEmailVariants.test.ts index ddbc387a1..7e499feb9 100644 --- a/backend/src/emails/sendEmailVariants.test.ts +++ b/backend/src/emails/sendEmailVariants.test.ts @@ -10,6 +10,7 @@ import { sendAccountMultiRegistrationEmail, sendContributionConfirmedEmail, sendContributionDeniedEmail, + sendContributionDeletedEmail, sendResetPasswordEmail, sendTransactionLinkRedeemedEmail, sendTransactionReceivedEmail, @@ -438,6 +439,84 @@ describe('sendEmailVariants', () => { }) }) + describe('sendContributionDeletedEmail', () => { + beforeAll(async () => { + result = await sendContributionDeletedEmail({ + firstName: 'Peter', + lastName: 'Lustig', + email: 'peter@lustig.de', + language: 'en', + senderFirstName: 'Bibi', + senderLastName: 'Bloxberg', + contributionMemo: 'My contribution.', + }) + }) + + describe('calls "sendEmailTranslated"', () => { + it('with expected parameters', () => { + expect(sendEmailTranslated).toBeCalledWith({ + receiver: { + to: 'Peter Lustig ', + }, + template: 'contributionDeleted', + locals: { + firstName: 'Peter', + lastName: 'Lustig', + locale: 'en', + senderFirstName: 'Bibi', + senderLastName: 'Bloxberg', + contributionMemo: 'My contribution.', + overviewURL: CONFIG.EMAIL_LINK_OVERVIEW, + supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, + communityURL: CONFIG.COMMUNITY_URL, + }, + }) + }) + + it('has expected result', () => { + expect(result).toMatchObject({ + envelope: { + from: 'info@gradido.net', + to: ['peter@lustig.de'], + }, + message: expect.any(String), + originalMessage: expect.objectContaining({ + to: 'Peter Lustig ', + from: 'Gradido (do not answer) ', + attachments: [], + subject: 'Gradido: Your common good contribution was deleted', + html: expect.any(String), + text: expect.stringContaining('GRADIDO: YOUR COMMON GOOD CONTRIBUTION WAS DELETED'), + }), + }) + expect(result.originalMessage.html).toContain('') + expect(result.originalMessage.html).toContain('') + expect(result.originalMessage.html).toContain( + 'Gradido: Your common good contribution was deleted', + ) + expect(result.originalMessage.html).toContain( + '>Gradido: Your common good contribution was deleted', + ) + expect(result.originalMessage.html).toContain('Hello Peter Lustig') + expect(result.originalMessage.html).toContain( + 'Your public good contribution “My contribution.” was deleted by Bibi Bloxberg.', + ) + expect(result.originalMessage.html).toContain( + 'To see your common good contributions and related messages, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!', + ) + expect(result.originalMessage.html).toContain( + `Link to your account: ${CONFIG.EMAIL_LINK_OVERVIEW}`, + ) + expect(result.originalMessage.html).toContain('Please do not reply to this email!') + expect(result.originalMessage.html).toContain('Kind regards,
your Gradido team') + expect(result.originalMessage.html).toContain('—————') + expect(result.originalMessage.html).toContain( + '
Gradido-Akademie Logo

Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland
support@supportmail.com
http://localhost/', + ) + }) + }) + }) + describe('sendResetPasswordEmail', () => { beforeAll(async () => { result = await sendResetPasswordEmail({ From 163c840446a01a7e105c53433fab8aac4ccb764d Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 9 Feb 2023 14:00:23 +0100 Subject: [PATCH 90/91] remove config version from .env.dist --- backend/.env.dist | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/.env.dist b/backend/.env.dist index 3e54b0566..8cf89ff0c 100644 --- a/backend/.env.dist +++ b/backend/.env.dist @@ -1,5 +1,3 @@ -CONFIG_VERSION=v14.2022-12-22 - # Server PORT=4000 JWT_SECRET=secret123 From a04a498882344c4b9be0756829740d41be1007b6 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 9 Feb 2023 14:02:11 +0100 Subject: [PATCH 91/91] remove config version from all .env.dist --- admin/.env.dist | 2 -- database/.env.dist | 2 -- dht-node/.env.dist | 2 -- frontend/.env.dist | 2 -- 4 files changed, 8 deletions(-) diff --git a/admin/.env.dist b/admin/.env.dist index d7044669a..66c84dda8 100644 --- a/admin/.env.dist +++ b/admin/.env.dist @@ -1,5 +1,3 @@ -CONFIG_VERSION=v1.2022-03-18 - GRAPHQL_URI=http://localhost:4000/graphql WALLET_AUTH_URL=http://localhost/authenticate?token={token} WALLET_URL=http://localhost/login diff --git a/database/.env.dist b/database/.env.dist index 58362a7b9..689e4f509 100644 --- a/database/.env.dist +++ b/database/.env.dist @@ -1,5 +1,3 @@ -CONFIG_VERSION=v1.2022-03-18 - DB_HOST=localhost DB_PORT=3306 DB_USER=root diff --git a/dht-node/.env.dist b/dht-node/.env.dist index 09e25ccb6..e005ca748 100644 --- a/dht-node/.env.dist +++ b/dht-node/.env.dist @@ -1,5 +1,3 @@ -CONFIG_VERSION=v1.2023-01-01 - # Database DB_HOST=localhost DB_PORT=3306 diff --git a/frontend/.env.dist b/frontend/.env.dist index 5ce6b430d..427d43359 100644 --- a/frontend/.env.dist +++ b/frontend/.env.dist @@ -1,5 +1,3 @@ -CONFIG_VERSION=v4.2022-12-20 - # Environment DEFAULT_PUBLISHER_ID=2896