Skip to content

Commit

Permalink
Merge pull request #4 from samply/feature/frontend-dto
Browse files Browse the repository at this point in the history
Feature/frontend dto
  • Loading branch information
djuarezgf authored Jan 10, 2024
2 parents 6f5cec5 + 5773882 commit c638777
Show file tree
Hide file tree
Showing 18 changed files with 298 additions and 44 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Accept, Reject and request changes in project results
- Notification Service
- Integration in Focus and Beam
- Frontend DTO
- Email as Mime message
- EmailSenderIfError annotation
- Frontend DTO converters
17 changes: 17 additions & 0 deletions src/main/java/de/samply/annotations/EmailSenderIfError.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package de.samply.annotations;

import de.samply.email.EmailRecipientType;
import de.samply.email.EmailTemplateType;

import java.lang.annotation.*;

@Repeatable(EmailSendersIfError.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface EmailSenderIfError {

// Recipients of the email
EmailRecipientType[] recipients() default {};

EmailTemplateType templateType();
}
12 changes: 12 additions & 0 deletions src/main/java/de/samply/annotations/EmailSendersIfError.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package de.samply.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface EmailSendersIfError {
EmailSenderIfError[] value();
}
88 changes: 68 additions & 20 deletions src/main/java/de/samply/aop/EmailSenderAspect.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package de.samply.aop;

import de.samply.annotations.EmailSender;
import de.samply.annotations.EmailSenderIfError;
import de.samply.annotations.EmailSenders;
import de.samply.annotations.EmailSendersIfError;
import de.samply.db.model.Project;
import de.samply.db.model.ProjectBridgehead;
import de.samply.db.model.ProjectBridgeheadUser;
import de.samply.db.repository.*;
import de.samply.email.EmailRecipient;
import de.samply.email.EmailService;
import de.samply.email.EmailServiceException;
import de.samply.email.*;
import de.samply.project.state.ProjectBridgeheadState;
import de.samply.security.SessionUser;
import de.samply.user.roles.OrganisationRoleToProjectRoleMapper;
Expand All @@ -26,6 +26,7 @@

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.function.Supplier;

@Component
@Aspect
Expand Down Expand Up @@ -66,33 +67,63 @@ public void emailSenderPointcut() {
public void emailSendersPointcut() {
}

@Pointcut("@annotation(de.samply.annotations.EmailSenderIfError)")
public void emailSenderIfErrorPointcut() {
}

@Pointcut("@annotation(de.samply.annotations.EmailSendersIfError)")
public void emailSendersIfErrorPointcut() {
}


@Around("emailSenderPointcut()")
public Object aroundEmailSender(ProceedingJoinPoint joinPoint) throws Throwable {
return aroundEmailSender(joinPoint, true);
return aroundEmailSender(joinPoint, true, false);
}

@Around("emailSendersPointcut()")
public Object aroundEmailSenders(ProceedingJoinPoint joinPoint) throws Throwable {
return aroundEmailSender(joinPoint, false);
return aroundEmailSender(joinPoint, false, false);
}

private <T extends Annotation> Object aroundEmailSender(ProceedingJoinPoint joinPoint, boolean isSingleEmailSender) throws Throwable {
@Around("emailSenderIfErrorPointcut()")
public Object aroundEmailSenderIfError(ProceedingJoinPoint joinPoint) throws Throwable {
return aroundEmailSender(joinPoint, true, true);
}

@Around("emailSendersIfErrorPointcut()")
public Object aroundEmailSendersIfError(ProceedingJoinPoint joinPoint) throws Throwable {
return aroundEmailSender(joinPoint, false, true);
}

private <T extends Annotation> Object aroundEmailSender(ProceedingJoinPoint joinPoint, boolean isSingleEmailSender, boolean ifError) throws Throwable {
try {
ResponseEntity responseEntity = (ResponseEntity) joinPoint.proceed();
if (responseEntity.getStatusCode().is2xxSuccessful()) {
sendEmail(joinPoint, isSingleEmailSender);
if (responseEntity.getStatusCode().is2xxSuccessful() ^ ifError) {
sendEmail(joinPoint, isSingleEmailSender, ifError);
}
return responseEntity;
} catch (Exception e) {
if (ifError) {
sendEmail(joinPoint, isSingleEmailSender, ifError);
}
throw new RuntimeException(e);
}
}

private void sendEmail(ProceedingJoinPoint joinPoint, boolean isSingleEmailSender) {
private void sendEmail(ProceedingJoinPoint joinPoint, boolean isSingleEmailSender, boolean ifError) {
if (isSingleEmailSender) {
sendEmailFromEmailSender(joinPoint, fetchEmailSender(joinPoint));
if (ifError) {
sendEmailFromEmailSenderIfError(joinPoint, fetchEmailSenderIfError(joinPoint));
} else {
sendEmailFromEmailSender(joinPoint, fetchEmailSender(joinPoint));
}
} else {
sendEmailFromEmailSenders(joinPoint, fetchEmailSenders(joinPoint));
if (ifError) {
sendEmailFromEmailSendersIfError(joinPoint, fetchEmailSendersIfError(joinPoint));
} else {
sendEmailFromEmailSenders(joinPoint, fetchEmailSenders(joinPoint));
}
}
}

Expand All @@ -102,13 +133,23 @@ private void sendEmailFromEmailSenders(ProceedingJoinPoint joinPoint, Optional<E
}

private void sendEmailFromEmailSender(ProceedingJoinPoint joinPoint, Optional<EmailSender> emailSenderOptional) {
emailSenderOptional.ifPresent(emailSender -> fetchEmailRecipients(emailSender, joinPoint)
.forEach(emailRecipient -> sendEmail(emailRecipient, emailSender)));
emailSenderOptional.ifPresent(emailSender -> fetchEmailRecipients(() -> emailSender.recipients(), joinPoint)
.forEach(emailRecipient -> sendEmail(emailRecipient, () -> emailSender.templateType())));
}

private void sendEmailFromEmailSendersIfError(ProceedingJoinPoint joinPoint, Optional<EmailSendersIfError> emailSendersIfErrorOptional) {
emailSendersIfErrorOptional.ifPresent(emailSendersIfError -> Arrays.stream(emailSendersIfError.value()).forEach(emailSenderIfError ->
sendEmailFromEmailSenderIfError(joinPoint, Optional.of(emailSenderIfError))));
}

private void sendEmail(EmailRecipient emailRecipient, EmailSender emailSender) {
private void sendEmailFromEmailSenderIfError(ProceedingJoinPoint joinPoint, Optional<EmailSenderIfError> emailSenderIfErrorOptional) {
emailSenderIfErrorOptional.ifPresent(emailSenderIfError -> fetchEmailRecipients(() -> emailSenderIfError.recipients(), joinPoint)
.forEach(emailRecipient -> sendEmail(emailRecipient, () -> emailSenderIfError.templateType())));
}

private void sendEmail(EmailRecipient emailRecipient, Supplier<EmailTemplateType> emailTemplateTypeSupplier) {
try {
emailService.sendEmail(emailRecipient.email(), emailRecipient.bridgehead(), emailRecipient.role(), emailSender.templateType());
emailService.sendEmail(emailRecipient.email(), emailRecipient.bridgehead(), emailRecipient.role(), emailTemplateTypeSupplier.get());
} catch (EmailServiceException e) {
throw new RuntimeException(e);
}
Expand All @@ -122,12 +163,20 @@ private Optional<EmailSenders> fetchEmailSenders(JoinPoint joinPoint) {
return AspectUtils.fetchT(joinPoint, EmailSenders.class);
}

private Set<EmailRecipient> fetchEmailRecipients(EmailSender emailSender, ProceedingJoinPoint joinPoint) {
private Optional<EmailSenderIfError> fetchEmailSenderIfError(JoinPoint joinPoint) {
return AspectUtils.fetchT(joinPoint, EmailSenderIfError.class);
}

private Optional<EmailSendersIfError> fetchEmailSendersIfError(JoinPoint joinPoint) {
return AspectUtils.fetchT(joinPoint, EmailSendersIfError.class);
}

private Set<EmailRecipient> fetchEmailRecipients(Supplier<EmailRecipientType[]> emailRecipientTypesSupplier, ProceedingJoinPoint joinPoint) {
Set<EmailRecipient> result = new HashSet<>();
Optional<String> projectCode = AspectUtils.fetchProjectCode(joinPoint);
Optional<String> bridgehead = AspectUtils.fetchBridghead(joinPoint);
Optional<String> email = AspectUtils.fetchEmail(joinPoint);
Arrays.stream(emailSender.recipients()).forEach(emailRecipientType ->
Arrays.stream(emailRecipientTypesSupplier.get()).forEach(emailRecipientType ->
result.addAll(switch (emailRecipientType) {
case SESSION_USER -> fetchEmailRecipientsForSessionUser(projectCode, bridgehead);
case EMAIL_ANNOTATION -> fetchEmailRecipientsForEmailAnnotation(projectCode, bridgehead, email);
Expand Down Expand Up @@ -247,9 +296,8 @@ private Set<ProjectBridgehead> fetchProjectBridgeheads(Optional<String> projectC

private Set<EmailRecipient> fetchEmailRecipientsForProjectManagerAdmin() {
Set<EmailRecipient> result = new HashSet<>();
projectManagerAdminUserRepository.findAll().forEach(projectManagerAdminUser -> {
result.add(new EmailRecipient(projectManagerAdminUser.getEmail(), Optional.empty(), ProjectRole.PROJECT_MANAGER_ADMIN));
});
projectManagerAdminUserRepository.findAll().forEach(projectManagerAdminUser ->
result.add(new EmailRecipient(projectManagerAdminUser.getEmail(), Optional.empty(), ProjectRole.PROJECT_MANAGER_ADMIN)));
return result;
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/de/samply/db/model/ProjectBridgehead.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ public class ProjectBridgehead {
private ProjectBridgeheadState state = ProjectBridgeheadState.CREATED;

@Column(name = "modified_at", nullable = false)
private Instant modifiedAt;
private Instant modifiedAt = Instant.now();

}
14 changes: 10 additions & 4 deletions src/main/java/de/samply/document/DocumentService.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import de.samply.db.model.ProjectDocument;
import de.samply.db.repository.ProjectDocumentRepository;
import de.samply.db.repository.ProjectRepository;
import de.samply.frontend.dto.DtoFactory;
import de.samply.security.SessionUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
Expand Down Expand Up @@ -192,12 +193,17 @@ public Optional<Path> fetchApplicationForm() {
return fetchPublicDocument(applicationFormFile);
}

public List<ProjectDocument> fetchPublications(String projectCode) {
return fetchDocuments(projectCode, Optional.empty(), DocumentType.PUBLICATION);
public List<de.samply.frontend.dto.ProjectDocument> fetchPublications(String projectCode) {
return convertToDto(fetchDocuments(projectCode, Optional.empty(), DocumentType.PUBLICATION));

}

private List<de.samply.frontend.dto.ProjectDocument> convertToDto(List<ProjectDocument> projectDocumentList){
return projectDocumentList.stream().map(DtoFactory::convert).toList();
}

public List<ProjectDocument> fetchOtherDocuments(String projectCode, Optional<String> bridgehead) {
return fetchDocuments(projectCode, bridgehead, DocumentType.OTHERS);
public List<de.samply.frontend.dto.ProjectDocument> fetchOtherDocuments(String projectCode, Optional<String> bridgehead) {
return convertToDto(fetchDocuments(projectCode, bridgehead, DocumentType.OTHERS));
}


Expand Down
28 changes: 21 additions & 7 deletions src/main/java/de/samply/email/EmailService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import de.samply.app.ProjectManagerConst;
import de.samply.user.roles.ProjectRole;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
Expand Down Expand Up @@ -43,22 +45,34 @@ public void sendEmail(@NotNull String email, Optional<String> bridgehead, @NotNu
}

public void sendEmail(@NotNull String email, Optional<String> bridgehead, @NotNull ProjectRole role, @NotNull EmailTemplateType type, Map<String, String> keyValues) throws EmailServiceException {
SimpleMailMessage message = new SimpleMailMessage();
Map<String, String> context = new HashMap<>();
bridgehead.ifPresent(b -> context.put(ProjectManagerConst.EMAIL_CONTEXT_BRIDGEHEAD, b));
context.putAll(keyValues);
context.putAll(emailContext.getKeyValues());
Optional<MessageSubject> messageSubject = createEmailMessageAndSubject(role, type, context);
if (messageSubject.isPresent()) {
message.setTo(email);
message.setFrom(emailFrom);
message.setSubject(messageSubject.get().subject());
message.setText(messageSubject.get().message());
mailSender.send(message);
mailSender.send(createMimeMessage(email, emailFrom, messageSubject.get()));
} else {
throw new EmailServiceException("Template not found for " + type.name() + " of role " + role.name());
}
}

private MimeMessage createMimeMessage(String emailTo, String emailFrom, MessageSubject messageSubject) throws EmailServiceException {
try {
return createMimeMessageWithoutHandlingException(emailTo, emailFrom, messageSubject);
} catch (MessagingException e) {
throw new EmailServiceException(e);
}
}

private MimeMessage createMimeMessageWithoutHandlingException(String emailTo, String emailFrom, MessageSubject messageSubject) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper messageHelper = new MimeMessageHelper(message, true);
messageHelper.setTo(emailTo);
message.setFrom(emailFrom);
message.setSubject(messageSubject.subject());
message.setText(messageSubject.message());
return message;
}

private Optional<MessageSubject> createEmailMessageAndSubject(ProjectRole role, EmailTemplateType type, Map<String, String> keyValues) {
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/de/samply/email/EmailServiceException.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ public EmailServiceException(String message) {
super(message);
}

public EmailServiceException(Throwable cause) {
super(cause);
}

}
66 changes: 66 additions & 0 deletions src/main/java/de/samply/frontend/dto/DtoFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package de.samply.frontend.dto;

import de.samply.project.state.ProjectBridgeheadState;
import jakarta.validation.constraints.NotNull;

import java.time.Instant;

public class DtoFactory {

public static Project convert(@NotNull de.samply.db.model.Project project) {
return new Project(
project.getCode(),
project.getCreatorEmail(),
project.getCreatedAt(),
project.getExpiresAt(),
project.getArchivedAt(),
project.getModifiedAt(),
project.getState(),
project.getType(),
project.getQuery().getQuery(),
project.getQuery().getHumanReadable(),
project.getQuery().getQueryFormat(),
project.getQuery().getOutputFormat(),
project.getQuery().getTemplateId(),
project.getQuery().getLabel(),
project.getQuery().getDescription(),
project.getQuery().getExplorerUrl(),
project.getQuery().getContext()
);
}

public static Notification convert(@NotNull de.samply.db.model.Notification notification) {
return new Notification(
notification.getEmail(),
notification.getTimestamp(),
notification.getProject().getCode(),
notification.getBridgehead(),
notification.getOperationType(),
notification.getDetails(),
notification.getError()
);
}

public static ProjectDocument convert(@NotNull de.samply.db.model.ProjectDocument projectDocument) {
return new ProjectDocument(
projectDocument.getProject().getCode(),
projectDocument.getOriginalFilename(),
projectDocument.getUrl(),
projectDocument.getCreatedAt(),
projectDocument.getBridgehead(),
projectDocument.getCreatorEmail(),
projectDocument.getLabel(),
projectDocument.getDocumentType()
);
}

public static ProjectBridgehead convert(@NotNull de.samply.db.model.ProjectBridgehead projectBridgehead) {
return new ProjectBridgehead(
projectBridgehead.getProject().getCode(),
projectBridgehead.getBridgehead(),
projectBridgehead.getState(),
projectBridgehead.getModifiedAt()
);
}

}
16 changes: 16 additions & 0 deletions src/main/java/de/samply/frontend/dto/Notification.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package de.samply.frontend.dto;

import de.samply.notification.OperationType;

import java.time.Instant;

public record Notification(
String email,
Instant timestamp,
String projectCode,
String bridgehead,
OperationType operationType,
String details,
String error
) {
}
Loading

0 comments on commit c638777

Please sign in to comment.