diff --git a/.vscode/settings.json b/.vscode/settings.json index 1aa396c..3e30470 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,8 @@ "**/Thumbs.db": true, "**/target": true, "**/docker": true, - "node_modules": true + "node_modules": true, + "**/.mvn": true }, "explorerExclude.backup": {}, "java.compile.nullAnalysis.mode": "automatic", diff --git a/account-service/pom.xml b/account-service/pom.xml index 3d8bd51..b941572 100644 --- a/account-service/pom.xml +++ b/account-service/pom.xml @@ -54,6 +54,12 @@ jose4j 0.9.3 + + net.datafaker + datafaker + 2.0.2 + test + io.quarkus quarkus-messaging-kafka diff --git a/account-service/src/main/java/com/drinkool/entities/ManagerEntity.java b/account-service/src/main/java/com/drinkool/entities/ManagerEntity.java index 6d4691d..8b08ce8 100644 --- a/account-service/src/main/java/com/drinkool/entities/ManagerEntity.java +++ b/account-service/src/main/java/com/drinkool/entities/ManagerEntity.java @@ -3,11 +3,20 @@ package com.drinkool.entities; import com.drinkool.Role; import com.drinkool.models.UserModel; import jakarta.persistence.*; +import java.util.ArrayList; +import java.util.List; @Entity @Table(name = Role.Manager) public class ManagerEntity extends UserModel { + @OneToMany( + mappedBy = "serve", + cascade = CascadeType.ALL, + orphanRemoval = true + ) + public List staffs = new ArrayList<>(); + public ManagerEntity() { super(Role.Manager); } diff --git a/account-service/src/main/java/com/drinkool/entities/StaffEntity.java b/account-service/src/main/java/com/drinkool/entities/StaffEntity.java new file mode 100644 index 0000000..7ba98b8 --- /dev/null +++ b/account-service/src/main/java/com/drinkool/entities/StaffEntity.java @@ -0,0 +1,30 @@ +package com.drinkool.entities; + +import com.drinkool.Role; +import com.drinkool.models.UserModel; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.transaction.Transactional; + +@Entity +@Table(name = Role.Staff) +public class StaffEntity extends UserModel { + + @Transactional + public void signup( + String name, + String phone, + String rawPassword, + ManagerEntity serve + ) { + this.serve = serve; + super.signup(name, phone, rawPassword); + } + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "managerId") + public ManagerEntity serve; +} diff --git a/account-service/src/main/java/com/drinkool/filters/HeaderRolesFilter.java b/account-service/src/main/java/com/drinkool/filters/HeaderRolesFilter.java new file mode 100644 index 0000000..618b711 --- /dev/null +++ b/account-service/src/main/java/com/drinkool/filters/HeaderRolesFilter.java @@ -0,0 +1,7 @@ +package com.drinkool.filters; + +import com.drinkool.lib.utils.BaseHeaderAuthentication; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class HeaderRolesFilter extends BaseHeaderAuthentication {} diff --git a/account-service/src/main/java/com/drinkool/services/AccountService.java b/account-service/src/main/java/com/drinkool/services/AccountService.java index caf12fd..b5843a1 100644 --- a/account-service/src/main/java/com/drinkool/services/AccountService.java +++ b/account-service/src/main/java/com/drinkool/services/AccountService.java @@ -3,6 +3,7 @@ package com.drinkool.services; import com.drinkool.Role; import com.drinkool.Url; import com.drinkool.entities.ManagerEntity; +import com.drinkool.entities.StaffEntity; import com.drinkool.lib.dtos.SmsOtp; import com.drinkool.models.UserModel; import jakarta.inject.Inject; @@ -28,6 +29,10 @@ public class AccountService { ManagerEntity.find("phone", input.phone).firstResult() != null ) return Response.accepted().entity(Role.Manager).build(); + if ( + StaffEntity.find("phone", input.phone).firstResult() != null + ) return Response.accepted().entity(Role.Staff).build(); + String otp = otpService.generateAndSaveOtp(input.phone); System.out.println("OTP cho " + input.phone + " là: " + otp); diff --git a/account-service/src/main/java/com/drinkool/services/ManagerService.java b/account-service/src/main/java/com/drinkool/services/ManagerService.java index 5e76b17..b9ef5f9 100644 --- a/account-service/src/main/java/com/drinkool/services/ManagerService.java +++ b/account-service/src/main/java/com/drinkool/services/ManagerService.java @@ -44,25 +44,25 @@ public class ManagerService extends BaseService { @POST @Path(Url.Login) public Response login(Login input) { - ManagerEntity customer = ManagerEntity.find( + ManagerEntity manager = ManagerEntity.find( "phone", input.phone ).firstResult(); - if (customer == null) return Response.status(Response.Status.BAD_REQUEST) + if (manager == null) return Response.status(Response.Status.BAD_REQUEST) .entity("InvalidPhoneNumber") .build(); - if (!customer.login(input.password)) return Response.status( + if (!manager.login(input.password)) return Response.status( Response.Status.BAD_REQUEST ) .entity("InvalidPassword") .build(); return Response.accepted() - .header(InternalValue.userId, customer.id.toString()) + .header(InternalValue.userId, manager.id.toString()) .header(InternalValue.role, Role.Manager) - .entity(customer) + .entity(manager) .build(); } } diff --git a/account-service/src/main/java/com/drinkool/services/StaffService.java b/account-service/src/main/java/com/drinkool/services/StaffService.java new file mode 100644 index 0000000..1a6c27c --- /dev/null +++ b/account-service/src/main/java/com/drinkool/services/StaffService.java @@ -0,0 +1,71 @@ +package com.drinkool.services; + +import com.drinkool.InternalValue; +import com.drinkool.Role; +import com.drinkool.Url; +import com.drinkool.entities.ManagerEntity; +import com.drinkool.entities.StaffEntity; +import com.drinkool.lib.dtos.Login; +import com.drinkool.lib.dtos.Signup; +import io.vertx.core.http.HttpServerRequest; +import jakarta.annotation.security.RolesAllowed; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Response; +import java.util.UUID; + +@Path(Role.Staff) +public class StaffService extends BaseService { + + @Inject + HttpServerRequest request; + + public StaffService() { + super(StaffEntity.class); + } + + @POST + @Path(Url.Signup) + @RolesAllowed(Role.Manager) + @Transactional + public Response signup(Signup input) { + UUID managerId = UUID.fromString(request.getHeader(InternalValue.userId)); + ManagerEntity manager = ManagerEntity.findById(managerId); + + if (manager == null) return Response.status(Response.Status.BAD_REQUEST) + .entity("InvalidManager") + .build(); + + StaffEntity staff = new StaffEntity(); + staff.signup(input.name, input.phone, input.password, manager); + + return Response.status(Response.Status.CREATED) + .header(InternalValue.userId, staff.id.toString()) + .header(InternalValue.role, Role.Staff) + .build(); + } + + @POST + @Path(Url.Login) + public Response login(Login input) { + StaffEntity staff = StaffEntity.find("phone", input.phone).firstResult(); + + if (staff == null) return Response.status(Response.Status.BAD_REQUEST) + .entity("InvalidPhoneNumber") + .build(); + + if (!staff.login(input.password)) return Response.status( + Response.Status.BAD_REQUEST + ) + .entity("InvalidPassword") + .build(); + + return Response.accepted() + .header(InternalValue.userId, staff.id.toString()) + .header(InternalValue.role, Role.Staff) + .entity(staff) + .build(); + } +} diff --git a/account-service/src/test/java/com/drinkool/services/StaffServiceTest.java b/account-service/src/test/java/com/drinkool/services/StaffServiceTest.java new file mode 100644 index 0000000..5cce294 --- /dev/null +++ b/account-service/src/test/java/com/drinkool/services/StaffServiceTest.java @@ -0,0 +1,120 @@ +package com.drinkool.services; + +import static io.restassured.RestAssured.*; +import static org.junit.jupiter.api.Assertions.*; + +import com.drinkool.*; +import com.drinkool.entities.StaffEntity; +import com.drinkool.lib.dtos.*; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.kafka.*; +import io.restassured.http.ContentType; +import io.smallrye.reactive.messaging.kafka.companion.ConsumerTask; +import io.smallrye.reactive.messaging.kafka.companion.KafkaCompanion; +import jakarta.ws.rs.core.Response; +import java.time.Duration; +import net.datafaker.Faker; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@QuarkusTest +@QuarkusTestResource(KafkaCompanionResource.class) +public class StaffServiceTest { + + private final Faker faker = new Faker(); + + private String managerId; + + @BeforeEach + void setup() { + ManagerSignup input = new ManagerSignup(); + input.name = "Ho Van Quan"; + input.phone = Utils.generateRandomPhone(); + input.password = "strong_password"; + input.eateryName = "Quan Com Ngon"; + + var response = given() + .contentType("application/json") + .body(input) + .when() + .post(Role.Manager + Url.Signup) + .then() + .statusCode(201) + .header(InternalValue.userId, org.hamcrest.Matchers.notNullValue()) + .header(InternalValue.role, Role.Manager) + .extract(); + + managerId = response.header(InternalValue.userId); + } + + @Test + void signup_Success() { + String staffName = faker.name().fullName(); + String staffPhone = Utils.generateRandomPhone(); + + Signup signup = new Signup( + staffName, + staffPhone, + faker.famousLastWords().lastWords() + ); + + given() + .contentType(ContentType.JSON) + .header(InternalValue.role, Role.Manager) + .header(InternalValue.userId, managerId) + .body(signup) + .when() + .post(Role.Staff + Url.Signup) + .then() + .statusCode(Response.Status.CREATED.getStatusCode()) + .header(InternalValue.userId, org.hamcrest.Matchers.notNullValue()) + .header(InternalValue.role, Role.Staff); + + StaffEntity staff = StaffEntity.find("phone", staffPhone).firstResult(); + + assertEquals(managerId, staff.serve.id.toString()); + } + + @Test + void signup_FailedDueToInvalidRole() { + given() + .contentType(ContentType.JSON) + .header(InternalValue.role, Role.Customer) + .header(InternalValue.userId, managerId) + .body("") + .when() + .post(Role.Staff + Url.Signup) + .then() + .statusCode(Response.Status.FORBIDDEN.getStatusCode()); + } + + @Test + void testLoginSuccess() { + String staffName = faker.name().fullName(); + String staffPhone = Utils.generateRandomPhone(); + String password = faker.famousLastWords().lastWords(); + + Signup signup = new Signup(staffName, staffPhone, password); + + given() + .contentType(ContentType.JSON) + .header(InternalValue.role, Role.Manager) + .header(InternalValue.userId, managerId) + .body(signup) + .when() + .post(Role.Staff + Url.Signup) + .then(); + + Login loginInput = new Login(staffPhone, password); + + given() + .contentType(ContentType.JSON) + .body(loginInput) + .when() + .post(Role.Staff + Url.Login) + .then() + .statusCode(Response.Status.ACCEPTED.getStatusCode()); + } +} diff --git a/cart-service/pom.xml b/cart-service/pom.xml index 9d6d072..8c4f4c4 100644 --- a/cart-service/pom.xml +++ b/cart-service/pom.xml @@ -181,6 +181,7 @@ + false false true diff --git a/gateway-service/src/main/java/com/drinkool/controller/EateryController.java b/gateway-service/src/main/java/com/drinkool/controller/EateryController.java index 4b67108..53e718b 100644 --- a/gateway-service/src/main/java/com/drinkool/controller/EateryController.java +++ b/gateway-service/src/main/java/com/drinkool/controller/EateryController.java @@ -1,5 +1,6 @@ package com.drinkool.controller; +import com.drinkool.InternalValue; import com.drinkool.Url; import com.drinkool.enums.SortBy; import com.drinkool.lib.dtos.GraphqlDto; @@ -9,6 +10,7 @@ import io.smallrye.mutiny.Uni; import jakarta.inject.Inject; import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; @@ -43,8 +45,12 @@ public class EateryController extends GraphqlBase { @POST @Path(Url.Review) - public Uni receiveReview(SendReview input) { - return reviewService.receiveReview(input); + public Uni receiveReview( + SendReview input, + @HeaderParam(InternalValue.userId) UUID userId, + @HeaderParam(InternalValue.role) String role + ) { + return reviewService.receiveReview(input, userId, role); } @POST diff --git a/gateway-service/src/main/java/com/drinkool/services/ReviewService.java b/gateway-service/src/main/java/com/drinkool/services/ReviewService.java index 47d4a61..8cee0e2 100644 --- a/gateway-service/src/main/java/com/drinkool/services/ReviewService.java +++ b/gateway-service/src/main/java/com/drinkool/services/ReviewService.java @@ -1,11 +1,13 @@ package com.drinkool.services; +import com.drinkool.InternalValue; import com.drinkool.Url; import com.drinkool.enums.SortBy; import com.drinkool.lib.dtos.SendReview; import io.smallrye.mutiny.Uni; import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; @@ -25,5 +27,9 @@ public interface ReviewService { ); @POST - public Uni receiveReview(SendReview input); + public Uni receiveReview( + SendReview input, + @HeaderParam(InternalValue.userId) UUID userId, + @HeaderParam(InternalValue.role) String role + ); } diff --git a/lib/src/main/java/com/drinkool/Role.java b/lib/src/main/java/com/drinkool/Role.java index ee2e5e6..4cdeac9 100644 --- a/lib/src/main/java/com/drinkool/Role.java +++ b/lib/src/main/java/com/drinkool/Role.java @@ -4,4 +4,5 @@ public class Role { public static final String Customer = "customer"; public static final String Manager = "manager"; + public static final String Staff = "Staff"; } diff --git a/lib/src/main/java/com/drinkool/lib/dtos/Login.java b/lib/src/main/java/com/drinkool/lib/dtos/Login.java index e87f3d8..9719e11 100644 --- a/lib/src/main/java/com/drinkool/lib/dtos/Login.java +++ b/lib/src/main/java/com/drinkool/lib/dtos/Login.java @@ -1,5 +1,10 @@ package com.drinkool.lib.dtos; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor public class Login { public String phone; diff --git a/lib/src/main/java/com/drinkool/lib/dtos/Signup.java b/lib/src/main/java/com/drinkool/lib/dtos/Signup.java index 0db754a..3d2af07 100644 --- a/lib/src/main/java/com/drinkool/lib/dtos/Signup.java +++ b/lib/src/main/java/com/drinkool/lib/dtos/Signup.java @@ -1,5 +1,10 @@ package com.drinkool.lib.dtos; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@AllArgsConstructor public class Signup { public String name;