Skip to content

Commit

Permalink
[FEATURE] Envoyer l'email de confirmation de suppression de compte en…
Browse files Browse the repository at this point in the history
… autonomie (PIX-14917)

 #10689
  • Loading branch information
pix-service-auto-merge authored Dec 6, 2024
2 parents 23496bd + b600abc commit 77443db
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,11 @@ const rememberUserHasSeenLastDataProtectionPolicyInformation = async function (
return dependencies.userSerializer.serialize(updatedUser);
};

const selfDeleteUserAccount = async function (request, h) {
const selfDeleteUserAccount = async function (request, h, dependencies = { requestResponseUtils }) {
const authenticatedUserId = request.auth.credentials.userId;
const locale = dependencies.requestResponseUtils.extractLocaleFromRequest(request);

await usecases.selfDeleteUserAccount({ userId: authenticatedUserId });
await usecases.selfDeleteUserAccount({ userId: authenticatedUserId, locale });

return h.response().code(204);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { EmailFactory } from '../../../shared/mail/domain/models/EmailFactory.js';
import { mailer } from '../../../shared/mail/infrastructure/services/mailer.js';

export function createSelfDeleteUserAccountEmail({ locale, email, firstName }) {
const factory = new EmailFactory({ app: 'pix-app', locale });

const { i18n, defaultVariables } = factory;

return factory.buildEmail({
template: mailer.selfAccountDeletionTemplateId,
subject: i18n.__('self-account-deletion-email.subject'),
to: email,
variables: {
homeName: defaultVariables.homeName,
homeUrl: defaultVariables.homeUrl,
helpdeskUrl: defaultVariables.helpdeskUrl,
displayNationalLogo: defaultVariables.displayNationalLogo,
doNotAnswer: i18n.__('common.email.doNotAnswer'),
moreOn: i18n.__('common.email.moreOn'),
pixPresentation: i18n.__('common.email.pixPresentation'),
title: i18n.__('self-account-deletion-email.params.title'),
hello: i18n.__('self-account-deletion-email.params.hello', { firstName }),
requestConfirmation: i18n.__('self-account-deletion-email.params.requestConfirmation'),
seeYouSoon: i18n.__('self-account-deletion-email.params.seeYouSoon'),
signing: i18n.__('self-account-deletion-email.params.signing'),
warning: i18n.__('self-account-deletion-email.params.warning'),
},
});
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ForbiddenAccess } from '../../../shared/domain/errors.js';
import { createSelfDeleteUserAccountEmail } from '../emails/create-self-delete-user-account.email.js';

/**
* @typedef {import('../../infrastructure/repositories/privacy-users-api.repository.js')} PrivacyUsersApiRepository
Expand All @@ -10,15 +11,33 @@ import { ForbiddenAccess } from '../../../shared/domain/errors.js';
* @param{PrivacyUsersApiRepository} privacyUsersApiRepository
* @returns {Promise<boolean>}
*/
export const selfDeleteUserAccount = async function ({ userId, privacyUsersApiRepository }) {
export const selfDeleteUserAccount = async function ({
userId,
locale,
userRepository,
privacyUsersApiRepository,
emailRepository,
}) {
const canSelfDeleteAccount = await privacyUsersApiRepository.canSelfDeleteAccount({ userId });

if (!canSelfDeleteAccount) {
throw new ForbiddenAccess();
}

const user = await userRepository.get(userId);

const anonymizedByUserId = userId;
const anonymizedByUserRole = 'USER';
const client = 'PIX_APP';
await privacyUsersApiRepository.anonymizeUser({ userId, anonymizedByUserId, anonymizedByUserRole, client });

if (user.email) {
await emailRepository.sendEmailAsync(
createSelfDeleteUserAccountEmail({
locale: locale,
email: user.email,
firstName: user.firstName,
}),
);
}
};
4 changes: 4 additions & 0 deletions api/src/shared/mail/infrastructure/services/mailer.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ class Mailer {
get targetProfileNotCertifiableTemplateId() {
return mailing[this._providerName].templates.targetProfileNotCertifiableTemplateId;
}

get selfAccountDeletionTemplateId() {
return mailing[this._providerName].templates.selfAccountDeletionTemplateId;
}
}

const mailer = new Mailer();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,34 @@ import { databaseBuilder, expect, sinon } from '../../../../test-helper.js';

describe('Integration | Identity Access Management | Domain | UseCase | self-delete-user-account', function () {
context('when user can self delete their account', function () {
it('doesn’t throw ForbiddenError', async function () {
// given
const userId = databaseBuilder.factory.buildUser().id;
await databaseBuilder.commit();
context('when user has an email', function () {
it('doesn’t throw ForbiddenError and creates a SendEmailJob', async function () {
// given
const userId = databaseBuilder.factory.buildUser().id;
await databaseBuilder.commit();

sinon.stub(config.featureToggles, 'isSelfAccountDeletionEnabled').value(true);
sinon.stub(config.featureToggles, 'isSelfAccountDeletionEnabled').value(true);

// when & then
await expect(usecases.selfDeleteUserAccount({ userId })).to.not.be.rejectedWith(ForbiddenAccess);
// when & then
await expect(usecases.selfDeleteUserAccount({ userId })).to.not.be.rejectedWith(ForbiddenAccess);

await expect('SendEmailJob').to.have.been.performed.withJobsCount(1);
});
});

context('when user doesn’t have an email', function () {
it('doesn’t throw ForbiddenError and doesn’t create a SendEmailJob', async function () {
// given
const userId = databaseBuilder.factory.buildUser.withoutPixAuthenticationMethod().id;
await databaseBuilder.commit();

sinon.stub(config.featureToggles, 'isSelfAccountDeletionEnabled').value(true);

// when & then
await expect(usecases.selfDeleteUserAccount({ userId })).to.not.be.rejectedWith(ForbiddenAccess);

await expect('SendEmailJob').to.have.been.performed.withJobsCount(0);
});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Email } from '../../../../../src/shared/mail/domain/models/Email.js';
import { mailer } from '../../../../../src/shared/mail/infrastructure/services/mailer.js';
import { expect } from '../../../../test-helper.js';

describe('Unit | Identity Access Management | Domain | Emails | create-account-creation', function () {
describe('Unit | Identity Access Management | Domain | Email | create-account-creation', function () {
it('creates account creation email with correct parameters', function () {
const emailParams = {
locale: 'fr',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { createSelfDeleteUserAccountEmail } from '../../../../../src/identity-access-management/domain/emails/create-self-delete-user-account.email.js';
import { Email } from '../../../../../src/shared/mail/domain/models/Email.js';
import { mailer } from '../../../../../src/shared/mail/infrastructure/services/mailer.js';
import { expect } from '../../../../test-helper.js';

describe('Unit | Identity Access Management | Domain | Email | create-self-delete-user-account', function () {
it('creates self delete user account email with correct parameters', function () {
const emailParams = {
locale: 'fr',
email: '[email protected]',
firstName: 'John',
};

const email = createSelfDeleteUserAccountEmail(emailParams);

expect(email).to.be.instanceof(Email);
expect(email).to.have.property('subject').that.is.a('string');
expect(email.to).to.equal(emailParams.email);
expect(email.template).to.equal(mailer.selfAccountDeletionTemplateId);

const variables = email.variables;
expect(variables).to.have.property('displayNationalLogo').that.is.a('boolean');
expect(variables).to.have.property('doNotAnswer').that.is.a('string');
expect(variables).to.have.property('helpdeskUrl').that.is.a('string');
expect(variables).to.have.property('homeName').that.is.a('string');
expect(variables).to.have.property('homeUrl').that.is.a('string');
expect(variables).to.have.property('moreOn').that.is.a('string');
expect(variables).to.have.property('pixPresentation').that.is.a('string');
expect(variables).to.have.property('title').that.is.a('string');
expect(variables).to.have.property('hello').that.is.a('string');
expect(variables).to.have.property('requestConfirmation').that.is.a('string');
expect(variables).to.have.property('seeYouSoon').that.is.a('string');
expect(variables).to.have.property('signing').that.is.a('string');
expect(variables).to.have.property('warning').that.is.a('string');
});
});

0 comments on commit 77443db

Please sign in to comment.