From a20720541bf17a3ec6996965d6dcaa2082d8b4b2 Mon Sep 17 00:00:00 2001 From: TakahashiNguyen Date: Wed, 22 Apr 2026 08:32:41 +0000 Subject: [PATCH] fix: add eatery query (#35) Co-authored-by: TakahashiNg <83152264+TakahashiNguyen@users.noreply.github.com> Reviewed-on: https://git.demonkernel.io.vn/FoodSurf/backend/pulls/35 --- eatery-service/pom.xml | 19 +++++- .../com/drinkool/entities/EateryEntity.java | 13 +++- .../com/drinkool/entities/MenuItemEntity.java | 7 ++ .../com/drinkool/services/EateryService.java | 17 ++++- .../drinkool/services/EateryServiceTest.java | 66 +++++++++++++++++++ gateway-service/pom.xml | 4 ++ .../drinkool/controller/EateryController.java | 34 ++++++++++ .../controller/BaseControllerTest.java | 5 +- k8s/gateway.yaml | 2 + k8s/kafdrop.yaml | 20 +++--- 10 files changed, 170 insertions(+), 17 deletions(-) create mode 100644 gateway-service/src/main/java/com/drinkool/controller/EateryController.java diff --git a/eatery-service/pom.xml b/eatery-service/pom.xml index 9f926e5..c3be3c3 100644 --- a/eatery-service/pom.xml +++ b/eatery-service/pom.xml @@ -1,5 +1,7 @@ - + 4.0.0 @@ -42,6 +44,16 @@ lib ${project.version} + + net.datafaker + datafaker + 2.0.2 + test + + + io.quarkus + quarkus-smallrye-graphql + io.quarkus quarkus-messaging-kafka @@ -144,7 +156,8 @@ @{argLine} - ${project.build.directory}/${project.build.finalName}-runner + + ${project.build.directory}/${project.build.finalName}-runner org.jboss.logmanager.LogManager ${maven.home} @@ -168,4 +181,4 @@ - + \ No newline at end of file diff --git a/eatery-service/src/main/java/com/drinkool/entities/EateryEntity.java b/eatery-service/src/main/java/com/drinkool/entities/EateryEntity.java index ae47d29..d8e16d9 100644 --- a/eatery-service/src/main/java/com/drinkool/entities/EateryEntity.java +++ b/eatery-service/src/main/java/com/drinkool/entities/EateryEntity.java @@ -3,6 +3,8 @@ package com.drinkool.entities; import com.drinkool.lib.entities.BaseEntity; import jakarta.persistence.*; import jakarta.transaction.Transactional; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; @Entity @@ -18,10 +20,17 @@ public class EateryEntity extends BaseEntity { @Column(nullable = false) public boolean isVerified = false; + @OneToMany( + mappedBy = "eatery", + cascade = CascadeType.ALL, + orphanRemoval = true + ) + public List menus = new ArrayList<>(); + public EateryEntity() {} @Transactional - public void create(UUID ownerId, String name) { + public EateryEntity create(UUID ownerId, String name) { EateryEntity existedEatery = EateryEntity.find( "ownerId", ownerId @@ -33,5 +42,7 @@ public class EateryEntity extends BaseEntity { this.name = name; this.persist(); + + return this; } } diff --git a/eatery-service/src/main/java/com/drinkool/entities/MenuItemEntity.java b/eatery-service/src/main/java/com/drinkool/entities/MenuItemEntity.java index efbd32a..39ddb09 100644 --- a/eatery-service/src/main/java/com/drinkool/entities/MenuItemEntity.java +++ b/eatery-service/src/main/java/com/drinkool/entities/MenuItemEntity.java @@ -9,9 +9,16 @@ import java.util.*; @Table public class MenuItemEntity extends BaseEntity { + @Column(nullable = false) public String name; + + @Column(nullable = false) public double price; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "eatery_id") + public EateryEntity eatery; + // Danh sách các nguyên liệu cấu thành món ăn này @OneToMany( mappedBy = "menuItem", diff --git a/eatery-service/src/main/java/com/drinkool/services/EateryService.java b/eatery-service/src/main/java/com/drinkool/services/EateryService.java index 68844ef..50a26af 100644 --- a/eatery-service/src/main/java/com/drinkool/services/EateryService.java +++ b/eatery-service/src/main/java/com/drinkool/services/EateryService.java @@ -2,12 +2,17 @@ package com.drinkool.services; import com.drinkool.dtos.event.ManagerCreate; import com.drinkool.entities.EateryEntity; - import jakarta.enterprise.context.ApplicationScoped; import jakarta.transaction.Transactional; +import java.util.List; +import java.util.UUID; +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Name; +import org.eclipse.microprofile.graphql.Query; import org.eclipse.microprofile.reactive.messaging.Incoming; @ApplicationScoped +@GraphQLApi public class EateryService { @Incoming("manager-in") @@ -17,4 +22,14 @@ public class EateryService { eatery.create(input.id, input.eateryName); } + + @Query("allEateries") + public List getAll() { + return EateryEntity.listAll(); + } + + @Query("eateryById") + public EateryEntity getEatery(@Name("id") UUID id) { + return EateryEntity.findById(id); + } } diff --git a/eatery-service/src/test/java/com/drinkool/services/EateryServiceTest.java b/eatery-service/src/test/java/com/drinkool/services/EateryServiceTest.java index 897be96..16bde3e 100644 --- a/eatery-service/src/test/java/com/drinkool/services/EateryServiceTest.java +++ b/eatery-service/src/test/java/com/drinkool/services/EateryServiceTest.java @@ -1,19 +1,30 @@ package com.drinkool.services; +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.Matchers.hasItems; + import com.drinkool.dtos.event.ManagerCreate; import com.drinkool.entities.EateryEntity; import io.quarkus.test.junit.QuarkusTest; +import io.restassured.http.ContentType; import io.smallrye.reactive.messaging.memory.InMemoryConnector; import jakarta.enterprise.context.control.ActivateRequestContext; import jakarta.enterprise.inject.Any; import jakarta.inject.Inject; +import jakarta.transaction.Transactional; import java.util.UUID; +import net.datafaker.Faker; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @QuarkusTest public class EateryServiceTest { + private final Faker faker = new Faker(); + @Inject @Any InMemoryConnector connector; @@ -40,4 +51,59 @@ public class EateryServiceTest { Assertions.assertNotNull(eatery, "Eatery should be created in DB"); Assertions.assertEquals("Quan Bun Cha Test", eatery.name); } + + private UUID lastSavedId; + private String lastSavedName; + + @BeforeEach + @Transactional + void setup() { + EateryEntity.deleteAll(); + + lastSavedName = faker.restaurant().name(); + + EateryEntity eatery = new EateryEntity(); + lastSavedId = eatery.create(UUID.randomUUID(), lastSavedName).id; + + for (int i = 0; i < 2; i++) { + EateryEntity e = new EateryEntity(); + e.create(UUID.randomUUID(), faker.restaurant().name()); + } + } + + @Test + void testGetAllEateries() { + // Cấu trúc query GraphQL + String query = "{ \"query\": \"{ allEateries { ownerId name } }\" }"; + + given() + .contentType(ContentType.JSON) + .body(query) + .when() + .post("/graphql") + .then() + .statusCode(200) + // GraphQL trả về dữ liệu bọc trong object "data" + .body("data.allEateries", notNullValue()) + .body("data.allEateries.name", hasItems(lastSavedName)); + } + + @Test + void testGetEateryById() { + // Sử dụng biến id đã lưu ở setup + // Lưu ý: Trong GraphQL, String/UUID phải nằm trong dấu ngoặc kép + String query = String.format( + "{ \"query\": \"{ eateryById(id: \\\"%s\\\") { ownerId name } }\" }", + lastSavedId + ); + + given() + .contentType(ContentType.JSON) + .body(query) + .when() + .post("/graphql") + .then() + .statusCode(200) + .body("data.eateryById.name", is(lastSavedName)); + } } diff --git a/gateway-service/pom.xml b/gateway-service/pom.xml index 1743d61..ecbdb89 100644 --- a/gateway-service/pom.xml +++ b/gateway-service/pom.xml @@ -48,6 +48,10 @@ jose4j 0.9.6 + + io.quarkus + quarkus-smallrye-graphql-client + com.drinkool lib diff --git a/gateway-service/src/main/java/com/drinkool/controller/EateryController.java b/gateway-service/src/main/java/com/drinkool/controller/EateryController.java new file mode 100644 index 0000000..3e98288 --- /dev/null +++ b/gateway-service/src/main/java/com/drinkool/controller/EateryController.java @@ -0,0 +1,34 @@ +package com.drinkool.controller; + +import io.smallrye.graphql.client.GraphQLClient; +import io.smallrye.graphql.client.dynamic.api.DynamicGraphQLClient; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Response; +import java.util.concurrent.ExecutionException; + +@Path("/api/eatery") +public class EateryController { + + @GraphQLClient("eatery") + DynamicGraphQLClient dynamicClient; + + @POST + @Path("/graphql") + public Response forward(String query) + throws ExecutionException, InterruptedException { + io.smallrye.graphql.client.Response response = dynamicClient.executeSync( + query + ); + + if (response.hasError()) { + return jakarta.ws.rs.core.Response.status(500) + .entity(response.getErrors()) + .build(); + } + + return jakarta.ws.rs.core.Response.ok( + response.getData().toString() + ).build(); + } +} diff --git a/gateway-service/src/test/java/com/drinkool/controller/BaseControllerTest.java b/gateway-service/src/test/java/com/drinkool/controller/BaseControllerTest.java index 3b04780..2382510 100644 --- a/gateway-service/src/test/java/com/drinkool/controller/BaseControllerTest.java +++ b/gateway-service/src/test/java/com/drinkool/controller/BaseControllerTest.java @@ -3,8 +3,9 @@ package com.drinkool.controller; import static io.restassured.RestAssured.*; import static org.hamcrest.CoreMatchers.*; -import com.drinkool.*; - +import com.drinkool.InternalValue; +import com.drinkool.Role; +import com.drinkool.Url; import io.quarkus.test.junit.QuarkusTest; import org.junit.jupiter.api.Test; diff --git a/k8s/gateway.yaml b/k8s/gateway.yaml index d4bc419..47f2316 100644 --- a/k8s/gateway.yaml +++ b/k8s/gateway.yaml @@ -21,6 +21,8 @@ spec: env: - name: QUARKUS_REST_CLIENT_ACCOUNT_URL value: "http://account-service" + - name: QUARKUS_SMALLRYE_GRAPHQL_CLIENT_EATERY_URL + value: "http://eatery-service/graphql" - name: GATEWAY_JWT_SECRET valueFrom: secretKeyRef: diff --git a/k8s/kafdrop.yaml b/k8s/kafdrop.yaml index 9e0a2ed..129d0ea 100644 --- a/k8s/kafdrop.yaml +++ b/k8s/kafdrop.yaml @@ -13,13 +13,13 @@ spec: app: kafdrop spec: containers: - - name: kafdrop - image: obsidiandynamics/kafdrop - ports: - - containerPort: 9000 - env: - - name: KAFKA_BROKERCONNECT - value: "kafka-service:9092" # Trỏ vào service Kafka của bạn + - name: kafdrop + image: obsidiandynamics/kafdrop + ports: + - containerPort: 9000 + env: + - name: KAFKA_BROKERCONNECT + value: "kafka-service:9092" --- apiVersion: v1 kind: Service @@ -30,6 +30,6 @@ spec: selector: app: kafdrop ports: - - protocol: TCP - port: 9000 - nodePort: 30900 \ No newline at end of file + - protocol: TCP + port: 9000 + nodePort: 30900