Skip to content

Commit a7d2351

Browse files
authored
Merge pull request #1 from kasramp/mysql-container
MySQL Testcontainers
2 parents c5a8b16 + 5bb352a commit a7d2351

File tree

17 files changed

+265
-11
lines changed

17 files changed

+265
-11
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ hs_err_pid*
2525
.idea/
2626
target/
2727
*.iml
28+
29+
.DS_Store

README.md

+9-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ For the tutorials check the links below,
99

1010
## How to run
1111

12-
First start the docker-compose which contains ZooKeeper, Kafka, and Kafdrop.
12+
First start the docker-compose which contains ZooKeeper, Kafka, Kafdrop, and MySQL.
1313

1414
```bash
1515
$ docker-compose -f docker-compose.yml up -d
@@ -23,7 +23,13 @@ $ ./mvnw spring-boot:run
2323

2424
Open the browser `localhost:8080/apidocs`.
2525

26-
You can interact with the `random` api to create a random user which then will be sent to Kafka and consumed by the consumer (see [`consumer/UserKafkaListener.java`](https://github.com/kasramp/spring-kafka-test/blob/master/src/main/java/com/madadipouya/springkafkatest/consumer/UserKafkaListener.java) file).
26+
You can interact with the `random` API to create a random user which then will be sent to Kafka and consumed by the consumer (see [`consumer/UserKafkaListener.java`](https://github.com/kasramp/spring-kafka-test/blob/master/src/main/java/com/madadipouya/springkafkatest/consumer/UserKafkaListener.java) file) and finally saves into the database.
2727

2828
To see whether the message has been sent to Kafka, open your browser `http://localhost:8085/topic/com.madadipouya.kafka.use` (Kafdrop environment),
29-
you should be able to see all messages that sent to `kafka.user` topic.
29+
you should be able to see all messages that sent to `kafka.user` topic.
30+
31+
To run Flyway migration scripts only run,
32+
33+
```bash
34+
$ ./mvnw flyway:migrate
35+
```

docker-compose.yml

+9-1
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,12 @@ services:
4444
KAFKA_BROKERCONNECT: kafka:29092
4545
healthcheck:
4646
test: "curl -f kafdrop:9000/actuator/health || exit 1"
47-
start_period: 30s
47+
start_period: 30s
48+
49+
mysql:
50+
image: mysql:8.0.30
51+
ports:
52+
- "3306:3306"
53+
environment:
54+
- MYSQL_ROOT_PASSWORD=secret
55+
- MYSQL_DATABASE=test

pom.xml

+34-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>org.springframework.boot</groupId>
77
<artifactId>spring-boot-starter-parent</artifactId>
8-
<version>2.3.4.RELEASE</version>
8+
<version>2.5.14</version>
99
<relativePath/> <!-- lookup parent from repository -->
1010
</parent>
1111
<groupId>com.madadipouya</groupId>
@@ -16,6 +16,10 @@
1616

1717
<properties>
1818
<java.version>11</java.version>
19+
<flyway.version>7.7.3</flyway.version>
20+
<flyway.url>jdbc:mysql://localhost:3306/test</flyway.url>
21+
<flyway.user>root</flyway.user>
22+
<flyway.password>secret</flyway.password>
1923
</properties>
2024

2125
<dependencies>
@@ -27,6 +31,25 @@
2731
<groupId>org.springframework.kafka</groupId>
2832
<artifactId>spring-kafka</artifactId>
2933
</dependency>
34+
<dependency>
35+
<groupId>javax.validation</groupId>
36+
<artifactId>validation-api</artifactId>
37+
<version>2.0.1.Final</version>
38+
</dependency>
39+
<dependency>
40+
<groupId>org.springframework.boot</groupId>
41+
<artifactId>spring-boot-starter-data-jpa</artifactId>
42+
</dependency>
43+
<dependency>
44+
<groupId>mysql</groupId>
45+
<artifactId>mysql-connector-java</artifactId>
46+
<scope>runtime</scope>
47+
</dependency>
48+
<dependency>
49+
<groupId>org.flywaydb</groupId>
50+
<artifactId>flyway-core</artifactId>
51+
<version>${flyway.version}</version>
52+
</dependency>
3053
<dependency>
3154
<groupId>io.springfox</groupId>
3255
<artifactId>springfox-boot-starter</artifactId>
@@ -72,6 +95,12 @@
7295
<version>1.17.3</version>
7396
<scope>test</scope>
7497
</dependency>
98+
<dependency>
99+
<groupId>com.h2database</groupId>
100+
<artifactId>h2</artifactId>
101+
<version>2.1.214</version>
102+
<scope>test</scope>
103+
</dependency>
75104
</dependencies>
76105

