diff --git a/pom.xml b/pom.xml index 78ee59d..0d67e2b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,39 +3,37 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - + com.javarush.khmelov - project-ledzeppelin + project-Pantera 1.0-SNAPSHOT - ProjectLedzeppelin + ProjectPantera war - + UTF-8 21 21 - 5.10.2 - - - org.springframework.boot - spring-boot-starter-parent - 3.3.5 - - + org.springframework.boot spring-boot-dependencies + 3.3.4 pom import - 3.3.5 - + + + org.projectlombok + lombok + provided + jakarta.servlet jakarta.servlet-api @@ -49,12 +47,20 @@ org.glassfish.web jakarta.servlet.jsp.jstl - - org.projectlombok - lombok - provided + org.slf4j + slf4j-api + + + org.apache.logging.log4j + log4j-slf4j2-impl + + org.apache.logging.log4j + log4j-core + + + org.junit.jupiter junit-jupiter-api @@ -65,30 +71,83 @@ junit-jupiter-engine test - + + org.mockito + mockito-core + test + + + org.mockito + mockito-junit-jupiter + test + + + - + org.apache.maven.plugins maven-war-plugin - 3.4.0 + 3.3.2 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + + **/*IT.java + + + org.apache.maven.plugins - maven-compiler-plugin + maven-failsafe-plugin + 3.2.5 + + + integration-test + + integration-test + + + + verify + + verify + + + - - - org.projectlombok - lombok - 1.18.34 - - + + **/*IT.java + + + + org.jacoco + jacoco-maven-plugin + 0.8.11 + + + + prepare-agent + + + + report + verify + + report + + + + - \ No newline at end of file diff --git a/src/main/java/com/javarush/khmelov/cmd/EditUser.java b/src/main/java/com/javarush/khmelov/cmd/EditUser.java deleted file mode 100644 index ae191b4..0000000 --- a/src/main/java/com/javarush/khmelov/cmd/EditUser.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.javarush.khmelov.cmd; - -import com.javarush.khmelov.entity.Role; -import com.javarush.khmelov.entity.User; -import com.javarush.khmelov.service.UserService; -import jakarta.servlet.http.HttpServletRequest; - -import java.util.Optional; - - -@SuppressWarnings("unused") -public class EditUser implements Command { - - private final UserService userService; - - public EditUser(UserService userService) { - this.userService = userService; - } - - - @Override - public String doGet(HttpServletRequest req) { - String stringId = req.getParameter("id"); - if (stringId != null) { - long id = Long.parseLong(stringId); - Optional optionalUser = userService.get(id); - if (optionalUser.isPresent()) { - User user = optionalUser.get(); - req.setAttribute("user", user); - } - } - return getView(); - } - - @Override - public String doPost(HttpServletRequest req) { - User user = User.builder() - .login(req.getParameter("login")) - .password(req.getParameter("password")) - .role(Role.valueOf(req.getParameter("role"))) - .build(); - if (req.getParameter("create") != null) { - userService.create(user); - } else if (req.getParameter("update") != null) { - user.setId(Long.parseLong(req.getParameter("id"))); - userService.update(user); - } - return getView() + "?id=" + user.getId(); - } - - -} \ No newline at end of file diff --git a/src/main/java/com/javarush/khmelov/controller/FrontController.java b/src/main/java/com/javarush/khmelov/controller/FrontController.java deleted file mode 100644 index 33242b2..0000000 --- a/src/main/java/com/javarush/khmelov/controller/FrontController.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.javarush.khmelov.controller; - -import com.javarush.khmelov.cmd.Command; -import com.javarush.khmelov.config.Winter; -import com.javarush.khmelov.entity.Role; -import jakarta.servlet.ServletConfig; -import jakarta.servlet.ServletException; -import jakarta.servlet.annotation.WebServlet; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import java.io.IOException; - -@WebServlet({"", "/home", "/list-user", "/edit-user"}) -public class FrontController extends HttpServlet { - - private final HttpResolver httpResolver = Winter.find(HttpResolver.class); - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - Command command = httpResolver.resolve(req); - String view = command.doGet(req); - String jsp = getJsp(view); - req.getRequestDispatcher(jsp).forward(req, resp); - } - - @Override - public void init(ServletConfig config) { - config.getServletContext().setAttribute("roles", Role.values()); - } - - private static String getJsp(String view) { - return "/WEB-INF/" + view + ".jsp"; - } - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - Command command = httpResolver.resolve(req); - String redirect = command.doPost(req); - resp.sendRedirect(redirect); - } -} diff --git a/src/main/java/com/javarush/khmelov/controller/HttpResolver.java b/src/main/java/com/javarush/khmelov/controller/HttpResolver.java deleted file mode 100644 index 18bb761..0000000 --- a/src/main/java/com/javarush/khmelov/controller/HttpResolver.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.javarush.khmelov.controller; - -import com.javarush.khmelov.cmd.Command; -import com.javarush.khmelov.config.Winter; -import jakarta.servlet.http.HttpServletRequest; - -public class HttpResolver { - - public Command resolve(HttpServletRequest request) { - // /cmd-example - try { - String requestURI = request.getRequestURI(); - requestURI = requestURI.equals("/") ? "/start-page" : requestURI; - String kebabName = requestURI.split("[?#/]")[1]; - String simpleName = convertKebabStyleToCamelCase(kebabName); - String fullName = Command.class.getPackageName() + "." + simpleName; - Class aClass = Class.forName(fullName); - return (Command) Winter.find(aClass); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - - private String convertKebabStyleToCamelCase(String input) { - StringBuilder result = new StringBuilder(); - boolean capitalizeNext = true; - for (char c : input.toCharArray()) { - if (c == '-') { - capitalizeNext = true; - } else { - if (capitalizeNext) { - result.append(Character.toUpperCase(c)); - capitalizeNext = false; - } else { - result.append(Character.toLowerCase(c)); - } - } - } - return result.toString(); - } -} diff --git a/src/main/java/com/javarush/khmelov/entity/User.java b/src/main/java/com/javarush/khmelov/entity/User.java deleted file mode 100644 index f7fa2d6..0000000 --- a/src/main/java/com/javarush/khmelov/entity/User.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.javarush.khmelov.entity; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class User { - - private Long id; - - private String login; - - private String password; - - private Role role; - - public String getImage() { //TODO move to DTO - return "image-" + id; - } - -} diff --git a/src/main/java/com/javarush/khmelov/repository/Repository.java b/src/main/java/com/javarush/khmelov/repository/Repository.java deleted file mode 100644 index f1abdac..0000000 --- a/src/main/java/com/javarush/khmelov/repository/Repository.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.javarush.khmelov.repository; - -import com.javarush.khmelov.entity.User; - -import java.util.Collection; -import java.util.Optional; - -public interface Repository { - - Collection getAll(); - - Optional get(long id); - - void create(T entity); - - void update(T entity); - - void delete(T entity); -} diff --git a/src/main/java/com/javarush/khmelov/repository/UserRepository.java b/src/main/java/com/javarush/khmelov/repository/UserRepository.java deleted file mode 100644 index 58b32ea..0000000 --- a/src/main/java/com/javarush/khmelov/repository/UserRepository.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.javarush.khmelov.repository; - -import com.javarush.khmelov.entity.Role; -import com.javarush.khmelov.entity.User; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicLong; - -public class UserRepository implements Repository { - - private final Map map = new HashMap<>(); - - public static final AtomicLong id = new AtomicLong(System.currentTimeMillis()); - - public UserRepository() { - map.put(1L, new User(1L, "Alisa", "qwerty", Role.USER)); - map.put(2L, new User(2L, "Bob", "", Role.GUEST)); - map.put(3L, new User(3L, "Carl", "admin", Role.ADMIN)); - map.put(4L, new User(4L, "Khmelov", "admin", Role.ADMIN)); - } - - @Override - public Collection getAll() { - return map.values(); - } - - @Override - public Optional get(long id) { - return Optional.ofNullable(map.get(id)); - } - - @Override - public void create(User entity) { - entity.setId(id.incrementAndGet()); - update(entity); - } - - @Override - public void update(User entity) { - map.put(entity.getId(), entity); - } - - @Override - public void delete(User entity) { - map.remove(entity.getId()); - } -} diff --git a/src/main/java/com/javarush/khmelov/service/UserService.java b/src/main/java/com/javarush/khmelov/service/UserService.java deleted file mode 100644 index b17527c..0000000 --- a/src/main/java/com/javarush/khmelov/service/UserService.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.javarush.khmelov.service; - -import com.javarush.khmelov.entity.User; -import com.javarush.khmelov.repository.UserRepository; - -import java.util.Collection; -import java.util.Optional; - -public class UserService { - - private final UserRepository userRepository; - - public UserService(UserRepository userRepository) { - this.userRepository = userRepository; - } - - public void create(User user) { - userRepository.create(user); - } - - public void update(User user) { - userRepository.update(user); - } - - public void delete(User user) { - userRepository.delete(user); - } - - public Collection getAll() { - return userRepository.getAll(); - } - - public Optional get(long id) { - return userRepository.get(id); - } -} diff --git a/src/main/java/com/javarush/popkov/BaseIT.java b/src/main/java/com/javarush/popkov/BaseIT.java new file mode 100644 index 0000000..9dc4ecf --- /dev/null +++ b/src/main/java/com/javarush/popkov/BaseIT.java @@ -0,0 +1,60 @@ +package com.javarush.popkov; + +import com.javarush.popkov.config.Config; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.entity.Role; +import com.javarush.popkov.entity.User; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BaseIT { + protected final HttpServletRequest request; + protected final HttpServletResponse response; + protected final HttpSession session; + protected final Config config; + protected final ServletConfig servletConfig; + protected final ServletContext servletContext; + protected User testAdmin; + protected User testUser; + protected User testGuest; + + protected BaseIT() { + //app config + config = Winter.find(Config.class); + config.fillEmptyRepository(); + //servlet config + servletConfig = mock(ServletConfig.class); + servletContext = mock(ServletContext.class); + when(servletConfig.getServletContext()).thenReturn(servletContext); + //current op + request = mock(HttpServletRequest.class); + response = mock(HttpServletResponse.class); + session = mock(HttpSession.class); + when(request.getSession()).thenReturn(session); + //test data + testAdmin = User.builder() + .id(1L) + .login("testAdmin") + .password("testAdmin") + .role(Role.ADMIN) + .build(); + testUser = User.builder() + .id(2L) + .login("testUser") + .password("testUser") + .role(Role.USER) + .build(); + testGuest = User.builder() + .id(3L) + .login("testGuest") + .password("testGuest") + .role(Role.USER) + .build(); + } +} diff --git a/src/main/java/com/javarush/khmelov/cmd/Command.java b/src/main/java/com/javarush/popkov/cmd/Command.java similarity index 96% rename from src/main/java/com/javarush/khmelov/cmd/Command.java rename to src/main/java/com/javarush/popkov/cmd/Command.java index fd4035b..0de057d 100644 --- a/src/main/java/com/javarush/khmelov/cmd/Command.java +++ b/src/main/java/com/javarush/popkov/cmd/Command.java @@ -1,4 +1,4 @@ -package com.javarush.khmelov.cmd; +package com.javarush.popkov.cmd; import jakarta.servlet.http.HttpServletRequest; diff --git a/src/main/java/com/javarush/popkov/cmd/CreateQuest.java b/src/main/java/com/javarush/popkov/cmd/CreateQuest.java new file mode 100644 index 0000000..9830bea --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/CreateQuest.java @@ -0,0 +1,26 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.entity.User; +import com.javarush.popkov.service.QuestService; +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.Key; +import com.javarush.popkov.util.RequestHelpers; +import jakarta.servlet.http.HttpServletRequest; +import lombok.AllArgsConstructor; + +import java.util.Optional; + +@AllArgsConstructor +public class CreateQuest implements Command { + + private final QuestService questService; + + @Override + public String doPost(HttpServletRequest request) { + String name = request.getParameter(Key.NAME); + String text = request.getParameter(Key.TEXT); + Optional optionalUser = RequestHelpers.getUser(request.getSession()); + optionalUser.ifPresent(user -> questService.create(name, text, user.getId())); + return Go.HOME; + } +} \ No newline at end of file diff --git a/src/main/java/com/javarush/popkov/cmd/CreateQuestIT.java b/src/main/java/com/javarush/popkov/cmd/CreateQuestIT.java new file mode 100644 index 0000000..9ee6fe6 --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/CreateQuestIT.java @@ -0,0 +1,33 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.entity.Role; +import com.javarush.popkov.entity.User; +import com.javarush.popkov.service.QuestService; +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.Key; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +class CreateQuestIT extends BaseIT { + + private final CreateQuest createQuest = Winter.find(CreateQuest.class); + private final QuestService questService = Winter.find(QuestService.class); + + + @Test + void whenCreateQuest_thenQuestsCountIncreaseByOne() { + User admin = User.builder().id(1L).role(Role.ADMIN).build(); + when(session.getAttribute(Key.USER)).thenReturn(admin); + when(request.getParameter(Key.NAME)).thenReturn("TestQuest"); + when(request.getParameter(Key.TEXT)).thenReturn("1: Test OK?\n2< Да\n3< Нет\n2+ win\n3- lost\n"); + + int count = questService.getAll().size(); + assertEquals(Go.HOME, createQuest.doPost(request)); + assertEquals(count + 1, questService.getAll().size()); + } + +} \ No newline at end of file diff --git a/src/main/java/com/javarush/popkov/cmd/EditUser.java b/src/main/java/com/javarush/popkov/cmd/EditUser.java new file mode 100644 index 0000000..ee48cdd --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/EditUser.java @@ -0,0 +1,71 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.entity.Gender; +import com.javarush.popkov.entity.Role; +import com.javarush.popkov.entity.User; +import com.javarush.popkov.service.ImageService; +import com.javarush.popkov.service.UserService; +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.Key; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.Part; +import lombok.SneakyThrows; + +@SuppressWarnings("unused") +public class EditUser implements Command { + + private final UserService userService; + private final ImageService imageService; + + public EditUser(UserService userService, ImageService imageService) { + this.userService = userService; + this.imageService = imageService; + } + + + public String doGet(HttpServletRequest req) { + String stringId = req.getParameter(Key.ID); + if (stringId != null) { + long id = Long.parseLong(stringId); + userService.get(id) + .ifPresent(user -> req.setAttribute(Key.USER, user)); + } + return getView(); + } + + @Override + @SneakyThrows + public String doPost(HttpServletRequest req) { + long id = Long.parseLong(req.getParameter(Key.ID)); + User existingUser = userService.get(id).orElse(null); + String roleParam = req.getParameter("role"); + String genderParam = req.getParameter("gender"); + Role role = roleParam != null + ? Role.valueOf(roleParam) + : existingUser != null ? existingUser.getRole() : Role.USER; + Gender gender = genderParam != null + ? Gender.valueOf(genderParam) + : existingUser != null ? existingUser.getGender() : Gender.MALE; + User user = User.builder() + .login(req.getParameter("login")) + .password(req.getParameter("password")) + .role(role) + .gender(gender) + .build(); + user.setId(id); + boolean isCreate = req.getParameter("create") != null; + if (isCreate) { + userService.create(user); + } + String imageId = "image-" + user.getId(); + Part imagePart = req.getPart("image"); + if (imagePart != null && imagePart.getInputStream().available() > 0) { + imageService.uploadImage(req, imageId); + user.setImageId(imageId); + } + userService.update(user); + return Go.EDIT_USER + "?id=" + user.getId(); + } + + +} \ No newline at end of file diff --git a/src/main/java/com/javarush/popkov/cmd/EditUserIT.java b/src/main/java/com/javarush/popkov/cmd/EditUserIT.java new file mode 100644 index 0000000..c88a256 --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/EditUserIT.java @@ -0,0 +1,40 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.entity.Role; +import com.javarush.popkov.entity.User; +import com.javarush.popkov.service.UserService; +import com.javarush.popkov.util.Key; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.verify; + +class EditUserIT extends BaseIT { + + private final EditUser editUser = Winter.find(EditUser.class); + private final UserService userService = Winter.find(UserService.class); + + @Test + void whenOpenPage_thenCommandReturnJspPage() { + User user = userService.getAll().stream().findFirst().orElseThrow(); + Mockito.when(request.getParameter(Key.ID)).thenReturn(user.getId().toString()); + String view = editUser.doGet(request); + assertEquals("edit-user", view); + verify(request).setAttribute(eq(Key.USER), eq(user)); + } + + @Test + void whenUpdateUser_thenGetPageByUserId() throws Exception { + Mockito.when(request.getParameter(Key.LOGIN)).thenReturn("TestName"); + Mockito.when(request.getParameter(Key.PASSWORD)).thenReturn("TestPassword"); + Mockito.when(request.getParameter(Key.ROLE)).thenReturn(Role.GUEST.toString()); + Mockito.when(request.getParameter(Key.ID)).thenReturn("1"); + String page = editUser.doPost(request); + assertTrue(page.endsWith("?id=1")); + } +} diff --git a/src/main/java/com/javarush/popkov/cmd/Home.java b/src/main/java/com/javarush/popkov/cmd/Home.java new file mode 100644 index 0000000..74aa12b --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/Home.java @@ -0,0 +1,22 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.service.QuestService; +import jakarta.servlet.http.HttpServletRequest; +import lombok.AllArgsConstructor; + +import static com.javarush.popkov.util.Key.QUESTS; + +@SuppressWarnings("unused") +@AllArgsConstructor +public class Home implements Command { + + private final QuestService questService; + + @Override + public String doGet(HttpServletRequest req) { + req.setAttribute(QUESTS, questService.getAll()); + return getView(); + } +} + + diff --git a/src/main/java/com/javarush/popkov/cmd/HomeIT.java b/src/main/java/com/javarush/popkov/cmd/HomeIT.java new file mode 100644 index 0000000..fdf3c3f --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/HomeIT.java @@ -0,0 +1,23 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.util.Key; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Collection; + +import static org.mockito.Mockito.*; + +class HomeIT extends BaseIT { + + + @Test + void whenOpenPage_thenCommandReturnJspPage() { + Home home = Winter.find(Home.class); + String view = home.doGet(request); + Assertions.assertEquals("home", view); + verify(request).setAttribute(eq(Key.QUESTS), any(Collection.class)); + } +} diff --git a/src/main/java/com/javarush/khmelov/cmd/ListUser.java b/src/main/java/com/javarush/popkov/cmd/ListUser.java similarity index 64% rename from src/main/java/com/javarush/khmelov/cmd/ListUser.java rename to src/main/java/com/javarush/popkov/cmd/ListUser.java index 9257917..b3c55d3 100644 --- a/src/main/java/com/javarush/khmelov/cmd/ListUser.java +++ b/src/main/java/com/javarush/popkov/cmd/ListUser.java @@ -1,20 +1,18 @@ -package com.javarush.khmelov.cmd; +package com.javarush.popkov.cmd; -import com.javarush.khmelov.entity.User; -import com.javarush.khmelov.service.UserService; +import com.javarush.popkov.entity.User; +import com.javarush.popkov.service.UserService; import jakarta.servlet.http.HttpServletRequest; +import lombok.AllArgsConstructor; import java.util.Collection; @SuppressWarnings("unused") +@AllArgsConstructor public class ListUser implements Command { private final UserService userService; - public ListUser(UserService userService) { - this.userService = userService; - } - @Override public String doGet(HttpServletRequest request) { Collection users = userService.getAll(); diff --git a/src/main/java/com/javarush/popkov/cmd/ListUserIT.java b/src/main/java/com/javarush/popkov/cmd/ListUserIT.java new file mode 100644 index 0000000..a316552 --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/ListUserIT.java @@ -0,0 +1,24 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.util.Key; +import org.junit.jupiter.api.Test; + +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +class ListUserIT extends BaseIT { + + ListUser listUser = Winter.find(ListUser.class); + + @Test + void whenGetListUsers_thenReturnJspPage() { + String jspPage = listUser.doGet(request); + + assertEquals("list-user", jspPage); + verify(request).setAttribute(eq(Key.USERS), any(Collection.class)); + } +} \ No newline at end of file diff --git a/src/main/java/com/javarush/popkov/cmd/Login.java b/src/main/java/com/javarush/popkov/cmd/Login.java new file mode 100644 index 0000000..f4297fd --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/Login.java @@ -0,0 +1,33 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.entity.User; +import com.javarush.popkov.service.UserService; +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.Key; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import lombok.AllArgsConstructor; + +import java.util.Optional; + +@SuppressWarnings("unused") +@AllArgsConstructor +public class Login implements Command { + + private final UserService userService; + + @Override + public String doPost(HttpServletRequest request) { + String login = request.getParameter(Key.LOGIN); + String password = request.getParameter(Key.PASSWORD); + Optional user = userService.get(login, password); + if (user.isPresent()) { + HttpSession session = request.getSession(); + session.setAttribute(Key.USER, user.get()); + return Go.PROFILE; + } else { + return Go.LOGIN; //todo add error message + } + } +} + diff --git a/src/main/java/com/javarush/popkov/cmd/LoginIT.java b/src/main/java/com/javarush/popkov/cmd/LoginIT.java new file mode 100644 index 0000000..6a4cac3 --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/LoginIT.java @@ -0,0 +1,42 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.entity.User; +import com.javarush.popkov.service.UserService; +import com.javarush.popkov.util.Key; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.mockito.Mockito.*; + +class LoginIT extends BaseIT { + + final Login login = Winter.find(Login.class); + final UserService userService = Winter.find(UserService.class); + + @Test + @DisplayName("When login admin then redirect to profile") + void whenLoginAdminThenRedirectToProfile() { + User user = userService.getAll().stream().findFirst().orElseThrow(); + when(request.getParameter("login")).thenReturn(user.getLogin()); + when(request.getParameter("password")).thenReturn(user.getPassword()); + + String actualRedirect = login.doPost(request); + Assertions.assertEquals("/profile", actualRedirect); + + verify(session) + .setAttribute(eq(Key.USER), any(User.class)); + } + + @Test + @DisplayName("When incorrect login then redirect to login") + void whenIncorrectLoginThenRedirectToLogin() { + when(request.getParameter("login")).thenReturn("Carl"); + when(request.getParameter("password")).thenReturn("err"); + + String actualRedirect = login.doPost(request); + Assertions.assertEquals("/login", actualRedirect); + } +} diff --git a/src/main/java/com/javarush/popkov/cmd/Logout.java b/src/main/java/com/javarush/popkov/cmd/Logout.java new file mode 100644 index 0000000..884ccfc --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/Logout.java @@ -0,0 +1,15 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.util.Go; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; + +@SuppressWarnings("unused") +public class Logout implements Command { + @Override + public String doGet(HttpServletRequest request) { + HttpSession session = request.getSession(); + session.invalidate(); + return Go.LOGIN; + } +} diff --git a/src/main/java/com/javarush/popkov/cmd/LogoutIT.java b/src/main/java/com/javarush/popkov/cmd/LogoutIT.java new file mode 100644 index 0000000..c15395a --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/LogoutIT.java @@ -0,0 +1,18 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class LogoutIT extends BaseIT { + + private final Logout logout = Winter.find(Logout.class); + + @Test + void whenOpenPage_thenInvalidateSession() { + logout.doGet(request); + Mockito.verify(session).invalidate(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/javarush/popkov/cmd/PlayGame.java b/src/main/java/com/javarush/popkov/cmd/PlayGame.java new file mode 100644 index 0000000..b6b4ede --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/PlayGame.java @@ -0,0 +1,79 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.entity.Game; +import com.javarush.popkov.entity.Question; +import com.javarush.popkov.entity.User; +import com.javarush.popkov.service.GameService; +import com.javarush.popkov.service.QuestionService; +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.Key; +import com.javarush.popkov.util.RequestHelpers; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; + +import java.util.Optional; + +@SuppressWarnings("unused") +@Slf4j +public class PlayGame implements Command { + + private final GameService gameService; + private final QuestionService questionService; + + public PlayGame(GameService gameService, QuestionService questionService) { + this.gameService = gameService; + this.questionService = questionService; + } + + @Override + public String doGet(HttpServletRequest request) { + Long questId = Long.parseLong(request.getParameter(Key.QUEST_ID)); + Optional user = RequestHelpers.getUser(request.getSession()); + if (user.isPresent()) { + Long userId = user.get().getId(); + Optional game = gameService.getGame(questId, userId); + if (game.isPresent()) { + showOneQuestion(request, game.get()); + return getView(); + } else { + String message = "Нет незавершенной игры"; + log.warn(message); + RequestHelpers.createError(request, message); + return Go.HOME; + } + } else { + String message = "Сначала нужно войти в аккаунт"; + log.warn(message); + RequestHelpers.createError(request, message); + return Go.LOGIN; + } + } + + @Override + public String doPost(HttpServletRequest request) { + Long gameId = RequestHelpers.getId(request); + Long answerId = RequestHelpers.getId(request, Key.ANSWER); + Optional game = gameService.processOneStep(gameId, answerId); + if (game.isPresent()) { + if (answerId == 0 && request.getParameter(Key.GAME) != null) { + String message = "Нужно выбрать какой-то ответ"; + log.warn(message); + RequestHelpers.createError(request, message); + } + Game currentGame = game.get(); + return "%s?questId=%d&id=%d".formatted(Go.PLAY_GAME, game.get().getQuestId(), game.get().getId()); + } else { + String message = "Нет такой игры"; + log.warn(message); + RequestHelpers.createError(request, message); + return Go.HOME; + } + } + + private void showOneQuestion(HttpServletRequest request, Game game) { + request.setAttribute(Key.GAME, game); + Optional question = questionService.get(game.getCurrentQuestionId()); + request.setAttribute(Key.QUESTION, question.orElseThrow()); + } + +} diff --git a/src/main/java/com/javarush/popkov/cmd/PlayGameIT.java b/src/main/java/com/javarush/popkov/cmd/PlayGameIT.java new file mode 100644 index 0000000..e115efb --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/PlayGameIT.java @@ -0,0 +1,28 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.entity.Game; +import com.javarush.popkov.entity.Question; +import com.javarush.popkov.util.Key; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +class PlayGameIT extends BaseIT { + + private final PlayGame playGame = Winter.find(PlayGame.class); + + @Test + void whenStartGame_thenSetGameAndQuestionInRequest() { + when(session.getAttribute(Key.USER)).thenReturn(testUser); + when(request.getParameter(Key.QUEST_ID)).thenReturn("1"); + String jspPage = playGame.doGet(request); + + assertEquals("play-game", jspPage); + verify(request).setAttribute(eq(Key.GAME), any(Game.class)); + verify(request).setAttribute(eq(Key.QUESTION), any(Question.class)); + } + +} \ No newline at end of file diff --git a/src/main/java/com/javarush/popkov/cmd/Profile.java b/src/main/java/com/javarush/popkov/cmd/Profile.java new file mode 100644 index 0000000..e56f496 --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/Profile.java @@ -0,0 +1,21 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.entity.User; +import com.javarush.popkov.util.Go; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; + +@SuppressWarnings("unused") +public class Profile implements Command { + + @Override + public String doPost(HttpServletRequest request) { + if (request.getParameter("logout") == null) { + HttpSession session = request.getSession(); + User user = (User) session.getAttribute("user"); + return Go.EDIT_USER + "?id=" + user.getId(); + } else { + return Go.LOGOUT; + } + } +} diff --git a/src/main/java/com/javarush/popkov/cmd/ProfileIT.java b/src/main/java/com/javarush/popkov/cmd/ProfileIT.java new file mode 100644 index 0000000..e385760 --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/ProfileIT.java @@ -0,0 +1,28 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.Key; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class ProfileIT extends BaseIT { + + private final Profile profile = Winter.find(Profile.class); + + @Test + void whenClickEditInProfile_thenGoToEditUserPage() { + Mockito.when(session.getAttribute(Key.USER)).thenReturn(testUser); + String uri = profile.doPost(request); + Assertions.assertEquals(Go.EDIT_USER + "?id=" + testUser.getId(), uri); + } + + @Test + void whenClickLogout_thenGoLogout() { + Mockito.when(request.getParameter(Key.LOGOUT)).thenReturn("true"); + String uri = profile.doPost(request); + Assertions.assertEquals(Go.LOGOUT, uri); + } +} diff --git a/src/main/java/com/javarush/popkov/cmd/Quest.java b/src/main/java/com/javarush/popkov/cmd/Quest.java new file mode 100644 index 0000000..d94c1fe --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/Quest.java @@ -0,0 +1,55 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.entity.Question; +import com.javarush.popkov.entity.Role; +import com.javarush.popkov.entity.User; +import com.javarush.popkov.service.ImageService; +import com.javarush.popkov.service.QuestService; +import com.javarush.popkov.service.QuestionService; +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.Key; +import com.javarush.popkov.util.RequestHelpers; +import jakarta.servlet.http.HttpServletRequest; +import lombok.AllArgsConstructor; +import lombok.SneakyThrows; + +import java.util.Optional; + +import static com.javarush.popkov.util.Key.QUEST; + + +@AllArgsConstructor +public class Quest implements Command { + + private final QuestService questService; + private final QuestionService questionService; + private final ImageService imageService; + + + @Override + public String doGet(HttpServletRequest req) { + long id = RequestHelpers.getId(req); + Optional quest = questService.get(id); + req.setAttribute(QUEST, quest.orElseThrow()); + return getView(); + } + + @Override + @SneakyThrows + public String doPost(HttpServletRequest req) { + Optional editor = RequestHelpers.getUser(req.getSession()); + if (editor.isPresent() && editor.get().getRole() == Role.ADMIN) { + Long id = RequestHelpers.getId(req); + Long questionId = RequestHelpers.getId(req, "questionId"); + String text = req.getParameter(Key.TEXT); + Optional question = questionService.update(questionId, text); + if (question.isPresent()) { + imageService.uploadImage(req, question.get().getImage()); + } + return "%s?id=%d#bookmark%d".formatted(Go.QUEST, id, questionId); + } else { + return Go.QUEST; //TODO добавить ошибку, что "Недостаточно прав для редактирования"; + } + } +} + diff --git a/src/main/java/com/javarush/popkov/cmd/QuestIT.java b/src/main/java/com/javarush/popkov/cmd/QuestIT.java new file mode 100644 index 0000000..a08d2f6 --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/QuestIT.java @@ -0,0 +1,48 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.Key; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class QuestIT extends BaseIT { + + private final Quest quest = Winter.find(Quest.class); + + @Test + void whenOpenQuestPageWithCorrectId_thenGetJsp() { + Mockito.when(request.getParameter(Key.ID)).thenReturn("1"); + String jsp = quest.doGet(request); + Assertions.assertEquals("quest", jsp); + } + + @Test + void whenAnonymousPostQuest_thenRedirectBackward() { + Mockito.when(request.getParameter(Key.ID)).thenReturn("1"); + String uri = quest.doPost(request); + Assertions.assertEquals(Go.QUEST, uri); + } + + @Test + void whenNonAdminPostQuest_thenRedirectBackward() { + Mockito.when(session.getAttribute(Key.USER)).thenReturn(testGuest); + Mockito.when(request.getParameter(Key.ID)).thenReturn("1"); + String uri = quest.doPost(request); + Assertions.assertEquals(Go.QUEST, uri); + } + + @Test + void whenAdminPostQuest_thenRedirectBackward() { + Mockito.when(session.getAttribute(Key.USER)).thenReturn(testAdmin); + Mockito.when(request.getParameter(Key.ID)).thenReturn("1"); + Mockito.when(request.getParameter(Key.QUEST_ID)).thenReturn("1"); + Mockito.when(request.getParameter(Key.QUESTION_ID)).thenReturn("1"); + Mockito.when(request.getParameter(Key.TEXT)).thenReturn("newTestTextQuestion"); + String actualUri = quest.doPost(request); + String expectedUri = "%s?id=%d#bookmark%d".formatted(Go.QUEST, 1, 1); + Assertions.assertEquals(expectedUri, actualUri); + } +} diff --git a/src/main/java/com/javarush/popkov/cmd/Signup.java b/src/main/java/com/javarush/popkov/cmd/Signup.java new file mode 100644 index 0000000..d327c88 --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/Signup.java @@ -0,0 +1,46 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.entity.Gender; +import com.javarush.popkov.entity.Role; +import com.javarush.popkov.entity.User; +import com.javarush.popkov.service.ImageService; +import com.javarush.popkov.service.UserService; +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.Key; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import jakarta.servlet.http.Part; +import lombok.AllArgsConstructor; +import lombok.SneakyThrows; + +@SuppressWarnings("unused") +@AllArgsConstructor +public class Signup implements Command { + + private final UserService userService; + private final ImageService imageService; + + @Override + @SneakyThrows + public String doPost(HttpServletRequest request) { + String genderParam = request.getParameter("gender"); + Gender gender = genderParam == null ? Gender.MALE : Gender.valueOf(genderParam); + User user = User.builder() + .login(request.getParameter(Key.LOGIN)) + .password(request.getParameter(Key.PASSWORD)) + .role(Role.USER) + .gender(gender) + .build(); + userService.create(user); + String imageId = "image-" + user.getId(); + Part imagePart = request.getPart("image"); + if (imagePart != null && imagePart.getInputStream().available() > 0) { + imageService.uploadImage(request, imageId); + user.setImageId(imageId); + userService.update(user); + } + HttpSession session = request.getSession(); + session.setAttribute(Key.USER, user); + return Go.PROFILE; + } +} diff --git a/src/main/java/com/javarush/popkov/cmd/SignupIT.java b/src/main/java/com/javarush/popkov/cmd/SignupIT.java new file mode 100644 index 0000000..d65a3ca --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/SignupIT.java @@ -0,0 +1,30 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.repository.UserRepository; +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.Key; +import jakarta.servlet.ServletException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.io.IOException; + +class SignupIT extends BaseIT { + + private final Signup signup = Winter.find(Signup.class); + private final UserRepository repository = Winter.find(UserRepository.class); + + @Test + void doPost() { + Mockito.when(request.getParameter(Key.LOGIN)).thenReturn("newTestLogin"); + Mockito.when(request.getParameter(Key.PASSWORD)).thenReturn("newTestPassword"); + Mockito.when(request.getParameter(Key.ROLE)).thenReturn("GUEST"); + + String uri = signup.doPost(request); + Assertions.assertEquals(Go.PROFILE, uri); + Assertions.assertTrue(repository.getAll().toString().contains("newTestLogin")); + } +} diff --git a/src/main/java/com/javarush/khmelov/cmd/StartPage.java b/src/main/java/com/javarush/popkov/cmd/StartPage.java similarity index 69% rename from src/main/java/com/javarush/khmelov/cmd/StartPage.java rename to src/main/java/com/javarush/popkov/cmd/StartPage.java index d268f93..51d975c 100644 --- a/src/main/java/com/javarush/khmelov/cmd/StartPage.java +++ b/src/main/java/com/javarush/popkov/cmd/StartPage.java @@ -1,4 +1,4 @@ -package com.javarush.khmelov.cmd; +package com.javarush.popkov.cmd; @SuppressWarnings("unused") public class StartPage implements Command { diff --git a/src/main/java/com/javarush/popkov/cmd/Statistics.java b/src/main/java/com/javarush/popkov/cmd/Statistics.java new file mode 100644 index 0000000..f444548 --- /dev/null +++ b/src/main/java/com/javarush/popkov/cmd/Statistics.java @@ -0,0 +1,24 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.service.StatService; +import jakarta.servlet.http.HttpServletRequest; + +import static com.javarush.popkov.util.Key.LIST_USER_STATISTICS; +import static com.javarush.popkov.util.Key.TOTAL_USER_STATISTICS; + +@SuppressWarnings("unused") +public class Statistics implements Command { + + private final StatService statService; + + public Statistics(StatService statService) { + this.statService = statService; + } + + @Override + public String doGet(HttpServletRequest req) { + req.setAttribute(LIST_USER_STATISTICS, statService.getUserStatistics()); + req.setAttribute(TOTAL_USER_STATISTICS, statService.getTotalUserStatistics()); + return getView(); + } +} \ No newline at end of file diff --git a/src/main/java/com/javarush/popkov/config/Config.java b/src/main/java/com/javarush/popkov/config/Config.java new file mode 100644 index 0000000..836d0ca --- /dev/null +++ b/src/main/java/com/javarush/popkov/config/Config.java @@ -0,0 +1,108 @@ +package com.javarush.popkov.config; + +import com.javarush.popkov.entity.Gender; +import com.javarush.popkov.entity.Role; +import com.javarush.popkov.entity.User; +import com.javarush.popkov.service.QuestService; +import com.javarush.popkov.service.UserService; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class Config { + private final UserService userService; + private final QuestService questService; + + public void fillEmptyRepository() { + if (userService.get(1L).isEmpty()) { + User admin = biuldUser("Jack", "admin", Role.ADMIN, Gender.MALE); + userService.create(admin); + User user = biuldUser("Sofia", "qwerty", Role.USER, Gender.FEMALE); + userService.create(user); + User guest = biuldUser("John", "123", Role.GUEST, Gender.MALE); + userService.create(guest); + addDemoQuests(admin); + } + } + + private void addDemoQuests (User author){ + Long authorId = author.getId(); + questService.create( + "Играем в неопознанный летающий объект (обязательный квест)", + """ + 1: Ты потерял память. Принять вызов НЛО? + 2< Принять вызов + 91< Отклонить вызов + + 2: Ты принял вызов. Подняться на мостик к капитану? + 92< Отказаться подниматься на мостик + 3< Подняться на мостик + + 3: Ты поднялся на мостик. Ты кто? + 93< Солгать о себе + 99< Рассказать правду + + 91- Ты отклонил вызов. Поражение. + 92- Ты не пошел на переговоры. Поражение. + 93- Твою ложь разоблачили. Поражение. + + 99+ Вы выиграли + """, + authorId + ); + + + questService.create( + "Сказочная сказка о приключениях богатыря-разработчика!", + """ + 1: Ты перед волшебным камнем, на нем есть надписи.
Что выберешь? + 2< налево пойдешь - в сказку попадешь + 3< направо пойдешь - свою смерть найдешь + 4< прямо пойдешь - в разработчики попадешь + + 2: Ты видишь закрытый сундук, ну-с...
И что будем делать? + 1< Кто ж его знает, что там, вернусь-ка я обратно... + 7< Я ничего не боюсь, я его открою... + + 3: Ты видишь какую-то пещеру, что будешь делать? + 1< Та ну её нафиг. Я боюсь. Вернусь-ка я за подмогой. + 8< Держите меня семеро! Я достаю меч и быстро захожу в пещеру... + + 4: Ты видишь Кикимору Болотную, которая что-то увлеченно печатает на ноутбуке. + 1< Кикимора? Ноутбук? Какие-то опасные грибы я съел... Бегом отсюда... + 5< Скажу громко "Эй ты, а ну отошла от ноута, ща я тебе покажу класс..." + 6< Похоже без ноута никак разработчиком не стать. Бью Кикимору дубиной по башке, и теперь у меня большое будущее. + + 5: Кикимора прыгнула в болото, ноут утонул вместе с ней, но зато справа я вижу какой-то тоннель + 3< Ок, пойду-ка я туда, делать-то нечего. + + 6: Настоящий поступок, настоящего программиста.
Теперь осталось только Java доучить, и все, дело в шляпе. + 9< Стоп, мне же еще нужна литература... Поищу-ка я ее, вон и тропинка видна какая-то с табличкой "в библиотеку" + 10< Да, точно. Все, я быстро-быстро иду все повторять, учить, и кодить-кодить-кодить. Игры - это зло. + + 7: Ого. Вот она, большая куча золота. + 11< Ура. Набиваем карманы.... + 2< Что-то тут не то, закрою-ка я этот сундук.. + + 8: Ты заходишь в пещеру, а там Кащей, с Бабой Ягой, и Змеем Горынычем смотрят ролики на Youtube.
Что будешь делать? + 12< Ясно что! В Бой!!! + + 9: Какая-то странная дорога, она ведет назад или мне это кажется? + 1< Делать нечего, иду дальше.... + + 10+ Ура! Это победа!
Но мне пора действительно кодить, хватит уже ерундой заниматься. + 11- И тут прибежали злые печенеги и убили тебя.
Жадность до добра не доведет.
Это поражение! + 12- Тут и сказочке конец.
Ужасная смерть в неравном бою.
Это поражение! + """, + authorId + ); + } + + private static User biuldUser(String name, String password, Role role, Gender gender) { + return User.builder() + .login(name) + .password(password) + .role(role) + .gender(gender) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/javarush/khmelov/config/Winter.java b/src/main/java/com/javarush/popkov/config/Winter.java similarity index 96% rename from src/main/java/com/javarush/khmelov/config/Winter.java rename to src/main/java/com/javarush/popkov/config/Winter.java index 48bd8a7..b7c0bfd 100644 --- a/src/main/java/com/javarush/khmelov/config/Winter.java +++ b/src/main/java/com/javarush/popkov/config/Winter.java @@ -1,4 +1,4 @@ -package com.javarush.khmelov.config; +package com.javarush.popkov.config; import lombok.SneakyThrows; diff --git a/src/main/java/com/javarush/popkov/controller/FrontController.java b/src/main/java/com/javarush/popkov/controller/FrontController.java new file mode 100644 index 0000000..4dbdc16 --- /dev/null +++ b/src/main/java/com/javarush/popkov/controller/FrontController.java @@ -0,0 +1,80 @@ +package com.javarush.popkov.controller; + +import com.javarush.popkov.cmd.Command; +import com.javarush.popkov.config.Config; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.entity.Gender; +import com.javarush.popkov.entity.Role; +import com.javarush.popkov.exception.AppException; +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.RequestHelpers; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.MultipartConfig; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; + +@Slf4j +@MultipartConfig(fileSizeThreshold = 1 << 20) +@WebServlet({ + Go.INDEX, Go.HOME, + Go.SIGNUP, Go.LOGIN, Go.LOGOUT, + Go.LIST_USER, Go.PROFILE, Go.EDIT_USER, + Go.CREATE_QUEST, Go.QUEST, + Go.PLAY_GAME, + Go.STATISTICS +}) +public class FrontController extends HttpServlet { + + private final HttpResolver httpResolver = Winter.find(HttpResolver.class); + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String uri = req.getRequestURI(); + String ctx = req.getContextPath(); + String path = uri.startsWith(ctx) ? uri.substring(ctx.length()) : uri; + if (path.startsWith("/assets/") || path.startsWith("/images/")) { + req.getServletContext().getNamedDispatcher("default").forward(req, resp); + return; + } + Command command = httpResolver.resolve(req); + String view = command.doGet(req); + String jsp = getJsp(view); + req.getRequestDispatcher(jsp).forward(req, resp); + } + + @Override + public void init(ServletConfig servletConfig) { + Config config = Winter.find(Config.class); + config.fillEmptyRepository(); + + ServletContext servletContext = servletConfig.getServletContext(); + servletContext.setAttribute("roles", Role.values()); + servletContext.setAttribute("genders", Gender.values()); + } + + private static String getJsp(String view) { + return "/WEB-INF/" + view + ".jsp"; + } + + + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { + try { + Command command = httpResolver.resolve(req); + String redirect = command.doPost(req); + resp.sendRedirect(req.getContextPath() + redirect); + } catch (AppException e) { + log.warn("Error: {}", e.getMessage()); + RequestHelpers.createError(req, e.getMessage()); + resp.sendRedirect(req.getRequestURI()); + } + } +} diff --git a/src/main/java/com/javarush/popkov/controller/FrontControllerIT.java b/src/main/java/com/javarush/popkov/controller/FrontControllerIT.java new file mode 100644 index 0000000..b312e84 --- /dev/null +++ b/src/main/java/com/javarush/popkov/controller/FrontControllerIT.java @@ -0,0 +1,24 @@ +package com.javarush.popkov.controller; + +import com.javarush.popkov.BaseIT; +import jakarta.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.Test; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class FrontControllerIT extends BaseIT { + + @Test + void whenFrontSendIncorrectMethod_thenReturn501() throws Exception { + FrontController frontController = new FrontController(); + when(request.getMethod()).thenReturn("IncorrectMethod"); + frontController.init(servletConfig); + when(request.getRequestURI()).thenReturn("/"); + frontController.service(request, response); + verify(response).sendError(eq(HttpServletResponse.SC_NOT_IMPLEMENTED), anyString()); + } + +} \ No newline at end of file diff --git a/src/main/java/com/javarush/popkov/controller/HttpResolver.java b/src/main/java/com/javarush/popkov/controller/HttpResolver.java new file mode 100644 index 0000000..b49952f --- /dev/null +++ b/src/main/java/com/javarush/popkov/controller/HttpResolver.java @@ -0,0 +1,55 @@ +package com.javarush.popkov.controller; + +import com.javarush.popkov.cmd.Command; +import com.javarush.popkov.cmd.StartPage; +import com.javarush.popkov.config.Winter; +import jakarta.servlet.http.HttpServletRequest; + +public class HttpResolver { + + public Command resolve(HttpServletRequest request) { + String requestURI = request.getRequestURI(); + requestURI = requestURI.equals("/") ? "/start-page" : requestURI; + + String[] parts = requestURI.split("/"); + String lastSegment = null; + for (int i = parts.length - 1; i >= 0; i--) { + if (!parts[i].isEmpty()) { + lastSegment = parts[i]; + break; + } + } + + if (lastSegment == null) { + lastSegment = "start-page"; + } + + String simpleName = convertToCamelCase(lastSegment); + String fullName = Command.class.getPackageName() + "." + simpleName; + + try { + Class clazz = Class.forName(fullName); + return (Command) Winter.find(clazz); + } catch (ClassNotFoundException e) { + return (Command) Winter.find(StartPage.class); + } + } + + private String convertToCamelCase(String input) { + StringBuilder result = new StringBuilder(); + boolean capitalizeNext = true; + for (char c : input.toCharArray()) { + if (c == '-' || c == '_') { + capitalizeNext = true; + } else { + if (capitalizeNext) { + result.append(Character.toUpperCase(c)); + capitalizeNext = false; + } else { + result.append(Character.toLowerCase(c)); + } + } + } + return result.toString(); + } +} diff --git a/src/main/java/com/javarush/popkov/controller/ImageController.java b/src/main/java/com/javarush/popkov/controller/ImageController.java new file mode 100644 index 0000000..710c42d --- /dev/null +++ b/src/main/java/com/javarush/popkov/controller/ImageController.java @@ -0,0 +1,29 @@ +package com.javarush.popkov.controller; + +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.service.ImageService; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.SneakyThrows; + +import java.nio.file.Files; +import java.nio.file.Path; + +@WebServlet("/user-images/*") +public class ImageController extends HttpServlet { + + + private final ImageService imageService = Winter.find(ImageService.class); + + @Override + @SneakyThrows + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + String requestURI = req.getRequestURI(); + String target = req.getContextPath() + "/user-images/"; + String nameImage = requestURI.replace(target, ""); + Path path = imageService.getImagePath(nameImage); + Files.copy(path, resp.getOutputStream()); + } +} \ No newline at end of file diff --git a/src/main/java/com/javarush/popkov/controller/QuestImageController.java b/src/main/java/com/javarush/popkov/controller/QuestImageController.java new file mode 100644 index 0000000..faa58ce --- /dev/null +++ b/src/main/java/com/javarush/popkov/controller/QuestImageController.java @@ -0,0 +1,28 @@ +package com.javarush.popkov.controller; + +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.service.ImageService; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.SneakyThrows; + +import java.nio.file.Files; +import java.nio.file.Path; + +@WebServlet("/quest-images/*") +public class QuestImageController extends HttpServlet { + + private final ImageService imageService = Winter.find(ImageService.class); + + @Override + @SneakyThrows + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + String requestURI = req.getRequestURI(); + String target = req.getContextPath() + "/quest-images/"; + String nameImage = requestURI.replace(target, ""); + Path path = imageService.getImagePath(nameImage); + Files.copy(path, resp.getOutputStream()); + } +} diff --git a/src/main/java/com/javarush/popkov/entity/AbstractEntity.java b/src/main/java/com/javarush/popkov/entity/AbstractEntity.java new file mode 100644 index 0000000..26b1ee4 --- /dev/null +++ b/src/main/java/com/javarush/popkov/entity/AbstractEntity.java @@ -0,0 +1,6 @@ +package com.javarush.popkov.entity; + +public interface AbstractEntity { + Long getId(); + void setId(Long id); +} \ No newline at end of file diff --git a/src/main/java/com/javarush/popkov/entity/Answer.java b/src/main/java/com/javarush/popkov/entity/Answer.java new file mode 100644 index 0000000..55f43ba --- /dev/null +++ b/src/main/java/com/javarush/popkov/entity/Answer.java @@ -0,0 +1,17 @@ +package com.javarush.popkov.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class Answer implements AbstractEntity { + private Long id; + private Long questionId; + private String text; + private Long nextQuestionId; +} diff --git a/src/main/java/com/javarush/popkov/entity/Game.java b/src/main/java/com/javarush/popkov/entity/Game.java new file mode 100644 index 0000000..b6dfcef --- /dev/null +++ b/src/main/java/com/javarush/popkov/entity/Game.java @@ -0,0 +1,18 @@ +package com.javarush.popkov.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class Game implements AbstractEntity { + private Long id; + private Long questId; + private Long userId; + private Long currentQuestionId; + private GameState gameState; +} diff --git a/src/main/java/com/javarush/popkov/entity/GameState.java b/src/main/java/com/javarush/popkov/entity/GameState.java new file mode 100644 index 0000000..0e494d9 --- /dev/null +++ b/src/main/java/com/javarush/popkov/entity/GameState.java @@ -0,0 +1,5 @@ +package com.javarush.popkov.entity; + +public enum GameState { + PLAY, WIN, LOST +} diff --git a/src/main/java/com/javarush/popkov/entity/Gender.java b/src/main/java/com/javarush/popkov/entity/Gender.java new file mode 100644 index 0000000..6614028 --- /dev/null +++ b/src/main/java/com/javarush/popkov/entity/Gender.java @@ -0,0 +1,6 @@ +package com.javarush.popkov.entity; + +public enum Gender { + MALE, + FEMALE +} diff --git a/src/main/java/com/javarush/popkov/entity/Quest.java b/src/main/java/com/javarush/popkov/entity/Quest.java new file mode 100644 index 0000000..ba938d1 --- /dev/null +++ b/src/main/java/com/javarush/popkov/entity/Quest.java @@ -0,0 +1,22 @@ +package com.javarush.popkov.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.Collection; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class Quest implements AbstractEntity { + private Long id; + private String name; + private String text; + private Long authorId; + private Long startQuestionId; + private final Collection questions = new ArrayList<>(); +} diff --git a/src/main/java/com/javarush/popkov/entity/Question.java b/src/main/java/com/javarush/popkov/entity/Question.java new file mode 100644 index 0000000..235914f --- /dev/null +++ b/src/main/java/com/javarush/popkov/entity/Question.java @@ -0,0 +1,31 @@ +package com.javarush.popkov.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.Collection; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class Question implements AbstractEntity { + private Long id; + private Long questId; + private Long label; + private String text; + private GameState gameState; + private final Collection answers = new ArrayList<>(); + public String getImage() { + if (questId != null && label != null) { + return "quest-" + questId + "-question-" + label; + } + if (label != null) { + return "question-" + label; + } + return "question-" + id; + } +} diff --git a/src/main/java/com/javarush/khmelov/entity/Role.java b/src/main/java/com/javarush/popkov/entity/Role.java similarity index 54% rename from src/main/java/com/javarush/khmelov/entity/Role.java rename to src/main/java/com/javarush/popkov/entity/Role.java index 5ae365f..5ada771 100644 --- a/src/main/java/com/javarush/khmelov/entity/Role.java +++ b/src/main/java/com/javarush/popkov/entity/Role.java @@ -1,4 +1,4 @@ -package com.javarush.khmelov.entity; +package com.javarush.popkov.entity; public enum Role { USER, ADMIN, GUEST diff --git a/src/main/java/com/javarush/popkov/entity/User.java b/src/main/java/com/javarush/popkov/entity/User.java new file mode 100644 index 0000000..8c4991c --- /dev/null +++ b/src/main/java/com/javarush/popkov/entity/User.java @@ -0,0 +1,40 @@ +package com.javarush.popkov.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.Collection; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class User implements AbstractEntity { + private Long id; + private String login; + private String password; + private Role role; + private Gender gender; + private String imageId; + private final Collection quests = new ArrayList<>(); + private final Collection games = new ArrayList<>(); + public String getImage() { //TODO move to DTO + if (imageId != null && !imageId.isBlank()) { + int dotIndex = imageId.lastIndexOf('.'); + return dotIndex > 0 + ? imageId.substring(0, dotIndex) + : imageId; + } + if (gender == Gender.FEMALE) { + return "female"; + } + if (gender == Gender.MALE) { + return "male"; + } + return "no-image"; + } + +} diff --git a/src/main/java/com/javarush/popkov/entity/UserStatistics.java b/src/main/java/com/javarush/popkov/entity/UserStatistics.java new file mode 100644 index 0000000..e49b8d7 --- /dev/null +++ b/src/main/java/com/javarush/popkov/entity/UserStatistics.java @@ -0,0 +1,14 @@ +package com.javarush.popkov.entity; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class UserStatistics { + String login; + long win; + long lost; + long play; + long total; +} diff --git a/src/main/java/com/javarush/popkov/exception/AppException.java b/src/main/java/com/javarush/popkov/exception/AppException.java new file mode 100644 index 0000000..3200a12 --- /dev/null +++ b/src/main/java/com/javarush/popkov/exception/AppException.java @@ -0,0 +1,20 @@ +package com.javarush.popkov.exception; + +@SuppressWarnings("unused") +public class AppException extends RuntimeException { + + public AppException() { + } + + public AppException(String message) { + super(message); + } + + public AppException(String message, Throwable cause) { + super(message, cause); + } + + public AppException(Throwable cause) { + super(cause); + } +} \ No newline at end of file diff --git a/src/main/java/com/javarush/popkov/filter/AuthorizationFilter.java b/src/main/java/com/javarush/popkov/filter/AuthorizationFilter.java new file mode 100644 index 0000000..1b3d3a7 --- /dev/null +++ b/src/main/java/com/javarush/popkov/filter/AuthorizationFilter.java @@ -0,0 +1,80 @@ +package com.javarush.popkov.filter; + +import com.javarush.popkov.entity.Role; +import com.javarush.popkov.entity.User; +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.RequestHelpers; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebFilter; +import jakarta.servlet.http.HttpFilter; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +@Slf4j +@WebFilter({ + Go.INDEX, Go.HOME, + Go.SIGNUP, Go.LOGIN, Go.LOGOUT, + Go.LIST_USER, Go.PROFILE, Go.EDIT_USER, + Go.CREATE_QUEST, Go.QUEST, + Go.PLAY_GAME, + Go.STATISTICS}) +public class AuthorizationFilter extends HttpFilter { + + private final Map> permissions = Map.of( + Role.GUEST, List.of( + Go.HOME, Go.INDEX, Go.LOGIN, Go.SIGNUP, Go.STATISTICS + ), + Role.USER, List.of( + Go.HOME, Go.INDEX, Go.LOGIN, Go.SIGNUP, Go.STATISTICS, + Go.PROFILE, Go.LOGOUT, Go.EDIT_USER, Go.PLAY_GAME, Go.QUEST, + Go.LIST_USER + ), + Role.ADMIN, List.of( + Go.HOME, Go.INDEX, Go.LOGIN, Go.SIGNUP, Go.STATISTICS, + Go.PROFILE, Go.LOGOUT, Go.EDIT_USER, Go.PLAY_GAME, Go.QUEST, + Go.LIST_USER, Go.CREATE_QUEST) + ); + + @Override + protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) + throws IOException, ServletException { + String requestURI = req.getRequestURI(); + String contextPath = req.getContextPath(); + String path = requestURI.startsWith(contextPath) + ? requestURI.substring(contextPath.length()) + : requestURI; + path = path.split("[?#]")[0]; + if (isStaticResource(path)) { + chain.doFilter(req, res); + return; + } + String cmdUri = path.isBlank() || "/".equals(path) ? "/home" : path; + HttpSession session = req.getSession(); + Role role = RequestHelpers.getUser(session) + .map(User::getRole) + .orElse(Role.GUEST); + if (permissions.get(role).contains(cmdUri)) { + chain.doFilter(req, res); + } else { + String message = "Access denied"; + log.warn(message); + RequestHelpers.createError(req, message); + res.sendRedirect(req.getContextPath() + Go.LOGIN); + } + } + + private boolean isStaticResource(String path) { + return path.startsWith("/assets/") + || path.startsWith("/images/") + || path.startsWith("/user-images/") + || path.startsWith("/quest-images/") + || path.equals("/favicon.ico"); + } +} diff --git a/src/main/java/com/javarush/popkov/filter/EncodingSetterFilter.java b/src/main/java/com/javarush/popkov/filter/EncodingSetterFilter.java new file mode 100644 index 0000000..39d2d5d --- /dev/null +++ b/src/main/java/com/javarush/popkov/filter/EncodingSetterFilter.java @@ -0,0 +1,21 @@ +package com.javarush.popkov.filter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebFilter; +import jakarta.servlet.http.HttpFilter; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +@WebFilter({"/*", ""}) +public class EncodingSetterFilter extends HttpFilter { + @Override + protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException { + req.setCharacterEncoding(StandardCharsets.UTF_8.name()); + res.setCharacterEncoding(StandardCharsets.UTF_8.name()); + chain.doFilter(req, res); + } +} diff --git a/src/main/java/com/javarush/popkov/filter/ErrorCleaner.java b/src/main/java/com/javarush/popkov/filter/ErrorCleaner.java new file mode 100644 index 0000000..d826e20 --- /dev/null +++ b/src/main/java/com/javarush/popkov/filter/ErrorCleaner.java @@ -0,0 +1,31 @@ +package com.javarush.popkov.filter; + +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.Key; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebFilter; +import jakarta.servlet.http.HttpFilter; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; + +import java.io.IOException; + +@WebFilter({ + Go.INDEX, Go.HOME, + Go.SIGNUP, Go.LOGIN, Go.LOGOUT, + Go.LIST_USER, Go.PROFILE, Go.EDIT_USER, + Go.CREATE_QUEST, Go.QUEST, Go.PLAY_GAME +}) +public class ErrorCleaner extends HttpFilter { + + @Override + protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException { + chain.doFilter(req, res); + HttpSession session = req.getSession(false); + if (req.getMethod().equals("GET") && session!=null) { + session.removeAttribute(Key.ERROR_MESSAGE); + } + } +} diff --git a/src/main/java/com/javarush/popkov/filter/ErrorCleanerFilter.java b/src/main/java/com/javarush/popkov/filter/ErrorCleanerFilter.java new file mode 100644 index 0000000..ba6445f --- /dev/null +++ b/src/main/java/com/javarush/popkov/filter/ErrorCleanerFilter.java @@ -0,0 +1,26 @@ +package com.javarush.popkov.filter; + +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.Key; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebFilter; +import jakarta.servlet.http.HttpFilter; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; + +import java.io.IOException; + +@WebFilter("/*") +public class ErrorCleanerFilter extends HttpFilter { + + @Override + protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException { + chain.doFilter(req, res); + HttpSession session = req.getSession(false); + if (req.getMethod().equals("GET") && session != null) { + session.removeAttribute(Key.ERROR_MESSAGE); + } + } +} diff --git a/src/main/java/com/javarush/popkov/repository/AnswerRepository.java b/src/main/java/com/javarush/popkov/repository/AnswerRepository.java new file mode 100644 index 0000000..675e87b --- /dev/null +++ b/src/main/java/com/javarush/popkov/repository/AnswerRepository.java @@ -0,0 +1,19 @@ +package com.javarush.popkov.repository; + +import com.javarush.popkov.entity.Answer; + +import java.util.stream.Stream; + +public class AnswerRepository extends BaseRepository { + + @Override + public Stream find(Answer pattern) { + return map.values() + .stream() + .filter(u -> nullOrEquals(pattern.getId(), u.getId())) + .filter(u -> nullOrEquals(pattern.getQuestionId(), u.getQuestionId())) + .filter(u -> nullOrEquals(pattern.getText(), u.getText())) + .filter(u -> nullOrEquals(pattern.getNextQuestionId(), u.getNextQuestionId())); + } + +} diff --git a/src/main/java/com/javarush/popkov/repository/BaseRepository.java b/src/main/java/com/javarush/popkov/repository/BaseRepository.java new file mode 100644 index 0000000..7145aca --- /dev/null +++ b/src/main/java/com/javarush/popkov/repository/BaseRepository.java @@ -0,0 +1,46 @@ +package com.javarush.popkov.repository; + +import com.javarush.popkov.entity.AbstractEntity; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +public abstract class BaseRepository implements Repository { + + protected final Map map = new ConcurrentHashMap<>(); + + public final AtomicLong id = new AtomicLong(0L); + + @Override + public Collection getAll() { + return map.values(); + } + + + @Override + public T get(long id) { + return map.get(id); + } + + @Override + public void create(T entity) { + entity.setId(id.incrementAndGet()); + update(entity); + } + + @Override + public void update(T entity) { + map.put(entity.getId(), entity); + } + + @Override + public void delete(T entity) { + map.remove(entity.getId()); + } + + protected boolean nullOrEquals(Object patternField, Object repoField) { + return patternField == null || patternField.equals(repoField); + } +} \ No newline at end of file diff --git a/src/main/java/com/javarush/popkov/repository/GameRepository.java b/src/main/java/com/javarush/popkov/repository/GameRepository.java new file mode 100644 index 0000000..a614ff3 --- /dev/null +++ b/src/main/java/com/javarush/popkov/repository/GameRepository.java @@ -0,0 +1,20 @@ +package com.javarush.popkov.repository; + +import com.javarush.popkov.entity.Game; + +import java.util.stream.Stream; + +public class GameRepository extends BaseRepository { + + @Override + public Stream find(Game pattern) { + return map.values() + .stream() + .filter(u -> nullOrEquals(pattern.getId(), u.getId())) + .filter(u -> nullOrEquals(pattern.getUserId(), u.getUserId())) + .filter(u -> nullOrEquals(pattern.getQuestId(), u.getQuestId())) + .filter(u -> nullOrEquals(pattern.getCurrentQuestionId(), u.getCurrentQuestionId())) + .filter(u -> nullOrEquals(pattern.getGameState(), u.getGameState())); + } + +} diff --git a/src/main/java/com/javarush/popkov/repository/QuestRepository.java b/src/main/java/com/javarush/popkov/repository/QuestRepository.java new file mode 100644 index 0000000..7f3486f --- /dev/null +++ b/src/main/java/com/javarush/popkov/repository/QuestRepository.java @@ -0,0 +1,20 @@ +package com.javarush.popkov.repository; + +import com.javarush.popkov.entity.Quest; + +import java.util.stream.Stream; + +public class QuestRepository extends BaseRepository { + + @Override + public Stream find(Quest pattern) { + return map.values() + .stream() + .filter(u -> nullOrEquals(pattern.getId(), u.getId())) + .filter(u -> nullOrEquals(pattern.getText(), u.getText())) + .filter(u -> nullOrEquals(pattern.getName(), u.getName())) + .filter(u -> nullOrEquals(pattern.getAuthorId(), u.getAuthorId())) + .filter(u -> nullOrEquals(pattern.getStartQuestionId(), u.getStartQuestionId())); + } + +} \ No newline at end of file diff --git a/src/main/java/com/javarush/popkov/repository/QuestionRepository.java b/src/main/java/com/javarush/popkov/repository/QuestionRepository.java new file mode 100644 index 0000000..4cbd4c9 --- /dev/null +++ b/src/main/java/com/javarush/popkov/repository/QuestionRepository.java @@ -0,0 +1,20 @@ +package com.javarush.popkov.repository; + +import com.javarush.popkov.entity.Question; + +import java.util.stream.Stream; + +public class QuestionRepository extends BaseRepository { + + + @Override + public Stream find(Question pattern) { + return map.values() + .stream() + .filter(u -> nullOrEquals(pattern.getId(), u.getId())) + .filter(u -> nullOrEquals(pattern.getQuestId(), u.getQuestId())) + .filter(u -> nullOrEquals(pattern.getText(), u.getText())) + .filter(u -> nullOrEquals(pattern.getGameState(), u.getGameState())); + } + +} diff --git a/src/main/java/com/javarush/popkov/repository/Repository.java b/src/main/java/com/javarush/popkov/repository/Repository.java new file mode 100644 index 0000000..88b4bea --- /dev/null +++ b/src/main/java/com/javarush/popkov/repository/Repository.java @@ -0,0 +1,19 @@ +package com.javarush.popkov.repository; + +import java.util.Collection; +import java.util.stream.Stream; + +public interface Repository { + + Collection getAll(); + + Stream find(T pattern); + + T get(long id); + + void create(T entity); + + void update(T entity); + + void delete(T entity); +} \ No newline at end of file diff --git a/src/main/java/com/javarush/popkov/repository/UserRepository.java b/src/main/java/com/javarush/popkov/repository/UserRepository.java new file mode 100644 index 0000000..f7e906c --- /dev/null +++ b/src/main/java/com/javarush/popkov/repository/UserRepository.java @@ -0,0 +1,19 @@ +package com.javarush.popkov.repository; + +import com.javarush.popkov.entity.User; + +import java.util.stream.Stream; + +public class UserRepository extends BaseRepository { + + @Override + public Stream find(User pattern) { + return map.values() + .stream() + .filter(u -> nullOrEquals(pattern.getId(), u.getId())) + .filter(u -> nullOrEquals(pattern.getLogin(), u.getLogin())) + .filter(u -> nullOrEquals(pattern.getPassword(), u.getPassword())) + .filter(u -> nullOrEquals(pattern.getRole(), u.getRole())); + } + +} diff --git a/src/main/java/com/javarush/popkov/service/GameService.java b/src/main/java/com/javarush/popkov/service/GameService.java new file mode 100644 index 0000000..eeca505 --- /dev/null +++ b/src/main/java/com/javarush/popkov/service/GameService.java @@ -0,0 +1,67 @@ +package com.javarush.popkov.service; + +import com.javarush.popkov.entity.*; +import com.javarush.popkov.repository.*; +import lombok.AllArgsConstructor; + +import java.util.Comparator; +import java.util.Optional; + +@AllArgsConstructor +public class GameService { + + private final UserRepository userRepository; + private final GameRepository gameRepository; + private final QuestRepository questRepository; + private final QuestionRepository questionRepository; + private final AnswerRepository answerRepository; + + public Optional getGame(Long questId, Long userId) { + Game gamePattern = Game.builder().questId(questId).build(); + gamePattern.setUserId(userId); + Optional currentGame = gameRepository + .find(gamePattern) + .max(Comparator.comparingLong(Game::getId)); + if (currentGame.isPresent()) { + return currentGame; + } else if (gamePattern.getQuestId() != null) { + return Optional.of(getNewGame(userId, gamePattern.getQuestId())); + } else { + return Optional.empty(); + } + } + + private Game getNewGame(Long userId, Long questId) { + Quest quest = questRepository.get(questId); + Long startQuestionId = quest.getStartQuestionId(); + Question firstQuestion = questionRepository.get(startQuestionId); + Game newGame = Game.builder() + .questId(questId) + .currentQuestionId(startQuestionId) + .gameState(firstQuestion.getGameState()) + .userId(userId) //from session + .build(); + userRepository.get(userId).getGames().add(newGame); + gameRepository.create(newGame); + return newGame; + } + + public Optional processOneStep(Long gameId, Long answerId) { + Game game = gameRepository.get(gameId); + if (game.getGameState() == GameState.PLAY) { + Answer answer = answerRepository.get(answerId); + Long nextQuestionId = answer != null + ? answer.getNextQuestionId() + : game.getCurrentQuestionId(); + game.setCurrentQuestionId(nextQuestionId); + Question question = questionRepository.get(nextQuestionId); + game.setGameState(question.getGameState()); + gameRepository.update(game); + } else { + game = getNewGame(game.getUserId(), game.getQuestId()); + } + return Optional.ofNullable(game); + } + +} + diff --git a/src/main/java/com/javarush/popkov/service/ImageService.java b/src/main/java/com/javarush/popkov/service/ImageService.java new file mode 100644 index 0000000..c7f18b6 --- /dev/null +++ b/src/main/java/com/javarush/popkov/service/ImageService.java @@ -0,0 +1,94 @@ +package com.javarush.popkov.service; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.Part; +import lombok.SneakyThrows; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.List; +import java.util.Objects; + +public class ImageService { + + private static final String IMAGES_FOLDER = "images"; + private static final String FALLBACK_FOLDER = "img"; + private static final String PART_NAME = "image"; + private static final String NO_IMAGE_PNG = "no-image.png"; + private static final List EXTENSIONS = List.of( + ".jpg", ".jpeg", ".png", ".bmp", ".gif", ".webp" + ); + + private final Path imagesFolder; + private final Path fallbackFolder; + + @SneakyThrows + public ImageService() { + URL url = ImageService.class.getResource("/"); + Path startPath = Paths.get(Objects.requireNonNull(url).toURI()); + String webInf = "WEB-INF"; + Path webPath = startPath.getParent().endsWith(webInf) + ? startPath.getParent() //run in tomcat (webapp/WEB-INF) + : startPath.resolve(webInf); //embedded tomcat (resources/WEB-INF) + imagesFolder = webPath.resolve(IMAGES_FOLDER); + fallbackFolder = webPath.resolve(FALLBACK_FOLDER); + Files.createDirectories(imagesFolder); + } + + @SneakyThrows + public Path getImagePath(String filename) { + return EXTENSIONS.stream() + .flatMap(ext -> List.of( + imagesFolder.resolve(filename + ext), + fallbackFolder.resolve(filename + ext) + ).stream()) + .filter(Files::exists) + .findAny() + .orElseGet(() -> { + Path fallback = fallbackFolder.resolve(NO_IMAGE_PNG); + return Files.exists(fallback) + ? fallback + : imagesFolder.resolve(NO_IMAGE_PNG); + }); + } + + public void uploadImage(HttpServletRequest req, String imageId) throws IOException, ServletException { + Part data = req.getPart(PART_NAME); + if (Objects.nonNull(data) && data.getInputStream().available() > 0) { + String filename = data.getSubmittedFileName(); + String ext = filename.substring(filename.lastIndexOf(".")); + deleteOldFiles(imageId); + filename = imageId + ext; + uploadImageInternal(filename, data.getInputStream()); + } + } + + private void deleteOldFiles(String filename) { + EXTENSIONS.stream() + .map(ext -> imagesFolder.resolve(filename + ext)) + .filter(Files::exists) + .forEach(p -> { + try { + Files.deleteIfExists(p); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + + @SneakyThrows + private void uploadImageInternal(String name, InputStream data) { + try (data) { + if (data.available() > 0) { + Files.copy(data, imagesFolder.resolve(name), StandardCopyOption.REPLACE_EXISTING); + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/javarush/popkov/service/QuestService.java b/src/main/java/com/javarush/popkov/service/QuestService.java new file mode 100644 index 0000000..4944c0b --- /dev/null +++ b/src/main/java/com/javarush/popkov/service/QuestService.java @@ -0,0 +1,147 @@ +package com.javarush.popkov.service; + +import com.javarush.popkov.entity.*; +import com.javarush.popkov.exception.AppException; +import com.javarush.popkov.repository.AnswerRepository; +import com.javarush.popkov.repository.QuestRepository; +import com.javarush.popkov.repository.QuestionRepository; +import com.javarush.popkov.repository.UserRepository; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class QuestService { + + public static final String QUEST_SYMBOL = ":"; + public static final String WIN_SYMBOL = "+"; + public static final String LOST_SYMBOL = "-"; + public static final String LINK_SYMBOL = "<"; + public static final String DIGITS = "\\d+"; + + private final UserRepository userRepository ; + private final QuestRepository questRepository; + private final QuestionRepository questionRepository; + private final AnswerRepository answerRepository; + + public QuestService(UserRepository userRepository, QuestRepository questRepository, QuestionRepository questionRepository, AnswerRepository answerRepository) { + this.userRepository = userRepository; + this.questRepository = questRepository; + this.questionRepository = questionRepository; + this.answerRepository = answerRepository; + } + + + public Collection getAll() { + return questRepository.getAll(); + } + + public Optional get(long id) { + return Optional.ofNullable(questRepository.get(id)); + } + + + public Optional create(String name, String text, Long userId) { + boolean exists = questRepository + .find(Quest.builder().name(name).authorId(userId).build()) + .findAny() + .isPresent(); + if (exists) { + throw new AppException("Квест уже создан"); + } + Map map = fillDraftMap(text); + if (map.isEmpty()) { + return Optional.empty(); + } + Quest quest = Quest.builder() + .authorId(userId) + .name(name) + .text(text) + .startQuestionId(0L) + .build(); + questRepository.create(quest); + User user = userRepository.get(userId); + Collection quests = user.getQuests(); + quests.add(quest); + questRepository.update(quest); + for (Question question : map.values()) { + question.setQuestId(quest.getId()); + questionRepository.create(question); + } + + Long startKey = findStartQuestionLabel(text); + Long startId = map.get(startKey).getId(); + quest.setStartQuestionId(startId); + + updateLinksAndId(map, quest); + map.values().stream() + .flatMap(q -> q.getAnswers().stream()) + .forEach(answerRepository::create); + return Optional.of(quest); + } + + private Long findStartQuestionLabel(String text) { + Matcher matcher = Pattern.compile(DIGITS).matcher(text); + if (matcher.find()) { + return Long.parseLong(matcher.group()); + } + throw new AppException("not found start index in text"); + } + + private Map fillDraftMap(String text) { + Map map = new TreeMap<>(); + text = "\n" + text; + String pattern = "\n(%s)([:<+-])".formatted(DIGITS); + String[] parts = text.split(pattern); + int index = 1; + Matcher labelIterator = Pattern.compile(pattern).matcher(text); + Question question = new Question(); + while (labelIterator.find()) { + long key = Long.parseLong(labelIterator.group(1)); + String type = labelIterator.group(2); + String partText = parts[index++].strip(); + Optional newQuestion = fillQuestion(question, key, type, partText); + if (newQuestion.isPresent()) { + question = newQuestion.get(); + map.put(key, question); + } + } + return map; + } + + private Optional fillQuestion(Question currentQuestion, long key, String type, String partText) { + currentQuestion = switch (type) { + case QUEST_SYMBOL -> Question.builder().text(partText).gameState(GameState.PLAY).label(key).build(); + case WIN_SYMBOL -> Question.builder().text(partText).gameState(GameState.WIN).label(key).build(); + case LOST_SYMBOL -> Question.builder().text(partText).gameState(GameState.LOST).label(key).build(); + case LINK_SYMBOL -> { + Answer build = Answer.builder() + .nextQuestionId(key) + .questionId(0L) + .text(partText) + .build(); + currentQuestion.getAnswers().add(build); + yield null; + } + default -> throw new AppException("incorrect parsing"); + }; + return Optional.ofNullable(currentQuestion); + } + + private void updateLinksAndId(Map map, Quest quest) { + for (Question question : map.values()) { + question.setQuestId(quest.getId()); + quest.getQuestions().add(question); + for (Answer answer : question.getAnswers()) { + answer.setQuestionId(question.getId()); + Long key = answer.getNextQuestionId(); //label (index in text) + if (map.containsKey(key)) { + answer.setNextQuestionId(map.get(key).getId()); //real index + } + } + } + } +} diff --git a/src/main/java/com/javarush/popkov/service/QuestionService.java b/src/main/java/com/javarush/popkov/service/QuestionService.java new file mode 100644 index 0000000..1675320 --- /dev/null +++ b/src/main/java/com/javarush/popkov/service/QuestionService.java @@ -0,0 +1,26 @@ +package com.javarush.popkov.service; + +import com.javarush.popkov.entity.Question; +import com.javarush.popkov.repository.QuestionRepository; +import lombok.AllArgsConstructor; +import lombok.SneakyThrows; + +import java.util.Optional; + +@AllArgsConstructor +public class QuestionService { + + private final QuestionRepository questionRepository; + + public Optional get(long id) { + return Optional.of(questionRepository.get(id)); + } + + @SneakyThrows + public Optional update(Long questionId, String text) { + Question question = questionRepository.get(questionId); + question.setText(text); + questionRepository.update(question); + return Optional.of(question); + } +} diff --git a/src/main/java/com/javarush/popkov/service/StatService.java b/src/main/java/com/javarush/popkov/service/StatService.java new file mode 100644 index 0000000..c7eb58b --- /dev/null +++ b/src/main/java/com/javarush/popkov/service/StatService.java @@ -0,0 +1,54 @@ +package com.javarush.popkov.service; + +import com.javarush.popkov.entity.Game; +import com.javarush.popkov.entity.GameState; +import com.javarush.popkov.entity.User; +import com.javarush.popkov.entity.UserStatistics; +import com.javarush.popkov.repository.GameRepository; +import com.javarush.popkov.repository.UserRepository; +import lombok.AllArgsConstructor; + +import java.util.Collection; +import java.util.List; + +@AllArgsConstructor +public class StatService { + + private final UserRepository userRepository; + private final GameRepository gameRepository; + + public Collection getUserStatistics() { + return userRepository.getAll() + .stream() + .map(this::calculateUserStatistics) + .toList(); + } + + public UserStatistics getTotalUserStatistics() { + UserStatistics all = UserStatistics.builder().login("ALL").build(); + for (UserStatistics userStatistic : getUserStatistics()) { + all.setPlay(all.getPlay() + userStatistic.getPlay()); + all.setTotal(all.getTotal() + userStatistic.getTotal()); + all.setWin(all.getWin() + userStatistic.getWin()); + all.setLost(all.getLost() + userStatistic.getLost()); + } + return all; + } + + private UserStatistics calculateUserStatistics(User user) { + Game pattern = Game.builder().userId(user.getId()).build(); + List games = gameRepository.find(pattern).toList(); + long win = games.stream().filter(game -> game.getGameState().equals(GameState.WIN)).count(); + long lost = games.stream().filter(game -> game.getGameState().equals(GameState.LOST)).count(); + long play = games.stream().filter(game -> game.getGameState().equals(GameState.PLAY)).count(); + return UserStatistics.builder() + .login(user.getLogin()) + .win(win) + .lost(lost) + .play(play) + .total(win + lost + play) + .build(); + } + +} + diff --git a/src/main/java/com/javarush/popkov/service/UserService.java b/src/main/java/com/javarush/popkov/service/UserService.java new file mode 100644 index 0000000..259edbb --- /dev/null +++ b/src/main/java/com/javarush/popkov/service/UserService.java @@ -0,0 +1,51 @@ +package com.javarush.popkov.service; + +import com.javarush.popkov.entity.User; +import com.javarush.popkov.exception.AppException; +import com.javarush.popkov.repository.UserRepository; + +import java.util.Collection; +import java.util.Optional; + +public class UserService { + + private final UserRepository userRepository; + + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + public void create(User user) { + User loginPattern = User.builder().login(user.getLogin()).build(); + if (userRepository.find(loginPattern).findAny().isEmpty()) { + userRepository.create(user); + } else { + throw new AppException("User with login " + user.getLogin() + " already exists"); + } + } + + public void update(User user) { + userRepository.update(user); + } + + public void delete(User user) { + userRepository.delete(user); + } + + public Collection getAll() { + return userRepository.getAll(); + } + + public Optional get(long id) { + return Optional.ofNullable(userRepository.get(id)); + } + + public Optional get(String login, String password) { + User patternUser = User + .builder() + .login(login) + .password(password) + .build(); + return userRepository.find(patternUser).findAny(); + } +} diff --git a/src/main/java/com/javarush/popkov/util/Go.java b/src/main/java/com/javarush/popkov/util/Go.java new file mode 100644 index 0000000..5117e76 --- /dev/null +++ b/src/main/java/com/javarush/popkov/util/Go.java @@ -0,0 +1,18 @@ +package com.javarush.popkov.util; + +@SuppressWarnings("unused") +public class Go { + public static final String INDEX = "/"; + public static final String HOME = "/home"; + public static final String SIGNUP = "/signup"; + public static final String LOGIN = "/login"; + public static final String LOGOUT = "/logout"; + public static final String PROFILE = "/profile"; + public static final String LIST_USER = "/list-user"; + public static final String EDIT_USER = "/edit-user"; + public static final String STATISTICS = "/statistics"; + public static final String CREATE_QUEST = "/create-quest"; + public static final String QUEST = "/quest"; + public static final String PLAY_GAME = "/play-game"; + +} diff --git a/src/main/java/com/javarush/popkov/util/Key.java b/src/main/java/com/javarush/popkov/util/Key.java new file mode 100644 index 0000000..2cb7765 --- /dev/null +++ b/src/main/java/com/javarush/popkov/util/Key.java @@ -0,0 +1,32 @@ +package com.javarush.popkov.util; + +@SuppressWarnings("unused") +public class Key { + public static final String INDEX = "index"; + + public static final String ID = "id"; + public static final String SIGNUP = "signup"; + public static final String LOGIN = "login"; + public static final String PASSWORD = "password"; + public static final String ROLE = "role"; + public static final String ROLES = "roles"; + public static final String USERS = "users"; + public static final String USER = "user"; + + public static final String CREATE_QUEST = "create-quest"; + public static final String QUESTS = "quests"; + public static final String QUEST_ID = "questId"; + public static final String QUEST = "quest"; + public static final String QUESTION = "question"; + + public static final String NAME = "name"; + public static final String TEXT = "text"; + + public static final String ERROR_MESSAGE = "errorMessage"; + public static final String GAME = "game"; + public static final String ANSWER = "answer"; + public static final String LOGOUT = "logout"; + public static final String QUESTION_ID = "questionId"; + public static final String LIST_USER_STATISTICS = "listUserStatistics"; + public static final String TOTAL_USER_STATISTICS = "totalUserStatistics"; +} diff --git a/src/main/java/com/javarush/popkov/util/RequestHelpers.java b/src/main/java/com/javarush/popkov/util/RequestHelpers.java new file mode 100644 index 0000000..d1b6c83 --- /dev/null +++ b/src/main/java/com/javarush/popkov/util/RequestHelpers.java @@ -0,0 +1,40 @@ +package com.javarush.popkov.util; + +import com.javarush.popkov.entity.User; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import lombok.experimental.UtilityClass; + +import java.util.Optional; + +@UtilityClass +public class RequestHelpers { + + public Long getId(HttpServletRequest req) { + return getId(req, Key.ID); + } + + public Long getId(HttpServletRequest req, String key) { + String id = req.getParameter(key); + return id != null && !id.isBlank() + ? Long.parseLong(id) + : 0L; + } + + public Long getId(HttpSession session) { + Object user = session.getAttribute(Key.USER); + return user != null + ? ((User) user).getId() + : 0L; + } + + public Optional getUser(HttpSession session) { + return Optional + .ofNullable(session.getAttribute(Key.USER)) + .map(User.class::cast); // equivalent to .map(u -> (User) u); + } + + public static void createError(HttpServletRequest request, String errorMessage) { + request.getSession().setAttribute(Key.ERROR_MESSAGE, errorMessage); + } +} diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml new file mode 100644 index 0000000..a2162cd --- /dev/null +++ b/src/main/resources/log4j2.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/create-quest.jsp b/src/main/webapp/WEB-INF/create-quest.jsp new file mode 100644 index 0000000..6e8a0a3 --- /dev/null +++ b/src/main/webapp/WEB-INF/create-quest.jsp @@ -0,0 +1,43 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ page contentType="text/html;charset=UTF-8" %> + + +
+
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + +
+
+
+
+ + + + diff --git a/src/main/webapp/WEB-INF/edit-user.jsp b/src/main/webapp/WEB-INF/edit-user.jsp index f274104..71e7b94 100644 --- a/src/main/webapp/WEB-INF/edit-user.jsp +++ b/src/main/webapp/WEB-INF/edit-user.jsp @@ -1,49 +1,57 @@ <%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@include file="head.jsp" %> - -
-
+<%@include file="parts/header.jsp" %> +
+
- Edit user: + Edit user + + +
+ +
+ ${requestScope.user.login} +
+
-
- -
+
+ +
- min 3 symbols +
min 3 characters
-
- -
+
+ +
- min 8 symb +
min 8 characters
-
- -
+
+ +
+ + + + +
+
+ + +
+ +
+ +
Upload a profile image (png, jpg, etc.)
+
+
+ -
- -
+
+ +
@@ -69,5 +101,18 @@
- + +<%@include file="parts/footer.jsp" %> diff --git a/src/main/webapp/WEB-INF/head.jsp b/src/main/webapp/WEB-INF/head.jsp index 2f7b9f2..fb94bc7 100644 --- a/src/main/webapp/WEB-INF/head.jsp +++ b/src/main/webapp/WEB-INF/head.jsp @@ -2,9 +2,13 @@ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> - Title + + + Pantera + + diff --git a/src/main/webapp/WEB-INF/home.jsp b/src/main/webapp/WEB-INF/home.jsp new file mode 100644 index 0000000..952e4b7 --- /dev/null +++ b/src/main/webapp/WEB-INF/home.jsp @@ -0,0 +1,28 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@include file="parts/header.jsp" %> + +
+

Выберите квест

+ +
+<%@include file="parts/footer.jsp" %> diff --git a/src/main/webapp/WEB-INF/images/question-1.jpg b/src/main/webapp/WEB-INF/images/question-1.jpg new file mode 100644 index 0000000..642b43c Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-1.jpg differ diff --git a/src/main/webapp/WEB-INF/images/question-11.jpg b/src/main/webapp/WEB-INF/images/question-11.jpg new file mode 100644 index 0000000..297b906 Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-11.jpg differ diff --git a/src/main/webapp/WEB-INF/images/question-12.jpg b/src/main/webapp/WEB-INF/images/question-12.jpg new file mode 100644 index 0000000..8b9f14b Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-12.jpg differ diff --git a/src/main/webapp/WEB-INF/images/question-13.jpg b/src/main/webapp/WEB-INF/images/question-13.jpg new file mode 100644 index 0000000..e6df4c8 Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-13.jpg differ diff --git a/src/main/webapp/WEB-INF/images/question-14.jpg b/src/main/webapp/WEB-INF/images/question-14.jpg new file mode 100644 index 0000000..5d8504a Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-14.jpg differ diff --git a/src/main/webapp/WEB-INF/images/question-15.jpg b/src/main/webapp/WEB-INF/images/question-15.jpg new file mode 100644 index 0000000..b7e28fb Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-15.jpg differ diff --git a/src/main/webapp/WEB-INF/images/question-16.jpeg b/src/main/webapp/WEB-INF/images/question-16.jpeg new file mode 100644 index 0000000..3fbd696 Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-16.jpeg differ diff --git a/src/main/webapp/WEB-INF/images/question-17.jpg b/src/main/webapp/WEB-INF/images/question-17.jpg new file mode 100644 index 0000000..6bc7051 Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-17.jpg differ diff --git a/src/main/webapp/WEB-INF/images/question-18.jpg b/src/main/webapp/WEB-INF/images/question-18.jpg new file mode 100644 index 0000000..5536c49 Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-18.jpg differ diff --git a/src/main/webapp/WEB-INF/images/question-2.jpg b/src/main/webapp/WEB-INF/images/question-2.jpg new file mode 100644 index 0000000..e35ecb9 Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-2.jpg differ diff --git a/src/main/webapp/WEB-INF/images/question-20.jpg b/src/main/webapp/WEB-INF/images/question-20.jpg new file mode 100644 index 0000000..4a58892 Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-20.jpg differ diff --git a/src/main/webapp/WEB-INF/images/question-21.jpg b/src/main/webapp/WEB-INF/images/question-21.jpg new file mode 100644 index 0000000..1d3334e Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-21.jpg differ diff --git a/src/main/webapp/WEB-INF/images/question-3.jpg b/src/main/webapp/WEB-INF/images/question-3.jpg new file mode 100644 index 0000000..5bd1148 Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-3.jpg differ diff --git a/src/main/webapp/WEB-INF/images/question-4.jpg b/src/main/webapp/WEB-INF/images/question-4.jpg new file mode 100644 index 0000000..5f2b854 Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-4.jpg differ diff --git a/src/main/webapp/WEB-INF/images/question-5.jpg b/src/main/webapp/WEB-INF/images/question-5.jpg new file mode 100644 index 0000000..5f2b854 Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-5.jpg differ diff --git a/src/main/webapp/WEB-INF/images/question-6.jpg b/src/main/webapp/WEB-INF/images/question-6.jpg new file mode 100644 index 0000000..5f2b854 Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-6.jpg differ diff --git a/src/main/webapp/WEB-INF/images/question-7.jpg b/src/main/webapp/WEB-INF/images/question-7.jpg new file mode 100644 index 0000000..c56f283 Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-7.jpg differ diff --git a/src/main/webapp/WEB-INF/images/question-8.jpg b/src/main/webapp/WEB-INF/images/question-8.jpg new file mode 100644 index 0000000..c56f283 Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-8.jpg differ diff --git a/src/main/webapp/WEB-INF/images/question-9.jpg b/src/main/webapp/WEB-INF/images/question-9.jpg new file mode 100644 index 0000000..5f2b854 Binary files /dev/null and b/src/main/webapp/WEB-INF/images/question-9.jpg differ diff --git a/src/main/webapp/WEB-INF/img/female.png b/src/main/webapp/WEB-INF/img/female.png new file mode 100644 index 0000000..63881db Binary files /dev/null and b/src/main/webapp/WEB-INF/img/female.png differ diff --git a/src/main/webapp/WEB-INF/img/male.png b/src/main/webapp/WEB-INF/img/male.png new file mode 100644 index 0000000..3b54b90 Binary files /dev/null and b/src/main/webapp/WEB-INF/img/male.png differ diff --git a/src/main/webapp/WEB-INF/img/no-image.png b/src/main/webapp/WEB-INF/img/no-image.png new file mode 100644 index 0000000..0c32da0 Binary files /dev/null and b/src/main/webapp/WEB-INF/img/no-image.png differ diff --git a/src/main/webapp/WEB-INF/list-user.jsp b/src/main/webapp/WEB-INF/list-user.jsp index dd52c55..5500433 100644 --- a/src/main/webapp/WEB-INF/list-user.jsp +++ b/src/main/webapp/WEB-INF/list-user.jsp @@ -1,9 +1,17 @@ <%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@include file="head.jsp"%> - - - ${user.login} - - - +<%@include file="parts/header.jsp"%> +
+

Users

+ +
+<%@include file="parts/footer.jsp"%> diff --git a/src/main/webapp/WEB-INF/login.jsp b/src/main/webapp/WEB-INF/login.jsp new file mode 100644 index 0000000..eb74fb5 --- /dev/null +++ b/src/main/webapp/WEB-INF/login.jsp @@ -0,0 +1,30 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@include file="parts/header.jsp" %> +
+
+
+
+

Login

+

Enter your login details

+
+
+
+
+
+
+
+ +
+
+
+
+
+

Forgot your password?

+
+
+
+
+
+
+
+<%@include file="parts/footer.jsp" %> diff --git a/src/main/webapp/WEB-INF/parts/footer.jsp b/src/main/webapp/WEB-INF/parts/footer.jsp new file mode 100644 index 0000000..decc85c --- /dev/null +++ b/src/main/webapp/WEB-INF/parts/footer.jsp @@ -0,0 +1,41 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> + + +
+ +
${sessionScope.errorMessage}
+
+
+ +
    +
  • + + + +
  • +
  • + + + +
  • +
  • + + + +
  • +
+

Copyright   © 2026 JRU Company Inc., Pantera Group. 

+
+
+ + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/parts/header.jsp b/src/main/webapp/WEB-INF/parts/header.jsp new file mode 100644 index 0000000..6aa4dbc --- /dev/null +++ b/src/main/webapp/WEB-INF/parts/header.jsp @@ -0,0 +1,52 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> + + + + + + + Pantera + + + + + + + + +
+ +
\ No newline at end of file diff --git a/src/main/webapp/WEB-INF/parts/quest-demo.jsp b/src/main/webapp/WEB-INF/parts/quest-demo.jsp new file mode 100644 index 0000000..db1751b --- /dev/null +++ b/src/main/webapp/WEB-INF/parts/quest-demo.jsp @@ -0,0 +1,23 @@ +<%@ page contentType="text/html;charset=UTF-8" +%> +Тут введите структуру вашего квеста в формате +[Метка]: Вопрос, где [Метка] - это уникальный числовой номер вопроса +[Метка]< Ответ, где [Метка] - номер, куда перейти при выборе этого ответа +[Метка]+ Сообщение о победе, где [Метка] - это уникальный номер сообщения +[Метка]- Сообщение о поражении, где [Метка] - это уникальный номер сообщения + +Пример: + +Отгадаете загадку? +2< Да, конечно +99< А что это такое? + +2: Без рук, без ног, а рисовать умеет? +99< Художник +99< Дождь +99< Ветер +100< Мороз + +99- Вы проиграли + +100+ Вы выиграли \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/parts/quest-jru.txt b/src/main/webapp/WEB-INF/parts/quest-jru.txt new file mode 100644 index 0000000..72c56c3 --- /dev/null +++ b/src/main/webapp/WEB-INF/parts/quest-jru.txt @@ -0,0 +1,16 @@ +<%@ page contentType="text/html;charset=UTF-8"%>02< Принять вызов +91< Отклонить вызов + +02: Ты принял вызов. Подняться на мостик к капитану? +92< Отказаться подниматься на мостик +03< Подняться на мостик + +03: Ты поднялся на мостик. Ты кто? +93< Солгать о себе +99< Рассказать правду + +91- Ты отклонил вызов. Поражение. +92- Ты не пошел на переговоры. Поражение. +93- Твою ложь разоблачили. Поражение. + +99+ Вы выиграли \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/parts/quest.txt b/src/main/webapp/WEB-INF/parts/quest.txt new file mode 100644 index 0000000..4f6e10b --- /dev/null +++ b/src/main/webapp/WEB-INF/parts/quest.txt @@ -0,0 +1,13 @@ +<%@ page contentType="text/html;charset=UTF-8"%>1: Отгадаете загадку? +2< Да, конечно +99< А что это такое? + +2: Без рук, без ног, а рисовать умеет? +99< Художник +99< Дождь +99< Ветер +100< Мороз + +99- Вы проиграли + +100+ Вы выиграли \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/play-game.jsp b/src/main/webapp/WEB-INF/play-game.jsp new file mode 100644 index 0000000..b35f54a --- /dev/null +++ b/src/main/webapp/WEB-INF/play-game.jsp @@ -0,0 +1,53 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ page contentType="text/html;charset=UTF-8" %> + + + + + + + +
+
+
+
+ +
+
+
+
+

${question.text}

+

Выберите вариант ответа

+
    + +
    + + +
    +
    +
+ + + + + + + + + + + +
+
+
+
+
+ + + diff --git a/src/main/webapp/WEB-INF/profile.jsp b/src/main/webapp/WEB-INF/profile.jsp new file mode 100644 index 0000000..dbd52d9 --- /dev/null +++ b/src/main/webapp/WEB-INF/profile.jsp @@ -0,0 +1,27 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ page contentType="text/html;charset=UTF-8" %> + +
+ + +
+

+

+ ${user.image} +
+

User login: ${user.login}

+

User role: ${user.role}

+
+
+
+ + + +
+
+
+
+
+ + diff --git a/src/main/webapp/WEB-INF/quest.jsp b/src/main/webapp/WEB-INF/quest.jsp new file mode 100644 index 0000000..41cd1bd --- /dev/null +++ b/src/main/webapp/WEB-INF/quest.jsp @@ -0,0 +1,24 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ page contentType="text/html;charset=UTF-8" %> + + +
+
${requestScope.quest.name}
+ + +
+ +
Image key: ${question.image}
+ ${question.text} + + + + +
+
+
+ + diff --git a/src/main/webapp/WEB-INF/signup.jsp b/src/main/webapp/WEB-INF/signup.jsp new file mode 100644 index 0000000..34e399d --- /dev/null +++ b/src/main/webapp/WEB-INF/signup.jsp @@ -0,0 +1,56 @@ +<%@ page contentType="text/html;charset=UTF-8" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@include file="parts/header.jsp" %> +
+
+
+
+
+
+
+

Signup

+
+

Click to upload a photo

+
+ + + +
+

Enter your registration details

+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+<%@include file="parts/footer.jsp" %> diff --git a/src/main/webapp/WEB-INF/start-page.jsp b/src/main/webapp/WEB-INF/start-page.jsp index 0531c1c..fb8a579 100644 --- a/src/main/webapp/WEB-INF/start-page.jsp +++ b/src/main/webapp/WEB-INF/start-page.jsp @@ -1,8 +1,7 @@ <%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@include file="head.jsp"%> - -

<%= "Hello World!" %> -

-
-List Users - +<%@include file="parts/header.jsp"%> +
+

<%= "This is your best quest begins!" %>

+ List Users +
+<%@include file="parts/footer.jsp"%> diff --git a/src/main/webapp/WEB-INF/statistics.jsp b/src/main/webapp/WEB-INF/statistics.jsp new file mode 100644 index 0000000..f001422 --- /dev/null +++ b/src/main/webapp/WEB-INF/statistics.jsp @@ -0,0 +1,47 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ page contentType="text/html;charset=UTF-8" %> + +
+
+

Статистика

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ЛогинВ процессеПобедаПоражениеВсего
${stat.login}${stat.play}${stat.win}${stat.lost}${stat.total}
${all.login}${all.play}${all.win}${all.lost}${all.total}
+
+ + diff --git a/src/main/webapp/assets/css/custom.css b/src/main/webapp/assets/css/custom.css new file mode 100644 index 0000000..b16cbf8 --- /dev/null +++ b/src/main/webapp/assets/css/custom.css @@ -0,0 +1,132 @@ +/* Motorcycle theme accents */ +:root { + --bs-primary: #e76f51; + --bs-primary-rgb: 231, 111, 81; + --bs-link-color: #f4a261; + --bs-link-hover-color: #ffd166; +} + +body { + min-height: 100vh; + color: #e9ecef; + background-color: #1b1e23; + background-repeat: no-repeat; + background-position: center calc(50% - 200px); + background-attachment: fixed; + background-size: cover; + font-size: 1.15rem; + font-weight: 600; +} + +.navbar.bg-secondary, +footer.bg-secondary { + background-color: rgba(35, 40, 46, 0.85) !important; +} + +.hero-title { + color: #000000; + font-size: 3.5rem; + font-weight: 700; + -webkit-text-stroke: 1px #ffffff; + text-shadow: 0 0 1px rgba(0, 0, 0, 0.25); +} + +.navbar, +.nav-link, +.btn, +.list-group-item, +body { + font-weight: 600; +} + +.list-group-item, +.list-group-item a, +.list-group-item a:visited, +.list-group-item a:hover, +.list-group-item a:active, +.list-group-item a:focus { + font-size: 1.25rem; + font-weight: 700; + color: #ffffff !important; + -webkit-text-stroke: 0; + text-shadow: none; +} + +.page-title { + font-weight: 800; + color: #ffffff !important; + -webkit-text-stroke: 0; + text-shadow: none; + font-size: 2rem; +} + +.content-panel { + background-color: rgba(0, 0, 0, 0.55); + border-radius: 12px; + padding: 1.25rem 1.5rem; +} + +.content-panel p { + font-weight: 700; + color: #ffffff !important; + -webkit-text-stroke: 1px #000000; + text-shadow: 0 0 2px rgba(0, 0, 0, 0.6); +} + +label, +.form-label, +.col-form-label, +legend, +.form-text { + color: #ffffff !important; + font-weight: 700 !important; + -webkit-text-stroke: 0.5px #000000 !important; + text-shadow: 0 0 2px rgba(0, 0, 0, 0.6) !important; +} + +.user-row { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.user-avatar { + width: 44px; + height: 44px; + border-radius: 50%; + object-fit: cover; + border: 2px solid rgba(255, 255, 255, 0.7); +} +.list-group { + background-color: transparent; +} + +.list-group-item { + background-color: transparent; + border: 0; + box-shadow: none; +} + +.list-group-item + .list-group-item { + border-top: 0; +} + +.page-title { + font-weight: 800; +} + +.question-text-bg { + display: inline-block; + background-color: rgba(33, 37, 41, 0.85); + color: #f1f3f5; + padding: 0.25rem 0.5rem; + border-radius: 0.35rem; +} + +.quest-text-bg { + display: inline-block; + background-color: rgba(33, 37, 41, 0.85); + color: #f1f3f5; + padding: 0.2rem 0.45rem; + border-radius: 0.35rem; +} diff --git a/src/main/webapp/assets/css/styles.min.css b/src/main/webapp/assets/css/styles.min.css new file mode 100644 index 0000000..320a7ad --- /dev/null +++ b/src/main/webapp/assets/css/styles.min.css @@ -0,0 +1 @@ +.fit-cover{object-fit:cover}.bs-icon{--bs-icon-size:.75rem;display:flex;flex-shrink:0;justify-content:center;align-items:center;font-size:var(--bs-icon-size);width:calc(var(--bs-icon-size) * 2);height:calc(var(--bs-icon-size) * 2);color:var(--bs-primary)}.bs-icon-xs{--bs-icon-size:1rem;width:calc(var(--bs-icon-size) * 1.5);height:calc(var(--bs-icon-size) * 1.5)}.bs-icon-sm{--bs-icon-size:1rem}.bs-icon-md{--bs-icon-size:1.5rem}.bs-icon-lg{--bs-icon-size:2rem}.bs-icon-xl{--bs-icon-size:2.5rem}.bs-icon.bs-icon-primary{color:var(--bs-white);background:var(--bs-primary)}.bs-icon.bs-icon-primary-light{color:var(--bs-primary);background:rgba(var(--bs-primary-rgb),.2)}.bs-icon.bs-icon-semi-white{color:var(--bs-primary);background:rgba(255,255,255,.5)}.bs-icon.bs-icon-rounded{border-radius:.5rem}.bs-icon.bs-icon-circle{border-radius:50%} \ No newline at end of file diff --git a/src/main/webapp/images/background-wallpaper.png b/src/main/webapp/images/background-wallpaper.png new file mode 100644 index 0000000..7d1216b Binary files /dev/null and b/src/main/webapp/images/background-wallpaper.png differ diff --git a/src/main/webapp/images/cat.png b/src/main/webapp/images/cat.png deleted file mode 100644 index 41771a1..0000000 Binary files a/src/main/webapp/images/cat.png and /dev/null differ diff --git a/src/main/webapp/images/no-image.png b/src/main/webapp/images/no-image.png new file mode 100644 index 0000000..0c32da0 Binary files /dev/null and b/src/main/webapp/images/no-image.png differ diff --git a/src/test/java/com/javarush/popkov/BaseIT.java b/src/test/java/com/javarush/popkov/BaseIT.java new file mode 100644 index 0000000..9dc4ecf --- /dev/null +++ b/src/test/java/com/javarush/popkov/BaseIT.java @@ -0,0 +1,60 @@ +package com.javarush.popkov; + +import com.javarush.popkov.config.Config; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.entity.Role; +import com.javarush.popkov.entity.User; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BaseIT { + protected final HttpServletRequest request; + protected final HttpServletResponse response; + protected final HttpSession session; + protected final Config config; + protected final ServletConfig servletConfig; + protected final ServletContext servletContext; + protected User testAdmin; + protected User testUser; + protected User testGuest; + + protected BaseIT() { + //app config + config = Winter.find(Config.class); + config.fillEmptyRepository(); + //servlet config + servletConfig = mock(ServletConfig.class); + servletContext = mock(ServletContext.class); + when(servletConfig.getServletContext()).thenReturn(servletContext); + //current op + request = mock(HttpServletRequest.class); + response = mock(HttpServletResponse.class); + session = mock(HttpSession.class); + when(request.getSession()).thenReturn(session); + //test data + testAdmin = User.builder() + .id(1L) + .login("testAdmin") + .password("testAdmin") + .role(Role.ADMIN) + .build(); + testUser = User.builder() + .id(2L) + .login("testUser") + .password("testUser") + .role(Role.USER) + .build(); + testGuest = User.builder() + .id(3L) + .login("testGuest") + .password("testGuest") + .role(Role.USER) + .build(); + } +} diff --git a/src/test/java/com/javarush/popkov/cmd/CreateQuestIT.java b/src/test/java/com/javarush/popkov/cmd/CreateQuestIT.java new file mode 100644 index 0000000..bbdae97 --- /dev/null +++ b/src/test/java/com/javarush/popkov/cmd/CreateQuestIT.java @@ -0,0 +1,33 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.entity.Role; +import com.javarush.popkov.entity.User; +import com.javarush.popkov.service.QuestService; +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.Key; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +class CreateQuestIT extends BaseIT { + + private final CreateQuest createQuest = Winter.find(CreateQuest.class); + private final QuestService questService = Winter.find(QuestService.class); + + + @Test + void whenCreateQuest_thenQuestsCountIncreaseByOne() { + User admin = User.builder().id(1L).role(Role.ADMIN).build(); + when(session.getAttribute(Key.USER)).thenReturn(admin); + when(request.getParameter(Key.NAME)).thenReturn("TestQuest"); + when(request.getParameter(Key.TEXT)).thenReturn("1: Test OK?\n2< Да\n3< Нет\n2+ win\n3- lost\n"); + + int count = questService.getAll().size(); + assertEquals(Go.HOME, createQuest.doPost(request)); + assertEquals(count + 1, questService.getAll().size()); + } + +} diff --git a/src/test/java/com/javarush/popkov/cmd/EditUserIT.java b/src/test/java/com/javarush/popkov/cmd/EditUserIT.java new file mode 100644 index 0000000..c88a256 --- /dev/null +++ b/src/test/java/com/javarush/popkov/cmd/EditUserIT.java @@ -0,0 +1,40 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.entity.Role; +import com.javarush.popkov.entity.User; +import com.javarush.popkov.service.UserService; +import com.javarush.popkov.util.Key; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.verify; + +class EditUserIT extends BaseIT { + + private final EditUser editUser = Winter.find(EditUser.class); + private final UserService userService = Winter.find(UserService.class); + + @Test + void whenOpenPage_thenCommandReturnJspPage() { + User user = userService.getAll().stream().findFirst().orElseThrow(); + Mockito.when(request.getParameter(Key.ID)).thenReturn(user.getId().toString()); + String view = editUser.doGet(request); + assertEquals("edit-user", view); + verify(request).setAttribute(eq(Key.USER), eq(user)); + } + + @Test + void whenUpdateUser_thenGetPageByUserId() throws Exception { + Mockito.when(request.getParameter(Key.LOGIN)).thenReturn("TestName"); + Mockito.when(request.getParameter(Key.PASSWORD)).thenReturn("TestPassword"); + Mockito.when(request.getParameter(Key.ROLE)).thenReturn(Role.GUEST.toString()); + Mockito.when(request.getParameter(Key.ID)).thenReturn("1"); + String page = editUser.doPost(request); + assertTrue(page.endsWith("?id=1")); + } +} diff --git a/src/test/java/com/javarush/popkov/cmd/HomeIT.java b/src/test/java/com/javarush/popkov/cmd/HomeIT.java new file mode 100644 index 0000000..fdf3c3f --- /dev/null +++ b/src/test/java/com/javarush/popkov/cmd/HomeIT.java @@ -0,0 +1,23 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.util.Key; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Collection; + +import static org.mockito.Mockito.*; + +class HomeIT extends BaseIT { + + + @Test + void whenOpenPage_thenCommandReturnJspPage() { + Home home = Winter.find(Home.class); + String view = home.doGet(request); + Assertions.assertEquals("home", view); + verify(request).setAttribute(eq(Key.QUESTS), any(Collection.class)); + } +} diff --git a/src/test/java/com/javarush/popkov/cmd/ListUserIT.java b/src/test/java/com/javarush/popkov/cmd/ListUserIT.java new file mode 100644 index 0000000..6a2b26d --- /dev/null +++ b/src/test/java/com/javarush/popkov/cmd/ListUserIT.java @@ -0,0 +1,24 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.util.Key; +import org.junit.jupiter.api.Test; + +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +class ListUserIT extends BaseIT { + + ListUser listUser = Winter.find(ListUser.class); + + @Test + void whenGetListUsers_thenReturnJspPage() { + String jspPage = listUser.doGet(request); + + assertEquals("list-user", jspPage); + verify(request).setAttribute(eq(Key.USERS), any(Collection.class)); + } +} diff --git a/src/test/java/com/javarush/popkov/cmd/LoginIT.java b/src/test/java/com/javarush/popkov/cmd/LoginIT.java new file mode 100644 index 0000000..6a4cac3 --- /dev/null +++ b/src/test/java/com/javarush/popkov/cmd/LoginIT.java @@ -0,0 +1,42 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.entity.User; +import com.javarush.popkov.service.UserService; +import com.javarush.popkov.util.Key; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.mockito.Mockito.*; + +class LoginIT extends BaseIT { + + final Login login = Winter.find(Login.class); + final UserService userService = Winter.find(UserService.class); + + @Test + @DisplayName("When login admin then redirect to profile") + void whenLoginAdminThenRedirectToProfile() { + User user = userService.getAll().stream().findFirst().orElseThrow(); + when(request.getParameter("login")).thenReturn(user.getLogin()); + when(request.getParameter("password")).thenReturn(user.getPassword()); + + String actualRedirect = login.doPost(request); + Assertions.assertEquals("/profile", actualRedirect); + + verify(session) + .setAttribute(eq(Key.USER), any(User.class)); + } + + @Test + @DisplayName("When incorrect login then redirect to login") + void whenIncorrectLoginThenRedirectToLogin() { + when(request.getParameter("login")).thenReturn("Carl"); + when(request.getParameter("password")).thenReturn("err"); + + String actualRedirect = login.doPost(request); + Assertions.assertEquals("/login", actualRedirect); + } +} diff --git a/src/test/java/com/javarush/popkov/cmd/LogoutIT.java b/src/test/java/com/javarush/popkov/cmd/LogoutIT.java new file mode 100644 index 0000000..4b7ac0c --- /dev/null +++ b/src/test/java/com/javarush/popkov/cmd/LogoutIT.java @@ -0,0 +1,18 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class LogoutIT extends BaseIT { + + private final Logout logout = Winter.find(Logout.class); + + @Test + void whenOpenPage_thenInvalidateSession() { + logout.doGet(request); + Mockito.verify(session).invalidate(); + } + +} diff --git a/src/test/java/com/javarush/popkov/cmd/PlayGameIT.java b/src/test/java/com/javarush/popkov/cmd/PlayGameIT.java new file mode 100644 index 0000000..2e263a0 --- /dev/null +++ b/src/test/java/com/javarush/popkov/cmd/PlayGameIT.java @@ -0,0 +1,28 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.entity.Game; +import com.javarush.popkov.entity.Question; +import com.javarush.popkov.util.Key; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +class PlayGameIT extends BaseIT { + + private final PlayGame playGame = Winter.find(PlayGame.class); + + @Test + void whenStartGame_thenSetGameAndQuestionInRequest() { + when(session.getAttribute(Key.USER)).thenReturn(testUser); + when(request.getParameter(Key.QUEST_ID)).thenReturn("1"); + String jspPage = playGame.doGet(request); + + assertEquals("play-game", jspPage); + verify(request).setAttribute(eq(Key.GAME), any(Game.class)); + verify(request).setAttribute(eq(Key.QUESTION), any(Question.class)); + } + +} diff --git a/src/test/java/com/javarush/popkov/cmd/ProfileIT.java b/src/test/java/com/javarush/popkov/cmd/ProfileIT.java new file mode 100644 index 0000000..e385760 --- /dev/null +++ b/src/test/java/com/javarush/popkov/cmd/ProfileIT.java @@ -0,0 +1,28 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.Key; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class ProfileIT extends BaseIT { + + private final Profile profile = Winter.find(Profile.class); + + @Test + void whenClickEditInProfile_thenGoToEditUserPage() { + Mockito.when(session.getAttribute(Key.USER)).thenReturn(testUser); + String uri = profile.doPost(request); + Assertions.assertEquals(Go.EDIT_USER + "?id=" + testUser.getId(), uri); + } + + @Test + void whenClickLogout_thenGoLogout() { + Mockito.when(request.getParameter(Key.LOGOUT)).thenReturn("true"); + String uri = profile.doPost(request); + Assertions.assertEquals(Go.LOGOUT, uri); + } +} diff --git a/src/test/java/com/javarush/popkov/cmd/QuestIT.java b/src/test/java/com/javarush/popkov/cmd/QuestIT.java new file mode 100644 index 0000000..a08d2f6 --- /dev/null +++ b/src/test/java/com/javarush/popkov/cmd/QuestIT.java @@ -0,0 +1,48 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.Key; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class QuestIT extends BaseIT { + + private final Quest quest = Winter.find(Quest.class); + + @Test + void whenOpenQuestPageWithCorrectId_thenGetJsp() { + Mockito.when(request.getParameter(Key.ID)).thenReturn("1"); + String jsp = quest.doGet(request); + Assertions.assertEquals("quest", jsp); + } + + @Test + void whenAnonymousPostQuest_thenRedirectBackward() { + Mockito.when(request.getParameter(Key.ID)).thenReturn("1"); + String uri = quest.doPost(request); + Assertions.assertEquals(Go.QUEST, uri); + } + + @Test + void whenNonAdminPostQuest_thenRedirectBackward() { + Mockito.when(session.getAttribute(Key.USER)).thenReturn(testGuest); + Mockito.when(request.getParameter(Key.ID)).thenReturn("1"); + String uri = quest.doPost(request); + Assertions.assertEquals(Go.QUEST, uri); + } + + @Test + void whenAdminPostQuest_thenRedirectBackward() { + Mockito.when(session.getAttribute(Key.USER)).thenReturn(testAdmin); + Mockito.when(request.getParameter(Key.ID)).thenReturn("1"); + Mockito.when(request.getParameter(Key.QUEST_ID)).thenReturn("1"); + Mockito.when(request.getParameter(Key.QUESTION_ID)).thenReturn("1"); + Mockito.when(request.getParameter(Key.TEXT)).thenReturn("newTestTextQuestion"); + String actualUri = quest.doPost(request); + String expectedUri = "%s?id=%d#bookmark%d".formatted(Go.QUEST, 1, 1); + Assertions.assertEquals(expectedUri, actualUri); + } +} diff --git a/src/test/java/com/javarush/popkov/cmd/SignupIT.java b/src/test/java/com/javarush/popkov/cmd/SignupIT.java new file mode 100644 index 0000000..d65a3ca --- /dev/null +++ b/src/test/java/com/javarush/popkov/cmd/SignupIT.java @@ -0,0 +1,30 @@ +package com.javarush.popkov.cmd; + +import com.javarush.popkov.BaseIT; +import com.javarush.popkov.config.Winter; +import com.javarush.popkov.repository.UserRepository; +import com.javarush.popkov.util.Go; +import com.javarush.popkov.util.Key; +import jakarta.servlet.ServletException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.io.IOException; + +class SignupIT extends BaseIT { + + private final Signup signup = Winter.find(Signup.class); + private final UserRepository repository = Winter.find(UserRepository.class); + + @Test + void doPost() { + Mockito.when(request.getParameter(Key.LOGIN)).thenReturn("newTestLogin"); + Mockito.when(request.getParameter(Key.PASSWORD)).thenReturn("newTestPassword"); + Mockito.when(request.getParameter(Key.ROLE)).thenReturn("GUEST"); + + String uri = signup.doPost(request); + Assertions.assertEquals(Go.PROFILE, uri); + Assertions.assertTrue(repository.getAll().toString().contains("newTestLogin")); + } +} diff --git a/src/test/java/com/javarush/popkov/controller/FrontControllerIT.java b/src/test/java/com/javarush/popkov/controller/FrontControllerIT.java new file mode 100644 index 0000000..4e26db6 --- /dev/null +++ b/src/test/java/com/javarush/popkov/controller/FrontControllerIT.java @@ -0,0 +1,24 @@ +package com.javarush.popkov.controller; + +import com.javarush.popkov.BaseIT; +import jakarta.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.Test; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class FrontControllerIT extends BaseIT { + + @Test + void whenFrontSendIncorrectMethod_thenReturn501() throws Exception { + FrontController frontController = new FrontController(); + when(request.getMethod()).thenReturn("IncorrectMethod"); + frontController.init(servletConfig); + when(request.getRequestURI()).thenReturn("/"); + frontController.service(request, response); + verify(response).sendError(eq(HttpServletResponse.SC_NOT_IMPLEMENTED), anyString()); + } + +} diff --git a/src/test/java/com/javarush/popkov/repository/UserRepositoryTest.java b/src/test/java/com/javarush/popkov/repository/UserRepositoryTest.java new file mode 100644 index 0000000..311e08d --- /dev/null +++ b/src/test/java/com/javarush/popkov/repository/UserRepositoryTest.java @@ -0,0 +1,52 @@ +package com.javarush.popkov.repository; + +import com.javarush.popkov.entity.Role; +import com.javarush.popkov.entity.User; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class UserRepositoryTest { + + private final UserRepository userRepository = new UserRepository(); + private User admin; + + @BeforeEach + void createAdmin() { + admin = User.builder() + .id(1L) + .login("testAdmin") + .password("testPassword") + .role(Role.ADMIN) + .build(); + userRepository.create(admin); + } + + @Test + void get() { + User user = userRepository.get(1L); + Assertions.assertEquals(admin, user); + } + + + @Test + void find() { + User pattern = User.builder().login("testAdmin").build(); + var userStream = userRepository.find(pattern); + Assertions.assertEquals(admin, userStream.findFirst().orElseThrow()); + } + + @Test + void update(){ + admin.setLogin("newLogin"); + userRepository.update(admin); + User user = userRepository.get(1L); + Assertions.assertEquals(admin, user); + } + + @Test + void delete(){ + userRepository.delete(admin); + Assertions.assertEquals(0, userRepository.getAll().size()); + } +} \ No newline at end of file