diff --git a/.github/workflows/jacoco_report.yml b/.github/workflows/jacoco_report.yml new file mode 100644 index 0000000..3cd3280 --- /dev/null +++ b/.github/workflows/jacoco_report.yml @@ -0,0 +1,31 @@ +# A Github action that publishes the JaCoCo report as a comment + +name: Measure coverage + +on: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 17 + uses: actions/setup-java@v1 + with: + java-version: '17' + - name: Build with Maven + run: mvn --batch-mode --update-snapshots test + + - name: Jacoco Report to PR + id: jacoco + uses: madrapps/jacoco-report@v1.6.1 + with: + paths: ${{ github.workspace }}/build/site/jacoco/jacoco.xml + token: ${{ secrets.GITHUB_TOKEN }} + min-coverage-overall: 40 + min-coverage-changed-files: 60 + title: Code Coverage + update-comment: true \ No newline at end of file diff --git a/pom.xml b/pom.xml index be5acb5..5cc290c 100644 --- a/pom.xml +++ b/pom.xml @@ -41,6 +41,15 @@ spring-security-test test + + org.springframework.boot + spring-boot-starter-validation + + + javax.validation + validation-api + 2.0.1.Final + org.postgresql postgresql diff --git a/src/main/java/ru/job4j/cars/controller/PostController.java b/src/main/java/ru/job4j/cars/controller/PostController.java index 2292e3a..0d9a61e 100644 --- a/src/main/java/ru/job4j/cars/controller/PostController.java +++ b/src/main/java/ru/job4j/cars/controller/PostController.java @@ -5,6 +5,7 @@ import lombok.AllArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import ru.job4j.cars.dto.ImageDto; @@ -13,6 +14,7 @@ import ru.job4j.cars.model.CarModel; import ru.job4j.cars.model.Post; import ru.job4j.cars.service.*; +import ru.job4j.cars.utilities.SearchValidator; @Controller @RequestMapping("/posts") @@ -29,14 +31,13 @@ public class PostController { private final FuelTypeService fuelTypeService; private final TransmissionService transmissionService; + private final SearchValidator searchValidator; + @GetMapping("/all") - public String getAllPosts(Model model, @ModelAttribute PostSearchDto searchDto) { + public String getAllPosts(Model model, @ModelAttribute PostSearchDto searchDto, BindingResult br) { addFormAttributesToModel(model); - boolean searchActive = searchDto.getCar() != null - || searchDto.getHighestPrice() > 0 - || searchDto.getLowestPrice() > 0 - || searchDto.getPostCreatedBeforeDays() > 0 - || searchDto.isImageExists(); + searchValidator.validate(searchDto, br); + boolean searchActive = searchIsActiveCheck(br); if (!searchActive) { model.addAttribute("posts", postService.findAllNotSold()); } else { @@ -75,32 +76,24 @@ public String getEditPage(Model model, @PathVariable int id) { } @PostMapping("/create") - public String createPost(@ModelAttribute Post post, @RequestParam MultipartFile file, Model model) { - try { - Optional savedPost = postService.save(post, - new ImageDto(file.getOriginalFilename(), file.getBytes())); - if (savedPost.isEmpty()) { - model.addAttribute("message", "Произошла ошибка при создании объявления."); - return "errors/404"; - } - } catch (IOException e) { - model.addAttribute("message", "Ошибка при создании объявления!"); + public String createPost(@ModelAttribute Post post, @RequestParam MultipartFile file, Model model) + throws IOException { + Optional savedPost = postService.save(post, + new ImageDto(file.getOriginalFilename(), file.getBytes())); + if (savedPost.isEmpty()) { + model.addAttribute("message", "Произошла ошибка при создании объявления."); return "errors/404"; } return "redirect:/posts/all"; } @PostMapping("/edit") - public String editPost(@ModelAttribute Post post, @RequestParam MultipartFile file, Model model) { - try { - boolean isUpdated = postService.update(post, - new ImageDto(file.getOriginalFilename(), file.getBytes())); - if (!isUpdated) { - model.addAttribute("message", "Произошла ошибка при обновлении объявления."); - return "errors/404"; - } - } catch (IOException e) { - model.addAttribute("message", "Ошибка при обновлении объявления!"); + public String editPost(@ModelAttribute Post post, @RequestParam MultipartFile file, Model model) + throws IOException { + boolean isUpdated = postService.update(post, + new ImageDto(file.getOriginalFilename(), file.getBytes())); + if (!isUpdated) { + model.addAttribute("message", "Произошла ошибка при обновлении объявления."); return "errors/404"; } return "redirect:/users/posts"; @@ -123,4 +116,8 @@ private Model addFormAttributesToModel(Model model) { return model; } + private boolean searchIsActiveCheck(BindingResult bindingResult) { + return bindingResult.getErrorCount() < PostSearchDto.class.getDeclaredFields().length; + } + } \ No newline at end of file diff --git a/src/main/java/ru/job4j/cars/handler/MultipartFileGlobalExceptionHandler.java b/src/main/java/ru/job4j/cars/handler/MultipartFileGlobalExceptionHandler.java new file mode 100644 index 0000000..a146459 --- /dev/null +++ b/src/main/java/ru/job4j/cars/handler/MultipartFileGlobalExceptionHandler.java @@ -0,0 +1,20 @@ +package ru.job4j.cars.handler; + +import java.io.IOException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +@Slf4j +public class MultipartFileGlobalExceptionHandler { + + @ExceptionHandler (value = {IOException.class}) + public String ioExceptionHandle(Exception e, Model model) { + log.error(e.getMessage(), e); + model.addAttribute("message", "Ошибка при работе с объявлением!"); + return "errors/404"; + } + +} \ No newline at end of file diff --git a/src/main/java/ru/job4j/cars/utilities/SearchValidator.java b/src/main/java/ru/job4j/cars/utilities/SearchValidator.java new file mode 100644 index 0000000..9febc65 --- /dev/null +++ b/src/main/java/ru/job4j/cars/utilities/SearchValidator.java @@ -0,0 +1,36 @@ +package ru.job4j.cars.utilities; + +import org.springframework.stereotype.Component; +import org.springframework.validation.Errors; +import org.springframework.validation.Validator; +import ru.job4j.cars.dto.PostSearchDto; + +@Component +public class SearchValidator implements Validator { + + @Override + public boolean supports(Class clazz) { + return PostSearchDto.class.equals(clazz); + } + + @Override + public void validate(Object target, Errors errors) { + PostSearchDto dto = (PostSearchDto) target; + if (dto.getCar() == null) { + errors.rejectValue("car", "Car is null"); + } + if (dto.getHighestPrice() <= 0) { + errors.rejectValue("highestPrice", "highest price is less than 1"); + } + if (dto.getLowestPrice() <= 0) { + errors.rejectValue("lowestPrice", "lowest price is less than 1"); + } + if (dto.getPostCreatedBeforeDays() <= 0) { + errors.rejectValue("postCreatedBeforeDays", "created before is less than 1"); + } + if (!dto.isImageExists()) { + errors.rejectValue("imageExists", " only image exists is false"); + } + } + +} \ No newline at end of file