create docker-compose

This commit is contained in:
Andrea Cavalli 2021-12-01 14:23:42 +01:00
parent da55d49316
commit 02ed372d99
25 changed files with 441 additions and 165 deletions

3
docker-compose/chats.env Normal file
View File

@ -0,0 +1,3 @@
QUARKUS_DATASOURCE_REACTIVE_URL=vertx-reactive:postgresql://database:5432/volvox
QUARKUS_DATASOURCE_USERNAME=volvox
QUARKUS_DATASOURCE_PASSWORD=volvox

View File

@ -0,0 +1,3 @@
POSTGRES_USER=volvox
POSTGRES_PASSWORD=volvox
POSTGRES_DB=volvox

View File

@ -0,0 +1,98 @@
version: "1.0.0"
services:
chats:
build:
context: ../service-chats
dockerfile: src/main/docker/Dockerfile.jvm
container_name: service-chats
env_file: chats.env
ports:
- 8282:8282
links:
- database
- elastic
td:
build:
context: ../service-td
dockerfile: src/main/docker/Dockerfile.jvm
container_name: service-td
env_file: td.env
ports:
- 8283:8283
links: []
database:
image: postgres
container_name: postgres01
env_file: database.env
ports:
- 5432:5432
volumes:
- database_data:/var/lib/postgresql/data/
es01:
image: docker.elastic.co/elasticsearch/elasticsearch:7.15.2
container_name: es01
environment:
- node.name=es01
- cluster.name=es-docker-cluster
- discovery.seed_hosts=es02,es03
- cluster.initial_master_nodes=es01,es02,es03
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- data01:/usr/share/elasticsearch/data
ports:
- 9200:9200
networks:
- elastic
es02:
image: docker.elastic.co/elasticsearch/elasticsearch:7.15.2
container_name: es02
environment:
- node.name=es02
- cluster.name=es-docker-cluster
- discovery.seed_hosts=es01,es03
- cluster.initial_master_nodes=es01,es02,es03
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- data02:/usr/share/elasticsearch/data
networks:
- elastic
es03:
image: docker.elastic.co/elasticsearch/elasticsearch:7.15.2
container_name: es03
environment:
- node.name=es03
- cluster.name=es-docker-cluster
- discovery.seed_hosts=es01,es02
- cluster.initial_master_nodes=es01,es02,es03
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- data03:/usr/share/elasticsearch/data
networks:
- elastic
volumes:
database_data:
driver: local
data01:
driver: local
data02:
driver: local
data03:
driver: local
networks:
elastic:
driver: bridge

0
docker-compose/td.env Normal file
View File

View File

@ -33,6 +33,7 @@
<id>complete</id> <id>complete</id>
<modules> <modules>
<module>service-td</module> <module>service-td</module>
<module>service-chats</module>
</modules> </modules>
</profile> </profile>
</profiles> </profiles>

View File

@ -8,13 +8,13 @@
<properties> <properties>
<compiler-plugin.version>3.8.1</compiler-plugin.version> <compiler-plugin.version>3.8.1</compiler-plugin.version>
<maven.compiler.parameters>true</maven.compiler.parameters> <maven.compiler.parameters>true</maven.compiler.parameters>
<maven.compiler.source>17</maven.compiler.source> <maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target> <maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>2.4.1.Final</quarkus.platform.version> <quarkus.platform.version>2.5.0.Final</quarkus.platform.version>
<surefire-plugin.version>3.0.0-M5</surefire-plugin.version> <surefire-plugin.version>3.0.0-M5</surefire-plugin.version>
<elasticsearch-reactive.version>0.2.0</elasticsearch-reactive.version> <elasticsearch-reactive.version>0.2.0</elasticsearch-reactive.version>
</properties> </properties>
@ -145,6 +145,9 @@
<systemPropertyVariables> <systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager> <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home> <maven.home>${maven.home}</maven.home>
<junit.jupiter.execution.parallel.enabled>false</junit.jupiter.execution.parallel.enabled>
<forkCount>1</forkCount>
<reuseForks>false</reuseForks>
</systemPropertyVariables> </systemPropertyVariables>
</configuration> </configuration>
</plugin> </plugin>

View File

