Skip to content

Commit

Permalink
fix: show different mails for incompleted users
Browse files Browse the repository at this point in the history
- fix: resend different mails for unregistered vs semi-registered users

# Squashed commits:
- 4fed666: fix: show different mails for non registered users
- 774ea3d: fix: send
- 9c4f866: test
- 6221404: revert: test
  • Loading branch information
yxuo committed Feb 15, 2024
1 parent 5d96c66 commit f5aa0eb
Show file tree
Hide file tree
Showing 17 changed files with 369 additions and 67 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ npm run seed:run user mailhistory
```
> O comando não diferencia maiúsculas de minúsculas
Rodar seed com todos os módulos exceto alguns
```
npm run seed:run __exclude user mailhistory
> A ordem dos parâmetros não influencia a execução
```

## Links

- Swagger: http://localhost:3000/docs
Expand Down
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { SettingsModule } from './settings/settings.module';
import { MailCountModule } from './mail-count/mail-count.module';
import { CronJobsModule } from './cron-jobs/cron-jobs.module';
import { BigqueryModule } from './bigquery/bigquery.module';
import { TestModule } from './test/test.module';

@Module({
imports: [
Expand Down Expand Up @@ -104,6 +105,7 @@ import { BigqueryModule } from './bigquery/bigquery.module';
MailCountModule,
CronJobsModule,
BigqueryModule,
TestModule,
],
})
export class AppModule {}
2 changes: 1 addition & 1 deletion src/config/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
Min,
} from 'class-validator';

enum Environment {
export enum Environment {
Development = 'development',
Production = 'production',
Local = 'local',
Expand Down
1 change: 1 addition & 0 deletions src/cron-jobs/cron-jobs.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ import { MailCountModule } from 'src/mail-count/mail-count.module';
MailCountModule,
],
providers: [CronJobsService],
exports: [CronJobsService],
})
export class CronJobsModule {}
18 changes: 13 additions & 5 deletions src/cron-jobs/cron-jobs.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,10 @@ export class CronJobsService implements OnModuleInit {
{
name: CrobJobsEnum.bulkResendInvites,
cronJobParameters: {
cronTime: '* * * * *', //'45 14 * * *', // 14:45 GMT = 11:45BRT (GMT-3)
onTick: async () => this.bulkResendInvites(),
cronTime: '45 14 * * *', // 14:45 GMT = 11:45BRT (GMT-3)
onTick: async () => {
await this.bulkResendInvites();
},
},
},
);
Expand Down Expand Up @@ -567,15 +569,15 @@ export class CronJobsService implements OnModuleInit {
};
}

async bulkResendInvites() {
async bulkResendInvites(): Promise<HttpStatus> {
const THIS_METHOD = `${this.bulkResendInvites.name}()`;
const notRegisteredUsers = await this.usersService.getNotRegisteredUsers();

if (notRegisteredUsers.length === 0) {
this.logger.log(
formatLog('Não há usuários para enviar, abortando...', THIS_METHOD),
);
return;
return HttpStatus.NOT_FOUND;
}
this.logger.log(
formatLog(
Expand All @@ -589,6 +591,7 @@ export class CronJobsService implements OnModuleInit {
for (const user of notRegisteredUsers) {
await this.resendInvite(user, THIS_METHOD);
}
return HttpStatus.OK;
}

async resendInvite(user: User, outerMethod: string) {
Expand All @@ -604,7 +607,12 @@ export class CronJobsService implements OnModuleInit {

// Success
if (mailSentInfo.success) {
this.logger.log(formatLog('Email enviado com sucesso.', THIS_METHOD));
this.logger.log(
formatLog(
`Email enviado com sucesso para ${mailSentInfo.envelope.to}.`,
THIS_METHOD,
),
);
}

// SMTP error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ export class MailHistorySeedDataService {
);
for (let i = 0; i < mailSeedData.length; i++) {
const mail = mailSeedData[i];
if (mail.email === 'registered.user@example.com') {
if (
[
'sent.user@example.com',
'used.user@example.com',
'registered.user@example.com',
].includes(mail.email as string)
) {
mail[i] = {
...mail,
sentAt: subDays(new Date(), 16),
Expand Down
81 changes: 59 additions & 22 deletions src/database/seeds/mail-history/mail-history-seed.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { UserSeedDataService } from '../user/user-seed-data.service';
@Injectable()
export class MailHistorySeedService {
private logger = new Logger('MailHistorySeedService', { timestamp: true });
private newMails: any[] = [];

constructor(
@InjectRepository(MailHistory)
Expand All @@ -27,39 +28,64 @@ export class MailHistorySeedService {
}

async run() {
for (const item of await this.mhSeedDataService.getDataFromConfig()) {
const itemUser = await this.getHistoryUser(item);
for (const mailFixture of await this.mhSeedDataService.getDataFromConfig()) {
const itemUser = await this.getHistoryUser(mailFixture);
const itemSeedUser = (
await this.userSeedDataService.getDataFromConfig()
).find((i) => i.email === itemUser.email);
const foundItem = await this.mailHistoryRepository.findOne({
const foundMail = await this.mailHistoryRepository.findOne({
where: {
user: { email: itemUser.email as string },
},
});

if (!foundItem) {
const newItem = { ...item };
newItem.user = itemUser;
newItem.email = itemUser.email as string;
newItem.hash = await this.generateInviteHash();
if (itemSeedUser?.inviteStatus) {
newItem.inviteStatus = itemSeedUser.inviteStatus;
}
this.logger.log(`Creating mail history: ${JSON.stringify(newItem)}`);
await this.mailHistoryRepository.save(
this.mailHistoryRepository.create(newItem),
);
await this.usersRepository.save(
this.usersRepository.create({
id: itemUser.id,
hash: newItem.hash,
}),
);
const hash = await this.generateInviteHash();
const newItem = { ...mailFixture };
newItem.user = itemUser;
newItem.email = itemUser.email as string;
newItem.hash = hash;
if (itemSeedUser?.inviteStatus) {
newItem.inviteStatus = itemSeedUser.inviteStatus;
}
await this.saveMailHistory(newItem, itemUser, foundMail);
this.pushNewMail(newItem, foundMail);
}

if (this.newMails.length) {
this.printResults();
} else {
this.logger.log('No new mails changed.');
}
}

pushNewMail(mail: IMailSeedData, foundMail: MailHistory | null) {
this.newMails.push({
status: foundMail ? 'updated' : 'created',
email: mail.email,
hash: mail.hash,
inviteStatus: mail.inviteStatus,
sentAt: mail.sentAt,
});
}

async saveMailHistory(
mail: IMailSeedData,
itemUser: User,
foundMail: MailHistory | null,
) {
await this.mailHistoryRepository.save(
this.mailHistoryRepository.create({
...mail,
id: foundMail?.id || mail.id,
}),
);
await this.usersRepository.save(
this.usersRepository.create({
id: itemUser.id,
hash: mail.hash,
}),
);
}

async generateInviteHash(): Promise<string> {
let hash = crypto
.createHash('sha256')
Expand All @@ -80,4 +106,15 @@ export class MailHistorySeedService {
});
return users[0];
}

printResults() {
this.logger.log('NEW USERS:');
this.logger.warn(
'The passwords shown are always new but if user exists the current password in DB wont be updated.\n' +
'Save these passwords in the first run or remove these users before seed',
);
for (const item of this.newMails) {
this.logger.log(item);
}
}
}
24 changes: 12 additions & 12 deletions src/database/seeds/user/user-seed-data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ export class UserSeedDataService {
await this.bigqueryService.runQuery(
BQSInstances.smtr,
`
SELECT
DISTINCT o.documento,
FROM \`rj-smtr-dev.cadastro.operadoras\` o
LEFT JOIN \`rj-smtr-dev.br_rj_riodejaneiro_bilhetagem_cct.transacao\` t ON t.id_operadora = o.id_operadora
WHERE t.modo = 'Van'
LIMIT 5
SELECT
DISTINCT o.documento,
FROM \`rj-smtr-dev.cadastro.operadoras\` o
LEFT JOIN \`rj-smtr-dev.br_rj_riodejaneiro_bilhetagem_cct.transacao\` t ON t.id_operadora = o.id_operadora
WHERE t.modo = 'Van'
LIMIT 5
`,
)
).reduce((l: string[], i) => [...l, i['documento']], []);
Expand All @@ -45,12 +45,12 @@ export class UserSeedDataService {
await this.bigqueryService.runQuery(
BQSInstances.smtr,
`
SELECT
DISTINCT c.cnpj,
FROM \`rj-smtr-dev.cadastro.consorcios\` c
LEFT JOIN \`rj-smtr-dev.br_rj_riodejaneiro_bilhetagem_cct.transacao\` t ON t.id_consorcio = c.id_consorcio
WHERE t.modo != 'Van' AND c.cnpj IS NOT NULL
LIMIT 5
SELECT
DISTINCT c.cnpj,
FROM \`rj-smtr-dev.cadastro.consorcios\` c
LEFT JOIN \`rj-smtr-dev.br_rj_riodejaneiro_bilhetagem_cct.transacao\` t ON t.id_consorcio = c.id_consorcio
WHERE t.modo != 'Van' AND c.cnpj IS NOT NULL
LIMIT 5
`,
)
).reduce((l: string[], i) => [...l, i['cnpj']], []);
Expand Down
52 changes: 31 additions & 21 deletions src/database/seeds/user/user-seed.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Repository } from 'typeorm';
import { UserSeedDataService } from './user-seed-data.service';
import * as crypto from 'crypto';
import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
import { UserDataInterface } from 'src/users/interfaces/user-data.interface';

