rewrite sendEmailVariants.test.ts to use EmailBuilder, add email for from moderator changed memo

This commit is contained in:
einhornimmond 2023-11-13 13:14:22 +01:00
parent 402be3a0cd
commit e60ab28f77
12 changed files with 170 additions and 159 deletions

View File

@ -8,7 +8,7 @@
"license": "Apache-2.0",
"private": false,
"scripts": {
"build": "tsc --build && mkdir -p build/src/emails/templates/ && cp -r src/emails/templates/* build/src/emails/templates/ && mkdir -p build/src/locales/ && cp -r src/locales/*.json build/src/locales/",
"build": "tsc --build && mkdirp build/src/emails/templates/ && ncp src/emails/templates build/src/emails/templates && mkdirp build/src/locales/ && ncp src/locales build/src/locales",
"clean": "tsc --build --clean",
"start": "cross-env TZ=UTC TS_NODE_BASEURL=./build node -r tsconfig-paths/register build/src/index.js",
"dev": "cross-env TZ=UTC nodemon -w src --ext ts,pug,json,css --exec ts-node -r tsconfig-paths/register src/index.ts",
@ -80,7 +80,9 @@
"ts-jest": "^27.0.5",
"ts-node": "^10.0.0",
"tsconfig-paths": "^3.14.0",
"typescript": "^4.3.4"
"typescript": "^4.3.4",
"mkdirp": "^3.0.1",
"ncp": "^2.0.0"
},
"nodemonConfig": {
"ignore": [

View File

@ -2,43 +2,17 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { Connection } from '@dbTools/typeorm'
import { ApolloServerTestClient } from 'apollo-server-testing'
import { Decimal } from 'decimal.js-light'
import { testEnvironment } from '@test/helpers'
import { logger, i18n as localization } from '@test/testSetup'
import { CONFIG } from '@/config'
import { sendEmailTranslated } from './sendEmailTranslated'
import {
sendAddedContributionMessageEmail,
sendAccountActivationEmail,
sendAccountMultiRegistrationEmail,
sendContributionConfirmedEmail,
sendContributionDeniedEmail,
sendContributionDeletedEmail,
sendResetPasswordEmail,
sendTransactionLinkRedeemedEmail,
sendTransactionReceivedEmail,
} from './sendEmailVariants'
let con: Connection
let testEnv: {
mutate: ApolloServerTestClient['mutate']
query: ApolloServerTestClient['query']
con: Connection
}
beforeAll(async () => {
testEnv = await testEnvironment(logger, localization)
con = testEnv.con
})
afterAll(async () => {
await con.close()
})
import { User } from '@entity/User'
import { UserContact } from '@entity/UserContact'
import { Contribution } from '@entity/Contribution'
import { EmailBuilder, EmailType } from './Email.builder'
jest.mock('./sendEmailTranslated', () => {
const originalModule = jest.requireActual('./sendEmailTranslated')
@ -51,18 +25,34 @@ jest.mock('./sendEmailTranslated', () => {
describe('sendEmailVariants', () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let result: any
const recipientUser = User.create()
recipientUser.firstName = 'Peter'
recipientUser.lastName = 'Lustig'
recipientUser.language = 'en'
const recipientUserContact = UserContact.create()
recipientUserContact.email = 'peter@lustig.de'
recipientUser.emailContact = recipientUserContact
const senderUser = User.create()
senderUser.firstName = 'Bibi'
senderUser.lastName = 'Bloxberg'
const senderUserContact = UserContact.create()
senderUserContact.email = 'bibi@bloxberg.de'
const contribution = Contribution.create()
contribution.memo = 'My contribution.'
contribution.amount = new Decimal(23.54)
const emailBuilder = new EmailBuilder()
describe('sendAddedContributionMessageEmail', () => {
beforeAll(async () => {
result = await sendAddedContributionMessageEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
contributionMemo: 'My contribution.',
})
result = await emailBuilder
.setSender(senderUser)
.setRecipient(recipientUser)
.setContribution(contribution)
.setType(EmailType.ADDED_CONTRIBUTION_MESSAGE)
.sendEmail()
})
describe('calls "sendEmailTranslated"', () => {
@ -114,14 +104,13 @@ describe('sendEmailVariants', () => {
describe('sendAccountActivationEmail', () => {
beforeAll(async () => {
result = await sendAccountActivationEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
activationLink: 'http://localhost/checkEmail/6627633878930542284',
timeDurationObject: { hours: 23, minutes: 30 },
})
result = await emailBuilder
.setRecipient(recipientUser)
.setSender(senderUser)
.setActivationLink('http://localhost/checkEmail/6627633878930542284')
.setTimeDurationObject({ hours: 23, minutes: 30 })
.setType(EmailType.ACCOUNT_ACTIVATION)
.sendEmail()
})
describe('calls "sendEmailTranslated"', () => {
@ -172,12 +161,10 @@ describe('sendEmailVariants', () => {
describe('sendAccountMultiRegistrationEmail', () => {
beforeAll(async () => {
result = await sendAccountMultiRegistrationEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
})
result = await emailBuilder
.setRecipient(recipientUser)
.setType(EmailType.ACCOUNT_MULTI_REGISTRATION)
.sendEmail()
})
describe('calls "sendEmailTranslated"', () => {
@ -226,16 +213,12 @@ describe('sendEmailVariants', () => {
describe('sendContributionConfirmedEmail', () => {
beforeAll(async () => {
result = await sendContributionConfirmedEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
contributionMemo: 'My contribution.',
contributionAmount: new Decimal(23.54),
})
result = await emailBuilder
.setRecipient(recipientUser)
.setSender(senderUser)
.setContribution(contribution)
.setType(EmailType.CONTRIBUTION_CONFIRMED)
.sendEmail()
})
describe('calls "sendEmailTranslated"', () => {
@ -288,15 +271,12 @@ describe('sendEmailVariants', () => {
describe('sendContributionDeniedEmail', () => {
beforeAll(async () => {
result = await sendContributionDeniedEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
contributionMemo: 'My contribution.',
})
result = await emailBuilder
.setRecipient(recipientUser)
.setSender(senderUser)
.setContribution(contribution)
.setType(EmailType.CONTRIBUTION_DENIED)
.sendEmail()
})
describe('calls "sendEmailTranslated"', () => {
@ -348,15 +328,12 @@ describe('sendEmailVariants', () => {
describe('sendContributionDeletedEmail', () => {
beforeAll(async () => {
result = await sendContributionDeletedEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
contributionMemo: 'My contribution.',
})
result = await emailBuilder
.setRecipient(recipientUser)
.setSender(senderUser)
.setContribution(contribution)
.setType(EmailType.CONTRIBUTION_DELETED)
.sendEmail()
})
describe('calls "sendEmailTranslated"', () => {
@ -408,14 +385,12 @@ describe('sendEmailVariants', () => {
describe('sendResetPasswordEmail', () => {
beforeAll(async () => {
result = await sendResetPasswordEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
resetLink: 'http://localhost/reset-password/3762660021544901417',
timeDurationObject: { hours: 23, minutes: 30 },
})
result = await emailBuilder
.setRecipient(recipientUser)
.setResetLink('http://localhost/reset-password/3762660021544901417')
.setTimeDurationObject({ hours: 23, minutes: 30 })
.setType(EmailType.RESET_PASSWORD)
.sendEmail()
})
describe('calls "sendEmailTranslated"', () => {
@ -466,17 +441,12 @@ describe('sendEmailVariants', () => {
describe('sendTransactionLinkRedeemedEmail', () => {
beforeAll(async () => {
result = await sendTransactionLinkRedeemedEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
senderEmail: 'bibi@bloxberg.de',
transactionMemo: 'You deserve it! 🙏🏼',
transactionAmount: new Decimal(17.65),
})
result = await emailBuilder
.setRecipient(recipientUser)
.setSender(senderUser)
.setTransaction(new Decimal(17.65), 'You deserve it! 🙏🏼')
.setType(EmailType.TRANSACTION_LINK_REDEEMED)
.sendEmail()
})
describe('calls "sendEmailTranslated"', () => {
@ -530,16 +500,12 @@ describe('sendEmailVariants', () => {
describe('sendTransactionReceivedEmail', () => {
beforeAll(async () => {
result = await sendTransactionReceivedEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
senderEmail: 'bibi@bloxberg.de',
transactionAmount: new Decimal(37.4),
})
result = await emailBuilder
.setRecipient(recipientUser)
.setSender(senderUser)
.setTransactionAmount(new Decimal(37.4))
.setType(EmailType.TRANSACTION_RECEIVED)
.sendEmail()
})
describe('calls "sendEmailTranslated"', () => {

View File

@ -1,5 +1,4 @@
import { Contribution } from '@entity/Contribution'
import { Transaction } from '@entity/Transaction'
import { User } from '@entity/User'
import { CONFIG } from '@/config'
@ -8,6 +7,7 @@ import { TimeDuration } from '@/util/time'
import { decimalSeparatorByLanguage, resetInterface } from '@/util/utilities'
import { sendEmailTranslated } from './sendEmailTranslated'
import { Decimal } from 'decimal.js-light'
export interface EmailLocals {
firstName: string
@ -27,6 +27,7 @@ export interface EmailLocals {
resetLink?: string
transactionMemo?: string
transactionAmount?: string
contributionMemoUpdated?: string
[key: string]: string | TimeDuration | undefined
}
@ -109,7 +110,12 @@ export class EmailBuilder {
this.checkIfFieldsSet(['senderFirstName', 'senderLastName', 'contributionMemo'])
break
case EmailType.CONTRIBUTION_CHANGED_BY_MODERATOR:
// this.checkIfFieldsSet([''])
this.checkIfFieldsSet([
'contributionMemoUpdated',
'senderFirstName',
'senderLastName',
'contributionMemo',
])
break
case EmailType.RESET_PASSWORD:
this.checkIfFieldsSet(['resetLink', 'timeDurationObject', 'resendLink'])
@ -193,15 +199,27 @@ export class EmailBuilder {
return this
}
public setTransaction(transaction: Transaction): this {
this.locals.transactionMemo = transaction.memo
public setUpdatedContributionMemo(updatedMemo: string): this {
this.locals.contributionMemoUpdated = updatedMemo
return this
}
public setTransaction(amount: Decimal, memo: string): this {
this.setTransactionMemo(memo)
this.setTransactionAmount(amount)
return this
}
public setTransactionAmount(amount: Decimal): this {
if (!this.locals.locale || this.locals.locale === '') {
throw new LogError('missing locale please call setRecipient before')
}
this.locals.transactionAmount = decimalSeparatorByLanguage(
transaction.amount,
this.locals.locale,
)
this.locals.transactionAmount = decimalSeparatorByLanguage(amount, this.locals.locale)
return this
}
public setTransactionMemo(memo: string): this {
this.locals.transactionMemo = memo
return this
}

View File

@ -0,0 +1,10 @@
extend ../layout.pug
block content
h2= t('emails.contributionChangedByModerator.title')
.text-block
include ../includes/salutation.pug
p= t('emails.contributionChangedByModerator.commonGoodContributionConfirmed', { contributionMemo, senderFirstName, senderLastName, contributionMemoUpdated })
.content
include ../includes/contributionDetailsCTA.pug
include ../includes/doNotReply.pug

View File

@ -0,0 +1 @@
= t('emails.contributionChangedByModerator.subject')

View File

@ -22,11 +22,7 @@ import { OpenCreation } from '@model/OpenCreation'
import { UnconfirmedContribution } from '@model/UnconfirmedContribution'
import { RIGHTS } from '@/auth/RIGHTS'
import {
sendContributionConfirmedEmail,
sendContributionDeletedEmail,
sendContributionDeniedEmail,
} from '@/emails/sendEmailVariants'
import { EmailBuilder, EmailType } from '@/emails/Email.builder'
import {
EVENT_CONTRIBUTION_CREATE,
EVENT_CONTRIBUTION_DELETE,
@ -50,7 +46,6 @@ import { getUserCreation, validateContribution, getOpenCreations } from './util/
import { findContributions } from './util/findContributions'
import { getLastTransaction } from './util/getLastTransaction'
import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector'
import { EmailBuilder, EmailType } from '@/emails/Email.builder'
@Resolver()
export class ContributionResolver {

View File

@ -22,6 +22,7 @@ import { User } from '@model/User'
import { RIGHTS } from '@/auth/RIGHTS'
import { CONFIG } from '@/config'
import { EmailBuilder, EmailType } from '@/emails/Email.builder'
import {
sendTransactionLinkRedeemedEmail,
sendTransactionReceivedEmail,
@ -180,28 +181,21 @@ export const executeTransaction = async (
} finally {
await queryRunner.release()
}
void sendTransactionReceivedEmail({
firstName: recipient.firstName,
lastName: recipient.lastName,
email: recipient.emailContact.email,
language: recipient.language,
senderFirstName: sender.firstName,
senderLastName: sender.lastName,
senderEmail: sender.emailContact.email,
transactionAmount: amount,
})
const emailBuilder = new EmailBuilder()
void emailBuilder
.setRecipient(recipient)
.setSender(sender)
.setTransactionAmount(amount)
.setType(EmailType.TRANSACTION_RECEIVED)
.sendEmail()
if (transactionLink) {
void sendTransactionLinkRedeemedEmail({
firstName: sender.firstName,
lastName: sender.lastName,
email: sender.emailContact.email,
language: sender.language,
senderFirstName: recipient.firstName,
senderLastName: recipient.lastName,
senderEmail: recipient.emailContact.email,
transactionAmount: amount,
transactionMemo: memo,
})
void emailBuilder
.setRecipient(sender)
.setSender(recipient)
.setTransaction(amount, memo)
.setType(EmailType.TRANSACTION_LINK_REDEEMED)
.sendEmail()
}
logger.info(`finished executeTransaction successfully`)
} finally {

View File

@ -387,15 +387,13 @@ export class UserResolver {
})
logger.info('optInCode for', email, user.emailContact)
void sendResetPasswordEmail({
firstName: user.firstName,
lastName: user.lastName,
email,
language: user.language,
resetLink: activationLink(user.emailContact.emailVerificationCode),
timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME),
})
const emailBuilder = new EmailBuilder()
void emailBuilder
.setRecipient(user)
.setResetLink(activationLink(user.emailContact.emailVerificationCode))
.setTimeDurationObject(getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME))
.setType(EmailType.RESET_PASSWORD)
.sendEmail()
logger.info(`forgotPassword(${email}) successful...`)
await EVENT_EMAIL_FORGOT_PASSWORD(user)

View File

@ -26,6 +26,11 @@
"contribution": {
"toSeeContributionsAndMessages": "Um deine Gemeinwohl-Beiträge und dazugehörige Nachrichten zu sehen, gehe in deinem Gradido-Konto ins Menü „Schöpfen“ auf den Tab „Meine Beiträge“."
},
"contributionChangedByModerator": {
"commonGoodContributionConfirmed": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde soeben von {senderFirstName} {senderLastName} geändert und lautet jetzt „{contributionMemoUpdated}“",
"subject": "Dein Gemeinwohl-Beitrag wurde geändert",
"title": "Dein Gemeinwohl-Beitrag wurde geändert"
},
"contributionConfirmed": {
"commonGoodContributionConfirmed": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde soeben von {senderFirstName} {senderLastName} bestätigt. Es wurden deinem Gradido-Konto {amountGDD} GDD gutgeschrieben.",
"subject": "Dein Gemeinwohl-Beitrag wurde bestätigt",

View File

@ -3679,7 +3679,7 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0:
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
"gradido-database@file:../database":
version "1.22.0"
version "2.0.0"
dependencies:
"@types/uuid" "^8.3.4"
cross-env "^7.0.3"
@ -5280,6 +5280,11 @@ mkdirp@^2.1.3:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19"
integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==
mkdirp@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50"
integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==
moo@^0.5.0, moo@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4"
@ -5371,6 +5376,11 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
ncp@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3"
integrity sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==
nearley@^2.20.1:
version "2.20.1"
resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.20.1.tgz#246cd33eff0d012faf197ff6774d7ac78acdd474"

View File

@ -8,7 +8,7 @@
"license": "Apache-2.0",
"private": false,
"scripts": {
"build": "mkdir -p build/src/config/ && cp src/config/*.txt build/src/config/ && tsc --build",
"build": "mkdirp build/src/config/ && ncp src/config build/src/config && tsc --build",
"clean": "tsc --build --clean",
"up": "cross-env TZ=UTC node build/src/index.js up",
"down": "cross-env TZ=UTC node build/src/index.js down",
@ -35,7 +35,9 @@
"eslint-plugin-security": "^1.7.1",
"prettier": "^2.8.7",
"ts-node": "^10.2.1",
"typescript": "^4.3.5"
"typescript": "^4.3.5",
"mkdirp": "^3.0.1",
"ncp": "^2.0.0"
},
"dependencies": {
"@types/uuid": "^8.3.4",

View File

@ -1718,6 +1718,11 @@ mkdirp@^2.1.3:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19"
integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==
mkdirp@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50"
integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
@ -1778,6 +1783,11 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
ncp@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3"
integrity sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==
npm-run-path@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"