@ -21,7 +21,7 @@
# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/service-chats-jvm # docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/service-chats-jvm
# #
### ###
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4 FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5
ARG JAVA_PACKAGE=java-11-openjdk-headless ARG JAVA_PACKAGE=java-11-openjdk-headless
ARG RUN_JAVA_VERSION=1.3.8 ARG RUN_JAVA_VERSION=1.3.8

View File

@ -21,7 +21,7 @@
# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/service-chats-legacy-jar # docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/service-chats-legacy-jar
# #
### ###
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4 FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5
ARG JAVA_PACKAGE=java-11-openjdk-headless ARG JAVA_PACKAGE=java-11-openjdk-headless
ARG RUN_JAVA_VERSION=1.3.8 ARG RUN_JAVA_VERSION=1.3.8

View File

@ -14,7 +14,7 @@
# docker run -i --rm -p 8080:8080 quarkus/service-chats # docker run -i --rm -p 8080:8080 quarkus/service-chats
# #
### ###
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4 FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5
WORKDIR /work/ WORKDIR /work/
RUN chown 1001 /work \ RUN chown 1001 /work \
&& chmod "g+rwX" /work \ && chmod "g+rwX" /work \

View File

@ -30,8 +30,8 @@ public class Chat extends PanacheEntityBase {
@Positive(message = "id is not positive") @Positive(message = "id is not positive")
@Max(message = "id is too big", value = ChatId.MASK) @Max(message = "id is too big", value = ChatId.MASK)
@Column(nullable = false, unique = true) @Column(nullable = false, unique = true)
@JsonSerialize(using = ChatIdJsonSerializer.class) @JsonSerialize(using = ChatIdLongJsonSerializer.class)
@JsonDeserialize(using = ChatIdJsonDeserializer.class) @JsonDeserialize(using = ChatIdLongJsonDeserializer.class)
public Long id; public Long id;
/** /**

View File

@ -1,116 +1,215 @@
package io.volvox.chats; package io.volvox.chats;
public record ChatId(Type type, long subId) { import java.util.Objects;
public static final int SUB_ID_MASK_BYTES = 52; public final class ChatId {
public static final int TYPE_MASK_BYTES = 2; private final Type type;
private final long subId;
public static final long SUB_ID_MASK = 0b001111111111111111111111111111111111111111111111111111L; public ChatId(Type type, long subId) {
public static final long TYPE_MASK = 0b11L << SUB_ID_MASK_BYTES; if ((subId & SUB_ID_MASK) != subId) {
public static final long MASK = SUB_ID_MASK | TYPE_MASK; throw new IllegalArgumentException("subId is too big");
public static final int TYPE_PRIVATE_INT = 0b00; }
public static final int TYPE_BASIC_INT = 0b01; this.type = type;
public static final int TYPE_SUPER_INT = 0b10; this.subId = subId;
public static final int TYPE_SECRET_INT = 0b11; }
public static final long TYPE_PRIVATE_LONG = 0;
public static final long TYPE_BASIC_LONG = 0b01L << SUB_ID_MASK_BYTES & TYPE_MASK;
public static final long TYPE_SUPER_LONG = 0b10L << SUB_ID_MASK_BYTES & TYPE_MASK;
public static final long TYPE_SECRET_LONG = 0b11L << SUB_ID_MASK_BYTES & TYPE_MASK;
public ChatId { public Type type() {
if ((subId & SUB_ID_MASK) != subId) { return type;
throw new IllegalArgumentException("subId is too big"); }
}
}
public static ChatId fromLong(long id) { public long subId() {
return new ChatId(getType(id), getIdLong(id)); return subId;
} }
private static Type getType(long id) { @Override
return switch ((int) ((id & TYPE_MASK) >> SUB_ID_MASK_BYTES)) { public boolean equals(Object obj) {
case TYPE_SUPER_INT -> Type.SUPER; if (obj == this) {
case TYPE_BASIC_INT -> Type.BASIC; return true;
case TYPE_PRIVATE_INT -> Type.PRIVATE; }
case TYPE_SECRET_INT -> Type.SECRET; if (obj == null || obj.getClass() != this.getClass()) {
default -> throw new IllegalArgumentException("Invalid id type: " + id); return false;
}; }
} var that = (ChatId) obj;
return Objects.equals(this.type, that.type) &&
this.subId == that.subId;
}
private static long getIdLong(long id) { @Override
return id & SUB_ID_MASK; public int hashCode() {
} return Objects.hash(type, subId);
}
public long toLong() { public static final int SUB_ID_MASK_BYTES = 52;
return switch (type) { public static final int TYPE_MASK_BYTES = 2;
case SUPER -> TYPE_SUPER_LONG;
case BASIC -> TYPE_BASIC_LONG;
case PRIVATE -> TYPE_PRIVATE_LONG;
case SECRET -> TYPE_SECRET_LONG;
} | (subId & SUB_ID_MASK);
}
public enum Type { public static final long SUB_ID_MASK = 0b001111111111111111111111111111111111111111111111111111L;
PRIVATE, public static final long TYPE_MASK = 0b11L << SUB_ID_MASK_BYTES;
BASIC, public static final long MASK = SUB_ID_MASK | TYPE_MASK;
SUPER, public static final int TYPE_PRIVATE_INT = 0b00;
SECRET public static final int TYPE_BASIC_INT = 0b01;
} public static final int TYPE_SUPER_INT = 0b10;
public static final int TYPE_SECRET_INT = 0b11;
public static final long TYPE_PRIVATE_LONG = 0;
public static final long TYPE_BASIC_LONG = 0b01L << SUB_ID_MASK_BYTES & TYPE_MASK;
public static final long TYPE_SUPER_LONG = 0b10L << SUB_ID_MASK_BYTES & TYPE_MASK;
public static final long TYPE_SECRET_LONG = 0b11L << SUB_ID_MASK_BYTES & TYPE_MASK;
@Override public static ChatId fromLong(long id) {
public String toString() { return new ChatId(getType(id), getIdLong(id));
return toString(this); }
}
public static String toString(ChatId chatId) { private static Type getType(long id) {
return Long.toUnsignedString(chatId.subId) + "-" + switch (chatId.type) { switch ((int) ((id & TYPE_MASK) >> SUB_ID_MASK_BYTES)) {
case SUPER -> 's'; case TYPE_SUPER_INT:
case BASIC -> 'b'; return Type.SUPER;
case PRIVATE -> 'u'; case TYPE_BASIC_INT:
case SECRET -> 'd'; return Type.BASIC;
}; case TYPE_PRIVATE_INT:
} return Type.PRIVATE;
case TYPE_SECRET_INT:
return Type.SECRET;
default:
throw new IllegalArgumentException("Invalid id type: " + id);
}
}
public static String toString(long chatId) { private static long getIdLong(long id) {
return Long.toUnsignedString(getIdLong(chatId)) + "-" + switch (getType(chatId)) { return id & SUB_ID_MASK;
case SUPER -> 's'; }
case BASIC -> 'b';
case PRIVATE -> 'u';
case SECRET -> 'd';
};
}
public static ChatId fromString(String chatId) { public long toLong() {
var parts = chatId.split("-", 2); long result;
if (parts.length != 2) { switch (type) {
throw new IllegalArgumentException("Malformed chat id"); case SUPER:
} result = TYPE_SUPER_LONG;
if (parts[1].length() != 1) { break;
throw new IllegalArgumentException("Chat type is too long"); case BASIC:
} result = TYPE_BASIC_LONG;
return new ChatId(switch(parts[1].charAt(0)) { break;
case 's' -> Type.SUPER; case PRIVATE:
case 'b' -> Type.BASIC; result = TYPE_PRIVATE_LONG;
case 'u' -> Type.PRIVATE; break;
case 'd' -> Type.SECRET; case SECRET:
default -> throw new IllegalStateException("Unexpected value: " + parts[1].charAt(0)); result = TYPE_SECRET_LONG;
}, Long.parseUnsignedLong(parts[0]) & SUB_ID_MASK); break;
} default:
throw new IllegalStateException("Unexpected value: " + type);
}
result |= (subId & SUB_ID_MASK);
return result;
}
public static Long stringToLong(String chatId) { public enum Type {
var parts = chatId.split("-", 2); PRIVATE,
if (parts.length != 2) { BASIC,
throw new IllegalArgumentException("Malformed chat id"); SUPER,
} SECRET
if (parts[1].length() != 1) { }
throw new IllegalArgumentException("Chat type is too long");
} @Override
return switch(parts[1].charAt(0)) { public String toString() {
case 's' -> TYPE_SUPER_LONG; return toString(this);
case 'b' -> TYPE_BASIC_LONG; }
case 'u' -> TYPE_PRIVATE_LONG;
case 'd' -> TYPE_SECRET_LONG; public static String toString(ChatId chatId) {
default -> throw new IllegalStateException("Unexpected value: " + parts[1].charAt(0)); char suffix;
} | (Long.parseUnsignedLong(parts[0]) & SUB_ID_MASK); switch (chatId.type) {
} case SUPER:
suffix = 's';
break;
case BASIC:
suffix = 'b';
break;
case PRIVATE:
suffix = 'u';
break;
case SECRET:
suffix = 'd';
break;
default:
throw new IllegalStateException("Unexpected value: " + chatId.type);
}
return Long.toUnsignedString(chatId.subId) + "-" + suffix;
}
public static String toString(long chatId) {
char suffix;
switch (getType(chatId)) {
case SUPER:
suffix = 's';
break;
case BASIC:
suffix = 'b';
break;
case PRIVATE:
suffix = 'u';
break;
case SECRET:
suffix = 'd';
break;
default:
throw new IllegalStateException("Unexpected value: " + chatId);
}
return Long.toUnsignedString(getIdLong(chatId)) + "-" + suffix;
}
public static ChatId fromString(String chatId) {
var parts = chatId.split("-", 2);
if (parts.length != 2) {
throw new IllegalArgumentException("Malformed chat id");
}
if (parts[1].length() != 1) {
throw new IllegalArgumentException("Chat type is too long");
}
Type type;
switch (parts[1].charAt(0)) {
case 's':
type = Type.SUPER;
break;
case 'b':
type = Type.BASIC;
break;
case 'u':
type = Type.PRIVATE;
break;
case 'd':
type = Type.SECRET;
break;
default:
throw new IllegalStateException("Unexpected value: " + parts[1].charAt(0));
}
return new ChatId(type, Long.parseUnsignedLong(parts[0]) & SUB_ID_MASK);
}
public static Long stringToLong(String chatId) {
var parts = chatId.split("-", 2);
if (parts.length != 2) {
throw new IllegalArgumentException("Malformed chat id");
}
if (parts[1].length() != 1) {
throw new IllegalArgumentException("Chat type is too long");
}
long result;
switch (parts[1].charAt(0)) {
case 's':
result = TYPE_SUPER_LONG;
break;
case 'b':
result = TYPE_BASIC_LONG;
break;
case 'u':
result = TYPE_PRIVATE_LONG;
break;
case 'd':
result = TYPE_SECRET_LONG;
break;
default:
throw new IllegalStateException("Unexpected value: " + parts[1].charAt(0));
}
result |= (Long.parseUnsignedLong(parts[0]) & SUB_ID_MASK);
return result;
}
} }

View File

@ -5,11 +5,11 @@ import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException; import java.io.IOException;
public class ChatIdJsonDeserializer extends JsonDeserializer<Long> { public class ChatIdJsonDeserializer extends JsonDeserializer<ChatId> {
@Override @Override
public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { public ChatId deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
var id = p.readValueAs(String.class); var id = p.readValueAs(String.class);
return ChatId.stringToLong(id); return ChatId.fromString(id);
} }
} }

View File

@ -5,10 +5,10 @@ import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException; import java.io.IOException;
public class ChatIdJsonSerializer extends JsonSerializer<Long> { public class ChatIdJsonSerializer extends JsonSerializer<ChatId> {
@Override @Override
public void serialize(Long value, JsonGenerator gen, SerializerProvider serializers) throws IOException { public void serialize(ChatId value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(ChatId.toString(value)); gen.writeString(ChatId.toString(value));
} }
} }

View File

@ -0,0 +1,15 @@
package io.volvox.chats;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
public class ChatIdLongJsonDeserializer extends JsonDeserializer<Long> {
@Override
public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
var id = p.readValueAs(String.class);
return ChatId.stringToLong(id);
}
}

View File

@ -0,0 +1,14 @@
package io.volvox.chats;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class ChatIdLongJsonSerializer extends JsonSerializer<Long> {
@Override
public void serialize(Long value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(ChatId.toString(value));
}
}

View File

@ -1,5 +1,8 @@
package io.volvox.chats; package io.volvox.chats;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize.Typing;
import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
import java.net.URI; import java.net.URI;
@ -20,6 +23,8 @@ import javax.ws.rs.core.Response;
@ApplicationScoped @ApplicationScoped
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@JsonSerialize(using = ChatIdLongJsonSerializer.class, as = ChatId.class, typing = Typing.STATIC)
@JsonDeserialize(using = ChatIdLongJsonDeserializer.class, as = ChatId.class)
public class ChatResource { public class ChatResource {
@Inject @Inject
@ -32,8 +37,8 @@ public class ChatResource {
@GET @GET
@Path("/{id}") @Path("/{id}")
public Uni<Chat> get(@PathParam("id") Long id) { public Uni<Chat> get(@PathParam("id") ChatId id) {
return chatService.get(id); return chatService.get(id.toLong());
} }
@POST @POST
@ -44,14 +49,14 @@ public class ChatResource {
@PUT @PUT
@Path("/{id}") @Path("/{id}")
public Uni<Chat> update(@PathParam("id") Long id, Chat chat) { public Uni<Chat> update(@PathParam("id") ChatId id, Chat chat) {
return chatService.update(id, chat); return chatService.update(id.toLong(), chat);
} }
@DELETE @DELETE
@Path("/{id}") @Path("/{id}")
public Uni<Void> delete(@PathParam("id") Long id) { public Uni<Void> delete(@PathParam("id") ChatId id) {
return chatService.delete(id); return chatService.delete(id.toLong());
} }
@GET @GET

View File

@ -2,16 +2,18 @@ quarkus.http.port=8282
# we don't need SSL here, let's disable it to have a more compact native executable # we don't need SSL here, let's disable it to have a more compact native executable
quarkus.ssl.native=false quarkus.ssl.native=false
%prod.quarkus.datasource.db-kind=postgresql quarkus.datasource.db-kind=postgresql
%prod.quarkus.datasource.username=quarkus_test %dev.quarkus.datasource.username=quarkus_test
%prod.quarkus.datasource.password=quarkus_test %dev.quarkus.datasource.password=quarkus_test
%prod.quarkus.datasource.username=volvox
%prod.quarkus.datasource.password=volvox
quarkus.hibernate-orm.database.generation=drop-and-create quarkus.hibernate-orm.database.generation=update
quarkus.hibernate-orm.log.sql=true %dev.quarkus.datasource.devservices.enabled=true
quarkus.hibernate-orm.sql-load-script=import.sql %dev.quarkus.datasource.devservices.image-name=postgres
# Reactive config # Reactive config
%prod.quarkus.datasource.reactive.url=vertx-reactive:postgresql://localhost/quarkus_test quarkus.vertx.prefer-native-transport=true
%prod.quarkus.datasource.reactive.url=vertx-reactive:postgresql://database/volvox-chats
quarkus.elasticsearch.health.enabled=false quarkus.elasticsearch.hosts=searchengine:9200
quarkus.elasticsearch.reactive.health.enabled=true

View File

@ -41,7 +41,7 @@ public class ChatsEndpointTest {
.when() .when()
.body("{\"name\" : \"Telegram Official\"}") .body("{\"name\" : \"Telegram Official\"}")
.contentType("application/json") .contentType("application/json")
.put("/chats/777000") .put("/chats/777000-u")
.then() .then()
.statusCode(200) .statusCode(200)
.body( .body(
@ -62,7 +62,7 @@ public class ChatsEndpointTest {
//Delete Telegram: //Delete Telegram:
given() given()
.when() .when()
.delete("/chats/777000") .delete("/chats/777000-u")
.then() .then()
.statusCode(204); .statusCode(204);
@ -101,7 +101,7 @@ public class ChatsEndpointTest {
public void testEntityNotFoundForDelete() { public void testEntityNotFoundForDelete() {
given() given()
.when() .when()
.delete("/chats/777123") .delete("/chats/777123-u")
.then() .then()
.statusCode(404) .statusCode(404)
.body(emptyString()); .body(emptyString());
@ -113,7 +113,7 @@ public class ChatsEndpointTest {
.when() .when()
.body("{\"id\": \"777234-u\", \"name\" : \"Juan\"}") .body("{\"id\": \"777234-u\", \"name\" : \"Juan\"}")
.contentType("application/json") .contentType("application/json")
.put("/chats/777234") .put("/chats/777234-u")
.then() .then()
.statusCode(200); .statusCode(200);
} }
@ -124,7 +124,7 @@ public class ChatsEndpointTest {
.when() .when()
.body("{\"name\" : \"Juan\"}") .body("{\"name\" : \"Juan\"}")
.contentType("application/json") .contentType("application/json")
.put("/chats/777234") .put("/chats/777234-u")
.then() .then()
.statusCode(500); .statusCode(500);
} }

View File

@ -1,9 +0,0 @@
package io.volvox.chats;
import io.quarkus.test.junit.QuarkusIntegrationTest;
@QuarkusIntegrationTest
public class ChatsEndpointTestIT extends ChatsEndpointTest {
// Execute the same tests but in native mode.
}

View File

@ -1,12 +1,17 @@
package io.volvox.chats; package io.volvox.chats;
import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.logging.Log; import io.quarkus.logging.Log;
import io.reactiverse.elasticsearch.client.mutiny.RestHighLevelClient; import io.reactiverse.elasticsearch.client.mutiny.RestHighLevelClient;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
import io.vertx.core.json.JsonObject; import io.vertx.core.json.JsonObject;
import java.io.IOException;
import java.util.Objects;
import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject; import javax.inject.Inject;
import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.admin.indices.cache.clear.ClearIndicesCacheRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.flush.FlushRequest; import org.elasticsearch.action.admin.indices.flush.FlushRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest; import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
@ -14,6 +19,7 @@ import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.indices.CreateIndexRequest; import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.common.collect.List;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.rest.RestStatus; import org.elasticsearch.rest.RestStatus;
@ -24,14 +30,29 @@ public class ChatsServiceWarmup {
@Inject @Inject
RestHighLevelClient restHighLevelClient; RestHighLevelClient restHighLevelClient;
public void warmup() { public void warmup() {
try {
resetDb();
} catch (IOException e) {
throw new IllegalStateException(e);
}
createIndices(); createIndices();
chatService.listAll().onItem().transformToUni(this::updateIndex).merge().select().last().toUni().await() chatService.listAll().onItem().transformToUni(this::updateIndex).merge().collect().last().await()
.indefinitely(); .indefinitely();
restHighLevelClient.indices().flushAsyncAndAwait(new FlushRequest("chats"), RequestOptions.DEFAULT); restHighLevelClient.indices().flushAsyncAndAwait(new FlushRequest("chats").force(true).waitIfOngoing(true), RequestOptions.DEFAULT);
restHighLevelClient.indices().refreshAsyncAndAwait(new RefreshRequest("chats"), RequestOptions.DEFAULT); restHighLevelClient.indices().refreshAsyncAndAwait(new RefreshRequest("chats"), RequestOptions.DEFAULT);
restHighLevelClient.indices().clearCacheAsyncAndAwait(new ClearIndicesCacheRequest("chats"), RequestOptions.DEFAULT);
}
private void resetDb() throws IOException {
var db = new String(Objects
.requireNonNull(ChatsServiceWarmup.class.getResourceAsStream("/import.sql"), "Cannot find import.sql")
.readAllBytes());
Panache.getSession().flatMap(sess -> Multi.createFrom().iterable(List.of(db.split("\n")))
.filter(s -> !s.isBlank() && !s.startsWith("#"))
.onItem().transformToUni(query -> sess.createNativeQuery(query).executeUpdate()).merge().collect().last()).await()
.indefinitely();
} }
private void createIndices() { private void createIndices() {

View File

@ -1,3 +1,5 @@
TRUNCATE chat, chat_name, chat_username CASCADE;
INSERT INTO chat(id, status, name, username) VALUES (9007199256673076, 1, 'My Supergroup', 'mysupergroup'); 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_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_username(chat_id, id, time, username) VALUES (9007199256673076, 12345001, current_timestamp, 'mysupergroup');

View File

@ -8,13 +8,13 @@
<properties> <properties>
<compiler-plugin.version>3.8.1</compiler-plugin.version> <compiler-plugin.version>3.8.1</compiler-plugin.version>
<maven.compiler.parameters>true</maven.compiler.parameters> <maven.compiler.parameters>true</maven.compiler.parameters>
<maven.compiler.source>17</maven.compiler.source> <maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target> <maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>2.4.1.Final</quarkus.platform.version> <quarkus.platform.version>2.5.0.Final</quarkus.platform.version>
<surefire-plugin.version>3.0.0-M5</surefire-plugin.version> <surefire-plugin.version>3.0.0-M5</surefire-plugin.version>
<volvox.tdlight.version>2.7.9.2</volvox.tdlight.version> <volvox.tdlight.version>2.7.9.2</volvox.tdlight.version>
<volvox.tdlight.natives.version>4.0.183</volvox.tdlight.natives.version> <volvox.tdlight.natives.version>4.0.183</volvox.tdlight.natives.version>
@ -87,6 +87,11 @@
<artifactId>rest-assured</artifactId> <artifactId>rest-assured</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>it.tdlight</groupId> <groupId>it.tdlight</groupId>

View File

@ -72,7 +72,8 @@ public class TdEventBusClient implements TdClient {
@ConsumeEvent(value = "td.send", codec = TdObjectCodec.class) @ConsumeEvent(value = "td.send", codec = TdObjectCodec.class)
public void onSendRequest(Message<TdObjectCodec.TdObject> msg) { public void onSendRequest(Message<TdObjectCodec.TdObject> msg) {
this.send(msg.body().getObject()).subscribe().with(message -> msg.reply(message, SEND_OPTS), ex -> { this.send(msg.body().getObject()).subscribe().with(message -> msg.reply(message, SEND_OPTS), ex -> {
if (ex instanceof TelegramException tdException) { if (ex instanceof TelegramException) {
TelegramException tdException = (TelegramException) ex;
msg.fail(tdException.getCode(), tdException.getMessage()); msg.fail(tdException.getCode(), tdException.getMessage());
} else { } else {
msg.fail(500, ex.toString()); msg.fail(500, ex.toString());

View File

@ -46,9 +46,14 @@ public class TdService {
} }
void shutdown(@Observes ShutdownEvent event) { void shutdown(@Observes ShutdownEvent event) {
clients.forEach((uuid, client) -> client.dispose()); disposeAll();
} }
public void disposeAll() {
clients.forEach((uuid, client) -> client.dispose());
clients.clear();
}
public Optional<TdClient> get(String uuid) { public Optional<TdClient> get(String uuid) {
if (uuid == null) return Optional.empty(); if (uuid == null) return Optional.empty();
return Optional.ofNullable(clients.get(uuid)); return Optional.ofNullable(clients.get(uuid));
@ -62,4 +67,4 @@ public class TdService {
public Set<Entry<String, TdClient>> getSessions() { public Set<Entry<String, TdClient>> getSessions() {
return clients.entrySet(); return clients.entrySet();
} }
} }

View File

@ -1,18 +1,22 @@
package io.volvox.td; package io.volvox.td;
import static io.restassured.RestAssured.given; import static io.restassured.RestAssured.given;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.text.IsEmptyString.emptyOrNullString; import static org.hamcrest.text.IsEmptyString.emptyOrNullString;
import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.junit.QuarkusTest;
import java.util.Set; import javax.inject.Inject;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@QuarkusTest @QuarkusTest
public class TdResourceTest { public class TdResourceTest {
@Inject TdService tdService;
@Test @Test
public void testEmptyList() { public void testEmptyList() {
given() given()
@ -50,18 +54,22 @@ public class TdResourceTest {
.body() .body()
.asString(); .asString();
var expectedBodyElems = Set.of(sessionId1, sessionId2); var bodyElems = given()
.when().get("/td/list")
.then()
.statusCode(200)
.extract()
.body()
.asString()
.split("\n");
var bodyElems = Set.of(given() assertThat(bodyElems).containsExactlyInAnyOrder(sessionId1, sessionId2);
.when().get("/td/list")
.then()
.statusCode(200)
.extract()
.body()
.asString()
.split("\n"));
Assertions.assertEquals(expectedBodyElems, bodyElems);
} }
} @BeforeEach
@AfterEach
public void resetTdServices() {
tdService.disposeAll();
}
}