diff --git a/compose.yaml b/compose.yaml
index 6d6e6a1..c19b286 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -26,69 +26,33 @@ services:
- cassandra-extractor:
- image: busybox:latest
- container_name: cassandra-extractor
- volumes:
- - ./docker/cassandra_exporter/datastax-mcac-agent-0.3.5.tar.gz:/opt/datastax-mcac-agent-0.3.5.tar.gz
- - cassandra_mcac:/opt/datastax-mcac-agent-0.3.5
- command: sh -c "tar -xzf /opt/datastax-mcac-agent-0.3.5.tar.gz -C /opt/"
-
-
- d1r1n1:
- image: cassandra:4.0.12
- container_name: d1r1n1
- environment: &environment
- CASSANDRA_SEEDS: d1r1n1
- CASSANDRA_CLUSTER_NAME: C1
- CASSANDRA_DC: D1
- CASSANDRA_RACK: R1
- CASSANDRA_ENDPOINT_SNITCH: GossipingPropertyFileSnitch
- CASSANDRA_NUM_TOKENS: 128
- JVM_OPTS: -javaagent:/opt/datastax-mcac-agent-0.3.5/lib/datastax-mcac-agent.jar
+ mariadb:
+ image: 'mariadb:11.3.2'
+ container_name: mariadb
+ environment:
+ - 'MARIADB_DATABASE=hicha'
+ - 'MARIADB_PASSWORD=secret'
+ - 'MARIADB_ROOT_PASSWORD=verysecret'
+ - 'MARIADB_USER=myuser'
ports:
- - "9042:9042"
- - "9103:9103"
- depends_on:
- cassandra-extractor:
- condition: service_completed_successfully
- volumes: &mcac
- - cassandra_mcac:/opt/datastax-mcac-agent-0.3.5
- healthcheck:
- test: [ "CMD-SHELL", "[ $$(nodetool statusgossip) = running ]" ]
-
+ - '3306:3306'
+ # deploy:
+ # resources:
+ # limits:
+ # cpus: '1' # Minimum CPU requirement for MariaDB (10% of one core)
+ # memory: 256M # Minimum memory requirement for MariaDB (256MB)
- d1r1n2:
- image: cassandra:4.0.12
- container_name: d1r1n2
- ports:
- - "9043:9042"
- - "9104:9103"
- environment:
- <<: *environment
-
- volumes: *mcac
- depends_on:
- d1r1n1:
- condition: service_healthy
- cassandra-extractor:
- condition: service_completed_successfully
- d1r1n3:
- image: cassandra:4.0.12
- container_name: d1r1n3
+ mariadb-exporter:
+ container_name: mariadb-exporter
+ image: 'prom/mysqld-exporter'
+ restart: always
+ command:
+ - "--mysqld.username=root:verysecret"
+ - "--mysqld.address=host.docker.internal:3306"
ports:
- - "9044:9042"
- - "9105:9103"
- environment:
- <<: *environment
- volumes: *mcac
- depends_on:
- d1r1n1:
- condition: service_healthy
- cassandra-extractor:
- condition: service_completed_successfully
+ - "9104:9104"
diff --git a/data.sql b/data.sql
new file mode 100644
index 0000000..2ea8a73
--- /dev/null
+++ b/data.sql
@@ -0,0 +1,5 @@
+insert into users(username) value ('huyvu'), ('admin'), ('testuser');
+insert into conversations () value ();
+INSERT INTO user_conversations (user_id, conversation_id) VALUES ((select users.user_id from users where username = 'huyvu' limit 1), (select conversation_id from conversations limit 1));
+insert into messages (conversation_id, sender_id, message_text)
+values (1, 1, 'This is a test message.');
\ No newline at end of file
diff --git a/hicha-business/pom.xml b/hicha-business/pom.xml
index 1052b46..f7cb53b 100644
--- a/hicha-business/pom.xml
+++ b/hicha-business/pom.xml
@@ -3,12 +3,10 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- org.springframework.boot
- spring-boot-starter-parent
- 3.2.5
-
+ io.huyvu.hicha
+ hicha-backend
+ 1.0-SNAPSHOT
- io.huyvu.hicha
hicha-business
0.0.1-SNAPSHOT
hicha-business
@@ -19,7 +17,6 @@
-
org.springframework.boot
spring-boot-starter-web
@@ -39,27 +36,11 @@
true
-
- org.springframework.boot
- spring-boot-starter-data-cassandra
-
-
-
org.springframework.boot
spring-boot-configuration-processor
true
-
- org.projectlombok
- lombok
- true
-
org.mapstruct
@@ -97,18 +78,6 @@
-
-
-
- org.testcontainers
- cassandra
- test
-
-
org.springframework.boot
spring-boot-starter-actuator
@@ -135,6 +104,20 @@
micrometer-registry-prometheus
+
+
+ io.huyvu.hicha
+ hicha-repository
+ 1.0-SNAPSHOT
+
+
+ io.huyvu.hicharepositoryimpl
+ hicha-repository-impl
+ 1.0-SNAPSHOT
+ runtime
+
+
+
@@ -147,7 +130,7 @@
org.springframework.boot
spring-boot-maven-plugin
- io.huyvu.hicha.hichabusiness.HichaBusinessApplication
+ io.huyvu.hicha.HichaBusinessApplication
org.projectlombok
@@ -197,8 +180,8 @@
- io/huyvu/hicha/hichabusiness/HichaBusinessApplication.class
- io/huyvu/hicha/hichabusiness/config/nativebuild/*
+ io/huyvu/hicha/HichaBusinessApplication.class
+ io/huyvu/hicha/config/nativebuild/*
diff --git a/hicha-business/src/main/java/io/huyvu/hicha/HichaBusinessApplication.java b/hicha-business/src/main/java/io/huyvu/hicha/HichaBusinessApplication.java
new file mode 100644
index 0000000..2aec71c
--- /dev/null
+++ b/hicha-business/src/main/java/io/huyvu/hicha/HichaBusinessApplication.java
@@ -0,0 +1,11 @@
+package io.huyvu.hicha;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class HichaBusinessApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(HichaBusinessApplication.class, args);
+ }
+}
diff --git a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/config/SecurityConfig.java b/hicha-business/src/main/java/io/huyvu/hicha/config/SecurityConfig.java
similarity index 97%
rename from hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/config/SecurityConfig.java
rename to hicha-business/src/main/java/io/huyvu/hicha/config/SecurityConfig.java
index e02a0c8..6dba7e5 100644
--- a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/config/SecurityConfig.java
+++ b/hicha-business/src/main/java/io/huyvu/hicha/config/SecurityConfig.java
@@ -1,4 +1,4 @@
-package io.huyvu.hicha.hichabusiness.config;
+package io.huyvu.hicha.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
diff --git a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/config/ThreadsConfiguration.java b/hicha-business/src/main/java/io/huyvu/hicha/config/ThreadsConfiguration.java
similarity index 95%
rename from hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/config/ThreadsConfiguration.java
rename to hicha-business/src/main/java/io/huyvu/hicha/config/ThreadsConfiguration.java
index 04bd920..7581e81 100644
--- a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/config/ThreadsConfiguration.java
+++ b/hicha-business/src/main/java/io/huyvu/hicha/config/ThreadsConfiguration.java
@@ -1,4 +1,4 @@
-package io.huyvu.hicha.hichabusiness.config;
+package io.huyvu.hicha.config;
import org.apache.coyote.ProtocolHandler;
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
diff --git a/hicha-business/src/main/java/io/huyvu/hicha/controller/MessageController.java b/hicha-business/src/main/java/io/huyvu/hicha/controller/MessageController.java
new file mode 100644
index 0000000..cfbe132
--- /dev/null
+++ b/hicha-business/src/main/java/io/huyvu/hicha/controller/MessageController.java
@@ -0,0 +1,34 @@
+package io.huyvu.hicha.controller;
+
+import io.huyvu.hicha.model.ConversationDetails;
+import io.huyvu.hicha.repository.model.Message;
+import io.huyvu.hicha.repository.repo.MessageRepository;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.time.Instant;
+
+@RestController
+@RequestMapping("api/v1/message")
+@RequiredArgsConstructor
+public class MessageController {
+ private final MessageRepository messageRepository;
+
+ @PostMapping
+ void sendMessage(@RequestBody Message message) {
+ if(message.getSentAt() == null){
+ message.setSentAt(Instant.now());
+ }
+ messageRepository.save(message);
+ }
+
+ @GetMapping("{id}")
+ ConversationDetails getConversationDetails(@PathVariable Long id) {
+ var messages = messageRepository.findByConversationId(id);
+ return ConversationDetails.builder()
+ .conversationId(id)
+ .conversationName("Conversation " + id)
+ .messages(messages)
+ .build();
+ }
+}
diff --git a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/HichaBusinessApplication.java b/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/HichaBusinessApplication.java
deleted file mode 100644
index 4a7009b..0000000
--- a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/HichaBusinessApplication.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package io.huyvu.hicha.hichabusiness;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;
-
-@SpringBootApplication
-@EnableCassandraRepositories()
-public class HichaBusinessApplication {
- public static void main(String[] args) {
- SpringApplication.run(HichaBusinessApplication.class, args);
- }
-}
diff --git a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/config/CassandraConfig.java b/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/config/CassandraConfig.java
deleted file mode 100644
index 4f8c778..0000000
--- a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/config/CassandraConfig.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package io.huyvu.hicha.hichabusiness.config;
-
-import org.springframework.boot.autoconfigure.cassandra.CassandraConnectionDetails;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Primary;
-
-import java.util.List;
-
-@Configuration
-public class CassandraConfig {
- @Bean
- @Primary
- CassandraConnectionDetails mainConnection(List conns){
- return conns.getFirst();
- }
-}
diff --git a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/controller/MessageController.java b/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/controller/MessageController.java
deleted file mode 100644
index d697fec..0000000
--- a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/controller/MessageController.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package io.huyvu.hicha.hichabusiness.controller;
-
-import io.huyvu.hicha.hichabusiness.model.Message;
-import io.huyvu.hicha.hichabusiness.repository.MessageCassandraRepository;
-import lombok.RequiredArgsConstructor;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-@RestController
-@RequestMapping("api/v1/message")
-@RequiredArgsConstructor
-public class MessageController {
- private final MessageCassandraRepository messageRepository;
-
- @GetMapping
- public List getAllMessages() {
- var result = new ArrayList();
- Iterable all = messageRepository.findAll();
- all.forEach(result::add);
- return result;
- }
-
-
- @PostMapping
- public Message saveMessage(@RequestBody Message message) {
- if(message.getMessageId() == null){
- message.setMessageId(UUID.randomUUID());
- }
- return messageRepository.save(message);
- }
-
- @GetMapping("{id}")
- public Message getMessage(@PathVariable Long id) {
- return messageRepository.findById(id).orElse(null);
- }
-}
diff --git a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/model/Message.java b/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/model/Message.java
deleted file mode 100644
index 6b18f40..0000000
--- a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/model/Message.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package io.huyvu.hicha.hichabusiness.model;
-
-import lombok.AccessLevel;
-import lombok.Data;
-import lombok.experimental.FieldDefaults;
-import org.springframework.data.cassandra.core.mapping.Column;
-import org.springframework.data.cassandra.core.mapping.PrimaryKey;
-import org.springframework.data.cassandra.core.mapping.Table;
-
-import java.time.Instant;
-import java.util.UUID;
-
-@Data
-@Table("messages")
-@FieldDefaults(level = AccessLevel.PRIVATE)
-public class Message {
- @PrimaryKey
- @Column("message_id")
- UUID messageId;
- @Column("conversation_id")
- Integer conversationId;
- @Column("sender_id")
- Integer senderId;
- @Column("message_text")
- String messageText;
- @Column("sent_at")
- Instant sentAt;
-
-
-}
diff --git a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/model/MessageContent.java b/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/model/MessageContent.java
deleted file mode 100644
index 4c5d78d..0000000
--- a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/model/MessageContent.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package io.huyvu.hicha.hichabusiness.model;
-
-public record MessageContent(long conversationId, String messageText) {
-}
diff --git a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/model/MessageInsert.java b/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/model/MessageInsert.java
deleted file mode 100644
index be66eae..0000000
--- a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/model/MessageInsert.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package io.huyvu.hicha.hichabusiness.model;
-
-public record MessageInsert(Long conversationId, Long senderId, String messageText) {
-}
diff --git a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/repository/MessageCassandraRepository.java b/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/repository/MessageCassandraRepository.java
deleted file mode 100644
index b79a3d5..0000000
--- a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/repository/MessageCassandraRepository.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package io.huyvu.hicha.hichabusiness.repository;
-
-import io.huyvu.hicha.hichabusiness.model.Message;
-import org.springframework.data.repository.CrudRepository;
-
-import java.util.List;
-
-public interface MessageCassandraRepository extends CrudRepository {
- List findByMessageId(long messageId);
-}
diff --git a/hicha-business/src/main/java/io/huyvu/hicha/model/ConversationDetails.java b/hicha-business/src/main/java/io/huyvu/hicha/model/ConversationDetails.java
new file mode 100644
index 0000000..f871de1
--- /dev/null
+++ b/hicha-business/src/main/java/io/huyvu/hicha/model/ConversationDetails.java
@@ -0,0 +1,18 @@
+package io.huyvu.hicha.model;
+
+import io.huyvu.hicha.repository.model.Message;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Data;
+import lombok.experimental.FieldDefaults;
+
+import java.util.List;
+
+@Data
+@FieldDefaults(level = AccessLevel.PRIVATE)
+@Builder
+public class ConversationDetails {
+ String conversationName;
+ Long conversationId;
+ List messages;
+}
diff --git a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/model/UserDTO.java b/hicha-business/src/main/java/io/huyvu/hicha/model/UserDTO.java
similarity index 83%
rename from hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/model/UserDTO.java
rename to hicha-business/src/main/java/io/huyvu/hicha/model/UserDTO.java
index 16ca465..36f3d5a 100644
--- a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/model/UserDTO.java
+++ b/hicha-business/src/main/java/io/huyvu/hicha/model/UserDTO.java
@@ -1,4 +1,4 @@
-package io.huyvu.hicha.hichabusiness.model;
+package io.huyvu.hicha.model;
import lombok.*;
import lombok.experimental.FieldDefaults;
diff --git a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/model/UserEntity.java b/hicha-business/src/main/java/io/huyvu/hicha/model/UserEntity.java
similarity index 81%
rename from hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/model/UserEntity.java
rename to hicha-business/src/main/java/io/huyvu/hicha/model/UserEntity.java
index c18b82d..e4e6be5 100644
--- a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/model/UserEntity.java
+++ b/hicha-business/src/main/java/io/huyvu/hicha/model/UserEntity.java
@@ -1,4 +1,4 @@
-package io.huyvu.hicha.hichabusiness.model;
+package io.huyvu.hicha.model;
import lombok.*;
import lombok.experimental.FieldDefaults;
diff --git a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/service/Post.java b/hicha-business/src/main/java/io/huyvu/hicha/service/Post.java
similarity index 63%
rename from hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/service/Post.java
rename to hicha-business/src/main/java/io/huyvu/hicha/service/Post.java
index 84d4bbe..4f60fb6 100644
--- a/hicha-business/src/main/java/io/huyvu/hicha/hichabusiness/service/Post.java
+++ b/hicha-business/src/main/java/io/huyvu/hicha/service/Post.java
@@ -1,4 +1,4 @@
-package io.huyvu.hicha.hichabusiness.service;
+package io.huyvu.hicha.service;
public record Post(Integer id, Integer userId, String title, String body) {
}
diff --git a/hicha-business/src/main/resources/application.yml b/hicha-business/src/main/resources/application.yml
index 731f1b6..549b584 100644
--- a/hicha-business/src/main/resources/application.yml
+++ b/hicha-business/src/main/resources/application.yml
@@ -5,17 +5,6 @@ spring:
init:
mode: always
- cassandra:
- schema-action: create_if_not_exists
- request:
- timeout: 60s
- connection:
- connect-timeout: 60s
- init-query-timeout: 60s
- keyspace-name: hicha
- local-datacenter: D1
- contact-points: d1r1n1
-
management:
endpoints:
@@ -33,3 +22,4 @@ management:
# level:
# root: info
+
diff --git a/hicha-business/src/main/resources/schema.sql b/hicha-business/src/main/resources/schema.sql
index ef053a7..264d53d 100644
--- a/hicha-business/src/main/resources/schema.sql
+++ b/hicha-business/src/main/resources/schema.sql
@@ -1,25 +1,33 @@
-CREATE KEYSPACE IF NOT EXISTS hicha
- WITH replication = {
- 'class': 'org.apache.cassandra.locator.NetworkTopologyStrategy',
- 'replication_factor': 3
- };
-USE hicha;
+# use hicha;
+create table if not exists users
+(
+ user_id bigint primary key auto_increment,
+ username varchar(255) not null
+);
-CREATE TABLE IF NOT EXISTS messages
+create table if not exists conversations
(
- conversation_id int,
- message_id uuid,
- sender_id int,
- message_text text,
- sent_at timestamp,
- PRIMARY KEY (conversation_id, sent_at)
-) WITH CLUSTERING ORDER BY (sent_at DESC);
+ conversation_id bigint auto_increment primary key
+);
+
+create table if not exists user_conversations
+(
+ user_id bigint,
+ conversation_id bigint,
+ primary key (user_id, conversation_id),
+ foreign key (user_id) references users (user_id),
+ foreign key (conversation_id) references conversations (conversation_id)
+);
-INSERT INTO hicha.messages (message_id, conversation_id, sender_id, message_text, sent_at)
-VALUES (1, 1, 1001, 'Hello, how are you?', '2024-05-03 08:00:00');
+create table if not exists messages
+(
+ message_id bigint auto_increment primary key,
+ conversation_id bigint,
+ sender_id bigint,
+ message_text text,
+ sent_at timestamp default current_timestamp,
+ foreign key (conversation_id) references conversations (conversation_id),
+ foreign key (sender_id) references users (user_id)
+);
-INSERT INTO hicha.messages (message_id, conversation_id, sender_id, message_text, sent_at)
-VALUES (2, 1, 1002, 'Im fine, thank you!', '2024-05-03 08:05:00');
-INSERT INTO hicha.messages (message_id, conversation_id, sender_id, message_text, sent_at)
-VALUES (3, 2, 1001, 'Hey there!', '2024-05-03 08:10:00');
diff --git a/hicha-business/src/test/java/io/huyvu/hicha/HichaBusinessApplicationTests.java b/hicha-business/src/test/java/io/huyvu/hicha/HichaBusinessApplicationTests.java
new file mode 100644
index 0000000..4b1d913
--- /dev/null
+++ b/hicha-business/src/test/java/io/huyvu/hicha/HichaBusinessApplicationTests.java
@@ -0,0 +1,14 @@
+package io.huyvu.hicha;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.testcontainers.junit.jupiter.Testcontainers;
+
+
+@Testcontainers
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@Slf4j
+class HichaBusinessApplicationTests {
+
+
+}
\ No newline at end of file
diff --git a/hicha-business/src/test/java/io/huyvu/hicha/hichabusiness/HichaBusinessApplicationTests.java b/hicha-business/src/test/java/io/huyvu/hicha/hichabusiness/HichaBusinessApplicationTests.java
deleted file mode 100644
index faadaaa..0000000
--- a/hicha-business/src/test/java/io/huyvu/hicha/hichabusiness/HichaBusinessApplicationTests.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package io.huyvu.hicha.hichabusiness;
-
-import io.huyvu.hicha.hichabusiness.model.UserDTO;
-import lombok.extern.slf4j.Slf4j;
-import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.web.client.TestRestTemplate;
-import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
-import org.testcontainers.containers.CassandraContainer;
-import org.testcontainers.junit.jupiter.Container;
-import org.testcontainers.junit.jupiter.Testcontainers;
-
-import java.util.List;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/*
- * Integration Test
- */
-
-@Testcontainers
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
-@Slf4j
-class HichaBusinessApplicationTests {
-
- @Container
- @ServiceConnection
- static CassandraContainer> cassandraDatabase = new CassandraContainer<>("cassandra:4.0.12");
-
-
- @Autowired
- TestRestTemplate restTemplate;
-
- @Test
- void returnAtLeaseOne(){
- var response = restTemplate.getForObject("/api/v1/user", List.class);
- assertThat(response.size()).isGreaterThan(0);
- }
-
- @Test
- void saveOneUser(){
-
- var result = restTemplate.postForObject("/api/v1/user", new UserDTO(null, "Jack Ma"), String.class);
- assertThat(result).isEqualTo("Success");
-
- var response = restTemplate.getForObject("/api/v1/user", List.class);
- assertThat(response.size()).isGreaterThan(1);
- }
-
- @Test
- void findByIdReturnOne(){
- UserDTO forObject = restTemplate.withBasicAuth("huyvu", "password").getForObject("/api/v1/user/1", UserDTO.class);
- log.info(String.valueOf(forObject));
- assertThat(forObject).isNotNull();
- }
-
- @Test
- void findByIdReturnNull(){
- UserDTO forObject = restTemplate.getForObject("/api/v1/user/-1", UserDTO.class);
- assertThat(forObject).isNull();
- }
-
-}
diff --git a/hicha-business/src/test/java/io/huyvu/hicha/hichabusiness/repository/MessageRepositoryTest.java b/hicha-business/src/test/java/io/huyvu/hicha/hichabusiness/repository/MessageRepositoryTest.java
deleted file mode 100644
index 78f35c0..0000000
--- a/hicha-business/src/test/java/io/huyvu/hicha/hichabusiness/repository/MessageRepositoryTest.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package io.huyvu.hicha.hichabusiness.repository;
-
-import com.github.javafaker.Faker;
-import io.huyvu.hicha.hichabusiness.model.Message;
-import lombok.extern.slf4j.Slf4j;
-import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.autoconfigure.data.cassandra.AutoConfigureDataCassandra;
-import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest;
-import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
-import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
-import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
-import org.testcontainers.containers.CassandraContainer;
-import org.testcontainers.junit.jupiter.Container;
-import org.testcontainers.junit.jupiter.Testcontainers;
-
-import java.util.Locale;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@Testcontainers
-@DataCassandraTest
-@AutoConfigureDataCassandra
-@Slf4j
-class MessageRepositoryTest {
- @Container
- @ServiceConnection
- static CassandraContainer> cassandraDatabase = new CassandraContainer<>("cassandra:4.0.12");
-
- @Autowired
- MessageCassandraRepository repository;
-
- static Faker faker = new Faker(Locale.of("vi"));
-
-
- @Test
- void shouldReturnMessages(){
- Iterable messages = repository.findAll();
-
- messages.forEach(e->log.info("item: {}",e.toString()));
-
- assertThat(2).isGreaterThan(1);
- }
-
-}
\ No newline at end of file
diff --git a/hicha-repository-impl/pom.xml b/hicha-repository-impl/pom.xml
new file mode 100644
index 0000000..a4d2e47
--- /dev/null
+++ b/hicha-repository-impl/pom.xml
@@ -0,0 +1,54 @@
+
+
+ 4.0.0
+
+ io.huyvu.hicha
+ hicha-backend
+ 1.0-SNAPSHOT
+
+
+ io.huyvu.hicharepositoryimpl
+ hicha-repository-impl
+
+
+ 21
+ 21
+ UTF-8
+
+
+
+
+
+ io.huyvu.hicha
+ hicha-repository
+ 1.0-SNAPSHOT
+
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ 3.0.3
+
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter-test
+ 3.0.3
+ test
+
+
+
+ org.mariadb.jdbc
+ mariadb-java-client
+ runtime
+
+
+
+ org.testcontainers
+ mariadb
+ test
+
+
+
\ No newline at end of file
diff --git a/hicha-repository-impl/src/main/java/io/huyvu/hicha/config/MyBatisConfiguration.java b/hicha-repository-impl/src/main/java/io/huyvu/hicha/config/MyBatisConfiguration.java
new file mode 100644
index 0000000..2f8e1cf
--- /dev/null
+++ b/hicha-repository-impl/src/main/java/io/huyvu/hicha/config/MyBatisConfiguration.java
@@ -0,0 +1,17 @@
+package io.huyvu.hicha.config;
+
+import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class MyBatisConfiguration {
+ @Bean
+ @ConditionalOnProperty(name = "mybatis.configuration.map-underscore-to-camel-case", matchIfMissing = true)
+ ConfigurationCustomizer mybatisConfigurationCustomizer() {
+ return configuration -> {
+ configuration.setMapUnderscoreToCamelCase(true);
+ };
+ }
+}
\ No newline at end of file
diff --git a/hicha-repository-impl/src/main/java/io/huyvu/hicha/config/MyBatisNativeConfiguration.java b/hicha-repository-impl/src/main/java/io/huyvu/hicha/config/MyBatisNativeConfiguration.java
new file mode 100644
index 0000000..2747e15
--- /dev/null
+++ b/hicha-repository-impl/src/main/java/io/huyvu/hicha/config/MyBatisNativeConfiguration.java
@@ -0,0 +1,303 @@
+package io.huyvu.hicha.config;
+
+import org.apache.commons.logging.LogFactory;
+import org.apache.ibatis.annotations.DeleteProvider;
+import org.apache.ibatis.annotations.InsertProvider;
+import org.apache.ibatis.annotations.SelectProvider;
+import org.apache.ibatis.annotations.UpdateProvider;
+import org.apache.ibatis.cache.decorators.FifoCache;
+import org.apache.ibatis.cache.decorators.LruCache;
+import org.apache.ibatis.cache.decorators.SoftCache;
+import org.apache.ibatis.cache.decorators.WeakCache;
+import org.apache.ibatis.cache.impl.PerpetualCache;
+import org.apache.ibatis.executor.Executor;
+import org.apache.ibatis.executor.parameter.ParameterHandler;
+import org.apache.ibatis.executor.resultset.ResultSetHandler;
+import org.apache.ibatis.executor.statement.BaseStatementHandler;
+import org.apache.ibatis.executor.statement.RoutingStatementHandler;
+import org.apache.ibatis.executor.statement.StatementHandler;
+import org.apache.ibatis.javassist.util.proxy.ProxyFactory;
+import org.apache.ibatis.javassist.util.proxy.RuntimeSupport;
+import org.apache.ibatis.logging.Log;
+import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl;
+import org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl;
+import org.apache.ibatis.logging.log4j2.Log4j2Impl;
+import org.apache.ibatis.logging.nologging.NoLoggingImpl;
+import org.apache.ibatis.logging.slf4j.Slf4jImpl;
+import org.apache.ibatis.logging.stdout.StdOutImpl;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.reflection.TypeParameterResolver;
+import org.apache.ibatis.scripting.defaults.RawLanguageDriver;
+import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.SqlSessionFactoryBean;
+import org.mybatis.spring.mapper.MapperFactoryBean;
+import org.mybatis.spring.mapper.MapperScannerConfigurer;
+import org.springframework.aot.hint.MemberCategory;
+import org.springframework.aot.hint.RuntimeHints;
+import org.springframework.aot.hint.RuntimeHintsRegistrar;
+import org.springframework.beans.PropertyValue;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
+import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
+import org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.config.ConstructorArgumentValues;
+import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
+import org.springframework.beans.factory.support.RegisteredBean;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.ImportRuntimeHints;
+import org.springframework.core.ResolvableType;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ReflectionUtils;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * This configuration will move to mybatis-spring-native.
+ */
+@Configuration(proxyBeanMethods = false)
+@ImportRuntimeHints(MyBatisNativeConfiguration.MyBaitsRuntimeHintsRegistrar.class)
+public class MyBatisNativeConfiguration {
+
+ @Bean
+ MyBatisBeanFactoryInitializationAotProcessor myBatisBeanFactoryInitializationAotProcessor() {
+ return new MyBatisBeanFactoryInitializationAotProcessor();
+ }
+
+ @Bean
+ static MyBatisMapperFactoryBeanPostProcessor myBatisMapperFactoryBeanPostProcessor() {
+ return new MyBatisMapperFactoryBeanPostProcessor();
+ }
+
+ static class MyBaitsRuntimeHintsRegistrar implements RuntimeHintsRegistrar {
+
+ @Override
+ public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
+ Stream.of(RawLanguageDriver.class,
+ // TODO 增加了MybatisXMLLanguageDriver.class
+ XMLLanguageDriver.class,
+ RuntimeSupport.class,
+ ProxyFactory.class,
+ Slf4jImpl.class,
+ Log.class,
+ JakartaCommonsLoggingImpl.class,
+ Log4j2Impl.class,
+ Jdk14LoggingImpl.class,
+ StdOutImpl.class,
+ NoLoggingImpl.class,
+ SqlSessionFactory.class,
+ PerpetualCache.class,
+ FifoCache.class,
+ LruCache.class,
+ SoftCache.class,
+ WeakCache.class,
+ //TODO 增加了MybatisSqlSessionFactoryBean.class
+ SqlSessionFactoryBean.class,
+ ArrayList.class,
+ HashMap.class,
+ TreeSet.class,
+ HashSet.class
+ ).forEach(x -> hints.reflection().registerType(x, MemberCategory.values()));
+ Stream.of(
+ "org/apache/ibatis/builder/xml/*.dtd",
+ "org/apache/ibatis/builder/xml/*.xsd"
+ ).forEach(hints.resources()::registerPattern);
+
+ hints.serialization().registerType(java.lang.invoke.SerializedLambda.class);
+ hints.reflection().registerType(java.lang.invoke.SerializedLambda.class);
+
+ hints.proxies().registerJdkProxy(StatementHandler.class);
+ hints.proxies().registerJdkProxy(Executor.class);
+ hints.proxies().registerJdkProxy(ResultSetHandler.class);
+ hints.proxies().registerJdkProxy(ParameterHandler.class);
+
+// hints.reflection().registerType(MybatisPlusInterceptor.class);
+
+
+ hints.reflection().registerType(BoundSql.class,MemberCategory.DECLARED_FIELDS);
+ hints.reflection().registerType(RoutingStatementHandler.class,MemberCategory.DECLARED_FIELDS);
+ hints.reflection().registerType(BaseStatementHandler.class,MemberCategory.DECLARED_FIELDS);
+
+ }
+ }
+
+ static class MyBatisBeanFactoryInitializationAotProcessor
+ implements BeanFactoryInitializationAotProcessor, BeanRegistrationExcludeFilter {
+
+ private final Set> excludeClasses = new HashSet<>();
+
+ MyBatisBeanFactoryInitializationAotProcessor() {
+ excludeClasses.add(MapperScannerConfigurer.class);
+ }
+
+ @Override public boolean isExcludedFromAotProcessing(RegisteredBean registeredBean) {
+ return excludeClasses.contains(registeredBean.getBeanClass());
+ }
+
+ @Override
+ public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
+ String[] beanNames = beanFactory.getBeanNamesForType(MapperFactoryBean.class);
+ if (beanNames.length == 0) {
+ return null;
+ }
+ return (context, code) -> {
+ RuntimeHints hints = context.getRuntimeHints();
+ for (String beanName : beanNames) {
+ BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName.substring(1));
+ PropertyValue mapperInterface = beanDefinition.getPropertyValues().getPropertyValue("mapperInterface");
+ if (mapperInterface != null && mapperInterface.getValue() != null) {
+ Class> mapperInterfaceType = (Class>) mapperInterface.getValue();
+ if (mapperInterfaceType != null) {
+ registerReflectionTypeIfNecessary(mapperInterfaceType, hints);
+ hints.proxies().registerJdkProxy(mapperInterfaceType);
+ hints.resources()
+ .registerPattern(mapperInterfaceType.getName().replace('.', '/').concat(".xml"));
+ registerMapperRelationships(mapperInterfaceType, hints);
+ }
+ }
+ }
+ };
+ }
+
+ private void registerMapperRelationships(Class> mapperInterfaceType, RuntimeHints hints) {
+ Method[] methods = ReflectionUtils.getAllDeclaredMethods(mapperInterfaceType);
+ for (Method method : methods) {
+ if (method.getDeclaringClass() != Object.class) {
+ ReflectionUtils.makeAccessible(method);
+ registerSqlProviderTypes(method, hints, SelectProvider.class, SelectProvider::value, SelectProvider::type);
+ registerSqlProviderTypes(method, hints, InsertProvider.class, InsertProvider::value, InsertProvider::type);
+ registerSqlProviderTypes(method, hints, UpdateProvider.class, UpdateProvider::value, UpdateProvider::type);
+ registerSqlProviderTypes(method, hints, DeleteProvider.class, DeleteProvider::value, DeleteProvider::type);
+ Class> returnType = MyBatisMapperTypeUtils.resolveReturnClass(mapperInterfaceType, method);
+ registerReflectionTypeIfNecessary(returnType, hints);
+ MyBatisMapperTypeUtils.resolveParameterClasses(mapperInterfaceType, method)
+ .forEach(x -> registerReflectionTypeIfNecessary(x, hints));
+ }
+ }
+ }
+
+ @SafeVarargs
+ private void registerSqlProviderTypes(
+ Method method, RuntimeHints hints, Class annotationType, Function>... providerTypeResolvers) {
+ for (T annotation : method.getAnnotationsByType(annotationType)) {
+ for (Function> providerTypeResolver : providerTypeResolvers) {
+ registerReflectionTypeIfNecessary(providerTypeResolver.apply(annotation), hints);
+ }
+ }
+ }
+
+ private void registerReflectionTypeIfNecessary(Class> type, RuntimeHints hints) {
+ if (!type.isPrimitive() && !type.getName().startsWith("java")) {
+ hints.reflection().registerType(type, MemberCategory.values());
+ }
+ }
+
+ }
+
+ static class MyBatisMapperTypeUtils {
+ private MyBatisMapperTypeUtils() {
+ // NOP
+ }
+
+ static Class> resolveReturnClass(Class> mapperInterface, Method method) {
+ Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
+ return typeToClass(resolvedReturnType, method.getReturnType());
+ }
+
+ static Set> resolveParameterClasses(Class> mapperInterface, Method method) {
+ return Stream.of(TypeParameterResolver.resolveParamTypes(method, mapperInterface))
+ .map(x -> typeToClass(x, x instanceof Class ? (Class>) x : Object.class)).collect(Collectors.toSet());
+ }
+
+ private static Class> typeToClass(Type src, Class> fallback) {
+ Class> result = null;
+ if (src instanceof Class>) {
+ if (((Class>) src).isArray()) {
+ result = ((Class>) src).getComponentType();
+ } else {
+ result = (Class>) src;
+ }
+ } else if (src instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) src;
+ int index = (parameterizedType.getRawType() instanceof Class
+ && Map.class.isAssignableFrom((Class>) parameterizedType.getRawType())
+ && parameterizedType.getActualTypeArguments().length > 1) ? 1 : 0;
+ Type actualType = parameterizedType.getActualTypeArguments()[index];
+ result = typeToClass(actualType, fallback);
+ }
+ if (result == null) {
+ result = fallback;
+ }
+ return result;
+ }
+
+ }
+
+ static class MyBatisMapperFactoryBeanPostProcessor implements MergedBeanDefinitionPostProcessor, BeanFactoryAware {
+
+ private static final org.apache.commons.logging.Log LOG = LogFactory.getLog(
+ MyBatisMapperFactoryBeanPostProcessor.class);
+
+ private static final String MAPPER_FACTORY_BEAN = "org.mybatis.spring.mapper.MapperFactoryBean";
+
+ private ConfigurableBeanFactory beanFactory;
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) {
+ this.beanFactory = (ConfigurableBeanFactory) beanFactory;
+ }
+
+ @Override
+ public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class> beanType, String beanName) {
+ if (ClassUtils.isPresent(MAPPER_FACTORY_BEAN, this.beanFactory.getBeanClassLoader())) {
+ resolveMapperFactoryBeanTypeIfNecessary(beanDefinition);
+ }
+ }
+
+ private void resolveMapperFactoryBeanTypeIfNecessary(RootBeanDefinition beanDefinition) {
+ if (!beanDefinition.hasBeanClass() || !MapperFactoryBean.class.isAssignableFrom(beanDefinition.getBeanClass())) {
+ return;
+ }
+ if (beanDefinition.getResolvableType().hasUnresolvableGenerics()) {
+ Class> mapperInterface = getMapperInterface(beanDefinition);
+ if (mapperInterface != null) {
+ // Exposes a generic type information to context for prevent early initializing
+ ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
+ constructorArgumentValues.addGenericArgumentValue(mapperInterface);
+ beanDefinition.setConstructorArgumentValues(constructorArgumentValues);
+ beanDefinition.setConstructorArgumentValues(constructorArgumentValues);
+ beanDefinition.setTargetType(ResolvableType.forClassWithGenerics(beanDefinition.getBeanClass(), mapperInterface));
+ }
+ }
+ }
+
+ private Class> getMapperInterface(RootBeanDefinition beanDefinition) {
+ try {
+ return (Class>) beanDefinition.getPropertyValues().get("mapperInterface");
+ }
+ catch (Exception e) {
+ LOG.debug("Fail getting mapper interface type.", e);
+ return null;
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/hicha-repository-impl/src/main/java/io/huyvu/hicha/repository/impl/MessageRepositoryImpl.java b/hicha-repository-impl/src/main/java/io/huyvu/hicha/repository/impl/MessageRepositoryImpl.java
new file mode 100644
index 0000000..30773f0
--- /dev/null
+++ b/hicha-repository-impl/src/main/java/io/huyvu/hicha/repository/impl/MessageRepositoryImpl.java
@@ -0,0 +1,30 @@
+package io.huyvu.hicha.repository.impl;
+
+import io.huyvu.hicha.repository.model.Message;
+import io.huyvu.hicha.repository.repo.MessageRepository;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+@Mapper
+public interface MessageRepositoryImpl extends MessageRepository {
+ @Override
+ @Select("""
+ select *
+ from messages
+ where conversation_id = #{conversationId}
+ limit 100""")
+ List findByConversationId(long conversationId);
+
+ @Override
+ @Insert("""
+ insert into messages
+ set conversation_id = #{conversationId},
+ sender_id = #{senderId},
+ message_text = #{messageText},
+ sent_at = #{sentAt}
+ """)
+ void save(Message message);
+}
diff --git a/hicha-repository/pom.xml b/hicha-repository/pom.xml
new file mode 100644
index 0000000..43e7409
--- /dev/null
+++ b/hicha-repository/pom.xml
@@ -0,0 +1,13 @@
+
+ 4.0.0
+
+ io.huyvu.hicha
+ hicha-backend
+ 1.0-SNAPSHOT
+
+ hicha-repository
+ Archetype - hicha-repository
+ https://maven.apache.org
+
+
diff --git a/hicha-repository/src/main/java/io/huyvu/hicha/repository/model/Message.java b/hicha-repository/src/main/java/io/huyvu/hicha/repository/model/Message.java
new file mode 100644
index 0000000..2e6fbc8
--- /dev/null
+++ b/hicha-repository/src/main/java/io/huyvu/hicha/repository/model/Message.java
@@ -0,0 +1,17 @@
+package io.huyvu.hicha.repository.model;
+
+import lombok.AccessLevel;
+import lombok.Data;
+import lombok.experimental.FieldDefaults;
+
+import java.time.Instant;
+
+@Data
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class Message {
+ Long messageId;
+ Integer conversationId;
+ Integer senderId;
+ String messageText;
+ Instant sentAt;
+}
diff --git a/hicha-repository/src/main/java/io/huyvu/hicha/repository/repo/MessageRepository.java b/hicha-repository/src/main/java/io/huyvu/hicha/repository/repo/MessageRepository.java
new file mode 100644
index 0000000..6d764ec
--- /dev/null
+++ b/hicha-repository/src/main/java/io/huyvu/hicha/repository/repo/MessageRepository.java
@@ -0,0 +1,10 @@
+package io.huyvu.hicha.repository.repo;
+
+import io.huyvu.hicha.repository.model.Message;
+
+import java.util.List;
+
+public interface MessageRepository {
+ List findByConversationId(long conversationId);
+ void save(Message message);
+}
diff --git a/pom.xml b/pom.xml
index 1fab38f..2af732e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,14 +1,23 @@
4.0.0
- io.huyvu.reboot
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.2.5
+
+
+
+ io.huyvu.hicha
hicha-backend
1.0-SNAPSHOT
Archetype - hicha-backend
http://maven.apache.org
pom
+
+ 21
huyvu8051
https://sonarcloud.io
**/HichaBusinessApplication.java, **/MyBatisNativeConfiguration.java
@@ -38,8 +47,18 @@
hicha-business
+ hicha-repository
+ hicha-repository-impl
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
diff --git a/test.http b/test.http
index 45b07ea..e74a014 100644
--- a/test.http
+++ b/test.http
@@ -1,13 +1,14 @@
# @no-redirect
# @no-cookie-jar
-POST http://localhost:8080/api/v1/user
+POST http://localhost:8080/api/v1/message
Content-Type: application/json
{
- "name": "{{$random.email}}"
-
+ "conversationId": 1,
+ "senderId": 1,
+ "messageText": "{{$random.alphanumeric(300)}}"
}
### Send GET request
-GET http://localhost:8080/api/v1/user
+GET http://localhost:8080/api/v1/message/1