Skip to content

Notification Service Implementation#96

Open
MasakiShiraishi wants to merge 18 commits intomainfrom
23-notifications
Open

Notification Service Implementation#96
MasakiShiraishi wants to merge 18 commits intomainfrom
23-notifications

Conversation

@MasakiShiraishi
Copy link
Copy Markdown

@MasakiShiraishi MasakiShiraishi commented Dec 29, 2024

I have implemented a notification service to send emails for events such as user registration and profile updates. The service is designed to send a notification email to a specified recipient when an event occurs.

Steps to Generate an App Password

  1. Log in to your Google Account:

  2. Navigate to the Security Page:

  • After logging in, click on "Security" in the left navigation panel.
  1. Enable Two-Factor Authentication:
  • Find the "2-Step Verification" section and click "Get Started." Follow the on-screen instructions to enable two- factor authentication. If you have already enabled it, skip this step.
  1. Generate an App Password:
  • Return to the "Security" page and find the "App passwords" section.
  • If you cannot find the "App passwords" section, type "App passwords" into the search bar.
  • Click on the "App passwords" link. You may be asked to log in again.
  • From the "Select app" dropdown menu, choose "Mail," and from the "Select device" dropdown menu, choose
    "Other (Custom name)." Enter a name (e.g., Spring Boot
    App) and click "Generate."
  • A 16-character app password will be displayed. Copy this password and set it in the .env file as MAIL_PASSWORD.

Configuration for Gmail Users

@add them to .env file in the root of the project
MAIL_HOST=smtp.gmail.com MAIL_PORT=587
MAIL_USERNAME= (replace your gmail adress)
MAIL_PASSWORD=(replace with your generated app password)
NOTIFICATION_EMAIL_RECIPIENT=(replace your gmail adress)

Summary by CodeRabbit

Release Notes

  • New Features

    • Added email notification service for user events.
    • Implemented secure user creation and update endpoints.
    • Introduced user creation and update confirmation pages.
  • Security Improvements

    • Enhanced password management with BCrypt encoding.
    • Added support for secure email configuration.
  • Configuration Updates

    • Updated Java version to 23.
    • Added mail server configuration properties.
    • Removed frontend Maven plugin.
  • Documentation

    • Updated README with App Password generation instructions.
  • Testing

    • Added unit tests for notification service.
  • Chores

    • Updated .gitignore to include .vscode directory.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 29, 2024

Walkthrough

This pull request introduces enhancements to the application's user management and notification system. Key changes include the addition of email notification capabilities, updates to user creation and update processes, improvements in security through password encoding, and modifications to the database entity configuration. The changes span multiple files, with new services, controllers, and configuration settings implemented to support robust user management and communication features.

Changes

File Change Summary
pom.xml - Added Spring Boot Mail starter dependency
- Updated Java version from 16 to 23
- Removed frontend-maven-plugin
readme.md - Added section on generating App Password for Gmail
- Included email configuration instructions
SecurityConfig.java - Added BCryptPasswordEncoder bean for password encoding
NotificationService.java - New service for sending email notifications
- Implements method to send event-based emails
UserController.java - Added endpoints for user creation and update
- Implemented /user/create and /user/update/{id} mappings
UserService.java - Integrated notification and password encoding services
- Added methods for secure user save and update
BaseEntity.java - Updated ID generation strategy to IDENTITY
application.properties - Added mail server configuration properties
- Included email notification recipient setting
templates/user/ - Added create.html and update.html for user notification views
.gitignore - Added entry to ignore .vscode/ directory

Sequence Diagram

sequenceDiagram
    participant User
    participant UserController
    participant UserService
    participant NotificationService
    participant JavaMailSender

    User->>UserController: Create/Update User
    UserController->>UserService: Save/Update User
    UserService->>UserService: Encode Password
    UserService->>NotificationService: Send Notification
    NotificationService->>JavaMailSender: Send Email
    JavaMailSender-->>NotificationService: Email Sent
    UserService-->>UserController: User Saved/Updated
    UserController-->>User: Confirmation View
Loading

Poem

🐰 A Rabbit's Ode to Code Evolution

Emails now dance, passwords securely encoded,
Users create, update - their journeys are loaded!
Notifications spring forth with magical might,
Transforming our system to a more brilliant light! 🌟
Hop, hop, hooray for technology's delight! 🚀


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e176e4 and 62f97f3.

📒 Files selected for processing (1)
  • src/main/java/org/fungover/system2024/notification/NotificationService.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/org/fungover/system2024/notification/NotificationService.java

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
src/main/java/org/fungover/system2024/user/UserService.java (2)

