Skip to content

Commit

Permalink
Merge branch 'master' into yosifov/extract-transaction-amount-from-id
Browse files Browse the repository at this point in the history
  • Loading branch information
igoychev authored Aug 25, 2023
2 parents 3ae1e08 + 87ddc2a commit ad4b1f9
Show file tree
Hide file tree
Showing 127 changed files with 3,820 additions and 198 deletions.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ SENDGRID_API_KEY=sendgrid-key
SENDGRID_SENDER_EMAIL=[email protected]
SENDGRID_INTERNAL_EMAIL=[email protected]
SENDGRID_CONTACTS_URL=/v3/marketing/contacts
MARKETING_LIST_ID=6add1a52-f74e-4c14-af56-ec7e1d2318f0
SENDGRID_SENDER_ID=
## if marketing notifications should be active --> true/false -> defaults to false
SEND_MARKETING_NOTIFICATIONS=

## Stripe ##
############
Expand Down
2 changes: 2 additions & 0 deletions apps/api/src/account/account.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import KeycloakConnect from 'keycloak-connect'
import { mock, mockDeep } from 'jest-mock-extended'
import { JwtService } from '@nestjs/jwt'
import { EmailService } from '../email/email.service'
import { MarketingNotificationsModule } from '../notifications/notifications.module'