77106
<build>
@@ -80,7 +109,10 @@
80109
<groupId>org.springframework.boot</groupId>
81110
<artifactId>spring-boot-maven-plugin</artifactId>
82111
</plugin>
112+
<plugin>
113+
<groupId>org.flywaydb</groupId>
114+
<artifactId>flyway-maven-plugin</artifactId>
115+
</plugin>
83116
</plugins>
84117
</build>
85-
86118
</project>

src/main/java/com/madadipouya/springkafkatest/controller/UserController.java

+16-5
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
import com.github.javafaker.Faker;
44
import com.madadipouya.springkafkatest.dto.User;
55
import com.madadipouya.springkafkatest.kafka.producer.UserKafkaProducer;
6+
import com.madadipouya.springkafkatest.service.UserService;
67
import io.swagger.annotations.Api;
78
import io.swagger.annotations.ApiOperation;
89
import org.springframework.http.HttpStatus;
9-
import org.springframework.web.bind.annotation.GetMapping;
10-
import org.springframework.web.bind.annotation.RequestMapping;
11-
import org.springframework.web.bind.annotation.ResponseStatus;
12-
import org.springframework.web.bind.annotation.RestController;
10+
import org.springframework.web.bind.annotation.*;
1311

12+
import java.util.List;
1413
import java.util.UUID;
14+
import java.util.stream.Collectors;
1515

