diff --git a/lombok.config b/lombok.config new file mode 100644 index 0000000..8f7e8aa --- /dev/null +++ b/lombok.config @@ -0,0 +1 @@ +lombok.addLombokGeneratedAnnotation = true \ No newline at end of file diff --git a/pom.xml b/pom.xml index 4607d9c..54f915b 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ Notification service for rbc library 17 + 1.4.2.Final productdock https://sonarcloud.io ProductDock_rbc-library-notification @@ -24,6 +25,19 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.security + spring-security-test + test + org.projectlombok @@ -39,6 +53,48 @@ spring-boot-starter-test test + + org.springframework.kafka + spring-kafka-test + 2.6.3 + test + + + org.testcontainers + junit-jupiter + 1.17.0 + test + + + org.testcontainers + kafka + 1.15.3 + test + + + + org.springframework.kafka + spring-kafka + + + org.springframework.boot + spring-boot-starter-data-mongodb + + + de.flapdoodle.embed + de.flapdoodle.embed.mongo + test + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + diff --git a/src/main/java/com/productdock/library/notification/NotificationApplication.java b/src/main/java/com/productdock/library/notification/NotificationApplication.java index 06cb599..1cc2f60 100644 --- a/src/main/java/com/productdock/library/notification/NotificationApplication.java +++ b/src/main/java/com/productdock/library/notification/NotificationApplication.java @@ -1,9 +1,15 @@ package com.productdock.library.notification; import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; +import org.springframework.kafka.annotation.EnableKafka; +@EnableKafka @SpringBootApplication +@EnableAutoConfiguration +@EnableMongoRepositories public class NotificationApplication { public static void main(String[] args) { diff --git a/src/main/java/com/productdock/library/notification/adapter/in/kafka/KafkaConsumer.java b/src/main/java/com/productdock/library/notification/adapter/in/kafka/KafkaConsumer.java new file mode 100644 index 0000000..2c1ab1b --- /dev/null +++ b/src/main/java/com/productdock/library/notification/adapter/in/kafka/KafkaConsumer.java @@ -0,0 +1,34 @@ +package com.productdock.library.notification.adapter.in.kafka; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.productdock.library.notification.adapter.in.kafka.messages.NotificationMapper; +import com.productdock.library.notification.adapter.in.kafka.messages.NotificationMessage; +import com.productdock.library.notification.application.port.in.AddNotificationUseCase; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +import java.time.OffsetDateTime; + +@Component +@Slf4j +@AllArgsConstructor +public class KafkaConsumer { + + private NotificationMapper notificationMapper; + private ObjectMapper objectMapper; + private AddNotificationUseCase addNotificationUseCase; + + @KafkaListener(topics = "${spring.kafka.topic.notifications}") + public synchronized void listenNotifications(ConsumerRecord message) throws JsonProcessingException { + log.info("Received notification kafka message: {}", message); + + var notificationMessage = objectMapper.readValue(message.value(), NotificationMessage.class); + var notification = notificationMapper.toDomain(notificationMessage); + notification.setCreatedDate(OffsetDateTime.now()); + addNotificationUseCase.saveNotification(notification); + } +} diff --git a/src/main/java/com/productdock/library/notification/adapter/in/kafka/messages/ActionMessage.java b/src/main/java/com/productdock/library/notification/adapter/in/kafka/messages/ActionMessage.java new file mode 100644 index 0000000..dee1e7b --- /dev/null +++ b/src/main/java/com/productdock/library/notification/adapter/in/kafka/messages/ActionMessage.java @@ -0,0 +1,13 @@ +package com.productdock.library.notification.adapter.in.kafka.messages; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@AllArgsConstructor +@Getter +@Builder +public class ActionMessage { + private String type; + private String target; +} diff --git a/src/main/java/com/productdock/library/notification/adapter/in/kafka/messages/NotificationMapper.java b/src/main/java/com/productdock/library/notification/adapter/in/kafka/messages/NotificationMapper.java new file mode 100644 index 0000000..c8543ac --- /dev/null +++ b/src/main/java/com/productdock/library/notification/adapter/in/kafka/messages/NotificationMapper.java @@ -0,0 +1,15 @@ +package com.productdock.library.notification.adapter.in.kafka.messages; + +import com.productdock.library.notification.domain.Notification; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; + +@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, componentModel = "spring") +public interface NotificationMapper { + + @Mapping(target = "read", ignore = true) + @Mapping(target = "id", ignore = true) + @Mapping(target = "createdDate", ignore = true) + public Notification toDomain(NotificationMessage source); +} diff --git a/src/main/java/com/productdock/library/notification/adapter/in/kafka/messages/NotificationMessage.java b/src/main/java/com/productdock/library/notification/adapter/in/kafka/messages/NotificationMessage.java new file mode 100644 index 0000000..ee44bed --- /dev/null +++ b/src/main/java/com/productdock/library/notification/adapter/in/kafka/messages/NotificationMessage.java @@ -0,0 +1,13 @@ +package com.productdock.library.notification.adapter.in.kafka.messages; + +import lombok.AllArgsConstructor; +import lombok.Builder; + +@Builder +@AllArgsConstructor +public class NotificationMessage { + public final String title; + public final String description; + public final String userId; + public final ActionMessage action; +} diff --git a/src/main/java/com/productdock/library/notification/adapter/in/web/NotificationsApi.java b/src/main/java/com/productdock/library/notification/adapter/in/web/NotificationsApi.java new file mode 100644 index 0000000..006688d --- /dev/null +++ b/src/main/java/com/productdock/library/notification/adapter/in/web/NotificationsApi.java @@ -0,0 +1,46 @@ +package com.productdock.library.notification.adapter.in.web; + +import com.productdock.library.notification.application.port.in.GetNotificationsQuery; +import com.productdock.library.notification.application.port.in.ReadNotificationsUseCase; +import com.productdock.library.notification.domain.Notification; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("api/notifications") +@Slf4j +@AllArgsConstructor +public class NotificationsApi { + + private GetNotificationsQuery getNotificationsQuery; + private ReadNotificationsUseCase readNotificationsUseCase; + + public static final String CLAIM_EMAIL = "email"; + + @GetMapping + public List getNotifications(Authentication authentication) { + var userId = getUserId(authentication); + return getNotificationsQuery.getNotifications(userId); + } + + @GetMapping("/unread") + public int getUnreadCount(Authentication authentication){ + var userId = getUserId(authentication); + return getNotificationsQuery.getUnreadNotificationsCount(userId); + } + + @PostMapping("/read") + public void readNotifications(Authentication authentication) { + var userId = getUserId(authentication); + readNotificationsUseCase.markAsReadNotifications(userId); + } + + private String getUserId(Authentication authentication) { + return ((Jwt) authentication.getCredentials()).getClaim(CLAIM_EMAIL).toString(); + } +} diff --git a/src/main/java/com/productdock/library/notification/adapter/out/mongo/NotificationPersistenceAdapter.java b/src/main/java/com/productdock/library/notification/adapter/out/mongo/NotificationPersistenceAdapter.java new file mode 100644 index 0000000..6d2ebe4 --- /dev/null +++ b/src/main/java/com/productdock/library/notification/adapter/out/mongo/NotificationPersistenceAdapter.java @@ -0,0 +1,47 @@ +package com.productdock.library.notification.adapter.out.mongo; + +import com.productdock.library.notification.adapter.out.mongo.enitity.NotificationEntity; +import com.productdock.library.notification.adapter.out.mongo.mapper.NotificationEntityMapper; +import com.productdock.library.notification.application.port.out.persistence.NotificationPersistenceOutPort; +import com.productdock.library.notification.domain.Notification; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +@Slf4j +@AllArgsConstructor +public class NotificationPersistenceAdapter implements NotificationPersistenceOutPort { + + private NotificationRepository notificationRepository; + private NotificationEntityMapper mapper; + private MongoTemplate mongoTemplate; + + @Override + public List getAllByUserId(String userId) { + return notificationRepository.findAllByUserId(userId) + .stream().map(notificationEntity -> mapper.toDomain(notificationEntity)).toList(); + } + + @Override + public List getUnreadByUserId(String userId) { + return notificationRepository.findAllByReadAndUserId(false, userId) + .stream().map(notificationEntity -> mapper.toDomain(notificationEntity)).toList(); + } + + @Override + public void save(Notification notification) { + log.info("Notification for saving: {}", notification); + var saved = notificationRepository.save(mapper.toEntity(notification)); + List savedNotification = notificationRepository.findAllByUserId(notification.getUserId()); + log.info("Saved notification: {}", savedNotification); + } + + @Override + public void saveAll(List notifications) { + notificationRepository.saveAll(notifications.stream().map(notification -> mapper.toEntity(notification)).toList()); + } +} diff --git a/src/main/java/com/productdock/library/notification/adapter/out/mongo/NotificationRepository.java b/src/main/java/com/productdock/library/notification/adapter/out/mongo/NotificationRepository.java new file mode 100644 index 0000000..103d277 --- /dev/null +++ b/src/main/java/com/productdock/library/notification/adapter/out/mongo/NotificationRepository.java @@ -0,0 +1,15 @@ +package com.productdock.library.notification.adapter.out.mongo; + +import com.productdock.library.notification.adapter.out.mongo.enitity.NotificationEntity; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface NotificationRepository extends MongoRepository { + + List findAllByUserId(String userId); + + List findAllByReadAndUserId(boolean read, String userId); +} diff --git a/src/main/java/com/productdock/library/notification/adapter/out/mongo/enitity/ActionEntity.java b/src/main/java/com/productdock/library/notification/adapter/out/mongo/enitity/ActionEntity.java new file mode 100644 index 0000000..711d248 --- /dev/null +++ b/src/main/java/com/productdock/library/notification/adapter/out/mongo/enitity/ActionEntity.java @@ -0,0 +1,17 @@ +package com.productdock.library.notification.adapter.out.mongo.enitity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ActionEntity implements Serializable { + private String type; + private String target; +} diff --git a/src/main/java/com/productdock/library/notification/adapter/out/mongo/enitity/NotificationEntity.java b/src/main/java/com/productdock/library/notification/adapter/out/mongo/enitity/NotificationEntity.java new file mode 100644 index 0000000..d85ddf2 --- /dev/null +++ b/src/main/java/com/productdock/library/notification/adapter/out/mongo/enitity/NotificationEntity.java @@ -0,0 +1,26 @@ +package com.productdock.library.notification.adapter.out.mongo.enitity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +import java.io.Serializable; + +@Document("notifications") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class NotificationEntity implements Serializable { + @Id + private String id; + private String title; + private String description; + private String userId; + private boolean read; + private String createdDate; + private ActionEntity action; +} diff --git a/src/main/java/com/productdock/library/notification/adapter/out/mongo/mapper/NotificationEntityMapper.java b/src/main/java/com/productdock/library/notification/adapter/out/mongo/mapper/NotificationEntityMapper.java new file mode 100644 index 0000000..33dc5d6 --- /dev/null +++ b/src/main/java/com/productdock/library/notification/adapter/out/mongo/mapper/NotificationEntityMapper.java @@ -0,0 +1,22 @@ +package com.productdock.library.notification.adapter.out.mongo.mapper; + +import com.productdock.library.notification.adapter.out.mongo.enitity.NotificationEntity; +import com.productdock.library.notification.domain.Notification; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +import java.time.OffsetDateTime; + +@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, componentModel = "spring") +public interface NotificationEntityMapper { + NotificationEntity toEntity(Notification source); + Notification toDomain(NotificationEntity source); + + default String map(OffsetDateTime value){ + return value.toString(); + } + + default OffsetDateTime map(String value) { + return OffsetDateTime.parse(value); + } +} diff --git a/src/main/java/com/productdock/library/notification/application/port/in/AddNotificationUseCase.java b/src/main/java/com/productdock/library/notification/application/port/in/AddNotificationUseCase.java new file mode 100644 index 0000000..8b7bf99 --- /dev/null +++ b/src/main/java/com/productdock/library/notification/application/port/in/AddNotificationUseCase.java @@ -0,0 +1,8 @@ +package com.productdock.library.notification.application.port.in; + +import com.productdock.library.notification.domain.Notification; + +public interface AddNotificationUseCase { + + void saveNotification(Notification notification); +} diff --git a/src/main/java/com/productdock/library/notification/application/port/in/GetNotificationsQuery.java b/src/main/java/com/productdock/library/notification/application/port/in/GetNotificationsQuery.java new file mode 100644 index 0000000..4603fe8 --- /dev/null +++ b/src/main/java/com/productdock/library/notification/application/port/in/GetNotificationsQuery.java @@ -0,0 +1,12 @@ +package com.productdock.library.notification.application.port.in; + +import com.productdock.library.notification.domain.Notification; + +import java.util.List; + +public interface GetNotificationsQuery { + + List getNotifications(String userId); + + int getUnreadNotificationsCount(String userId); +} diff --git a/src/main/java/com/productdock/library/notification/application/port/in/ReadNotificationsUseCase.java b/src/main/java/com/productdock/library/notification/application/port/in/ReadNotificationsUseCase.java new file mode 100644 index 0000000..a192da9 --- /dev/null +++ b/src/main/java/com/productdock/library/notification/application/port/in/ReadNotificationsUseCase.java @@ -0,0 +1,6 @@ +package com.productdock.library.notification.application.port.in; + +public interface ReadNotificationsUseCase { + + void markAsReadNotifications(String userId); +} diff --git a/src/main/java/com/productdock/library/notification/application/port/out/persistence/NotificationPersistenceOutPort.java b/src/main/java/com/productdock/library/notification/application/port/out/persistence/NotificationPersistenceOutPort.java new file mode 100644 index 0000000..e352d9e --- /dev/null +++ b/src/main/java/com/productdock/library/notification/application/port/out/persistence/NotificationPersistenceOutPort.java @@ -0,0 +1,16 @@ +package com.productdock.library.notification.application.port.out.persistence; + +import com.productdock.library.notification.domain.Notification; + +import java.util.List; + +public interface NotificationPersistenceOutPort { + + List getAllByUserId(String userId); + + List getUnreadByUserId(String userId); + + void save(Notification notification); + + void saveAll(List notifications); +} diff --git a/src/main/java/com/productdock/library/notification/application/service/AddNotificationService.java b/src/main/java/com/productdock/library/notification/application/service/AddNotificationService.java new file mode 100644 index 0000000..f6cb836 --- /dev/null +++ b/src/main/java/com/productdock/library/notification/application/service/AddNotificationService.java @@ -0,0 +1,18 @@ +package com.productdock.library.notification.application.service; + +import com.productdock.library.notification.application.port.in.AddNotificationUseCase; +import com.productdock.library.notification.application.port.out.persistence.NotificationPersistenceOutPort; +import com.productdock.library.notification.domain.Notification; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@AllArgsConstructor +public class AddNotificationService implements AddNotificationUseCase { + + private NotificationPersistenceOutPort notificationPersistenceOutPort; + @Override + public void saveNotification(Notification notification) { + notificationPersistenceOutPort.save(notification); + } +} diff --git a/src/main/java/com/productdock/library/notification/application/service/GetNotificationsService.java b/src/main/java/com/productdock/library/notification/application/service/GetNotificationsService.java new file mode 100644 index 0000000..089c118 --- /dev/null +++ b/src/main/java/com/productdock/library/notification/application/service/GetNotificationsService.java @@ -0,0 +1,27 @@ +package com.productdock.library.notification.application.service; + +import com.productdock.library.notification.application.port.in.GetNotificationsQuery; +import com.productdock.library.notification.application.port.out.persistence.NotificationPersistenceOutPort; +import com.productdock.library.notification.domain.Notification; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@AllArgsConstructor +@Slf4j +public class GetNotificationsService implements GetNotificationsQuery { + + private NotificationPersistenceOutPort notificationPersistenceOutPort; + @Override + public List getNotifications(String userId) { + return notificationPersistenceOutPort.getAllByUserId(userId); + } + + @Override + public int getUnreadNotificationsCount(String userId) { + return notificationPersistenceOutPort.getUnreadByUserId(userId).size(); + } +} diff --git a/src/main/java/com/productdock/library/notification/application/service/ReadNotificationsService.java b/src/main/java/com/productdock/library/notification/application/service/ReadNotificationsService.java new file mode 100644 index 0000000..1e9d433 --- /dev/null +++ b/src/main/java/com/productdock/library/notification/application/service/ReadNotificationsService.java @@ -0,0 +1,23 @@ +package com.productdock.library.notification.application.service; + +import com.productdock.library.notification.application.port.in.ReadNotificationsUseCase; +import com.productdock.library.notification.application.port.out.persistence.NotificationPersistenceOutPort; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.stereotype.Service; + +@Service +@AllArgsConstructor +@Slf4j +public class ReadNotificationsService implements ReadNotificationsUseCase { + + private NotificationPersistenceOutPort notificationPersistenceOutPort; + + @Override + public void markAsReadNotifications(String userId) { + var notifications = notificationPersistenceOutPort.getUnreadByUserId(userId); + notifications.forEach(notification -> notification.setRead(true)); + notificationPersistenceOutPort.saveAll(notifications); + } +} diff --git a/src/main/java/com/productdock/library/notification/config/SecurityConfig.java b/src/main/java/com/productdock/library/notification/config/SecurityConfig.java new file mode 100644 index 0000000..690a298 --- /dev/null +++ b/src/main/java/com/productdock/library/notification/config/SecurityConfig.java @@ -0,0 +1,34 @@ +package com.productdock.library.notification.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; + +import java.security.interfaces.RSAPublicKey; + +@Configuration +public class SecurityConfig { + + @Value("${jwt.public.key}") + RSAPublicKey key; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.authorizeRequests(authorize -> authorize.antMatchers("/actuator/**").permitAll().anyRequest().authenticated()) + .cors().and() + .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); + return http.build(); + } + + @Bean + JwtDecoder jwtDecoder() { + return NimbusJwtDecoder.withPublicKey(this.key).build(); + } +} diff --git a/src/main/java/com/productdock/library/notification/domain/Action.java b/src/main/java/com/productdock/library/notification/domain/Action.java new file mode 100644 index 0000000..a870e7d --- /dev/null +++ b/src/main/java/com/productdock/library/notification/domain/Action.java @@ -0,0 +1,13 @@ +package com.productdock.library.notification.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@AllArgsConstructor +@Getter +@Builder +public class Action { + private String type; + private String target; +} diff --git a/src/main/java/com/productdock/library/notification/domain/Notification.java b/src/main/java/com/productdock/library/notification/domain/Notification.java new file mode 100644 index 0000000..61ae753 --- /dev/null +++ b/src/main/java/com/productdock/library/notification/domain/Notification.java @@ -0,0 +1,20 @@ +package com.productdock.library.notification.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +import java.time.OffsetDateTime; + +@Data +@Builder +@AllArgsConstructor +public class Notification { + private String id; + private String title; + private String description; + private String userId; + private boolean read; + private OffsetDateTime createdDate; + private Action action; +} diff --git a/src/main/resources/app-local.pub b/src/main/resources/app-local.pub new file mode 100644 index 0000000..0b2ee7b --- /dev/null +++ b/src/main/resources/app-local.pub @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3FlqJr5TRskIQIgdE3Dd +7D9lboWdcTUT8a+fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRv +c5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4/1tfRgG6ii4Uhxh6 +iI8qNMJQX+fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2 +kJdJ/ZIV+WW4noDdzpKqHcwmB8FsrumlVY/DNVvUSDIipiq9PbP4H99TXN1o746o +RaNa07rq1hoCgMSSy+85SagCoxlmyE+D+of9SsMY8Ol9t0rdzpobBuhyJ/o5dfvj +KwIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/src/main/resources/application-local.yaml b/src/main/resources/application-local.yaml new file mode 100644 index 0000000..c2af6a7 --- /dev/null +++ b/src/main/resources/application-local.yaml @@ -0,0 +1,8 @@ +KAFKA_SERVER_URL: localhost:9093 +USER_PROFILES_JWT_PUBLIC_KEY: src/main/resources/app-local.pub +MONGO_SERVER_URL: localhost +MONGO_SERVER_PORT: 28017 +MONGO_USERNAME: root +MONGO_PASSWORD: root1 + +LOG_TO_FILE: false \ No newline at end of file diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 18386cc..7eb1695 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,12 +1,48 @@ +spring: + kafka: + enabled: true + bootstrap-servers: ${KAFKA_SERVER_URL} + value-seriliazer: org.apache.kafka.common.serialization.StringSerializer + key-serializer: org.apache.kafka.common.serialization.StringSerializer + producer: + client-id: kafka-message-producer + acks: all + value-seriliazer: org.apache.kafka.common.serialization.StringSerializer + key-serializer: org.apache.kafka.common.serialization.StringSerializer + consumer: + enable-auto-commit: true + auto-offset-reset: earliest + group-id: notifications-group + topic: + notifications: notifications + + data: + mongodb: + authentication-database: notification + host: ${MONGO_SERVER_URL} + port: ${MONGO_SERVER_PORT} + username: ${MONGO_USERNAME} + password: ${MONGO_PASSWORD} + database: notification + +logging: + level: + com.productdock.library.notification: INFO + +file-logging-enabled: ${LOG_TO_FILE} + +jwt: + public.key: file:${USER_PROFILES_JWT_PUBLIC_KEY} + server: - port: 8080 + port: 8086 management: server: - port: 8087 + port: 8099 endpoint: health: show-details: always endpoints: web: exposure: - include: '*' + include: '*' \ No newline at end of file diff --git a/src/test/java/com/productdock/library/notification/adapter/in/kafka/messages/NotificationMapperShould.java b/src/test/java/com/productdock/library/notification/adapter/in/kafka/messages/NotificationMapperShould.java new file mode 100644 index 0000000..a8de572 --- /dev/null +++ b/src/test/java/com/productdock/library/notification/adapter/in/kafka/messages/NotificationMapperShould.java @@ -0,0 +1,33 @@ +package com.productdock.library.notification.adapter.in.kafka.messages; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static com.productdock.library.notification.data.provider.in.kafka.NotificationMessageMother.notificationMessage; +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = {NotificationMapperImpl.class}) +class NotificationMapperShould { + + @Autowired + private NotificationMapper notificationMapper; + + @Test + void mapMessageToDomain() { + var notificationMessage = notificationMessage(); + + var notification = notificationMapper.toDomain(notificationMessage); + + assertThat(notification.getTitle()).isEqualTo(notificationMessage.title); + assertThat(notification.getDescription()).isEqualTo(notificationMessage.description); + assertThat(notification.getAction().getType()).isEqualTo(notificationMessage.action.getType()); + assertThat(notification.getAction().getTarget()).isEqualTo(notificationMessage.action.getTarget()); + assertThat(notification.isRead()).isFalse(); + assertThat(notification.getCreatedDate()).isNull(); + assertThat(notification.getId()).isNull(); + } +} diff --git a/src/test/java/com/productdock/library/notification/adapter/out/mongo/NotificationPersistenceAdapterShould.java b/src/test/java/com/productdock/library/notification/adapter/out/mongo/NotificationPersistenceAdapterShould.java new file mode 100644 index 0000000..f92bf36 --- /dev/null +++ b/src/test/java/com/productdock/library/notification/adapter/out/mongo/NotificationPersistenceAdapterShould.java @@ -0,0 +1,53 @@ +package com.productdock.library.notification.adapter.out.mongo; + +import com.productdock.library.notification.adapter.out.mongo.mapper.NotificationEntityMapper; +import com.productdock.library.notification.domain.Notification; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.ArrayList; + +import static com.productdock.library.notification.data.provider.domain.NotificationMother.notification; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class NotificationPersistenceAdapterShould { + + private static final String USER_ID = "1"; + private static final boolean READ = false; + private static final Notification NOTIFICATION = notification(); + + @InjectMocks + private NotificationPersistenceAdapter notificationPersistenceAdapter; + @Mock + private NotificationRepository notificationRepository; + @Mock + private NotificationEntityMapper notificationEntityMapper; + + @Test + void getAllNotificationsForUser() { + notificationPersistenceAdapter.getAllByUserId(USER_ID); + + verify(notificationRepository).findAllByUserId(USER_ID); + } + + @Test + void getUnreadNotificationsForUser() { + notificationPersistenceAdapter.getUnreadByUserId(USER_ID); + + verify(notificationRepository).findAllByReadAndUserId(READ, USER_ID); + } + + @Test + void saveAll(){ + var notifications = new ArrayList(); + notifications.add(NOTIFICATION); + notificationPersistenceAdapter.saveAll(notifications); + + verify(notificationRepository).saveAll(any()); + } +} diff --git a/src/test/java/com/productdock/library/notification/adapter/out/mongo/mapper/NotificationMapperShould.java b/src/test/java/com/productdock/library/notification/adapter/out/mongo/mapper/NotificationMapperShould.java new file mode 100644 index 0000000..5bee822 --- /dev/null +++ b/src/test/java/com/productdock/library/notification/adapter/out/mongo/mapper/NotificationMapperShould.java @@ -0,0 +1,49 @@ +package com.productdock.library.notification.adapter.out.mongo.mapper; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static com.productdock.library.notification.data.provider.domain.NotificationMother.notification; +import static com.productdock.library.notification.data.provider.out.mongo.NotificationEntityMother.notificationEntity; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = {NotificationEntityMapperImpl.class}) +class NotificationMapperShould { + + @Autowired + private NotificationEntityMapper notificationEntityMapper; + + @Test + void mapNotificationToNotificationEntity(){ + var notification = notification(); + var notificationEntity = notificationEntityMapper.toEntity(notification); + + assertThat(notificationEntity.getId()).isEqualTo(notification.getId()); + assertThat(notificationEntity.getUserId()).isEqualTo(notification.getUserId()); + assertThat(notificationEntity.getTitle()).isEqualTo(notification.getTitle()); + assertThat(notificationEntity.getDescription()).isEqualTo(notification.getDescription()); + assertThat(notificationEntity.isRead()).isEqualTo(notification.isRead()); + assertThat(notificationEntity.getCreatedDate()).isEqualTo(notification.getCreatedDate().toString()); + assertThat(notificationEntity.getAction().getTarget()).isEqualTo(notification.getAction().getTarget()); + assertThat(notificationEntity.getAction().getType()).isEqualTo(notification.getAction().getType()); + } + + @Test + void mapNotificationEntityToNotification(){ + var notificationEntity = notificationEntity(); + var notification = notificationEntityMapper.toDomain(notificationEntity); + + assertThat(notification.getId()).isEqualTo(notificationEntity.getId()); + assertThat(notification.getTitle()).isEqualTo(notificationEntity.getTitle()); + assertThat(notification.getDescription()).isEqualTo(notificationEntity.getDescription()); + assertThat(notification.getUserId()).isEqualTo(notificationEntity.getUserId()); + assertThat(notification.getCreatedDate().toString()).isEqualTo(notificationEntity.getCreatedDate()); + assertThat(notification.getAction().getTarget()).isEqualTo(notification.getAction().getTarget()); + assertThat(notification.getAction().getType()).isEqualTo(notification.getAction().getType()); + } + +} diff --git a/src/test/java/com/productdock/library/notification/application/service/AddNotificationServiceShould.java b/src/test/java/com/productdock/library/notification/application/service/AddNotificationServiceShould.java new file mode 100644 index 0000000..d051592 --- /dev/null +++ b/src/test/java/com/productdock/library/notification/application/service/AddNotificationServiceShould.java @@ -0,0 +1,30 @@ +package com.productdock.library.notification.application.service; + +import com.productdock.library.notification.application.port.out.persistence.NotificationPersistenceOutPort; +import com.productdock.library.notification.domain.Notification; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static com.productdock.library.notification.data.provider.domain.NotificationMother.notification; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class AddNotificationServiceShould { + + private static final Notification NOTIFICATION = notification(); + + @InjectMocks + private AddNotificationService addNotificationService; + @Mock + private NotificationPersistenceOutPort notificationPersistenceOutPort; + + @Test + void addNotification(){ + addNotificationService.saveNotification(NOTIFICATION); + + verify(notificationPersistenceOutPort).save(NOTIFICATION); + } +} diff --git a/src/test/java/com/productdock/library/notification/application/service/GetNotificationsServiceShould.java b/src/test/java/com/productdock/library/notification/application/service/GetNotificationsServiceShould.java new file mode 100644 index 0000000..657bc19 --- /dev/null +++ b/src/test/java/com/productdock/library/notification/application/service/GetNotificationsServiceShould.java @@ -0,0 +1,38 @@ +package com.productdock.library.notification.application.service; + +import com.productdock.library.notification.application.port.out.persistence.NotificationPersistenceOutPort; +import com.productdock.library.notification.domain.Notification; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static com.productdock.library.notification.data.provider.domain.NotificationMother.notification; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class GetNotificationsServiceShould { + + private static final String USER_ID = "1"; + private static final Notification NOTIFICATION = notification(); + + @InjectMocks + private GetNotificationsService getNotificationsService; + @Mock + private NotificationPersistenceOutPort notificationPersistenceOutPort; + + @Test + void getNotifications(){ + getNotificationsService.getNotifications(USER_ID); + + verify(notificationPersistenceOutPort).getAllByUserId(USER_ID); + } + + @Test + void getUnreadNotificationsCount(){ + getNotificationsService.getUnreadNotificationsCount(USER_ID); + + verify(notificationPersistenceOutPort).getUnreadByUserId(USER_ID); + } +} diff --git a/src/test/java/com/productdock/library/notification/application/service/ReadNotificationServiceShould.java b/src/test/java/com/productdock/library/notification/application/service/ReadNotificationServiceShould.java new file mode 100644 index 0000000..0cd23a0 --- /dev/null +++ b/src/test/java/com/productdock/library/notification/application/service/ReadNotificationServiceShould.java @@ -0,0 +1,33 @@ +package com.productdock.library.notification.application.service; + +import com.productdock.library.notification.application.port.out.persistence.NotificationPersistenceOutPort; +import com.productdock.library.notification.domain.Notification; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.ArrayList; + +import static com.productdock.library.notification.data.provider.domain.NotificationMother.notification; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class ReadNotificationServiceShould { + + private static final String USER_ID = "1"; + + @InjectMocks + private ReadNotificationsService readNotificationsService; + @Mock + private NotificationPersistenceOutPort notificationPersistenceOutPort; + + @Test + void markNotificationAsRead(){ + readNotificationsService.markAsReadNotifications(USER_ID); + + verify(notificationPersistenceOutPort).saveAll(any()); + } +} diff --git a/src/test/java/com/productdock/library/notification/config/SecurityConfigTest.java b/src/test/java/com/productdock/library/notification/config/SecurityConfigTest.java new file mode 100644 index 0000000..2bbe855 --- /dev/null +++ b/src/test/java/com/productdock/library/notification/config/SecurityConfigTest.java @@ -0,0 +1,24 @@ +package com.productdock.library.notification.config; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +class SecurityConfigTest { + + @Autowired + private MockMvc mockMvc; + + @Test + void givenUnauthenticated_thenUnauthorizedResponse() throws Exception { + mockMvc.perform(get("/api/notifications/")) + .andExpect(status().isUnauthorized()); + } +} diff --git a/src/test/java/com/productdock/library/notification/data/provider/domain/NotificationMother.java b/src/test/java/com/productdock/library/notification/data/provider/domain/NotificationMother.java new file mode 100644 index 0000000..e0e30d1 --- /dev/null +++ b/src/test/java/com/productdock/library/notification/data/provider/domain/NotificationMother.java @@ -0,0 +1,32 @@ +package com.productdock.library.notification.data.provider.domain; + +import com.productdock.library.notification.domain.Action; +import com.productdock.library.notification.domain.Notification; + +import java.time.OffsetDateTime; + +public class NotificationMother { + + public static final String defaultId = "11111"; + public static final String defaultUserId = "userEmail"; + public static final String defaultTitle = "title"; + public static final String defaultDescription = "description"; + public static final String defaultBookId = "1"; + public static final String defaultType = "bookSubscription"; + public static final Action defaultAction = Action.builder().target(defaultBookId).type(defaultType).build(); + + public static Notification notification(){ + return notificationBuilder().build(); + } + + public static Notification.NotificationBuilder notificationBuilder(){ + return Notification.builder() + .id(defaultId) + .userId(defaultUserId) + .title(defaultTitle) + .read(false) + .description(defaultDescription) + .createdDate(OffsetDateTime.now()) + .action(defaultAction); + } +} diff --git a/src/test/java/com/productdock/library/notification/data/provider/in/kafka/NotificationMessageMother.java b/src/test/java/com/productdock/library/notification/data/provider/in/kafka/NotificationMessageMother.java new file mode 100644 index 0000000..4553367 --- /dev/null +++ b/src/test/java/com/productdock/library/notification/data/provider/in/kafka/NotificationMessageMother.java @@ -0,0 +1,27 @@ +package com.productdock.library.notification.data.provider.in.kafka; + +import com.productdock.library.notification.adapter.in.kafka.messages.ActionMessage; +import com.productdock.library.notification.adapter.in.kafka.messages.NotificationMessage; + +public class NotificationMessageMother { + + private static final String defaultTitle = "title"; + private static final String defaultDescription = "description"; + private static final String defaultUserId = "userEmail"; + private static final String defalutType = "bookSubscription"; + private static final String defaultTarget = "1"; + private static final ActionMessage defaultAction = ActionMessage.builder().type(defalutType).target(defaultTarget).build(); + + + public static NotificationMessage notificationMessage() { + return notificationMessageBuilder().build(); + } + + public static NotificationMessage.NotificationMessageBuilder notificationMessageBuilder() { + return NotificationMessage.builder() + .title(defaultTitle) + .description(defaultDescription) + .userId(defaultUserId) + .action(defaultAction); + } +} diff --git a/src/test/java/com/productdock/library/notification/data/provider/out/mongo/NotificationEntityMother.java b/src/test/java/com/productdock/library/notification/data/provider/out/mongo/NotificationEntityMother.java new file mode 100644 index 0000000..c1aa9f3 --- /dev/null +++ b/src/test/java/com/productdock/library/notification/data/provider/out/mongo/NotificationEntityMother.java @@ -0,0 +1,31 @@ +package com.productdock.library.notification.data.provider.out.mongo; + +import com.productdock.library.notification.adapter.out.mongo.enitity.ActionEntity; +import com.productdock.library.notification.adapter.out.mongo.enitity.NotificationEntity; + +import java.time.OffsetDateTime; + +public class NotificationEntityMother { + + public static final String defaultId = "11111"; + public static final String defaultUserId = "userEmail"; + public static final String defaultTitle = "title"; + public static final String defaultDescription = "description"; + public static final String defaultBookId = "1"; + public static final String defaultType = "bookSubscription"; + public static final ActionEntity defaultActionEntity = ActionEntity.builder().target(defaultBookId).type(defaultType).build(); + + public static NotificationEntity notificationEntity(){ + return notificationBuilder().build(); + } + + public static NotificationEntity.NotificationEntityBuilder notificationBuilder(){ + return NotificationEntity.builder() + .userId(defaultUserId) + .title(defaultTitle) + .read(false) + .description(defaultDescription) + .createdDate(OffsetDateTime.now().toString()) + .action(defaultActionEntity); + } +} diff --git a/src/test/java/com/productdock/library/notification/integration/GetNotificationApiTest.java b/src/test/java/com/productdock/library/notification/integration/GetNotificationApiTest.java new file mode 100644 index 0000000..c880944 --- /dev/null +++ b/src/test/java/com/productdock/library/notification/integration/GetNotificationApiTest.java @@ -0,0 +1,80 @@ +package com.productdock.library.notification.integration; + +import com.productdock.library.notification.adapter.out.mongo.NotificationRepository; +import com.productdock.library.notification.adapter.out.mongo.enitity.NotificationEntity; +import com.productdock.library.notification.integration.kafka.KafkaTestBase; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; + +import static com.productdock.library.notification.data.provider.out.mongo.NotificationEntityMother.notificationEntity; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class GetNotificationApiTest extends KafkaTestBase { + + public static final String USER_ID = "userEmail"; + + public static final NotificationEntity NOTIFICATION_ENTITY = notificationEntity(); + + @Autowired + private MockMvc mockMvc; + @Autowired + private NotificationRepository notificationRepository; + + @BeforeEach + @AfterEach + void before() { + notificationRepository.deleteAll(); + } + + @Test + @WithMockUser + void shouldReturnNotifications() throws Exception { + notificationRepository.save(NOTIFICATION_ENTITY); + + mockMvc.perform(get("/api/notifications") + .with(jwt().jwt(jwt -> { + jwt.claim("email", USER_ID); + }))) + .andExpect(status().isOk()); + } + + @Test + @WithMockUser + void shouldReturnUnreadNotificationsCount() throws Exception { + notificationRepository.save(NOTIFICATION_ENTITY); + + var response = mockMvc.perform(get("/api/notifications/unread") + .with(jwt().jwt(jwt -> { + jwt.claim("email", USER_ID); + }))) + .andExpect(status().isOk()) + .andReturn().getResponse(); + + assertThat(response.getContentAsString()).isEqualTo("1"); + } + + @Test + @WithMockUser + void shouldMarkNotificationsAsRead() throws Exception { + notificationRepository.save(NOTIFICATION_ENTITY); + + mockMvc.perform(post("/api/notifications/read") + .with(jwt().jwt(jwt -> { + jwt.claim("email", USER_ID); + }))) + .andExpect(status().isOk()); + + var notifications = notificationRepository.findAllByUserId(USER_ID); + notifications.forEach(notificationEntity -> assertThat(notificationEntity.isRead()).isTrue()); + } + +} diff --git a/src/test/java/com/productdock/library/notification/integration/ReceiveNotificationTest.java b/src/test/java/com/productdock/library/notification/integration/ReceiveNotificationTest.java new file mode 100644 index 0000000..ddcd417 --- /dev/null +++ b/src/test/java/com/productdock/library/notification/integration/ReceiveNotificationTest.java @@ -0,0 +1,62 @@ +package com.productdock.library.notification.integration; + +import com.productdock.library.notification.adapter.in.kafka.messages.NotificationMessage; +import com.productdock.library.notification.adapter.out.mongo.NotificationRepository; +import com.productdock.library.notification.adapter.out.mongo.enitity.NotificationEntity; +import com.productdock.library.notification.integration.kafka.KafkaTestBase; +import com.productdock.library.notification.integration.kafka.KafkaTestProducer; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.mongodb.core.MongoTemplate; + +import java.time.Duration; + +import static com.productdock.library.notification.data.provider.in.kafka.NotificationMessageMother.notificationMessage; +import static com.productdock.library.notification.data.provider.out.mongo.NotificationEntityMother.notificationEntity; +import static org.assertj.core.api.Assertions.assertThat; +import static org.testcontainers.shaded.org.awaitility.Awaitility.await; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@Slf4j +public class ReceiveNotificationTest extends KafkaTestBase { + + public static final NotificationMessage NOTIFICATION_MESSAGE = notificationMessage(); + @Autowired + private KafkaTestProducer producer; + @Autowired + private NotificationRepository notificationRepository; + @Value("${spring.kafka.topic.notifications}") + private String topic; + + @Autowired + private MongoTemplate mongoTemplate; + + @BeforeEach + void before() { + //mongoTemplate.remove(new Query(), "notifications"); + notificationRepository.deleteAll(); + } + + @Test + void shouldSaveNotification_whenMessageReceived() throws Exception { + producer.sendNotificationMessage(topic, NOTIFICATION_MESSAGE); + + notificationRepository.save(notificationEntity()); + await() + .atMost(Duration.ofSeconds(20)) + .until(() -> { + log.info("Fetching..."); + var savedNotification = notificationRepository.findAll(); + log.info("Fetched notifications:{}", savedNotification); + return !savedNotification.isEmpty(); + }); + + log.warn("{}", notificationRepository.findAllByUserId(NOTIFICATION_MESSAGE.userId)); + var notifications = notificationRepository.findAllByUserId(NOTIFICATION_MESSAGE.userId); + assertThat(notifications).isNotEmpty(); + } +} diff --git a/src/test/java/com/productdock/library/notification/integration/kafka/KafkaTestBase.java b/src/test/java/com/productdock/library/notification/integration/kafka/KafkaTestBase.java new file mode 100644 index 0000000..ee62df8 --- /dev/null +++ b/src/test/java/com/productdock/library/notification/integration/kafka/KafkaTestBase.java @@ -0,0 +1,10 @@ +package com.productdock.library.notification.integration.kafka; + + +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.kafka.test.context.EmbeddedKafka; + +@AutoConfigureMockMvc +@EmbeddedKafka(partitions = 1, brokerProperties = {"listeners=PLAINTEXT://localhost:9093", "port=9093"}) +public class KafkaTestBase { +} diff --git a/src/test/java/com/productdock/library/notification/integration/kafka/KafkaTestProducer.java b/src/test/java/com/productdock/library/notification/integration/kafka/KafkaTestProducer.java new file mode 100644 index 0000000..d11fe77 --- /dev/null +++ b/src/test/java/com/productdock/library/notification/integration/kafka/KafkaTestProducer.java @@ -0,0 +1,22 @@ +package com.productdock.library.notification.integration.kafka; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.productdock.library.notification.adapter.in.kafka.messages.NotificationMessage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Component; + +@Component +public class KafkaTestProducer { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + @Autowired + private KafkaTemplate kafkaTemplate; + + public void sendNotificationMessage(String topic, NotificationMessage notificationMessage) throws JsonProcessingException { + String message = OBJECT_MAPPER.writeValueAsString(notificationMessage); + kafkaTemplate.send(topic, message); + } +} diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml new file mode 100644 index 0000000..6a35cda --- /dev/null +++ b/src/test/resources/application.yaml @@ -0,0 +1,23 @@ +spring: + mongodb: + embedded: + version: 3.5.5 + kafka: + bootstrap-servers: localhost:9093 + producer: + client-id: kafka-message-producer + acks: all + value-seriliazer: org.apache.kafka.common.serialization.StringSerializer + key-serializer: org.apache.kafka.common.serialization.StringSerializer + consumer: + auto-offset-reset: earliest + group-id: rbc-library + topic: + notifications: test-notifications + +logging: + level: + com.productdock.library.rental: DEBUG + +jwt: + public.key: classpath:test.pub \ No newline at end of file diff --git a/src/test/resources/test.pub b/src/test/resources/test.pub new file mode 100644 index 0000000..0b2ee7b --- /dev/null +++ b/src/test/resources/test.pub @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3FlqJr5TRskIQIgdE3Dd +7D9lboWdcTUT8a+fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRv +c5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4/1tfRgG6ii4Uhxh6 +iI8qNMJQX+fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2 +kJdJ/ZIV+WW4noDdzpKqHcwmB8FsrumlVY/DNVvUSDIipiq9PbP4H99TXN1o746o +RaNa07rq1hoCgMSSy+85SagCoxlmyE+D+of9SsMY8Ol9t0rdzpobBuhyJ/o5dfvj +KwIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file