45-50: Validate user input before saving
While the password encoding and notification logic are correct, consider adding validations (e.g., non-null checks, length constraints) for user fields to maintain data integrity.


52-65: Enhance error handling for missing user
Currently, a RuntimeException is thrown if the user is not found. It might be more informative to throw a custom exception (e.g., ResourceNotFoundException) or return a 404 HTTP status in controllers to clearly indicate the issue.

src/main/java/org/fungover/system2024/notification/NotificationService.java (1)

13-15: Setter injection usage
Setter injection is acceptable here. However, constructor injection could further ensure immutability and make dependencies mandatory.

src/main/java/org/fungover/system2024/user/UserController.java (1)

23-32: Return status codes or success messages
While returning a view name works, consider also returning status codes and structured responses (e.g., 201 Created) to improve API clarity.

readme.md (1)

59-76: Correct minor Markdown lint issues in headings and punctuation.

Headings with trailing punctuation (lines 60, 61, 63, 67) may trigger lint warnings. Removing the trailing colon in headings will address those warnings.
Additionally, ensure each fenced code block has a language specified (line 79).

Apply the following diff to fix the headings and specify a language for the code block:

-### 1. Log in to your Google Account:
+### 1. Log in to your Google Account

-### 2. Navigate to the Security Page:
+### 2. Navigate to the Security Page

-### 3. Enable Two-Factor Authentication:
+### 3. Enable Two-Factor Authentication

-### 4. Generate an App Password:
+### 4. Generate an App Password

-```
+```bash
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
...

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Markdownlint (0.37.0)</summary>

60-60: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)

---

61-61: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)

---

63-63: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)

---

67-67: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 113b82eccebe5dca8518790c371a181d596ec414 and ce0d461e1b4edd76359142ddfe4bde4fe4f6cc42.

</details>

<details>
<summary>⛔ Files ignored due to path filters (2)</summary>

* `frontend/package-lock.json` is excluded by `!**/package-lock.json`
* `logs/app.log` is excluded by `!**/*.log`

</details>

<details>
<summary>📒 Files selected for processing (11)</summary>

* `pom.xml` (2 hunks)
* `readme.md` (1 hunks)
* `src/main/java/org/fungover/system2024/config/SecurityConfig.java` (2 hunks)
* `src/main/java/org/fungover/system2024/notification/NotificationService.java` (1 hunks)
* `src/main/java/org/fungover/system2024/user/UserController.java` (2 hunks)
* `src/main/java/org/fungover/system2024/user/UserService.java` (3 hunks)
* `src/main/java/org/fungover/system2024/user/entity/BaseEntity.java` (1 hunks)
* `src/main/resources/application.properties` (1 hunks)
* `src/main/resources/templates/user/create.html` (1 hunks)
* `src/main/resources/templates/user/update.html` (1 hunks)
* `src/test/java/org/fungover/system2024/notification/NotificationServiceTest.java` (1 hunks)

</details>

<details>
<summary>✅ Files skipped from review due to trivial changes (2)</summary>

* src/main/resources/templates/user/create.html
* src/main/resources/templates/user/update.html

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 Markdownlint (0.37.0)</summary>

<details>
<summary>readme.md</summary>

60-60: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)

---

61-61: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)

---

63-63: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)

---

67-67: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)

---

79-79: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (15)</summary>

<details>
<summary>src/main/java/org/fungover/system2024/user/UserService.java (3)</summary>

`5-12`: **Proper import usage**  
These newly added imports look correct and necessary for handling notifications and password encoding. No immediate issues spotted.

---

`21-24`: **Ensure dependency injection is managed effectively**  
It's good that `NotificationService` and `BCryptPasswordEncoder` are declared `final`. This ensures both are properly injected and cannot be reassigned, promoting immutability.

---

`25-28`: **Constructor injection best practice**  
Constructor-based injection is recommended in Spring, so this approach is aligned with recommended best practices.

</details>
<details>
<summary>src/main/java/org/fungover/system2024/user/entity/BaseEntity.java (1)</summary>

`15-15`: **Identity strategy consideration**  
Switching to `GenerationType.IDENTITY` can be beneficial for certain databases and can help avoid sequence table overhead. Ensure your DB setup supports this approach properly.

</details>
<details>
<summary>src/main/java/org/fungover/system2024/notification/NotificationService.java (1)</summary>

`1-9`: **Well-structured service class**  
The imports and annotations are appropriate. Autowiring `JavaMailSender` is standard for email sending in Spring.

</details>
<details>
<summary>src/main/java/org/fungover/system2024/user/UserController.java (2)</summary>

