Skip to content
This repository was archived by the owner on Mar 20, 2023. It is now read-only.

Commit b21f090

Browse files
feat(api): create slice SendEmailMessage -> EmailMessageWasSent (#261) (#340)
* Configure application email account. * Sending email messages with NodeMailer.
1 parent eb40dd6 commit b21f090

19 files changed

+335
-0
lines changed

packages/api/.env.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,10 @@ PROCESS_ST_CHECKLIST_URL="https://app.process.st/workflows/CodersCamp-Test-Check
1414
EVENT_REPOSITORY="prisma"
1515
SUBSCRIPTION_QUEUE_MAX_RETRY_COUNT=10
1616
SUBSCRIPTION_QUEUE_WAITING_TIME_ON_RETRY_MS=100
17+
18+
EMAIL_SENDER_TYPE="just-log"
19+
APP_EMAIL_ADDRESS_FROM="rejestracja@coderscamp.edu.pl"
20+
NODEMAILER_PORT=
21+
NODEMAILER_HOST=""
22+
NODEMAILER_USER=""
23+
NODEMAILER_PASSWORD=""

packages/api/.env.test

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ COOKIE_SECRET="786Tgiu3564Rti8oh89YG6re6"
1212

1313
PROCESS_ST_CHECKLIST_URL="https://app.process.st/workflows/CodersCamp-Test-Checklist-kTrxJoZgP-9IabhRbohIrw/run-link"
1414
EVENT_REPOSITORY="prisma"
15+
16+
EMAIL_SENDER_TYPE="just-log"
17+
APP_EMAIL_ADDRESS_FROM="test-email-address@coderscamp.edu.pl"

packages/api/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"express": "4.17.1",
4040
"leaked-handles": "5.2.0",
4141
"lodash": "4.17.21",
42+
"nodemailer": "6.6.3",
4243
"passport": "0.4.1",
4344
"passport-github2": "0.1.12",
4445
"passport-jwt": "4.0.0",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { AbstractApplicationCommand } from '@/module/application-command-events';
2+
3+
import { SendEmailMessage } from './send-email-message.domain-command';
4+
5+
export class SendEmailMessageApplicationCommand extends AbstractApplicationCommand<SendEmailMessage> {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { EmailMessageId } from '@/shared/domain.types';
2+
3+
export type SendEmailMessage = {
4+
type: 'SendEmailMessage';
5+
data: {
6+
emailMessageId: EmailMessageId;
7+
to: string;
8+
subject: string;
9+
text: string;
10+
html: string;
11+
};
12+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { EmailMessageId } from '@/shared/domain.types';
2+
3+
export type EmailMessageWasSent = {
4+
type: 'EmailMessageWasSent';
5+
data: {
6+
emailMessageId: EmailMessageId;
7+
from: string;
8+
to: string;
9+
subject: string;
10+
text: string;
11+
html: string;
12+
};
13+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const EMAIL_SENDER = Symbol('EMAIL_SENDER');
2+
3+
export type EmailMessage = { from: string; to: string; subject: string; text: string; html: string };
4+
5+
export interface EmailSender {
6+
sendAnEmail(message: EmailMessage): Promise<void>;
7+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Inject } from '@nestjs/common';
2+
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
3+
4+
import { SendEmailMessageApplicationCommand } from '@/commands/send-email-message.application-command';
5+
import { env } from '@/shared/env';
6+
import { EMAIL_SENDER, EmailSender } from '@/write/email-sender/application/email-sender';
7+
import { EmailMessageDomainEvent } from '@/write/email-sender/domain/events';
8+
import { sendEmailMessage } from '@/write/email-sender/domain/sendEmailMessage';
9+
import { APPLICATION_SERVICE, ApplicationService } from '@/write/shared/application/application-service';
10+
import { EventStreamName } from '@/write/shared/application/event-stream-name.value-object';
11+
12+
@CommandHandler(SendEmailMessageApplicationCommand)
13+
export class SendEmailMessageCommandHandler implements ICommandHandler<SendEmailMessageApplicationCommand> {
14+
constructor(
15+
@Inject(APPLICATION_SERVICE)
16+
private readonly applicationService: ApplicationService,
17+
18+
@Inject(EMAIL_SENDER)
19+
private readonly emailSender: EmailSender,
20+
) {}
21+
22+
async execute(command: SendEmailMessageApplicationCommand): Promise<void> {
23+
const appEmailAddress = env.APP_EMAIL_ADDRESS_FROM;
24+
const { emailMessageId, to, subject, text, html } = command.data;
25+
const eventStream = EventStreamName.from('EmailMessage', `EmailMessage_${emailMessageId}`);
26+
27+
await this.emailSender.sendAnEmail({
28+
from: appEmailAddress,
29+
to,
30+
subject,
31+
text,
32+
html,
33+
});
34+
35+
await this.applicationService.execute<EmailMessageDomainEvent>(
36+
eventStream,
37+
{ causationId: command.id, correlationId: command.metadata.correlationId },
38+
(pastEvents) => sendEmailMessage(pastEvents, command, appEmailAddress),
39+
);
40+
}
41+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { EmailMessageWasSent } from '@/events/email-message-was-sent.domain-event';
2+
3+
export type EmailMessageDomainEvent = EmailMessageWasSent;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { SendEmailMessage } from '@/commands/send-email-message.domain-command';
2+
import { EmailMessageDomainEvent } from '@/write/email-sender/domain/events';
3+
import { sendEmailMessage } from '@/write/email-sender/domain/sendEmailMessage';
4+
5+
describe('sending email message', () => {
6+
it('create event EmailMessageWasSent when sendEmailMessage is invoked', () => {
7+
// Given
8+
const pastEvents: EmailMessageDomainEvent[] = [];
9+
10+
const emailData = {
11+
emailMessageId: '577e13dd-20ad-4d95-bdd9-d544c73ce358',
12+
to: 'test@test.com',
13+
subject: 'Test email',
14+
text: 'Welcome on board!',
15+
html: 'Welcome on board! HTML',
16+
};
17+
const appEmail = 'coderscamp@gmail.com';
18+
19+
// When
20+
const command: SendEmailMessage = {
21+
type: 'SendEmailMessage',
22+
data: emailData,
23+
};
24+
const events = sendEmailMessage(pastEvents, command, appEmail);
25+
26+
// Then
27+
expect(events).toStrictEqual([
28+
{
29+
type: 'EmailMessageWasSent',
30+
data: { ...emailData, from: appEmail },
31+
},
32+
]);
33+
});
34+
});

0 commit comments

Comments
 (0)