Implemented Persistence with Spring Data JPA. Closes #1
parent
76ee50b63f
commit
cbe55fbd5a
|
@ -31,3 +31,5 @@ build/
|
|||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
*.db
|
10
pom.xml
10
pom.xml
|
@ -32,12 +32,22 @@
|
|||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.springframework.http.MediaType;
|
|||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.time.Clock;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
@ -34,17 +35,19 @@ public class ForwardMessageFormTest {
|
|||
|
||||
@TestConfiguration
|
||||
static class TestConfig {
|
||||
|
||||
@Autowired
|
||||
private Relay relay;
|
||||
|
||||
@Bean
|
||||
public Clock clock() {
|
||||
return FakeClock.fixed(FIXED_TIME);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Relay relay() {
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
RelayAccount account = new RelayAccount(UUID.randomUUID(), "company-account", TOKEN, List.of("vip@company.com"));
|
||||
Relay relay = new Relay();
|
||||
relay.addAccount(account);
|
||||
return relay;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
spring.main.allow-bean-definition-overriding=true
|
||||
spring.main.allow-bean-definition-overriding=true
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.datasource.url=jdbc:h2:mem:testdb
|
|
@ -1,14 +1,10 @@
|
|||
package de.nclazz.service.mailrelay;
|
||||
|
||||
import de.nclazz.service.mailrelay.domain.Relay;
|
||||
import de.nclazz.service.mailrelay.domain.RelayAccount;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringBootApplication
|
||||
public class MailRelayApplication {
|
||||
|
@ -22,14 +18,4 @@ public class MailRelayApplication {
|
|||
return Clock.systemDefaultZone();
|
||||
}
|
||||
|
||||
// Temporary setup for testing. Will be removed when persistence is enabled
|
||||
@Bean
|
||||
public Relay relay() {
|
||||
Relay relay = new Relay();
|
||||
RelayAccount account = new RelayAccount(UUID.randomUUID(), "My Company", "my-token", List.of("vip@mycompany.com"));
|
||||
relay.addAccount(account);
|
||||
|
||||
return relay;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package de.nclazz.service.mailrelay.adapter.jpa;
|
||||
|
||||
import de.nclazz.service.mailrelay.domain.Message;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface JpaMessageRepository extends JpaRepository<Message, UUID> {
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package de.nclazz.service.mailrelay.adapter.jpa;
|
||||
|
||||
import de.nclazz.service.mailrelay.domain.Message;
|
||||
import de.nclazz.service.mailrelay.domain.MessageRepository;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class JpaMessageRepositoryAdapter implements MessageRepository {
|
||||
|
||||
private final JpaMessageRepository jpaMessageRepository;
|
||||
|
||||
@Override
|
||||
public Message save(@NonNull Message message) {
|
||||
return this.jpaMessageRepository.save(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package de.nclazz.service.mailrelay.adapter.jpa;
|
||||
|
||||
import de.nclazz.service.mailrelay.domain.RelayAccount;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface JpaRelayAccountRepository extends JpaRepository<RelayAccount, UUID> {
|
||||
|
||||
Optional<RelayAccount> findByToken(String token);
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package de.nclazz.service.mailrelay.adapter.jpa;
|
||||
|
||||
import de.nclazz.service.mailrelay.domain.RelayAccount;
|
||||
import de.nclazz.service.mailrelay.domain.RelayAccountRepository;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class JpaRelayAccountRepositoryAdapter implements RelayAccountRepository {
|
||||
|
||||
private final JpaRelayAccountRepository jpaRepository;
|
||||
|
||||
@Override
|
||||
public List<RelayAccount> findAll() {
|
||||
return this.jpaRepository.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<RelayAccount> findByGuid(@NonNull UUID guid) {
|
||||
return this.jpaRepository.findById(guid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<RelayAccount> findByToken(@NonNull String token) {
|
||||
return this.jpaRepository.findByToken(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RelayAccount save(@NonNull RelayAccount account) {
|
||||
return this.jpaRepository.save(account);
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ public class MessageForm {
|
|||
}
|
||||
|
||||
public Message toMessage(@NonNull LocalDateTime timestamp) {
|
||||
return new Message(
|
||||
return Message.of(
|
||||
this.subject,
|
||||
this.content,
|
||||
this.from,
|
||||
|
|
|
@ -1,19 +1,57 @@
|
|||
package de.nclazz.service.mailrelay.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Lob;
|
||||
import javax.persistence.Table;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "messages")
|
||||
@Data
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Message {
|
||||
|
||||
@Id
|
||||
@Column(name = "guid", columnDefinition = "char(36)")
|
||||
@GeneratedValue(generator = "uuid")
|
||||
@GenericGenerator(name = "uuid", strategy ="org.hibernate.id.UUIDGenerator")
|
||||
@Type(type = "uuid-char")
|
||||
@JsonIgnore
|
||||
private UUID guid;
|
||||
|
||||
private String subject;
|
||||
|
||||
@Lob
|
||||
@Column(length=2048 )
|
||||
private String content;
|
||||
|
||||
@Column(name = "from_address")
|
||||
private String from;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private LocalDateTime timestamp;
|
||||
|
||||
public static Message of(String subject, String content, String from, LocalDateTime timestamp) {
|
||||
Message message = new Message();
|
||||
message.setSubject(subject);
|
||||
message.setContent(content);
|
||||
message.setFrom(from);
|
||||
message.setTimestamp(timestamp);
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package de.nclazz.service.mailrelay.domain;
|
||||
|
||||
import lombok.NonNull;
|
||||
|
||||
public interface MessageRepository {
|
||||
|
||||
Message save(@NonNull Message message);
|
||||
|
||||
}
|
|
@ -3,37 +3,31 @@ package de.nclazz.service.mailrelay.domain;
|
|||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@Data
|
||||
@Slf4j
|
||||
//@Service
|
||||
@Service
|
||||
public class Relay {
|
||||
|
||||
private final Set<RelayAccount> accounts = new HashSet<>();
|
||||
|
||||
public void addAccount(@NonNull RelayAccount account) {
|
||||
this.accounts.add(account);
|
||||
}
|
||||
private final RelayAccountRepository accountRepository;
|
||||
private final MessageRepository messageRepository;
|
||||
|
||||
public Message forwardMessage(@NonNull String token, @NonNull Message message) {
|
||||
log.info("Forward message '{}' to <{}>", message.getSubject(), token);
|
||||
Optional<RelayAccount> accountOptional = findAccountByToken(token);
|
||||
Optional<RelayAccount> accountOptional = this.accountRepository.findByToken(token);
|
||||
|
||||
if(accountOptional.isPresent()) {
|
||||
return accountOptional.get().forwardMessage(message);
|
||||
message = this.messageRepository.save(message);
|
||||
accountOptional.get().forwardMessage(message);
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private Optional<RelayAccount> findAccountByToken(String token) {
|
||||
return this.accounts.stream()
|
||||
.filter(acc -> acc.matchesToken(token))
|
||||
.findAny();
|
||||
public void addAccount(@NonNull RelayAccount account) {
|
||||
this.accountRepository.save(account);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,30 +2,52 @@ package de.nclazz.service.mailrelay.domain;
|
|||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import javax.persistence.CollectionTable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "accounts", indexes = @Index(columnList = "token"))
|
||||
@Data
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class RelayAccount {
|
||||
|
||||
@Id
|
||||
@Column(name = "guid", columnDefinition = "char(36)")
|
||||
@GeneratedValue(generator = "uuid")
|
||||
@GenericGenerator(name = "uuid", strategy ="org.hibernate.id.UUIDGenerator")
|
||||
@Type(type = "uuid-char")
|
||||
private UUID guid;
|
||||
private String name;
|
||||
private String token;
|
||||
|
||||
private List<String> forwardTo;
|
||||
@ElementCollection
|
||||
@CollectionTable(name = "accounts_receivers")
|
||||
private List<String> receivers;
|
||||
|
||||
@OneToMany
|
||||
private final List<Message> sentMessages = new ArrayList<>();
|
||||
|
||||
public Message forwardMessage(@NonNull Message message) {
|
||||
public void forwardMessage(@NonNull Message message) {
|
||||
log.info("Forward message '{}' on <{}> ({} <{}>)", message.getSubject(), this.token, this.name, this.guid);
|
||||
this.sentMessages.add(message);
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package de.nclazz.service.mailrelay.domain;
|
||||
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface RelayAccountRepository {
|
||||
|
||||
List<RelayAccount> findAll();
|
||||
|
||||
Optional<RelayAccount> findByGuid(@NonNull UUID guid);
|
||||
|
||||
Optional<RelayAccount> findByToken(@NonNull String token);
|
||||
|
||||
RelayAccount save(@NonNull RelayAccount account);
|
||||
|
||||
|
||||
}
|
|
@ -1 +1,3 @@
|
|||
|
||||
# DDL auto schema generation (update, create, create-drop, none, validate)
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.datasource.url=jdbc:h2:file:./test-db
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
package de.nclazz.service.mailrelay.adapter.web;
|
||||
|
||||
import de.nclazz.service.mailrelay.FakeClock;
|
||||
import de.nclazz.service.mailrelay.domain.Message;
|
||||
import de.nclazz.service.mailrelay.domain.FakeMessageRepository;
|
||||
import de.nclazz.service.mailrelay.domain.FakeRelayAccountRepository;
|
||||
import de.nclazz.service.mailrelay.domain.Relay;
|
||||
import de.nclazz.service.mailrelay.domain.RelayAccount;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -25,7 +26,7 @@ class MessageFormControllerTest {
|
|||
List.of("vip@account.com")
|
||||
);
|
||||
|
||||
Relay relay = new Relay();
|
||||
Relay relay = new Relay(new FakeRelayAccountRepository(), new FakeMessageRepository());
|
||||
relay.addAccount(account);
|
||||
|
||||
LocalDateTime now = LocalDateTime.of(2020, 12, 24, 12, 54);
|
||||
|
@ -39,7 +40,9 @@ class MessageFormControllerTest {
|
|||
|
||||
assertThat(account.getSentMessages())
|
||||
.hasSize(1)
|
||||
.containsExactly(new Message("Subject", "Message", "sender@company.com", now));
|
||||
.element(0)
|
||||
.extracting("subject", "content", "from", "timestamp")
|
||||
.containsExactly("Subject", "Message", "sender@company.com", now);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
package de.nclazz.service.mailrelay.adapter.web;
|
||||
|
||||
import de.nclazz.service.mailrelay.FakeClock;
|
||||
import de.nclazz.service.mailrelay.domain.Message;
|
||||
import de.nclazz.service.mailrelay.domain.FakeMessageRepository;
|
||||
import de.nclazz.service.mailrelay.domain.FakeRelayAccountRepository;
|
||||
import de.nclazz.service.mailrelay.domain.Relay;
|
||||
import de.nclazz.service.mailrelay.domain.RelayAccount;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -25,7 +26,7 @@ class MessageRestControllerTest {
|
|||
List.of("vip@account.com")
|
||||
);
|
||||
|
||||
Relay relay = new Relay();
|
||||
Relay relay = new Relay(new FakeRelayAccountRepository(), new FakeMessageRepository());
|
||||
relay.addAccount(account);
|
||||
|
||||
LocalDateTime now = LocalDateTime.of(2020, 12, 24, 12, 54);
|
||||
|
@ -36,6 +37,7 @@ class MessageRestControllerTest {
|
|||
MessageRestController controller = new MessageRestController(relay, fakeClock);
|
||||
|
||||
assertThat(controller.forwardMessage(form))
|
||||
.isEqualTo(new Message("Subject", "Message", "sender@company.com", now));
|
||||
.extracting("subject", "content", "from", "timestamp")
|
||||
.containsExactly("Subject", "Message", "sender@company.com", now);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package de.nclazz.service.mailrelay.domain;
|
||||
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FakeMessageRepository implements MessageRepository {
|
||||
|
||||
private final Set<Message> messages = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public Message save(@NonNull Message message) {
|
||||
UUID guid = (message.getGuid() != null ? message.getGuid() : UUID.randomUUID());
|
||||
message.setGuid(guid);
|
||||
this.messages.add(message);
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package de.nclazz.service.mailrelay.domain;
|
||||
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FakeRelayAccountRepository implements RelayAccountRepository {
|
||||
|
||||
private final Set<RelayAccount> accounts = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public List<RelayAccount> findAll() {
|
||||
return new ArrayList<>(this.accounts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<RelayAccount> findByGuid(@NonNull UUID guid) {
|
||||
return this.accounts.stream()
|
||||
.filter(account -> account.getGuid().equals(guid))
|
||||
.findAny();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<RelayAccount> findByToken(@NonNull String token) {
|
||||
return this.accounts.stream()
|
||||
.filter(account -> account.getToken().equals(token))
|
||||
.findAny();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RelayAccount save(@NonNull RelayAccount account) {
|
||||
UUID guid = (account.getGuid() != null ? account.getGuid() : UUID.randomUUID());
|
||||
account.setGuid(guid);
|
||||
|
||||
this.accounts.add(account);
|
||||
|
||||
return account;
|
||||
}
|
||||
}
|
|
@ -20,11 +20,11 @@ class ForwardMessageTest {
|
|||
List.of("info@company.com")
|
||||
);
|
||||
|
||||
Relay relay = new Relay();
|
||||
Relay relay = new Relay(new FakeRelayAccountRepository(), new FakeMessageRepository());
|
||||
relay.addAccount(account);
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
Message message = new Message(
|
||||
Message message = Message.of(
|
||||
"Urgent message",
|
||||
"Please reply",
|
||||
"vip@company.com",
|
||||
|
@ -47,11 +47,11 @@ class ForwardMessageTest {
|
|||
List.of("info@company.com")
|
||||
);
|
||||
|
||||
Relay relay = new Relay();
|
||||
Relay relay = new Relay(new FakeRelayAccountRepository(), new FakeMessageRepository());
|
||||
relay.addAccount(account);
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
Message message = new Message(
|
||||
Message message = Message.of(
|
||||
"Urgent message",
|
||||
"Please reply",
|
||||
"vip@company.com",
|
||||
|
|
|
@ -20,7 +20,7 @@ class RelayAccountTest {
|
|||
);
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
Message message = new Message(
|
||||
Message message = Message.of(
|
||||
"Urgent message",
|
||||
"Please reply",
|
||||
"vip@company.com",
|
||||
|
|
|
@ -18,10 +18,11 @@ class RelayTest {
|
|||
List.of("vip@account.com")
|
||||
);
|
||||
|
||||
Relay relay = new Relay();
|
||||
FakeRelayAccountRepository repository = new FakeRelayAccountRepository();
|
||||
Relay relay = new Relay(repository, new FakeMessageRepository());
|
||||
relay.addAccount(account);
|
||||
|
||||
assertThat(relay.getAccounts())
|
||||
assertThat(repository.findAll())
|
||||
.hasSize(1)
|
||||
.containsExactly(account);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue