From d23a060abaa988c5d6f74a81815dc39f1ca33c6e Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 19 Jul 2022 12:10:09 +0200 Subject: [PATCH 01/72] Add text to send to the sender of a transaction link after the receiver redeem it. --- .../src/mailer/text/transactionReceived.ts | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/backend/src/mailer/text/transactionReceived.ts b/backend/src/mailer/text/transactionReceived.ts index 07d03ad45..336cee3b3 100644 --- a/backend/src/mailer/text/transactionReceived.ts +++ b/backend/src/mailer/text/transactionReceived.ts @@ -29,6 +29,41 @@ Mit freundlichen Grüßen, dein Gradido-Team +Link zu deinem Konto: ${data.overviewURL}`, + }, +} + +export const transactionLinkRedeemed = { + de: { + subject: 'Gradido link eingelösst', + text: (data: { + email: string + senderFirstName: string + senderLastName: string + recipientFirstName: string + recipientLastName: string + senderEmail: string + amount: Decimal + memo: string + overviewURL: string + }): string => + `Hallo ${data.recipientFirstName} ${data.recipientLastName} + +${data.senderFirstName} ${data.senderLastName} (${ + data.senderEmail + }) hat soeben dein Link eingelösst. +Du hattest ihm ${data.amount.toFixed(2).replace('.', ',')} GDD, +mit dem folgenden Text: + +${data.memo} + +gesendet. + +Bitte antworte nicht auf diese E-Mail! + +Mit freundlichen Grüßen, +dein Gradido-Team + Link zu deinem Konto: ${data.overviewURL}`, }, } From c1ec142121a1e7ec682cb2037f26c99f0abf7e11 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 19 Jul 2022 12:10:59 +0200 Subject: [PATCH 02/72] Add method to send email to sender of a transactionLink after the receiver redeemed it. --- .../mailer/sendTransactionReceivedEmail.ts | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/backend/src/mailer/sendTransactionReceivedEmail.ts b/backend/src/mailer/sendTransactionReceivedEmail.ts index 692f92f9a..55f63e37e 100644 --- a/backend/src/mailer/sendTransactionReceivedEmail.ts +++ b/backend/src/mailer/sendTransactionReceivedEmail.ts @@ -1,7 +1,7 @@ import { backendLogger as logger } from '@/server/logger' import Decimal from 'decimal.js-light' import { sendEMail } from './sendEMail' -import { transactionReceived } from './text/transactionReceived' +import { transactionLinkRedeemed, transactionReceived } from './text/transactionReceived' export const sendTransactionReceivedEmail = (data: { senderFirstName: string @@ -26,3 +26,27 @@ export const sendTransactionReceivedEmail = (data: { text: transactionReceived.de.text(data), }) } + +export const sendTransactionLinkRedeemedEmail = (data: { + email: string + senderFirstName: string + senderLastName: string + recipientFirstName: string + recipientLastName: string + senderEmail: string + amount: Decimal + memo: string + overviewURL: string +}): Promise => { + logger.info( + `sendEmail(): to=${data.recipientFirstName} ${data.recipientLastName}, + <${data.email}>, + subject=${transactionLinkRedeemed.de.subject}, + text=${transactionLinkRedeemed.de.text(data)}`, + ) + return sendEMail({ + to: `${data.recipientFirstName} ${data.recipientLastName} <${data.email}>`, + subject: transactionLinkRedeemed.de.subject, + text: transactionLinkRedeemed.de.text(data), + }) +} From 97a83e633a37cc4031e0b465340910a567ee3326 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 19 Jul 2022 12:11:57 +0200 Subject: [PATCH 03/72] Add block to send the email to the sender of a transactionLink after the receiver redeemed it. --- .../src/graphql/resolver/TransactionResolver.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 023e5b2ff..f09657ca4 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -8,7 +8,7 @@ import { Context, getUser } from '@/server/context' import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql' import { getCustomRepository, getConnection } from '@dbTools/typeorm' -import { sendTransactionReceivedEmail } from '@/mailer/sendTransactionReceivedEmail' +import { sendTransactionLinkRedeemedEmail, sendTransactionReceivedEmail } from '@/mailer/sendTransactionReceivedEmail' import { Transaction } from '@model/Transaction' import { TransactionList } from '@model/TransactionList' @@ -156,6 +156,19 @@ export const executeTransaction = async ( memo, overviewURL: CONFIG.EMAIL_LINK_OVERVIEW, }) + if (transactionLink) { + await sendTransactionLinkRedeemedEmail({ + senderFirstName: recipient.firstName, + senderLastName: recipient.lastName, + recipientFirstName: sender.firstName, + recipientLastName: sender.lastName, + email: sender.email, + senderEmail: recipient.email, + amount, + memo, + overviewURL: CONFIG.EMAIL_LINK_OVERVIEW, + }) + } logger.info(`finished executeTransaction successfully`) return true } From 93e0451124890b476f01df7bf00e8e07d33984b8 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 19 Jul 2022 12:24:42 +0200 Subject: [PATCH 04/72] Fix linting. --- backend/src/graphql/resolver/TransactionResolver.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index f09657ca4..a128de520 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -8,7 +8,10 @@ import { Context, getUser } from '@/server/context' import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql' import { getCustomRepository, getConnection } from '@dbTools/typeorm' -import { sendTransactionLinkRedeemedEmail, sendTransactionReceivedEmail } from '@/mailer/sendTransactionReceivedEmail' +import { + sendTransactionLinkRedeemedEmail, + sendTransactionReceivedEmail, +} from '@/mailer/sendTransactionReceivedEmail' import { Transaction } from '@model/Transaction' import { TransactionList } from '@model/TransactionList' From 96b2b9a416ab3babbea31a6438d43454255b268a Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Thu, 11 Aug 2022 14:40:22 +0200 Subject: [PATCH 05/72] Update backend/src/mailer/text/transactionReceived.ts Co-authored-by: Moriz Wahl --- backend/src/mailer/text/transactionReceived.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/mailer/text/transactionReceived.ts b/backend/src/mailer/text/transactionReceived.ts index 336cee3b3..50cdd2ff5 100644 --- a/backend/src/mailer/text/transactionReceived.ts +++ b/backend/src/mailer/text/transactionReceived.ts @@ -51,7 +51,7 @@ export const transactionLinkRedeemed = { ${data.senderFirstName} ${data.senderLastName} (${ data.senderEmail - }) hat soeben dein Link eingelösst. + }) hat soeben deinen Link eingelösst. Du hattest ihm ${data.amount.toFixed(2).replace('.', ',')} GDD, mit dem folgenden Text: From 75d9867c5519239d977b1ed43054a17ba4114991 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Thu, 11 Aug 2022 14:40:36 +0200 Subject: [PATCH 06/72] Update backend/src/mailer/text/transactionReceived.ts Co-authored-by: Moriz Wahl --- backend/src/mailer/text/transactionReceived.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/backend/src/mailer/text/transactionReceived.ts b/backend/src/mailer/text/transactionReceived.ts index 50cdd2ff5..2764d1b60 100644 --- a/backend/src/mailer/text/transactionReceived.ts +++ b/backend/src/mailer/text/transactionReceived.ts @@ -52,12 +52,8 @@ export const transactionLinkRedeemed = { ${data.senderFirstName} ${data.senderLastName} (${ data.senderEmail }) hat soeben deinen Link eingelösst. -Du hattest ihm ${data.amount.toFixed(2).replace('.', ',')} GDD, -mit dem folgenden Text: - -${data.memo} - -gesendet. +Betrag: ${data.amount.toFixed(2).replace('.', ',')} GDD, +Memo: ${data.memo} Bitte antworte nicht auf diese E-Mail! From 3f950a9bb8adbae50d25efa0e4d86752df9d195d Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 15 Aug 2022 11:02:05 +0200 Subject: [PATCH 07/72] Method to retrieve not registered emails. --- backend/src/auth/RIGHTS.ts | 1 + .../src/graphql/resolver/KlicktippResolver.ts | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index d5e2cc7ce..8e51c182e 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -48,4 +48,5 @@ export enum RIGHTS { LIST_CONTRIBUTION_LINKS = 'LIST_CONTRIBUTION_LINKS', DELETE_CONTRIBUTION_LINK = 'DELETE_CONTRIBUTION_LINK', UPDATE_CONTRIBUTION_LINK = 'UPDATE_CONTRIBUTION_LINK', + ADMIN_RETRIEVE_NOT_REGISTERED_EMAILS = 'ADMIN_RETRIEVE_NOT_REGISTERED_EMAILS', } diff --git a/backend/src/graphql/resolver/KlicktippResolver.ts b/backend/src/graphql/resolver/KlicktippResolver.ts index ce9a097e2..fecf6f08c 100644 --- a/backend/src/graphql/resolver/KlicktippResolver.ts +++ b/backend/src/graphql/resolver/KlicktippResolver.ts @@ -7,6 +7,8 @@ import { } from '@/apis/KlicktippController' import { RIGHTS } from '@/auth/RIGHTS' import SubscribeNewsletterArgs from '@arg/SubscribeNewsletterArgs' +import { User } from '@entity/User' +import { backendLogger } from '@/server/logger' @Resolver() export class KlicktippResolver { @@ -35,4 +37,21 @@ export class KlicktippResolver { ): Promise { return await klicktippSignIn(email, language) } + + @Authorized([RIGHTS.ADMIN_RETRIEVE_NOT_REGISTERED_EMAILS]) + @Query(() => [String]) + async retrieveNotRegisteredEmails(): Promise { + const users = await User.find() + const notRegisteredUser = [] + for (let i = 0; i < users.length; i++) { + const user = users[i] + try { + await getKlickTippUser(user.email) + } catch (err) { + notRegisteredUser.push(user.email) + backendLogger.error(`Error with email: ${user.email}; ${err}`) + } + } + return notRegisteredUser + } } From 32b5bf3d6b02adf701b9b071fefa14d7a7f2251c Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 16 Aug 2022 07:01:33 +0200 Subject: [PATCH 08/72] Remove resolver method for klickTipp not registered users. --- .../src/graphql/resolver/KlicktippResolver.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/backend/src/graphql/resolver/KlicktippResolver.ts b/backend/src/graphql/resolver/KlicktippResolver.ts index fecf6f08c..07b872c43 100644 --- a/backend/src/graphql/resolver/KlicktippResolver.ts +++ b/backend/src/graphql/resolver/KlicktippResolver.ts @@ -37,21 +37,4 @@ export class KlicktippResolver { ): Promise { return await klicktippSignIn(email, language) } - - @Authorized([RIGHTS.ADMIN_RETRIEVE_NOT_REGISTERED_EMAILS]) - @Query(() => [String]) - async retrieveNotRegisteredEmails(): Promise { - const users = await User.find() - const notRegisteredUser = [] - for (let i = 0; i < users.length; i++) { - const user = users[i] - try { - await getKlickTippUser(user.email) - } catch (err) { - notRegisteredUser.push(user.email) - backendLogger.error(`Error with email: ${user.email}; ${err}`) - } - } - return notRegisteredUser - } } From 49de7366f44d47ac3c2e4449454f3aad6b4db14f Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 16 Aug 2022 07:02:07 +0200 Subject: [PATCH 09/72] Add method to log every user that is not registered at KlickTipp. --- backend/src/klicktipp.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 backend/src/klicktipp.ts diff --git a/backend/src/klicktipp.ts b/backend/src/klicktipp.ts new file mode 100644 index 000000000..3139cd296 --- /dev/null +++ b/backend/src/klicktipp.ts @@ -0,0 +1,28 @@ +import connection from '@/typeorm/connection' +import { getKlickTippUser } from './apis/KlicktippController' +import { User } from '../../database/entity/User' +import { getConnection } from '@dbTools/typeorm' + +export async function retrieveNotRegisteredEmails(): Promise { + const con = await connection() + if (!con) { + throw new Error('No connection to database') + } + const users = await User.find() + const notRegisteredUser = [] + for (let i = 0; i < users.length; i++) { + const user = users[i] + try { + await getKlickTippUser(user.email) + } catch (err) { + notRegisteredUser.push(user.email) + // eslint-disable-next-line no-console + console.log(`${user.email}`) + } + } + await con.close() + console.log('User die nicht bei KlickTipp vorhanden sind: ', notRegisteredUser) + return notRegisteredUser +} + +retrieveNotRegisteredEmails() From ea85f9c3ae17d458eb917fdd6f0481419fface59 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 16 Aug 2022 07:02:34 +0200 Subject: [PATCH 10/72] Add yarn klicktipp to start the script of not registered user. --- backend/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/package.json b/backend/package.json index c56c8e960..f3033a620 100644 --- a/backend/package.json +++ b/backend/package.json @@ -14,7 +14,8 @@ "dev": "cross-env TZ=UTC nodemon -w src --ext ts --exec ts-node -r tsconfig-paths/register src/index.ts", "lint": "eslint --max-warnings=0 --ext .js,.ts .", "test": "cross-env TZ=UTC NODE_ENV=development jest --runInBand --coverage --forceExit --detectOpenHandles", - "seed": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/index.ts" + "seed": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/index.ts", + "klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/klicktipp.ts" }, "dependencies": { "@types/jest": "^27.0.2", From de2bd1113e0cc6ba833ef97d9570116c27a9cdc9 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 1 Sep 2022 15:38:05 +0200 Subject: [PATCH 11/72] refactor: Improve Statistics Query --- .../graphql/resolver/StatisticsResolver.ts | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/backend/src/graphql/resolver/StatisticsResolver.ts b/backend/src/graphql/resolver/StatisticsResolver.ts index 4c1500839..c2cebbed5 100644 --- a/backend/src/graphql/resolver/StatisticsResolver.ts +++ b/backend/src/graphql/resolver/StatisticsResolver.ts @@ -7,49 +7,49 @@ import { getConnection } from '@dbTools/typeorm' import Decimal from 'decimal.js-light' import { calculateDecay } from '@/util/decay' +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + @Resolver() export class StatisticsResolver { @Authorized([RIGHTS.COMMUNITY_STATISTICS]) @Query(() => CommunityStatistics) - async communityStatistics(): Promise { - const allUsers = await DbUser.find({ withDeleted: true }) + async communityStatistics(@Info() info: any): Promise { - let totalUsers = 0 - let activeUsers = 0 - let deletedUsers = 0 + const allUsers = await DbUser.count({ withDeleted: true }) + const totalUsers = await DbUser.count() + const deletedUsers = allUsers - totalUsers let totalGradidoAvailable: Decimal = new Decimal(0) let totalGradidoUnbookedDecayed: Decimal = new Decimal(0) const receivedCallDate = new Date() - for (let i = 0; i < allUsers.length; i++) { - if (allUsers[i].deletedAt) { - deletedUsers++ - } else { - totalUsers++ - const lastTransaction = await DbTransaction.findOne({ - where: { userId: allUsers[i].id }, - order: { balanceDate: 'DESC' }, - }) - if (lastTransaction) { - activeUsers++ - const decay = calculateDecay( - lastTransaction.balance, - lastTransaction.balanceDate, - receivedCallDate, - ) - if (decay) { - totalGradidoAvailable = totalGradidoAvailable.plus(decay.balance.toString()) - totalGradidoUnbookedDecayed = totalGradidoUnbookedDecayed.plus(decay.decay.toString()) - } - } - } - } - const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() + const lastUserTransactions = await queryRunner.manager + .createQueryBuilder(DbUser, 'user') + .select('transaction.balance', 'balance') + .addSelect('transaction.balance_date', 'balanceDate') + .innerJoin(DbTransaction, 'transaction', 'user.id = transaction.user_id') + .where( + `transaction.balance_date = (SELECT MAX(t.balance_date) FROM transactions AS t WHERE t.user_id = user.id)`, + ) + .orderBy('transaction.balance_date', 'DESC') + .addOrderBy('transaction.id', 'DESC') + .getRawMany() + + const activeUsers = lastUserTransactions.length + + lastUserTransactions.forEach(({ balance, balanceDate }) => { + const decay = calculateDecay(new Decimal(balance), new Date(balanceDate), receivedCallDate) + if (decay) { + totalGradidoAvailable = totalGradidoAvailable.plus(decay.balance.toString()) + totalGradidoUnbookedDecayed = totalGradidoUnbookedDecayed.plus(decay.decay.toString()) + } + }) + const { totalGradidoCreated } = await queryRunner.manager .createQueryBuilder() .select('SUM(transaction.amount) AS totalGradidoCreated') From eb8a245c2d526758c2d9f751a0ebe4fee8a63369 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 1 Sep 2022 16:26:57 +0200 Subject: [PATCH 12/72] fix resolver args --- backend/src/graphql/resolver/StatisticsResolver.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/StatisticsResolver.ts b/backend/src/graphql/resolver/StatisticsResolver.ts index c2cebbed5..b0c061d91 100644 --- a/backend/src/graphql/resolver/StatisticsResolver.ts +++ b/backend/src/graphql/resolver/StatisticsResolver.ts @@ -14,8 +14,7 @@ import { calculateDecay } from '@/util/decay' export class StatisticsResolver { @Authorized([RIGHTS.COMMUNITY_STATISTICS]) @Query(() => CommunityStatistics) - async communityStatistics(@Info() info: any): Promise { - + async communityStatistics(): Promise { const allUsers = await DbUser.count({ withDeleted: true }) const totalUsers = await DbUser.count() const deletedUsers = allUsers - totalUsers From bfbbd26a5704759c47c138a676b2181f74731e92 Mon Sep 17 00:00:00 2001 From: mahula Date: Mon, 5 Sep 2022 10:55:28 +0200 Subject: [PATCH 13/72] fix locales for Nederlands --- frontend/src/locales/de.json | 2 +- frontend/src/locales/en.json | 2 +- frontend/src/locales/es.json | 4 ++-- frontend/src/locales/fr.json | 8 ++++---- frontend/src/locales/index.js | 2 +- frontend/src/locales/nl.json | 8 ++++---- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index 1525ed98c..1091088c6 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -255,7 +255,7 @@ "en": "English", "es": "Español", "fr": "Français", - "nl": "Dutch", + "nl": "Nederlands", "success": "Deine Sprache wurde erfolgreich geändert." }, "name": { diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 6ec9ef8b8..4971cdf39 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -255,7 +255,7 @@ "en": "English", "es": "Español", "fr": "Français", - "nl": "Holandés", + "nl": "Nederlands", "success": "Your language has been successfully updated." }, "name": { diff --git a/frontend/src/locales/es.json b/frontend/src/locales/es.json index 939a877f7..987bb71ef 100644 --- a/frontend/src/locales/es.json +++ b/frontend/src/locales/es.json @@ -254,8 +254,8 @@ "de": "Deutsch", "en": "English", "es": "Español", - "fr": "Francés", - "nl": "Holandés", + "fr": "Français", + "nl": "Nederlands", "success": "Tu idioma ha sido cambiado con éxito." }, "name": { diff --git a/frontend/src/locales/fr.json b/frontend/src/locales/fr.json index 07237d16d..61037af1a 100644 --- a/frontend/src/locales/fr.json +++ b/frontend/src/locales/fr.json @@ -251,11 +251,11 @@ "settings": { "language": { "changeLanguage": "Changer la langue", - "de": "Allemand", - "en": "Anglais", - "es": "Espagnol", + "de": "Deutsch", + "en": "English", + "es": "Español", "fr": "Français", - "nl": "Néerlandais", + "nl": "Nederlands", "success": "Votre langue de préférence a bien été actualisée." }, "name": { diff --git a/frontend/src/locales/index.js b/frontend/src/locales/index.js index aa27a2e32..eed77ec2a 100644 --- a/frontend/src/locales/index.js +++ b/frontend/src/locales/index.js @@ -24,7 +24,7 @@ const locales = [ enabled: true, }, { - name: 'Holandés', + name: 'Nederlands', code: 'nl', iso: 'nl-NL', enabled: true, diff --git a/frontend/src/locales/nl.json b/frontend/src/locales/nl.json index f84cdaa2a..99b972514 100644 --- a/frontend/src/locales/nl.json +++ b/frontend/src/locales/nl.json @@ -251,10 +251,10 @@ "settings": { "language": { "changeLanguage": "Taal veranderen", - "de": "Duits", - "en": "Engels", - "es": "Spaans", - "fr": "Frans", + "de": "Deutsch", + "en": "English", + "es": "Español", + "fr": "Français", "nl": "Nederlands", "success": "Jouw taal werd succesvol veranderd." }, From 177fb4b14c23e7718ca1a30fa275fb5b27f22687 Mon Sep 17 00:00:00 2001 From: mahula Date: Mon, 5 Sep 2022 10:58:53 +0200 Subject: [PATCH 14/72] adapt unit tests regarding locales --- frontend/src/components/LanguageSwitch.spec.js | 12 ++++++------ frontend/src/components/LanguageSwitch2.spec.js | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/LanguageSwitch.spec.js b/frontend/src/components/LanguageSwitch.spec.js index a6abc2359..13ba979d3 100644 --- a/frontend/src/components/LanguageSwitch.spec.js +++ b/frontend/src/components/LanguageSwitch.spec.js @@ -94,11 +94,11 @@ describe('LanguageSwitch', () => { describe('navigator language is "nl-NL"', () => { const languageGetter = jest.spyOn(navigator, 'language', 'get') - it('shows Dutch as language ', async () => { + it('shows Nederlands as language ', async () => { languageGetter.mockReturnValue('nl-NL') wrapper.vm.setCurrentLanguage() await wrapper.vm.$nextTick() - expect(wrapper.find('button.dropdown-toggle').text()).toBe('Holandés - nl') + expect(wrapper.find('button.dropdown-toggle').text()).toBe('Nederlands - nl') }) }) @@ -153,11 +153,11 @@ describe('LanguageSwitch', () => { }) describe('language "nl" in store', () => { - it('shows Dutch as language', async () => { + it('shows Nederlands as language', async () => { wrapper.vm.$store.state.language = 'nl' wrapper.vm.setCurrentLanguage() await wrapper.vm.$nextTick() - expect(wrapper.find('button.dropdown-toggle').text()).toBe('Holandés - nl') + expect(wrapper.find('button.dropdown-toggle').text()).toBe('Nederlands - nl') }) }) @@ -182,8 +182,8 @@ describe('LanguageSwitch', () => { expect(wrapper.findAll('li').at(3).text()).toBe('Français') }) - it('has Dutch as second language to choose', () => { - expect(wrapper.findAll('li').at(4).text()).toBe('Holandés') + it('has Nederlands as second language to choose', () => { + expect(wrapper.findAll('li').at(4).text()).toBe('Nederlands') }) }) }) diff --git a/frontend/src/components/LanguageSwitch2.spec.js b/frontend/src/components/LanguageSwitch2.spec.js index 080cc9d42..23550e5d3 100644 --- a/frontend/src/components/LanguageSwitch2.spec.js +++ b/frontend/src/components/LanguageSwitch2.spec.js @@ -86,11 +86,11 @@ describe('LanguageSwitch', () => { }) describe('navigator language is "nl-NL"', () => { const languageGetter = jest.spyOn(navigator, 'language', 'get') - it('shows Dutch as language ', async () => { + it('shows Nederlands as language ', async () => { languageGetter.mockReturnValue('nl-NL') wrapper.vm.setCurrentLanguage() await wrapper.vm.$nextTick() - expect(wrapper.findAll('span.locales').at(4).text()).toBe('Holandés') + expect(wrapper.findAll('span.locales').at(4).text()).toBe('Nederlands') }) }) describe('navigator language is "it-IT" (not supported)', () => { @@ -137,7 +137,7 @@ describe('LanguageSwitch', () => { }) }) describe('language "nl" in store', () => { - it('shows Dutch as language', async () => { + it('shows Nederlands as language', async () => { wrapper.vm.$store.state.language = 'nl' wrapper.vm.setCurrentLanguage() await wrapper.vm.$nextTick() @@ -149,7 +149,7 @@ describe('LanguageSwitch', () => { expect(wrapper.findAll('span.locales')).toHaveLength(5) }) it('has English as first language to choose', () => { - expect(wrapper.findAll('span.locales').at(0).text()).toBe('Holandés') + expect(wrapper.findAll('span.locales').at(0).text()).toBe('Nederlands') }) it('has German as second language to choose', () => { expect(wrapper.findAll('span.locales').at(1).text()).toBe('English') @@ -160,7 +160,7 @@ describe('LanguageSwitch', () => { it('has French as third language to choose', () => { expect(wrapper.findAll('span.locales').at(3).text()).toBe('Español') }) - it('has Dutch as third language to choose', () => { + it('has Nederlands as third language to choose', () => { expect(wrapper.findAll('span.locales').at(4).text()).toBe('Français') }) }) From cea6ef6988862619d43b1c058561dd0d83a82ba4 Mon Sep 17 00:00:00 2001 From: mahula Date: Mon, 5 Sep 2022 13:51:49 +0200 Subject: [PATCH 15/72] update language switch unit tests to test for all current availlable languages --- .../src/components/LanguageSwitch.spec.js | 10 +-- .../src/components/LanguageSwitch2.spec.js | 72 ++++++++++++++++--- 2 files changed, 66 insertions(+), 16 deletions(-) diff --git a/frontend/src/components/LanguageSwitch.spec.js b/frontend/src/components/LanguageSwitch.spec.js index 13ba979d3..7f37c535a 100644 --- a/frontend/src/components/LanguageSwitch.spec.js +++ b/frontend/src/components/LanguageSwitch.spec.js @@ -45,7 +45,7 @@ describe('LanguageSwitch', () => { expect(wrapper.find('div.language-switch').exists()).toBeTruthy() }) - describe('with locales en, de and es', () => { + describe('with locales en, de, es, fr, and nl', () => { describe('empty store', () => { describe('navigator language is "en-US"', () => { const languageGetter = jest.spyOn(navigator, 'language', 'get') @@ -162,7 +162,7 @@ describe('LanguageSwitch', () => { }) describe('dropdown menu', () => { - it('has English and German as languages to choose', () => { + it('has five languages to choose from', () => { expect(wrapper.findAll('li')).toHaveLength(5) }) @@ -174,15 +174,15 @@ describe('LanguageSwitch', () => { expect(wrapper.findAll('li').at(1).text()).toBe('Deutsch') }) - it('has Español as second language to choose', () => { + it('has Español as third language to choose', () => { expect(wrapper.findAll('li').at(2).text()).toBe('Español') }) - it('has French as second language to choose', () => { + it('has French as fourth language to choose', () => { expect(wrapper.findAll('li').at(3).text()).toBe('Français') }) - it('has Nederlands as second language to choose', () => { + it('has Nederlands as fith language to choose', () => { expect(wrapper.findAll('li').at(4).text()).toBe('Nederlands') }) }) diff --git a/frontend/src/components/LanguageSwitch2.spec.js b/frontend/src/components/LanguageSwitch2.spec.js index 23550e5d3..0d2b485ec 100644 --- a/frontend/src/components/LanguageSwitch2.spec.js +++ b/frontend/src/components/LanguageSwitch2.spec.js @@ -46,10 +46,11 @@ describe('LanguageSwitch', () => { expect(wrapper.find('div.language-switch').exists()).toBe(true) }) - describe('with locales en and de', () => { + describe('with locales en, de, es, fr, and nl', () => { describe('empty store', () => { describe('navigator language is "en-US"', () => { const languageGetter = jest.spyOn(navigator, 'language', 'get') + it('shows English as default navigator langauge', async () => { languageGetter.mockReturnValue('en-US') wrapper.vm.setCurrentLanguage() @@ -57,8 +58,10 @@ describe('LanguageSwitch', () => { expect(wrapper.findAll('span.locales').at(0).text()).toBe('English') }) }) + describe('navigator language is "de-DE"', () => { const languageGetter = jest.spyOn(navigator, 'language', 'get') + it('shows Deutsch as language ', async () => { languageGetter.mockReturnValue('de-DE') wrapper.vm.setCurrentLanguage() @@ -66,8 +69,10 @@ describe('LanguageSwitch', () => { expect(wrapper.findAll('span.locales').at(1).text()).toBe('Deutsch') }) }) + describe('navigator language is "es-ES"', () => { const languageGetter = jest.spyOn(navigator, 'language', 'get') + it('shows Español as language ', async () => { languageGetter.mockReturnValue('es-ES') wrapper.vm.setCurrentLanguage() @@ -75,8 +80,10 @@ describe('LanguageSwitch', () => { expect(wrapper.findAll('span.locales').at(2).text()).toBe('Español') }) }) + describe('navigator language is "fr-FR"', () => { const languageGetter = jest.spyOn(navigator, 'language', 'get') + it('shows French as language ', async () => { languageGetter.mockReturnValue('fr-FR') wrapper.vm.setCurrentLanguage() @@ -84,8 +91,10 @@ describe('LanguageSwitch', () => { expect(wrapper.findAll('span.locales').at(3).text()).toBe('Français') }) }) + describe('navigator language is "nl-NL"', () => { const languageGetter = jest.spyOn(navigator, 'language', 'get') + it('shows Nederlands as language ', async () => { languageGetter.mockReturnValue('nl-NL') wrapper.vm.setCurrentLanguage() @@ -93,8 +102,10 @@ describe('LanguageSwitch', () => { expect(wrapper.findAll('span.locales').at(4).text()).toBe('Nederlands') }) }) + describe('navigator language is "it-IT" (not supported)', () => { const languageGetter = jest.spyOn(navigator, 'language', 'get') + it('shows English as language ', async () => { languageGetter.mockReturnValue('it-IT') wrapper.vm.setCurrentLanguage() @@ -102,8 +113,10 @@ describe('LanguageSwitch', () => { expect(wrapper.findAll('span.locales').at(0).text()).toBe('English') }) }) + describe('no navigator langauge', () => { const languageGetter = jest.spyOn(navigator, 'language', 'get') + it('shows English as language ', async () => { languageGetter.mockReturnValue(null) wrapper.vm.setCurrentLanguage() @@ -112,6 +125,7 @@ describe('LanguageSwitch', () => { }) }) }) + describe('language "de" in store', () => { it('shows Deutsch as language', async () => { wrapper.vm.$store.state.language = 'de' @@ -120,6 +134,7 @@ describe('LanguageSwitch', () => { expect(wrapper.findAll('span.locales').at(1).text()).toBe('English') }) }) + describe('language "es" in store', () => { it('shows Español as language', async () => { wrapper.vm.$store.state.language = 'es' @@ -128,6 +143,7 @@ describe('LanguageSwitch', () => { expect(wrapper.findAll('span.locales').at(2).text()).toBe('Deutsch') }) }) + describe('language "fr" in store', () => { it('shows French as language', async () => { wrapper.vm.$store.state.language = 'fr' @@ -136,6 +152,7 @@ describe('LanguageSwitch', () => { expect(wrapper.findAll('span.locales').at(3).text()).toBe('Español') }) }) + describe('language "nl" in store', () => { it('shows Nederlands as language', async () => { wrapper.vm.$store.state.language = 'nl' @@ -144,35 +161,68 @@ describe('LanguageSwitch', () => { expect(wrapper.findAll('span.locales').at(4).text()).toBe('Français') }) }) + describe('language menu', () => { - it('has English, German and Español as languages to choose', () => { + beforeAll(async () => { + wrapper.vm.$store.state.language = 'en' + wrapper.vm.setCurrentLanguage() + await wrapper.vm.$nextTick() + }) + + it('has five languages to choose from', () => { expect(wrapper.findAll('span.locales')).toHaveLength(5) }) + it('has English as first language to choose', () => { - expect(wrapper.findAll('span.locales').at(0).text()).toBe('Nederlands') + expect(wrapper.findAll('span.locales').at(0).text()).toBe('English') }) - it('has German as second language to choose', () => { - expect(wrapper.findAll('span.locales').at(1).text()).toBe('English') + + it('has Deutsch as second language to choose', () => { + expect(wrapper.findAll('span.locales').at(1).text()).toBe('Deutsch') }) + it('has Español as third language to choose', () => { - expect(wrapper.findAll('span.locales').at(2).text()).toBe('Deutsch') + expect(wrapper.findAll('span.locales').at(2).text()).toBe('Español') }) - it('has French as third language to choose', () => { - expect(wrapper.findAll('span.locales').at(3).text()).toBe('Español') + + it('has Français as fourth language to choose', () => { + expect(wrapper.findAll('span.locales').at(3).text()).toBe('Français') }) - it('has Nederlands as third language to choose', () => { - expect(wrapper.findAll('span.locales').at(4).text()).toBe('Français') + + it('has Nederlands as fifth language to choose', () => { + expect(wrapper.findAll('span.locales').at(4).text()).toBe('Nederlands') }) }) }) describe('calls the API', () => { it("with locale 'de'", () => { - wrapper.findAll('span.locales').at(2).trigger('click') + wrapper.findAll('span.locales').at(1).trigger('click') expect(updateUserInfosMutationMock).toBeCalledWith( expect.objectContaining({ variables: { locale: 'de' } }), ) }) + + it("with locale 'es'", () => { + wrapper.findAll('span.locales').at(2).trigger('click') + expect(updateUserInfosMutationMock).toBeCalledWith( + expect.objectContaining({ variables: { locale: 'es' } }), + ) + }) + + it("with locale 'fr'", () => { + wrapper.findAll('span.locales').at(3).trigger('click') + expect(updateUserInfosMutationMock).toBeCalledWith( + expect.objectContaining({ variables: { locale: 'fr' } }), + ) + }) + + it("with locale 'nl'", () => { + wrapper.findAll('span.locales').at(4).trigger('click') + expect(updateUserInfosMutationMock).toBeCalledWith( + expect.objectContaining({ variables: { locale: 'nl' } }), + ) + }) }) }) }) From 04b96bac2fcb90c25611abd856673fcc5b998081 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 6 Sep 2022 10:39:20 +0200 Subject: [PATCH 16/72] Throw error if moderator tries to answer his own contribution in adminCreateContributionMessage. --- backend/src/graphql/resolver/AdminResolver.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index f4656aec8..6ed56e082 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -717,6 +717,9 @@ export class AdminResolver { if (!contribution) { throw new Error('Contribution not found') } + if (contribution.userId === user.id) { + throw new Error('Can not answer on own contribution') + } contributionMessage.contributionId = contributionId contributionMessage.createdAt = new Date() contributionMessage.message = message From ced14bd752e00adb6635a34893f89cbd383662bf Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 6 Sep 2022 10:59:32 +0200 Subject: [PATCH 17/72] Change error message Admin can not answer on own contribution. --- backend/src/graphql/resolver/AdminResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 6ed56e082..65662e8eb 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -718,7 +718,7 @@ export class AdminResolver { throw new Error('Contribution not found') } if (contribution.userId === user.id) { - throw new Error('Can not answer on own contribution') + throw new Error('Admin can not answer on own contribution') } contributionMessage.contributionId = contributionId contributionMessage.createdAt = new Date() From aa9d60c78c77d56c23461e45d107a1f7736ef672 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 6 Sep 2022 11:07:23 +0200 Subject: [PATCH 18/72] Add test that admin can not call adminCreateContributionMessage on own contribution --- .../ContributionMessageResolver.test.ts | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index 6c617acb4..c0e330750 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -93,6 +93,38 @@ describe('ContributionMessageResolver', () => { }), ) }) + + it('throws error when contribution.userId equals user.id', async () => { + await query({ + query: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + const result2 = await mutate({ + mutation: createContribution, + variables: { + amount: 100.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), + }, + }) + await expect( + mutate({ + mutation: adminCreateContributionMessage, + variables: { + contributionId: result2.data.createContribution.id, + message: 'Test', + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + new GraphQLError( + 'ContributionMessage was not successful: Error: Admin can not answer on own contribution', + ), + ], + }), + ) + }) }) describe('valid input', () => { From 0dc45d81c5f4b421f5bc880b2fabf44c412ca8c6 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 6 Sep 2022 11:22:16 +0200 Subject: [PATCH 19/72] Add is_moderator field to the contribution_messages table. --- database/entity/0047-messages_tables/ContributionMessage.ts | 3 +++ database/migrations/0047-messages_tables.ts | 1 + 2 files changed, 4 insertions(+) diff --git a/database/entity/0047-messages_tables/ContributionMessage.ts b/database/entity/0047-messages_tables/ContributionMessage.ts index e5226043d..dd3787547 100644 --- a/database/entity/0047-messages_tables/ContributionMessage.ts +++ b/database/entity/0047-messages_tables/ContributionMessage.ts @@ -48,4 +48,7 @@ export class ContributionMessage extends BaseEntity { @Column({ length: 12, nullable: false, collation: 'utf8mb4_unicode_ci' }) type: string + + @Column({ name: 'is_moderator', type: 'bool', nullable: false, default: false }) + isModerator: boolean } diff --git a/database/migrations/0047-messages_tables.ts b/database/migrations/0047-messages_tables.ts index 36fa42a8e..aafc3020f 100644 --- a/database/migrations/0047-messages_tables.ts +++ b/database/migrations/0047-messages_tables.ts @@ -20,6 +20,7 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis \`deleted_at\` datetime DEFAULT NULL, \`deleted_by\` int(10) unsigned DEFAULT NULL, \`type\` varchar(12) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT "DIALOG", + \`is_moderator\` boolean NOT NULL DEFAULT false, PRIMARY KEY (\`id\`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; `) From ecc42b49fa65e186079ae9781988811f714e4bdb Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 6 Sep 2022 11:23:57 +0200 Subject: [PATCH 20/72] Add isModerator to the createContributionMessage calls. --- backend/src/graphql/resolver/AdminResolver.ts | 1 + backend/src/graphql/resolver/ContributionMessageResolver.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index f4656aec8..fbeea716c 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -722,6 +722,7 @@ export class AdminResolver { contributionMessage.message = message contributionMessage.userId = user.id contributionMessage.type = ContributionMessageType.DIALOG + contributionMessage.isModerator = true await queryRunner.manager.insert(DbContributionMessage, contributionMessage) if ( diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index 408481513..fb92806d0 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -39,6 +39,7 @@ export class ContributionMessageResolver { contributionMessage.message = message contributionMessage.userId = user.id contributionMessage.type = ContributionMessageType.DIALOG + contributionMessage.isModerator = false await queryRunner.manager.insert(DbContributionMessage, contributionMessage) if (contribution.contributionStatus === ContributionStatus.IN_PROGRESS) { From dfa97fd855157a14159f72e8be634dce51b4e7ae Mon Sep 17 00:00:00 2001 From: ogerly Date: Tue, 6 Sep 2022 11:00:29 +0200 Subject: [PATCH 21/72] moderator cannot answer himself --- .../components/Tables/OpenCreationsTable.vue | 59 +++++++++++-------- .../graphql/listUnconfirmedContributions.js | 1 + 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/admin/src/components/Tables/OpenCreationsTable.vue b/admin/src/components/Tables/OpenCreationsTable.vue index 86c5ecce6..af9947f85 100644 --- a/admin/src/components/Tables/OpenCreationsTable.vue +++ b/admin/src/components/Tables/OpenCreationsTable.vue @@ -12,33 +12,42 @@ diff --git a/frontend/src/components/ContributionMessages/ContributionMessagesList.vue b/frontend/src/components/ContributionMessages/ContributionMessagesList.vue index 6530a5a53..5f1c03b22 100644 --- a/frontend/src/components/ContributionMessages/ContributionMessagesList.vue +++ b/frontend/src/components/ContributionMessages/ContributionMessagesList.vue @@ -7,7 +7,6 @@ {{ message.userFirstName }} {{ message.userLastName }} {{ $d(new Date(message.createdAt), 'short') }} {{ $t('community.moderator') }} -
{{ message.message }}
+
{{ message.message }}
diff --git a/frontend/src/components/ContributionMessages/ContributionMessagesFormular.vue b/frontend/src/components/ContributionMessages/ContributionMessagesFormular.vue index 52240afa0..e3f9fd5e7 100644 --- a/frontend/src/components/ContributionMessages/ContributionMessagesFormular.vue +++ b/frontend/src/components/ContributionMessages/ContributionMessagesFormular.vue @@ -14,7 +14,9 @@ {{ $t('form.cancel') }} - {{ $t('form.reply') }} + + {{ $t('form.reply') }} + @@ -63,5 +65,13 @@ export default { this.form.text = '' }, }, + computed: { + disabled() { + if (this.form.text !== '') { + return false + } + return true + }, + }, } From 655e4058660ee42d878cbe312245dd38018ed59a Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 9 Sep 2022 14:43:30 +0200 Subject: [PATCH 43/72] Change text of email. --- backend/src/mailer/text/contributionMessageReceived.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/mailer/text/contributionMessageReceived.ts b/backend/src/mailer/text/contributionMessageReceived.ts index e0a19d7dc..4affb71a8 100644 --- a/backend/src/mailer/text/contributionMessageReceived.ts +++ b/backend/src/mailer/text/contributionMessageReceived.ts @@ -12,10 +12,10 @@ export const contributionMessageReceived = { message: string overviewURL: string }): string => - `Hallo ${data.recipientFirstName} ${data.recipientLastName} + `Hallo ${data.recipientFirstName} ${data.recipientLastName}, -Du hast soeben eine nachfrage zur Schöpfung "${data.contributionMemo}" von ${data.senderFirstName} ${data.senderLastName} erhalten. -${data.senderFirstName} ${data.senderLastName} schreibt: +Du hast soeben zu deinem eingereichten Gradido Schöpfungsantrag "${data.contributionMemo}" eine Rückfrage von ${data.senderFirstName} ${data.senderLastName} erhalten. +Die Rückfrage lautet: ${data.message} From 93e7684eda56f1ac42b22ea1fb08b2abce87af52 Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 9 Sep 2022 14:44:00 +0200 Subject: [PATCH 44/72] Change import to the new file. --- backend/src/graphql/resolver/AdminResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 34b0f5ccf..a1c091283 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -66,7 +66,7 @@ import { ContributionMessage as DbContributionMessage } from '@entity/Contributi import ContributionMessageArgs from '@arg/ContributionMessageArgs' import { ContributionMessageType } from '@enum/MessageType' import { ContributionMessage } from '@model/ContributionMessage' -import { sendAddedContributionMessageEmail } from '@/mailer/sendTransactionReceivedEmail' +import { sendAddedContributionMessageEmail } from '@/mailer/sendAddedContributionMessageEmail' // const EMAIL_OPT_IN_REGISTER = 1 // const EMAIL_OPT_UNKNOWN = 3 // elopage? From a3d65228f90c31ff87de16ce8fa4065000edfd96 Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 9 Sep 2022 14:44:22 +0200 Subject: [PATCH 45/72] Add sendEmail mock to the test file. --- .../src/mailer/sendAddedContributionMessageEmail.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/backend/src/mailer/sendAddedContributionMessageEmail.test.ts b/backend/src/mailer/sendAddedContributionMessageEmail.test.ts index 8b1bc2727..a35836e2a 100644 --- a/backend/src/mailer/sendAddedContributionMessageEmail.test.ts +++ b/backend/src/mailer/sendAddedContributionMessageEmail.test.ts @@ -1,6 +1,13 @@ import { sendAddedContributionMessageEmail } from './sendAddedContributionMessageEmail' import { sendEMail } from './sendEMail' +jest.mock('./sendEMail', () => { + return { + __esModule: true, + sendEMail: jest.fn(), + } +}) + describe('sendAddedContributionMessageEmail', () => { beforeEach(async () => { await sendAddedContributionMessageEmail({ From 42f667674ef4ecca73c48764023bb1812425ddd1 Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 9 Sep 2022 14:54:37 +0200 Subject: [PATCH 46/72] Remove unused import. --- backend/src/mailer/sendTransactionReceivedEmail.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/mailer/sendTransactionReceivedEmail.ts b/backend/src/mailer/sendTransactionReceivedEmail.ts index 3e5171251..692f92f9a 100644 --- a/backend/src/mailer/sendTransactionReceivedEmail.ts +++ b/backend/src/mailer/sendTransactionReceivedEmail.ts @@ -2,7 +2,6 @@ import { backendLogger as logger } from '@/server/logger' import Decimal from 'decimal.js-light' import { sendEMail } from './sendEMail' import { transactionReceived } from './text/transactionReceived' -import { contributionMessageReceived } from './text/contributionMessageReceived' export const sendTransactionReceivedEmail = (data: { senderFirstName: string From 39d7de3d4be59a94c4dde2a0739d24a000cded3e Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 12 Sep 2022 10:47:07 +0200 Subject: [PATCH 47/72] release: v1.12.0 --- CHANGELOG.md | 41 +++++++++++++++++++++++++++++++++++++++++ admin/package.json | 2 +- backend/package.json | 2 +- database/package.json | 2 +- frontend/package.json | 2 +- package.json | 2 +- 6 files changed, 46 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63b0c2c90..352286e45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,49 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [1.12.0](https://github.com/gradido/gradido/compare/1.11.0...1.12.0) + +- if message empty else disabled button [`#2189`](https://github.com/gradido/gradido/pull/2189) +- messages show if Confirmed [`#2185`](https://github.com/gradido/gradido/pull/2185) +- text in messages smaller [`#2186`](https://github.com/gradido/gradido/pull/2186) +- feat: 🍰 Klicktipp retrieve not registered email [`#2181`](https://github.com/gradido/gradido/pull/2181) +- fix: 🍰 isModerator on messages to switch the messages side in the messages overview [`#2182`](https://github.com/gradido/gradido/pull/2182) +- Refactor locales for Nederlands [`#2174`](https://github.com/gradido/gradido/pull/2174) +- Add is moderator to contribution message [`#2180`](https://github.com/gradido/gradido/pull/2180) +- feat: 🍰 Moderator Cannot Answer Himself [`#2178`](https://github.com/gradido/gradido/pull/2178) +- refactor: Improve Statistics Query [`#2170`](https://github.com/gradido/gradido/pull/2170) +- fix: Remove Statistics from Wallet [`#2171`](https://github.com/gradido/gradido/pull/2171) +- feat: 🍰 Contribution Messages In Frontend [`#2164`](https://github.com/gradido/gradido/pull/2164) +- feat: 🚀 CRUD For Contribution Messages [`#2149`](https://github.com/gradido/gradido/pull/2149) +- fix: 🍰 Decay Calculation In Community Statistics [`#2167`](https://github.com/gradido/gradido/pull/2167) +- chore: 🍰 Remove Fetch Policy Network Only From Statistics [`#2159`](https://github.com/gradido/gradido/pull/2159) +- feat: 🍰 Remove Some Statistics Data From Frontend [`#2153`](https://github.com/gradido/gradido/pull/2153) +- feat: 🍰 Add Toogle Collaps On Language Name [`#2156`](https://github.com/gradido/gradido/pull/2156) +- 2145 corrections style for frontend [`#2147`](https://github.com/gradido/gradido/pull/2147) +- 2072 feature usecase contribution messaging [`#2073`](https://github.com/gradido/gradido/pull/2073) +- 2151 add hint to redeem link [`#2158`](https://github.com/gradido/gradido/pull/2158) +- 🍰 Create `contribution messages` table [`#2137`](https://github.com/gradido/gradido/pull/2137) +- feat: 🍰 Add The Languages French And Dutch [`#2138`](https://github.com/gradido/gradido/pull/2138) +- 1973 list open contribution links in the wallet [`#1975`](https://github.com/gradido/gradido/pull/1975) +- feat: 🍰 Admin Interface Displays Statistics [`#2124`](https://github.com/gradido/gradido/pull/2124) +- feat: Statistics Resolver [`#2041`](https://github.com/gradido/gradido/pull/2041) +- 2116 retrieve admin and moderators [`#2127`](https://github.com/gradido/gradido/pull/2127) +- 2125 feature gradido id: new column gradidoid in users table [`#2126`](https://github.com/gradido/gradido/pull/2126) +- 2119 new menu item gdt [`#2120`](https://github.com/gradido/gradido/pull/2120) +- feat: Migrate Contributions Table [`#2136`](https://github.com/gradido/gradido/pull/2136) +- chore: 🍰 Refactor Contribution Form Logic And Write Tests [`#2092`](https://github.com/gradido/gradido/pull/2092) +- fix: 🍰 Add `emailChecked` Before Changing `optIn` State & Log Error On klicktipp Middleware [`#2107`](https://github.com/gradido/gradido/pull/2107) +- Add RIGHTS.LIST_CONTRIBUTION_LINKS to ROLE_USER [`#2123`](https://github.com/gradido/gradido/pull/2123) +- 2121 translate locales to spanish [`#2122`](https://github.com/gradido/gradido/pull/2122) +- add formatter on input amount replace point and comma [`#2115`](https://github.com/gradido/gradido/pull/2115) +- remove required from form.memo [`#2114`](https://github.com/gradido/gradido/pull/2114) +- Fix pagination ellipsis [`#2104`](https://github.com/gradido/gradido/pull/2104) + #### [1.11.0](https://github.com/gradido/gradido/compare/1.10.1...1.11.0) +> 28 July 2022 + +- release: Version 1.11.0 [`#2103`](https://github.com/gradido/gradido/pull/2103) - Fix navbar community item [`#2102`](https://github.com/gradido/gradido/pull/2102) - Add validation date info to copied text after transaction link creation [`#2101`](https://github.com/gradido/gradido/pull/2101) - Remove member area from menu (desktop and mobile), when user has no elopage account [`#2099`](https://github.com/gradido/gradido/pull/2099) diff --git a/admin/package.json b/admin/package.json index 9879064de..f31b43bbd 100644 --- a/admin/package.json +++ b/admin/package.json @@ -3,7 +3,7 @@ "description": "Administraion Interface for Gradido", "main": "index.js", "author": "Moriz Wahl", - "version": "1.11.0", + "version": "1.12.0", "license": "Apache-2.0", "private": false, "scripts": { diff --git a/backend/package.json b/backend/package.json index 858aa1328..b826183b6 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "gradido-backend", - "version": "1.11.0", + "version": "1.12.0", "description": "Gradido unified backend providing an API-Service for Gradido Transactions", "main": "src/index.ts", "repository": "https://github.com/gradido/gradido/backend", diff --git a/database/package.json b/database/package.json index 837e61438..40e5d0294 100644 --- a/database/package.json +++ b/database/package.json @@ -1,6 +1,6 @@ { "name": "gradido-database", - "version": "1.11.0", + "version": "1.12.0", "description": "Gradido Database Tool to execute database migrations", "main": "src/index.ts", "repository": "https://github.com/gradido/gradido/database", diff --git a/frontend/package.json b/frontend/package.json index 8121e2ca2..b3832b080 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "bootstrap-vue-gradido-wallet", - "version": "1.11.0", + "version": "1.12.0", "private": true, "scripts": { "start": "node run/server.js", diff --git a/package.json b/package.json index 2bba1b52c..519c1b59c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gradido", - "version": "1.11.0", + "version": "1.12.0", "description": "Gradido", "main": "index.js", "repository": "git@github.com:gradido/gradido.git", From 72f85c0d598799f959262f4ca8ad1da29b38e4e1 Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 12 Sep 2022 11:35:01 +0200 Subject: [PATCH 48/72] Add test for sendContributionConfirmedEmail. --- .../sendContributionConfirmedEmail.test.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 backend/src/mailer/sendContributionConfirmedEmail.test.ts diff --git a/backend/src/mailer/sendContributionConfirmedEmail.test.ts b/backend/src/mailer/sendContributionConfirmedEmail.test.ts new file mode 100644 index 000000000..7fcb7c993 --- /dev/null +++ b/backend/src/mailer/sendContributionConfirmedEmail.test.ts @@ -0,0 +1,39 @@ +import Decimal from 'decimal.js-light' +import { sendContributionConfirmedEmail } from './sendContributionConfirmedEmail' +import { sendEMail } from './sendEMail' + +jest.mock('./sendEMail', () => { + return { + __esModule: true, + sendEMail: jest.fn(), + } +}) + +describe('sendContributionConfirmedEmail', () => { + beforeEach(async () => { + await sendContributionConfirmedEmail({ + senderFirstName: 'Peter', + senderLastName: 'Lustig', + recipientFirstName: 'Bibi', + recipientLastName: 'Bloxberg', + recipientEmail: 'bibi@bloxberg.de', + contributionMemo: 'Vielen herzlichen Dank für den neuen Hexenbesen!', + contributionAmount: new Decimal(200.0), + overviewURL: 'http://localhost/overview', + }) + }) + + it('calls sendEMail', () => { + expect(sendEMail).toBeCalledWith({ + to: 'Bibi Bloxberg ', + subject: 'Schöpfung wurde bestätigt', + text: + expect.stringContaining('Hallo Bibi Bloxberg') && + expect.stringContaining( + 'Deine Gradido Schöpfungsantrag "Vielen herzlichen Dank für den neuen Hexenbesen!" wurde soeben bestätigt.', + ) && + expect.stringContaining('Betrag: 200,00 GDD') && + expect.stringContaining('Link zu deinem Konto: http://localhost/overview'), + }) + }) +}) From 7812a941b5f35ba337065c7ef10b42b77c997e95 Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 12 Sep 2022 11:35:31 +0200 Subject: [PATCH 49/72] Add EMail text for contributionConfirmed. --- .../src/mailer/text/contributionConfirmed.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 backend/src/mailer/text/contributionConfirmed.ts diff --git a/backend/src/mailer/text/contributionConfirmed.ts b/backend/src/mailer/text/contributionConfirmed.ts new file mode 100644 index 000000000..d5d35fdda --- /dev/null +++ b/backend/src/mailer/text/contributionConfirmed.ts @@ -0,0 +1,30 @@ +import Decimal from 'decimal.js-light' + +export const contributionConfirmed = { + de: { + subject: 'Schöpfung wurde bestätigt', + text: (data: { + senderFirstName: string + senderLastName: string + recipientFirstName: string + recipientLastName: string + contributionMemo: string + contributionAmount: Decimal + overviewURL: string + }): string => + `Hallo ${data.recipientFirstName} ${data.recipientLastName}, + +Deine Gradido Schöpfungsantrag "${data.contributionMemo}" wurde soeben von ${ + data.senderFirstName + } ${data.senderLastName} bestätigt. +Betrag: ${data.contributionAmount.toFixed(2).replace('.', ',')} GDD + +Bitte antworte nicht auf diese E-Mail! + +Mit freundlichen Grüßen, +dein Gradido-Team + + +Link zu deinem Konto: ${data.overviewURL}`, + }, +} From 0356ae6f2645514cae2a702f4485798eddbd1a3a Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 12 Sep 2022 11:35:56 +0200 Subject: [PATCH 50/72] Add method to send email when contribution has been confirmed. --- .../mailer/sendContributionConfirmedEmail.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 backend/src/mailer/sendContributionConfirmedEmail.ts diff --git a/backend/src/mailer/sendContributionConfirmedEmail.ts b/backend/src/mailer/sendContributionConfirmedEmail.ts new file mode 100644 index 000000000..439d240eb --- /dev/null +++ b/backend/src/mailer/sendContributionConfirmedEmail.ts @@ -0,0 +1,26 @@ +import { backendLogger as logger } from '@/server/logger' +import Decimal from 'decimal.js-light' +import { sendEMail } from './sendEMail' +import { contributionConfirmed } from './text/contributionConfirmed' + +export const sendContributionConfirmedEmail = (data: { + senderFirstName: string + senderLastName: string + recipientFirstName: string + recipientLastName: string + recipientEmail: string + contributionMemo: string + contributionAmount: Decimal + overviewURL: string +}): Promise => { + logger.info( + `sendEmail(): to=${data.recipientFirstName} ${data.recipientLastName} <${data.recipientEmail}>, + subject=${contributionConfirmed.de.subject}, + text=${contributionConfirmed.de.text(data)}`, + ) + return sendEMail({ + to: `${data.recipientFirstName} ${data.recipientLastName} <${data.recipientEmail}>`, + subject: contributionConfirmed.de.subject, + text: contributionConfirmed.de.text(data), + }) +} From 7ab7380f0942e228175f040a672a867f900f891c Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 12 Sep 2022 11:36:24 +0200 Subject: [PATCH 51/72] Call new method to send confirmedContribution EMail to the user. --- backend/src/graphql/resolver/AdminResolver.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index ac3936e4f..5904299a0 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -66,6 +66,7 @@ import { ContributionMessage as DbContributionMessage } from '@entity/Contributi import ContributionMessageArgs from '@arg/ContributionMessageArgs' import { ContributionMessageType } from '@enum/MessageType' import { ContributionMessage } from '@model/ContributionMessage' +import { sendContributionConfirmedEmail } from '@/mailer/sendContributionConfirmedEmail' // const EMAIL_OPT_IN_REGISTER = 1 // const EMAIL_OPT_UNKNOWN = 3 // elopage? @@ -470,6 +471,16 @@ export class AdminResolver { await queryRunner.commitTransaction() logger.info('creation commited successfuly.') + sendContributionConfirmedEmail({ + senderFirstName: moderatorUser.firstName, + senderLastName: moderatorUser.lastName, + recipientFirstName: user.firstName, + recipientLastName: user.lastName, + recipientEmail: user.email, + contributionMemo: contribution.memo, + contributionAmount: contribution.amount, + overviewURL: CONFIG.EMAIL_LINK_OVERVIEW, + }) } catch (e) { await queryRunner.rollbackTransaction() logger.error(`Creation was not successful: ${e}`) From f7454e31460b4d6923f81a23a1f17d5be5b1d3ac Mon Sep 17 00:00:00 2001 From: ogerly Date: Tue, 13 Sep 2022 10:18:16 +0200 Subject: [PATCH 52/72] fix bug --- .../src/components/Contributions/ContributionList.vue | 6 ++++++ .../components/Contributions/ContributionListItem.vue | 9 +++++++-- frontend/src/pages/Community.vue | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Contributions/ContributionList.vue b/frontend/src/components/Contributions/ContributionList.vue index 11cc0f9dd..ca4e7a9a0 100644 --- a/frontend/src/components/Contributions/ContributionList.vue +++ b/frontend/src/components/Contributions/ContributionList.vue @@ -4,6 +4,7 @@ {{ memo }}
@@ -141,6 +141,11 @@ export default { type: Number, required: true, }, + allContribution: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { diff --git a/frontend/src/pages/Community.vue b/frontend/src/pages/Community.vue index 8906f40e7..c98bfff2d 100644 --- a/frontend/src/pages/Community.vue +++ b/frontend/src/pages/Community.vue @@ -73,6 +73,7 @@ :contributionCount="contributionCountAll" :showPagination="true" :pageSize="pageSizeAll" + :allContribution="true" /> From 65318c4498ca509be82c617db813f350db4bdd92 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 13 Sep 2022 13:37:30 +0200 Subject: [PATCH 53/72] release: Version 1.12.1 --- CHANGELOG.md | 7 +++++++ admin/package.json | 2 +- backend/package.json | 2 +- database/package.json | 2 +- frontend/package.json | 2 +- package.json | 2 +- 6 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 352286e45..46a704739 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,15 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [1.12.1](https://github.com/gradido/gradido/compare/1.12.0...1.12.1) + +- fix: 🍰 Show Not Icons In `allContribution` List [`#2195`](https://github.com/gradido/gradido/pull/2195) + #### [1.12.0](https://github.com/gradido/gradido/compare/1.11.0...1.12.0) +> 12 September 2022 + +- release: v1.12.0 [`#2191`](https://github.com/gradido/gradido/pull/2191) - if message empty else disabled button [`#2189`](https://github.com/gradido/gradido/pull/2189) - messages show if Confirmed [`#2185`](https://github.com/gradido/gradido/pull/2185) - text in messages smaller [`#2186`](https://github.com/gradido/gradido/pull/2186) diff --git a/admin/package.json b/admin/package.json index f31b43bbd..f56ae0a87 100644 --- a/admin/package.json +++ b/admin/package.json @@ -3,7 +3,7 @@ "description": "Administraion Interface for Gradido", "main": "index.js", "author": "Moriz Wahl", - "version": "1.12.0", + "version": "1.12.1", "license": "Apache-2.0", "private": false, "scripts": { diff --git a/backend/package.json b/backend/package.json index b826183b6..2a054314f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "gradido-backend", - "version": "1.12.0", + "version": "1.12.1", "description": "Gradido unified backend providing an API-Service for Gradido Transactions", "main": "src/index.ts", "repository": "https://github.com/gradido/gradido/backend", diff --git a/database/package.json b/database/package.json index 40e5d0294..cabb1b47a 100644 --- a/database/package.json +++ b/database/package.json @@ -1,6 +1,6 @@ { "name": "gradido-database", - "version": "1.12.0", + "version": "1.12.1", "description": "Gradido Database Tool to execute database migrations", "main": "src/index.ts", "repository": "https://github.com/gradido/gradido/database", diff --git a/frontend/package.json b/frontend/package.json index b3832b080..011193b58 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "bootstrap-vue-gradido-wallet", - "version": "1.12.0", + "version": "1.12.1", "private": true, "scripts": { "start": "node run/server.js", diff --git a/package.json b/package.json index 519c1b59c..8bc24d402 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gradido", - "version": "1.12.0", + "version": "1.12.1", "description": "Gradido", "main": "index.js", "repository": "git@github.com:gradido/gradido.git", From a87667af02206293072ea7148b9d3a9ceff9dd5d Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Wed, 14 Sep 2022 08:34:41 +0200 Subject: [PATCH 54/72] Update backend/src/mailer/sendAddedContributionMessageEmail.test.ts Co-authored-by: Moriz Wahl --- backend/src/mailer/sendAddedContributionMessageEmail.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/mailer/sendAddedContributionMessageEmail.test.ts b/backend/src/mailer/sendAddedContributionMessageEmail.test.ts index a35836e2a..d867fd55b 100644 --- a/backend/src/mailer/sendAddedContributionMessageEmail.test.ts +++ b/backend/src/mailer/sendAddedContributionMessageEmail.test.ts @@ -30,7 +30,7 @@ describe('sendAddedContributionMessageEmail', () => { text: expect.stringContaining('Hallo Bibi Bloxberg') && expect.stringContaining('Peter Lustig') && - expect.stringContaining('Vielen herzlichen Dank für den neuen Hexenbesen!') && + expect.stringContaining('Du hast soeben zu deinem eingereichten Gradido Schöpfungsantrag "Vielen herzlichen Dank für den neuen Hexenbesen!" eine Rückfrage von Peter Lustig erhalten.') && expect.stringContaining('Was für ein Besen ist es geworden?') && expect.stringContaining('http://localhost/overview'), }) From d65bd827ca87648975faa77c18a24cfb5be52860 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 14 Sep 2022 09:03:28 +0200 Subject: [PATCH 55/72] Change search to get the contribution.user instead of queriing it after wards. --- backend/src/graphql/resolver/AdminResolver.ts | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index a1c091283..9b9c1d8d1 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -714,7 +714,10 @@ export class AdminResolver { await queryRunner.startTransaction('READ UNCOMMITTED') const contributionMessage = DbContributionMessage.create() try { - const contribution = await Contribution.findOne({ id: contributionId }) + const contribution = await Contribution.findOne({ + where: { id: contributionId }, + relations: ['user'], + }) if (!contribution) { throw new Error('Contribution not found') } @@ -738,20 +741,18 @@ export class AdminResolver { await queryRunner.manager.update(Contribution, { id: contributionId }, contribution) } await queryRunner.commitTransaction() - const contributionUser = await dbUser.findOne({ id: contribution.userId }) - if (contributionUser) { - await sendAddedContributionMessageEmail({ - senderFirstName: user.firstName, - senderLastName: user.lastName, - recipientFirstName: contributionUser.firstName, - recipientLastName: contributionUser.lastName, - recipientEmail: contributionUser.email, - senderEmail: user.email, - contributionMemo: contribution.memo, - message, - overviewURL: CONFIG.EMAIL_LINK_OVERVIEW, - }) - } + + await sendAddedContributionMessageEmail({ + senderFirstName: user.firstName, + senderLastName: user.lastName, + recipientFirstName: contribution.user.firstName, + recipientLastName: contribution.user.lastName, + recipientEmail: contribution.user.email, + senderEmail: user.email, + contributionMemo: contribution.memo, + message, + overviewURL: CONFIG.EMAIL_LINK_OVERVIEW, + }) } catch (e) { await queryRunner.rollbackTransaction() logger.error(`ContributionMessage was not successful: ${e}`) From 0820d8d529e44e6a466bd33bb02215ec4a2f848b Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 14 Sep 2022 09:04:39 +0200 Subject: [PATCH 56/72] Tests that the method sendAddedContributionMessageEmail is called. --- .../ContributionMessageResolver.test.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index c0e330750..40e9e2ace 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -12,6 +12,14 @@ import { listContributionMessages, login } from '@/seeds/graphql/queries' import { userFactory } from '@/seeds/factory/user' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { peterLustig } from '@/seeds/users/peter-lustig' +import { sendAddedContributionMessageEmail } from '@/mailer/sendAddedContributionMessageEmail' + +jest.mock('@/mailer/sendAddedContributionMessageEmail', () => { + return { + __esModule: true, + sendAddedContributionMessageEmail: jest.fn(), + } +}) let mutate: any, query: any, con: any let testEnv: any @@ -151,6 +159,20 @@ describe('ContributionMessageResolver', () => { }), ) }) + + it('calls sendAddedContributionMessageEmail', async () => { + expect(sendAddedContributionMessageEmail).toBeCalledWith({ + senderFirstName: 'Peter', + senderLastName: 'Lustig', + recipientFirstName: 'Bibi', + recipientLastName: 'Bloxberg', + recipientEmail: 'bibi@bloxberg.de', + senderEmail: 'peter@lustig.de', + contributionMemo: 'Test env contribution', + message: 'Admin Test', + overviewURL: 'http://localhost/overview', + }) + }) }) }) }) From 3b810b2addf33494d5f457b3f70cfebd8b2260dd Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 14 Sep 2022 09:31:39 +0200 Subject: [PATCH 57/72] Tests that sendContributionConfirmedEmail is called, removed contributionAmount since it can not be checked. --- .../graphql/resolver/AdminResolver.test.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index f0ce064b4..74669a40a 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -40,6 +40,8 @@ import Decimal from 'decimal.js-light' import { Contribution } from '@entity/Contribution' import { Transaction as DbTransaction } from '@entity/Transaction' import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' +import { sendContributionConfirmedEmail } from '@/mailer/sendContributionConfirmedEmail' +import { Any } from '@dbTools/typeorm' // mock account activation email to avoid console spam jest.mock('@/mailer/sendAccountActivationEmail', () => { @@ -49,6 +51,14 @@ jest.mock('@/mailer/sendAccountActivationEmail', () => { } }) +// mock account activation email to avoid console spam +jest.mock('@/mailer/sendContributionConfirmedEmail', () => { + return { + __esModule: true, + sendContributionConfirmedEmail: jest.fn(), + } +}) + let mutate: any, query: any, con: any let testEnv: any @@ -1450,6 +1460,20 @@ describe('AdminResolver', () => { expect(transaction[0].linkedUserId).toEqual(null) expect(transaction[0].typeId).toEqual(1) }) + + it('calls sendContributionConfirmedEmail', async () => { + expect(sendContributionConfirmedEmail).toBeCalledWith( + expect.objectContaining({ + contributionMemo: 'Herzlich Willkommen bei Gradido liebe Bibi!', + overviewURL: 'http://localhost/overview', + recipientEmail: 'bibi@bloxberg.de', + recipientFirstName: 'Bibi', + recipientLastName: 'Bloxberg', + senderFirstName: 'Peter', + senderLastName: 'Lustig', + }), + ) + }) }) describe('confirm two creations one after the other quickly', () => { From 6d15ea5c2b337d5b686b649160c0789943d9f56c Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 14 Sep 2022 11:20:50 +0200 Subject: [PATCH 58/72] Remove unused import. --- backend/src/graphql/resolver/AdminResolver.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index 74669a40a..75c672bd5 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -41,7 +41,6 @@ import { Contribution } from '@entity/Contribution' import { Transaction as DbTransaction } from '@entity/Transaction' import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' import { sendContributionConfirmedEmail } from '@/mailer/sendContributionConfirmedEmail' -import { Any } from '@dbTools/typeorm' // mock account activation email to avoid console spam jest.mock('@/mailer/sendAccountActivationEmail', () => { From e3257c1f717cc32ac589b0e8c016bf121de6b34e Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 14 Sep 2022 11:22:42 +0200 Subject: [PATCH 59/72] Fix prettier. --- backend/src/mailer/sendAddedContributionMessageEmail.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/mailer/sendAddedContributionMessageEmail.test.ts b/backend/src/mailer/sendAddedContributionMessageEmail.test.ts index d867fd55b..1151a0abc 100644 --- a/backend/src/mailer/sendAddedContributionMessageEmail.test.ts +++ b/backend/src/mailer/sendAddedContributionMessageEmail.test.ts @@ -30,7 +30,9 @@ describe('sendAddedContributionMessageEmail', () => { text: expect.stringContaining('Hallo Bibi Bloxberg') && expect.stringContaining('Peter Lustig') && - expect.stringContaining('Du hast soeben zu deinem eingereichten Gradido Schöpfungsantrag "Vielen herzlichen Dank für den neuen Hexenbesen!" eine Rückfrage von Peter Lustig erhalten.') && + expect.stringContaining( + 'Du hast soeben zu deinem eingereichten Gradido Schöpfungsantrag "Vielen herzlichen Dank für den neuen Hexenbesen!" eine Rückfrage von Peter Lustig erhalten.', + ) && expect.stringContaining('Was für ein Besen ist es geworden?') && expect.stringContaining('http://localhost/overview'), }) From a2309ea1a552335f6b95ca7fa6855e3aada801c4 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 14 Sep 2022 11:28:28 +0200 Subject: [PATCH 60/72] Correct text, 'Deine' => 'Dein'. --- backend/src/mailer/sendContributionConfirmedEmail.test.ts | 2 +- backend/src/mailer/text/contributionConfirmed.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/mailer/sendContributionConfirmedEmail.test.ts b/backend/src/mailer/sendContributionConfirmedEmail.test.ts index 7fcb7c993..1935144fd 100644 --- a/backend/src/mailer/sendContributionConfirmedEmail.test.ts +++ b/backend/src/mailer/sendContributionConfirmedEmail.test.ts @@ -30,7 +30,7 @@ describe('sendContributionConfirmedEmail', () => { text: expect.stringContaining('Hallo Bibi Bloxberg') && expect.stringContaining( - 'Deine Gradido Schöpfungsantrag "Vielen herzlichen Dank für den neuen Hexenbesen!" wurde soeben bestätigt.', + 'Dein Gradido Schöpfungsantrag "Vielen herzlichen Dank für den neuen Hexenbesen!" wurde soeben bestätigt.', ) && expect.stringContaining('Betrag: 200,00 GDD') && expect.stringContaining('Link zu deinem Konto: http://localhost/overview'), diff --git a/backend/src/mailer/text/contributionConfirmed.ts b/backend/src/mailer/text/contributionConfirmed.ts index d5d35fdda..2d9fce6a8 100644 --- a/backend/src/mailer/text/contributionConfirmed.ts +++ b/backend/src/mailer/text/contributionConfirmed.ts @@ -14,9 +14,9 @@ export const contributionConfirmed = { }): string => `Hallo ${data.recipientFirstName} ${data.recipientLastName}, -Deine Gradido Schöpfungsantrag "${data.contributionMemo}" wurde soeben von ${ - data.senderFirstName - } ${data.senderLastName} bestätigt. +Dein Gradido Schöpfungsantrag "${data.contributionMemo}" wurde soeben von ${data.senderFirstName} ${ + data.senderLastName + } bestätigt. Betrag: ${data.contributionAmount.toFixed(2).replace('.', ',')} GDD Bitte antworte nicht auf diese E-Mail! From ded649f342a61798b521ff68406a8549efeedff0 Mon Sep 17 00:00:00 2001 From: joseji Date: Thu, 15 Sep 2022 11:42:03 +0200 Subject: [PATCH 61/72] Events and logs completed in User Resolver --- .../src/graphql/resolver/UserResolver.test.ts | 67 +++++++++++++++++-- backend/src/graphql/resolver/UserResolver.ts | 6 +- 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 36bae56de..58d697d8e 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -19,6 +19,8 @@ import { contributionLinkFactory } from '@/seeds/factory/contributionLink' import { ContributionLink } from '@model/ContributionLink' // import { TransactionLink } from '@entity/TransactionLink' +import { EventProtocolType } from '@/event/EventProtocolType' +import { EventProtocol } from '@entity/EventProtocol' import { logger } from '@test/testSetup' import { validate as validateUUID, version as versionUUID } from 'uuid' import { peterLustig } from '@/seeds/users/peter-lustig' @@ -169,6 +171,14 @@ describe('UserResolver', () => { duration: expect.any(String), }) }) + + it('stores the send confirmation event in the database', () => { + expect(EventProtocol.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventProtocolType.SEND_CONFIRMATION_EMAIL, + }), + ) + }) }) describe('email already exists', () => { @@ -383,6 +393,10 @@ bei Gradidio sei dabei!`, }), ) }) + + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith('Password entered is lexically invalid') + }) }) describe('no valid optin code', () => { @@ -405,6 +419,10 @@ bei Gradidio sei dabei!`, }), ) }) + + it('logs the error found', () => { + expect(logger.error).toBeCalledWith('Could not login with emailVerificationCode') + }) }) }) @@ -433,6 +451,10 @@ bei Gradidio sei dabei!`, }), ) }) + + it('logs the error found', () => { + expect(logger.error).toBeCalledWith('User with email=bibi@bloxberg.de does not exist') + }) }) describe('user is in database and correct login data', () => { @@ -475,6 +497,7 @@ bei Gradidio sei dabei!`, describe('user is in database and wrong password', () => { beforeAll(async () => { await userFactory(testEnv, bibiBloxberg) + result = await query({ query: login, variables: { ...variables, password: 'wrong' } }) }) afterAll(async () => { @@ -482,14 +505,16 @@ bei Gradidio sei dabei!`, }) it('returns an error', () => { - expect( - query({ query: login, variables: { ...variables, password: 'wrong' } }), - ).resolves.toEqual( + expect(result).toEqual( expect.objectContaining({ errors: [new GraphQLError('No user with this credentials')], }), ) }) + + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith('The User has no valid credentials.') + }) }) }) @@ -595,6 +620,14 @@ bei Gradidio sei dabei!`, }), ) }) + + it('stores the login event in the database', () => { + expect(EventProtocol.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventProtocolType.LOGIN, + }), + ) + }) }) }) }) @@ -649,13 +682,17 @@ bei Gradidio sei dabei!`, }) describe('request reset password again', () => { - it('thows an error', async () => { + it('throws an error', async () => { await expect(mutate({ mutation: forgotPassword, variables })).resolves.toEqual( expect.objectContaining({ 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`) + }) }) }) }) @@ -766,7 +803,7 @@ bei Gradidio sei dabei!`, }) describe('language is not valid', () => { - it('thows an error', async () => { + it('throws an error', async () => { await expect( mutate({ mutation: updateUserInfos, @@ -780,6 +817,10 @@ bei Gradidio sei dabei!`, }), ) }) + + it('logs the error found', () => { + expect(logger.error).toBeCalledWith(`"not-valid" isn't a valid language`) + }) }) describe('password', () => { @@ -799,6 +840,10 @@ bei Gradidio sei dabei!`, }), ) }) + + it('logs the error found', () => { + expect(logger.error).toBeCalledWith(`Old password is invalid`) + }) }) describe('invalid new password', () => { @@ -821,6 +866,10 @@ bei Gradidio sei dabei!`, }), ) }) + + it('logs the error found', () => { + expect(logger.error).toBeCalledWith('newPassword does not fullfil the rules') + }) }) describe('correct old and new password', () => { @@ -840,7 +889,7 @@ bei Gradidio sei dabei!`, ) }) - it('can login wtih new password', async () => { + it('can login with new password', async () => { await expect( query({ query: login, @@ -860,7 +909,7 @@ bei Gradidio sei dabei!`, ) }) - it('cannot login wtih old password', async () => { + it('cannot login with old password', async () => { await expect( query({ query: login, @@ -875,6 +924,10 @@ bei Gradidio sei dabei!`, }), ) }) + + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith('The User has no valid credentials.') + }) }) }) }) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 3b7013323..2e2f5aeec 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -273,7 +273,7 @@ export class UserResolver { logger.info(`login with ${email}, ***, ${publisherId} ...`) email = email.trim().toLowerCase() const dbUser = await DbUser.findOneOrFail({ email }, { withDeleted: true }).catch(() => { - logger.error(`User with email=${email} does not exists`) + logger.error(`User with email=${email} does not exist`) throw new Error('No user with this credentials') }) if (dbUser.deletedAt) { @@ -389,7 +389,7 @@ export class UserResolver { /* uncomment this, when you need the activation link on the console */ // In case EMails are disabled log the activation link for the user if (!emailSent) { - logger.debug(`Email not send!`) + logger.debug(`Email not sent!`) } logger.info('createUser() faked and send multi registration mail...') @@ -548,6 +548,7 @@ export class UserResolver { logger.info(`setPassword(${code}, ***)...`) // Validate Password if (!isPassword(password)) { + logger.error('Password entered is lexically invalid') throw new Error( 'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!', ) @@ -727,6 +728,7 @@ export class UserResolver { try { await queryRunner.manager.save(userEntity).catch((error) => { + logger.error('error saving user: ' + error) throw new Error('error saving user: ' + error) }) From dbc32973056a1d3f004c95837f649d6de3fc791a Mon Sep 17 00:00:00 2001 From: ogerly Date: Thu, 15 Sep 2022 21:02:16 +0200 Subject: [PATCH 62/72] change support mail --- frontend/src/pages/InfoStatistic.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/InfoStatistic.vue b/frontend/src/pages/InfoStatistic.vue index 309404f48..7d65c4928 100644 --- a/frontend/src/pages/InfoStatistic.vue +++ b/frontend/src/pages/InfoStatistic.vue @@ -83,7 +83,7 @@ export default { countAdminUser: null, itemsContributionLinks: [], itemsAdminUser: [], - supportMail: 'support@supportemail.de', + supportMail: 'support@gradido.net', membersCount: '1203', totalUsers: null, totalGradidoCreated: null, From f19b5217264a59bb0823f39b4574709c6edabb8f Mon Sep 17 00:00:00 2001 From: ogerly Date: Fri, 16 Sep 2022 09:01:29 +0200 Subject: [PATCH 63/72] add support mail in env --- deployment/bare_metal/.env.dist | 4 +++- deployment/bare_metal/install.sh | 2 +- frontend/.env.dist | 7 +++++-- frontend/.env.template | 5 ++++- frontend/src/config/index.js | 7 ++++++- frontend/src/pages/InfoStatistic.vue | 2 +- 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/deployment/bare_metal/.env.dist b/deployment/bare_metal/.env.dist index 1255104c9..ebb6717e7 100644 --- a/deployment/bare_metal/.env.dist +++ b/deployment/bare_metal/.env.dist @@ -61,7 +61,7 @@ EVENT_PROTOCOL_DISABLED=false DATABASE_CONFIG_VERSION=v1.2022-03-18 # frontend -FRONTEND_CONFIG_VERSION=v2.2022-04-07 +FRONTEND_CONFIG_VERSION=v3.2022-09-16 GRAPHQL_URI=https://stage1.gradido.net/graphql ADMIN_AUTH_URL=https://stage1.gradido.net/admin/authenticate?token={token} @@ -77,6 +77,8 @@ META_KEYWORDS_DE="Grundeinkommen, Währung, Dankbarkeit, Schenk-Ökonomie, Natü META_KEYWORDS_EN="Basic Income, Currency, Gratitude, Gift Economy, Natural Economy of Life, Economy, Ecology, Potential Development, Giving and Thanking, Cycle of Life, Monetary System" META_AUTHOR="Bernd Hückstädt - Gradido-Akademie" +SUPPORT_MAIL=support@supportmail.com + # admin ADMIN_CONFIG_VERSION=v1.2022-03-18 diff --git a/deployment/bare_metal/install.sh b/deployment/bare_metal/install.sh index 0fcf6d847..9366cbc75 100755 --- a/deployment/bare_metal/install.sh +++ b/deployment/bare_metal/install.sh @@ -75,7 +75,7 @@ pm2 startup sudo apt-get install -y certbot sudo apt-get install -y python3-certbot-nginx sudo certbot -> Enter email address (used for urgent renewal and security notices) > support@gradido.net +> Enter email address (used for urgent renewal and security notices) > e.g. support@supportmail.com > Please read the Terms of Service at > Y > Would you be willing, once your first certificate is successfully issued, to > N > No names were found in your configuration files. Please enter in your domain > stage1.gradido.net diff --git a/frontend/.env.dist b/frontend/.env.dist index 65d9ae305..4127f339a 100644 --- a/frontend/.env.dist +++ b/frontend/.env.dist @@ -1,4 +1,4 @@ -CONFIG_VERSION=v2.2022-04-07 +CONFIG_VERSION=v3.2022-09-16 # Environment DEFAULT_PUBLISHER_ID=2896 @@ -21,4 +21,7 @@ META_DESCRIPTION_DE="Dankbarkeit ist die Währung der neuen Zeit. Immer mehr Men META_DESCRIPTION_EN="Gratitude is the currency of the new age. More and more people are unleashing their potential and shaping a good future for all." META_KEYWORDS_DE="Grundeinkommen, Währung, Dankbarkeit, Schenk-Ökonomie, Natürliche Ökonomie des Lebens, Ökonomie, Ökologie, Potenzialentfaltung, Schenken und Danken, Kreislauf des Lebens, Geldsystem" META_KEYWORDS_EN="Basic Income, Currency, Gratitude, Gift Economy, Natural Economy of Life, Economy, Ecology, Potential Development, Giving and Thanking, Cycle of Life, Monetary System" -META_AUTHOR="Bernd Hückstädt - Gradido-Akademie" \ No newline at end of file +META_AUTHOR="Bernd Hückstädt - Gradido-Akademie" + +# Support Mail +SUPPORT_MAIL=support@supportmail.com \ No newline at end of file diff --git a/frontend/.env.template b/frontend/.env.template index 6d6b83fc7..0b9d34b38 100644 --- a/frontend/.env.template +++ b/frontend/.env.template @@ -21,4 +21,7 @@ META_DESCRIPTION_DE=$META_DESCRIPTION_DE META_DESCRIPTION_EN=$META_DESCRIPTION_EN META_KEYWORDS_DE=$META_KEYWORDS_DE META_KEYWORDS_EN=$META_KEYWORDS_EN -META_AUTHOR=$META_AUTHOR \ No newline at end of file +META_AUTHOR=$META_AUTHOR + +# Support Mail +SUPPORT_MAIL=$SUPPORT_MAIL \ No newline at end of file diff --git a/frontend/src/config/index.js b/frontend/src/config/index.js index 917a25d70..5ab5f2392 100644 --- a/frontend/src/config/index.js +++ b/frontend/src/config/index.js @@ -8,7 +8,7 @@ const constants = { DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 CONFIG_VERSION: { DEFAULT: 'DEFAULT', - EXPECTED: 'v2.2022-04-07', + EXPECTED: 'v3.2022-09-16', CURRENT: '', }, } @@ -60,6 +60,10 @@ const meta = { META_AUTHOR: process.env.META_AUTHOR || 'Bernd Hückstädt - Gradido-Akademie', } +const supportmail = { + SUPPORT_MAIL: process.env.SUPPORT_MAIL || 'support@supportmail.com', +} + // Check config version constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION || constants.CONFIG_VERSION.DEFAULT if ( @@ -79,6 +83,7 @@ const CONFIG = { ...endpoints, ...community, ...meta, + ...supportmail, } module.exports = CONFIG diff --git a/frontend/src/pages/InfoStatistic.vue b/frontend/src/pages/InfoStatistic.vue index 7d65c4928..1e09f83ed 100644 --- a/frontend/src/pages/InfoStatistic.vue +++ b/frontend/src/pages/InfoStatistic.vue @@ -83,7 +83,7 @@ export default { countAdminUser: null, itemsContributionLinks: [], itemsAdminUser: [], - supportMail: 'support@gradido.net', + supportMail: CONFIG.SUPPORT_MAIL, membersCount: '1203', totalUsers: null, totalGradidoCreated: null, From 390bb1db3de2c1621a2e44a502737056c352ce9a Mon Sep 17 00:00:00 2001 From: joseji Date: Fri, 16 Sep 2022 12:22:32 +0200 Subject: [PATCH 64/72] IDs also tested while event saving, added ActivateAccount event when setting password --- backend/src/graphql/resolver/UserResolver.test.ts | 12 ++++++++++++ backend/src/graphql/resolver/UserResolver.ts | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 58d697d8e..2c6406939 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -176,6 +176,7 @@ describe('UserResolver', () => { expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.SEND_CONFIRMATION_EMAIL, + userId: expect.any(Number), // as it is randomly generated }), ) }) @@ -255,6 +256,7 @@ describe('UserResolver', () => { mutation: setPassword, variables: { code: emailOptIn, password: 'Aa12345_' }, }) + // make Peter Lustig Admin const peter = await User.findOneOrFail({ id: user[0].id }) peter.isAdmin = new Date() @@ -281,6 +283,15 @@ describe('UserResolver', () => { }), ) }) + + it('stores the account activated event in the database', () => { + expect(EventProtocol.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventProtocolType.ACTIVATE_ACCOUNT, + userId: expect.any(Number), // as it is randomly generated + }), + ) + }) }) /* A transaction link requires GDD on account @@ -625,6 +636,7 @@ bei Gradidio sei dabei!`, expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.LOGIN, + userId: expect.any(Number), // as it is randomly generated }), ) }) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 2e2f5aeec..fad0fadd3 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -30,6 +30,7 @@ import { EventRedeemRegister, EventRegister, EventSendConfirmationEmail, + EventActivateAccount, } from '@/event/Event' import { getUserCreation } from './util/creations' import { UserRepository } from '@/typeorm/repository/User' @@ -611,6 +612,8 @@ export class UserResolver { await queryRunner.connect() await queryRunner.startTransaction('READ UNCOMMITTED') + const event = new Event() + try { // Save user await queryRunner.manager.save(user).catch((error) => { @@ -619,6 +622,9 @@ export class UserResolver { }) await queryRunner.commitTransaction() + const eventActivateAccount = new EventActivateAccount() + eventActivateAccount.userId = user.id + eventProtocol.writeEvent(event.setEventActivateAccount(eventActivateAccount)) logger.info('User data written successfully...') } catch (e) { await queryRunner.rollbackTransaction() From cfdec3d1226a029e719d28d0db5c09cccf267bd2 Mon Sep 17 00:00:00 2001 From: joseji Date: Fri, 16 Sep 2022 13:45:20 +0200 Subject: [PATCH 65/72] Fixed wrong handling of dates in tests failing depending on the day you execute them --- backend/src/graphql/resolver/UserResolver.test.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 2c6406939..367f82b4f 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -261,14 +261,21 @@ describe('UserResolver', () => { const peter = await User.findOneOrFail({ id: user[0].id }) peter.isAdmin = new Date() await peter.save() + + // date statement + const actualDate = new Date() + const futureDate = new Date() // Create a future day from the executed day + futureDate.setDate(futureDate.getDate() + 1) + // factory logs in as Peter Lustig link = await contributionLinkFactory(testEnv, { name: 'Dokumenta 2022', memo: 'Vielen Dank für deinen Besuch bei der Dokumenta 2022', amount: 200, - validFrom: new Date(2022, 5, 18), - validTo: new Date(2022, 8, 25), + validFrom: actualDate, + validTo: futureDate, }) + resetToken() await mutate({ mutation: createUser, From 5f94cfca88de2896624a8de48c4457adf8331556 Mon Sep 17 00:00:00 2001 From: jjimenezgarcia <99907380+jjimenezgarcia@users.noreply.github.com> Date: Mon, 19 Sep 2022 15:40:59 +0200 Subject: [PATCH 66/72] Update backend/src/graphql/resolver/UserResolver.test.ts Co-authored-by: Moriz Wahl --- backend/src/graphql/resolver/UserResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 367f82b4f..5be3c3e20 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -176,7 +176,7 @@ describe('UserResolver', () => { expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.SEND_CONFIRMATION_EMAIL, - userId: expect.any(Number), // as it is randomly generated + userId: user.id, }), ) }) From 1203dada61f234a8afc8ca4878c06b7fccb44a5b Mon Sep 17 00:00:00 2001 From: joseji Date: Mon, 19 Sep 2022 19:18:06 +0200 Subject: [PATCH 67/72] ids are now checked in event creation --- backend/src/graphql/resolver/UserResolver.test.ts | 9 ++++++--- backend/src/graphql/resolver/UserResolver.ts | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 5be3c3e20..13715e088 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -176,7 +176,7 @@ describe('UserResolver', () => { expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.SEND_CONFIRMATION_EMAIL, - userId: user.id, + userId: user[0].id, }), ) }) @@ -295,7 +295,7 @@ describe('UserResolver', () => { expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.ACTIVATE_ACCOUNT, - userId: expect.any(Number), // as it is randomly generated + userId: user[0].id, }), ) }) @@ -605,6 +605,8 @@ bei Gradidio sei dabei!`, }) describe('authenticated', () => { + let user: User[] + const variables = { email: 'bibi@bloxberg.de', password: 'Aa12345_', @@ -612,6 +614,7 @@ bei Gradidio sei dabei!`, beforeAll(async () => { await query({ query: login, variables }) + user = await User.find() }) afterAll(() => { @@ -643,7 +646,7 @@ bei Gradidio sei dabei!`, expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.LOGIN, - userId: expect.any(Number), // as it is randomly generated + userId: user[0].id, }), ) }) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index fad0fadd3..f2fd048fc 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -622,9 +622,11 @@ export class UserResolver { }) await queryRunner.commitTransaction() + const eventActivateAccount = new EventActivateAccount() eventActivateAccount.userId = user.id eventProtocol.writeEvent(event.setEventActivateAccount(eventActivateAccount)) + logger.info('User data written successfully...') } catch (e) { await queryRunner.rollbackTransaction() From bf6ed35c6e1b359599fa6f28483e8e3c8eb65587 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 21 Sep 2022 12:05:49 +0200 Subject: [PATCH 68/72] Change text template of ContributionMessageReceived. --- .../src/mailer/text/contributionMessageReceived.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/backend/src/mailer/text/contributionMessageReceived.ts b/backend/src/mailer/text/contributionMessageReceived.ts index 4affb71a8..b0c9c4d30 100644 --- a/backend/src/mailer/text/contributionMessageReceived.ts +++ b/backend/src/mailer/text/contributionMessageReceived.ts @@ -14,17 +14,15 @@ export const contributionMessageReceived = { }): string => `Hallo ${data.recipientFirstName} ${data.recipientLastName}, -Du hast soeben zu deinem eingereichten Gradido Schöpfungsantrag "${data.contributionMemo}" eine Rückfrage von ${data.senderFirstName} ${data.senderLastName} erhalten. -Die Rückfrage lautet: +du hast soeben zu deinem eingereichten Gemeinwohl-Beitrag "${data.contributionMemo}" eine Rückfrage von ${data.senderFirstName} ${data.senderLastName} erhalten. -${data.message} +Bitte beantworte die Rückfrage in deinem Gradido-Konto im Menü "Gemeinschaft" im Tab "Meine Beiträge zum Gemeinwohl"! + +Link zu deinem Konto: ${data.overviewURL} Bitte antworte nicht auf diese E-Mail! Mit freundlichen Grüßen, -dein Gradido-Team - - -Link zu deinem Konto: ${data.overviewURL}`, +dein Gradido-Team`, }, } From 6c446fb4772d7af635658c6d25e86eb68b9a6029 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 21 Sep 2022 12:07:19 +0200 Subject: [PATCH 69/72] Change text template of transactionReceived. --- backend/src/mailer/text/transactionReceived.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/backend/src/mailer/text/transactionReceived.ts b/backend/src/mailer/text/transactionReceived.ts index 2764d1b60..c533b5b1f 100644 --- a/backend/src/mailer/text/transactionReceived.ts +++ b/backend/src/mailer/text/transactionReceived.ts @@ -19,17 +19,13 @@ export const transactionReceived = { Du hast soeben ${data.amount.toFixed(2).replace('.', ',')} GDD von ${data.senderFirstName} ${ data.senderLastName } (${data.senderEmail}) erhalten. -${data.senderFirstName} ${data.senderLastName} schreibt: -${data.memo} +Details zur Transaktion findest du in deinem Gradido-Konto: ${data.overviewURL} Bitte antworte nicht auf diese E-Mail! Mit freundlichen Grüßen, -dein Gradido-Team - - -Link zu deinem Konto: ${data.overviewURL}`, +dein Gradido-Team`, }, } From 3b3a963ac5cc4c1a35b63a45957b71292d31ce89 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 21 Sep 2022 12:09:36 +0200 Subject: [PATCH 70/72] Change text template of contributionConfirmed. --- backend/src/mailer/text/contributionConfirmed.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/src/mailer/text/contributionConfirmed.ts b/backend/src/mailer/text/contributionConfirmed.ts index 2d9fce6a8..dc82d7615 100644 --- a/backend/src/mailer/text/contributionConfirmed.ts +++ b/backend/src/mailer/text/contributionConfirmed.ts @@ -14,9 +14,10 @@ export const contributionConfirmed = { }): string => `Hallo ${data.recipientFirstName} ${data.recipientLastName}, -Dein Gradido Schöpfungsantrag "${data.contributionMemo}" wurde soeben von ${data.senderFirstName} ${ - data.senderLastName - } bestätigt. +Dein eingereichter Gemeinwohl-Beitrag "${data.contributionMemo}" wurde soeben von ${ + data.senderFirstName + } ${data.senderLastName} bestätigt. + Betrag: ${data.contributionAmount.toFixed(2).replace('.', ',')} GDD Bitte antworte nicht auf diese E-Mail! From 686c43fa895ced5ad94042b2469365ccabe35d10 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 21 Sep 2022 12:12:23 +0200 Subject: [PATCH 71/72] Change text of linkRedeemed template. --- backend/src/mailer/text/transactionReceived.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/src/mailer/text/transactionReceived.ts b/backend/src/mailer/text/transactionReceived.ts index c533b5b1f..4f10f41c8 100644 --- a/backend/src/mailer/text/transactionReceived.ts +++ b/backend/src/mailer/text/transactionReceived.ts @@ -47,15 +47,16 @@ export const transactionLinkRedeemed = { ${data.senderFirstName} ${data.senderLastName} (${ data.senderEmail - }) hat soeben deinen Link eingelösst. + }) hat soeben deinen Link eingelöst. + Betrag: ${data.amount.toFixed(2).replace('.', ',')} GDD, Memo: ${data.memo} +Details zur Transaktion findest du in deinem Gradido-Konto: ${data.overviewURL} + Bitte antworte nicht auf diese E-Mail! Mit freundlichen Grüßen, -dein Gradido-Team - -Link zu deinem Konto: ${data.overviewURL}`, +dein Gradido-Team`, }, } From 6c0cb17196e82119f16e57f9130006361ab112ea Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 21 Sep 2022 12:41:25 +0200 Subject: [PATCH 72/72] Refactor sendTransactionLinkredeemed . --- .../graphql/resolver/TransactionResolver.ts | 7 +-- .../sendTransactionLinkRedeemed.test.ts | 44 +++++++++++++++++++ .../src/mailer/sendTransactionLinkRedeemed.ts | 28 ++++++++++++ .../sendTransactionReceivedEmail.test.ts | 2 - .../mailer/sendTransactionReceivedEmail.ts | 27 +----------- .../mailer/text/transactionLinkRedeemed.ts | 33 ++++++++++++++ .../src/mailer/text/transactionReceived.ts | 33 -------------- 7 files changed, 108 insertions(+), 66 deletions(-) create mode 100644 backend/src/mailer/sendTransactionLinkRedeemed.test.ts create mode 100644 backend/src/mailer/sendTransactionLinkRedeemed.ts create mode 100644 backend/src/mailer/text/transactionLinkRedeemed.ts diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 1d8748f58..c192ae9dc 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -8,10 +8,7 @@ import { Context, getUser } from '@/server/context' import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql' import { getCustomRepository, getConnection } from '@dbTools/typeorm' -import { - sendTransactionLinkRedeemedEmail, - sendTransactionReceivedEmail, -} from '@/mailer/sendTransactionReceivedEmail' +import { sendTransactionReceivedEmail } from '@/mailer/sendTransactionReceivedEmail' import { Transaction } from '@model/Transaction' import { TransactionList } from '@model/TransactionList' @@ -38,6 +35,7 @@ import Decimal from 'decimal.js-light' import { BalanceResolver } from './BalanceResolver' import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const' +import { sendTransactionLinkRedeemedEmail } from '@/mailer/sendTransactionLinkRedeemed' export const executeTransaction = async ( amount: Decimal, @@ -154,7 +152,6 @@ export const executeTransaction = async ( email: recipient.email, senderEmail: sender.email, amount, - memo, overviewURL: CONFIG.EMAIL_LINK_OVERVIEW, }) if (transactionLink) { diff --git a/backend/src/mailer/sendTransactionLinkRedeemed.test.ts b/backend/src/mailer/sendTransactionLinkRedeemed.test.ts new file mode 100644 index 000000000..b56ff40a1 --- /dev/null +++ b/backend/src/mailer/sendTransactionLinkRedeemed.test.ts @@ -0,0 +1,44 @@ +import { sendEMail } from './sendEMail' +import Decimal from 'decimal.js-light' +import { sendTransactionLinkRedeemedEmail } from './sendTransactionLinkRedeemed' + +jest.mock('./sendEMail', () => { + return { + __esModule: true, + sendEMail: jest.fn(), + } +}) + +describe('sendTransactionLinkRedeemedEmail', () => { + beforeEach(async () => { + await sendTransactionLinkRedeemedEmail({ + email: 'bibi@bloxberg.de', + senderFirstName: 'Peter', + senderLastName: 'Lustig', + recipientFirstName: 'Bibi', + recipientLastName: 'Bloxberg', + senderEmail: 'peter@lustig.de', + amount: new Decimal(42.0), + memo: 'Vielen Dank dass Du dabei bist', + overviewURL: 'http://localhost/overview', + }) + }) + + it('calls sendEMail', () => { + expect(sendEMail).toBeCalledWith({ + to: `Bibi Bloxberg `, + subject: 'Gradido-Link wurde eingelöst', + text: + expect.stringContaining('Hallo Bibi Bloxberg') && + expect.stringContaining( + 'Peter Lustig (peter@lustig.de) hat soeben deinen Link eingelöst.', + ) && + expect.stringContaining('Betrag: 42,00 GDD,') && + expect.stringContaining('Memo: Vielen Dank dass Du dabei bist') && + expect.stringContaining( + 'Details zur Transaktion findest du in deinem Gradido-Konto: http://localhost/overview', + ) && + expect.stringContaining('Bitte antworte nicht auf diese E-Mail!'), + }) + }) +}) diff --git a/backend/src/mailer/sendTransactionLinkRedeemed.ts b/backend/src/mailer/sendTransactionLinkRedeemed.ts new file mode 100644 index 000000000..a78f3b3c9 --- /dev/null +++ b/backend/src/mailer/sendTransactionLinkRedeemed.ts @@ -0,0 +1,28 @@ +import { backendLogger as logger } from '@/server/logger' +import Decimal from 'decimal.js-light' +import { sendEMail } from './sendEMail' +import { transactionLinkRedeemed } from './text/transactionLinkRedeemed' + +export const sendTransactionLinkRedeemedEmail = (data: { + email: string + senderFirstName: string + senderLastName: string + recipientFirstName: string + recipientLastName: string + senderEmail: string + amount: Decimal + memo: string + overviewURL: string +}): Promise => { + logger.info( + `sendEmail(): to=${data.recipientFirstName} ${data.recipientLastName}, + <${data.email}>, + subject=${transactionLinkRedeemed.de.subject}, + text=${transactionLinkRedeemed.de.text(data)}`, + ) + return sendEMail({ + to: `${data.recipientFirstName} ${data.recipientLastName} <${data.email}>`, + subject: transactionLinkRedeemed.de.subject, + text: transactionLinkRedeemed.de.text(data), + }) +} diff --git a/backend/src/mailer/sendTransactionReceivedEmail.test.ts b/backend/src/mailer/sendTransactionReceivedEmail.test.ts index 75631cc7a..9f2ba9938 100644 --- a/backend/src/mailer/sendTransactionReceivedEmail.test.ts +++ b/backend/src/mailer/sendTransactionReceivedEmail.test.ts @@ -19,7 +19,6 @@ describe('sendTransactionReceivedEmail', () => { email: 'peter@lustig.de', senderEmail: 'bibi@bloxberg.de', amount: new Decimal(42.0), - memo: 'Vielen herzlichen Dank für den neuen Hexenbesen!', overviewURL: 'http://localhost/overview', }) }) @@ -33,7 +32,6 @@ describe('sendTransactionReceivedEmail', () => { expect.stringContaining('42,00 GDD') && expect.stringContaining('Bibi Bloxberg') && expect.stringContaining('(bibi@bloxberg.de)') && - expect.stringContaining('Vielen herzlichen Dank für den neuen Hexenbesen!') && expect.stringContaining('http://localhost/overview'), }) }) diff --git a/backend/src/mailer/sendTransactionReceivedEmail.ts b/backend/src/mailer/sendTransactionReceivedEmail.ts index 55f63e37e..5e981659c 100644 --- a/backend/src/mailer/sendTransactionReceivedEmail.ts +++ b/backend/src/mailer/sendTransactionReceivedEmail.ts @@ -1,7 +1,7 @@ import { backendLogger as logger } from '@/server/logger' import Decimal from 'decimal.js-light' import { sendEMail } from './sendEMail' -import { transactionLinkRedeemed, transactionReceived } from './text/transactionReceived' +import { transactionReceived } from './text/transactionReceived' export const sendTransactionReceivedEmail = (data: { senderFirstName: string @@ -11,7 +11,6 @@ export const sendTransactionReceivedEmail = (data: { email: string senderEmail: string amount: Decimal - memo: string overviewURL: string }): Promise => { logger.info( @@ -26,27 +25,3 @@ export const sendTransactionReceivedEmail = (data: { text: transactionReceived.de.text(data), }) } - -export const sendTransactionLinkRedeemedEmail = (data: { - email: string - senderFirstName: string - senderLastName: string - recipientFirstName: string - recipientLastName: string - senderEmail: string - amount: Decimal - memo: string - overviewURL: string -}): Promise => { - logger.info( - `sendEmail(): to=${data.recipientFirstName} ${data.recipientLastName}, - <${data.email}>, - subject=${transactionLinkRedeemed.de.subject}, - text=${transactionLinkRedeemed.de.text(data)}`, - ) - return sendEMail({ - to: `${data.recipientFirstName} ${data.recipientLastName} <${data.email}>`, - subject: transactionLinkRedeemed.de.subject, - text: transactionLinkRedeemed.de.text(data), - }) -} diff --git a/backend/src/mailer/text/transactionLinkRedeemed.ts b/backend/src/mailer/text/transactionLinkRedeemed.ts new file mode 100644 index 000000000..4d8e89cae --- /dev/null +++ b/backend/src/mailer/text/transactionLinkRedeemed.ts @@ -0,0 +1,33 @@ +import Decimal from 'decimal.js-light' + +export const transactionLinkRedeemed = { + de: { + subject: 'Gradido-Link wurde eingelöst', + text: (data: { + email: string + senderFirstName: string + senderLastName: string + recipientFirstName: string + recipientLastName: string + senderEmail: string + amount: Decimal + memo: string + overviewURL: string + }): string => + `Hallo ${data.recipientFirstName} ${data.recipientLastName} + + ${data.senderFirstName} ${data.senderLastName} (${ + data.senderEmail + }) hat soeben deinen Link eingelöst. + + Betrag: ${data.amount.toFixed(2).replace('.', ',')} GDD, + Memo: ${data.memo} + + Details zur Transaktion findest du in deinem Gradido-Konto: ${data.overviewURL} + + Bitte antworte nicht auf diese E-Mail! + + Mit freundlichen Grüßen, + dein Gradido-Team`, + }, +} diff --git a/backend/src/mailer/text/transactionReceived.ts b/backend/src/mailer/text/transactionReceived.ts index 4f10f41c8..ba61ae680 100644 --- a/backend/src/mailer/text/transactionReceived.ts +++ b/backend/src/mailer/text/transactionReceived.ts @@ -11,7 +11,6 @@ export const transactionReceived = { email: string senderEmail: string amount: Decimal - memo: string overviewURL: string }): string => `Hallo ${data.recipientFirstName} ${data.recipientLastName} @@ -28,35 +27,3 @@ Mit freundlichen Grüßen, dein Gradido-Team`, }, } - -export const transactionLinkRedeemed = { - de: { - subject: 'Gradido link eingelösst', - text: (data: { - email: string - senderFirstName: string - senderLastName: string - recipientFirstName: string - recipientLastName: string - senderEmail: string - amount: Decimal - memo: string - overviewURL: string - }): string => - `Hallo ${data.recipientFirstName} ${data.recipientLastName} - -${data.senderFirstName} ${data.senderLastName} (${ - data.senderEmail - }) hat soeben deinen Link eingelöst. - -Betrag: ${data.amount.toFixed(2).replace('.', ',')} GDD, -Memo: ${data.memo} - -Details zur Transaktion findest du in deinem Gradido-Konto: ${data.overviewURL} - -Bitte antworte nicht auf diese E-Mail! - -Mit freundlichen Grüßen, -dein Gradido-Team`, - }, -}