Skip to content

Commit

Permalink
Email send task (#665)
Browse files Browse the repository at this point in the history
* add findAll and findOne for admin only usage

* add delete file endpoint, findOne now accessible by organizer and fix fileUpload

* re-write isAdminFlag

* add send email functionality

when new campaign application is added the organizer will receive an email

* fix isAdmin import

* edit email

* fix person type in campaign application

* add email template for admin for created campaign-application

* fix sendEmailsOnCreatedCampaignApplication method and failing tests

* add adminEmail to .env and add promise.all to sendEmailsOnCreatedCampaignApplication method

* fix conflict

* fix: TS errors

* fix test

---------

Co-authored-by: Aleksandar Petkov <[email protected]>
Co-authored-by: Aleksandar <[email protected]>
  • Loading branch information
3 people authored Sep 27, 2024
1 parent 71bbdf5 commit 1bcb946
Show file tree
Hide file tree
Showing 11 changed files with 290 additions and 19 deletions.
4 changes: 4 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,7 @@ CAMPAIGN_ADMIN_MAIL=responsible for campaign management
## Cache ##
##############
CACHE_TTL=30000

## AdminEmail ##
##############
CAMPAIGN_COORDINATOR_EMAIL=[email protected]
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,7 @@ CAMPAIGN_ADMIN_MAIL=responsible for campaign management
## Cache ##
##############
CACHE_TTL=30000

## AdminEmail ##
##############
CAMPAIGN_COORDINATOR_EMAIL=[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"subject": "Създадена нова кампания"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<mjml>
<mj-body background-color="#ffffff">
<mj-section background-color="#009FE3" padding-bottom="0px" padding-top="0">
<mj-column vertical-align="top" width="100%">
<mj-text
align="left"
color="#ffffff"
font-size="35px"
font-weight="bold"
font-family="open Sans Helvetica, Arial, sans-serif"
padding-left="25px"
padding-right="25px"
padding-bottom="30px"
padding-top="50px">
Успешно създадена кампания от {{firstName}}
</mj-text>
</mj-column>
</mj-section>
<mj-section background-color="#009fe3" padding-bottom="20px" padding-top="20px">
<mj-column vertical-align="middle" width="100%">
<mj-text
align="left"
color="#ffffff"
font-size="15px"
font-family="open Sans Helvetica, Arial, sans-serif"
padding-left="25px"
padding-right="25px">
Организатор {{firstName}} с имейл {{email}} създаде нова кампания
<a style="color: #feeb35" href="{{campaignApplicationLink}}" target="_blank"
>{{campaignApplicationName}}</a
>!<br /><br />
</mj-text>
<mj-text
align="left"
color="#ffffff"
font-size="15px"
font-family="open Sans Helvetica, Arial, sans-serif"
padding-left="25px"
padding-right="25px">
Поздрави, <br />
Екипът на Подкрепи.бг
</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"subject": "Създадена нова кампания"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<mjml>
<mj-body background-color="#ffffff">
<mj-section background-color="#009FE3" padding-bottom="0px" padding-top="0">
<mj-column vertical-align="top" width="100%">
<mj-text
align="left"
color="#ffffff"
font-size="35px"
font-weight="bold"
font-family="open Sans Helvetica, Arial, sans-serif"
padding-left="25px"
padding-right="25px"
padding-bottom="30px"
padding-top="50px">
Успешно създадохте кампания в Подкрепи.бг
</mj-text>
</mj-column>
</mj-section>
<mj-section background-color="#009fe3" padding-bottom="20px" padding-top="20px">
<mj-column vertical-align="middle" width="100%">
<mj-text
align="left"
color="#ffffff"
font-size="22px"
font-family="open Sans Helvetica, Arial, sans-serif"
padding-left="25px"
padding-right="25px">
<span style="color: #feeb35"> Здравейте {{firstName}}, </span>
<br /><br />
</mj-text>
<mj-text
align="left"
color="#ffffff"
font-size="15px"
font-family="open Sans Helvetica, Arial, sans-serif"
padding-left="25px"
padding-right="25px">
Вашата кампания е създадена успешно! Вижте я
<a style="color: #feeb35" href="{{campaignApplicationLink}}" target="_blank">ТУК</a
>!<br /><br />

Пожелаваме успешно набиране на средствата!
</mj-text>
<mj-text
align="left"
color="#ffffff"
font-size="15px"
font-family="open Sans Helvetica, Arial, sans-serif"
padding-left="25px"
padding-right="25px">
Поздрави, <br />
Екипът на Подкрепи.бг
</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CampaignApplicationState } from '@prisma/client'
import { CampaignApplicationState, CampaignTypeCategory } from '@prisma/client'

export const mockNewCampaignApplication = {
campaignName: 'Test Campaign',
Expand Down Expand Up @@ -38,12 +38,10 @@ export const mockSingleCampaignApplication = {
otherFinanceSources: 'test otherFinanceSources1',
otherNotes: 'test otherNotes1',
state: CampaignApplicationState.review,
campaignTypeId: 'ffdbcc41-85ec-0000-9e59-0662f3b433af',
category: CampaignTypeCategory.medical,
ticketURL: 'testsodifhso1',
archived: false,
documents: [{ id: 'fileId' }],
campaignEnd: 'funds',
campaignEndDate: undefined,
}

export const mockCampaigns = [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { EmailService } from './../email/email.service'
import { Module } from '@nestjs/common'
import { CampaignApplicationService } from './campaign-application.service'
import { CampaignApplicationController } from './campaign-application.controller'
import { PrismaModule } from '../prisma/prisma.module'
import { PersonModule } from '../person/person.module'
import { OrganizerModule } from '../organizer/organizer.module'
import { S3Service } from '../s3/s3.service'
import { TemplateService } from '../email/template.service'
@Module({
imports: [PrismaModule, PersonModule, OrganizerModule],
controllers: [CampaignApplicationController],
providers: [CampaignApplicationService, S3Service],
providers: [CampaignApplicationService, S3Service, EmailService, TemplateService],
})
export class CampaignApplicationModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,18 @@ import {
mockCampaignApplicationFilesFn,
} from './__mocks__/campaing-application-file-mocks'
import { CampaignApplicationService } from './campaign-application.service'
import {
CreateCampaignApplicationAdminEmailDto,
CreateCampaignApplicationOrganizerEmailDto,
} from '../email/template.interface'
import { CreateCampaignApplicationDto } from './dto/create-campaign-application.dto'
import { EmailService } from '../email/email.service'
import { ConfigService } from 'aws-sdk'
import { ConfigModule } from '@nestjs/config'

describe('CampaignApplicationService', () => {
let service: CampaignApplicationService
let configService: ConfigService

Check warning on line 30 in apps/api/src/campaign-application/campaign-application.service.spec.ts

View workflow job for this annotation

GitHub Actions / Run API tests

'configService' is defined but never used

const mockPerson = {
...personMock,
Expand All @@ -40,13 +48,23 @@ describe('CampaignApplicationService', () => {
deleteObject: jest.fn(),
}

const mockEmailService = {
sendFromTemplate: jest.fn(),
}

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule.forFeature(async () => ({
APP_URL: process.env.APP_URL,
})),
],
providers: [
CampaignApplicationService,
MockPrismaService,
{ provide: OrganizerService, useValue: mockOrganizerService },
{ provide: S3Service, useValue: mockS3Service },
{ provide: EmailService, useValue: mockEmailService },
],
}).compile()

Expand Down Expand Up @@ -99,14 +117,14 @@ describe('CampaignApplicationService', () => {
)
})

it('should add a new campaign-application to db if all agreements are true', async () => {
it('should add a new campaign-application to db a if all agreements are true', async () => {
const dto: CreateCampaignApplicationDto = {
...mockNewCampaignApplication,
acceptTermsAndConditions: true,
transparencyTermsAccepted: true,
personalInformationProcessingAccepted: true,
toEntity: new CreateCampaignApplicationDto().toEntity,
campaignEndDate: '2024-01-01'
campaignEndDate: '2024-01-01',
}

const mockOrganizerId = 'mockOrganizerId'
Expand All @@ -119,6 +137,10 @@ describe('CampaignApplicationService', () => {
.spyOn(prismaMock.campaignApplication, 'create')
.mockResolvedValue(mockCreatedCampaignApplication)

const sendEmailsOnCreatedCampaignApplicationSpy = jest
.spyOn(service, 'sendEmailsOnCreatedCampaignApplication')
.mockResolvedValue(undefined)

const result = await service.create(dto, mockPerson)

expect(result).toEqual(mockCreatedCampaignApplication)
Expand Down Expand Up @@ -149,8 +171,63 @@ describe('CampaignApplicationService', () => {
},
})

expect(sendEmailsOnCreatedCampaignApplicationSpy).toHaveBeenCalledWith(
mockCreatedCampaignApplication.campaignName,
mockCreatedCampaignApplication.id,
mockPerson,
)

expect(mockOrganizerService.create).toHaveBeenCalledTimes(1)
expect(prismaMock.campaignApplication.create).toHaveBeenCalledTimes(1)
expect(sendEmailsOnCreatedCampaignApplicationSpy).toHaveBeenCalledTimes(1)
})
})

describe('sendEmailsOnCreatedCampaignApplication', () => {
it('should send emails to both the organizer and the admin', async () => {
const mockAdminEmail = '[email protected]'
const userEmail = { to: [mockPerson.email] }
const adminEmail = { to: [mockAdminEmail] }

const emailAdminData = {
campaignApplicationName: mockSingleCampaignApplication.campaignName,
campaignApplicationLink: `${process.env.APP_URL}/admin/campaigns/${mockSingleCampaignApplication.id}`,
email: mockPerson.email as string,
firstName: mockPerson.firstName,
}

const emailOrganizerData = {
campaignApplicationName: mockSingleCampaignApplication.campaignName,
campaignApplicationLink: `${process.env.APP_URL}/campaign/applications/${mockSingleCampaignApplication.id}`,
email: mockPerson.email as string,
firstName: mockPerson.firstName,
}

const mailAdmin = new CreateCampaignApplicationAdminEmailDto(emailAdminData)
const mailOrganizer = new CreateCampaignApplicationOrganizerEmailDto(emailOrganizerData)

mockEmailService.sendFromTemplate.mockResolvedValueOnce(undefined)

await service.sendEmailsOnCreatedCampaignApplication(
mockSingleCampaignApplication.campaignName,
mockSingleCampaignApplication.id,
mockPerson,
)

expect(mockEmailService.sendFromTemplate).toHaveBeenNthCalledWith(
1,
mailOrganizer,
userEmail,
{
bypassUnsubscribeManagement: { enable: true },
},
)

expect(mockEmailService.sendFromTemplate).toHaveBeenNthCalledWith(2, mailAdmin, adminEmail, {
bypassUnsubscribeManagement: { enable: true },
})

expect(mockEmailService.sendFromTemplate).toHaveBeenCalledTimes(2)
})
})

Expand Down Expand Up @@ -284,7 +361,7 @@ describe('CampaignApplicationService', () => {
where: { id: '1' },
data: {
...mockUpdateCampaignApplication,
campaignEndDate: new Date('2024-09-09T00:00:00.000Z')
campaignEndDate: new Date('2024-09-09T00:00:00.000Z'),
},
})
})
Expand Down Expand Up @@ -340,7 +417,7 @@ describe('CampaignApplicationService', () => {
where: { id: '1' },
data: {
...mockUpdateCampaignApplication,
campaignEndDate: new Date('2024-09-09T00:00:00.000Z')
campaignEndDate: new Date('2024-09-09T00:00:00.000Z'),
},
})
})
Expand Down
Loading

0 comments on commit 1bcb946

Please sign in to comment.