-
Notifications
You must be signed in to change notification settings - Fork 0
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
Notify the user via email about granted project access #365
Conversation
…as-been-granted-access-to-a-project-with-a-link-to-it
There was a problem hiding this 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; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
private final EmailService emailService; | ||
|
||
private final JobScheduler jobScheduler; | ||
private final UserRepository userRepository; |
There was a problem hiding this comment.
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
)
There was a problem hiding this comment.
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.
*/ | ||
public interface AppContextProvider { | ||
|
||
/** |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
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)); | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.
emailService.send(recipient.emailAddress().get(), recipient.fullName().get(), | ||
"Project access granted", composeMessage(projectId, recipient, projectTitle)); |
There was a problem hiding this comment.
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
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")); |
@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) | ||
); | ||
} |
There was a problem hiding this comment.
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 :)
@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) | |
); | |
} |
/** | ||
* <b><class short description - 1 Line!></b> | ||
* | ||
* <p><More detailed description - When to use, what it solves, etc.></p> | ||
* | ||
* @since <version tag> | ||
*/ |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
Kudos, SonarCloud Quality Gate passed! 0 Bugs No Coverage information |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 🚀
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.