1616
@RestController
1717
@RequestMapping("v1/users")
@@ -20,10 +20,13 @@ public class UserController {
2020

2121
private final UserKafkaProducer kafkaProducer;
2222

23+
private final UserService userService;
24+
2325
private final Faker faker;
2426

25-
public UserController(UserKafkaProducer kafkaProducer) {
27+
public UserController(UserKafkaProducer kafkaProducer, UserService userService) {
2628
this.kafkaProducer = kafkaProducer;
29+
this.userService = userService;
2730
faker = new Faker();
2831
}
2932

@@ -33,4 +36,12 @@ public UserController(UserKafkaProducer kafkaProducer) {
3336
public void generateRandomUser() {
3437
kafkaProducer.writeToKafka(new User(UUID.randomUUID().toString(), faker.name().firstName(), faker.name().lastName()));
3538
}
39+
40+
@GetMapping("/{firstName}")
41+
@ResponseStatus
42+
@ApiOperation(value = "Returns a list of users that matchers the given name")
43+
public List<User> getUsers(@PathVariable(name = "firstName") String name) {
44+
List<com.madadipouya.springkafkatest.entity.User> users = userService.getUsers(name);
45+
return users.stream().map(user -> new User(user.getId(), user.getFirstName(), user.getLastName())).collect(Collectors.toList());
46+
}
3647
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.madadipouya.springkafkatest.entity;
2+
3+
import javax.persistence.Column;
4+
import javax.persistence.Entity;
5+
import javax.persistence.Id;
6+
import javax.persistence.Table;
7+
import javax.validation.constraints.NotBlank;
8+
9+
@Entity()
10+
@Table(name = "users")
11+
public class User {
12+
13+
@Id
14+
private String id;
15+
16+
@NotBlank
17+
@Column(name = "first_name", nullable = false)
18+
private String firstName;
19+
20+
@NotBlank
21+
@Column(name = "last_name", nullable = false)
22+
private String lastName;
23+
24+
protected User() {
25+
26+
}
27+
28+
public User(String id, String firstName, String lastName) {
29+
this.id = id;
30+
this.firstName = firstName;
31+
this.lastName = lastName;
32+
}
33+
34+
public String getId() {
35+
return id;
36+
}
37+
38+
public String getFirstName() {
39+
return firstName;
40+
}
41+
42+
public void setFirstName(String firstName) {
43+
this.firstName = firstName;
44+
}
45+
46+
public String getLastName() {
47+
return lastName;
48+
}
49+
50+
public void setLastName(String lastName) {
51+
this.lastName = lastName;
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.madadipouya.springkafkatest.repository;
2+
3+
import com.madadipouya.springkafkatest.entity.User;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.stereotype.Repository;
6+
7+
import java.util.List;
8+
9+
@Repository
10+
public interface UserRepository extends JpaRepository<User, String> {
11+
12+
List<User> getByFirstNameIgnoreCaseOrderByFirstNameAscLastNameAsc(String firstName);
13+
14+
}

src/main/java/com/madadipouya/springkafkatest/service/UserService.java

+4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
import com.madadipouya.springkafkatest.dto.User;
44

5+
import java.util.List;
6+
57
public interface UserService {
68

79
void save(User user);
10+
11+
List<com.madadipouya.springkafkatest.entity.User> getUsers(String firstName);
812
}
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
11
package com.madadipouya.springkafkatest.service.impl;
22

33
import com.madadipouya.springkafkatest.dto.User;
4+
import com.madadipouya.springkafkatest.repository.UserRepository;
45
import com.madadipouya.springkafkatest.service.UserService;
56
import org.slf4j.Logger;
67
import org.slf4j.LoggerFactory;
78
import org.springframework.stereotype.Service;
89

10+
import java.util.List;
11+
912
@Service
1013
public class DefaultUserService implements UserService {
1114

1215
private static final Logger logger = LoggerFactory.getLogger(DefaultUserService.class);
1316

17+
private final UserRepository userRepository;
18+
19+
public DefaultUserService(UserRepository userRepository) {
20+
this.userRepository = userRepository;
21+
}
22+
1423
@Override
1524
public void save(User user) {
1625
logger.info("Saving user with id = {}", user.getUuid());
26+
userRepository.save(new com.madadipouya.springkafkatest.entity.User(user.getUuid(), user.getFirstName(), user.getLastName()));
27+
}
28+
29+
@Override
30+
public List<com.madadipouya.springkafkatest.entity.User> getUsers(String firstName) {
31+
return userRepository.getByFirstNameIgnoreCaseOrderByFirstNameAscLastNameAsc(firstName);
1732
}
1833
}

src/main/resources/application.properties

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
spring.jpa.hibernate.ddl-auto=none
2+
spring.datasource.hikari.maxLifetime=600000
3+
spring.datasource.initialization-mode=always
4+
spring.datasource.url=jdbc:mysql://localhost:3306/test
5+
spring.datasource.username=root
6+
spring.datasource.password=secret
17
spring.kafka.bootstrap-servers=localhost:9092
28
spring.kafka.topic.name=com.madadipouya.kafka.user
39
spring.kafka.replication.factor=3
@@ -13,3 +19,4 @@ spring.kafka.producer.value-serializer=org.springframework.kafka.support.seriali
1319
spring.kafka.producer.properties.spring.json.value.default.type=com.madadipouya.springkafkatest.dto.User
1420
# more about default serializer and deserializer here:
1521
# https://docs.spring.io/spring-kafka/api/constant-values.html
22+
#spring.flyway.enabled=true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CREATE TABLE IF NOT EXISTS users (
2+
id VARCHAR(36) PRIMARY KEY,
3+
first_name VARCHAR(256) NOT NULL,
4+
last_name VARCHAR(256) NOT NULL,
5+
CONSTRAINT uc_user_email_address UNIQUE(first_name, last_name),
6+
INDEX idx_user_id (id)
7+
) engine=InnoDB;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
INSERT INTO users (id, first_name, last_name) VALUES
2+
( 'b9e7100b-ff81-4a42-ace7-18802e00a9ab', 'Alejandro', 'Gillick'),
3+
( 'd8096b43-091d-46c4-acca-0869b0502e07', 'Frank', 'Castle'),
4+
( 'c656d2cd-8fe8-4640-b4ee-e9675790e926', 'John', 'Wick'),
5+
( '21494008-a54e-41d1-9c71-13344161378b', 'John', 'Rambo'),
6+
( 'cca1c70e-77d5-415e-8c2f-97ee55c3845d', 'Robert', 'McCall');

src/test/java/com/madadipouya/springkafkatest/kafka/UserKafkaTestcontainersTest.java

+6
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ class UserKafkaTestcontainersTest {
3131
@DynamicPropertySource
3232
static void kafkaProperties(DynamicPropertyRegistry registry) {
3333
registry.add("spring.kafka.bootstrap-servers", kafkaContainer::getBootstrapServers);
34+
// TODO - replace this with test container
35+
registry.add("spring.datasource.url", () -> "jdbc:h2:mem:test");
36+
registry.add("spring.datasource.driverClassName", () -> "org.h2.Driver");
37+
registry.add("spring.datasource.username", () -> "root");
38+
registry.add("spring.datasource.password", () -> "secret");
39+
registry.add("spring.flyway.enabled", () -> "false");
3440
}
3541

3642
@Autowired

src/test/java/com/madadipouya/springkafkatest/kafka/consumer/UserKafkaConsumerTest.java

+11
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import org.springframework.kafka.test.EmbeddedKafkaBroker;
2222
import org.springframework.kafka.test.context.EmbeddedKafka;
2323
import org.springframework.kafka.test.utils.KafkaTestUtils;
24+
import org.springframework.test.context.DynamicPropertyRegistry;
25+
import org.springframework.test.context.DynamicPropertySource;
2426

2527
import java.util.HashMap;
2628
import java.util.Map;
@@ -64,6 +66,15 @@ class UserKafkaConsumerTest {
6466
@Captor
6567
ArgumentCaptor<Long> offsetArgumentCaptor;
6668

69+
@DynamicPropertySource
70+
static void kafkaProperties(DynamicPropertyRegistry registry) {
71+
registry.add("spring.datasource.url", () -> "jdbc:h2:mem:test");
72+
registry.add("spring.datasource.driverClassName", () -> "org.h2.Driver");
73+
registry.add("spring.datasource.username", () -> "root");
74+
registry.add("spring.datasource.password", () -> "secret");
75+
registry.add("spring.flyway.enabled", () -> "false");
76+
}
77+
6778
@BeforeAll
6879
void setUp() {
6980
Map<String, Object> configs = new HashMap<>(KafkaTestUtils.producerProps(embeddedKafkaBroker));

src/test/java/com/madadipouya/springkafkatest/kafka/producer/UserKafkaProducerTest.java

+11
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import org.springframework.kafka.test.EmbeddedKafkaBroker;
2020
import org.springframework.kafka.test.context.EmbeddedKafka;
2121
import org.springframework.kafka.test.utils.ContainerTestUtils;
22+
import org.springframework.test.context.DynamicPropertyRegistry;
23+
import org.springframework.test.context.DynamicPropertySource;
2224

2325
import java.util.Map;
2426
import java.util.concurrent.BlockingQueue;
@@ -46,6 +48,15 @@ class UserKafkaProducerTest {
4648
@Autowired
4749
private ObjectMapper objectMapper;
4850

51+
@DynamicPropertySource
52+
static void kafkaProperties(DynamicPropertyRegistry registry) {
53+
registry.add("spring.datasource.url", () -> "jdbc:h2:mem:test");
54+
registry.add("spring.datasource.driverClassName", () -> "org.h2.Driver");
55+
registry.add("spring.datasource.username", () -> "root");
56+
registry.add("spring.datasource.password", () -> "secret");
57+
registry.add("spring.flyway.enabled", () -> "false");
58+
}
59+
4960
@BeforeAll
5061
void setUp() {
5162
DefaultKafkaConsumerFactory<String, String> consumerFactory = new DefaultKafkaConsumerFactory<>(getConsumerProperties());

0 commit comments

Comments
 (0)