diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 06b35905..7245f0d6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -7,11 +7,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the code - uses: actions/checkout@v3 - - name: Set up JDK 1.17 - uses: actions/setup-java@v3 + uses: actions/checkout@v4 + - name: Set up JDK 21 + uses: actions/setup-java@v4 with: - java-version: 17.0.4+1 + java-version: 21 distribution: zulu - name: Build the project with Maven run: ./mvnw --batch-mode -update-snapshots package diff --git a/Dockerfile b/Dockerfile index 3b70f93f..c38de528 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ -FROM openjdk:17-slim +FROM openjdk:21-slim ADD target/dancer.jar /dancer.jar CMD ["java", "--enable-preview", "-jar", "/dancer.jar"] diff --git a/Readme.md b/Readme.md index 92475e02..8b0a492c 100644 --- a/Readme.md +++ b/Readme.md @@ -58,7 +58,7 @@ Here you can configure the connection to the postgres instance: # stopping dancer in docker-compose docker-compose stop dancer; # running the boot app with overwriting the needed host -./mvnw spring-boot:run -Dspring-boot.run.arguments="--spring.datasource.url=jdbc:postgresql://localhost:5432/dancer --spring.kafka.bootstrap-servers=localhost:9092" +./mvnw spring-boot:run -Dspring-boot.run.arguments="--spring.datasource.url=jdbc:postgresql://localhost:5432/dancer ```` #### checking test-coverage diff --git a/swagger.yaml b/api/swagger.yaml similarity index 98% rename from swagger.yaml rename to api/swagger.yaml index 46379e9f..b033b738 100644 --- a/swagger.yaml +++ b/api/swagger.yaml @@ -398,7 +398,9 @@ paths: tags: - Contacts requestBody: - description: the mail will end up in dev@dancier.net + description: > + The mail will end up in dev@dancier.net, the sender will also get a copy of the mail. + When the user of this endpoint is logged in, the senders email-address will be the one from the user. In this case the provided value in this payload will be ignored. required: true content: application/json: diff --git a/docker-compose.yml b/docker-compose.yml index 1dff1177..6b63e3af 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,6 +12,7 @@ services: - dancer-db environment: # https://ckinan.com/blog/remote-debug-spring-boot-docker-intellij/: + - SPRING_DATASOURCE_URL=jdbc:postgresql://dancer-db:5432/dancer - SPRING_PROFILES_ACTIVE="dev" - JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 dancer-db: @@ -27,7 +28,7 @@ services: - ./volumes/dancer-data:/var/lib/postgresql/data pg-admin: - image: dpage/pgadmin4:7 + image: dpage/pgadmin4:8.1 restart: always ports: - "5050:80" diff --git a/pom.xml b/pom.xml index e944ad25..c409b436 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.14 + 3.2.0 net.dancier @@ -14,8 +14,7 @@ dancer Demo project for Spring Boot - 5.8.8 - 17 + 21 1.15.1 @@ -51,15 +50,35 @@ org.springframework.boot spring-boot-starter-webflux + + io.cloudevents + cloudevents-kafka + 2.5.0 + + + io.cloudevents + cloudevents-json-jackson + 2.5.0 + org.springframework.boot spring-boot-starter-validation + + org.springframework.kafka + spring-kafka-test + test + org.springframework.security spring-security-test test + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity6 + com.vladmihalcea hibernate-types-52 @@ -101,6 +120,7 @@ org.projectlombok lombok + true io.minio @@ -151,37 +171,17 @@ io.micrometer micrometer-registry-prometheus + runtime dancer - - org.apache.maven.plugins - maven-compiler-plugin - - 17 - 17 - --enable-preview - - - - maven-surefire-plugin - - --enable-preview - - - - maven-failsafe-plugin - - --enable-preview - - org.jacoco jacoco-maven-plugin - 0.8.7 + 0.8.11 @@ -201,7 +201,12 @@ org.springframework.boot spring-boot-maven-plugin - --enable-preview + + + org.projectlombok + lombok + + diff --git a/src/main/java/net/dancier/dancer/DancerApplication.java b/src/main/java/net/dancier/dancer/DancerApplication.java index b07e8843..24c05cf8 100644 --- a/src/main/java/net/dancier/dancer/DancerApplication.java +++ b/src/main/java/net/dancier/dancer/DancerApplication.java @@ -8,7 +8,7 @@ import org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters; import org.springframework.scheduling.annotation.EnableScheduling; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; import java.util.TimeZone; @SpringBootApplication diff --git a/src/main/java/net/dancier/dancer/authentication/AdminUserInitializer.java b/src/main/java/net/dancier/dancer/authentication/AdminUserInitializer.java index 3a952c4c..67b2c1b3 100644 --- a/src/main/java/net/dancier/dancer/authentication/AdminUserInitializer.java +++ b/src/main/java/net/dancier/dancer/authentication/AdminUserInitializer.java @@ -14,8 +14,8 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; -import javax.annotation.PostConstruct; -import javax.transaction.Transactional; +import jakarta.annotation.PostConstruct; +import jakarta.transaction.Transactional; import java.util.Collection; import java.util.Optional; diff --git a/src/main/java/net/dancier/dancer/authentication/AuthenticationController.java b/src/main/java/net/dancier/dancer/authentication/AuthenticationController.java index f21fa4de..bc7dddbe 100644 --- a/src/main/java/net/dancier/dancer/authentication/AuthenticationController.java +++ b/src/main/java/net/dancier/dancer/authentication/AuthenticationController.java @@ -27,9 +27,9 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import java.net.URI; import java.util.HashMap; import java.util.Map; @@ -64,6 +64,7 @@ public ResponseEntity whoami() { @Secured({ROLE_HUMAN}) @PostMapping("/registrations") public ResponseEntity register(@Valid @RequestBody RegisterRequestDto registerRequest) { + log.info("About to register user: " + registerRequest); try { authenticationService.registerUser(registerRequest); } catch (UserOrEmailAlreadyExistsException userOrEmailAlreadyExistsException) { @@ -107,19 +108,19 @@ public ResponseEntity loginAsHuman(@RequestHeader(required = false, name = "X HttpServletResponse httpServletResponse) { log.info("Log in as human"); ResponseCookie cookie = null; + String jwt = null; try { captchaService.verifyToken(token); + jwt = authenticationService.generateJwtToken("HUMAN"); cookie = authenticationService - .generateCookie( - authenticationService.generateJwtToken("HUMAN") - ); + .generateCookie(jwt); httpServletResponse.addHeader(HttpHeaders.SET_COOKIE, cookie.toString()); } catch (CaptchaException captchaException) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body( new ApiResponse(false, "Not authorized as a human: " + captchaException.getMessage()) ); } - return ResponseEntity.ok().build(); + return ResponseEntity.ok(new JwtAuthenticationResponse(jwt)); } @GetMapping("/logout") diff --git a/src/main/java/net/dancier/dancer/authentication/dto/RegisterRequestDto.java b/src/main/java/net/dancier/dancer/authentication/dto/RegisterRequestDto.java index 543ecbba..9f021bac 100644 --- a/src/main/java/net/dancier/dancer/authentication/dto/RegisterRequestDto.java +++ b/src/main/java/net/dancier/dancer/authentication/dto/RegisterRequestDto.java @@ -4,7 +4,7 @@ import lombok.Data; import lombok.NoArgsConstructor; -import javax.validation.constraints.*; +import jakarta.validation.constraints.*; @Data @AllArgsConstructor diff --git a/src/main/java/net/dancier/dancer/authentication/dto/SendLinkDto.java b/src/main/java/net/dancier/dancer/authentication/dto/SendLinkDto.java index f1b12146..99e0e6ae 100644 --- a/src/main/java/net/dancier/dancer/authentication/dto/SendLinkDto.java +++ b/src/main/java/net/dancier/dancer/authentication/dto/SendLinkDto.java @@ -3,9 +3,9 @@ import lombok.Data; import lombok.NoArgsConstructor; -import javax.validation.constraints.Email; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Size; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; @Data @NoArgsConstructor diff --git a/src/main/java/net/dancier/dancer/authentication/event/NewUserCreatedEventListener.java b/src/main/java/net/dancier/dancer/authentication/event/NewUserCreatedEventListener.java index cbf8c43e..2d96bb82 100644 --- a/src/main/java/net/dancier/dancer/authentication/event/NewUserCreatedEventListener.java +++ b/src/main/java/net/dancier/dancer/authentication/event/NewUserCreatedEventListener.java @@ -4,10 +4,11 @@ import net.dancier.dancer.authentication.model.EmailValidationCode; import net.dancier.dancer.authentication.repository.EmailValidationCodeRepository; import net.dancier.dancer.mail.service.MailCreationService; -import net.dancier.dancer.mail.service.MailEnqueueService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.event.EventListener; +import org.springframework.mail.SimpleMailMessage; import org.springframework.stereotype.Component; import java.time.Instant; @@ -23,7 +24,8 @@ public class NewUserCreatedEventListener { private final static Logger log = LoggerFactory.getLogger(NewUserCreatedEventListener.class); private final EmailValidationCodeRepository emailValidationCodeRepository; - private final MailEnqueueService mailEnqueueService; + + private final ApplicationEventPublisher applicationEventPublisher; private final MailCreationService mailCreationService; @@ -41,20 +43,20 @@ void handle(NewUserCreatedEvent newUserCreatedEvent) { emailValidationCode.setUserId(newUserCreatedEvent.getId()); emailValidationCode.setCode(UUID.randomUUID().toString()); emailValidationCodeRepository.save(emailValidationCode); - enqueueUserMail(newUserCreatedEvent, emailValidationCode.getCode()); + applicationEventPublisher.publishEvent( + createMailMessage(newUserCreatedEvent, emailValidationCode.getCode()) + ); log.debug("Created validation code: " + emailValidationCode.getCode() + " for user: " + newUserCreatedEvent); } - private void enqueueUserMail(NewUserCreatedEvent newUserCreatedEvent, String code) { - mailEnqueueService.enqueueMail( - mailCreationService.createDancierMessageFromTemplate( + private SimpleMailMessage createMailMessage(NewUserCreatedEvent newUserCreatedEvent, String code) { + return mailCreationService.createDancierMessageFromTemplate( newUserCreatedEvent.getEmail(), MailCreationService.NO_REPLY_FROM, "Dancier - bestätige Deine E-Mail-Adresse!", MailCreationService.NEW_USER_VALIDATE_EMAIL, Map.of( "validationLink", emailValidationLink(code) - )) - ); + )); } private String emailValidationLink(String validationCode) { diff --git a/src/main/java/net/dancier/dancer/authentication/model/EmailValidationCode.java b/src/main/java/net/dancier/dancer/authentication/model/EmailValidationCode.java index d15e4a3a..b8e359ce 100644 --- a/src/main/java/net/dancier/dancer/authentication/model/EmailValidationCode.java +++ b/src/main/java/net/dancier/dancer/authentication/model/EmailValidationCode.java @@ -2,8 +2,8 @@ import lombok.Data; -import javax.persistence.*; -import javax.validation.constraints.NotNull; +import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; import java.time.Instant; import java.util.UUID; diff --git a/src/main/java/net/dancier/dancer/authentication/model/PasswordResetCode.java b/src/main/java/net/dancier/dancer/authentication/model/PasswordResetCode.java index 41405d63..ef4169ee 100644 --- a/src/main/java/net/dancier/dancer/authentication/model/PasswordResetCode.java +++ b/src/main/java/net/dancier/dancer/authentication/model/PasswordResetCode.java @@ -2,7 +2,7 @@ import lombok.Data; -import javax.persistence.*; +import jakarta.persistence.*; import java.time.Instant; import java.util.UUID; diff --git a/src/main/java/net/dancier/dancer/authentication/model/Role.java b/src/main/java/net/dancier/dancer/authentication/model/Role.java index b092df7b..726df04f 100644 --- a/src/main/java/net/dancier/dancer/authentication/model/Role.java +++ b/src/main/java/net/dancier/dancer/authentication/model/Role.java @@ -1,9 +1,8 @@ package net.dancier.dancer.authentication.model; +import jakarta.persistence.*; import lombok.Data; -import org.hibernate.annotations.NaturalId; -import javax.persistence.*; import java.util.UUID; @Data diff --git a/src/main/java/net/dancier/dancer/authentication/model/User.java b/src/main/java/net/dancier/dancer/authentication/model/User.java index 077f5fea..9e4aae11 100644 --- a/src/main/java/net/dancier/dancer/authentication/model/User.java +++ b/src/main/java/net/dancier/dancer/authentication/model/User.java @@ -2,11 +2,11 @@ import lombok.Data; -import javax.persistence.*; -import javax.validation.constraints.Email; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; +import jakarta.persistence.*; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import java.util.Collection; import java.util.UUID; diff --git a/src/main/java/net/dancier/dancer/authentication/model/VerifiedActionCode.java b/src/main/java/net/dancier/dancer/authentication/model/VerifiedActionCode.java index 92a8e68a..d30ec3f2 100644 --- a/src/main/java/net/dancier/dancer/authentication/model/VerifiedActionCode.java +++ b/src/main/java/net/dancier/dancer/authentication/model/VerifiedActionCode.java @@ -2,8 +2,8 @@ import lombok.Data; -import javax.persistence.*; -import javax.validation.constraints.NotNull; +import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; import java.time.Instant; import java.util.UUID; diff --git a/src/main/java/net/dancier/dancer/authentication/service/AuthenticationService.java b/src/main/java/net/dancier/dancer/authentication/service/AuthenticationService.java index 502b30ca..2997fd03 100644 --- a/src/main/java/net/dancier/dancer/authentication/service/AuthenticationService.java +++ b/src/main/java/net/dancier/dancer/authentication/service/AuthenticationService.java @@ -1,5 +1,7 @@ package net.dancier.dancer.authentication.service; +import jakarta.persistence.EntityNotFoundException; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import net.dancier.dancer.authentication.dto.RegisterRequestDto; import net.dancier.dancer.authentication.event.NewUserCreatedEvent; @@ -10,19 +12,16 @@ import net.dancier.dancer.core.exception.BusinessException; import net.dancier.dancer.core.exception.NotFoundException; import net.dancier.dancer.mail.service.MailCreationService; -import net.dancier.dancer.mail.service.MailEnqueueService; import net.dancier.dancer.security.JwtTokenProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationEventPublisher; import org.springframework.http.ResponseCookie; -import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; -import javax.persistence.EntityNotFoundException; -import javax.transaction.Transactional; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; @@ -44,21 +43,22 @@ public class AuthenticationService { private final PasswordResetCodeRepository passwordResetCodeRepository; - private final AuthenticationManager authenticationManager; + private final AuthenticationProvider authenticationProvider; private final JwtTokenProvider tokenProvider; - private final MailEnqueueService mailEnqueueService; - private final MailCreationService mailCreationService; private final VerifiedActionCodeRepository verifiedActionCodeRepository; + private final String frontendBaseName; + private final ApplicationEventPublisher applicationEventPublisher; + private final CookieConfiguration cookieConfiguration; public Authentication authenticate(Authentication authentication) { - return this.authenticationManager.authenticate(authentication); + return this.authenticationProvider.authenticate(authentication); } public String generateJwtToken(Authentication authentication) { return this.tokenProvider.generateJwtToken(authentication); @@ -83,8 +83,11 @@ public ResponseCookie generateCookie(String token) { * By setting the maxAge to 0, the cookie will be deleted by the browser. */ public ResponseCookie generateClearingCookie() { - return ResponseCookie.from("jwt-token", "") - .build(); + return ResponseCookie + .from("jwt-token", "") + .path("/") + .maxAge(0) + .build(); } public User getUser(UUID userId) { @@ -136,7 +139,7 @@ private void handleRegistrationAttemptOfAlreadyExistingAccount(User user) { loginLink(): emailValidationLink(createEmailValidationCode(user)); - enqueueTypedUserMail(user.getEmail(),"Du bist schon Mitglied bei dancier.net ;-)", MailCreationService.USER_ALREADY_EXISTS_EMAIL, + sendUserMail(user.getEmail(),"Du bist schon Mitglied bei dancier.net ;-)", MailCreationService.USER_ALREADY_EXISTS_EMAIL, Map.of("passwordResetLink", passwordResetLink(passwordResetCode), "email", user.getEmail(), "loginLink", loginLink) @@ -226,17 +229,19 @@ public void checkPasswortCodeRequestAndCreateNew(String code, String newPassword } public void sendChangePasswordMail(String email, String code) { - enqueueTypedUserMail(email, + sendUserMail(email, "Du möchtest dein Passwort auf dancier.net ändern...", MailCreationService.PASSWORD_CHANGE_REQUEST_EMAIL, Map.of("changePasswordLink", passwordResetLink(code)) ); - } private void enqueueTypedUserMail(String email, - String subject, - String template, - Map data) { - mailEnqueueService.enqueueMail( + } + + private void sendUserMail(String email, + String subject, + String template, + Map data) { + applicationEventPublisher.publishEvent( mailCreationService.createDancierMessageFromTemplate( email, MailCreationService.NO_REPLY_FROM, @@ -247,15 +252,14 @@ public void sendChangePasswordMail(String email, String code) { ); } private void enqueueUserMail(User user, String validationCode) { - mailEnqueueService.enqueueMail( - mailCreationService.createDancierMessageFromTemplate( - user.getEmail(), - MailCreationService.NO_REPLY_FROM, - "Dancier - bestätige Deine E-Mail-Adresse!", - MailCreationService.NEW_USER_VALIDATE_EMAIL, - Map.of( "validationLink", emailValidationLink(validationCode) + applicationEventPublisher.publishEvent( + mailCreationService.createDancierMessageFromTemplate( + user.getEmail(), + MailCreationService.NO_REPLY_FROM, + "Dancier - bestätige Deine E-Mail-Adresse!", + MailCreationService.NEW_USER_VALIDATE_EMAIL, + Map.of( "validationLink", emailValidationLink(validationCode) )) ); } - } diff --git a/src/main/java/net/dancier/dancer/authentication/service/CaptchaClientProd.java b/src/main/java/net/dancier/dancer/authentication/service/CaptchaClientProd.java index 8166d09d..64a81b27 100644 --- a/src/main/java/net/dancier/dancer/authentication/service/CaptchaClientProd.java +++ b/src/main/java/net/dancier/dancer/authentication/service/CaptchaClientProd.java @@ -23,9 +23,6 @@ public class CaptchaClientProd implements CaptchaClient { @Value("${app.captcha.siteKey}") private String siteKey; - @Value("${app.mail.user}") - private String mailuser; - @Value("${app.captcha.apiKey}") private String apiKey; diff --git a/src/main/java/net/dancier/dancer/chat/DancerController.java b/src/main/java/net/dancier/dancer/chat/DancerController.java index 4a5594a7..e32c6a23 100644 --- a/src/main/java/net/dancier/dancer/chat/DancerController.java +++ b/src/main/java/net/dancier/dancer/chat/DancerController.java @@ -36,4 +36,10 @@ public ResponseEntity> post( dancerService.getDancerMap(dancerIdsDto) ); } + + @GetMapping("/{dancerId}/mail") + public ResponseEntity getMail(@PathVariable UUID dancierId) { + log.info("Getting email-Address for: " + dancierId); + return ResponseEntity.ok("foo"); + } } diff --git a/src/main/java/net/dancier/dancer/chat/client/ChatServiceClient.java b/src/main/java/net/dancier/dancer/chat/client/ChatServiceClient.java index 32f6b910..15b66879 100644 --- a/src/main/java/net/dancier/dancer/chat/client/ChatServiceClient.java +++ b/src/main/java/net/dancier/dancer/chat/client/ChatServiceClient.java @@ -1,12 +1,13 @@ package net.dancier.dancer.chat.client; import io.netty.handler.logging.LogLevel; -import net.dancier.dancer.chat.dto.*; +import net.dancier.dancer.chat.dto.ChatDto; +import net.dancier.dancer.chat.dto.CreateChatDto; +import net.dancier.dancer.chat.dto.CreatedChatDto; +import net.dancier.dancer.chat.dto.MessageDto; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties; -import org.springframework.http.ResponseEntity; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.stereotype.Service; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; @@ -16,7 +17,7 @@ import reactor.netty.transport.logging.AdvancedByteBufFormat; import reactor.util.retry.Retry; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; import java.time.Duration; import java.util.Optional; import java.util.UUID; diff --git a/src/main/java/net/dancier/dancer/chat/dto/ChatDto.java b/src/main/java/net/dancier/dancer/chat/dto/ChatDto.java index 7073ebb4..d82bd5b0 100644 --- a/src/main/java/net/dancier/dancer/chat/dto/ChatDto.java +++ b/src/main/java/net/dancier/dancer/chat/dto/ChatDto.java @@ -3,8 +3,6 @@ import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; import java.time.OffsetDateTime; import java.util.List; import java.util.UUID; @@ -13,9 +11,9 @@ public class ChatDto { private UUID chatId; private List participantIds; -//2023-12-15T13:00:23.897543763Z -// @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX") - //private OffsetDateTime lastActivity; + @JsonFormat(shape = JsonFormat.Shape.STRING) + private OffsetDateTime lastActivity; private MessageDto lastMessage; + @JsonFormat(shape = JsonFormat.Shape.STRING) private OffsetDateTime createdAt; } diff --git a/src/main/java/net/dancier/dancer/chat/dto/ChatType.java b/src/main/java/net/dancier/dancer/chat/dto/ChatType.java deleted file mode 100644 index a335a95a..00000000 --- a/src/main/java/net/dancier/dancer/chat/dto/ChatType.java +++ /dev/null @@ -1,6 +0,0 @@ -package net.dancier.dancer.chat.dto; - -public enum ChatType { - GROUP, - DIRECT -} diff --git a/src/main/java/net/dancier/dancer/chat/dto/ChatsDto.java b/src/main/java/net/dancier/dancer/chat/dto/ChatsDto.java deleted file mode 100644 index 329828f1..00000000 --- a/src/main/java/net/dancier/dancer/chat/dto/ChatsDto.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.dancier.dancer.chat.dto; - -import lombok.Data; - -import java.util.List; - -@Data -public class ChatsDto { - private List chats; -} diff --git a/src/main/java/net/dancier/dancer/chat/dto/CreateChatDto.java b/src/main/java/net/dancier/dancer/chat/dto/CreateChatDto.java index e24f3fae..5c34385e 100644 --- a/src/main/java/net/dancier/dancer/chat/dto/CreateChatDto.java +++ b/src/main/java/net/dancier/dancer/chat/dto/CreateChatDto.java @@ -2,8 +2,6 @@ import lombok.Data; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; import java.util.List; import java.util.UUID; diff --git a/src/main/java/net/dancier/dancer/chat/dto/MessageDto.java b/src/main/java/net/dancier/dancer/chat/dto/MessageDto.java index ee85002c..bb2e12c8 100644 --- a/src/main/java/net/dancier/dancer/chat/dto/MessageDto.java +++ b/src/main/java/net/dancier/dancer/chat/dto/MessageDto.java @@ -13,7 +13,7 @@ public class MessageDto { private UUID authorId; private String text; private List readByParticipants; -// @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX") -// private OffsetDateTime createdAt; + @JsonFormat(shape = JsonFormat.Shape.STRING) + private OffsetDateTime createdAt; } diff --git a/src/main/java/net/dancier/dancer/contact/ContactController.java b/src/main/java/net/dancier/dancer/contact/ContactController.java index 7fe3a241..5d8f1c5d 100644 --- a/src/main/java/net/dancier/dancer/contact/ContactController.java +++ b/src/main/java/net/dancier/dancer/contact/ContactController.java @@ -1,6 +1,8 @@ package net.dancier.dancer.contact; import lombok.RequiredArgsConstructor; +import net.dancier.dancer.security.AuthenticatedUser; +import net.dancier.dancer.security.CurrentUser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; @@ -11,6 +13,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.stream.Collectors; + import static net.dancier.dancer.authentication.Constants.ROLE_HUMAN; @RestController @@ -23,10 +27,12 @@ public class ContactController { private final ContactService contactService; @PostMapping - @Secured(ROLE_HUMAN) - public ResponseEntity sentMail(@RequestBody ContactDto contactDto) { + @Secured({ROLE_HUMAN}) + public ResponseEntity sentMail(@RequestBody ContactDto contactDto, + @CurrentUser AuthenticatedUser currentUser) { + log.info("Sending {} to {}.", contactDto.getMessage(), contactDto.getSender()); - contactService.send(contactDto); + contactService.send(contactDto, currentUser); return ResponseEntity.status(HttpStatus.CREATED).build(); } diff --git a/src/main/java/net/dancier/dancer/contact/ContactDto.java b/src/main/java/net/dancier/dancer/contact/ContactDto.java index bf4202a4..12c69fbd 100644 --- a/src/main/java/net/dancier/dancer/contact/ContactDto.java +++ b/src/main/java/net/dancier/dancer/contact/ContactDto.java @@ -2,14 +2,13 @@ import lombok.Data; -import javax.validation.constraints.Email; -import javax.validation.constraints.NotNull; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotNull; @Data public class ContactDto { @Email - @NotNull private String sender; @NotNull diff --git a/src/main/java/net/dancier/dancer/contact/ContactService.java b/src/main/java/net/dancier/dancer/contact/ContactService.java index 4c555d0a..0f7a86b3 100644 --- a/src/main/java/net/dancier/dancer/contact/ContactService.java +++ b/src/main/java/net/dancier/dancer/contact/ContactService.java @@ -1,39 +1,54 @@ package net.dancier.dancer.contact; import lombok.RequiredArgsConstructor; -import net.dancier.dancer.mail.model.DancierMailMessage; import net.dancier.dancer.mail.service.MailCreationService; -import net.dancier.dancer.mail.service.MailEnqueueService; +import net.dancier.dancer.security.AuthenticatedUser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.security.core.GrantedAuthority; import org.springframework.stereotype.Service; +import java.util.List; import java.util.Map; +import java.util.Set; + +import static net.dancier.dancer.authentication.Constants.ROLE_USER; @Service @RequiredArgsConstructor public class ContactService { + private final static Logger log = LoggerFactory.getLogger(ContactService.class); + private final MailCreationService mailCreationService; - private final MailEnqueueService mailEnqueueService; - void send(ContactDto contactDto) { - DancierMailMessage mailToSender = mailCreationService.createDancierMessageFromTemplate( - contactDto.getSender(), + private final ApplicationEventPublisher applicationEventPublisher; + + void send(ContactDto contactDto, AuthenticatedUser authenticatedUserOfSender) { + String senderMailAddress = (authenticatedUserOfSender.getAuthorities().contains(ROLE_USER)) + ? authenticatedUserOfSender.getEmail() + : contactDto.getSender(); + SimpleMailMessage mailToSender = mailCreationService.createDancierMessageFromTemplate( + senderMailAddress, "dev@dancier.net", "Vielen Dank - Team Dancier", MailCreationService.CONTACT_FORMULAR_FEEDBACK, Map.of()); + applicationEventPublisher.publishEvent(mailToSender); - mailEnqueueService.enqueueMail(mailToSender); - DancierMailMessage mailToTeamDancier = mailCreationService.createDancierMessageFromTemplate( + SimpleMailMessage mailToTeamDancier = mailCreationService.createDancierMessageFromTemplate( "dev@dancier.net", - contactDto.getSender(), + senderMailAddress, "Mail über das Kontakt formular", MailCreationService.CONTACT_FORMULAR, Map.of( "sender", contactDto.getSender(), "message", contactDto.getMessage()) ); - mailEnqueueService.enqueueMail(mailToTeamDancier); + + applicationEventPublisher.publishEvent(mailToTeamDancier); } } diff --git a/src/main/java/net/dancier/dancer/core/AppInstanceIdFilter.java b/src/main/java/net/dancier/dancer/core/AppInstanceIdFilter.java index ae02331e..c6e4d3f7 100644 --- a/src/main/java/net/dancier/dancer/core/AppInstanceIdFilter.java +++ b/src/main/java/net/dancier/dancer/core/AppInstanceIdFilter.java @@ -4,10 +4,10 @@ import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @Component diff --git a/src/main/java/net/dancier/dancer/core/ProfileController.java b/src/main/java/net/dancier/dancer/core/ProfileController.java index 77bb826c..1bd8346e 100644 --- a/src/main/java/net/dancier/dancer/core/ProfileController.java +++ b/src/main/java/net/dancier/dancer/core/ProfileController.java @@ -8,12 +8,13 @@ import net.dancier.dancer.core.exception.UnresolvableZipCode; import net.dancier.dancer.security.AuthenticatedUser; import net.dancier.dancer.security.CurrentUser; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.annotation.Secured; import org.springframework.web.bind.annotation.*; -import javax.validation.Valid; +import jakarta.validation.Valid; import java.util.Map; import java.util.UUID; diff --git a/src/main/java/net/dancier/dancer/core/ProfileService.java b/src/main/java/net/dancier/dancer/core/ProfileService.java index 50036cb8..5d4465b6 100644 --- a/src/main/java/net/dancier/dancer/core/ProfileService.java +++ b/src/main/java/net/dancier/dancer/core/ProfileService.java @@ -17,6 +17,8 @@ import net.dancier.dancer.core.util.ModelMapper; import net.dancier.dancer.location.ZipCode; import net.dancier.dancer.location.ZipCodeRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -30,6 +32,8 @@ @RequiredArgsConstructor public class ProfileService { + private Logger log = LoggerFactory.getLogger(ProfileService.class); + private final UserRepository userRepository; private final DanceService danceService; @@ -64,6 +68,8 @@ public void updateProfileForUserId(UUID userId, ProfileOfCurrentUserDto profileO d.setVersion(0); return d; }); + Integer oldVersion = dancer.getVersion().intValue(); + dancer.setGender(profileOfCurrentUserDto.getGender()); dancer.setBirthDate(profileOfCurrentUserDto.getBirthDate()); dancer.setSize(profileOfCurrentUserDto.getSize()); @@ -90,17 +96,20 @@ public void updateProfileForUserId(UUID userId, ProfileOfCurrentUserDto profileO } handleDancerProfiles(dancer, profileOfCurrentUserDto); dancer.setUpdatedAt(Instant.now()); - if (dancer.getVersion()!=null) { - dancer.setVersion(dancer.getVersion() + 1); + dancerRepository.save(dancer); + + log.info("{}/{}", dancer.getVersion(), oldVersion); + // this hould be unequal but it does not work then + if (dancer.getVersion().equals(oldVersion)) { + log.info("Profile-Change detected"); + applicationEventPublisher.publishEvent( + ProfileUpdatedEvent + .builder() + .dancer(dancer) + .build()); } else { - dancer.setVersion(0); + log.info("Version unchanged"); } - dancerRepository.save(dancer); - applicationEventPublisher.publishEvent( - ProfileUpdatedEvent - .builder() - .dancer(dancer) - .build()); } private void checkDancerNameRules(String dancerName) { diff --git a/src/main/java/net/dancier/dancer/core/ScheduleMessagePort.java b/src/main/java/net/dancier/dancer/core/ScheduleMessagePort.java new file mode 100644 index 00000000..5a5c7224 --- /dev/null +++ b/src/main/java/net/dancier/dancer/core/ScheduleMessagePort.java @@ -0,0 +1,14 @@ +package net.dancier.dancer.core; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.cloudevents.CloudEvent; + +import java.net.URI; + +public interface ScheduleMessagePort { + void schedule(Object object, + String key, + URI source, + String type); + +} diff --git a/src/main/java/net/dancier/dancer/core/config/KafkaTopicConfig.java b/src/main/java/net/dancier/dancer/core/config/KafkaTopicConfig.java deleted file mode 100644 index 4e2e3bd8..00000000 --- a/src/main/java/net/dancier/dancer/core/config/KafkaTopicConfig.java +++ /dev/null @@ -1,35 +0,0 @@ -package net.dancier.dancer.core.config; - -import org.apache.kafka.clients.admin.AdminClientConfig; -import org.apache.kafka.clients.admin.NewTopic; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.kafka.core.KafkaAdmin; - -import java.util.HashMap; -import java.util.Map; - -@Configuration -public class KafkaTopicConfig { - private static Logger log = LoggerFactory.getLogger(KafkaTopicConfig.class); - - @Value(value = "${spring.kafka.bootstrap-servers}") - private String bootstrapAddress; - - @Bean - public KafkaAdmin kafkaAdmin() { - Map configs = new HashMap<>(); - configs.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress); - return new KafkaAdmin(configs); - } - - @Bean - public NewTopic profileUpdated() { - return new NewTopic("profile-updated", 1, (short) 1); - } - -} diff --git a/src/main/java/net/dancier/dancer/core/config/ObjectMapperConfiguration.java b/src/main/java/net/dancier/dancer/core/config/ObjectMapperConfiguration.java index c9902f86..b1193abb 100644 --- a/src/main/java/net/dancier/dancer/core/config/ObjectMapperConfiguration.java +++ b/src/main/java/net/dancier/dancer/core/config/ObjectMapperConfiguration.java @@ -12,11 +12,10 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -@Configuration +//@Configuration public class ObjectMapperConfiguration { - @Bean - @Primary +// @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); JavaTimeModule javaTimeModule=new JavaTimeModule(); diff --git a/src/main/java/net/dancier/dancer/core/config/SecurityConfig.java b/src/main/java/net/dancier/dancer/core/config/SecurityConfig.java deleted file mode 100644 index 4f7999fd..00000000 --- a/src/main/java/net/dancier/dancer/core/config/SecurityConfig.java +++ /dev/null @@ -1,73 +0,0 @@ -package net.dancier.dancer.core.config; - -import lombok.RequiredArgsConstructor; -import net.dancier.dancer.security.CustomUserDetailsServiceImpl; -import net.dancier.dancer.security.JwtAuthenticationEntryPoint; -import net.dancier.dancer.security.JwtAuthenticationFilter; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.BeanIds; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; - -@Configuration -@EnableWebSecurity -@RequiredArgsConstructor -public class SecurityConfig extends WebSecurityConfigurerAdapter { - - private final CustomUserDetailsServiceImpl customUserDetailsService; - - private final JwtAuthenticationEntryPoint unauthorizedHandler; - - private final JwtAuthenticationFilter jwtAuthenticationFilter; - @Override - public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { - authenticationManagerBuilder - .userDetailsService(customUserDetailsService) - .passwordEncoder(passwordEncoder()); - } - - @Bean(BeanIds.AUTHENTICATION_MANAGER) - @Override - public AuthenticationManager authenticationManagerBean() throws Exception { - return super.authenticationManagerBean(); - } - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .cors() - .and() - .csrf() - .disable() - .exceptionHandling() - .authenticationEntryPoint(unauthorizedHandler) - .and() - .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.NEVER) - .and() - .authorizeRequests() - .antMatchers("/authentication/**", - "/profile/checkDancerNameAvailability/*", - "/eventlog/**", - "/actuator/**" - ) - .permitAll() - .anyRequest() - .authenticated(); - - http.addFilterAfter(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); - } -} \ No newline at end of file diff --git a/src/main/java/net/dancier/dancer/core/config/SecurityConfiguration.java b/src/main/java/net/dancier/dancer/core/config/SecurityConfiguration.java new file mode 100644 index 00000000..c53dada2 --- /dev/null +++ b/src/main/java/net/dancier/dancer/core/config/SecurityConfiguration.java @@ -0,0 +1,58 @@ +package net.dancier.dancer.core.config; + + +import lombok.RequiredArgsConstructor; +import net.dancier.dancer.security.JwtAuthenticationFilter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +@RequiredArgsConstructor +public class SecurityConfiguration { + + @Bean + AuthenticationProvider authenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) { + DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); + authenticationProvider.setUserDetailsService(userDetailsService); + authenticationProvider.setPasswordEncoder(passwordEncoder); + return authenticationProvider; + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + SecurityFilterChain filterChain(HttpSecurity httpSecurity, + AuthenticationProvider authenticationProvider, + JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception { + httpSecurity + .cors(Customizer.withDefaults()) + .csrf(c -> c.disable()) + .exceptionHandling(Customizer.withDefaults()) + .authorizeHttpRequests( + authz -> authz + .requestMatchers( + "/authentication/**", + "/eventlog/**" + + ) + .permitAll() + .anyRequest().authenticated() + ) + .authenticationProvider(authenticationProvider) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + return httpSecurity.build(); + } + +} diff --git a/src/main/java/net/dancier/dancer/core/controller/payload/LoginRequestDto.java b/src/main/java/net/dancier/dancer/core/controller/payload/LoginRequestDto.java index 43ff4f76..28a9371b 100644 --- a/src/main/java/net/dancier/dancer/core/controller/payload/LoginRequestDto.java +++ b/src/main/java/net/dancier/dancer/core/controller/payload/LoginRequestDto.java @@ -2,7 +2,7 @@ import lombok.Data; -import javax.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotBlank; @Data public class LoginRequestDto { diff --git a/src/main/java/net/dancier/dancer/core/dto/ProfileOfCurrentUserDto.java b/src/main/java/net/dancier/dancer/core/dto/ProfileOfCurrentUserDto.java index 79cfea6f..5e26ec54 100644 --- a/src/main/java/net/dancier/dancer/core/dto/ProfileOfCurrentUserDto.java +++ b/src/main/java/net/dancier/dancer/core/dto/ProfileOfCurrentUserDto.java @@ -5,7 +5,6 @@ import net.dancier.dancer.core.model.Gender; import java.time.LocalDate; -import java.util.Date; import java.util.Set; import java.util.UUID; diff --git a/src/main/java/net/dancier/dancer/core/events/ApplicationEventListener.java b/src/main/java/net/dancier/dancer/core/events/ApplicationEventListener.java new file mode 100644 index 00000000..a485d9e8 --- /dev/null +++ b/src/main/java/net/dancier/dancer/core/events/ApplicationEventListener.java @@ -0,0 +1,42 @@ +package net.dancier.dancer.core.events; + +import lombok.RequiredArgsConstructor; +import net.dancier.dancer.core.ScheduleMessagePort; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.event.EventListener; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.stereotype.Component; + +import java.net.URI; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class ApplicationEventListener { + + public static final Logger log = LoggerFactory.getLogger(ApplicationEventListener.class); + private static final URI SOURCE = URI.create("http://dancer.dancier.net"); + + private final ScheduleMessagePort scheduleMessagePort; + + @EventListener + public void handle(SimpleMailMessage dancierMailMessage) { + scheduleMessagePort.schedule( + dancierMailMessage, + UUID.randomUUID().toString(), + SOURCE, + "email-sending-requested" + ); + } + + @EventListener + public void handle(ProfileUpdatedEvent profileUpdatedEvent) { + log.info("Got a Profile Change: {}", profileUpdatedEvent); + scheduleMessagePort.schedule( + profileUpdatedEvent, + profileUpdatedEvent.getDancer().getId().toString(), + SOURCE, + "profile-updated"); + } +} diff --git a/src/main/java/net/dancier/dancer/core/events/ProfileUpdateEventListener.java b/src/main/java/net/dancier/dancer/core/events/ProfileUpdateEventListener.java deleted file mode 100644 index 2984ce41..00000000 --- a/src/main/java/net/dancier/dancer/core/events/ProfileUpdateEventListener.java +++ /dev/null @@ -1,24 +0,0 @@ -package net.dancier.dancer.core.events; - -import lombok.RequiredArgsConstructor; -import net.dancier.dancer.eventlog.service.EventlogService; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class ProfileUpdateEventListener { - - private final EventlogService eventlogService; - - private final EventCreator eventCreator; - - @EventListener - public void handle(ProfileUpdatedEvent profileUpdatedEvent) { - eventlogService.appendNew( - eventCreator.createEventlog( - "profile-updated", - profileUpdatedEvent.getDancer())); - } - -} diff --git a/src/main/java/net/dancier/dancer/core/model/Address.java b/src/main/java/net/dancier/dancer/core/model/Address.java index 7db142e4..1c56ff3e 100644 --- a/src/main/java/net/dancier/dancer/core/model/Address.java +++ b/src/main/java/net/dancier/dancer/core/model/Address.java @@ -1,6 +1,6 @@ package net.dancier.dancer.core.model; -import javax.persistence.*; +import jakarta.persistence.*; import java.util.UUID; @Entity diff --git a/src/main/java/net/dancier/dancer/core/model/Dance.java b/src/main/java/net/dancier/dancer/core/model/Dance.java index c3e74fd8..5ef83fee 100644 --- a/src/main/java/net/dancier/dancer/core/model/Dance.java +++ b/src/main/java/net/dancier/dancer/core/model/Dance.java @@ -1,11 +1,10 @@ package net.dancier.dancer.core.model; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import javax.persistence.*; +import jakarta.persistence.*; import java.util.UUID; @Entity diff --git a/src/main/java/net/dancier/dancer/core/model/DanceProfile.java b/src/main/java/net/dancier/dancer/core/model/DanceProfile.java index bde01733..df8df8c6 100644 --- a/src/main/java/net/dancier/dancer/core/model/DanceProfile.java +++ b/src/main/java/net/dancier/dancer/core/model/DanceProfile.java @@ -3,7 +3,7 @@ import lombok.Data; import org.hibernate.annotations.Cascade; -import javax.persistence.*; +import jakarta.persistence.*; import java.util.UUID; @Data diff --git a/src/main/java/net/dancier/dancer/core/model/Dancer.java b/src/main/java/net/dancier/dancer/core/model/Dancer.java index 0e63a121..4a6b3627 100644 --- a/src/main/java/net/dancier/dancer/core/model/Dancer.java +++ b/src/main/java/net/dancier/dancer/core/model/Dancer.java @@ -1,12 +1,11 @@ package net.dancier.dancer.core.model; +import jakarta.persistence.*; import lombok.Data; import org.hibernate.annotations.Cascade; -import javax.persistence.*; import java.time.Instant; import java.time.LocalDate; -import java.util.Date; import java.util.HashSet; import java.util.Set; import java.util.UUID; @@ -20,6 +19,9 @@ public class Dancer implements Recommendable{ @GeneratedValue(strategy = GenerationType.AUTO) private UUID id; + @Version + private Integer version; + private UUID userId; private String dancerName; @@ -62,6 +64,5 @@ public class Dancer implements Recommendable{ private Instant updatedAt; - private Integer version; } diff --git a/src/main/java/net/dancier/dancer/eventlog/controller/EventlogController.java b/src/main/java/net/dancier/dancer/eventlog/controller/EventlogController.java index c75353f3..5d0b802c 100644 --- a/src/main/java/net/dancier/dancer/eventlog/controller/EventlogController.java +++ b/src/main/java/net/dancier/dancer/eventlog/controller/EventlogController.java @@ -25,16 +25,20 @@ public class EventlogController { private static Logger log = LoggerFactory.getLogger(EventlogController.class); + private final EventlogService eventlogService; + @PostMapping public ResponseEntity publish(@RequestBody NewEventlogDto newEventlogDto) { Eventlog eventlog = EventlogMapper.toEventlog(newEventlogDto); setRolesAndUser(eventlog); eventlogService.appendNew(eventlog); + log.info("Appended " + eventlog + " to the eventlog."); return ResponseEntity.ok().build(); } + private void setRolesAndUser(Eventlog eventlog) { switch (SecurityContextHolder.getContext().getAuthentication().getPrincipal()) { case AuthenticatedUser authenticatedUser -> { @@ -47,7 +51,7 @@ private void setRolesAndUser(Eventlog eventlog) { .collect(Collectors.toSet()) ); } - case default -> { + default -> { eventlog.setUserId(null); eventlog.setRoles(Set.of("ROLE_ANONYMOUS")); } diff --git a/src/main/java/net/dancier/dancer/eventlog/service/EventlogS3ServiceImpl.java b/src/main/java/net/dancier/dancer/eventlog/service/EventlogS3ServiceImpl.java index 40517aa2..ec2062b0 100644 --- a/src/main/java/net/dancier/dancer/eventlog/service/EventlogS3ServiceImpl.java +++ b/src/main/java/net/dancier/dancer/eventlog/service/EventlogS3ServiceImpl.java @@ -18,7 +18,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/net/dancier/dancer/eventlog/service/EventlogService.java b/src/main/java/net/dancier/dancer/eventlog/service/EventlogService.java index 84f96486..8b855d35 100644 --- a/src/main/java/net/dancier/dancer/eventlog/service/EventlogService.java +++ b/src/main/java/net/dancier/dancer/eventlog/service/EventlogService.java @@ -1,22 +1,53 @@ package net.dancier.dancer.eventlog.service; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; import lombok.RequiredArgsConstructor; +import net.dancier.dancer.core.ScheduleMessagePort; import net.dancier.dancer.core.exception.ApplicationException; +import net.dancier.dancer.core.exception.BusinessException; import net.dancier.dancer.eventlog.model.Eventlog; import net.dancier.dancer.eventlog.repository.EventlogDAO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; +import java.net.URI; import java.sql.SQLException; import java.time.Instant; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class EventlogService { + private final Logger log = LoggerFactory.getLogger(EventlogService.class); + private final EventlogDAO eventlogDAO; + private final ScheduleMessagePort scheduleMessagePort; + + public static final URI FRONTEND_SOURCE = URI.create("http://dancier.net"); + private final static Set AT_LEAST_HUMAN = Set.of("ROLE_HUMAN", "ROLE_USER", "ROLE_ADMIN"); + private final static Set NO_SPECIAL_ROLE_NEEDED = Set.of("ROLE_ANONYMOUS"); + private final static Set allowedEvents = Set.of( + EventlogConfig.of("app_instance_id_created", NO_SPECIAL_ROLE_NEEDED), + EventlogConfig.of("navigated_to_page", NO_SPECIAL_ROLE_NEEDED), + EventlogConfig.of("human_session_created", AT_LEAST_HUMAN), + EventlogConfig.of("contact_message_sent", AT_LEAST_HUMAN), + + EventlogConfig.of("app-instance-id-created", NO_SPECIAL_ROLE_NEEDED), + EventlogConfig.of("navigated-to-page", NO_SPECIAL_ROLE_NEEDED), + EventlogConfig.of("human-session-created", AT_LEAST_HUMAN), + EventlogConfig.of("contact-message-sent", AT_LEAST_HUMAN) + ); + public void appendNew(Eventlog eventlog) { + validateTopic(eventlog); + authorize(eventlog); try { eventlog.setId(UUID.randomUUID()); eventlog.setCreated(Instant.now()); @@ -24,6 +55,47 @@ public void appendNew(Eventlog eventlog) { } catch (SQLException sqlException) { throw new ApplicationException("Unable to create new Eventlog-Entry.", sqlException); } + log.info("Now scheduling..." + eventlog); + scheduleMessagePort.schedule( + eventlog, + eventlog.getId().toString(), + FRONTEND_SOURCE, + eventlog.getTopic().replaceAll("_", "-")); // as long halbe kanne is still sending old event format + } + + private void validateTopic(Eventlog eventlog) { + String topic = eventlog.getTopic(); + log.info("Validating Topic: {}", eventlog.getTopic()); + if (!allowedEvents.stream() + .map(EventlogConfig::getName) + .collect(Collectors.toSet()) + .contains(topic)) { + throw new BusinessException("this eventlog topic is not allowed at all: " + topic); + } + + } + private void authorize(Eventlog eventlog) { + String topic = eventlog.getTopic(); + Set usedRoles = eventlog.getRoles(); + Set neededRoles = allowedEvents + .stream() + .filter(eventlogConfig -> eventlogConfig.name.equals(topic)) + .flatMap(eventlogConfig -> eventlogConfig.neededRoles.stream()) + .collect(Collectors.toSet()); + log.info("Authorizing eventlog request: {}", topic); + Boolean authorized = usedRoles.stream().anyMatch(usedRole -> neededRoles.contains(usedRole)); + if (!authorized) { + throw new BusinessException("We got this roles: " + usedRoles + " but needed this:" + neededRoles); + } } + @AllArgsConstructor(access = AccessLevel.PRIVATE) + @Getter + private static class EventlogConfig { + public static EventlogConfig of(String name, Set roles) { + return new EventlogConfig(name, roles); + } + private String name; + private Set neededRoles; + } } \ No newline at end of file diff --git a/src/main/java/net/dancier/dancer/location/ZipCode.java b/src/main/java/net/dancier/dancer/location/ZipCode.java index 824648c5..a3b52d6d 100644 --- a/src/main/java/net/dancier/dancer/location/ZipCode.java +++ b/src/main/java/net/dancier/dancer/location/ZipCode.java @@ -2,7 +2,7 @@ import lombok.Data; -import javax.persistence.*; +import jakarta.persistence.*; import java.util.UUID; @Data diff --git a/src/main/java/net/dancier/dancer/mail/OutgoingMailProcessor.java b/src/main/java/net/dancier/dancer/mail/OutgoingMailProcessor.java deleted file mode 100644 index 826d67c8..00000000 --- a/src/main/java/net/dancier/dancer/mail/OutgoingMailProcessor.java +++ /dev/null @@ -1,54 +0,0 @@ -package net.dancier.dancer.mail; - -import lombok.RequiredArgsConstructor; -import net.dancier.dancer.mail.model.OutgoingMail; -import net.dancier.dancer.mail.model.OutgoingMailStatus; -import net.dancier.dancer.mail.repository.OutgoingMailRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Profile; -import org.springframework.mail.MailAuthenticationException; -import org.springframework.mail.MailException; -import org.springframework.mail.javamail.JavaMailSender; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -import java.util.Collection; - -@Profile("!test") -@Component -@RequiredArgsConstructor -public class OutgoingMailProcessor { - - private final Logger log = LoggerFactory.getLogger(OutgoingMailProcessor.class); - - private final OutgoingMailRepository outgoingMailRepository; - - private final JavaMailSender javaMailSender; - - @Scheduled(fixedRate = 10000) - public void process() { - log.debug("Processing outgoing mails..."); - Collection outgoingMailList = outgoingMailRepository.lockAndList(); - log.debug("Got this: " + outgoingMailList); - for(OutgoingMail outgoingMail: outgoingMailList) { - processOneMail(outgoingMail); - } - } - - private void processOneMail(OutgoingMail outgoingMail) { - log.debug("About to send this mail..."); - outgoingMail.setRetry(outgoingMail.getRetry() + 1); - try { - this.javaMailSender.send(outgoingMail.getMail()); - outgoingMail.setStatus(OutgoingMailStatus.OK); - } catch (MailAuthenticationException mailAuthenticationException) { - outgoingMail.setStatus(OutgoingMailStatus.TEMPORARY_FAILED); - log.error("Problem with password." + mailAuthenticationException); - } catch (MailException mailException) { - outgoingMail.setStatus(OutgoingMailStatus.FINALLY_FAILED); - log.error("Some: " + mailException); - } - outgoingMailRepository.save(outgoingMail); - } -} diff --git a/src/main/java/net/dancier/dancer/mail/configuration/AllForOneMailSender.java b/src/main/java/net/dancier/dancer/mail/configuration/AllForOneMailSender.java deleted file mode 100644 index c358cbd4..00000000 --- a/src/main/java/net/dancier/dancer/mail/configuration/AllForOneMailSender.java +++ /dev/null @@ -1,83 +0,0 @@ -package net.dancier.dancer.mail.configuration; - -import org.hibernate.cfg.NotYetImplementedException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.mail.MailException; -import org.springframework.mail.SimpleMailMessage; -import org.springframework.mail.javamail.JavaMailSender; -import org.springframework.mail.javamail.JavaMailSenderImpl; -import org.springframework.mail.javamail.MimeMessagePreparator; - -import javax.mail.internet.MimeMessage; -import java.io.InputStream; -import java.util.Properties; - -public class AllForOneMailSender implements JavaMailSender { - - private static final Logger log = LoggerFactory.getLogger(AllForOneMailSender.class); - - private final JavaMailSenderImpl springJavaMailSenderImpl; - - private final String allForOneAddress; - - public AllForOneMailSender(String hostname, Integer port, String user, String pass, String allForOneAddress) { - JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl(); - javaMailSender.setHost(hostname); - javaMailSender.setPort(Integer.valueOf(port)); - - javaMailSender.setUsername(user); - javaMailSender.setPassword(pass); - - Properties props = javaMailSender.getJavaMailProperties(); - props.put("mail.smtp.ssl.enable", "true"); - props.put("mail.debug", "true"); - this.allForOneAddress = allForOneAddress; - this.springJavaMailSenderImpl = javaMailSender; - } - - @Override - public void send(SimpleMailMessage simpleMessage) throws MailException { - simpleMessage.setBcc(); - simpleMessage.setCc(); - simpleMessage.setFrom("dev@dancier.net"); - simpleMessage.setReplyTo("dev@dancier.net"); - log.debug("Sending mail: " + simpleMessage); - this.springJavaMailSenderImpl.send(simpleMessage); - } - - @Override - public MimeMessage createMimeMessage() { - throw new NotYetImplementedException(); - } - - @Override - public MimeMessage createMimeMessage(InputStream contentStream) throws MailException { - throw new NotYetImplementedException(); - } - - @Override - public void send(MimeMessage mimeMessage) throws MailException { - throw new NotYetImplementedException(); - } - - @Override - public void send(MimeMessage... mimeMessages) throws MailException { - throw new NotYetImplementedException(); - } - - @Override - public void send(MimeMessagePreparator mimeMessagePreparator) throws MailException { - throw new NotYetImplementedException(); - } - - @Override - public void send(MimeMessagePreparator... mimeMessagePreparators) throws MailException { - throw new NotYetImplementedException(); - } - - @Override - public void send(SimpleMailMessage... simpleMessages) throws MailException { - throw new NotYetImplementedException(); - } -} diff --git a/src/main/java/net/dancier/dancer/mail/configuration/DumpingMailSender.java b/src/main/java/net/dancier/dancer/mail/configuration/DumpingMailSender.java deleted file mode 100644 index 83bfc105..00000000 --- a/src/main/java/net/dancier/dancer/mail/configuration/DumpingMailSender.java +++ /dev/null @@ -1,59 +0,0 @@ -package net.dancier.dancer.mail.configuration; - -import org.hibernate.cfg.NotYetImplementedException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.mail.MailException; -import org.springframework.mail.SimpleMailMessage; -import org.springframework.mail.javamail.JavaMailSender; -import org.springframework.mail.javamail.JavaMailSenderImpl; -import org.springframework.mail.javamail.MimeMessagePreparator; - -import javax.mail.internet.MimeMessage; -import java.io.InputStream; -import java.util.Properties; - -public class DumpingMailSender implements JavaMailSender { - - private static final Logger log = LoggerFactory.getLogger(DumpingMailSender.class); - - @Override - public MimeMessage createMimeMessage() { - throw new NotYetImplementedException(); - } - - @Override - public void send(SimpleMailMessage simpleMessage) throws MailException { - log.debug("Sending Mail: " + simpleMessage); - } - - @Override - public MimeMessage createMimeMessage(InputStream contentStream) throws MailException { - throw new NotYetImplementedException(); - } - - @Override - public void send(MimeMessage mimeMessage) throws MailException { - throw new NotYetImplementedException(); - } - - @Override - public void send(MimeMessage... mimeMessages) throws MailException { - throw new NotYetImplementedException(); - } - - @Override - public void send(MimeMessagePreparator mimeMessagePreparator) throws MailException { - throw new NotYetImplementedException(); - } - - @Override - public void send(MimeMessagePreparator... mimeMessagePreparators) throws MailException { - throw new NotYetImplementedException(); - } - - @Override - public void send(SimpleMailMessage... simpleMessages) throws MailException { - throw new NotYetImplementedException(); - } -} diff --git a/src/main/java/net/dancier/dancer/mail/configuration/MailSenderConfiguration.java b/src/main/java/net/dancier/dancer/mail/configuration/MailSenderConfiguration.java deleted file mode 100644 index fa15b71d..00000000 --- a/src/main/java/net/dancier/dancer/mail/configuration/MailSenderConfiguration.java +++ /dev/null @@ -1,60 +0,0 @@ -package net.dancier.dancer.mail.configuration; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.mail.javamail.JavaMailSender; -import org.springframework.mail.javamail.JavaMailSenderImpl; - -import java.util.Properties; - -@Configuration -public class MailSenderConfiguration { - - @Bean("javaMailSender") - @Profile("prod") - public JavaMailSender getSpringJavaMailSender( - @Value("${app.mail.host}") String hostname, - @Value("${app.mail.port}") String port, - @Value("${app.mail.user}") String user, - @Value("${app.mail.pass}") String pass - ) { - JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl(); - javaMailSender.setHost(hostname); - javaMailSender.setPort(Integer.valueOf(port)); - - javaMailSender.setUsername(user); - javaMailSender.setPassword(pass); - - Properties props = javaMailSender.getJavaMailProperties(); - props.put("mail.smtp.ssl.enable", "true"); - props.put("mail.debug", "true"); - - return javaMailSender; - } - - @Bean("javaMailSender") - @Profile("staging") - public JavaMailSender getAllForOneJavaMailSender( - @Value("${app.mail.host}") String hostname, - @Value("${app.mail.port}") String port, - @Value("${app.mail.user}") String user, - @Value("${app.mail.pass}") String pass, - @Value("${app.mail.allForOneAddress}") String allForOneAddress - ) { - JavaMailSender javaMailSender = new AllForOneMailSender( - hostname, - Integer.valueOf(port), - user, - pass, - allForOneAddress); - return javaMailSender; - } - - @Bean("javaMailSender") - @Profile({"dev", "it"}) - public JavaMailSender getDumpingOnlySender() { - return new DumpingMailSender(); - } -} diff --git a/src/main/java/net/dancier/dancer/mail/configuration/MailTemplateConfiguration.java b/src/main/java/net/dancier/dancer/mail/configuration/MailTemplateConfiguration.java index b5b0d13b..78b93ef1 100644 --- a/src/main/java/net/dancier/dancer/mail/configuration/MailTemplateConfiguration.java +++ b/src/main/java/net/dancier/dancer/mail/configuration/MailTemplateConfiguration.java @@ -3,7 +3,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.thymeleaf.TemplateEngine; -import org.thymeleaf.spring5.SpringTemplateEngine; +import org.thymeleaf.spring6.SpringTemplateEngine; import org.thymeleaf.templatemode.TemplateMode; import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; import org.thymeleaf.templateresolver.ITemplateResolver; @@ -26,7 +26,7 @@ private ITemplateResolver textTemplateResolver() { final ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver(); templateResolver.setOrder(Integer.valueOf(1)); templateResolver.setResolvablePatterns(Collections.singleton("text/*")); - templateResolver.setPrefix("/mail/"); + templateResolver.setPrefix("/templates/mail/"); templateResolver.setSuffix(".txt"); templateResolver.setTemplateMode(TemplateMode.TEXT); templateResolver.setCharacterEncoding(EMAIL_TEMPLATE_ENCODING); diff --git a/src/main/java/net/dancier/dancer/mail/model/DancierMailMessage.java b/src/main/java/net/dancier/dancer/mail/model/DancierMailMessage.java deleted file mode 100644 index f0dcd3da..00000000 --- a/src/main/java/net/dancier/dancer/mail/model/DancierMailMessage.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.dancier.dancer.mail.model; - -import org.springframework.mail.SimpleMailMessage; - -public class DancierMailMessage extends SimpleMailMessage { - - public void setTo(String[] in) { - super.setTo(in); - } -} diff --git a/src/main/java/net/dancier/dancer/mail/model/EntityWithUUID.java b/src/main/java/net/dancier/dancer/mail/model/EntityWithUUID.java deleted file mode 100644 index 46ffb764..00000000 --- a/src/main/java/net/dancier/dancer/mail/model/EntityWithUUID.java +++ /dev/null @@ -1,24 +0,0 @@ -package net.dancier.dancer.mail.model; - -import com.vladmihalcea.hibernate.type.json.JsonBinaryType; -import org.hibernate.annotations.Type; -import org.hibernate.annotations.TypeDef; -import org.hibernate.annotations.TypeDefs; - -import javax.persistence.Id; -import javax.persistence.MappedSuperclass; -import java.util.UUID; - -@MappedSuperclass -@TypeDefs({ - @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) -}) -public class EntityWithUUID { - @Id - @Type(type = "pg-uuid") - private UUID id; - - public EntityWithUUID() { - this.id = UUID.randomUUID(); - } -} \ No newline at end of file diff --git a/src/main/java/net/dancier/dancer/mail/model/OutgoingMail.java b/src/main/java/net/dancier/dancer/mail/model/OutgoingMail.java deleted file mode 100644 index 1257e4ba..00000000 --- a/src/main/java/net/dancier/dancer/mail/model/OutgoingMail.java +++ /dev/null @@ -1,25 +0,0 @@ -package net.dancier.dancer.mail.model; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.hibernate.annotations.Type; - -import javax.persistence.*; -import java.io.Serializable; - -@Entity -@Data -@AllArgsConstructor -@NoArgsConstructor -public class OutgoingMail extends EntityWithUUID implements Serializable { - - @Enumerated(EnumType.STRING) - private OutgoingMailStatus status; - private Integer retry; - - @Type(type = "jsonb") - @Column(columnDefinition = "jsonb") - @Basic(fetch = FetchType.EAGER) - private DancierMailMessage mail; -} diff --git a/src/main/java/net/dancier/dancer/mail/model/OutgoingMailStatus.java b/src/main/java/net/dancier/dancer/mail/model/OutgoingMailStatus.java deleted file mode 100644 index e42f4cfe..00000000 --- a/src/main/java/net/dancier/dancer/mail/model/OutgoingMailStatus.java +++ /dev/null @@ -1,9 +0,0 @@ -package net.dancier.dancer.mail.model; - -public enum OutgoingMailStatus { - QUEUED, - IN_PROGRESS, - TEMPORARY_FAILED, - FINALLY_FAILED, - OK -} diff --git a/src/main/java/net/dancier/dancer/mail/repository/OutgoingMailRepository.java b/src/main/java/net/dancier/dancer/mail/repository/OutgoingMailRepository.java deleted file mode 100644 index 3acb35d2..00000000 --- a/src/main/java/net/dancier/dancer/mail/repository/OutgoingMailRepository.java +++ /dev/null @@ -1,27 +0,0 @@ -package net.dancier.dancer.mail.repository; - -import net.dancier.dancer.mail.model.OutgoingMail; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.CrudRepository; - -import java.util.Collection; -import java.util.UUID; - -public interface OutgoingMailRepository extends CrudRepository { - @Query( - value = """ - UPDATE outgoing_mail - SET status = 'IN_PROGRESS' - WHERE id IN ( - SELECT id - FROM outgoing_mail - WHERE status = 'QUEUED' - LIMIT 1 - FOR UPDATE - ) - RETURNING *; - """, - nativeQuery = true - ) - Collection lockAndList(); -} diff --git a/src/main/java/net/dancier/dancer/mail/service/MailCreationService.java b/src/main/java/net/dancier/dancer/mail/service/MailCreationService.java index ca84f370..119eeb90 100644 --- a/src/main/java/net/dancier/dancer/mail/service/MailCreationService.java +++ b/src/main/java/net/dancier/dancer/mail/service/MailCreationService.java @@ -1,7 +1,6 @@ package net.dancier.dancer.mail.service; import lombok.RequiredArgsConstructor; -import net.dancier.dancer.mail.model.DancierMailMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.mail.SimpleMailMessage; @@ -32,14 +31,14 @@ public class MailCreationService { private final TemplateEngine emailTemplateEngine; - public DancierMailMessage createDancierMessageFromTemplate( + public SimpleMailMessage createDancierMessageFromTemplate( String to, String from, String subject, String bodyTemplate, Map context) { - final DancierMailMessage dancierMailMessage = new DancierMailMessage(); + final SimpleMailMessage dancierMailMessage = new SimpleMailMessage(); dancierMailMessage.setFrom(from); dancierMailMessage.setTo(to); dancierMailMessage.setSubject(subject); diff --git a/src/main/java/net/dancier/dancer/mail/service/MailEnqueueService.java b/src/main/java/net/dancier/dancer/mail/service/MailEnqueueService.java deleted file mode 100644 index 8afe08a9..00000000 --- a/src/main/java/net/dancier/dancer/mail/service/MailEnqueueService.java +++ /dev/null @@ -1,29 +0,0 @@ -package net.dancier.dancer.mail.service; - -import lombok.RequiredArgsConstructor; -import net.dancier.dancer.mail.model.DancierMailMessage; -import net.dancier.dancer.mail.repository.OutgoingMailRepository; -import net.dancier.dancer.mail.model.OutgoingMail; -import net.dancier.dancer.mail.model.OutgoingMailStatus; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.mail.SimpleMailMessage; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class MailEnqueueService { - - private static Logger log = LoggerFactory.getLogger(MailEnqueueService.class); - - private final OutgoingMailRepository outgoingMailRepository; - - public void enqueueMail(DancierMailMessage dancierMailMessage) { - OutgoingMail outgoingMail = new OutgoingMail(); - outgoingMail.setStatus(OutgoingMailStatus.QUEUED); - outgoingMail.setRetry(0); - outgoingMail.setMail(dancierMailMessage); - this.outgoingMailRepository.save(outgoingMail); - } - -} diff --git a/src/main/java/net/dancier/dancer/core/config/KafkaProducerConfiguration.java b/src/main/java/net/dancier/dancer/messaging/KafkaConfiguration.java similarity index 75% rename from src/main/java/net/dancier/dancer/core/config/KafkaProducerConfiguration.java rename to src/main/java/net/dancier/dancer/messaging/KafkaConfiguration.java index 064c9504..a1dd2c2e 100644 --- a/src/main/java/net/dancier/dancer/core/config/KafkaProducerConfiguration.java +++ b/src/main/java/net/dancier/dancer/messaging/KafkaConfiguration.java @@ -1,5 +1,7 @@ -package net.dancier.dancer.core.config; +package net.dancier.dancer.messaging; +import io.cloudevents.CloudEvent; +import io.cloudevents.kafka.CloudEventSerializer; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.serialization.StringSerializer; import org.slf4j.Logger; @@ -15,24 +17,24 @@ import java.util.Map; @Configuration -public class KafkaProducerConfiguration { +public class KafkaConfiguration { - private static Logger log = LoggerFactory.getLogger(KafkaProducerConfiguration.class); + private static Logger log = LoggerFactory.getLogger(KafkaConfiguration.class); @Value(value = "${spring.kafka.bootstrap-servers}") private String bootstrapAddress; @Bean - ProducerFactory producerFactory() { + ProducerFactory producerFactory() { Map configProps = new HashMap<>(); configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress); configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); - configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, CloudEventSerializer.class); configProps.put(ProducerConfig.MAX_REQUEST_SIZE_CONFIG, "20971520"); return new DefaultKafkaProducerFactory<>(configProps); } @Bean - public KafkaTemplate kafkaTemplate() { + public KafkaTemplate kafkaTemplate() { return new KafkaTemplate<>(producerFactory()); } diff --git a/src/main/java/net/dancier/dancer/messaging/OutboxJpaEntity.java b/src/main/java/net/dancier/dancer/messaging/OutboxJpaEntity.java new file mode 100644 index 00000000..88ae7406 --- /dev/null +++ b/src/main/java/net/dancier/dancer/messaging/OutboxJpaEntity.java @@ -0,0 +1,45 @@ +package net.dancier.dancer.messaging; + +import com.fasterxml.jackson.databind.JsonNode; +import jakarta.persistence.*; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; + +import java.time.OffsetDateTime; +import java.util.UUID; + +@Data +@Entity +@Table(name = "outbox") +@NoArgsConstructor +public class OutboxJpaEntity { + + @Id + @GeneratedValue + private UUID id; + + private String type; + + private String source; + + private String key; + + @JdbcTypeCode(SqlTypes.JSON) + private JsonNode data; + + private OffsetDateTime createdAt; + + @Enumerated(EnumType.STRING) + private STATUS status; + + public static enum STATUS { + NEW, + IN_PROGRESS, + TEMP_FAILED, + FINALLY_FAILED, + DONE + } + +} diff --git a/src/main/java/net/dancier/dancer/messaging/OutboxJpaRepository.java b/src/main/java/net/dancier/dancer/messaging/OutboxJpaRepository.java new file mode 100644 index 00000000..e84de7c5 --- /dev/null +++ b/src/main/java/net/dancier/dancer/messaging/OutboxJpaRepository.java @@ -0,0 +1,27 @@ +package net.dancier.dancer.messaging; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.Collection; +import java.util.UUID; + +public interface OutboxJpaRepository extends JpaRepository { + + @Query( + value = """ + UPDATE outbox + SET status = 'IN_PROGRESS' + WHERE id IN ( + SELECT id + FROM outbox + WHERE status = 'NEW' + LIMIT 1 + FOR UPDATE + ) + RETURNING *; """, + nativeQuery = true + ) + Collection lockAndList(); + +} diff --git a/src/main/java/net/dancier/dancer/messaging/ProfileUpdatedEventDto.java b/src/main/java/net/dancier/dancer/messaging/ProfileUpdatedEventDto.java new file mode 100644 index 00000000..6614dd87 --- /dev/null +++ b/src/main/java/net/dancier/dancer/messaging/ProfileUpdatedEventDto.java @@ -0,0 +1,4 @@ +package net.dancier.dancer.messaging; + +public class ProfileUpdatedEventDto { +} diff --git a/src/main/java/net/dancier/dancer/messaging/ScheduleMessageAdapter.java b/src/main/java/net/dancier/dancer/messaging/ScheduleMessageAdapter.java new file mode 100644 index 00000000..31ea7673 --- /dev/null +++ b/src/main/java/net/dancier/dancer/messaging/ScheduleMessageAdapter.java @@ -0,0 +1,43 @@ +package net.dancier.dancer.messaging; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import net.dancier.dancer.core.ScheduleMessagePort; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationContextException; +import org.springframework.stereotype.Component; + +import java.net.URI; +import java.time.OffsetDateTime; + +@RequiredArgsConstructor +@Component +public class ScheduleMessageAdapter implements ScheduleMessagePort { + + public static final Logger log = LoggerFactory.getLogger(ScheduleMessageAdapter.class); + + private final OutboxJpaRepository outboxJpaRepository; + + private final ObjectMapper objectMapper; + + @Override + public void schedule(Object object, + String key, + URI source, + String type) { + log.info("sending object: " + object); + log.info("with key:" + key); + + OutboxJpaEntity outboxJpaEntity = new OutboxJpaEntity(); + outboxJpaEntity.setData(objectMapper.convertValue(object, JsonNode.class)); + outboxJpaEntity.setType(type); + outboxJpaEntity.setSource(source.toString()); + outboxJpaEntity.setKey(key); + outboxJpaEntity.setCreatedAt(OffsetDateTime.now()); + outboxJpaEntity.setStatus(OutboxJpaEntity.STATUS.NEW); + outboxJpaRepository.save(outboxJpaEntity); + } +} diff --git a/src/main/java/net/dancier/dancer/messaging/SendMessagesJob.java b/src/main/java/net/dancier/dancer/messaging/SendMessagesJob.java new file mode 100644 index 00000000..4fdf97c7 --- /dev/null +++ b/src/main/java/net/dancier/dancer/messaging/SendMessagesJob.java @@ -0,0 +1,53 @@ +package net.dancier.dancer.messaging; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.cloudevents.CloudEvent; +import io.cloudevents.core.builder.CloudEventBuilder; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.net.URI; +import java.util.Collection; + +@RequiredArgsConstructor +@Component +public class SendMessagesJob { + + private final static Logger log = LoggerFactory.getLogger(SendMessagesJob.class); + + private final OutboxJpaRepository outboxJpaRepository; + + private final KafkaTemplate kafkaTemplate; + + private final ObjectMapper objectMapper; + + @Transactional + @Scheduled(fixedRate = 2000) + public void sendMessages() throws JsonProcessingException { + Collection itemsToSend = outboxJpaRepository.lockAndList(); + for (OutboxJpaEntity item: itemsToSend) { + log.info("Sending: {}", item); + send(item); + item.setStatus(OutboxJpaEntity.STATUS.DONE); + } + kafkaTemplate.flush(); + } + + private void send(OutboxJpaEntity item) throws JsonProcessingException { + CloudEvent cloudEvent = CloudEventBuilder.v1() + .withId(item.getId().toString()) + .withSource(URI.create(item.getSource())) + .withType(item.getType()) + .withData(objectMapper.writeValueAsBytes(item.getData())) + .build(); + + kafkaTemplate.send(item.getType(), item.getKey(), cloudEvent); + + } +} diff --git a/src/main/java/net/dancier/dancer/messaging/TopicConfiguration.java b/src/main/java/net/dancier/dancer/messaging/TopicConfiguration.java new file mode 100644 index 00000000..4a93ead3 --- /dev/null +++ b/src/main/java/net/dancier/dancer/messaging/TopicConfiguration.java @@ -0,0 +1,29 @@ +package net.dancier.dancer.messaging; + +import org.apache.kafka.clients.admin.NewTopic; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.core.KafkaAdmin; + +@Configuration +public class TopicConfiguration { + @Bean + public KafkaAdmin.NewTopics createTopics() { + + NewTopic profileUpdated = new NewTopic("profile-updated", 1, (short) 1); + NewTopic appInstanceIdCreated = new NewTopic("app-instance-id-created", 1, (short) 1); + NewTopic navigatedToPage = new NewTopic("navigated-to-page", 1, (short) 1); + NewTopic humanSessionCreated = new NewTopic("human-session-created", 1, (short) 1); + NewTopic contactMessageSent = new NewTopic("contact-message-sent", 1, (short) 1); + NewTopic emailSendingRequested = new NewTopic("email-sending-requested", 1, (short) 1); + return new KafkaAdmin.NewTopics( + profileUpdated, + appInstanceIdCreated, + navigatedToPage, + humanSessionCreated, + contactMessageSent, + emailSendingRequested); + } + +} diff --git a/src/main/java/net/dancier/dancer/messaging/UserCreatedEventDto.java b/src/main/java/net/dancier/dancer/messaging/UserCreatedEventDto.java new file mode 100644 index 00000000..f7de51a3 --- /dev/null +++ b/src/main/java/net/dancier/dancer/messaging/UserCreatedEventDto.java @@ -0,0 +1,4 @@ +package net.dancier.dancer.messaging; + +public class UserCreatedEventDto { +} diff --git a/src/main/java/net/dancier/dancer/recommendation/RecommendationService.java b/src/main/java/net/dancier/dancer/recommendation/RecommendationService.java index ba58250e..f1256c9d 100644 --- a/src/main/java/net/dancier/dancer/recommendation/RecommendationService.java +++ b/src/main/java/net/dancier/dancer/recommendation/RecommendationService.java @@ -3,16 +3,12 @@ import lombok.RequiredArgsConstructor; import net.dancier.dancer.core.DancerRepository; import net.dancier.dancer.core.model.Dancer; -import net.dancier.dancer.core.model.Recommendable; -import net.dancier.dancer.recommendation.dto.RecommendationDto; import net.dancier.dancer.recommendation.model.BaseRecommendation; import net.dancier.dancer.recommendation.model.RecommendationWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; -import javax.persistence.criteria.CriteriaBuilder; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; diff --git a/src/main/java/net/dancier/dancer/recommendation/RecommendationServiceClient.java b/src/main/java/net/dancier/dancer/recommendation/RecommendationServiceClient.java index 664f7f06..62d5ec1e 100644 --- a/src/main/java/net/dancier/dancer/recommendation/RecommendationServiceClient.java +++ b/src/main/java/net/dancier/dancer/recommendation/RecommendationServiceClient.java @@ -7,7 +7,7 @@ import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.client.WebClient; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; import java.util.Arrays; import java.util.List; import java.util.UUID; diff --git a/src/main/java/net/dancier/dancer/recommendation/dto/Mapper.java b/src/main/java/net/dancier/dancer/recommendation/dto/Mapper.java index 8e87c8b7..5ab8bd01 100644 --- a/src/main/java/net/dancier/dancer/recommendation/dto/Mapper.java +++ b/src/main/java/net/dancier/dancer/recommendation/dto/Mapper.java @@ -45,7 +45,7 @@ public static ExposedRecommendationDto recommendationWrapper2ExposedRecommendati dancerPayload.setScore(recommendationWrapper.getScore()); exposedRecommendationDto.setPayload(dancerPayload); } - case default -> { + default -> { throw new IllegalStateException(); } } diff --git a/src/main/java/net/dancier/dancer/school/School.java b/src/main/java/net/dancier/dancer/school/School.java index fc552b9b..f54f3cd6 100644 --- a/src/main/java/net/dancier/dancer/school/School.java +++ b/src/main/java/net/dancier/dancer/school/School.java @@ -5,7 +5,7 @@ import net.dancier.dancer.core.model.Dance; import net.dancier.dancer.core.model.Recommendable; -import javax.persistence.*; +import jakarta.persistence.*; import java.util.Set; import java.util.UUID; diff --git a/src/main/java/net/dancier/dancer/security/AuthenticatedUser.java b/src/main/java/net/dancier/dancer/security/AuthenticatedUser.java index 00f7693b..05c75abf 100644 --- a/src/main/java/net/dancier/dancer/security/AuthenticatedUser.java +++ b/src/main/java/net/dancier/dancer/security/AuthenticatedUser.java @@ -57,6 +57,10 @@ public UUID getUserId() { return userId; } + public String getEmail() { + return this.email; + } + public boolean isEmailValidated() { return isEmailValidated; } diff --git a/src/main/java/net/dancier/dancer/security/CustomUserDetailsServiceImpl.java b/src/main/java/net/dancier/dancer/security/CustomUserDetailsServiceImpl.java index 2b2eb6e7..978455b9 100644 --- a/src/main/java/net/dancier/dancer/security/CustomUserDetailsServiceImpl.java +++ b/src/main/java/net/dancier/dancer/security/CustomUserDetailsServiceImpl.java @@ -1,16 +1,14 @@ package net.dancier.dancer.security; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import net.dancier.dancer.authentication.model.User; import net.dancier.dancer.authentication.repository.UserRepository; import net.dancier.dancer.core.DancerRepository; import net.dancier.dancer.core.model.Dancer; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; -import javax.transaction.Transactional; import java.util.UUID; @Service diff --git a/src/main/java/net/dancier/dancer/security/JwtAuthenticationEntryPoint.java b/src/main/java/net/dancier/dancer/security/JwtAuthenticationEntryPoint.java index 49e14c79..bfaf737d 100644 --- a/src/main/java/net/dancier/dancer/security/JwtAuthenticationEntryPoint.java +++ b/src/main/java/net/dancier/dancer/security/JwtAuthenticationEntryPoint.java @@ -6,9 +6,9 @@ import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @Component @@ -23,4 +23,5 @@ public void commence(HttpServletRequest request, log.error("Problem while trying to authenticate: " + authException.getMessage()); response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage()); } + } diff --git a/src/main/java/net/dancier/dancer/security/JwtAuthenticationFilter.java b/src/main/java/net/dancier/dancer/security/JwtAuthenticationFilter.java index e5aeedc1..3aaaf8d2 100644 --- a/src/main/java/net/dancier/dancer/security/JwtAuthenticationFilter.java +++ b/src/main/java/net/dancier/dancer/security/JwtAuthenticationFilter.java @@ -14,11 +14,11 @@ import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Collection; import java.util.List; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6198d8f5..d2abca0a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -2,13 +2,13 @@ spring: profiles: active: dev kafka: - bootstrap-servers: kafka:9092 + bootstrap-servers: localhost:9092 jpa: show-sql: false hibernate: ddl-auto: validate datasource: - url: jdbc:postgresql://dancer-db:5432/dancer + url: jdbc:postgresql://localhost:5432/dancer username: dancer password: dancer liquibase: @@ -35,12 +35,6 @@ app: cors: allowedOrigins: http://localhost:4200 # Comma separated list of allowed origins # differentiate between success and failure - mail: - host: mail.your-server.de - port: 465 - user: user - pass: pass - allForOneAddress: dev@dancier.net file: dir: ./images-dir captcha: diff --git a/src/main/resources/liquibase-changeLog.xml b/src/main/resources/liquibase-changeLog.xml index ab139570..aa27f783 100644 --- a/src/main/resources/liquibase-changeLog.xml +++ b/src/main/resources/liquibase-changeLog.xml @@ -543,4 +543,19 @@ ADD COLUMN version INTEGER; + + + CREATE TABLE IF NOT EXISTS outbox + ( + id uuid NOT NULL, + type character varying(256) NOT NULL, + data jsonb NOT NULL, + created_at timestamp without time zone NOT NULL, + status character varying(256) NOT NULL, + key character varying(1024), + source character varying(256) NOT NULL + ) + + + \ No newline at end of file diff --git a/src/main/resources/mail/text/contact-formular-feedback.txt b/src/main/resources/templates/mail/text/contact-formular-feedback.txt similarity index 100% rename from src/main/resources/mail/text/contact-formular-feedback.txt rename to src/main/resources/templates/mail/text/contact-formular-feedback.txt diff --git a/src/main/resources/mail/text/contact-formular.txt b/src/main/resources/templates/mail/text/contact-formular.txt similarity index 100% rename from src/main/resources/mail/text/contact-formular.txt rename to src/main/resources/templates/mail/text/contact-formular.txt diff --git a/src/main/resources/mail/text/new-user-already-exists-email.txt b/src/main/resources/templates/mail/text/new-user-already-exists-email.txt similarity index 100% rename from src/main/resources/mail/text/new-user-already-exists-email.txt rename to src/main/resources/templates/mail/text/new-user-already-exists-email.txt diff --git a/src/main/resources/mail/text/new-user-validate-email.txt b/src/main/resources/templates/mail/text/new-user-validate-email.txt similarity index 100% rename from src/main/resources/mail/text/new-user-validate-email.txt rename to src/main/resources/templates/mail/text/new-user-validate-email.txt diff --git a/src/main/resources/mail/text/password-change-request-email.txt b/src/main/resources/templates/mail/text/password-change-request-email.txt similarity index 100% rename from src/main/resources/mail/text/password-change-request-email.txt rename to src/main/resources/templates/mail/text/password-change-request-email.txt diff --git a/src/test/java/net/dancier/dancer/TestDatabaseHelper.java b/src/test/java/net/dancier/dancer/TestDatabaseHelper.java index d1ec8f29..38734ca1 100644 --- a/src/test/java/net/dancier/dancer/TestDatabaseHelper.java +++ b/src/test/java/net/dancier/dancer/TestDatabaseHelper.java @@ -4,7 +4,7 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.util.UUID; /** diff --git a/src/test/java/net/dancier/dancer/authentication/AuthenticationControllerTest.java b/src/test/java/net/dancier/dancer/authentication/AuthenticationControllerTest.java index b8a20502..b2e924f9 100644 --- a/src/test/java/net/dancier/dancer/authentication/AuthenticationControllerTest.java +++ b/src/test/java/net/dancier/dancer/authentication/AuthenticationControllerTest.java @@ -1,6 +1,7 @@ package net.dancier.dancer.authentication; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.Cookie; import net.dancier.dancer.AbstractPostgreSQLEnabledTest; import net.dancier.dancer.authentication.dto.RegisterRequestDto; import net.dancier.dancer.authentication.model.User; @@ -10,15 +11,11 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.HttpHeaders; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; -import javax.servlet.http.Cookie; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; diff --git a/src/test/java/net/dancier/dancer/authentication/AuthenticationServiceTest.java b/src/test/java/net/dancier/dancer/authentication/AuthenticationServiceTest.java index 767f7840..77e77f5a 100644 --- a/src/test/java/net/dancier/dancer/authentication/AuthenticationServiceTest.java +++ b/src/test/java/net/dancier/dancer/authentication/AuthenticationServiceTest.java @@ -10,7 +10,6 @@ import net.dancier.dancer.authentication.service.AuthenticationService; import net.dancier.dancer.core.exception.NotFoundException; import net.dancier.dancer.mail.service.MailCreationService; -import net.dancier.dancer.mail.service.MailEnqueueService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -19,9 +18,10 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.context.ApplicationEventPublisher; +import org.springframework.mail.SimpleMailMessage; import org.springframework.security.crypto.password.PasswordEncoder; -import javax.persistence.EntityNotFoundException; +import jakarta.persistence.EntityNotFoundException; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Optional; @@ -49,9 +49,6 @@ class AuthenticationServiceTest { @Mock private MailCreationService mailCreationService; - @Mock - private MailEnqueueService mailEnqueueService; - @Mock private EmailValidationCodeRepository validationCodeRepositoryMock; @@ -105,9 +102,11 @@ void mailIsEnqueudWhenAccountAlreadyExists() { dummyUser().getEmail())) .thenReturn(Optional.of(dummyUser(true))); + when(mailCreationService.createDancierMessageFromTemplate(any(), any(), any(), any(), any())).thenReturn(new SimpleMailMessage()); + underTest.registerUser(dummyRegisterRequestDto(dummyUser())); - verify(mailEnqueueService, times(1)).enqueueMail(any()); + verify(applicationEventPublisherMock, times(1)).publishEvent(any(SimpleMailMessage.class)); } @Test diff --git a/src/test/java/net/dancier/dancer/authentication/EndToEndAuthenticationTest.java b/src/test/java/net/dancier/dancer/authentication/EndToEndAuthenticationTest.java index 162e3a8d..ed73b240 100644 --- a/src/test/java/net/dancier/dancer/authentication/EndToEndAuthenticationTest.java +++ b/src/test/java/net/dancier/dancer/authentication/EndToEndAuthenticationTest.java @@ -1,31 +1,33 @@ package net.dancier.dancer.authentication; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.Cookie; import net.dancier.dancer.AbstractPostgreSQLEnabledTest; import net.dancier.dancer.TestDatabaseHelper; import net.dancier.dancer.authentication.dto.RegisterRequestDto; import net.dancier.dancer.authentication.dto.SendLinkDto; import net.dancier.dancer.authentication.model.User; import net.dancier.dancer.core.controller.payload.LoginRequestDto; -import net.dancier.dancer.mail.service.MailEnqueueService; import net.dancier.dancer.security.JwtTokenProvider; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.ApplicationEvent; import org.springframework.http.MediaType; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.test.context.event.ApplicationEvents; +import org.springframework.test.context.event.RecordApplicationEvents; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -import javax.servlet.http.Cookie; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.times; +import static org.assertj.core.api.BDDAssertions.then; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +@RecordApplicationEvents public class EndToEndAuthenticationTest extends AbstractPostgreSQLEnabledTest { @Autowired @@ -37,8 +39,8 @@ public class EndToEndAuthenticationTest extends AbstractPostgreSQLEnabledTest { @Autowired private JwtTokenProvider jwtTokenProvider; - @MockBean - private MailEnqueueService mailEnqueueService; + @Autowired + ApplicationEvents applicationEvents; @Test void registrationHappyPath() throws Exception { @@ -82,8 +84,7 @@ void registrationOfAlreadyExistingAccount() throws Exception { registerUser(dummyUser) .andExpect(status().isCreated()); - Mockito.verify(mailEnqueueService, times(2)).enqueueMail(any()); - + then(applicationEvents.stream(SimpleMailMessage.class).count()).isEqualTo(2); } @Test diff --git a/src/test/java/net/dancier/dancer/chat/ChatControllerTest.java b/src/test/java/net/dancier/dancer/chat/ChatControllerTest.java index 1d9e38f8..8d49f02d 100644 --- a/src/test/java/net/dancier/dancer/chat/ChatControllerTest.java +++ b/src/test/java/net/dancier/dancer/chat/ChatControllerTest.java @@ -179,10 +179,8 @@ void getMessagesShouldReturnMessagesIfUserIsInChat() throws Exception { ChatDto chat = new ChatDto(); chat.setParticipantIds(List.of(dancerId, UUID.randomUUID())); - MessagesDto messages = new MessagesDto(); MessageDto message = new MessageDto(); message.setText("Hallo"); - messages.setMessages(List.of(message)); when(chatServiceClient.getChat(chatId)).thenReturn(chat); when(chatServiceClient.getMessages(chatId, dancerId, Optional.empty())).thenReturn(new MessageDto[]{message}); diff --git a/src/test/java/net/dancier/dancer/core/EndToEndProfileTest.java b/src/test/java/net/dancier/dancer/core/EndToEndProfileTest.java index bf805b73..37c4e7ea 100644 --- a/src/test/java/net/dancier/dancer/core/EndToEndProfileTest.java +++ b/src/test/java/net/dancier/dancer/core/EndToEndProfileTest.java @@ -11,6 +11,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; +import org.springframework.kafka.test.context.EmbeddedKafka; import org.springframework.security.test.context.support.WithUserDetails; import org.springframework.test.web.servlet.ResultActions; @@ -24,6 +25,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +@EmbeddedKafka public class EndToEndProfileTest extends AbstractPostgreSQLEnabledTest { @Autowired diff --git a/src/test/java/net/dancier/dancer/mail/EndToEndMailTest.java b/src/test/java/net/dancier/dancer/mail/MailCreationTest.java similarity index 65% rename from src/test/java/net/dancier/dancer/mail/EndToEndMailTest.java rename to src/test/java/net/dancier/dancer/mail/MailCreationTest.java index 59ff76be..f414719d 100644 --- a/src/test/java/net/dancier/dancer/mail/EndToEndMailTest.java +++ b/src/test/java/net/dancier/dancer/mail/MailCreationTest.java @@ -1,36 +1,34 @@ package net.dancier.dancer.mail; import net.dancier.dancer.AbstractPostgreSQLEnabledTest; -import net.dancier.dancer.mail.model.DancierMailMessage; import net.dancier.dancer.mail.service.MailCreationService; -import net.dancier.dancer.mail.service.MailEnqueueService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mail.SimpleMailMessage; import org.springframework.test.context.ActiveProfiles; -import javax.mail.MessagingException; +import jakarta.mail.MessagingException; import java.util.Map; +import static org.assertj.core.api.BDDAssertions.then; + @ActiveProfiles({"test", "dev"}) -public class EndToEndMailTest extends AbstractPostgreSQLEnabledTest { +public class MailCreationTest extends AbstractPostgreSQLEnabledTest { @Autowired MailCreationService mailCreationService; - @Autowired - MailEnqueueService mailEnqueueService; - @Test public void checkSending() throws MessagingException { Map context = Map.of("name", "Marc", "validationLink", "http:/"); - DancierMailMessage dancierMailMessage = mailCreationService.createDancierMessageFromTemplate( + SimpleMailMessage dancierMailMessage = mailCreationService.createDancierMessageFromTemplate( "gorzala@gmx.de", "no-reply@dancier.net", "Bestätige Deine Email-Adresse", MailCreationService.NEW_USER_VALIDATE_EMAIL, context ); - mailEnqueueService.enqueueMail(dancierMailMessage); + then(dancierMailMessage).isNotNull(); } }