This commit is contained in:
Florian THIERRY
2021-04-30 15:44:28 +02:00
commit df3b76f166
27 changed files with 1052 additions and 0 deletions

131
kafka-consumer/pom.xml Normal file
View File

@@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ippon.trainning.kafkaintegrationtest</groupId>
<artifactId>kafka-integration-test-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>kafka-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>11</java.version>
<jupiter.version>5.7.0</jupiter.version>
<build-helper-maven-plugin.version>3.2.0</build-helper-maven-plugin.version>
<integration-test.source.directory>src/integration-test/java</integration-test.source.directory>
<integration-test.resources.directory>src/integration-test/resources</integration-test.resources.directory>
<maven-failsafe-plugin.version>2.22.2</maven-failsafe-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.ippon.trainning.kafkaintegrationtest</groupId>
<artifactId>kafka-common-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Mandatory for executable jar -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
<!-- Tests d'intégrations, ajout des dossiers sources + exécution -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>${build-helper-maven-plugin.version}</version>
<executions>
<execution>
<id>add-integration-test</id>
<goals>
<goal>add-test-source</goal>
<goal>add-test-resource</goal>
</goals>
<configuration>
<sources>
<source>${integration-test.source.directory}</source>
</sources>
<resources>
<resource>
<directory>${integration-test.resources.directory}</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${maven-failsafe-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,85 @@
package com.ippon.trainning.kafkaintegrationtest.kafkaconsumer.consumer;
import com.ippon.trainning.kafkaintegrationtest.kafkacommontest.extension.EmbeddedKafkaExtension;
import com.ippon.trainning.kafkaintegrationtest.kafkaconsumer.service.MessageService;
import org.apache.kafka.common.serialization.StringSerializer;
import org.awaitility.Durations;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.kafka.core.DefaultKafkaProducerFactory;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.core.ProducerFactory;
import org.springframework.kafka.test.context.EmbeddedKafka;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import java.util.Map;
import java.util.UUID;
import static org.apache.kafka.clients.producer.ProducerConfig.*;
import static org.awaitility.Awaitility.await;
import static org.mockito.BDDMockito.then;
@ExtendWith({
SpringExtension.class,
MockitoExtension.class,
EmbeddedKafkaExtension.class
})
@SpringBootTest(classes = {
KafkaConsumerIT.KafkaConsumerITConfiguration.class
}, properties = "spring.kafka.bootstrap-servers=${spring.embedded.kafka.brokers}")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@EmbeddedKafka
@ActiveProfiles({"test"})
public class KafkaConsumerIT {
@TestConfiguration
public static class KafkaConsumerITConfiguration {
@Bean
public DefaultKafkaProducerFactory<String, String> producerFactory(
@Value("${server.kafka.bootstrapAddress}") String bootstrapAddress
) {
Map<String, Object> configProperties = Map.of(
BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress,
KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class,
VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class
);
return new DefaultKafkaProducerFactory<>(configProperties);
}
@Bean
public KafkaTemplate<String, String> kafkaTemplate(ProducerFactory<String, String> producerFactory) {
return new KafkaTemplate<>(producerFactory);
}
}
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
@Value("${server.kafka.topic}")
private String topic;
@MockBean
private MessageService messageService;
@Test
void should_comsume_a_message_from_kafka_topic() {
// given
String key = UUID.randomUUID().toString();
String message = "A message to consume";
// when
kafkaTemplate.send(topic, key, message);
// then
await().atMost(Durations.FIVE_SECONDS).untilAsserted(() -> then(messageService).should().handleMessage(key, message));
}
}

View File

@@ -0,0 +1,5 @@
server:
kafka:
bootstrapAddress: "${spring.embedded.kafka.brokers:}"
topic: topic-test
groupId: groupId-test

View File

@@ -0,0 +1,11 @@
package com.ippon.trainning.kafkaintegrationtest.kafkaconsumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class KafkaConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(KafkaConsumerApplication.class, args);
}
}

View File

@@ -0,0 +1,46 @@
package com.ippon.trainning.kafkaintegrationtest.kafkaconsumer.config;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
import java.util.Map;
import static org.apache.kafka.clients.CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG;
import static org.apache.kafka.clients.CommonClientConfigs.GROUP_ID_CONFIG;
import static org.apache.kafka.clients.consumer.ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG;
import static org.apache.kafka.clients.consumer.ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG;
@Configuration
public class KafkaConsumerConfiguration {
private final String bootstrapAddress;
private final String groupId;
public KafkaConsumerConfiguration(@Value("${server.kafka.bootstrapAddress}") String bootstrapAddress,
@Value("${server.kafka.groupId}") String groupId) {
this.bootstrapAddress = bootstrapAddress;
this.groupId = groupId;
}
@Bean
public ConsumerFactory<String, String> consumerFactory() {
Map<String, Object> props = Map.of(
BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress,
VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class,
KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class,
GROUP_ID_CONFIG, groupId
);
return new DefaultKafkaConsumerFactory<>(props);
}
@Bean
public ConcurrentKafkaListenerContainerFactory<String, String> containerFactory(ConsumerFactory<String, String> consumerFactory) {
ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory);
return factory;
}
}

View File

@@ -0,0 +1,25 @@
package com.ippon.trainning.kafkaintegrationtest.kafkaconsumer.consumer;
import com.ippon.trainning.kafkaintegrationtest.kafkaconsumer.service.MessageService;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class KafkaConsumer {
private final MessageService messageService;
public KafkaConsumer(MessageService messageService) {
this.messageService = messageService;
}
@KafkaListener(
containerFactory = "containerFactory",
topics = "${server.kafka.topic}"
)
public void listenTopic(ConsumerRecord<String, String> record) {
messageService.handleMessage(record.key(), record.value());
}
}

View File

@@ -0,0 +1,12 @@
package com.ippon.trainning.kafkaintegrationtest.kafkaconsumer.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class MessageService {
public void handleMessage(String key, String message) {
log.info("Message consumed: <{}> - <{}>", key, message);
}
}

View File

@@ -0,0 +1,6 @@
server:
port: 8081
kafka:
bootstrapAddress: localhost:9092
topic: topic-test
groupId: groupId-test

View File

@@ -0,0 +1,13 @@
package com.ippon.trainning.kafkaintegrationtest.kafkaconsumer;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class KafkaConsumerApplicationTests {
@Test
void contextLoads() {
}
}