describe('AccountController', () => {
let controller: AccountController
Expand All @@ -28,6 +29,7 @@ describe('AccountController', () => {

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [MarketingNotificationsModule],
controllers: [AccountController],
providers: [
AccountService,
Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/account/account.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Person } from '../domain/generated/person/entities'
import { UpdatePersonDto } from '../person/dto/update-person.dto'
import { PersonService } from '../person/person.service'
import { AccountService } from './account.service'
import { ApiTags } from '@nestjs/swagger';
import { ApiTags } from '@nestjs/swagger'

@Controller('account')
@ApiTags('account')
Expand Down
3 changes: 3 additions & 0 deletions apps/api/src/account/account.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ import { PersonService } from '../person/person.service'
import { MockPrismaService } from '../prisma/prisma-client.mock'
import { AccountService } from './account.service'

import { MarketingNotificationsModule } from '../notifications/notifications.module'

describe('AccountService', () => {
let service: AccountService

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [MarketingNotificationsModule],
providers: [
AccountService,
PersonService,
Expand Down
4 changes: 3 additions & 1 deletion apps/api/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ import { NotificationModule } from '../sockets/notifications/notification.module
import { ScheduleModule } from '@nestjs/schedule'
import { TasksModule } from '../tasks/tasks.module'
import { BankTransactionsModule } from '../bank-transactions/bank-transactions.module'
import { CacheModule, CacheInterceptor } from '@nestjs/cache-manager'
import { CacheModule } from '@nestjs/cache-manager'
import { CampaignNewsModule } from '../campaign-news/campaign-news.module'
import { CampaignNewsFileModule } from '../campaign-news-file/campaign-news-file.module'
import { MarketingNotificationsModule } from '../notifications/notifications.module'

@Module({
imports: [
Expand Down Expand Up @@ -116,6 +117,7 @@ import { CampaignNewsFileModule } from '../campaign-news-file/campaign-news-file
}),
CampaignNewsModule,
CampaignNewsFileModule,
MarketingNotificationsModule,
],
controllers: [AppController],
providers: [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"subject": "Потвърдете абонирането си"
}
71 changes: 71 additions & 0 deletions apps/api/src/assets/templates/confirm-notifications-consent.mjml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<mjml>
<mj-body background-color="#ffffff" font-size="13px" width="90%">
<mj-section
background-color="#009FE3"
vertical-align="top"
padding-bottom="0px"
padding-top="0">
<mj-column vertical-align="top" width="100%">
<mj-text
align="left"
color="#ffffff"
font-size="45px"
font-weight="bold"
font-family="open Sans Helvetica, Arial, sans-serif"
padding-left="25px"
padding-right="25px"
padding-bottom="15px"
padding-top="50px">
Потвърдете съгласието си за получаване на известия и новини от Podkrepi.bg
</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">
<br /><br />
</mj-text>
<mj-text
align="left"
color="#ffffff"
font-size="18px"
font-family="open Sans Helvetica, Arial, sans-serif"
padding-left="25px"
padding-right="25px">
Благодарим ти, че ставаш част от нещо смислено. Абонирайки се ще получаваш актуална
информация за всичко, което се случва в нашата платформа - събрани дарения, нови кампании,
интересни новини.
</mj-text>

<mj-button
background-color="#FFCB57"
font-family="Helvetica, Arial, sans-serif"
font-size="17px"
border-radius="30px"
color="#000000"
padding="15px 30px"
href="{{subscribeLink}}"
target="_blank">
Потвърждавам абонирането си
</mj-button>

<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>
2 changes: 2 additions & 0 deletions apps/api/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ProviderLoginController } from './provider-login.controller'
import { JwtModule, JwtService } from '@nestjs/jwt'
import { EmailService } from '../email/email.service'
import { TemplateService } from '../email/template.service'
import { MarketingNotificationsModule } from '../notifications/notifications.module'

@Module({
controllers: [LoginController, RegisterController, RefreshController, ProviderLoginController],
Expand All @@ -25,6 +26,7 @@ import { TemplateService } from '../email/template.service'
useExisting: KeycloakConfigService,
imports: [AppConfigModule],
}),
MarketingNotificationsModule,
],
exports: [AuthService],
})
Expand Down
126 changes: 121 additions & 5 deletions apps/api/src/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ import { ProviderDto } from './dto/provider.dto'
import { EmailService } from '../email/email.service'
import { JwtService } from '@nestjs/jwt'
import { TemplateService } from '../email/template.service'
import { PrismaService } from '../prisma/prisma.service'
import { SendGridNotificationsProvider } from '../notifications/providers/notifications.sendgrid.provider'
import { NotificationsProviderInterface } from '../notifications/providers/notifications.interface.providers'
import { MarketingNotificationsModule } from '../notifications/notifications.module'

jest.mock('@keycloak/keycloak-admin-client')

Expand All @@ -28,6 +30,7 @@ describe('AuthService', () => {
let config: ConfigService
let admin: KeycloakAdminClient
let keycloak: KeycloakConnect.Keycloak
let marketing: NotificationsProviderInterface<any>

Check warning on line 33 in apps/api/src/auth/auth.service.spec.ts

View workflow job for this annotation

GitHub Actions / Run API tests

Unexpected any. Specify a different type

const person: Person = {
id: 'e43348aa-be33-4c12-80bf-2adfbf8736cd',
Expand All @@ -50,6 +53,7 @@ describe('AuthService', () => {

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [MarketingNotificationsModule],
providers: [
AuthService,
{
Expand All @@ -58,6 +62,7 @@ describe('AuthService', () => {
get: jest.fn((key: string) => {
if (key === 'keycloak.clientId') return 'realm-a12345'
if (key === 'keycloak.secret') return 'a12345'
if (key === 'sendgrid.marketingListId') return 'list-id'
return null
}),
},
Expand Down Expand Up @@ -87,12 +92,27 @@ describe('AuthService', () => {
provide: TemplateService,
useValue: mockDeep<TemplateService>(),
},
{
provide: NotificationsProviderInterface,
useClass: SendGridNotificationsProvider,
},
],
}).compile()
})
.overrideProvider(ConfigService)
.useValue({
get: jest.fn((key: string) => {
if (key === 'keycloak.clientId') return 'realm-a12345'
if (key === 'keycloak.secret') return 'a12345'
if (key === 'sendgrid.marketingListId') return 'list-id'
return null
}),
})
.compile()

service = module.get<AuthService>(AuthService)
config = module.get<ConfigService>(ConfigService)
admin = module.get<KeycloakAdminClient>(KeycloakAdminClient)
marketing = module.get<NotificationsProviderInterface<any>>(NotificationsProviderInterface)

Check warning on line 115 in apps/api/src/auth/auth.service.spec.ts

View workflow job for this annotation

GitHub Actions / Run API tests

Unexpected any. Specify a different type
keycloak = module.get<KeycloakConnect.Keycloak>(KEYCLOAK_INSTANCE)
})

Expand Down Expand Up @@ -303,13 +323,20 @@ describe('AuthService', () => {
const password = 's3cret'
const firstName = 'John'
const lastName = 'Doe'
const newsletter = true

it('should call keycloak and prisma', async () => {
const keycloakId = 'u123'
const registerDto = plainToClass(RegisterDto, { email, password, firstName, lastName })
const registerDto = plainToClass(RegisterDto, {
email,
password,
firstName,
lastName,
newsletter,
})
jest.spyOn(marketing, 'addContactsToList').mockImplementation(async () => true)
const createUserSpy = jest.spyOn(service, 'createUser')
const adminSpy = jest.spyOn(admin.users, 'create').mockResolvedValue({ id: keycloakId })

const prismaSpy = jest.spyOn(prismaMock.person, 'upsert').mockResolvedValue(person)

expect(await service.createUser(registerDto)).toBe(person)
Expand Down Expand Up @@ -346,7 +373,7 @@ describe('AuthService', () => {

// Check db creation
expect(prismaSpy).toHaveBeenCalledWith({
create: { keycloakId, email, firstName, lastName },
create: { keycloakId, email, firstName, lastName, newsletter },
update: { keycloakId },
where: { email },
})
Expand Down Expand Up @@ -377,5 +404,94 @@ describe('AuthService', () => {
expect(loggerSpy).toBeCalled()
loggerSpy.mockRestore()
})

it('should subscribe email to marketing list if consent is given', async () => {
const keycloakId = 'u123'
const registerDto = plainToClass(RegisterDto, {
email,
password,
firstName,
lastName,
// Add to marketing list
newsletter: true,
})
const person: Person = {
id: 'e43348aa-be33-4c12-80bf-2adfbf8736cd',
firstName,
lastName,
keycloakId,
email,
emailConfirmed: false,
phone: null,
company: null,
picture: null,
createdAt: new Date('2021-10-07T13:38:11.097Z'),
updatedAt: new Date('2021-10-07T13:38:11.097Z'),
newsletter: true,
address: null,
birthday: null,
personalNumber: null,
stripeCustomerId: null,
}
jest.spyOn(prismaMock.person, 'upsert').mockResolvedValue(person)
jest.spyOn(admin.users, 'create').mockResolvedValue({ id: keycloakId })
const marketingSpy = jest
.spyOn(marketing, 'addContactsToList')
.mockImplementation(async () => true)

await service.createUser(registerDto)

// Check was added to list
expect(marketingSpy).toHaveBeenCalledWith({
contacts: [
{
email,
first_name: firstName,
last_name: lastName,
},
],
list_ids: ['list-id'],
})
})

it('should NOT subscribe email to marketing list if NO consent is given', async () => {
const keycloakId = 'u123'
const registerDto = plainToClass(RegisterDto, {
email,
password,
firstName,
lastName,
// Don't subscribe to marketing list
newsletter: false,
})
const person: Person = {
id: 'e43348aa-be33-4c12-80bf-2adfbf8736cd',
firstName,
lastName,
keycloakId,
email,
emailConfirmed: false,
phone: null,
company: null,
picture: null,
createdAt: new Date('2021-10-07T13:38:11.097Z'),
updatedAt: new Date('2021-10-07T13:38:11.097Z'),
newsletter: false,
address: null,
birthday: null,
personalNumber: null,
stripeCustomerId: null,
}
jest.spyOn(prismaMock.person, 'upsert').mockResolvedValue(person)
jest.spyOn(admin.users, 'create').mockResolvedValue({ id: keycloakId })
const marketingSpy = jest
.spyOn(marketing, 'addContactsToList')
.mockImplementation(async () => true)

await service.createUser(registerDto)

// Check was not added to list
expect(marketingSpy).not.toHaveBeenCalled()
})
})
})
Loading

0 comments on commit ad4b1f9

Please sign in to comment.