`11-11`: **Base request mapping clarity**  
Defining the base path `/user` is clear and helps group user-related endpoints.

---

`33-42`: **Ensure consistent update approach**  
The update method returns a view name. Confirm that a PUT endpoint is the correct REST semantic or consider `PATCH` if partially updating fields.

</details>
<details>
<summary>src/test/java/org/fungover/system2024/notification/NotificationServiceTest.java (3)</summary>

`17-18`: **Good use of MockitoExtension**  
Using `@ExtendWith(MockitoExtension.class)` simplifies the setup of mocks in JUnit 5.

---

`32-39`: **Comprehensive test coverage**  
Verifying that `mailSender.send` is called ensures the notification flow is correctly tested.

---

`41-50`: **Test exception handling**  
Simulating the `RuntimeException` from `mailSender` to test error flows is a good practice that boosts reliability.

</details>
<details>
<summary>src/main/java/org/fungover/system2024/config/SecurityConfig.java (2)</summary>

`10-10`: **BCryptPasswordEncoder import looks good.**

This import is necessary to enable secure password encoding.

---

`47-51`: **Great addition for secure password storage.**

Defining a `BCryptPasswordEncoder` bean is excellent for secure password hashing. Ensure all services and controllers handling credentials use this encoder.

</details>
<details>
<summary>src/main/resources/application.properties (1)</summary>

`22-28`: **Ensure environment variables are properly configured.**

These mail-related environment variables must be set in the deployment environment or `.env` file. Verify they are provided and tested to avoid runtime errors.

</details>
<details>
<summary>pom.xml (2)</summary>

`145-148`: **Mail dependency is correctly introduced.**

This dependency allows the application to send emails using Spring Boot’s Mail starter. Ensure it’s included in your build environment.

---

`215-216`: **Confirm Java version alignment.**

The property `<java.version>23</java.version>` indicates a Java 23 target. Ensure your environments (local, CI/CD, production) support Java 23.

</details>

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Copy link
Copy Markdown

@maxlin94 maxlin94 left a comment

Choose a reason for hiding this comment

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

Looked it over and it looks good. The SonarCloud analysis seems like a quick fix(just adding some lang attributes), and maybe consider what coderabbit said with the logging/error handling.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce0d461 and bd46db2.