@Injectable()
export class UserSeedService {
Expand All @@ -23,48 +24,57 @@ export class UserSeedService {

async run() {
const userFixtures = await this.userSeedDataService.getDataFromConfig();
for (const item of userFixtures) {
const foundItem = await this.userSeedRepository.findOne({
for (const userFixture of userFixtures) {
const foundUserFixture = await this.userSeedRepository.findOne({
where: {
email: item.email,
email: userFixture.email,
},
});

let createdItem: User;
if (foundItem) {
const newItem = new User(foundItem);
newItem.update(item, true);
if (foundUserFixture) {
const newUser = new User(foundUserFixture);
newUser.update(userFixture, true);
await this.userSeedRepository.save(
this.userSeedRepository.create(newItem),
this.userSeedRepository.create(newUser),
);
createdItem = (await this.userSeedRepository.findOne({
where: {
email: item.email as string,
email: userFixture.email as string,
},
})) as User;
} else {
createdItem = await this.userSeedRepository.save(
this.userSeedRepository.create(item),
this.userSeedRepository.create(userFixture),
);
}
item.hash = await this.generateHash();
this.newUsers.push({
status: foundItem ? 'updated' : 'created',
fullName: item.fullName,
email: item.email,
password: item.password,
cpfCnpj: item.cpfCnpj,
hashedPassword: createdItem.password,
});
userFixture.hash = await this.generateHash();
this.pushNewUser(userFixture, foundUserFixture, createdItem);
}

if (this.newUsers.length) {
this.printPasswords();
this.printResults();
} else {
this.logger.log('No new users created.');
this.logger.log('No new users changed.');
}
}

printPasswords() {
pushNewUser(
userFixture: UserDataInterface,
foundUserFixture: User | null,
createdItem: User,
) {
this.newUsers.push({
status: foundUserFixture ? 'updated' : 'created',
fullName: userFixture.fullName,
email: userFixture.email,
password: userFixture.password,
cpfCnpj: userFixture.cpfCnpj,
hashedPassword: createdItem.password,
});
}

printResults() {
this.logger.log('NEW USERS:');
this.logger.warn(
'The passwords shown are always new but if user exists the current password in DB wont be updated.\n' +
Expand Down
11 changes: 6 additions & 5 deletions src/mail/mail.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,18 +249,19 @@ export class MailService {
const appName = this.configService.get('app.name', {
infer: true,
});
const userLink =
inviteStatus.id === InviteStatusEnum.used
? `${frontendDomain}sign-in`
: `${frontendDomain}conclude-registration/${mailData.data.hash}`;
const response = await this.safeSendMail({
from,
to: mailData.to,
subject: mailTitle,
text: mailTitle,
text: `reminder-complete-registration ${userLink} ${mailTitle}`,
template: 'user-daily-conclude',
context: {
title: 'Confirme seu email',
userLink:
inviteStatus.id === InviteStatusEnum.used
? `${frontendDomain}sign-in`
: `${frontendDomain}conclude-registration/${mailData.data.hash}`,
userLink,
headerTitle: appName,
},
});
Expand Down
14 changes: 14 additions & 0 deletions src/test/test-environments.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { CanActivate, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Environment } from 'src/config/app.config';

@Injectable()
export class TestEnvironmentsGuard implements CanActivate {
constructor(private readonly configService: ConfigService) {}

canActivate(): boolean {
const nodeEnv = () =>
this.configService.getOrThrow<Environment>('app.nodeEnv');
return [Environment.Test, Environment.Local].includes(nodeEnv());
}
}
Loading

0 comments on commit f5aa0eb

Please sign in to comment.