Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

<dependency>
<groupId>com.graphql-java-kickstart</groupId>
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/example/tasklist/TasklistApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@EnableTransactionManagement
@EnableCaching
@EnableScheduling
public class TasklistApplication {

public static void main(final String[] args) {
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/com/example/tasklist/config/MailConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.example.tasklist.config;

import com.example.tasklist.service.props.MailProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;

@Configuration
@RequiredArgsConstructor
public class MailConfig {

private final MailProperties mailProperties;

@Bean
public JavaMailSender mailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(mailProperties.getHost());
mailSender.setPort(mailProperties.getPort());
mailSender.setUsername(mailProperties.getUsername());
mailSender.setPassword(mailProperties.getPassword());
mailSender.setJavaMailProperties(mailProperties.getProperties());
mailSender.getJavaMailProperties();
return mailSender;
}

}
8 changes: 8 additions & 0 deletions src/main/java/com/example/tasklist/domain/MailType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example.tasklist.domain;

public enum MailType {

REGISTRATION,
REMINDER

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.sql.Timestamp;
import java.util.List;

public interface TaskRepository extends JpaRepository<Task, Long> {
Expand All @@ -17,6 +18,14 @@ public interface TaskRepository extends JpaRepository<Task, Long> {
""", nativeQuery = true)
List<Task> findAllByUserId(@Param("userId") Long userId);

@Query(value = """
SELECT * FROM tasks t
WHERE t.expiration_date is not null
AND t.expiration_date between :start and :end
""", nativeQuery = true)
List<Task> findAllSoonTasks(@Param("start") Timestamp start,
@Param("end") Timestamp end);

@Modifying
@Query(value = """
INSERT INTO users_tasks (user_id, task_id)
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/com/example/tasklist/repository/UserRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ public interface UserRepository extends JpaRepository<User, Long> {

Optional<User> findByUsername(String username);

@Query(value = """
SELECT u.id as id,
u.name as name,
u.username as username,
u.password as password
FROM users_tasks ut
JOIN users u ON ut.user_id = u.id
WHERE ut.task_id = :taskId
""", nativeQuery = true)
Optional<User> findTaskAuthor(@Param("taskId") Long taskId);

@Query(value = """
SELECT exists(
SELECT 1
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/com/example/tasklist/service/MailService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.example.tasklist.service;

import com.example.tasklist.domain.MailType;
import com.example.tasklist.domain.user.User;

import java.util.Properties;

public interface MailService {

void sendEmail(User user, MailType type, Properties params);

}
7 changes: 7 additions & 0 deletions src/main/java/com/example/tasklist/service/Reminder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.tasklist.service;

public interface Reminder {

void remindForTask();

}
3 changes: 3 additions & 0 deletions src/main/java/com/example/tasklist/service/TaskService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.example.tasklist.domain.task.Task;
import com.example.tasklist.domain.task.TaskImage;

import java.time.Duration;
import java.util.List;

public interface TaskService {
Expand All @@ -11,6 +12,8 @@ public interface TaskService {

List<Task> getAllByUserId(Long id);

List<Task> getAllSoonTasks(Duration duration);

Task update(Task task);

Task create(Task task, Long userId);
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/example/tasklist/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public interface UserService {

boolean isTaskOwner(Long userId, Long taskId);

User getTaskAuthor(Long taskId);

void delete(Long id);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.example.tasklist.service.impl;

import com.example.tasklist.domain.MailType;
import com.example.tasklist.domain.user.User;
import com.example.tasklist.service.MailService;
import freemarker.template.Configuration;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

@Service
@RequiredArgsConstructor
public class MailServiceImpl implements MailService {

private final Configuration configuration;
private final JavaMailSender mailSender;

@Override
public void sendEmail(final User user,
final MailType type,
final Properties params) {
switch (type) {
case REGISTRATION -> sendRegistrationEmail(user, params);
case REMINDER -> sendReminderEmail(user, params);
default -> {
}
}
}

@SneakyThrows
private void sendRegistrationEmail(final User user,
final Properties params) {
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,
false,
"UTF-8");
helper.setSubject("Thank you for registration, " + user.getName());
helper.setTo(user.getUsername());
String emailContent = getRegistrationEmailContent(user, params);
helper.setText(emailContent, true);
mailSender.send(mimeMessage);
}

@SneakyThrows
private void sendReminderEmail(final User user, final Properties params) {
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,
false,
"UTF-8");
helper.setSubject("You have task to do in 1 hour");
helper.setTo(user.getUsername());
String emailContent = getReminderEmailContent(user, params);
helper.setText(emailContent, true);
mailSender.send(mimeMessage);
}

@SneakyThrows
private String getRegistrationEmailContent(final User user,
final Properties properties) {
StringWriter writer = new StringWriter();
Map<String, Object> model = new HashMap<>();
model.put("name", user.getName());
configuration.getTemplate("register.ftlh")
.process(model, writer);
return writer.getBuffer().toString();
}

@SneakyThrows
private String getReminderEmailContent(final User user,
final Properties properties) {
StringWriter writer = new StringWriter();
Map<String, Object> model = new HashMap<>();
model.put("name", user.getName());
model.put("title", properties.getProperty("task.title"));
model.put("description", properties.getProperty("task.description"));
configuration.getTemplate("reminder.ftlh")
.process(model, writer);
return writer.getBuffer().toString();
}

}
41 changes: 41 additions & 0 deletions src/main/java/com/example/tasklist/service/impl/ReminderImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.example.tasklist.service.impl;

import com.example.tasklist.domain.MailType;
import com.example.tasklist.domain.task.Task;
import com.example.tasklist.domain.user.User;
import com.example.tasklist.service.MailService;
import com.example.tasklist.service.Reminder;
import com.example.tasklist.service.TaskService;
import com.example.tasklist.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.List;
import java.util.Properties;

@Service
@RequiredArgsConstructor
public class ReminderImpl implements Reminder {

private final TaskService taskService;
private final UserService userService;
private final MailService mailService;
private final Duration duration = Duration.ofHours(1);

// @Scheduled(cron = "0 0 * * * *")
@Scheduled(cron = "0 * * * * *")
@Override
public void remindForTask() {
List<Task> tasks = taskService.getAllSoonTasks(duration);
tasks.forEach(task -> {
User user = userService.getTaskAuthor(task.getId());
Properties properties = new Properties();
properties.setProperty("task.title", task.getTitle());
properties.setProperty("task.description", task.getDescription());
mailService.sendEmail(user, MailType.REMINDER, properties);
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;

@Service
Expand All @@ -38,6 +41,14 @@ public List<Task> getAllByUserId(final Long id) {
return taskRepository.findAllByUserId(id);
}

@Override
@Transactional(readOnly = true)
public List<Task> getAllSoonTasks(final Duration duration) {
LocalDateTime now = LocalDateTime.now();
return taskRepository.findAllSoonTasks(Timestamp.valueOf(now),
Timestamp.valueOf(now.plus(duration)));
}

@Override
@Transactional
@CachePut(value = "TaskService::getById", key = "#task.id")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.example.tasklist.service.impl;

import com.example.tasklist.domain.MailType;
import com.example.tasklist.domain.exception.ResourceNotFoundException;
import com.example.tasklist.domain.user.Role;
import com.example.tasklist.domain.user.User;
import com.example.tasklist.repository.UserRepository;
import com.example.tasklist.service.MailService;
import com.example.tasklist.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
Expand All @@ -14,6 +16,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Properties;
import java.util.Set;

@Service
Expand All @@ -22,6 +25,7 @@ public class UserServiceImpl implements UserService {

private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final MailService mailService;

@Override
@Transactional(readOnly = true)
Expand Down Expand Up @@ -78,6 +82,7 @@ public User create(final User user) {
Set<Role> roles = Set.of(Role.ROLE_USER);
user.setRoles(roles);
userRepository.save(user);
mailService.sendEmail(user, MailType.REGISTRATION, new Properties());
return user;
}

Expand All @@ -89,6 +94,16 @@ public boolean isTaskOwner(final Long userId, final Long taskId) {
return userRepository.isTaskOwner(userId, taskId);
}

@Override
@Transactional(readOnly = true)
@Cacheable(value = "UserService::getTaskAuthor",
key = "#taskId")
public User getTaskAuthor(final Long taskId) {
return userRepository.findTaskAuthor(taskId)
.orElseThrow(() ->
new ResourceNotFoundException("User not found."));
}

@Override
@Transactional
@CacheEvict(value = "UserService::getById", key = "#id")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.example.tasklist.service.props;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.Properties;

@Component
@Data
@ConfigurationProperties(prefix = "spring.mail")
public class MailProperties {

private String host;
private int port;
private String username;
private String password;
private Properties properties;

}
Loading