📒 Files selected for processing (3)
  • .gitignore (1 hunks)
  • src/main/resources/templates/user/create.html (1 hunks)
  • src/main/resources/templates/user/update.html (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/resources/templates/user/update.html
  • src/main/resources/templates/user/create.html
🔇 Additional comments (1)
.gitignore (1)

39-39: LGTM! Critical security measure for protecting sensitive credentials.

Adding .env to .gitignore is essential as the file will contain sensitive email credentials and configuration.

@fungover fungover deleted a comment from coderabbitai Bot Dec 29, 2024
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (2)
src/main/java/org/fungover/system2024/notification/NotificationService.java (1)

25-25: Add method documentation

Add Javadoc to document the method's purpose, parameters, and potential exceptions.

+    /**
+     * Sends a notification email about a user event.
+     *
+     * @param username the username of the user who triggered the event
+     * @param eventType the type of event that occurred
+     * @throws NotificationException if the email fails to send
+     * @throws IllegalArgumentException if username or eventType is null or empty
+     */
     public void sendNotification(String username, String eventType){
src/test/java/org/fungover/system2024/notification/NotificationServiceTest.java (1)

14-14: Update to JUnit 5 assertions

Replace JUnit 4's assertThrows with JUnit 5's version.

-import static org.junit.Assert.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertThrows;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e677a2 and 0e176e4.

📒 Files selected for processing (2)
  • src/main/java/org/fungover/system2024/notification/NotificationService.java (1 hunks)
  • src/test/java/org/fungover/system2024/notification/NotificationServiceTest.java (1 hunks)
🔇 Additional comments (1)
src/main/java/org/fungover/system2024/notification/NotificationService.java (1)

35-37: 🛠️ Refactor suggestion

Improve error handling

The current implementation swallows the exception without notifying the caller. Consider wrapping the MailException in a custom exception or propagating it to allow proper handling by the caller.

+    public class NotificationException extends RuntimeException {
+        public NotificationException(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }

         } catch (MailException e) {
             logger.error("Failed to send notification for {}: {}", username, eventType, e);
+            throw new NotificationException("Failed to send notification", e);
         }

Likely invalid or redundant comment.

Comment thread src/main/java/org/fungover/system2024/notification/NotificationService.java Outdated
Comment thread src/main/java/org/fungover/system2024/notification/NotificationService.java Outdated
Comment on lines +29 to +32
message.setSubject("Notification");
message.setText("Hello,\n\n" + username + " has successfully completed their " +
eventType + ".\n\n" + "Please review their profile at your earliest convenience.\n\n" +
"Best regards,\n" + "System2024");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance email content and add input validation

The current implementation has several areas for improvement:

  1. Generic subject line
  2. No input validation
  3. Message construction mixed with sending logic
+    private void validateInput(String username, String eventType) {
+        if (username == null || username.trim().isEmpty()) {
+            throw new IllegalArgumentException("Username cannot be null or empty");
+        }
+        if (eventType == null || eventType.trim().isEmpty()) {
+            throw new IllegalArgumentException("Event type cannot be null or empty");
+        }
+    }
+
+    private SimpleMailMessage createMessage(String username, String eventType) {
+        SimpleMailMessage message = new SimpleMailMessage();
+        message.setTo(recipientEmail);
+        message.setSubject("System2024 Notification: " + eventType);
+        message.setText(String.format("""
+            Hello,
+            
+            %s has successfully completed their %s.
+            
+            Please review their profile at your earliest convenience.
+            
+            Best regards,
+            System2024
+            """, username, eventType));
+        return message;
+    }

Committable suggestion skipped: line range outside the PR's diff.

Comment thread src/test/java/org/fungover/system2024/notification/NotificationServiceTest.java Outdated
Comment on lines +35 to +42
@Test
public void testSendNotification() {
String username = "testUser";
String eventType = "testEvent";

notificationService.sendNotification(username, eventType);
verify(mailSender).send(any(SimpleMailMessage.class));
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance success test case

The current test only verifies that send was called but doesn't validate the email content.

     @Test
     public void testSendNotification() {
         String username = "testUser";
         String eventType = "testEvent";
 
         notificationService.sendNotification(username, eventType);
-        verify(mailSender).send(any(SimpleMailMessage.class));
+        ArgumentCaptor<SimpleMailMessage> messageCaptor = ArgumentCaptor.forClass(SimpleMailMessage.class);
+        verify(mailSender).send(messageCaptor.capture());
+        
+        SimpleMailMessage capturedMessage = messageCaptor.getValue();
+        assertEquals("System2024 Notification: " + eventType, capturedMessage.getSubject());
+        assertTrue(capturedMessage.getText().contains(username));
+        assertTrue(capturedMessage.getText().contains(eventType));
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Test
public void testSendNotification() {
String username = "testUser";
String eventType = "testEvent";
notificationService.sendNotification(username, eventType);
verify(mailSender).send(any(SimpleMailMessage.class));
}
@Test
public void testSendNotification() {
String username = "testUser";
String eventType = "testEvent";
notificationService.sendNotification(username, eventType);
ArgumentCaptor<SimpleMailMessage> messageCaptor = ArgumentCaptor.forClass(SimpleMailMessage.class);
verify(mailSender).send(messageCaptor.capture());
SimpleMailMessage capturedMessage = messageCaptor.getValue();
assertEquals("System2024 Notification: " + eventType, capturedMessage.getSubject());
assertTrue(capturedMessage.getText().contains(username));
assertTrue(capturedMessage.getText().contains(eventType));
}

Comment on lines +44 to +53
@Test
public void testSendNotificationWithException() {
String username = "testUser";
String eventType = "testEvent";
doThrow(new RuntimeException("Mail server error"))
.when(mailSender).send(any(SimpleMailMessage.class));
assertThrows(RuntimeException.class, () -> {
notificationService.sendNotification(username, eventType);
});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add test cases for input validation

Add test cases to verify behavior with invalid inputs.

+    @Test
+    public void testSendNotificationWithNullUsername() {
+        assertThrows(IllegalArgumentException.class, () -> 
+            notificationService.sendNotification(null, "testEvent"));
+    }
+
+    @Test
+    public void testSendNotificationWithEmptyUsername() {
+        assertThrows(IllegalArgumentException.class, () -> 
+            notificationService.sendNotification("", "testEvent"));
+    }
+
+    @Test
+    public void testSendNotificationWithNullEventType() {
+        assertThrows(IllegalArgumentException.class, () -> 
+            notificationService.sendNotification("testUser", null));
+    }
+
+    @Test
+    public void testSendNotificationWithEmptyEventType() {
+        assertThrows(IllegalArgumentException.class, () -> 
+            notificationService.sendNotification("testUser", ""));
+    }

Committable suggestion skipped: line range outside the PR's diff.

@sonarqubecloud
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants