Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notify the user via email about granted project access #365

Conversation

sven1103
Copy link
Contributor

@sven1103 sven1103 commented Sep 8, 2023

After a user has been granted access to a project, they will now get
notified with an email.

The email also contains a link to the project to simplify access for the user.

@sven1103 sven1103 marked this pull request as ready for review September 8, 2023 08:12
@sven1103 sven1103 requested a review from a team as a code owner September 8, 2023 08:12
@sven1103 sven1103 changed the base branch from main to development September 8, 2023 08:13
…as-been-granted-access-to-a-project-with-a-link-to-it
@sven1103 sven1103 changed the title Notify the user after they have been granted project access Notify the user via email about granted project access Sep 8, 2023
Copy link
Contributor

@Steffengreiner Steffengreiner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heya @sven1103 ! Thanks for the awesome implementation.
However i have some questions:
1.) The email generation code looks very simliar to the code provided in the UserContactService, is there a reason for a distinction here?
2.) I was unable to get an email if i granted project access to my user accounts or after project creation (@uni-tuebingen, @qbic.uni-tuebingen.de). Do i need to set additional environment variables or parameters to trigger the email sending?
I checked Jobrunner and found the following error:

org.jobrunr.scheduling.exceptions.JobClassNotFoundException:
life.qbic.authorization.application.policy.directive.InformUserAboutGrantedAccess.notifyUser(java.lang.String,java.lang.String,java.lang.String)

@@ -0,0 +1,21 @@
package life.qbic.authorization.application;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since then navigation is based on URL and the user authorization is not checked in the contextprovider i'd recommend moving the interface from the authorization package to the more general life.qbic.datamanager package

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dependency Inversion Principle. My only concern here was to introduce lose coupling so the auth module is not depending on the app UI module.

Comment on lines +26 to +29
private final EmailService emailService;

private final JobScheduler jobScheduler;
private final UserRepository userRepository;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could think about having all the email sending logic be handled by the UserContactService class. If there is a reason for the distinction then i'd recommend renaming the UserContactService class to make the distinction more clear (e.g. UserAccountContactService)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would be a strong violation of the Single Responsibility Pattern. I think it is not of the user contact service concern to know what it is about, but to deliver a message to a user with a given id. Imo, the service should care about finding the user's contact email address and sending the mail and only this. The content comes from the domain, where business logic policy demand to inform the user about an event.

Comment on lines +9 to +12
*/
public interface AppContextProvider {

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks similiar to the PasswortResetLinkSupplier class maybe this could be streamlined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes exactly, however this was my first effort to refactor it. The reset link supplier and confirmation link supplier would be also implemented in the app module and the interface extended accordingly.

Comment on lines +41 to +58
private String composeMessage(String projectId, User recipient, String projectTitle) {
return String.format("""
Dear %s,

you have been granted access to project:

'%s'

Please click the link below to access the project after login:

%s

Need help? Contact us for further questions at [email protected]

Best regards,\
The QBiC team
""", recipient.fullName(), projectTitle, appContextProvider.urlToProject(projectId));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a EmailFactory class which i'd expect to provide the content of the email messages. Is there a reason for the seperation here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Different domain.

@@ -3,4 +3,6 @@
public interface EmailService {

void send(Email email);

void send(String recipient, String recipientFullName, String subject, String message);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this is unnecessary since we can just generate and provide the email record. (Even better if that's handled by the UserContactService similiar to the password reset email generation)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is, that Email is introduced as domain concept but with a technical low level detail: mime type. To not break existing functionality, I decided to override the method with simple String values.

Comment on lines +74 to +75
emailService.send(recipient.emailAddress().get(), recipient.fullName().get(),
"Project access granted", composeMessage(projectId, recipient, projectTitle));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO we can just generate and provide the email record here

Suggested change
emailService.send(recipient.emailAddress().get(), recipient.fullName().get(),
"Project access granted", composeMessage(projectId, recipient, projectTitle));
new Email(composeMessage(projectId, recipient, projectTitle), "Project access granted",
"[email protected]",
new Recipient(recipient.emailAddress().get(), recipient.fullName().get()),
"text/plain"));

Comment on lines +82 to +90
@Override
public void send(String recipientAddress, String recipientFullName, String subject, String message) {
var email = new Email(message, subject, "[email protected]",
new Recipient(recipientAddress, recipientFullName), "text/plain");
sendPlainEmail(email).ifSuccessOrElse(
successResponse -> reportSuccess(successResponse, email),
failureResponse -> reportFailure(failureResponse, email)
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary if we stick to the Email DTO which enables us to avoid redundancy here :)

Suggested change
@Override
public void send(String recipientAddress, String recipientFullName, String subject, String message) {
var email = new Email(message, subject, "[email protected]",
new Recipient(recipientAddress, recipientFullName), "text/plain");
sendPlainEmail(email).ifSuccessOrElse(
successResponse -> reportSuccess(successResponse, email),
failureResponse -> reportFailure(failureResponse, email)
);
}

Comment on lines 8 to 14
/**
* <b><class short description - 1 Line!></b>
*
* <p><More detailed description - When to use, what it solves, etc.></p>
*
* @since <version tag>
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing JD

* @since 1.0.0
*/
@Component
public class DataManagerContextProvider implements AppContextProvider {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: Should the DataManagerContextProvider always provide a project endpoint? 🤔
Also the functionality looks really simliar to previous URL generation classes like e.g. the PasswordResetLinkSupplier class

@sonarcloud
Copy link

sonarcloud bot commented Sep 11, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 2 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

Copy link
Contributor

@Steffengreiner Steffengreiner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

@sven1103 sven1103 merged commit 40b7fa7 into development Sep 11, 2023
5 checks passed
@sven1103 sven1103 deleted the feature/dm-976-notify-the-user-that-has-been-granted-access-to-a-project-with-a-link-to-it branch September 11, 2023 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants