Implement chats name and username history
This commit is contained in:
parent
e7424c811c
commit
041804eb1f
@ -5,16 +5,22 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import io.quarkus.hibernate.reactive.panache.PanacheEntityBase;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import java.util.SortedSet;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.TreeSet;
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OrderBy;
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Positive;
|
||||
import javax.validation.constraints.Size;
|
||||
import org.hibernate.annotations.SortNatural;
|
||||
|
||||
@Entity
|
||||
@Cacheable
|
||||
@ -27,17 +33,43 @@ public class Chat extends PanacheEntityBase {
|
||||
@JsonSerialize(using = ChatIdJsonSerializer.class)
|
||||
@JsonDeserialize(using = ChatIdJsonDeserializer.class)
|
||||
public Long id;
|
||||
|
||||
/**
|
||||
* null = unknown username
|
||||
* "" = empty username
|
||||
*/
|
||||
@Column(length = 128)
|
||||
public String name;
|
||||
@Size(message = "Username length is not valid", min = 5)
|
||||
|
||||
// Field definition and bounds
|
||||
@OneToMany(orphanRemoval = true, mappedBy = "chat", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
|
||||
@OrderBy("time DESC")
|
||||
@SortNatural
|
||||
// Field serialization
|
||||
@JsonIgnore
|
||||
public SortedSet<HistoricChatName> nameHistory = new TreeSet<>();
|
||||
|
||||
/**
|
||||
* null = unknown username
|
||||
* "" = empty username
|
||||
*/
|
||||
@Column(length = 48)
|
||||
@Size(message = "Username must not be an empty string", min = 1, max = 12 + 32)
|
||||
@Pattern(message = "Username contains invalid characters", regexp = "^(?:[a-zA-Z\\d][_]?)+$")
|
||||
@Pattern(message = "Username is not valid", regexp = "^(?:translation_|mv_)?[a-zA-Z]([a-zA-Z_\\d]){1,30}[a-zA-Z\\d]$")
|
||||
@Pattern(message = "Username contains invalid characters", regexp = "^(?:|(?:[a-zA-Z\\d][_]?)+)$")
|
||||
@Pattern(message = "Username is not valid", regexp = "^(?:|(?:translation_|mv_)?[a-zA-Z]([a-zA-Z_\\d]){1,30}[a-zA-Z\\d])$")
|
||||
public String username;
|
||||
|
||||
// Field definition and bounds
|
||||
@OneToMany(orphanRemoval = true, mappedBy = "chat", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
|
||||
@OrderBy("time DESC")
|
||||
@SortNatural
|
||||
// Field serialization
|
||||
@JsonIgnore
|
||||
public SortedSet<HistoricChatUsername> usernameHistory = new TreeSet<>();
|
||||
|
||||
@Enumerated
|
||||
public Status status;
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
public ChatId getChatId() {
|
||||
return ChatId.fromLong(id);
|
||||
|
@ -1,11 +1,11 @@
|
||||
package io.volvox.chats;
|
||||
|
||||
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||
import io.quarkus.hibernate.reactive.panache.PanacheEntityBase;
|
||||
import io.reactiverse.elasticsearch.client.mutiny.RestHighLevelClient;
|
||||
import io.smallrye.mutiny.Multi;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import java.sql.Date;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@ -15,9 +15,8 @@ import javax.ws.rs.NotFoundException;
|
||||
import org.elasticsearch.action.delete.DeleteRequest;
|
||||
import org.elasticsearch.action.delete.DeleteResponse;
|
||||
import org.elasticsearch.action.get.GetRequest;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.update.UpdateResponse;
|
||||
import org.elasticsearch.client.RequestOptions;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
@ -30,11 +29,10 @@ public class ChatService {
|
||||
@Inject
|
||||
RestHighLevelClient restHighLevelClient;
|
||||
|
||||
private Uni<IndexResponse> updateIndex(Chat chat) {
|
||||
var request = new IndexRequest("chats");
|
||||
request.id(ChatId.toString(chat.id));
|
||||
request.source(JsonObject.mapFrom(chat).toString(), XContentType.JSON);
|
||||
return restHighLevelClient.indexAsync(request, RequestOptions.DEFAULT);
|
||||
private Uni<UpdateResponse> updateIndex(Chat chat) {
|
||||
var request = new org.elasticsearch.action.update.UpdateRequest("chats", ChatId.toString(chat.id))
|
||||
.docAsUpsert(true).doc(JsonObject.mapFrom(chat).toString(), XContentType.JSON);
|
||||
return restHighLevelClient.updateAsync(request, RequestOptions.DEFAULT);
|
||||
}
|
||||
|
||||
private Uni<DeleteResponse> removeFromIndex(Long id) {
|
||||
@ -66,9 +64,17 @@ public class ChatService {
|
||||
}
|
||||
|
||||
public Uni<Void> delete(Long id) {
|
||||
return Panache.withTransaction(() -> Chat.findById(id)
|
||||
HistoricChatName.deleteByChatId(id);
|
||||
return Panache.withTransaction(() -> Chat.<Chat>findById(id)
|
||||
.onItem().ifNull().failWith(NotFoundException::new)
|
||||
.flatMap(PanacheEntityBase::delete)
|
||||
.flatMap(entity -> {
|
||||
var nameDelete = HistoricChatName.deleteByChatId(id);
|
||||
var usernameDelete = HistoricChatUsername.deleteByChatId(id);
|
||||
|
||||
var entityDelete = entity.delete();
|
||||
return Uni.combine().all().unis(nameDelete, usernameDelete).combinedWith((a, b) -> null)
|
||||
.replaceWith(entityDelete);
|
||||
})
|
||||
.replaceWith(removeFromIndex(id))
|
||||
.onItem().transform(DeleteResponse::status)
|
||||
.replaceWithVoid()
|
||||
@ -79,14 +85,16 @@ public class ChatService {
|
||||
if (chat.id != null && id != null && !Objects.equals(chat.id, id)) {
|
||||
throw new IllegalArgumentException("Chat id is different than id");
|
||||
}
|
||||
|
||||
return Panache.withTransaction(() -> {
|
||||
var transactionTimestamp = new Date(System.currentTimeMillis());
|
||||
// Find chat by id
|
||||
return Panache.withTransaction(() -> Chat.<Chat>findById(id)
|
||||
var oldChat = Chat.<Chat>findById(id);
|
||||
return oldChat
|
||||
.flatMap(entity -> {
|
||||
if (entity == null) {
|
||||
// Persist the chat if not found
|
||||
return Chat.persist(chat)
|
||||
// Return the chat
|
||||
.replaceWith(chat);
|
||||
return chat.persist();
|
||||
} else {
|
||||
// Update all fields
|
||||
if (chat.name != null) {
|
||||
@ -102,8 +110,44 @@ public class ChatService {
|
||||
return Uni.createFrom().item(entity);
|
||||
}
|
||||
})
|
||||
.flatMap(updatedEntity -> {
|
||||
// Update the username with history
|
||||
var usernameUpdater = HistoricChatUsername.findNewest(updatedEntity.id).flatMap(newestUsername -> {
|
||||
if (chat.username != null
|
||||
&& (newestUsername == null || !Objects.equals(newestUsername.username, chat.username))) {
|
||||
updatedEntity.username = chat.username;
|
||||
|
||||
var newUsername = new HistoricChatUsername();
|
||||
newUsername.chat = updatedEntity;
|
||||
newUsername.username = chat.username;
|
||||
newUsername.time = transactionTimestamp;
|
||||
return newUsername.persist().replaceWithVoid();
|
||||
} else {
|
||||
return Uni.createFrom().voidItem();
|
||||
}
|
||||
});
|
||||
|
||||
// Update the name with history
|
||||
var nameUpdater = HistoricChatName.findNewest(updatedEntity.id).flatMap(newestName -> {
|
||||
if (chat.name != null
|
||||
&& (newestName == null || !Objects.equals(newestName.name, chat.name))) {
|
||||
updatedEntity.name = chat.name;
|
||||
|
||||
var newName = new HistoricChatName();
|
||||
newName.chat = updatedEntity;
|
||||
newName.name = chat.name;
|
||||
newName.time = transactionTimestamp;
|
||||
return newName.persist().replaceWithVoid();
|
||||
} else {
|
||||
return Uni.createFrom().voidItem();
|
||||
}
|
||||
});
|
||||
|
||||
return nameUpdater.replaceWith(usernameUpdater).replaceWith(updatedEntity);
|
||||
})
|
||||
// Update index
|
||||
.onItem().transformToUni(updatedChat -> updateIndex(updatedChat).replaceWith(updatedChat))
|
||||
.chain(updatedChat -> updateIndex(updatedChat).replaceWith(updatedChat));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,66 @@
|
||||
package io.volvox.chats;
|
||||
|
||||
import io.quarkus.hibernate.reactive.panache.PanacheEntityBase;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import io.smallrye.mutiny.Multi;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import java.sql.Date;
|
||||
import java.util.StringJoiner;
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
@Entity(name = "chat_name")
|
||||
@Cacheable
|
||||
public class HistoricChatName extends PanacheEntityBase implements Comparable<HistoricChatName> {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
public Long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = true)
|
||||
@JoinColumn(name = "chat_id", referencedColumnName = "id", nullable = true)
|
||||
Chat chat;
|
||||
|
||||
// Field definition and bounds
|
||||
@Column(name = "time", nullable = false)
|
||||
public Date time;
|
||||
|
||||
// Field definition and bounds
|
||||
@Column(length = 128)
|
||||
public String name;
|
||||
|
||||
public static Uni<HistoricChatName> findNewest(Long chatId) {
|
||||
if (chatId == null) {
|
||||
throw new NullPointerException("Id must not be null");
|
||||
}
|
||||
return find("from chat_name where chat_id = ?1", Sort.by("time").descending(), chatId).firstResult();
|
||||
}
|
||||
|
||||
public static Multi<HistoricChatName> listAll(Long chatId) {
|
||||
if (chatId == null) {
|
||||
throw new NullPointerException("Id must not be null");
|
||||
}
|
||||
return find("from chat_name where chat_id = ?1", Sort.by("time").ascending(), chatId).stream();
|
||||
}
|
||||
|
||||
public static Uni<Long> deleteByChatId(Long chatId) {
|
||||
return delete("from chat_name where chat_id = ?1", chatId);
|
||||
}
|
||||
|
||||
@Override public int compareTo(HistoricChatName o) {
|
||||
return o.time.compareTo(this.time);
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return new StringJoiner(", ", HistoricChatName.class.getSimpleName() + "[", "]")
|
||||
.add("id=" + id)
|
||||
.add("time=" + time)
|
||||
.add("name='" + name + "'")
|
||||
.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package io.volvox.chats;
|
||||
|
||||
import io.quarkus.hibernate.reactive.panache.PanacheEntityBase;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import io.smallrye.mutiny.Multi;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import java.sql.Date;
|
||||
import java.util.StringJoiner;
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
@Entity(name = "chat_username")
|
||||
@Cacheable
|
||||
public class HistoricChatUsername extends PanacheEntityBase implements Comparable<HistoricChatUsername> {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
public Long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = true)
|
||||
@JoinColumn(name = "chat_id", referencedColumnName = "id", nullable = true)
|
||||
Chat chat;
|
||||
|
||||
// Field definition and bounds
|
||||
@Column(name = "time", nullable = false)
|
||||
public Date time;
|
||||
|
||||
// Field definition and bounds
|
||||
@Size(message = "Username length is not valid", min = 5)
|
||||
@Column(length = 48)
|
||||
@Size(message = "Username must not be an empty string", min = 1, max = 12 + 32)
|
||||
@Pattern(message = "Username contains invalid characters", regexp = "^(?:[a-zA-Z\\d][_]?)+$")
|
||||
@Pattern(message = "Username is not valid", regexp = "^(?:translation_|mv_)?[a-zA-Z]([a-zA-Z_\\d]){1,30}[a-zA-Z\\d]$")
|
||||
// Keep field history
|
||||
public String username;
|
||||
|
||||
public static Uni<HistoricChatUsername> findNewest(Long chatId) {
|
||||
if (chatId == null) {
|
||||
throw new NullPointerException("Id must not be null");
|
||||
}
|
||||
return find("from chat_username where chat_id = ?1", Sort.by("time").descending(), chatId).firstResult();
|
||||
}
|
||||
|
||||
public static Multi<HistoricChatUsername> listAll(Long chatId) {
|
||||
if (chatId == null) {
|
||||
throw new NullPointerException("Id must not be null");
|
||||
}
|
||||
return find("from chat_username where chat_id = ?1", Sort.by("time").ascending(), chatId).stream();
|
||||
}
|
||||
|
||||
public static Uni<Long> deleteByChatId(Long chatId) {
|
||||
return delete("from chat_username where chat_id = ?1", chatId);
|
||||
}
|
||||
|
||||
@Override public int compareTo(HistoricChatUsername o) {
|
||||
return o.time.compareTo(this.time);
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return new StringJoiner(", ", HistoricChatUsername.class.getSimpleName() + "[", "]")
|
||||
.add("id=" + id)
|
||||
.add("time=" + time)
|
||||
.add("username='" + username + "'")
|
||||
.toString();
|
||||
}
|
||||
}
|
@ -1,4 +1,14 @@
|
||||
INSERT INTO chat(id, name, username, status) VALUES (9007199256673076, 'My Supergroup', 'mysupergroup', 1);
|
||||
INSERT INTO chat(id, name, username, status) VALUES (777000, 'Telegram', 'telegram', null);
|
||||
INSERT INTO chat(id, name, username, status) VALUES (4503599627464345, 'School group', null, 1);
|
||||
INSERT INTO chat(id, name, username, status) VALUES (4503599627382355, 'Old school group', null, 0);
|
||||
INSERT INTO chat(id, status, name, username) VALUES (9007199256673076, 1, 'My Supergroup', 'mysupergroup');
|
||||
INSERT INTO chat_name(chat_id, id, time, name) VALUES (9007199256673076, 12345000, current_timestamp, 'My Supergroup');
|
||||
INSERT INTO chat_username(chat_id, id, time, username) VALUES (9007199256673076, 12345001, current_timestamp, 'mysupergroup');
|
||||
|
||||
INSERT INTO chat(id, status, name, username) VALUES (777000, 1, 'Telegram', 'telegram');
|
||||
INSERT INTO chat_name(chat_id, id, time, name) VALUES (777000, 12345002, current_timestamp, 'Telegram');
|
||||
INSERT INTO chat_username(chat_id, id, time, username) VALUES (777000, 12345003, current_timestamp, 'telegram');
|
||||
|
||||
|
||||
INSERT INTO chat(id, status, name) VALUES (4503599627464345, 1, 'School group');
|
||||
INSERT INTO chat_name(chat_id, id, time, name) VALUES (4503599627464345, 12345004, current_timestamp, 'School group');
|
||||
|
||||
INSERT INTO chat(id, status, name) VALUES (4503599627382355, 0, 'Old school group');
|
||||
INSERT INTO chat_name(chat_id, id, time, name) VALUES (4503599627382355, 12345005, current_timestamp, 'Old school group');
|
||||
|
122
service-chats/src/test/java/io/volvox/chats/ChatServiceTest.java
Normal file
122
service-chats/src/test/java/io/volvox/chats/ChatServiceTest.java
Normal file
@ -0,0 +1,122 @@
|
||||
package io.volvox.chats;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||
import io.quarkus.test.common.QuarkusTestResource;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import javax.inject.Inject;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
@QuarkusTestResource(ElasticsearchContainerTestResource.class)
|
||||
public class ChatServiceTest {
|
||||
|
||||
@Inject ChatService chatService;
|
||||
|
||||
@Inject ChatsServiceWarmup chatsServiceWarmup;
|
||||
|
||||
@BeforeEach
|
||||
public void beforeEach() {
|
||||
chatsServiceWarmup.warmup();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCount() {
|
||||
var count = chatService.count().await().indefinitely();
|
||||
assertEquals(4, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListAll() {
|
||||
var list = chatService.listAll().map(Chat::getChatId).collect().asList().await().indefinitely();
|
||||
assertThat(list)
|
||||
.containsExactlyInAnyOrder(ChatId.fromLong(9007199256673076L), ChatId.fromLong(777000),
|
||||
ChatId.fromLong(4503599627464345L), ChatId.fromLong(4503599627382355L)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByName() {
|
||||
var chat = chatService.searchByName("Telegram").await().indefinitely();
|
||||
assertThat(chat.size()).isEqualTo(1L);
|
||||
assertThat(chat.get(0).getChatId()).isEqualTo(ChatId.fromLong(777000L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByUsername() {
|
||||
var chat = chatService.searchByUsername("telegram").await().indefinitely();
|
||||
assertThat(chat.size()).isEqualTo(1L);
|
||||
assertThat(chat.get(0).getChatId()).isEqualTo(ChatId.fromLong(777000));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateNonexistent() {
|
||||
var newChat = new Chat();
|
||||
newChat.id = 777234L;
|
||||
newChat.name = "TestChat";
|
||||
newChat.username = "uname";
|
||||
newChat.status = Status.ALIVE;
|
||||
chatService.create(newChat).await().indefinitely();
|
||||
Panache.getSession().onItem().invoke(() -> {
|
||||
assertThat(newChat.isPersistent()).isEqualTo(true);
|
||||
assertThat(newChat.id).isEqualTo(777234L);
|
||||
assertThat(newChat.name).isEqualTo("TestChat");
|
||||
assertThat(newChat.username).isEqualTo("uname");
|
||||
assertThat(newChat.status).isEqualTo(Status.ALIVE);
|
||||
}).await().indefinitely();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateNonexistent() {
|
||||
var chat = new Chat();
|
||||
chat.id = 777234L;
|
||||
chat.name = "TestChat";
|
||||
chat.username = "uname";
|
||||
chat.status = Status.ALIVE;
|
||||
var newChat = chatService.update(777234L, chat).await().indefinitely();
|
||||
Panache.getSession().onItem().invoke(() -> {
|
||||
assertThat(newChat).isNotNull();
|
||||
assertThat(newChat.isPersistent()).isEqualTo(true);
|
||||
assertThat(newChat.id).isEqualTo(777234L);
|
||||
assertThat(newChat.name).isEqualTo("TestChat");
|
||||
assertThat(newChat.username).isEqualTo("uname");
|
||||
assertThat(newChat.status).isEqualTo(Status.ALIVE);
|
||||
}).await().indefinitely();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateExisting() {
|
||||
// Create chat
|
||||
{
|
||||
var chat = new Chat();
|
||||
chat.id = 777234L;
|
||||
chat.name = "TestChat";
|
||||
chat.username = "uname";
|
||||
chat.status = Status.ALIVE;
|
||||
chatService.create(chat).await().indefinitely();
|
||||
}
|
||||
// Test update
|
||||
{
|
||||
var chat = new Chat();
|
||||
chat.id = 777234L;
|
||||
chat.username = "mario";
|
||||
var newChat = chatService.update(777234L, chat).await().indefinitely();
|
||||
Panache.getSession().onItem().invoke(() -> {
|
||||
assertThat(newChat).isNotNull();
|
||||
assertThat(newChat.isPersistent()).isEqualTo(true);
|
||||
assertThat(newChat.id).isEqualTo(777234L);
|
||||
assertThat(newChat.name).isEqualTo("TestChat");
|
||||
assertThat(newChat.username).isEqualTo("mario");
|
||||
assertThat(newChat.status).isEqualTo(Status.ALIVE);
|
||||
}).await().indefinitely();
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void tearDown(){
|
||||
Panache.withTransaction(() -> Chat.deleteById(777234L)).await().indefinitely();
|
||||
}
|
||||
}
|
@ -2,15 +2,14 @@ package io.volvox.chats;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.emptyString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||
import io.quarkus.test.common.QuarkusTestResource;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.response.Response;
|
||||
import javax.inject.Inject;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@ -18,6 +17,13 @@ import org.junit.jupiter.api.Test;
|
||||
@QuarkusTestResource(ElasticsearchContainerTestResource.class)
|
||||
public class ChatsEndpointTest {
|
||||
|
||||
@Inject ChatsServiceWarmup chatsServiceWarmup;
|
||||
|
||||
@BeforeEach
|
||||
public void beforeEach() {
|
||||
chatsServiceWarmup.warmup();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListAllChats() {
|
||||
//List all, should have all 3 usernames the database has initially:
|
||||
@ -123,19 +129,6 @@ public class ChatsEndpointTest {
|
||||
.statusCode(500);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHealth() {
|
||||
given()
|
||||
.when()
|
||||
.get("/q/health/ready")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("status", is("UP"),
|
||||
"checks.status", containsInAnyOrder("UP"),
|
||||
"checks.name", containsInAnyOrder("Elasticsearch cluster health check"));
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void tearDown(){
|
||||
Panache.withTransaction(() -> Chat.deleteById(777234L)).await().indefinitely();
|
||||
|
@ -0,0 +1,59 @@
|
||||
package io.volvox.chats;
|
||||
|
||||
import io.quarkus.logging.Log;
|
||||
import io.reactiverse.elasticsearch.client.mutiny.RestHighLevelClient;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.inject.Inject;
|
||||
import org.elasticsearch.ElasticsearchStatusException;
|
||||
import org.elasticsearch.action.admin.indices.flush.FlushRequest;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.action.update.UpdateResponse;
|
||||
import org.elasticsearch.client.RequestOptions;
|
||||
import org.elasticsearch.client.indices.CreateIndexRequest;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
|
||||
@ApplicationScoped
|
||||
public class ChatsServiceWarmup {
|
||||
@Inject
|
||||
ChatService chatService;
|
||||
@Inject
|
||||
RestHighLevelClient restHighLevelClient;
|
||||
|
||||
|
||||
public void warmup() {
|
||||
createIndices();
|
||||
chatService.listAll().onItem().transformToUni(this::updateIndex).merge().select().last().toUni().await()
|
||||
.indefinitely();
|
||||
|
||||
restHighLevelClient.indices().flushAsyncAndAwait(new FlushRequest("chats"), RequestOptions.DEFAULT);
|
||||
restHighLevelClient.indices().refreshAsyncAndAwait(new RefreshRequest("chats"), RequestOptions.DEFAULT);
|
||||
}
|
||||
|
||||
private void createIndices() {
|
||||
var req = new CreateIndexRequest("chats");
|
||||
try {
|
||||
restHighLevelClient.indices().createAsyncAndAwait(req, RequestOptions.DEFAULT);
|
||||
} catch (ElasticsearchStatusException ex) {
|
||||
if (ex.status() != RestStatus.BAD_REQUEST) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Uni<UpdateResponse> updateIndex(Chat chat) {
|
||||
var request = new UpdateRequest("chats", ChatId.toString(chat.id)).docAsUpsert(true);
|
||||
request.doc(JsonObject.mapFrom(chat).toString(), XContentType.JSON);
|
||||
|
||||
Log.infof("Index chat %s", chat);
|
||||
|
||||
return restHighLevelClient.updateAsync(request, RequestOptions.DEFAULT).onItem().invoke(response -> {
|
||||
if (response.status() != RestStatus.CREATED && response.status() != RestStatus.OK) {
|
||||
throw new UnsupportedOperationException("Unexpected status: " + response.status().toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,12 +1,10 @@
|
||||
package io.volvox.chats;
|
||||
|
||||
import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
|
||||
import java.util.Map;
|
||||
|
||||
import org.testcontainers.elasticsearch.ElasticsearchContainer;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
|
||||
import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
|
||||
|
||||
public class ElasticsearchContainerTestResource implements QuarkusTestResourceLifecycleManager {
|
||||
|
||||
static ElasticsearchContainer elasticsearchContainer = new ElasticsearchContainer(
|
||||
@ -17,8 +15,7 @@ public class ElasticsearchContainerTestResource implements QuarkusTestResourceLi
|
||||
public Map<String, String> start() {
|
||||
elasticsearchContainer.withEnv("action.auto_create_index", "true");
|
||||
elasticsearchContainer.start();
|
||||
return Map.of(
|
||||
"quarkus.elasticsearch.hosts", elasticsearchContainer.getHttpHostAddress());
|
||||
return Map.of("quarkus.elasticsearch.hosts", elasticsearchContainer.getHttpHostAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,29 @@
|
||||
package io.volvox.chats;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import io.quarkus.test.common.QuarkusTestResource;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.reactiverse.elasticsearch.client.mutiny.RestHighLevelClient;
|
||||
import javax.inject.Inject;
|
||||
import org.elasticsearch.client.RequestOptions;
|
||||
import org.elasticsearch.client.core.CountRequest;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
@QuarkusTestResource(ElasticsearchContainerTestResource.class)
|
||||
public class IndexWarmupTest {
|
||||
@Inject
|
||||
RestHighLevelClient restHighLevelClient;
|
||||
|
||||
@Inject ChatsServiceWarmup chatsServiceWarmup;
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
chatsServiceWarmup.warmup();
|
||||
var count = restHighLevelClient.countAsyncAndAwait(new CountRequest("chats"), RequestOptions.DEFAULT);
|
||||
assertEquals(RestStatus.OK, count.status());
|
||||
assertEquals(4, count.getCount());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user