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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ dependencies {
// Security
implementation("org.springframework.boot:spring-boot-starter-security")

// RabbitMQ
implementation("org.springframework.boot:spring-boot-starter-amqp")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")

// JWT
implementation("io.jsonwebtoken:jjwt-api:0.12.6")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.6")
Expand Down Expand Up @@ -65,6 +69,8 @@ dependencies {
testImplementation("org.springframework.security:spring-security-test")
testImplementation("org.testcontainers:junit-jupiter:1.20.4")
testImplementation("org.testcontainers:postgresql:1.20.4")
testImplementation("org.springframework.amqp:spring-rabbit-test")
testImplementation("org.testcontainers:rabbitmq:1.20.4")
testImplementation("io.rest-assured:rest-assured:5.5.0")

testCompileOnly("org.projectlombok:lombok")
Expand Down
7 changes: 6 additions & 1 deletion environment/.local.env
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,9 @@ ZIPKIN_PORT=9411
POSTGRES_HOST=devoops-postgres
POSGTES_PORT=5432
DB_USERNAME=user-service
DB_PASSWORD=user-service-pass
DB_PASSWORD=user-service-pass
RABBITMQ_HOST=devoops-rabbitmq
RABBITMQ_PORT=5672
RABBITMQ_USERNAME=devoops
RABBITMQ_PASSWORD=devoops123
RABBITMQ_VHOST=/
38 changes: 38 additions & 0 deletions src/main/java/com/devoops/user/config/RabbitMQConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.devoops.user.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMQConfig {

@Value("${rabbitmq.exchange.notification}")
private String notificationExchange;

@Bean
public MessageConverter jsonMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
return new Jackson2JsonMessageConverter(objectMapper);
}

@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(jsonMessageConverter());
return rabbitTemplate;
}

@Bean
public TopicExchange notificationExchange() {
return new TopicExchange(notificationExchange);
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/devoops/user/dto/message/UserCreatedMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.devoops.user.dto.message;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.io.Serializable;
import java.util.UUID;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserCreatedMessage implements Serializable {
private UUID userId;
private String userEmail;
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class AuthenticationService {
private final JwtService jwtService;
private final AuthenticationManager authenticationManager;
private final UserMapper userMapper;
private final UserEventPublisherService userEventPublisherService;

@Transactional
public AuthenticationResponse register(RegisterRequest request) {
Expand All @@ -52,6 +53,8 @@ public AuthenticationResponse register(RegisterRequest request) {
log.info("Registration successful for user: {} (id: {}, role: {})",
savedUser.getUsername(), savedUser.getId(), savedUser.getRole());

userEventPublisherService.publishUserCreated(savedUser.getId(), savedUser.getEmail());

return new AuthenticationResponse(token, jwtService.getExpirationTime(), userMapper.toUserResponse(savedUser));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.devoops.user.service;

import com.devoops.user.dto.message.UserCreatedMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.UUID;

@Slf4j
@Service
@RequiredArgsConstructor
public class UserEventPublisherService {

private final RabbitTemplate rabbitTemplate;

@Value("${rabbitmq.exchange.notification}")
private String notificationExchange;

@Value("${rabbitmq.routing-key.user-created}")
private String userCreatedRoutingKey;

public void publishUserCreated(UUID userId, String email) {
UserCreatedMessage message = UserCreatedMessage.builder()
.userId(userId)
.userEmail(email)
.build();

log.info("Publishing user.created event for userId: {}, email: {}", userId, email);
rabbitTemplate.convertAndSend(notificationExchange, userCreatedRoutingKey, message);
log.debug("Successfully published user.created event for userId: {}", userId);
}
}
11 changes: 11 additions & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,14 @@ cors.allowed-origins=${CORS_ALLOWED_ORIGINS:http://localhost:4200}
management.endpoints.web.exposure.include=health,prometheus,metrics
management.endpoint.health.show-details=always
management.prometheus.metrics.export.enabled=true

# RabbitMQ
spring.rabbitmq.host=${RABBITMQ_HOST:localhost}
spring.rabbitmq.port=${RABBITMQ_PORT:5672}
spring.rabbitmq.username=${RABBITMQ_USERNAME:devoops}
spring.rabbitmq.password=${RABBITMQ_PASSWORD:devoops123}
spring.rabbitmq.virtual-host=${RABBITMQ_VHOST:/}

# Queue config
rabbitmq.exchange.notification=notification.exchange
rabbitmq.routing-key.user-created=user.created
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.containers.RabbitMQContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

Expand All @@ -32,13 +33,21 @@ class AuthenticationIntegrationTest {
.withUsername("test")
.withPassword("test");

@Container
static RabbitMQContainer rabbitmq = new RabbitMQContainer("rabbitmq:3-management-alpine")
.withExposedPorts(5672, 15672);

@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
registry.add("spring.flyway.enabled", () -> "true");
registry.add("spring.jpa.hibernate.ddl-auto", () -> "validate");
registry.add("spring.rabbitmq.host", rabbitmq::getHost);
registry.add("spring.rabbitmq.port", rabbitmq::getAmqpPort);
registry.add("spring.rabbitmq.username", () -> "guest");
registry.add("spring.rabbitmq.password", () -> "guest");
}

@LocalServerPort
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ class AuthenticationServiceTest {
@Mock
private UserMapper userMapper;

@Mock
private UserEventPublisherService userEventPublisherService;

@InjectMocks
private AuthenticationService authenticationService;

Expand Down