From a53ffa125d34f77fa6cf1cdce3996fccb2e98098 Mon Sep 17 00:00:00 2001 From: Lena Sotnik Date: Tue, 19 Sep 2023 07:31:53 -0700 Subject: [PATCH 01/10] Commit with new endpoint PUT[/habit/update/{habitId}] --- .../java/greencity/config/SecurityConfig.java | 1 + .../greencity/controller/HabitController.java | 47 +++-- core/src/test/java/greencity/ModelUtils.java | 12 +- .../controller/HabitControllerTest.java | 23 ++- .../java/greencity/constant/ErrorMessage.java | 1 + ...va => AddUpdateCustomHabitDtoRequest.java} | 2 +- ...a => AddUpdateCustomHabitDtoResponse.java} | 2 +- .../java/greencity/service/HabitService.java | 26 ++- .../src/test/java/greencity/ModelUtils.java | 12 +- ...> AddUpdateCustomHabitDtoRequestTest.java} | 26 +-- ... AddUpdateCustomHabitDtoResponseTest.java} | 22 +-- .../greencity/mapping/CustomHabitMapper.java | 6 +- .../greencity/service/HabitServiceImpl.java | 133 +++++++++---- .../src/test/java/greencity/ModelUtils.java | 39 +++- .../mapping/CustomHabitMapperTest.java | 4 +- .../service/HabitServiceImplTest.java | 181 ++++++++++++++++-- 16 files changed, 413 insertions(+), 124 deletions(-) rename service-api/src/main/java/greencity/dto/habit/{AddCustomHabitDtoRequest.java => AddUpdateCustomHabitDtoRequest.java} (96%) rename service-api/src/main/java/greencity/dto/habit/{AddCustomHabitDtoResponse.java => AddUpdateCustomHabitDtoResponse.java} (96%) rename service-api/src/test/java/greencity/dto/habit/{AddCustomHabitDtoRequestTest.java => AddUpdateCustomHabitDtoRequestTest.java} (81%) rename service-api/src/test/java/greencity/dto/habit/{AddCustomHabitDtoResponseTest.java => AddUpdateCustomHabitDtoResponseTest.java} (82%) diff --git a/core/src/main/java/greencity/config/SecurityConfig.java b/core/src/main/java/greencity/config/SecurityConfig.java index dafa932779..e242d84490 100644 --- a/core/src/main/java/greencity/config/SecurityConfig.java +++ b/core/src/main/java/greencity/config/SecurityConfig.java @@ -274,6 +274,7 @@ protected void configure(HttpSecurity http) throws Exception { "/ownSecurity", "/user/profile", EVENTS + "/update", + "/habit/update/{habitId}", HABIT_ASSIGN_ID + "/update-habit-duration", "/habit/assign/{habitAssignId}/updateProgressNotificationHasDisplayed", HABIT_ASSIGN_ID + "/allUserAndCustomList", diff --git a/core/src/main/java/greencity/controller/HabitController.java b/core/src/main/java/greencity/controller/HabitController.java index 65f56c2dca..02107c5fb7 100644 --- a/core/src/main/java/greencity/controller/HabitController.java +++ b/core/src/main/java/greencity/controller/HabitController.java @@ -7,8 +7,8 @@ import greencity.annotations.ValidLanguage; import greencity.constant.HttpStatuses; import greencity.dto.PageableDto; -import greencity.dto.habit.AddCustomHabitDtoRequest; -import greencity.dto.habit.AddCustomHabitDtoResponse; +import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; +import greencity.dto.habit.AddUpdateCustomHabitDtoResponse; import greencity.dto.shoppinglistitem.ShoppingListItemDto; import greencity.dto.habit.HabitDto; import greencity.dto.habit.HabitVO; @@ -34,14 +34,7 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import springfox.documentation.annotations.ApiIgnore; @@ -214,23 +207,23 @@ public ResponseEntity> findAllHabitsTags(@ApiIgnore @ValidLanguage /** * Method for creating Custom Habit. * - * @param request {@link AddCustomHabitDtoRequest} - new custom habit dto. - * @return dto {@link AddCustomHabitDtoResponse} + * @param request {@link AddUpdateCustomHabitDtoRequest} - new custom habit dto. + * @return dto {@link AddUpdateCustomHabitDtoResponse} * * @author Lilia Mokhnatska. */ @ApiOperation(value = "Add new custom habit.") @ResponseStatus(value = HttpStatus.CREATED) @ApiResponses(value = { - @ApiResponse(code = 201, message = HttpStatuses.CREATED, response = AddCustomHabitDtoResponse.class), + @ApiResponse(code = 201, message = HttpStatuses.CREATED, response = AddUpdateCustomHabitDtoResponse.class), @ApiResponse(code = 400, message = HttpStatuses.BAD_REQUEST), @ApiResponse(code = 401, message = HttpStatuses.UNAUTHORIZED), @ApiResponse(code = 404, message = HttpStatuses.NOT_FOUND), }) @PostMapping(value = "/custom", consumes = {MediaType.APPLICATION_JSON_UTF8_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE}) - public ResponseEntity addCustomHabit( - @RequestPart @Valid AddCustomHabitDtoRequest request, + public ResponseEntity addCustomHabit( + @RequestPart @Valid AddUpdateCustomHabitDtoRequest request, @ApiParam(value = "Image of habit") @ImageValidation @RequestPart(required = false) MultipartFile image, @ApiIgnore Principal principal) { return ResponseEntity @@ -262,4 +255,28 @@ public ResponseEntity> getFriendsAssignedToHabitProf return ResponseEntity.status(HttpStatus.OK) .body(habitService.getFriendsAssignedToHabitProfilePictures(habitId, userVO.getId())); } + + /** + * Method for updating Custom Habit. + * + * @param request {@link AddUpdateCustomHabitDtoRequest} - custom habit dto. + * @return dto {@link AddUpdateCustomHabitDtoResponse} + * + * @author Olena Sotnik. + */ + @ApiOperation(value = "Update new custom habit.") + @ApiResponses(value = { + @ApiResponse(code = 200, message = HttpStatuses.OK, response = AddUpdateCustomHabitDtoResponse.class), + @ApiResponse(code = 400, message = HttpStatuses.BAD_REQUEST), + @ApiResponse(code = 401, message = HttpStatuses.UNAUTHORIZED), + @ApiResponse(code = 403, message = HttpStatuses.FORBIDDEN), + @ApiResponse(code = 404, message = HttpStatuses.NOT_FOUND) + }) + @PutMapping(value = "/update/{habitId}") + public ResponseEntity updateCustomHabit(@PathVariable Long habitId, + @RequestBody @Valid AddUpdateCustomHabitDtoRequest request, @ApiIgnore Principal principal, + @ApiParam(value = "Image of habit") @ImageValidation @RequestPart(required = false) MultipartFile image) { + return ResponseEntity.status(HttpStatus.OK) + .body(habitService.updateCustomHabit(request, habitId, principal.getName(), image)); + } } diff --git a/core/src/test/java/greencity/ModelUtils.java b/core/src/test/java/greencity/ModelUtils.java index 81a69498e7..9a8db3a23c 100644 --- a/core/src/test/java/greencity/ModelUtils.java +++ b/core/src/test/java/greencity/ModelUtils.java @@ -31,7 +31,7 @@ import greencity.dto.habit.HabitAssignPropertiesDto; import greencity.dto.habit.HabitVO; import greencity.dto.habit.UpdateUserShoppingListDto; -import greencity.dto.habit.AddCustomHabitDtoRequest; +import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; import greencity.dto.habit.UserShoppingAndCustomShoppingListsDto; import greencity.dto.habitfact.HabitFactPostDto; import greencity.dto.habitfact.HabitFactTranslationUpdateDto; @@ -113,6 +113,7 @@ import java.time.LocalTime; import java.time.ZoneId; import java.time.ZonedDateTime; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -737,8 +738,8 @@ public static AddEventDtoRequest getAddEventDtoRequest() { .build())).tags(List.of("first", "second", "third")).build(); } - public static AddCustomHabitDtoRequest getAddCustomHabitDtoRequest() { - return AddCustomHabitDtoRequest.builder() + public static AddUpdateCustomHabitDtoRequest getAddCustomHabitDtoRequest() { + return AddUpdateCustomHabitDtoRequest.builder() .complexity(2) .customShoppingListItemDto(List.of( CustomShoppingListItemResponseDto.builder() @@ -767,4 +768,9 @@ public static FilterEventDto getNullFilterEventDto() { .tags(null) .build(); } + + public static Habit getHabitWithCustom() { + return Habit.builder().id(1L).image("image.png") + .complexity(1).isCustomHabit(true).tags(Set.of(getTag())).build(); + } } diff --git a/core/src/test/java/greencity/controller/HabitControllerTest.java b/core/src/test/java/greencity/controller/HabitControllerTest.java index 3252ad893b..004e432632 100644 --- a/core/src/test/java/greencity/controller/HabitControllerTest.java +++ b/core/src/test/java/greencity/controller/HabitControllerTest.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import greencity.ModelUtils; -import greencity.dto.habit.AddCustomHabitDtoRequest; +import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; import greencity.dto.user.UserVO; import greencity.exception.handler.CustomExceptionHandler; import greencity.service.HabitService; @@ -36,6 +36,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.validation.Validator; @@ -281,7 +282,7 @@ void getShoppingListItems() throws Exception { @Test void postCustomHabit() throws Exception { - AddCustomHabitDtoRequest dto = ModelUtils.getAddCustomHabitDtoRequest(); + AddUpdateCustomHabitDtoRequest dto = ModelUtils.getAddCustomHabitDtoRequest(); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.findAndRegisterModules(); @@ -307,4 +308,22 @@ void getFriendsAssignedToHabitProfilePictures() throws Exception { verify(habitService).getFriendsAssignedToHabitProfilePictures(habitId, null); } + + @Test + void updateCustomHabit() throws Exception { + Long habitId = 1L; + AddUpdateCustomHabitDtoRequest dto = ModelUtils.getAddCustomHabitDtoRequest(); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.findAndRegisterModules(); + + String requestedJson = objectMapper.writeValueAsString(dto); + + mockMvc.perform(put(habitLink + "/update/{habitId}", habitId) + .principal(principal) + .contentType(MediaType.APPLICATION_JSON) + .content(requestedJson)) + .andExpect(status().isOk()); + + verify(habitService).updateCustomHabit(dto, habitId, principal.getName(), null); + } } diff --git a/service-api/src/main/java/greencity/constant/ErrorMessage.java b/service-api/src/main/java/greencity/constant/ErrorMessage.java index 941af0bd6b..a89e2df1a9 100644 --- a/service-api/src/main/java/greencity/constant/ErrorMessage.java +++ b/service-api/src/main/java/greencity/constant/ErrorMessage.java @@ -78,6 +78,7 @@ public final class ErrorMessage { public static final String HABIT_FACT_NOT_DELETED_BY_ID = "The habitfact does not deleted by id: "; public static final String SPECIFICATION_VALUE_NOT_FOUND_BY_ID = "The specification value does not exist by this id: "; + public static final String CUSTOM_HABIT_NOT_FOUND = "Custom habit does not exist by id: "; public static final String SPECIFICATION_NOT_FOUND_BY_NAME = "The specification does not exist by this name: "; public static final String LOCATION_IS_PRESENT = "Location is present."; public static final String PHOTO_IS_PRESENT = "Photo is present."; diff --git a/service-api/src/main/java/greencity/dto/habit/AddCustomHabitDtoRequest.java b/service-api/src/main/java/greencity/dto/habit/AddUpdateCustomHabitDtoRequest.java similarity index 96% rename from service-api/src/main/java/greencity/dto/habit/AddCustomHabitDtoRequest.java rename to service-api/src/main/java/greencity/dto/habit/AddUpdateCustomHabitDtoRequest.java index 7cbf114b1a..226d23c1d3 100644 --- a/service-api/src/main/java/greencity/dto/habit/AddCustomHabitDtoRequest.java +++ b/service-api/src/main/java/greencity/dto/habit/AddUpdateCustomHabitDtoRequest.java @@ -24,7 +24,7 @@ @Getter @Setter @EqualsAndHashCode -public class AddCustomHabitDtoRequest { +public class AddUpdateCustomHabitDtoRequest { @Min(value = 1, message = ServiceValidationConstants.HABIT_COMPLEXITY) @Max(value = 3, message = ServiceValidationConstants.HABIT_COMPLEXITY) @NotNull(message = ServiceValidationConstants.HABIT_COMPLEXITY) diff --git a/service-api/src/main/java/greencity/dto/habit/AddCustomHabitDtoResponse.java b/service-api/src/main/java/greencity/dto/habit/AddUpdateCustomHabitDtoResponse.java similarity index 96% rename from service-api/src/main/java/greencity/dto/habit/AddCustomHabitDtoResponse.java rename to service-api/src/main/java/greencity/dto/habit/AddUpdateCustomHabitDtoResponse.java index eaaa35c3c8..617f15e2ba 100644 --- a/service-api/src/main/java/greencity/dto/habit/AddCustomHabitDtoResponse.java +++ b/service-api/src/main/java/greencity/dto/habit/AddUpdateCustomHabitDtoResponse.java @@ -24,7 +24,7 @@ @Getter @Setter @EqualsAndHashCode -public class AddCustomHabitDtoResponse { +public class AddUpdateCustomHabitDtoResponse { private Long id; private Long userId; private String image; diff --git a/service-api/src/main/java/greencity/service/HabitService.java b/service-api/src/main/java/greencity/service/HabitService.java index 9bedd7eec6..d4958e0b08 100644 --- a/service-api/src/main/java/greencity/service/HabitService.java +++ b/service-api/src/main/java/greencity/service/HabitService.java @@ -1,8 +1,8 @@ package greencity.service; import greencity.dto.PageableDto; -import greencity.dto.habit.AddCustomHabitDtoRequest; -import greencity.dto.habit.AddCustomHabitDtoResponse; +import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; +import greencity.dto.habit.AddUpdateCustomHabitDtoResponse; import greencity.dto.habit.HabitVO; import greencity.dto.shoppinglistitem.ShoppingListItemDto; import greencity.dto.habit.HabitDto; @@ -110,15 +110,17 @@ PageableDto getAllByDifferentParameters(UserVO userVO, Pageable pageab List addAllShoppingListItemsByListOfId(Long habitId, List listId); /** - * Method to save {@link AddCustomHabitDtoResponse}. + * Method to save {@link AddUpdateCustomHabitDtoResponse}. * - * @param addCustomHabitDtoRequest dto with {@link AddCustomHabitDtoRequest} + * @param addCustomHabitDtoRequest dto with + * {@link AddUpdateCustomHabitDtoRequest} * entered info about field that need to edit. * @param userEmail {@link String} - user email. - * @return {@link AddCustomHabitDtoResponse} instance. + * @return {@link AddUpdateCustomHabitDtoResponse} instance. * @author Lilia Mokhnatska */ - AddCustomHabitDtoResponse addCustomHabit(AddCustomHabitDtoRequest addCustomHabitDtoRequest, MultipartFile image, + AddUpdateCustomHabitDtoResponse addCustomHabit(AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest, + MultipartFile image, String userEmail); /** @@ -130,4 +132,16 @@ AddCustomHabitDtoResponse addCustomHabit(AddCustomHabitDtoRequest addCustomHabit * @return List of friends' profile pictures. */ List getFriendsAssignedToHabitProfilePictures(Long habitId, Long userId); + + /** + * Method to update {@link AddUpdateCustomHabitDtoResponse}. + * + * @param customHabitDtoRequest dto with {@link AddUpdateCustomHabitDtoRequest} + * entered info about field that need to edit. + * @param userEmail {@link String} - user email. + * @return {@link AddUpdateCustomHabitDtoResponse} instance. + * @author Olena Sotnik. + */ + AddUpdateCustomHabitDtoResponse updateCustomHabit(AddUpdateCustomHabitDtoRequest customHabitDtoRequest, + Long habitId, String userEmail, MultipartFile image); } diff --git a/service-api/src/test/java/greencity/ModelUtils.java b/service-api/src/test/java/greencity/ModelUtils.java index 45ca5c04c6..88cb4aff62 100644 --- a/service-api/src/test/java/greencity/ModelUtils.java +++ b/service-api/src/test/java/greencity/ModelUtils.java @@ -7,8 +7,8 @@ import greencity.dto.event.EventDto; import greencity.dto.eventcomment.EventCommentAuthorDto; import greencity.dto.eventcomment.EventCommentForSendEmailDto; -import greencity.dto.habit.AddCustomHabitDtoRequest; -import greencity.dto.habit.AddCustomHabitDtoResponse; +import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; +import greencity.dto.habit.AddUpdateCustomHabitDtoResponse; import greencity.dto.habit.UserShoppingAndCustomShoppingListsDto; import greencity.dto.newssubscriber.NewsSubscriberResponseDto; import greencity.dto.place.PlaceNotificationDto; @@ -193,8 +193,8 @@ public static UserShoppingAndCustomShoppingListsDto getUserShoppingAndCustomShop .build(); } - public static AddCustomHabitDtoRequest getAddCustomHabitDtoRequest() { - return AddCustomHabitDtoRequest.builder() + public static AddUpdateCustomHabitDtoRequest getAddCustomHabitDtoRequest() { + return AddUpdateCustomHabitDtoRequest.builder() .complexity(1) .image("") .defaultDuration(14) @@ -202,8 +202,8 @@ public static AddCustomHabitDtoRequest getAddCustomHabitDtoRequest() { .build(); } - public static AddCustomHabitDtoResponse getAddCustomHabitDtoResponse() { - return AddCustomHabitDtoResponse.builder() + public static AddUpdateCustomHabitDtoResponse getAddCustomHabitDtoResponse() { + return AddUpdateCustomHabitDtoResponse.builder() .id(1L) .complexity(1) .image("") diff --git a/service-api/src/test/java/greencity/dto/habit/AddCustomHabitDtoRequestTest.java b/service-api/src/test/java/greencity/dto/habit/AddUpdateCustomHabitDtoRequestTest.java similarity index 81% rename from service-api/src/test/java/greencity/dto/habit/AddCustomHabitDtoRequestTest.java rename to service-api/src/test/java/greencity/dto/habit/AddUpdateCustomHabitDtoRequestTest.java index 52f0c3985c..e8c8cfca55 100644 --- a/service-api/src/test/java/greencity/dto/habit/AddCustomHabitDtoRequestTest.java +++ b/service-api/src/test/java/greencity/dto/habit/AddUpdateCustomHabitDtoRequestTest.java @@ -16,20 +16,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -class AddCustomHabitDtoRequestTest { +class AddUpdateCustomHabitDtoRequestTest { @SneakyThrows @ParameterizedTest @MethodSource("provideFieldsAndValidValues") void validComplexityInAddCustomHabitDtoRequestTest(Integer complexity) { - var dto = AddCustomHabitDtoRequest.builder() + var dto = AddUpdateCustomHabitDtoRequest.builder() .complexity(complexity) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertTrue(constraintViolations.isEmpty()); @@ -39,14 +39,14 @@ void validComplexityInAddCustomHabitDtoRequestTest(Integer complexity) { @ParameterizedTest @MethodSource("provideFieldsAndInvalidValues") void invalidComplexityInAddCustomHabitDtoRequestTest(Integer complexity) { - var dto = AddCustomHabitDtoRequest.builder() + var dto = AddUpdateCustomHabitDtoRequest.builder() .complexity(complexity) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(1, constraintViolations.size()); @@ -54,14 +54,14 @@ void invalidComplexityInAddCustomHabitDtoRequestTest(Integer complexity) { @Test void invalidComplexityIsNullInAddCustomHabitDtoRequestTest() { - var dto = AddCustomHabitDtoRequest.builder() + var dto = AddUpdateCustomHabitDtoRequest.builder() .complexity(null) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(1, constraintViolations.size()); @@ -85,14 +85,14 @@ private static Stream provideFieldsAndInvalidValues() { @Test void invalidNumberOfTagsIdNullInAddCustomHabitDtoRequestTest() { - var dto = AddCustomHabitDtoRequest.builder() + var dto = AddUpdateCustomHabitDtoRequest.builder() .tagIds(null) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(1, constraintViolations.size()); @@ -102,14 +102,14 @@ void invalidNumberOfTagsIdNullInAddCustomHabitDtoRequestTest() { @ParameterizedTest @MethodSource("provideFieldsAndValidValuesForTagsIds") void validNumberOfTagsIdsInAddCustomHabitDtoRequestTest(Set tagIds) { - var dto = AddCustomHabitDtoRequest.builder() + var dto = AddUpdateCustomHabitDtoRequest.builder() .tagIds(tagIds) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(1, constraintViolations.size()); @@ -119,14 +119,14 @@ void validNumberOfTagsIdsInAddCustomHabitDtoRequestTest(Set tagIds) { @ParameterizedTest @MethodSource("provideFieldsAndInvalidValuesForTagsIds") void invalidNumberOfTagsIdsInAddCustomHabitDtoRequestTest(Set tagIds) { - var dto = AddCustomHabitDtoRequest.builder() + var dto = AddUpdateCustomHabitDtoRequest.builder() .tagIds(tagIds) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(2, constraintViolations.size()); diff --git a/service-api/src/test/java/greencity/dto/habit/AddCustomHabitDtoResponseTest.java b/service-api/src/test/java/greencity/dto/habit/AddUpdateCustomHabitDtoResponseTest.java similarity index 82% rename from service-api/src/test/java/greencity/dto/habit/AddCustomHabitDtoResponseTest.java rename to service-api/src/test/java/greencity/dto/habit/AddUpdateCustomHabitDtoResponseTest.java index f458bbc343..63244c29fc 100644 --- a/service-api/src/test/java/greencity/dto/habit/AddCustomHabitDtoResponseTest.java +++ b/service-api/src/test/java/greencity/dto/habit/AddUpdateCustomHabitDtoResponseTest.java @@ -16,20 +16,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -class AddCustomHabitDtoResponseTest { +class AddUpdateCustomHabitDtoResponseTest { @SneakyThrows @ParameterizedTest @MethodSource("provideFieldsAndValidValues") void validComplexityInAddCustomHabitDtoResponseTest(Integer complexity) { - var dto = AddCustomHabitDtoResponse.builder() + var dto = AddUpdateCustomHabitDtoResponse.builder() .complexity(complexity) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertTrue(constraintViolations.isEmpty()); @@ -39,14 +39,14 @@ void validComplexityInAddCustomHabitDtoResponseTest(Integer complexity) { @ParameterizedTest @MethodSource("provideFieldsAndInvalidValues") void invalidComplexityInAddCustomHabitDtoResponseTest(Integer complexity) { - var dto = AddCustomHabitDtoResponse.builder() + var dto = AddUpdateCustomHabitDtoResponse.builder() .complexity(complexity) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(1, constraintViolations.size()); @@ -54,14 +54,14 @@ void invalidComplexityInAddCustomHabitDtoResponseTest(Integer complexity) { @Test void invalidComplexityIsNullInAddCustomHabitDtoResponseTest() { - var dto = AddCustomHabitDtoResponse.builder() + var dto = AddUpdateCustomHabitDtoResponse.builder() .complexity(null) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(1, constraintViolations.size()); @@ -87,14 +87,14 @@ private static Stream provideFieldsAndInvalidValues() { @ParameterizedTest @MethodSource("provideFieldsAndValidValuesForTagsIds") void validNumberOfTagsIdsInAddCustomHabitDtoResponseTest(Set tagIds) { - var dto = AddCustomHabitDtoResponse.builder() + var dto = AddUpdateCustomHabitDtoResponse.builder() .tagIds(tagIds) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(1, constraintViolations.size()); @@ -104,14 +104,14 @@ void validNumberOfTagsIdsInAddCustomHabitDtoResponseTest(Set tagIds) { @ParameterizedTest @MethodSource("provideFieldsAndInvalidValuesForTagsIds") void invalidNumberOfTagsIdsInAddCustomHabitDtoResponseTest(Set tagIds) { - var dto = AddCustomHabitDtoResponse.builder() + var dto = AddUpdateCustomHabitDtoResponse.builder() .tagIds(tagIds) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(2, constraintViolations.size()); diff --git a/service/src/main/java/greencity/mapping/CustomHabitMapper.java b/service/src/main/java/greencity/mapping/CustomHabitMapper.java index 891fc541ac..1ccfa2add6 100644 --- a/service/src/main/java/greencity/mapping/CustomHabitMapper.java +++ b/service/src/main/java/greencity/mapping/CustomHabitMapper.java @@ -1,14 +1,14 @@ package greencity.mapping; -import greencity.dto.habit.AddCustomHabitDtoRequest; +import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; import greencity.entity.Habit; import org.modelmapper.AbstractConverter; import org.springframework.stereotype.Component; @Component -public class CustomHabitMapper extends AbstractConverter { +public class CustomHabitMapper extends AbstractConverter { @Override - public Habit convert(AddCustomHabitDtoRequest addCustomHabitDtoRequest) { + public Habit convert(AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest) { return Habit.builder() .image(addCustomHabitDtoRequest.getImage()) .complexity(addCustomHabitDtoRequest.getComplexity()) diff --git a/service/src/main/java/greencity/service/HabitServiceImpl.java b/service/src/main/java/greencity/service/HabitServiceImpl.java index 3d61b4651b..25d8b20b8c 100644 --- a/service/src/main/java/greencity/service/HabitServiceImpl.java +++ b/service/src/main/java/greencity/service/HabitServiceImpl.java @@ -3,8 +3,8 @@ import greencity.constant.AppConstant; import greencity.constant.ErrorMessage; import greencity.dto.PageableDto; -import greencity.dto.habit.AddCustomHabitDtoRequest; -import greencity.dto.habit.AddCustomHabitDtoResponse; +import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; +import greencity.dto.habit.AddUpdateCustomHabitDtoResponse; import greencity.dto.habit.HabitDto; import greencity.dto.shoppinglistitem.ShoppingListItemDto; import greencity.dto.user.UserProfilePictureDto; @@ -15,7 +15,9 @@ import greencity.entity.HabitTranslation; import greencity.entity.Tag; import greencity.entity.User; +import greencity.enums.Role; import greencity.exception.exceptions.NotFoundException; +import greencity.exception.exceptions.UserHasNoPermissionToAccessException; import greencity.exception.exceptions.WrongEmailException; import greencity.mapping.CustomHabitMapper; import greencity.mapping.CustomShoppingListMapper; @@ -27,11 +29,11 @@ import greencity.repository.ShoppingListItemTranslationRepo; import greencity.repository.HabitAssignRepo; +import java.util.Objects; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import java.util.Optional; -import java.util.Set; import java.util.stream.Collectors; import greencity.repository.CustomShoppingListItemRepo; @@ -47,6 +49,7 @@ import org.springframework.web.multipart.MultipartFile; import javax.transaction.Transactional; +import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; /** * Implementation of {@link HabitService}. @@ -300,10 +303,11 @@ public List addAllShoppingListItemsByListOfId(Long habitId, List lis @Transactional @Override - public AddCustomHabitDtoResponse addCustomHabit( - AddCustomHabitDtoRequest addCustomHabitDtoRequest, MultipartFile image, String userEmail) { + public AddUpdateCustomHabitDtoResponse addCustomHabit( + AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest, MultipartFile image, String userEmail) { User user = userRepo.findByEmail(userEmail) .orElseThrow(() -> new WrongEmailException(ErrorMessage.USER_NOT_FOUND_BY_EMAIL + userEmail)); + if (StringUtils.isNotBlank(addCustomHabitDtoRequest.getImage())) { image = fileService.convertToMultipartImage(addCustomHabitDtoRequest.getImage()); } @@ -312,48 +316,24 @@ public AddCustomHabitDtoResponse addCustomHabit( } else { addCustomHabitDtoRequest.setImage(DEFAULT_TITLE_IMAGE_PATH); } - Habit habit = habitRepo.save(customHabitMapper.convert(addCustomHabitDtoRequest)); habit.setUserId(user.getId()); - Set tagIds = addCustomHabitDtoRequest.getTagIds(); - - habit.setTags(tagIds.stream().map(tagId -> tagsRepo.findById(tagId) - .orElseThrow(() -> new NotFoundException(ErrorMessage.TAG_NOT_FOUND + tagId))).collect(Collectors.toSet())); - - List habitTranslationListForUa = - habitTranslationMapper.mapAllToList((addCustomHabitDtoRequest.getHabitTranslations())); - habitTranslationListForUa.forEach(habitTranslation -> habitTranslation.setHabit(habit)); - habitTranslationListForUa.forEach(habitTranslation -> habitTranslation.setLanguage( - languageRepo.findByCode("ua") - .orElseThrow(NoSuchElementException::new))); - habitTranslationRepo.saveAll(habitTranslationListForUa); - - List habitTranslationListForEn = - habitTranslationMapper.mapAllToList((addCustomHabitDtoRequest.getHabitTranslations())); - habitTranslationListForEn.forEach(habitTranslation -> habitTranslation.setHabit(habit)); - habitTranslationListForEn.forEach(habitTranslation -> habitTranslation.setLanguage( - languageRepo.findByCode("en") - .orElseThrow(NoSuchElementException::new))); - habitTranslationRepo.saveAll(habitTranslationListForEn); - - List customShoppingListItems = - customShoppingListMapper.mapAllToList(addCustomHabitDtoRequest.getCustomShoppingListItemDto()); - customShoppingListItems.forEach(customShoppingListItem -> customShoppingListItem.setHabit(habit)); - customShoppingListItems.forEach(customShoppingListItem -> customShoppingListItem.setUser(user)); - customShoppingListItemRepo.saveAll(customShoppingListItems); + setTagsIdsToHabit(addCustomHabitDtoRequest, habit); + saveHabitTranslationListsToHabitTranslationRepo(addCustomHabitDtoRequest, habit); + setCustomShoppingListItemToHabit(addCustomHabitDtoRequest, habit, user); return buildAddCustomHabitDtoResponse(habit, user.getId()); } /** - * Method that build {@link AddCustomHabitDtoResponse} from {@link Habit}. + * Method that build {@link AddUpdateCustomHabitDtoResponse} from {@link Habit}. * * @param habit {@link Habit} * @param userId {@link Long} - * @return {@link AddCustomHabitDtoResponse} + * @return {@link AddUpdateCustomHabitDtoResponse} * @author Lilia Mokhnatska */ - private AddCustomHabitDtoResponse buildAddCustomHabitDtoResponse(Habit habit, Long userId) { - AddCustomHabitDtoResponse response = modelMapper.map(habit, AddCustomHabitDtoResponse.class); + private AddUpdateCustomHabitDtoResponse buildAddCustomHabitDtoResponse(Habit habit, Long userId) { + AddUpdateCustomHabitDtoResponse response = modelMapper.map(habit, AddUpdateCustomHabitDtoResponse.class); response.setCustomShoppingListItemDto(customShoppingListResponseDtoMapper .mapAllToList(customShoppingListItemRepo.findAllByUserIdAndHabitId(userId, habit.getId()))); @@ -378,4 +358,85 @@ public List getFriendsAssignedToHabitProfilePictures(Long return users.stream().map(user -> modelMapper.map(user, UserProfilePictureDto.class)) .collect(Collectors.toList()); } + + @Transactional + @Override + public AddUpdateCustomHabitDtoResponse updateCustomHabit(AddUpdateCustomHabitDtoRequest habitDto, + Long habitId, String userEmail, MultipartFile image) { + User user = userRepo.findByEmail(userEmail) + .orElseThrow(() -> new WrongEmailException(ErrorMessage.USER_NOT_FOUND_BY_EMAIL + userEmail)); + Habit toUpdate = habitRepo.findById(habitId) + .orElseThrow(() -> new NotFoundException(ErrorMessage.CUSTOM_HABIT_NOT_FOUND + habitId)); + checkAccessForAdminAndModerator(user, toUpdate); + enhanceHabitWithNewData(toUpdate, habitDto, user, image); + Habit updatedHabit = habitRepo.save(toUpdate); + return buildAddCustomHabitDtoResponse(updatedHabit, user.getId()); + } + + private void enhanceHabitWithNewData(Habit toUpdate, AddUpdateCustomHabitDtoRequest habitDto, + User user, MultipartFile image) { + if (Objects.nonNull(habitDto.getComplexity())) { + toUpdate.setComplexity(habitDto.getComplexity()); + } + if (Objects.nonNull(habitDto.getDefaultDuration())) { + toUpdate.setDefaultDuration(habitDto.getDefaultDuration()); + } + if (isNotEmpty(habitDto.getHabitTranslations())) { + toUpdate.setHabitTranslations(mapHabitTranslationFromAddCustomHabitDtoRequest(habitDto)); + saveHabitTranslationListsToHabitTranslationRepo(habitDto, toUpdate); + } + if (isNotEmpty(habitDto.getCustomShoppingListItemDto())) { + setCustomShoppingListItemToHabit(habitDto, toUpdate, user); + } + if (StringUtils.isNotBlank(habitDto.getImage())) { + image = fileService.convertToMultipartImage(habitDto.getImage()); + } + if (image != null) { + toUpdate.setImage(fileService.upload(image)); + } + if (isNotEmpty(habitDto.getTagIds())) { + setTagsIdsToHabit(habitDto, toUpdate); + } + } + + private void saveHabitTranslationListsToHabitTranslationRepo(AddUpdateCustomHabitDtoRequest habitDto, Habit habit) { + List habitTranslationListForUa = mapHabitTranslationFromAddCustomHabitDtoRequest(habitDto); + habitTranslationListForUa.forEach(habitTranslation -> habitTranslation.setHabit(habit)); + habitTranslationListForUa.forEach(habitTranslation -> habitTranslation.setLanguage( + languageRepo.findByCode("ua").orElseThrow(NoSuchElementException::new))); + habitTranslationRepo.saveAll(habitTranslationListForUa); + + List habitTranslationListForEn = mapHabitTranslationFromAddCustomHabitDtoRequest(habitDto); + habitTranslationListForEn.forEach(habitTranslation -> habitTranslation.setHabit(habit)); + habitTranslationListForEn.forEach(habitTranslation -> habitTranslation.setLanguage( + languageRepo.findByCode("en").orElseThrow(NoSuchElementException::new))); + habitTranslationRepo.saveAll(habitTranslationListForEn); + } + + private List mapHabitTranslationFromAddCustomHabitDtoRequest( + AddUpdateCustomHabitDtoRequest habitDto) { + return habitTranslationMapper.mapAllToList(habitDto.getHabitTranslations()); + } + + private void setTagsIdsToHabit(AddUpdateCustomHabitDtoRequest habitDto, Habit habit) { + habit.setTags(habitDto.getTagIds().stream().map(tagId -> tagsRepo.findById(tagId) + .orElseThrow(() -> new NotFoundException(ErrorMessage.TAG_NOT_FOUND + tagId))) + .collect(Collectors.toSet())); + } + + private void setCustomShoppingListItemToHabit(AddUpdateCustomHabitDtoRequest habitDto, Habit habit, User user) { + List customShoppingListItems = + customShoppingListMapper.mapAllToList(habitDto.getCustomShoppingListItemDto()); + customShoppingListItems.forEach(customShoppingListItem -> customShoppingListItem.setHabit(habit)); + customShoppingListItems.forEach(customShoppingListItem -> customShoppingListItem.setUser(user)); + customShoppingListItemRepo.saveAll(customShoppingListItems); + habit.setCustomShoppingListItems(customShoppingListItems); + } + + private void checkAccessForAdminAndModerator(User user, Habit habit) { + if (user.getRole() != Role.ROLE_ADMIN && user.getRole() != Role.ROLE_MODERATOR + && !user.getId().equals(habit.getUserId())) { + throw new UserHasNoPermissionToAccessException(ErrorMessage.USER_HAS_NO_PERMISSION); + } + } } diff --git a/service/src/test/java/greencity/ModelUtils.java b/service/src/test/java/greencity/ModelUtils.java index 73e07a94e6..4f22b07414 100644 --- a/service/src/test/java/greencity/ModelUtils.java +++ b/service/src/test/java/greencity/ModelUtils.java @@ -61,8 +61,8 @@ import greencity.dto.friends.UserFriendDto; import greencity.dto.geocoding.AddressLatLngResponse; import greencity.dto.geocoding.AddressResponse; -import greencity.dto.habit.AddCustomHabitDtoRequest; -import greencity.dto.habit.AddCustomHabitDtoResponse; +import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; +import greencity.dto.habit.AddUpdateCustomHabitDtoResponse; import greencity.dto.habit.HabitAssignDto; import greencity.dto.habit.HabitAssignPropertiesDto; import greencity.dto.habit.HabitAssignUserDurationDto; @@ -218,6 +218,7 @@ public class ModelUtils { public static String HABIT_TRANSLATION_DESCRIPTION = "Description"; public static String SHOPPING_LIST_TEXT = "buy a shopper"; public static String HABIT_ITEM = "Item"; + public static String HABIT_DEFAULT_IMAGE = "img/habit-default.png"; public static EventAttenderDto getEventAttenderDto() { return EventAttenderDto.builder().id(1L).name(TestConst.NAME).build(); @@ -2800,8 +2801,8 @@ public static UserShoppingAndCustomShoppingListsDto getUserShoppingAndCustomShop .build(); } - public static AddCustomHabitDtoRequest getAddCustomHabitDtoRequest() { - return AddCustomHabitDtoRequest.builder() + public static AddUpdateCustomHabitDtoRequest getAddCustomHabitDtoRequest() { + return AddUpdateCustomHabitDtoRequest.builder() .complexity(2) .defaultDuration(7) .build(); @@ -2826,8 +2827,8 @@ public static HabitTranslation getHabitTranslationForServiceTest() { .build(); } - public static AddCustomHabitDtoRequest getAddCustomHabitDtoRequestForServiceTest() { - return AddCustomHabitDtoRequest.builder() + public static AddUpdateCustomHabitDtoRequest getAddCustomHabitDtoRequestForServiceTest() { + return AddUpdateCustomHabitDtoRequest.builder() .complexity(2) .customShoppingListItemDto(List.of( CustomShoppingListItemResponseDto.builder() @@ -2847,8 +2848,30 @@ public static AddCustomHabitDtoRequest getAddCustomHabitDtoRequestForServiceTest .build(); } - public static AddCustomHabitDtoResponse getAddCustomHabitDtoResponse() { - return AddCustomHabitDtoResponse.builder() + public static AddUpdateCustomHabitDtoRequest getAddCustomHabitDtoRequestWithImage() { + return AddUpdateCustomHabitDtoRequest.builder() + .complexity(2) + .customShoppingListItemDto(List.of( + CustomShoppingListItemResponseDto.builder() + .id(1L) + .status(ShoppingListItemStatus.ACTIVE) + .text(SHOPPING_LIST_TEXT) + .build())) + .defaultDuration(7) + .image(HABIT_DEFAULT_IMAGE) + .habitTranslations( + List.of(HabitTranslationDto.builder() + .description(HABIT_TRANSLATION_DESCRIPTION) + .habitItem(HABIT_ITEM) + .languageCode("ua") + .name(HABIT_TRANSLATION_NAME) + .build())) + .tagIds(Set.of(20L)) + .build(); + } + + public static AddUpdateCustomHabitDtoResponse getAddCustomHabitDtoResponse() { + return AddUpdateCustomHabitDtoResponse.builder() .id(1L) .complexity(2) .customShoppingListItemDto(List.of( diff --git a/service/src/test/java/greencity/mapping/CustomHabitMapperTest.java b/service/src/test/java/greencity/mapping/CustomHabitMapperTest.java index f9fe91c904..a33c7006b4 100644 --- a/service/src/test/java/greencity/mapping/CustomHabitMapperTest.java +++ b/service/src/test/java/greencity/mapping/CustomHabitMapperTest.java @@ -1,7 +1,7 @@ package greencity.mapping; import greencity.ModelUtils; -import greencity.dto.habit.AddCustomHabitDtoRequest; +import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; import greencity.entity.Habit; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -17,7 +17,7 @@ class CustomHabitMapperTest { @Test void convertTest() { - AddCustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequest(); + AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequest(); Habit expected = Habit.builder() .complexity(addCustomHabitDtoRequest.getComplexity()) diff --git a/service/src/test/java/greencity/service/HabitServiceImplTest.java b/service/src/test/java/greencity/service/HabitServiceImplTest.java index e29660a82f..4ada85faf0 100644 --- a/service/src/test/java/greencity/service/HabitServiceImplTest.java +++ b/service/src/test/java/greencity/service/HabitServiceImplTest.java @@ -3,8 +3,8 @@ import greencity.ModelUtils; import greencity.constant.ErrorMessage; import greencity.dto.PageableDto; -import greencity.dto.habit.AddCustomHabitDtoRequest; -import greencity.dto.habit.AddCustomHabitDtoResponse; +import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; +import greencity.dto.habit.AddUpdateCustomHabitDtoResponse; import greencity.dto.habit.HabitDto; import greencity.dto.habittranslation.HabitTranslationDto; import greencity.dto.shoppinglistitem.CustomShoppingListItemResponseDto; @@ -18,7 +18,9 @@ import greencity.entity.Tag; import greencity.entity.User; import greencity.entity.localization.ShoppingListItemTranslation; +import greencity.enums.Role; import greencity.exception.exceptions.NotFoundException; +import greencity.exception.exceptions.UserHasNoPermissionToAccessException; import greencity.exception.exceptions.WrongEmailException; import greencity.mapping.CustomShoppingListResponseDtoMapper; import greencity.mapping.HabitTranslationDtoMapper; @@ -435,9 +437,10 @@ void addCustomHabitTestWithImagePathInDto() throws IOException { ModelUtils.getCustomShoppingListItemResponseDtoForServiceTest(); CustomShoppingListItem customShoppingListItem = ModelUtils.getCustomShoppingListItemForServiceTest(); - AddCustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); + AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); addCustomHabitDtoRequest.setImage(imageToEncode); - AddCustomHabitDtoResponse addCustomHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); + AddUpdateCustomHabitDtoResponse addCustomHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); addCustomHabitDtoResponse.setImage(imageToEncode); HabitTranslationDto habitTranslationDto = ModelUtils.getHabitTranslationDto(); @@ -461,7 +464,7 @@ void addCustomHabitTestWithImagePathInDto() throws IOException { when(customShoppingListItemRepo.findAllByUserIdAndHabitId(1L, 1L)).thenReturn(List.of(customShoppingListItem)); when(customShoppingListMapper.mapAllToList(List.of(customShoppingListItemResponseDto))) .thenReturn(List.of(customShoppingListItem)); - when(modelMapper.map(habit, AddCustomHabitDtoResponse.class)).thenReturn(addCustomHabitDtoResponse); + when(modelMapper.map(habit, AddUpdateCustomHabitDtoResponse.class)).thenReturn(addCustomHabitDtoResponse); when(customShoppingListResponseDtoMapper.mapAllToList(List.of(customShoppingListItem))) .thenReturn(List.of(customShoppingListItemResponseDto)); when(habitTranslationRepo.findAllByHabit(habit)).thenReturn(habitTranslationList); @@ -480,7 +483,7 @@ void addCustomHabitTestWithImagePathInDto() throws IOException { verify(languageRepo, times(2)).findByCode(anyString()); verify(customShoppingListItemRepo).findAllByUserIdAndHabitId(1L, 1L); verify(customShoppingListMapper).mapAllToList(anyList()); - verify(modelMapper).map(habit, AddCustomHabitDtoResponse.class); + verify(modelMapper).map(habit, AddUpdateCustomHabitDtoResponse.class); verify(customShoppingListResponseDtoMapper).mapAllToList(List.of(customShoppingListItem)); verify(habitTranslationRepo).findAllByHabit(habit); verify(habitTranslationDtoMapper).mapAllToList(habitTranslationList); @@ -503,8 +506,9 @@ void addCustomHabitTestWithImageFile() throws IOException { ModelUtils.getCustomShoppingListItemResponseDtoForServiceTest(); CustomShoppingListItem customShoppingListItem = ModelUtils.getCustomShoppingListItemForServiceTest(); - AddCustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); - AddCustomHabitDtoResponse addCustomHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); + AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); + AddUpdateCustomHabitDtoResponse addCustomHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); addCustomHabitDtoResponse.setImage(imageToEncode); HabitTranslationDto habitTranslationDto = ModelUtils.getHabitTranslationDto(); @@ -528,7 +532,7 @@ void addCustomHabitTestWithImageFile() throws IOException { when(customShoppingListItemRepo.findAllByUserIdAndHabitId(1L, 1L)).thenReturn(List.of(customShoppingListItem)); when(customShoppingListMapper.mapAllToList(List.of(customShoppingListItemResponseDto))) .thenReturn(List.of(customShoppingListItem)); - when(modelMapper.map(habit, AddCustomHabitDtoResponse.class)).thenReturn(addCustomHabitDtoResponse); + when(modelMapper.map(habit, AddUpdateCustomHabitDtoResponse.class)).thenReturn(addCustomHabitDtoResponse); when(customShoppingListResponseDtoMapper.mapAllToList(List.of(customShoppingListItem))) .thenReturn(List.of(customShoppingListItemResponseDto)); when(habitTranslationRepo.findAllByHabit(habit)).thenReturn(habitTranslationList); @@ -547,7 +551,7 @@ void addCustomHabitTestWithImageFile() throws IOException { verify(languageRepo, times(2)).findByCode(anyString()); verify(customShoppingListItemRepo).findAllByUserIdAndHabitId(1L, 1L); verify(customShoppingListMapper).mapAllToList(anyList()); - verify(modelMapper).map(habit, AddCustomHabitDtoResponse.class); + verify(modelMapper).map(habit, AddUpdateCustomHabitDtoResponse.class); verify(customShoppingListResponseDtoMapper).mapAllToList(List.of(customShoppingListItem)); verify(habitTranslationRepo).findAllByHabit(habit); verify(habitTranslationDtoMapper).mapAllToList(habitTranslationList); @@ -570,8 +574,9 @@ void addCustomHabitTest2() throws IOException { ModelUtils.getCustomShoppingListItemResponseDtoForServiceTest(); CustomShoppingListItem customShoppingListItem = ModelUtils.getCustomShoppingListItemForServiceTest(); - AddCustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); - AddCustomHabitDtoResponse addCustomHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); + AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); + AddUpdateCustomHabitDtoResponse addCustomHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); addCustomHabitDtoResponse.setImage(imageToEncode); HabitTranslationDto habitTranslationDto = ModelUtils.getHabitTranslationDto(); @@ -595,7 +600,7 @@ void addCustomHabitTest2() throws IOException { when(customShoppingListItemRepo.findAllByUserIdAndHabitId(1L, 1L)).thenReturn(List.of(customShoppingListItem)); when(customShoppingListMapper.mapAllToList(List.of(customShoppingListItemResponseDto))) .thenReturn(List.of(customShoppingListItem)); - when(modelMapper.map(habit, AddCustomHabitDtoResponse.class)).thenReturn(addCustomHabitDtoResponse); + when(modelMapper.map(habit, AddUpdateCustomHabitDtoResponse.class)).thenReturn(addCustomHabitDtoResponse); when(customShoppingListResponseDtoMapper.mapAllToList(List.of(customShoppingListItem))) .thenReturn(List.of(customShoppingListItemResponseDto)); when(habitTranslationRepo.findAllByHabit(habit)).thenReturn(habitTranslationList); @@ -614,7 +619,7 @@ void addCustomHabitTest2() throws IOException { verify(languageRepo, times(2)).findByCode(anyString()); verify(customShoppingListItemRepo).findAllByUserIdAndHabitId(1L, 1L); verify(customShoppingListMapper).mapAllToList(anyList()); - verify(modelMapper).map(habit, AddCustomHabitDtoResponse.class); + verify(modelMapper).map(habit, AddUpdateCustomHabitDtoResponse.class); verify(customShoppingListResponseDtoMapper).mapAllToList(List.of(customShoppingListItem)); verify(habitTranslationRepo).findAllByHabit(habit); verify(habitTranslationDtoMapper).mapAllToList(habitTranslationList); @@ -630,7 +635,8 @@ void addCustomHabitNoSuchElementExceptionWithNotExistingLanguageCodeTestUa() thr habit.setTags(Set.of(tag)); habit.setUserId(1L); habit.setImage(imageToEncode); - AddCustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); + AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); addCustomHabitDtoRequest.setImage(imageToEncode); HabitTranslationDto habitTranslationDto = ModelUtils.getHabitTranslationDto(); habitTranslationDto.setLanguageCode("ua"); @@ -665,7 +671,8 @@ void addCustomHabitNoSuchElementExceptionWithNotExistingLanguageCodeEn() throws habit.setTags(Set.of(tag)); habit.setUserId(1L); habit.setImage(imageToEncode); - AddCustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); + AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); HabitTranslationDto habitTranslationDto = ModelUtils.getHabitTranslationDto(); habitTranslationDto.setLanguageCode("ua"); HabitTranslation habitTranslationUa = ModelUtils.getHabitTranslationForServiceTest(); @@ -692,7 +699,8 @@ void addCustomHabitNoSuchElementExceptionWithNotExistingLanguageCodeEn() throws @Test void addCustomHabitThrowUserNotFoundException() { - AddCustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); + AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); MultipartFile image = ModelUtils.getFile(); when(userRepo.findByEmail("user@gmail.com")).thenReturn(Optional.empty()); when(habitRepo.save(customHabitMapper.convert(addCustomHabitDtoRequest))).thenReturn(nullable(Habit.class)); @@ -769,4 +777,143 @@ void getFriendsAssignedToHabitProfilePicturesWhenHabitNotFoundTest() { verify(userRepo, never()).getFriendsAssignedToHabit(anyLong(), anyLong()); verify(modelMapper, never()).map(any(), any()); } + + @Test + void updateCustomHabitTest() throws IOException { + User user = ModelUtils.getUser(); + Tag tag = ModelUtils.getTagHabitForServiceTest(); + Language languageUa = ModelUtils.getLanguageUa(); + Language languageEn = ModelUtils.getLanguage(); + Habit habit = ModelUtils.getCustomHabitForServiceTest(); + MultipartFile image = ModelUtils.getFile(); + String imageToEncode = Base64.getEncoder().encodeToString(image.getBytes()); + habit.setTags(Set.of(tag)); + habit.setUserId(1L); + habit.setImage(imageToEncode); + CustomShoppingListItemResponseDto customShoppingListItemResponseDto = + ModelUtils.getCustomShoppingListItemResponseDtoForServiceTest(); + CustomShoppingListItem customShoppingListItem = ModelUtils.getCustomShoppingListItemForServiceTest(); + + AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + ModelUtils.getAddCustomHabitDtoRequestWithImage(); + AddUpdateCustomHabitDtoResponse addCustomHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); + addCustomHabitDtoResponse.setImage(imageToEncode); + + HabitTranslationDto habitTranslationDto = ModelUtils.getHabitTranslationDto(); + + List habitTranslationDtoList = List.of( + habitTranslationDto.setLanguageCode("en"), + habitTranslationDto.setLanguageCode("ua")); + + HabitTranslation habitTranslationUa = ModelUtils.getHabitTranslationForServiceTest(); + List habitTranslationList = List.of( + habitTranslationUa.setLanguage(languageEn), + habitTranslationUa.setLanguage(languageUa)); + + when(userRepo.findByEmail(user.getEmail())).thenReturn(Optional.of(user)); + when(habitRepo.findById(1L)).thenReturn(Optional.of(habit)); + when(habitRepo.save(customHabitMapper.convert(addCustomHabitDtoRequest))).thenReturn(habit); + when(tagsRepo.findById(20L)).thenReturn(Optional.of(tag)); + when(habitTranslationMapper.mapAllToList(List.of(habitTranslationDto))) + .thenReturn(List.of(habitTranslationUa)); + when(languageRepo.findByCode("ua")).thenReturn(Optional.of(languageUa)); + when(languageRepo.findByCode("en")).thenReturn(Optional.of(languageEn)); + when(customShoppingListItemRepo.findAllByUserIdAndHabitId(anyLong(), anyLong())) + .thenReturn(List.of(customShoppingListItem)); + when(customShoppingListMapper.mapAllToList(List.of(customShoppingListItemResponseDto))) + .thenReturn(List.of(customShoppingListItem)); + when(modelMapper.map(habit, AddUpdateCustomHabitDtoResponse.class)).thenReturn(addCustomHabitDtoResponse); + when(habitTranslationRepo.findAllByHabit(habit)).thenReturn(habitTranslationList); + when(habitTranslationDtoMapper.mapAllToList(habitTranslationList)).thenReturn(habitTranslationDtoList); + when(habitRepo.save(habit)).thenReturn(habit); + when(fileService.upload(image)).thenReturn(imageToEncode); + + assertEquals(addCustomHabitDtoResponse, + habitService.updateCustomHabit(addCustomHabitDtoRequest, 1L, "taras@gmail.com", image)); + + verify(habitRepo).findById(anyLong()); + verify(userRepo).findByEmail(user.getEmail()); + verify(habitRepo).save(any()); + verify(customHabitMapper).convert(addCustomHabitDtoRequest); + verify(tagsRepo).findById(20L); + verify(habitTranslationMapper, times(3)).mapAllToList(List.of(habitTranslationDto)); + verify(languageRepo, times(2)).findByCode(anyString()); + verify(customShoppingListItemRepo).findAllByUserIdAndHabitId(anyLong(), anyLong()); + verify(customShoppingListMapper).mapAllToList(anyList()); + verify(modelMapper).map(habit, AddUpdateCustomHabitDtoResponse.class); + verify(customShoppingListResponseDtoMapper).mapAllToList(List.of(customShoppingListItem)); + verify(habitTranslationRepo).findAllByHabit(habit); + verify(habitTranslationDtoMapper).mapAllToList(habitTranslationList); + } + + @Test + void updateCustomHabitThrowsUserNotFoundException() { + AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); + when(userRepo.findByEmail("user@gmail.com")).thenReturn(Optional.empty()); + when(habitRepo.save(customHabitMapper.convert(addCustomHabitDtoRequest))).thenReturn(nullable(Habit.class)); + + assertThrows(WrongEmailException.class, + () -> habitService.updateCustomHabit(addCustomHabitDtoRequest, 1L, "user@gmail.com", null)); + + verify(userRepo).findByEmail("user@gmail.com"); + verify(customHabitMapper).convert(addCustomHabitDtoRequest); + } + + @Test + void updateCustomHabitNoSuchElementExceptionWithNotExistingLanguageCodes() throws IOException { + User user = ModelUtils.getUser(); + user.setRole(Role.ROLE_MODERATOR); + Tag tag = ModelUtils.getTagHabitForServiceTest(); + Language languageUa = ModelUtils.getLanguageUa(); + Habit habit = ModelUtils.getCustomHabitForServiceTest(); + MultipartFile image = ModelUtils.getFile(); + String imageToEncode = Base64.getEncoder().encodeToString(image.getBytes()); + habit.setTags(Set.of(tag)); + habit.setUserId(1L); + habit.setImage(imageToEncode); + AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); + addCustomHabitDtoRequest.setImage(ModelUtils.HABIT_DEFAULT_IMAGE); + + HabitTranslationDto habitTranslationDto = ModelUtils.getHabitTranslationDto(); + habitTranslationDto.setLanguageCode("ua"); + HabitTranslation habitTranslationUa = ModelUtils.getHabitTranslationForServiceTest(); + + when(habitRepo.findById(1L)).thenReturn(Optional.of(habit)); + when(userRepo.findByEmail(user.getEmail())).thenReturn(Optional.of(user)); + when(habitTranslationMapper.mapAllToList(List.of(habitTranslationDto))) + .thenReturn(List.of(habitTranslationUa)); + when(languageRepo.findByCode("ua")).thenReturn(Optional.of(languageUa)); + when(languageRepo.findByCode("en")).thenReturn(Optional.empty()); + + assertThrows(NoSuchElementException.class, () -> habitService.updateCustomHabit( + addCustomHabitDtoRequest, 1L, "taras@gmail.com", image)); + + verify(habitRepo).findById(anyLong()); + verify(userRepo).findByEmail(user.getEmail()); + verify(habitTranslationMapper, times(3)) + .mapAllToList(addCustomHabitDtoRequest.getHabitTranslations()); + verify(languageRepo, times(2)).findByCode(anyString()); + } + + @Test + void updateCustomHabitThrowsUserHasNoPermissionToAccessException() { + AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + ModelUtils.getAddCustomHabitDtoRequestWithImage(); + User user = ModelUtils.getUser(); + String email = user.getEmail(); + user.setRole(Role.ROLE_USER); + + Habit habit = ModelUtils.getCustomHabitForServiceTest(); + + when(habitRepo.findById(habit.getId())).thenReturn(Optional.of(habit)); + when(userRepo.findByEmail(email)).thenReturn(Optional.of(user)); + + assertThrows(UserHasNoPermissionToAccessException.class, + () -> habitService.updateCustomHabit(addCustomHabitDtoRequest, 1L, email, null)); + + verify(userRepo).findByEmail(anyString()); + verify(habitRepo).findById(anyLong()); + } } From 1edc4d2c89b6fff2acd922857abafea610743616 Mon Sep 17 00:00:00 2001 From: Lena Sotnik Date: Tue, 19 Sep 2023 08:02:43 -0700 Subject: [PATCH 02/10] Removed asterisk from imports in HabitController --- .../java/greencity/controller/HabitController.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/greencity/controller/HabitController.java b/core/src/main/java/greencity/controller/HabitController.java index 02107c5fb7..3d9fae1485 100644 --- a/core/src/main/java/greencity/controller/HabitController.java +++ b/core/src/main/java/greencity/controller/HabitController.java @@ -34,7 +34,16 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import springfox.documentation.annotations.ApiIgnore; From f68c6a4686e7ef92c1ed47bea698015462cf6a66 Mon Sep 17 00:00:00 2001 From: Lena Sotnik Date: Wed, 20 Sep 2023 00:09:55 -0700 Subject: [PATCH 03/10] Commit with rename of custom habit dto --- .../greencity/controller/HabitController.java | 26 +++++----- core/src/test/java/greencity/ModelUtils.java | 6 +-- .../controller/HabitControllerTest.java | 6 +-- ...equest.java => CustomHabitDtoRequest.java} | 2 +- ...ponse.java => CustomHabitDtoResponse.java} | 2 +- .../java/greencity/service/HabitService.java | 26 +++++----- .../src/test/java/greencity/ModelUtils.java | 12 ++--- ...st.java => CustomHabitDtoRequestTest.java} | 26 +++++----- ...t.java => CustomHabitDtoResponseTest.java} | 22 ++++----- .../greencity/mapping/CustomHabitMapper.java | 6 +-- .../greencity/service/HabitServiceImpl.java | 30 ++++++------ .../src/test/java/greencity/ModelUtils.java | 20 ++++---- .../mapping/CustomHabitMapperTest.java | 4 +- .../service/HabitServiceImplTest.java | 48 +++++++++---------- 14 files changed, 118 insertions(+), 118 deletions(-) rename service-api/src/main/java/greencity/dto/habit/{AddUpdateCustomHabitDtoRequest.java => CustomHabitDtoRequest.java} (96%) rename service-api/src/main/java/greencity/dto/habit/{AddUpdateCustomHabitDtoResponse.java => CustomHabitDtoResponse.java} (96%) rename service-api/src/test/java/greencity/dto/habit/{AddUpdateCustomHabitDtoRequestTest.java => CustomHabitDtoRequestTest.java} (81%) rename service-api/src/test/java/greencity/dto/habit/{AddUpdateCustomHabitDtoResponseTest.java => CustomHabitDtoResponseTest.java} (82%) diff --git a/core/src/main/java/greencity/controller/HabitController.java b/core/src/main/java/greencity/controller/HabitController.java index 3d9fae1485..d6e641d9be 100644 --- a/core/src/main/java/greencity/controller/HabitController.java +++ b/core/src/main/java/greencity/controller/HabitController.java @@ -7,8 +7,8 @@ import greencity.annotations.ValidLanguage; import greencity.constant.HttpStatuses; import greencity.dto.PageableDto; -import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; -import greencity.dto.habit.AddUpdateCustomHabitDtoResponse; +import greencity.dto.habit.CustomHabitDtoRequest; +import greencity.dto.habit.CustomHabitDtoResponse; import greencity.dto.shoppinglistitem.ShoppingListItemDto; import greencity.dto.habit.HabitDto; import greencity.dto.habit.HabitVO; @@ -216,23 +216,23 @@ public ResponseEntity> findAllHabitsTags(@ApiIgnore @ValidLanguage /** * Method for creating Custom Habit. * - * @param request {@link AddUpdateCustomHabitDtoRequest} - new custom habit dto. - * @return dto {@link AddUpdateCustomHabitDtoResponse} + * @param request {@link CustomHabitDtoRequest} - new custom habit dto. + * @return dto {@link CustomHabitDtoResponse} * * @author Lilia Mokhnatska. */ @ApiOperation(value = "Add new custom habit.") @ResponseStatus(value = HttpStatus.CREATED) @ApiResponses(value = { - @ApiResponse(code = 201, message = HttpStatuses.CREATED, response = AddUpdateCustomHabitDtoResponse.class), + @ApiResponse(code = 201, message = HttpStatuses.CREATED, response = CustomHabitDtoResponse.class), @ApiResponse(code = 400, message = HttpStatuses.BAD_REQUEST), @ApiResponse(code = 401, message = HttpStatuses.UNAUTHORIZED), @ApiResponse(code = 404, message = HttpStatuses.NOT_FOUND), }) @PostMapping(value = "/custom", consumes = {MediaType.APPLICATION_JSON_UTF8_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE}) - public ResponseEntity addCustomHabit( - @RequestPart @Valid AddUpdateCustomHabitDtoRequest request, + public ResponseEntity addCustomHabit( + @RequestPart @Valid CustomHabitDtoRequest request, @ApiParam(value = "Image of habit") @ImageValidation @RequestPart(required = false) MultipartFile image, @ApiIgnore Principal principal) { return ResponseEntity @@ -268,23 +268,23 @@ public ResponseEntity> getFriendsAssignedToHabitProf /** * Method for updating Custom Habit. * - * @param request {@link AddUpdateCustomHabitDtoRequest} - custom habit dto. - * @return dto {@link AddUpdateCustomHabitDtoResponse} + * @param request {@link CustomHabitDtoRequest} - custom habit dto. + * @return dto {@link CustomHabitDtoResponse} * * @author Olena Sotnik. */ @ApiOperation(value = "Update new custom habit.") @ApiResponses(value = { - @ApiResponse(code = 200, message = HttpStatuses.OK, response = AddUpdateCustomHabitDtoResponse.class), + @ApiResponse(code = 200, message = HttpStatuses.OK, response = CustomHabitDtoResponse.class), @ApiResponse(code = 400, message = HttpStatuses.BAD_REQUEST), @ApiResponse(code = 401, message = HttpStatuses.UNAUTHORIZED), @ApiResponse(code = 403, message = HttpStatuses.FORBIDDEN), @ApiResponse(code = 404, message = HttpStatuses.NOT_FOUND) }) @PutMapping(value = "/update/{habitId}") - public ResponseEntity updateCustomHabit(@PathVariable Long habitId, - @RequestBody @Valid AddUpdateCustomHabitDtoRequest request, @ApiIgnore Principal principal, - @ApiParam(value = "Image of habit") @ImageValidation @RequestPart(required = false) MultipartFile image) { + public ResponseEntity updateCustomHabit(@PathVariable Long habitId, + @RequestBody @Valid CustomHabitDtoRequest request, @ApiIgnore Principal principal, + @ApiParam(value = "Image of habit") @ImageValidation @RequestPart(required = false) MultipartFile image) { return ResponseEntity.status(HttpStatus.OK) .body(habitService.updateCustomHabit(request, habitId, principal.getName(), image)); } diff --git a/core/src/test/java/greencity/ModelUtils.java b/core/src/test/java/greencity/ModelUtils.java index 9a8db3a23c..fb4bcf7b5e 100644 --- a/core/src/test/java/greencity/ModelUtils.java +++ b/core/src/test/java/greencity/ModelUtils.java @@ -31,7 +31,7 @@ import greencity.dto.habit.HabitAssignPropertiesDto; import greencity.dto.habit.HabitVO; import greencity.dto.habit.UpdateUserShoppingListDto; -import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; +import greencity.dto.habit.CustomHabitDtoRequest; import greencity.dto.habit.UserShoppingAndCustomShoppingListsDto; import greencity.dto.habitfact.HabitFactPostDto; import greencity.dto.habitfact.HabitFactTranslationUpdateDto; @@ -738,8 +738,8 @@ public static AddEventDtoRequest getAddEventDtoRequest() { .build())).tags(List.of("first", "second", "third")).build(); } - public static AddUpdateCustomHabitDtoRequest getAddCustomHabitDtoRequest() { - return AddUpdateCustomHabitDtoRequest.builder() + public static CustomHabitDtoRequest getAddCustomHabitDtoRequest() { + return CustomHabitDtoRequest.builder() .complexity(2) .customShoppingListItemDto(List.of( CustomShoppingListItemResponseDto.builder() diff --git a/core/src/test/java/greencity/controller/HabitControllerTest.java b/core/src/test/java/greencity/controller/HabitControllerTest.java index 004e432632..4a61091139 100644 --- a/core/src/test/java/greencity/controller/HabitControllerTest.java +++ b/core/src/test/java/greencity/controller/HabitControllerTest.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import greencity.ModelUtils; -import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; +import greencity.dto.habit.CustomHabitDtoRequest; import greencity.dto.user.UserVO; import greencity.exception.handler.CustomExceptionHandler; import greencity.service.HabitService; @@ -282,7 +282,7 @@ void getShoppingListItems() throws Exception { @Test void postCustomHabit() throws Exception { - AddUpdateCustomHabitDtoRequest dto = ModelUtils.getAddCustomHabitDtoRequest(); + CustomHabitDtoRequest dto = ModelUtils.getAddCustomHabitDtoRequest(); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.findAndRegisterModules(); @@ -312,7 +312,7 @@ void getFriendsAssignedToHabitProfilePictures() throws Exception { @Test void updateCustomHabit() throws Exception { Long habitId = 1L; - AddUpdateCustomHabitDtoRequest dto = ModelUtils.getAddCustomHabitDtoRequest(); + CustomHabitDtoRequest dto = ModelUtils.getAddCustomHabitDtoRequest(); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.findAndRegisterModules(); diff --git a/service-api/src/main/java/greencity/dto/habit/AddUpdateCustomHabitDtoRequest.java b/service-api/src/main/java/greencity/dto/habit/CustomHabitDtoRequest.java similarity index 96% rename from service-api/src/main/java/greencity/dto/habit/AddUpdateCustomHabitDtoRequest.java rename to service-api/src/main/java/greencity/dto/habit/CustomHabitDtoRequest.java index 226d23c1d3..6bc7838b8f 100644 --- a/service-api/src/main/java/greencity/dto/habit/AddUpdateCustomHabitDtoRequest.java +++ b/service-api/src/main/java/greencity/dto/habit/CustomHabitDtoRequest.java @@ -24,7 +24,7 @@ @Getter @Setter @EqualsAndHashCode -public class AddUpdateCustomHabitDtoRequest { +public class CustomHabitDtoRequest { @Min(value = 1, message = ServiceValidationConstants.HABIT_COMPLEXITY) @Max(value = 3, message = ServiceValidationConstants.HABIT_COMPLEXITY) @NotNull(message = ServiceValidationConstants.HABIT_COMPLEXITY) diff --git a/service-api/src/main/java/greencity/dto/habit/AddUpdateCustomHabitDtoResponse.java b/service-api/src/main/java/greencity/dto/habit/CustomHabitDtoResponse.java similarity index 96% rename from service-api/src/main/java/greencity/dto/habit/AddUpdateCustomHabitDtoResponse.java rename to service-api/src/main/java/greencity/dto/habit/CustomHabitDtoResponse.java index 617f15e2ba..9105f142d0 100644 --- a/service-api/src/main/java/greencity/dto/habit/AddUpdateCustomHabitDtoResponse.java +++ b/service-api/src/main/java/greencity/dto/habit/CustomHabitDtoResponse.java @@ -24,7 +24,7 @@ @Getter @Setter @EqualsAndHashCode -public class AddUpdateCustomHabitDtoResponse { +public class CustomHabitDtoResponse { private Long id; private Long userId; private String image; diff --git a/service-api/src/main/java/greencity/service/HabitService.java b/service-api/src/main/java/greencity/service/HabitService.java index d4958e0b08..dae2ea47eb 100644 --- a/service-api/src/main/java/greencity/service/HabitService.java +++ b/service-api/src/main/java/greencity/service/HabitService.java @@ -1,8 +1,8 @@ package greencity.service; import greencity.dto.PageableDto; -import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; -import greencity.dto.habit.AddUpdateCustomHabitDtoResponse; +import greencity.dto.habit.CustomHabitDtoRequest; +import greencity.dto.habit.CustomHabitDtoResponse; import greencity.dto.habit.HabitVO; import greencity.dto.shoppinglistitem.ShoppingListItemDto; import greencity.dto.habit.HabitDto; @@ -110,18 +110,18 @@ PageableDto getAllByDifferentParameters(UserVO userVO, Pageable pageab List addAllShoppingListItemsByListOfId(Long habitId, List listId); /** - * Method to save {@link AddUpdateCustomHabitDtoResponse}. + * Method to save {@link CustomHabitDtoResponse}. * * @param addCustomHabitDtoRequest dto with - * {@link AddUpdateCustomHabitDtoRequest} + * {@link CustomHabitDtoRequest} * entered info about field that need to edit. * @param userEmail {@link String} - user email. - * @return {@link AddUpdateCustomHabitDtoResponse} instance. + * @return {@link CustomHabitDtoResponse} instance. * @author Lilia Mokhnatska */ - AddUpdateCustomHabitDtoResponse addCustomHabit(AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest, - MultipartFile image, - String userEmail); + CustomHabitDtoResponse addCustomHabit(CustomHabitDtoRequest addCustomHabitDtoRequest, + MultipartFile image, + String userEmail); /** * Retrieves the list of profile pictures of the user's friends (which have @@ -134,14 +134,14 @@ AddUpdateCustomHabitDtoResponse addCustomHabit(AddUpdateCustomHabitDtoRequest ad List getFriendsAssignedToHabitProfilePictures(Long habitId, Long userId); /** - * Method to update {@link AddUpdateCustomHabitDtoResponse}. + * Method to update {@link CustomHabitDtoResponse}. * - * @param customHabitDtoRequest dto with {@link AddUpdateCustomHabitDtoRequest} + * @param customHabitDtoRequest dto with {@link CustomHabitDtoRequest} * entered info about field that need to edit. * @param userEmail {@link String} - user email. - * @return {@link AddUpdateCustomHabitDtoResponse} instance. + * @return {@link CustomHabitDtoResponse} instance. * @author Olena Sotnik. */ - AddUpdateCustomHabitDtoResponse updateCustomHabit(AddUpdateCustomHabitDtoRequest customHabitDtoRequest, - Long habitId, String userEmail, MultipartFile image); + CustomHabitDtoResponse updateCustomHabit(CustomHabitDtoRequest customHabitDtoRequest, + Long habitId, String userEmail, MultipartFile image); } diff --git a/service-api/src/test/java/greencity/ModelUtils.java b/service-api/src/test/java/greencity/ModelUtils.java index 88cb4aff62..6c6b44820b 100644 --- a/service-api/src/test/java/greencity/ModelUtils.java +++ b/service-api/src/test/java/greencity/ModelUtils.java @@ -7,8 +7,8 @@ import greencity.dto.event.EventDto; import greencity.dto.eventcomment.EventCommentAuthorDto; import greencity.dto.eventcomment.EventCommentForSendEmailDto; -import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; -import greencity.dto.habit.AddUpdateCustomHabitDtoResponse; +import greencity.dto.habit.CustomHabitDtoRequest; +import greencity.dto.habit.CustomHabitDtoResponse; import greencity.dto.habit.UserShoppingAndCustomShoppingListsDto; import greencity.dto.newssubscriber.NewsSubscriberResponseDto; import greencity.dto.place.PlaceNotificationDto; @@ -193,8 +193,8 @@ public static UserShoppingAndCustomShoppingListsDto getUserShoppingAndCustomShop .build(); } - public static AddUpdateCustomHabitDtoRequest getAddCustomHabitDtoRequest() { - return AddUpdateCustomHabitDtoRequest.builder() + public static CustomHabitDtoRequest getAddCustomHabitDtoRequest() { + return CustomHabitDtoRequest.builder() .complexity(1) .image("") .defaultDuration(14) @@ -202,8 +202,8 @@ public static AddUpdateCustomHabitDtoRequest getAddCustomHabitDtoRequest() { .build(); } - public static AddUpdateCustomHabitDtoResponse getAddCustomHabitDtoResponse() { - return AddUpdateCustomHabitDtoResponse.builder() + public static CustomHabitDtoResponse getAddCustomHabitDtoResponse() { + return CustomHabitDtoResponse.builder() .id(1L) .complexity(1) .image("") diff --git a/service-api/src/test/java/greencity/dto/habit/AddUpdateCustomHabitDtoRequestTest.java b/service-api/src/test/java/greencity/dto/habit/CustomHabitDtoRequestTest.java similarity index 81% rename from service-api/src/test/java/greencity/dto/habit/AddUpdateCustomHabitDtoRequestTest.java rename to service-api/src/test/java/greencity/dto/habit/CustomHabitDtoRequestTest.java index e8c8cfca55..798a24d65d 100644 --- a/service-api/src/test/java/greencity/dto/habit/AddUpdateCustomHabitDtoRequestTest.java +++ b/service-api/src/test/java/greencity/dto/habit/CustomHabitDtoRequestTest.java @@ -16,20 +16,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -class AddUpdateCustomHabitDtoRequestTest { +class CustomHabitDtoRequestTest { @SneakyThrows @ParameterizedTest @MethodSource("provideFieldsAndValidValues") void validComplexityInAddCustomHabitDtoRequestTest(Integer complexity) { - var dto = AddUpdateCustomHabitDtoRequest.builder() + var dto = CustomHabitDtoRequest.builder() .complexity(complexity) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertTrue(constraintViolations.isEmpty()); @@ -39,14 +39,14 @@ void validComplexityInAddCustomHabitDtoRequestTest(Integer complexity) { @ParameterizedTest @MethodSource("provideFieldsAndInvalidValues") void invalidComplexityInAddCustomHabitDtoRequestTest(Integer complexity) { - var dto = AddUpdateCustomHabitDtoRequest.builder() + var dto = CustomHabitDtoRequest.builder() .complexity(complexity) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(1, constraintViolations.size()); @@ -54,14 +54,14 @@ void invalidComplexityInAddCustomHabitDtoRequestTest(Integer complexity) { @Test void invalidComplexityIsNullInAddCustomHabitDtoRequestTest() { - var dto = AddUpdateCustomHabitDtoRequest.builder() + var dto = CustomHabitDtoRequest.builder() .complexity(null) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(1, constraintViolations.size()); @@ -85,14 +85,14 @@ private static Stream provideFieldsAndInvalidValues() { @Test void invalidNumberOfTagsIdNullInAddCustomHabitDtoRequestTest() { - var dto = AddUpdateCustomHabitDtoRequest.builder() + var dto = CustomHabitDtoRequest.builder() .tagIds(null) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(1, constraintViolations.size()); @@ -102,14 +102,14 @@ void invalidNumberOfTagsIdNullInAddCustomHabitDtoRequestTest() { @ParameterizedTest @MethodSource("provideFieldsAndValidValuesForTagsIds") void validNumberOfTagsIdsInAddCustomHabitDtoRequestTest(Set tagIds) { - var dto = AddUpdateCustomHabitDtoRequest.builder() + var dto = CustomHabitDtoRequest.builder() .tagIds(tagIds) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(1, constraintViolations.size()); @@ -119,14 +119,14 @@ void validNumberOfTagsIdsInAddCustomHabitDtoRequestTest(Set tagIds) { @ParameterizedTest @MethodSource("provideFieldsAndInvalidValuesForTagsIds") void invalidNumberOfTagsIdsInAddCustomHabitDtoRequestTest(Set tagIds) { - var dto = AddUpdateCustomHabitDtoRequest.builder() + var dto = CustomHabitDtoRequest.builder() .tagIds(tagIds) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(2, constraintViolations.size()); diff --git a/service-api/src/test/java/greencity/dto/habit/AddUpdateCustomHabitDtoResponseTest.java b/service-api/src/test/java/greencity/dto/habit/CustomHabitDtoResponseTest.java similarity index 82% rename from service-api/src/test/java/greencity/dto/habit/AddUpdateCustomHabitDtoResponseTest.java rename to service-api/src/test/java/greencity/dto/habit/CustomHabitDtoResponseTest.java index 63244c29fc..8cfa097d20 100644 --- a/service-api/src/test/java/greencity/dto/habit/AddUpdateCustomHabitDtoResponseTest.java +++ b/service-api/src/test/java/greencity/dto/habit/CustomHabitDtoResponseTest.java @@ -16,20 +16,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -class AddUpdateCustomHabitDtoResponseTest { +class CustomHabitDtoResponseTest { @SneakyThrows @ParameterizedTest @MethodSource("provideFieldsAndValidValues") void validComplexityInAddCustomHabitDtoResponseTest(Integer complexity) { - var dto = AddUpdateCustomHabitDtoResponse.builder() + var dto = CustomHabitDtoResponse.builder() .complexity(complexity) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertTrue(constraintViolations.isEmpty()); @@ -39,14 +39,14 @@ void validComplexityInAddCustomHabitDtoResponseTest(Integer complexity) { @ParameterizedTest @MethodSource("provideFieldsAndInvalidValues") void invalidComplexityInAddCustomHabitDtoResponseTest(Integer complexity) { - var dto = AddUpdateCustomHabitDtoResponse.builder() + var dto = CustomHabitDtoResponse.builder() .complexity(complexity) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(1, constraintViolations.size()); @@ -54,14 +54,14 @@ void invalidComplexityInAddCustomHabitDtoResponseTest(Integer complexity) { @Test void invalidComplexityIsNullInAddCustomHabitDtoResponseTest() { - var dto = AddUpdateCustomHabitDtoResponse.builder() + var dto = CustomHabitDtoResponse.builder() .complexity(null) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(1, constraintViolations.size()); @@ -87,14 +87,14 @@ private static Stream provideFieldsAndInvalidValues() { @ParameterizedTest @MethodSource("provideFieldsAndValidValuesForTagsIds") void validNumberOfTagsIdsInAddCustomHabitDtoResponseTest(Set tagIds) { - var dto = AddUpdateCustomHabitDtoResponse.builder() + var dto = CustomHabitDtoResponse.builder() .tagIds(tagIds) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(1, constraintViolations.size()); @@ -104,14 +104,14 @@ void validNumberOfTagsIdsInAddCustomHabitDtoResponseTest(Set tagIds) { @ParameterizedTest @MethodSource("provideFieldsAndInvalidValuesForTagsIds") void invalidNumberOfTagsIdsInAddCustomHabitDtoResponseTest(Set tagIds) { - var dto = AddUpdateCustomHabitDtoResponse.builder() + var dto = CustomHabitDtoResponse.builder() .tagIds(tagIds) .build(); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); final Validator validator = factory.getValidator(); - Set> constraintViolations = + Set> constraintViolations = validator.validate(dto); assertEquals(2, constraintViolations.size()); diff --git a/service/src/main/java/greencity/mapping/CustomHabitMapper.java b/service/src/main/java/greencity/mapping/CustomHabitMapper.java index 1ccfa2add6..68cd369c1a 100644 --- a/service/src/main/java/greencity/mapping/CustomHabitMapper.java +++ b/service/src/main/java/greencity/mapping/CustomHabitMapper.java @@ -1,14 +1,14 @@ package greencity.mapping; -import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; +import greencity.dto.habit.CustomHabitDtoRequest; import greencity.entity.Habit; import org.modelmapper.AbstractConverter; import org.springframework.stereotype.Component; @Component -public class CustomHabitMapper extends AbstractConverter { +public class CustomHabitMapper extends AbstractConverter { @Override - public Habit convert(AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest) { + public Habit convert(CustomHabitDtoRequest addCustomHabitDtoRequest) { return Habit.builder() .image(addCustomHabitDtoRequest.getImage()) .complexity(addCustomHabitDtoRequest.getComplexity()) diff --git a/service/src/main/java/greencity/service/HabitServiceImpl.java b/service/src/main/java/greencity/service/HabitServiceImpl.java index 25d8b20b8c..ec7c748aaa 100644 --- a/service/src/main/java/greencity/service/HabitServiceImpl.java +++ b/service/src/main/java/greencity/service/HabitServiceImpl.java @@ -3,8 +3,8 @@ import greencity.constant.AppConstant; import greencity.constant.ErrorMessage; import greencity.dto.PageableDto; -import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; -import greencity.dto.habit.AddUpdateCustomHabitDtoResponse; +import greencity.dto.habit.CustomHabitDtoRequest; +import greencity.dto.habit.CustomHabitDtoResponse; import greencity.dto.habit.HabitDto; import greencity.dto.shoppinglistitem.ShoppingListItemDto; import greencity.dto.user.UserProfilePictureDto; @@ -303,8 +303,8 @@ public List addAllShoppingListItemsByListOfId(Long habitId, List lis @Transactional @Override - public AddUpdateCustomHabitDtoResponse addCustomHabit( - AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest, MultipartFile image, String userEmail) { + public CustomHabitDtoResponse addCustomHabit( + CustomHabitDtoRequest addCustomHabitDtoRequest, MultipartFile image, String userEmail) { User user = userRepo.findByEmail(userEmail) .orElseThrow(() -> new WrongEmailException(ErrorMessage.USER_NOT_FOUND_BY_EMAIL + userEmail)); @@ -325,15 +325,15 @@ public AddUpdateCustomHabitDtoResponse addCustomHabit( } /** - * Method that build {@link AddUpdateCustomHabitDtoResponse} from {@link Habit}. + * Method that build {@link CustomHabitDtoResponse} from {@link Habit}. * * @param habit {@link Habit} * @param userId {@link Long} - * @return {@link AddUpdateCustomHabitDtoResponse} + * @return {@link CustomHabitDtoResponse} * @author Lilia Mokhnatska */ - private AddUpdateCustomHabitDtoResponse buildAddCustomHabitDtoResponse(Habit habit, Long userId) { - AddUpdateCustomHabitDtoResponse response = modelMapper.map(habit, AddUpdateCustomHabitDtoResponse.class); + private CustomHabitDtoResponse buildAddCustomHabitDtoResponse(Habit habit, Long userId) { + CustomHabitDtoResponse response = modelMapper.map(habit, CustomHabitDtoResponse.class); response.setCustomShoppingListItemDto(customShoppingListResponseDtoMapper .mapAllToList(customShoppingListItemRepo.findAllByUserIdAndHabitId(userId, habit.getId()))); @@ -361,8 +361,8 @@ public List getFriendsAssignedToHabitProfilePictures(Long @Transactional @Override - public AddUpdateCustomHabitDtoResponse updateCustomHabit(AddUpdateCustomHabitDtoRequest habitDto, - Long habitId, String userEmail, MultipartFile image) { + public CustomHabitDtoResponse updateCustomHabit(CustomHabitDtoRequest habitDto, + Long habitId, String userEmail, MultipartFile image) { User user = userRepo.findByEmail(userEmail) .orElseThrow(() -> new WrongEmailException(ErrorMessage.USER_NOT_FOUND_BY_EMAIL + userEmail)); Habit toUpdate = habitRepo.findById(habitId) @@ -373,7 +373,7 @@ public AddUpdateCustomHabitDtoResponse updateCustomHabit(AddUpdateCustomHabitDto return buildAddCustomHabitDtoResponse(updatedHabit, user.getId()); } - private void enhanceHabitWithNewData(Habit toUpdate, AddUpdateCustomHabitDtoRequest habitDto, + private void enhanceHabitWithNewData(Habit toUpdate, CustomHabitDtoRequest habitDto, User user, MultipartFile image) { if (Objects.nonNull(habitDto.getComplexity())) { toUpdate.setComplexity(habitDto.getComplexity()); @@ -399,7 +399,7 @@ private void enhanceHabitWithNewData(Habit toUpdate, AddUpdateCustomHabitDtoRequ } } - private void saveHabitTranslationListsToHabitTranslationRepo(AddUpdateCustomHabitDtoRequest habitDto, Habit habit) { + private void saveHabitTranslationListsToHabitTranslationRepo(CustomHabitDtoRequest habitDto, Habit habit) { List habitTranslationListForUa = mapHabitTranslationFromAddCustomHabitDtoRequest(habitDto); habitTranslationListForUa.forEach(habitTranslation -> habitTranslation.setHabit(habit)); habitTranslationListForUa.forEach(habitTranslation -> habitTranslation.setLanguage( @@ -414,17 +414,17 @@ private void saveHabitTranslationListsToHabitTranslationRepo(AddUpdateCustomHabi } private List mapHabitTranslationFromAddCustomHabitDtoRequest( - AddUpdateCustomHabitDtoRequest habitDto) { + CustomHabitDtoRequest habitDto) { return habitTranslationMapper.mapAllToList(habitDto.getHabitTranslations()); } - private void setTagsIdsToHabit(AddUpdateCustomHabitDtoRequest habitDto, Habit habit) { + private void setTagsIdsToHabit(CustomHabitDtoRequest habitDto, Habit habit) { habit.setTags(habitDto.getTagIds().stream().map(tagId -> tagsRepo.findById(tagId) .orElseThrow(() -> new NotFoundException(ErrorMessage.TAG_NOT_FOUND + tagId))) .collect(Collectors.toSet())); } - private void setCustomShoppingListItemToHabit(AddUpdateCustomHabitDtoRequest habitDto, Habit habit, User user) { + private void setCustomShoppingListItemToHabit(CustomHabitDtoRequest habitDto, Habit habit, User user) { List customShoppingListItems = customShoppingListMapper.mapAllToList(habitDto.getCustomShoppingListItemDto()); customShoppingListItems.forEach(customShoppingListItem -> customShoppingListItem.setHabit(habit)); diff --git a/service/src/test/java/greencity/ModelUtils.java b/service/src/test/java/greencity/ModelUtils.java index 4f22b07414..350e4ba9d8 100644 --- a/service/src/test/java/greencity/ModelUtils.java +++ b/service/src/test/java/greencity/ModelUtils.java @@ -61,8 +61,8 @@ import greencity.dto.friends.UserFriendDto; import greencity.dto.geocoding.AddressLatLngResponse; import greencity.dto.geocoding.AddressResponse; -import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; -import greencity.dto.habit.AddUpdateCustomHabitDtoResponse; +import greencity.dto.habit.CustomHabitDtoRequest; +import greencity.dto.habit.CustomHabitDtoResponse; import greencity.dto.habit.HabitAssignDto; import greencity.dto.habit.HabitAssignPropertiesDto; import greencity.dto.habit.HabitAssignUserDurationDto; @@ -2801,8 +2801,8 @@ public static UserShoppingAndCustomShoppingListsDto getUserShoppingAndCustomShop .build(); } - public static AddUpdateCustomHabitDtoRequest getAddCustomHabitDtoRequest() { - return AddUpdateCustomHabitDtoRequest.builder() + public static CustomHabitDtoRequest getAddCustomHabitDtoRequest() { + return CustomHabitDtoRequest.builder() .complexity(2) .defaultDuration(7) .build(); @@ -2827,8 +2827,8 @@ public static HabitTranslation getHabitTranslationForServiceTest() { .build(); } - public static AddUpdateCustomHabitDtoRequest getAddCustomHabitDtoRequestForServiceTest() { - return AddUpdateCustomHabitDtoRequest.builder() + public static CustomHabitDtoRequest getAddCustomHabitDtoRequestForServiceTest() { + return CustomHabitDtoRequest.builder() .complexity(2) .customShoppingListItemDto(List.of( CustomShoppingListItemResponseDto.builder() @@ -2848,8 +2848,8 @@ public static AddUpdateCustomHabitDtoRequest getAddCustomHabitDtoRequestForServi .build(); } - public static AddUpdateCustomHabitDtoRequest getAddCustomHabitDtoRequestWithImage() { - return AddUpdateCustomHabitDtoRequest.builder() + public static CustomHabitDtoRequest getAddCustomHabitDtoRequestWithImage() { + return CustomHabitDtoRequest.builder() .complexity(2) .customShoppingListItemDto(List.of( CustomShoppingListItemResponseDto.builder() @@ -2870,8 +2870,8 @@ public static AddUpdateCustomHabitDtoRequest getAddCustomHabitDtoRequestWithImag .build(); } - public static AddUpdateCustomHabitDtoResponse getAddCustomHabitDtoResponse() { - return AddUpdateCustomHabitDtoResponse.builder() + public static CustomHabitDtoResponse getAddCustomHabitDtoResponse() { + return CustomHabitDtoResponse.builder() .id(1L) .complexity(2) .customShoppingListItemDto(List.of( diff --git a/service/src/test/java/greencity/mapping/CustomHabitMapperTest.java b/service/src/test/java/greencity/mapping/CustomHabitMapperTest.java index a33c7006b4..7f5bf8f974 100644 --- a/service/src/test/java/greencity/mapping/CustomHabitMapperTest.java +++ b/service/src/test/java/greencity/mapping/CustomHabitMapperTest.java @@ -1,7 +1,7 @@ package greencity.mapping; import greencity.ModelUtils; -import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; +import greencity.dto.habit.CustomHabitDtoRequest; import greencity.entity.Habit; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -17,7 +17,7 @@ class CustomHabitMapperTest { @Test void convertTest() { - AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequest(); + CustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequest(); Habit expected = Habit.builder() .complexity(addCustomHabitDtoRequest.getComplexity()) diff --git a/service/src/test/java/greencity/service/HabitServiceImplTest.java b/service/src/test/java/greencity/service/HabitServiceImplTest.java index 4ada85faf0..c05bfc2633 100644 --- a/service/src/test/java/greencity/service/HabitServiceImplTest.java +++ b/service/src/test/java/greencity/service/HabitServiceImplTest.java @@ -3,8 +3,8 @@ import greencity.ModelUtils; import greencity.constant.ErrorMessage; import greencity.dto.PageableDto; -import greencity.dto.habit.AddUpdateCustomHabitDtoRequest; -import greencity.dto.habit.AddUpdateCustomHabitDtoResponse; +import greencity.dto.habit.CustomHabitDtoRequest; +import greencity.dto.habit.CustomHabitDtoResponse; import greencity.dto.habit.HabitDto; import greencity.dto.habittranslation.HabitTranslationDto; import greencity.dto.shoppinglistitem.CustomShoppingListItemResponseDto; @@ -437,10 +437,10 @@ void addCustomHabitTestWithImagePathInDto() throws IOException { ModelUtils.getCustomShoppingListItemResponseDtoForServiceTest(); CustomShoppingListItem customShoppingListItem = ModelUtils.getCustomShoppingListItemForServiceTest(); - AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + CustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); addCustomHabitDtoRequest.setImage(imageToEncode); - AddUpdateCustomHabitDtoResponse addCustomHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); + CustomHabitDtoResponse addCustomHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); addCustomHabitDtoResponse.setImage(imageToEncode); HabitTranslationDto habitTranslationDto = ModelUtils.getHabitTranslationDto(); @@ -464,7 +464,7 @@ void addCustomHabitTestWithImagePathInDto() throws IOException { when(customShoppingListItemRepo.findAllByUserIdAndHabitId(1L, 1L)).thenReturn(List.of(customShoppingListItem)); when(customShoppingListMapper.mapAllToList(List.of(customShoppingListItemResponseDto))) .thenReturn(List.of(customShoppingListItem)); - when(modelMapper.map(habit, AddUpdateCustomHabitDtoResponse.class)).thenReturn(addCustomHabitDtoResponse); + when(modelMapper.map(habit, CustomHabitDtoResponse.class)).thenReturn(addCustomHabitDtoResponse); when(customShoppingListResponseDtoMapper.mapAllToList(List.of(customShoppingListItem))) .thenReturn(List.of(customShoppingListItemResponseDto)); when(habitTranslationRepo.findAllByHabit(habit)).thenReturn(habitTranslationList); @@ -483,7 +483,7 @@ void addCustomHabitTestWithImagePathInDto() throws IOException { verify(languageRepo, times(2)).findByCode(anyString()); verify(customShoppingListItemRepo).findAllByUserIdAndHabitId(1L, 1L); verify(customShoppingListMapper).mapAllToList(anyList()); - verify(modelMapper).map(habit, AddUpdateCustomHabitDtoResponse.class); + verify(modelMapper).map(habit, CustomHabitDtoResponse.class); verify(customShoppingListResponseDtoMapper).mapAllToList(List.of(customShoppingListItem)); verify(habitTranslationRepo).findAllByHabit(habit); verify(habitTranslationDtoMapper).mapAllToList(habitTranslationList); @@ -506,9 +506,9 @@ void addCustomHabitTestWithImageFile() throws IOException { ModelUtils.getCustomShoppingListItemResponseDtoForServiceTest(); CustomShoppingListItem customShoppingListItem = ModelUtils.getCustomShoppingListItemForServiceTest(); - AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + CustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); - AddUpdateCustomHabitDtoResponse addCustomHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); + CustomHabitDtoResponse addCustomHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); addCustomHabitDtoResponse.setImage(imageToEncode); HabitTranslationDto habitTranslationDto = ModelUtils.getHabitTranslationDto(); @@ -532,7 +532,7 @@ void addCustomHabitTestWithImageFile() throws IOException { when(customShoppingListItemRepo.findAllByUserIdAndHabitId(1L, 1L)).thenReturn(List.of(customShoppingListItem)); when(customShoppingListMapper.mapAllToList(List.of(customShoppingListItemResponseDto))) .thenReturn(List.of(customShoppingListItem)); - when(modelMapper.map(habit, AddUpdateCustomHabitDtoResponse.class)).thenReturn(addCustomHabitDtoResponse); + when(modelMapper.map(habit, CustomHabitDtoResponse.class)).thenReturn(addCustomHabitDtoResponse); when(customShoppingListResponseDtoMapper.mapAllToList(List.of(customShoppingListItem))) .thenReturn(List.of(customShoppingListItemResponseDto)); when(habitTranslationRepo.findAllByHabit(habit)).thenReturn(habitTranslationList); @@ -551,7 +551,7 @@ void addCustomHabitTestWithImageFile() throws IOException { verify(languageRepo, times(2)).findByCode(anyString()); verify(customShoppingListItemRepo).findAllByUserIdAndHabitId(1L, 1L); verify(customShoppingListMapper).mapAllToList(anyList()); - verify(modelMapper).map(habit, AddUpdateCustomHabitDtoResponse.class); + verify(modelMapper).map(habit, CustomHabitDtoResponse.class); verify(customShoppingListResponseDtoMapper).mapAllToList(List.of(customShoppingListItem)); verify(habitTranslationRepo).findAllByHabit(habit); verify(habitTranslationDtoMapper).mapAllToList(habitTranslationList); @@ -574,9 +574,9 @@ void addCustomHabitTest2() throws IOException { ModelUtils.getCustomShoppingListItemResponseDtoForServiceTest(); CustomShoppingListItem customShoppingListItem = ModelUtils.getCustomShoppingListItemForServiceTest(); - AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + CustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); - AddUpdateCustomHabitDtoResponse addCustomHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); + CustomHabitDtoResponse addCustomHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); addCustomHabitDtoResponse.setImage(imageToEncode); HabitTranslationDto habitTranslationDto = ModelUtils.getHabitTranslationDto(); @@ -600,7 +600,7 @@ void addCustomHabitTest2() throws IOException { when(customShoppingListItemRepo.findAllByUserIdAndHabitId(1L, 1L)).thenReturn(List.of(customShoppingListItem)); when(customShoppingListMapper.mapAllToList(List.of(customShoppingListItemResponseDto))) .thenReturn(List.of(customShoppingListItem)); - when(modelMapper.map(habit, AddUpdateCustomHabitDtoResponse.class)).thenReturn(addCustomHabitDtoResponse); + when(modelMapper.map(habit, CustomHabitDtoResponse.class)).thenReturn(addCustomHabitDtoResponse); when(customShoppingListResponseDtoMapper.mapAllToList(List.of(customShoppingListItem))) .thenReturn(List.of(customShoppingListItemResponseDto)); when(habitTranslationRepo.findAllByHabit(habit)).thenReturn(habitTranslationList); @@ -619,7 +619,7 @@ void addCustomHabitTest2() throws IOException { verify(languageRepo, times(2)).findByCode(anyString()); verify(customShoppingListItemRepo).findAllByUserIdAndHabitId(1L, 1L); verify(customShoppingListMapper).mapAllToList(anyList()); - verify(modelMapper).map(habit, AddUpdateCustomHabitDtoResponse.class); + verify(modelMapper).map(habit, CustomHabitDtoResponse.class); verify(customShoppingListResponseDtoMapper).mapAllToList(List.of(customShoppingListItem)); verify(habitTranslationRepo).findAllByHabit(habit); verify(habitTranslationDtoMapper).mapAllToList(habitTranslationList); @@ -635,7 +635,7 @@ void addCustomHabitNoSuchElementExceptionWithNotExistingLanguageCodeTestUa() thr habit.setTags(Set.of(tag)); habit.setUserId(1L); habit.setImage(imageToEncode); - AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + CustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); addCustomHabitDtoRequest.setImage(imageToEncode); HabitTranslationDto habitTranslationDto = ModelUtils.getHabitTranslationDto(); @@ -671,7 +671,7 @@ void addCustomHabitNoSuchElementExceptionWithNotExistingLanguageCodeEn() throws habit.setTags(Set.of(tag)); habit.setUserId(1L); habit.setImage(imageToEncode); - AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + CustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); HabitTranslationDto habitTranslationDto = ModelUtils.getHabitTranslationDto(); habitTranslationDto.setLanguageCode("ua"); @@ -699,7 +699,7 @@ void addCustomHabitNoSuchElementExceptionWithNotExistingLanguageCodeEn() throws @Test void addCustomHabitThrowUserNotFoundException() { - AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + CustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); MultipartFile image = ModelUtils.getFile(); when(userRepo.findByEmail("user@gmail.com")).thenReturn(Optional.empty()); @@ -794,9 +794,9 @@ void updateCustomHabitTest() throws IOException { ModelUtils.getCustomShoppingListItemResponseDtoForServiceTest(); CustomShoppingListItem customShoppingListItem = ModelUtils.getCustomShoppingListItemForServiceTest(); - AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + CustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestWithImage(); - AddUpdateCustomHabitDtoResponse addCustomHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); + CustomHabitDtoResponse addCustomHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); addCustomHabitDtoResponse.setImage(imageToEncode); HabitTranslationDto habitTranslationDto = ModelUtils.getHabitTranslationDto(); @@ -822,7 +822,7 @@ void updateCustomHabitTest() throws IOException { .thenReturn(List.of(customShoppingListItem)); when(customShoppingListMapper.mapAllToList(List.of(customShoppingListItemResponseDto))) .thenReturn(List.of(customShoppingListItem)); - when(modelMapper.map(habit, AddUpdateCustomHabitDtoResponse.class)).thenReturn(addCustomHabitDtoResponse); + when(modelMapper.map(habit, CustomHabitDtoResponse.class)).thenReturn(addCustomHabitDtoResponse); when(habitTranslationRepo.findAllByHabit(habit)).thenReturn(habitTranslationList); when(habitTranslationDtoMapper.mapAllToList(habitTranslationList)).thenReturn(habitTranslationDtoList); when(habitRepo.save(habit)).thenReturn(habit); @@ -840,7 +840,7 @@ void updateCustomHabitTest() throws IOException { verify(languageRepo, times(2)).findByCode(anyString()); verify(customShoppingListItemRepo).findAllByUserIdAndHabitId(anyLong(), anyLong()); verify(customShoppingListMapper).mapAllToList(anyList()); - verify(modelMapper).map(habit, AddUpdateCustomHabitDtoResponse.class); + verify(modelMapper).map(habit, CustomHabitDtoResponse.class); verify(customShoppingListResponseDtoMapper).mapAllToList(List.of(customShoppingListItem)); verify(habitTranslationRepo).findAllByHabit(habit); verify(habitTranslationDtoMapper).mapAllToList(habitTranslationList); @@ -848,7 +848,7 @@ void updateCustomHabitTest() throws IOException { @Test void updateCustomHabitThrowsUserNotFoundException() { - AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + CustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); when(userRepo.findByEmail("user@gmail.com")).thenReturn(Optional.empty()); when(habitRepo.save(customHabitMapper.convert(addCustomHabitDtoRequest))).thenReturn(nullable(Habit.class)); @@ -872,7 +872,7 @@ void updateCustomHabitNoSuchElementExceptionWithNotExistingLanguageCodes() throw habit.setTags(Set.of(tag)); habit.setUserId(1L); habit.setImage(imageToEncode); - AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + CustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); addCustomHabitDtoRequest.setImage(ModelUtils.HABIT_DEFAULT_IMAGE); @@ -899,7 +899,7 @@ void updateCustomHabitNoSuchElementExceptionWithNotExistingLanguageCodes() throw @Test void updateCustomHabitThrowsUserHasNoPermissionToAccessException() { - AddUpdateCustomHabitDtoRequest addCustomHabitDtoRequest = + CustomHabitDtoRequest addCustomHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestWithImage(); User user = ModelUtils.getUser(); String email = user.getEmail(); From a7ea72d8dff782a77fe432d4c1d339403fe64328 Mon Sep 17 00:00:00 2001 From: Lena Sotnik Date: Wed, 20 Sep 2023 00:18:43 -0700 Subject: [PATCH 04/10] Commit with formatting code after last commit --- .../java/greencity/controller/HabitController.java | 4 ++-- .../main/java/greencity/service/HabitService.java | 13 ++++++------- .../java/greencity/service/HabitServiceImpl.java | 6 +++--- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/greencity/controller/HabitController.java b/core/src/main/java/greencity/controller/HabitController.java index d6e641d9be..8fbf0758ff 100644 --- a/core/src/main/java/greencity/controller/HabitController.java +++ b/core/src/main/java/greencity/controller/HabitController.java @@ -283,8 +283,8 @@ public ResponseEntity> getFriendsAssignedToHabitProf }) @PutMapping(value = "/update/{habitId}") public ResponseEntity updateCustomHabit(@PathVariable Long habitId, - @RequestBody @Valid CustomHabitDtoRequest request, @ApiIgnore Principal principal, - @ApiParam(value = "Image of habit") @ImageValidation @RequestPart(required = false) MultipartFile image) { + @RequestBody @Valid CustomHabitDtoRequest request, @ApiIgnore Principal principal, + @ApiParam(value = "Image of habit") @ImageValidation @RequestPart(required = false) MultipartFile image) { return ResponseEntity.status(HttpStatus.OK) .body(habitService.updateCustomHabit(request, habitId, principal.getName(), image)); } diff --git a/service-api/src/main/java/greencity/service/HabitService.java b/service-api/src/main/java/greencity/service/HabitService.java index dae2ea47eb..f5f01454f1 100644 --- a/service-api/src/main/java/greencity/service/HabitService.java +++ b/service-api/src/main/java/greencity/service/HabitService.java @@ -112,16 +112,15 @@ PageableDto getAllByDifferentParameters(UserVO userVO, Pageable pageab /** * Method to save {@link CustomHabitDtoResponse}. * - * @param addCustomHabitDtoRequest dto with - * {@link CustomHabitDtoRequest} + * @param addCustomHabitDtoRequest dto with {@link CustomHabitDtoRequest} * entered info about field that need to edit. * @param userEmail {@link String} - user email. * @return {@link CustomHabitDtoResponse} instance. * @author Lilia Mokhnatska */ CustomHabitDtoResponse addCustomHabit(CustomHabitDtoRequest addCustomHabitDtoRequest, - MultipartFile image, - String userEmail); + MultipartFile image, + String userEmail); /** * Retrieves the list of profile pictures of the user's friends (which have @@ -136,12 +135,12 @@ CustomHabitDtoResponse addCustomHabit(CustomHabitDtoRequest addCustomHabitDtoReq /** * Method to update {@link CustomHabitDtoResponse}. * - * @param customHabitDtoRequest dto with {@link CustomHabitDtoRequest} - * entered info about field that need to edit. + * @param customHabitDtoRequest dto with {@link CustomHabitDtoRequest} entered + * info about field that need to edit. * @param userEmail {@link String} - user email. * @return {@link CustomHabitDtoResponse} instance. * @author Olena Sotnik. */ CustomHabitDtoResponse updateCustomHabit(CustomHabitDtoRequest customHabitDtoRequest, - Long habitId, String userEmail, MultipartFile image); + Long habitId, String userEmail, MultipartFile image); } diff --git a/service/src/main/java/greencity/service/HabitServiceImpl.java b/service/src/main/java/greencity/service/HabitServiceImpl.java index ec7c748aaa..68cf71af66 100644 --- a/service/src/main/java/greencity/service/HabitServiceImpl.java +++ b/service/src/main/java/greencity/service/HabitServiceImpl.java @@ -304,7 +304,7 @@ public List addAllShoppingListItemsByListOfId(Long habitId, List lis @Transactional @Override public CustomHabitDtoResponse addCustomHabit( - CustomHabitDtoRequest addCustomHabitDtoRequest, MultipartFile image, String userEmail) { + CustomHabitDtoRequest addCustomHabitDtoRequest, MultipartFile image, String userEmail) { User user = userRepo.findByEmail(userEmail) .orElseThrow(() -> new WrongEmailException(ErrorMessage.USER_NOT_FOUND_BY_EMAIL + userEmail)); @@ -361,8 +361,8 @@ public List getFriendsAssignedToHabitProfilePictures(Long @Transactional @Override - public CustomHabitDtoResponse updateCustomHabit(CustomHabitDtoRequest habitDto, - Long habitId, String userEmail, MultipartFile image) { + public CustomHabitDtoResponse updateCustomHabit(CustomHabitDtoRequest habitDto, Long habitId, + String userEmail, MultipartFile image) { User user = userRepo.findByEmail(userEmail) .orElseThrow(() -> new WrongEmailException(ErrorMessage.USER_NOT_FOUND_BY_EMAIL + userEmail)); Habit toUpdate = habitRepo.findById(habitId) From 6335d997c71c33e7ab2b96b70dc646b6cb3ec3ba Mon Sep 17 00:00:00 2001 From: Lena Sotnik Date: Wed, 20 Sep 2023 02:13:59 -0700 Subject: [PATCH 05/10] Commit with few more tests --- .../greencity/service/HabitServiceImpl.java | 3 +- .../src/test/java/greencity/ModelUtils.java | 20 ++-- .../service/HabitServiceImplTest.java | 102 ++++++++++++++---- 3 files changed, 99 insertions(+), 26 deletions(-) diff --git a/service/src/main/java/greencity/service/HabitServiceImpl.java b/service/src/main/java/greencity/service/HabitServiceImpl.java index 68cf71af66..a49758cdb3 100644 --- a/service/src/main/java/greencity/service/HabitServiceImpl.java +++ b/service/src/main/java/greencity/service/HabitServiceImpl.java @@ -413,8 +413,7 @@ private void saveHabitTranslationListsToHabitTranslationRepo(CustomHabitDtoReque habitTranslationRepo.saveAll(habitTranslationListForEn); } - private List mapHabitTranslationFromAddCustomHabitDtoRequest( - CustomHabitDtoRequest habitDto) { + private List mapHabitTranslationFromAddCustomHabitDtoRequest(CustomHabitDtoRequest habitDto) { return habitTranslationMapper.mapAllToList(habitDto.getHabitTranslations()); } diff --git a/service/src/test/java/greencity/ModelUtils.java b/service/src/test/java/greencity/ModelUtils.java index 350e4ba9d8..445e8151de 100644 --- a/service/src/test/java/greencity/ModelUtils.java +++ b/service/src/test/java/greencity/ModelUtils.java @@ -194,12 +194,7 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import static greencity.enums.UserStatus.ACTIVATED; @@ -2848,6 +2843,19 @@ public static CustomHabitDtoRequest getAddCustomHabitDtoRequestForServiceTest() .build(); } + public static CustomHabitDtoRequest getСustomHabitDtoRequestWithTagsForServiceTest() { + return CustomHabitDtoRequest.builder() + .tagIds(Set.of(20L)) + .build(); + } + + public static CustomHabitDtoRequest getСustomHabitDtoRequestWithComplexityAndDuration() { + return CustomHabitDtoRequest.builder() + .complexity(2) + .defaultDuration(7) + .build(); + } + public static CustomHabitDtoRequest getAddCustomHabitDtoRequestWithImage() { return CustomHabitDtoRequest.builder() .complexity(2) diff --git a/service/src/test/java/greencity/service/HabitServiceImplTest.java b/service/src/test/java/greencity/service/HabitServiceImplTest.java index c05bfc2633..01ccd90aae 100644 --- a/service/src/test/java/greencity/service/HabitServiceImplTest.java +++ b/service/src/test/java/greencity/service/HabitServiceImplTest.java @@ -781,6 +781,7 @@ void getFriendsAssignedToHabitProfilePicturesWhenHabitNotFoundTest() { @Test void updateCustomHabitTest() throws IOException { User user = ModelUtils.getUser(); + user.setRole(Role.ROLE_ADMIN); Tag tag = ModelUtils.getTagHabitForServiceTest(); Language languageUa = ModelUtils.getLanguageUa(); Language languageEn = ModelUtils.getLanguage(); @@ -794,10 +795,10 @@ void updateCustomHabitTest() throws IOException { ModelUtils.getCustomShoppingListItemResponseDtoForServiceTest(); CustomShoppingListItem customShoppingListItem = ModelUtils.getCustomShoppingListItemForServiceTest(); - CustomHabitDtoRequest addCustomHabitDtoRequest = + CustomHabitDtoRequest customHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestWithImage(); - CustomHabitDtoResponse addCustomHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); - addCustomHabitDtoResponse.setImage(imageToEncode); + CustomHabitDtoResponse customHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); + customHabitDtoResponse.setImage(imageToEncode); HabitTranslationDto habitTranslationDto = ModelUtils.getHabitTranslationDto(); @@ -812,7 +813,7 @@ void updateCustomHabitTest() throws IOException { when(userRepo.findByEmail(user.getEmail())).thenReturn(Optional.of(user)); when(habitRepo.findById(1L)).thenReturn(Optional.of(habit)); - when(habitRepo.save(customHabitMapper.convert(addCustomHabitDtoRequest))).thenReturn(habit); + when(habitRepo.save(customHabitMapper.convert(customHabitDtoRequest))).thenReturn(habit); when(tagsRepo.findById(20L)).thenReturn(Optional.of(tag)); when(habitTranslationMapper.mapAllToList(List.of(habitTranslationDto))) .thenReturn(List.of(habitTranslationUa)); @@ -822,19 +823,19 @@ void updateCustomHabitTest() throws IOException { .thenReturn(List.of(customShoppingListItem)); when(customShoppingListMapper.mapAllToList(List.of(customShoppingListItemResponseDto))) .thenReturn(List.of(customShoppingListItem)); - when(modelMapper.map(habit, CustomHabitDtoResponse.class)).thenReturn(addCustomHabitDtoResponse); + when(modelMapper.map(habit, CustomHabitDtoResponse.class)).thenReturn(customHabitDtoResponse); when(habitTranslationRepo.findAllByHabit(habit)).thenReturn(habitTranslationList); when(habitTranslationDtoMapper.mapAllToList(habitTranslationList)).thenReturn(habitTranslationDtoList); when(habitRepo.save(habit)).thenReturn(habit); when(fileService.upload(image)).thenReturn(imageToEncode); - assertEquals(addCustomHabitDtoResponse, - habitService.updateCustomHabit(addCustomHabitDtoRequest, 1L, "taras@gmail.com", image)); + assertEquals(customHabitDtoResponse, + habitService.updateCustomHabit(customHabitDtoRequest, 1L, "taras@gmail.com", image)); verify(habitRepo).findById(anyLong()); verify(userRepo).findByEmail(user.getEmail()); verify(habitRepo).save(any()); - verify(customHabitMapper).convert(addCustomHabitDtoRequest); + verify(customHabitMapper).convert(customHabitDtoRequest); verify(tagsRepo).findById(20L); verify(habitTranslationMapper, times(3)).mapAllToList(List.of(habitTranslationDto)); verify(languageRepo, times(2)).findByCode(anyString()); @@ -848,16 +849,16 @@ void updateCustomHabitTest() throws IOException { @Test void updateCustomHabitThrowsUserNotFoundException() { - CustomHabitDtoRequest addCustomHabitDtoRequest = + CustomHabitDtoRequest customHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); when(userRepo.findByEmail("user@gmail.com")).thenReturn(Optional.empty()); - when(habitRepo.save(customHabitMapper.convert(addCustomHabitDtoRequest))).thenReturn(nullable(Habit.class)); + when(habitRepo.save(customHabitMapper.convert(customHabitDtoRequest))).thenReturn(nullable(Habit.class)); assertThrows(WrongEmailException.class, - () -> habitService.updateCustomHabit(addCustomHabitDtoRequest, 1L, "user@gmail.com", null)); + () -> habitService.updateCustomHabit(customHabitDtoRequest, 1L, "user@gmail.com", null)); verify(userRepo).findByEmail("user@gmail.com"); - verify(customHabitMapper).convert(addCustomHabitDtoRequest); + verify(customHabitMapper).convert(customHabitDtoRequest); } @Test @@ -872,9 +873,10 @@ void updateCustomHabitNoSuchElementExceptionWithNotExistingLanguageCodes() throw habit.setTags(Set.of(tag)); habit.setUserId(1L); habit.setImage(imageToEncode); - CustomHabitDtoRequest addCustomHabitDtoRequest = + + CustomHabitDtoRequest customHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestForServiceTest(); - addCustomHabitDtoRequest.setImage(ModelUtils.HABIT_DEFAULT_IMAGE); + customHabitDtoRequest.setImage(ModelUtils.HABIT_DEFAULT_IMAGE); HabitTranslationDto habitTranslationDto = ModelUtils.getHabitTranslationDto(); habitTranslationDto.setLanguageCode("ua"); @@ -888,18 +890,18 @@ void updateCustomHabitNoSuchElementExceptionWithNotExistingLanguageCodes() throw when(languageRepo.findByCode("en")).thenReturn(Optional.empty()); assertThrows(NoSuchElementException.class, () -> habitService.updateCustomHabit( - addCustomHabitDtoRequest, 1L, "taras@gmail.com", image)); + customHabitDtoRequest, 1L, "taras@gmail.com", image)); verify(habitRepo).findById(anyLong()); verify(userRepo).findByEmail(user.getEmail()); verify(habitTranslationMapper, times(3)) - .mapAllToList(addCustomHabitDtoRequest.getHabitTranslations()); + .mapAllToList(customHabitDtoRequest.getHabitTranslations()); verify(languageRepo, times(2)).findByCode(anyString()); } @Test void updateCustomHabitThrowsUserHasNoPermissionToAccessException() { - CustomHabitDtoRequest addCustomHabitDtoRequest = + CustomHabitDtoRequest customHabitDtoRequest = ModelUtils.getAddCustomHabitDtoRequestWithImage(); User user = ModelUtils.getUser(); String email = user.getEmail(); @@ -911,9 +913,73 @@ void updateCustomHabitThrowsUserHasNoPermissionToAccessException() { when(userRepo.findByEmail(email)).thenReturn(Optional.of(user)); assertThrows(UserHasNoPermissionToAccessException.class, - () -> habitService.updateCustomHabit(addCustomHabitDtoRequest, 1L, email, null)); + () -> habitService.updateCustomHabit(customHabitDtoRequest, 1L, email, null)); verify(userRepo).findByEmail(anyString()); verify(habitRepo).findById(anyLong()); } + + @Test + void updateCustomHabitWithOneParameterToUpdateTest() throws IOException { + User user = ModelUtils.getTestUser(); + user.setRole(Role.ROLE_ADMIN); + Tag tag = ModelUtils.getTagHabitForServiceTest(); + Habit habit = ModelUtils.getCustomHabitForServiceTest(); + MultipartFile image = ModelUtils.getFile(); + String imageToEncode = Base64.getEncoder().encodeToString(image.getBytes()); + habit.setUserId(1L); + habit.setImage(imageToEncode); + + CustomHabitDtoRequest customHabitDtoRequest = ModelUtils.getСustomHabitDtoRequestWithTagsForServiceTest(); + CustomHabitDtoResponse customHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); + + when(userRepo.findByEmail(user.getEmail())).thenReturn(Optional.of(user)); + when(habitRepo.findById(1L)).thenReturn(Optional.of(habit)); + when(tagsRepo.findById(20L)).thenReturn(Optional.of(tag)); + when(habitRepo.save(habit)).thenReturn(habit); + when(fileService.upload(image)).thenReturn(imageToEncode); + when(modelMapper.map(habit, CustomHabitDtoResponse.class)).thenReturn(customHabitDtoResponse); + + assertEquals(customHabitDtoResponse, + habitService.updateCustomHabit(customHabitDtoRequest, 1L, "user@email.com", image)); + + verify(habitRepo).findById(anyLong()); + verify(userRepo).findByEmail(user.getEmail()); + verify(habitRepo).save(any()); + verify(tagsRepo).findById(20L); + verify(modelMapper).map(habit, CustomHabitDtoResponse.class); + verify(habitTranslationRepo).findAllByHabit(habit); + } + + @Test + void updateCustomHabitWithComplexityToUpdateTest() throws IOException { + User user = ModelUtils.getTestUser(); + user.setRole(Role.ROLE_ADMIN); + + Tag tag = ModelUtils.getTagHabitForServiceTest(); + Habit habit = ModelUtils.getCustomHabitForServiceTest(); + MultipartFile image = ModelUtils.getFile(); + String imageToEncode = Base64.getEncoder().encodeToString(image.getBytes()); + habit.setUserId(1L); + habit.setImage(imageToEncode); + habit.setTags(Set.of(tag)); + + CustomHabitDtoRequest customHabitDtoRequest = ModelUtils.getСustomHabitDtoRequestWithComplexityAndDuration(); + CustomHabitDtoResponse customHabitDtoResponse = ModelUtils.getAddCustomHabitDtoResponse(); + + when(userRepo.findByEmail(user.getEmail())).thenReturn(Optional.of(user)); + when(habitRepo.findById(1L)).thenReturn(Optional.of(habit)); + when(habitRepo.save(customHabitMapper.convert(customHabitDtoRequest))).thenReturn(habit); + when(modelMapper.map(habit, CustomHabitDtoResponse.class)).thenReturn(customHabitDtoResponse); + when(habitRepo.save(habit)).thenReturn(habit); + + assertEquals(customHabitDtoResponse, + habitService.updateCustomHabit(customHabitDtoRequest, 1L, "user@email.com", null)); + + verify(habitRepo).findById(anyLong()); + verify(userRepo).findByEmail(user.getEmail()); + verify(habitRepo).save(any()); + verify(modelMapper).map(habit, CustomHabitDtoResponse.class); + verify(habitTranslationRepo).findAllByHabit(habit); + } } From 6980a711a25798dbf3fe0a3a38e5d68882541c9b Mon Sep 17 00:00:00 2001 From: Lena Sotnik Date: Wed, 20 Sep 2023 02:28:59 -0700 Subject: [PATCH 06/10] Commit with one more test --- .../service/HabitServiceImplTest.java | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/service/src/test/java/greencity/service/HabitServiceImplTest.java b/service/src/test/java/greencity/service/HabitServiceImplTest.java index 01ccd90aae..1c2f85bdb9 100644 --- a/service/src/test/java/greencity/service/HabitServiceImplTest.java +++ b/service/src/test/java/greencity/service/HabitServiceImplTest.java @@ -60,9 +60,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; @@ -982,4 +980,26 @@ void updateCustomHabitWithComplexityToUpdateTest() throws IOException { verify(modelMapper).map(habit, CustomHabitDtoResponse.class); verify(habitTranslationRepo).findAllByHabit(habit); } + + @Test + void updateCustomHabitThrowsUserHasNoPermissionToAccessExceptionWithDiffrentUserId() { + CustomHabitDtoRequest customHabitDtoRequest = + ModelUtils.getAddCustomHabitDtoRequestWithImage(); + User user = ModelUtils.getTestUser(); + String email = user.getEmail(); + user.setRole(Role.ROLE_USER); + + Habit habit = ModelUtils.getCustomHabitForServiceTest(); + habit.setUserId(1L); + + when(habitRepo.findById(habit.getId())).thenReturn(Optional.of(habit)); + when(userRepo.findByEmail(email)).thenReturn(Optional.of(user)); + + assertThrows(UserHasNoPermissionToAccessException.class, + () -> habitService.updateCustomHabit(customHabitDtoRequest, 1L, email, null)); + + assertNotEquals(user.getId(), habit.getUserId()); + verify(userRepo).findByEmail(anyString()); + verify(habitRepo).findById(anyLong()); + } } From d7cef156888718fcdb77b883888309c6a0c7fadb Mon Sep 17 00:00:00 2001 From: Lena Sotnik Date: Wed, 20 Sep 2023 02:35:35 -0700 Subject: [PATCH 07/10] Commit with formatting --- .../src/test/java/greencity/service/HabitServiceImplTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/src/test/java/greencity/service/HabitServiceImplTest.java b/service/src/test/java/greencity/service/HabitServiceImplTest.java index 1c2f85bdb9..2a236a0b06 100644 --- a/service/src/test/java/greencity/service/HabitServiceImplTest.java +++ b/service/src/test/java/greencity/service/HabitServiceImplTest.java @@ -984,7 +984,7 @@ void updateCustomHabitWithComplexityToUpdateTest() throws IOException { @Test void updateCustomHabitThrowsUserHasNoPermissionToAccessExceptionWithDiffrentUserId() { CustomHabitDtoRequest customHabitDtoRequest = - ModelUtils.getAddCustomHabitDtoRequestWithImage(); + ModelUtils.getAddCustomHabitDtoRequestWithImage(); User user = ModelUtils.getTestUser(); String email = user.getEmail(); user.setRole(Role.ROLE_USER); @@ -996,7 +996,7 @@ void updateCustomHabitThrowsUserHasNoPermissionToAccessExceptionWithDiffrentUser when(userRepo.findByEmail(email)).thenReturn(Optional.of(user)); assertThrows(UserHasNoPermissionToAccessException.class, - () -> habitService.updateCustomHabit(customHabitDtoRequest, 1L, email, null)); + () -> habitService.updateCustomHabit(customHabitDtoRequest, 1L, email, null)); assertNotEquals(user.getId(), habit.getUserId()); verify(userRepo).findByEmail(anyString()); From bde4a79fc92926248460f620d46584b2df8759ce Mon Sep 17 00:00:00 2001 From: Lena Sotnik Date: Fri, 22 Sep 2023 14:02:50 -0700 Subject: [PATCH 08/10] Commit changes in HabitController and HabitServiceImpl --- core/src/test/resources/test.jpg | Bin 0 -> 36407 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 core/src/test/resources/test.jpg diff --git a/core/src/test/resources/test.jpg b/core/src/test/resources/test.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a790bbcc3cf13581e244201d5ee877ba9037dc33 GIT binary patch literal 36407 zcmX6^Wmp_dvmM-mvpB&51P?)j6WkpZcXxMpCqaX|yE_CA1YIn+yM*9=_x zKt>iJ^dgLlisV`UBoVL|y~{>J!kOjSv7J?zgm)U!zYFOlFk!OOjMej zwRTx=q|0U3GteA4CM|IhdTZ=vhvZlAsxLw=bR5`fz9Lrog<+88(kOZG1Z5e{Q8;G9 zhnVaXcbW7XPceO`)uV1(k16CG$(TR)3P0iSag<{(R6ZL35ZiPc${ZvjB(2T{iO+l@ zghe-J;Q=Yk^ZW|sZuVds_@aF6Mf4G~cf#%*-9&<${!x##I{~9et2(cJH_Bt#0AL1lho&TYe82Ru?6+0!0ajfzTpS$$ z1nzxgbfmaK8X;5zbzyys?Ess|ezYLAVS;CZ{;B=lO(~M_3l1O;#~K2BOk_avVQ80= z&N+hK4y#B60FEJuJb)dnhUhd_#RpTyYtpzYxrjjMB%zm41ROi*QW%SH{(ON0BJ<=b zZN;m9DldSMmzD!dWoi1XfV_s?737f#OkuY`5I#pT6@nT1dmE9j_^>Vbd0TU87!#-Y z>Skd8@JGqF7Yt;;oRa_yV(S`zGS)86E%=}vr1@97@r?U8KJ~)ST+4##WdknDSGs4h zOmKi-($1IMeCeK;Is}insJTpvYJ4F-ewN~2v}w* zc9!b!FgjX)|Jv|+klpuKtaG0~Iz0aM*pL8{6|asdz{YEvfdH#|uj1(~ug7n=yc@^d z@_gYwSS8rT7&@zqs3+evzw^|9(y7&F2lz2q`$2iyAF^y2Lx5UX*)7kx2CxhVZ%ZS> zx$1h(>uGZMFTwZX?7EpxP!0(64dle%MPjL`lCqZS|Av%D25mMmG&a_S>YpRtMfdI0 zQOA>@B!H*Lml1?j58SKncVjCy{lk+D6#B~xdjkjC;+%I}%|x_tc}Lbm<4iUQdQ5ZZ z$+2H|K@TeU3D?aG&poG15W=z9AuT)5c?tu3hKFrY$vduLB045wQ=1HtbVz8nH_$5f z@W1gm%n=p(00;{jmXQHncutB%F`NVJU^qa@g2E6clFGzn^%WO$`ronf>CI4w!^?K< z%=*rq3fJb$drYv=O8A=LhybjxJ|2hy4So@lsA5=&_z*ee>LkE{iwp*WM}GmW2TN z3P9lVf0O(tvAY?>O$z|9EmV15Ye>I%CkXmJv&f7GsbbD-_-!p(I+^7NWB0|iO$ZOd zCdM-$nD*Pf6TS)zWF}LqPNBA#_$MWUR`m-`(!SWkcdyk_T`PZg(;JQgrEWObljDvm z%ID=^qm|QdtSt=25+AD$7~N&k4-Y6iT~9*bCIk8q zVY#?@MCukoVbN`o*zxOv6W%t-4@o@ye&2`LFq0dGAt)HeAygN^Eb`my0LPf{wzR@k z3#q|wzk-r5mES)7 zEM{+B-1~$eKo9f_6L%D@4LihfQ{$aB3Ur9_3@HqmDKvCMrFnM`PX*-%_kSG?;a&&y z{AJ{5bj$*PJpg+J-?4z%*@kP25Dw^SQtSI#)sl)6XDO~FfjPzhHQ*`CV4kS~7=&}n zOZv%(3N{g8EH_H3INdG~M-_H93_>`|H^rRZaX~8o_@3XJuX~5FR}^(>aeGB@Q{vrs z7;H3cY1&0UCvHXTFHX^{8p?J6mI4|AQ$?7D3e1|5Zwn6$W82<02Z&FN7Zmlz5Ej7G zGS=n_zZ=T5!0O6o0G3<~JQFFqwyb!p`N%$o@{I4vx#IMQ!!sSM7Azx^6q1;2Qs1vw z`M3-N>6jmb6&Z-SX23lJ76xr=*_xqepy^(%=SRyC2=^z9kI~)b9<{ikOHNXlD#M@*1DN%$P2Nyl}HrJ_XzM)+5 zXL%K$xju_QfqzUqR>$8u{vSdG6E=@7aL6$lMz{@9GPAi&LX#{<{mn$WI2@U6ASe&D z6+Y@K75LAJ<7?lx1|lNJpZQCt=l6>K*Cfp8mEPx-riv;osGSnZbe1eU2FQ|~6ThzP zNZ603+cT?TknE|qG7fv(2@?hY%HDa>(!fZfHKk;rf>#^*#E1C8vb1?5)OS|ye4_>3 zAie)J1RSGi59Yck$zJym#@d!9UcfI?Wi43*)q|CFd=LTzm))YmI3|{?C5t^a6Iw))R3ld#vMRj~O|Fj@${MJ`&WVjLlLYgiA>oCPoMI zL*BnMw9>E?tqR^Jg;mA3`{oV0u)F{769Ti}!!KLi*i!OltR&rrXyfw)b-_CC zA29qjJ9;UhjRA8cO=Tjg<$-}=mIy@3TjOO4HSGpHFCo_{e1iW?j&S`a4it982waYD zIPgq#U>y;Awd3y%XpOT51-zHJi?5)yp0`dYsse%DAq!lV#9z?B3RqQnjtovezM$3D z@k_dWy$*SKs(p44qy3K$5(Ck~aci&`uc2c#8GDug9io5R}F9J3qhoUgUlSuTigVK<$LrenLn&g5Y8CjX{By z&lho(ldnz{j6$A=mDj&W-hMz$nvf=E7Hk;oY=4P%bHrf5p#4N878Yk&+wi*OJ8ahM zjSBA=P?Hq&-w`VRdTaAPN6e>(7X*^!+5ckK@l@v-;kkc_u(+;@F%Sv3{f-G~;j~a8 z^MZCF5+lnu@xR_qzsp$OPA~}%Nv~!F5hCFS!j?&$HTevLT@$dr5DGl35~!?U{mfc_ zP1%r8g0eA76w9SnIE9-8TXxGol%^*L@JyDN?F(|3xa4HSrHcYr4o7LZj|Uv9B*nb- z7lSX?=Qgj&|G5I#Ux&>LqBO-;#4PiVAynFOI^|*_gQr=X%fHRiJYJ(2NCdnlkr$Jl zOy=`C4ZIlSv9903z(RYd+qM(1gUiqo^ntI`Oxq@A64$uJH#lAh`ebs1MX`|sjG6v_ zRTmA}CzJDHrw|rT`uV6gkAaO@0VIclDv=fF8m#*9RiS63OgKwdLk5 zSWX`cnEy7!S@Y?UXV%f8hZ;aw$JOzN`1iM+~QObJ-=%Ph9*mC$x)}86hV;HT^$j!U zj`clT4&I4W#r({q{0Y=?;dW#gpTm1L^`g&qRA=N#VvHeyaWS3kN>MK%%xpFk=1A1X z(V>9|eIG1fe4zXNkU96^&$Jui$@G^@mO{xhZ&baS=w-c=snWNOSBFCz5C7!jzBliF z^4TlaH!os%h=9Cr77Pp;02DX2HOW0CkQFOL5K*NvvDs*witkN))~sj7stZ=R!`|jp zq%cjwzOzn7uuT9g*~3??JJ1W;pEl!W=$*pO`Fvxz{GO5Cq>>74I4PeF^%P4p?|frC zhsZg|AJ$inw$C(Yevq)1a5%DL!@C9}0D&?T{E>L#l*&RLp;BkAu^HO@DWQ?_FMu6^ zQAlE{^Znnf36clo9SjunL`H0pEI`T^X=r0JztBTT#hgHK*6q$O-oNc2Glifw7|P*a zw`_Wa`tIurybqAyL028<>)fxeeGI5{;_xmw8*zCG&UV=^K9UPr5isPAUi+K$IvmK) zyEHK-P&f;uC|%xzHmpLz<9TfKICb*+9Wi787CI;(kuB7lr|1vHab<(UmFk@x65VIA zs{^oM5Y&eFXi&LJ!}sig1%zp^SP7p>Zq&Ep*^Itu zidP=KXB2Gq&K%114Dmt0c%Moj zVuqyMF5jJhxc!QUUzOY5r9n13^!{WN?%)%va1E3AW_Y*eq^bnTq~DGdxW`nMF$^To zgz8&SC)tW+5U1CL$9jP{T|AQ!J8?0Z*MD~8a!u$FaBR>4MN}qdT(~#q-~F*<%&9Xd%INmqv!EraVbE3! zmRLCw26@2qPgeND9+5TU+6?D)yMRH-L!8BIRfC*MWlYnwL4no{B#J0($(3@Sv+zldsr_ZL5pGp-K#NKI&G>f6po$*!|%U+7<6<1X@X#~al& zWvoUu%(4ePDb!=g>p$XjxVt7R zRQ<+=5{sWaEgYh?J-5}@kK{cG8p(n|S)*SM7n*-&l;$DjeZ`|S^y>2fGRE%z;>}}o zaY($38^~}okwK9b`19}lsYi;nd!N<%OeKpIp&2KpZd(H;QhQq1nV?SO4)vTIs2G{P z_s(cP{Nj4>Yy=HkiI|Jz?MXRzH{7$Y2wflHL%#Q$y(_?13wNcR)J3pW72v1_TxdT8?M}Qw{~x+aW5k)5=;O66RY? z4s0C|r4FYEI)kvzBU%V7;i<3%gCWwFi#Gj&Euw0;=zJFZrEjwOVjird{{z~;FD7@> zgtUUh6q+dXY5Bz&J$t^UO~!Zkho%?cI>3L1AZt?AP$rM#Mf{bE4w3s8jRPg^B$b!89eFUsG;t?-Cmk>60fvOU{Pq)Xc73z! zNjgKrlWa1=SI^h|KU1aT8@^#Qg5)_uUoZR*8Lv|YxFzl1lH&*q&& z_i=@-vl|<4Uc3#i46Z9t>t!uxXf1-N!=2R2T_G`ahkiaiBDt#paL}0}yWb)2Ny}-v zq`DDbl{mJfMbsPo98Sku_T^l{ZavL)VOuP4+Du6)R!Gjo*CUL4to^umk$dvJ9h+p1Tq?f!P%pNpaU+R>PEfYoEK zUxT_rXV(TLw|N-8{u_5kl@SgtVoaKN)}^v+k&2>nA^byXh*%Buzs~cDaq7ggTU&0P z`_O{S*}in7R*VJ7Tc~Ov2R$^!rHR$tZQE%+Oa^Q%j40C?|6U!b5s*AtIW{k1FKS1Z zyqvXDgf84NG>5J|xVa0)qOxuE)}HA08?1V{jO`E&)G|t}1{A`7WK;}pO=B%vN`TKp*)cgAyT3wKdC7ITs2DZ>lG)qVxNO%-qfeJ>aq_`(4*{OTy)p@|hr{5kK z%ht@rbd4yPW_(QM_l*Ybgh%6U<2u8ID(Q9&G^nX>WqSlIZ=F>)(CF zZ-V7$fFr!_^K4CEHXIkElkcD%^H&aesw7;9bD$mmOlIw67|xxoh{|(|LU&t$=UY7b@x`p3l0!gZ@-$Oc0HTnlTvP@G*)Mm z4Hse?-$()Pl8ECt5qG`UrpXiOBhJK~3?cs0Q8Vjh*?*%{Y!dYpbMUC$wsJJHTpy=K zv$Sy7Qz5N&$O@6X(^!QVH8;}MCun7CNBe;#iBu1B8^6Mv zY-fEejiDWzF`H$r9mlR(ZlG82nx^A+DZ6JJ|B3PXaYaa2GR@pKEmvPJPpIqcg`>0M zu=BYDn!)p(zcC?P8Z`&qJ%n&SvOq&Cvxi^u`*c2NDyqaSCbsR>9Iy87&PaZ+B2{^f zS6d-qNklK0(paR@vN>_WK{w?*Q~fu2L2#B1*u7ds{#XrWVXMRL436f2tk>?st?6#i zPYr?Aoj?5ju{%Ac*xF?Zb*m*yw`w;Vu}bxnq}tENIO^8f%!|iQYfg{frq3{0vWr;B z9`mi6&nFbwG0t?I+aN zFVUm{03a1zhQ_Mxf|$+MlT8RIF1+o1Sn~{LOMMxzHFpm(Kfo`CoGFzSD(da1?pi1m zU`e}JkToid*tkxA{)ui0sK-Dt(F(EpbL4l=8hGl`w3eS+>6-^uyACdBj+#*;~i(+%k#WWJ0N$OsZLv%t_Cc6 zY&mtB1q=m`wJ!a!x{qpAbdIfMn;J=}Xi zaZ98c5hOE_Zfn|mr5vP*FqW7@bK-H(tcf3!AT$d@%+7fbTvsT%qsF!NOCFl!=jHIX zIYuV;Q0nVZX*@9+g0pvp+hBoz`E0NniQ>)Z-%?}w6BZjjD<=H*7ur$t+M0q?+e{WU zXp#N+00r8c-Jk6t+MBc!u;XTzGRr2_+8Rqd{h(?_0JS(#r>5snAi!H>NcHA(j>fy^=U^8c1LaQy3Au_`qFwM5>4jm~=NwDJ=; zGx5N>Fl9{bsM*HqYCG^%fs#xihRit}1@2|+GKFiLuFgl;kCU$+)4i*Yu8*SvwfQRv z0T~*}2o-3ZT)M($A@>8&O=F-?%fiD{_nwjIkDpPaGd2Ftf^9gDE$cuRfH+!~|GF$+ zI7@@P*JV(*cT)d~qhsgZqKf&wpkFqZUooEuL5&ZLUdxtQlgU3eJiZZ}@2zvr6MoeocIkLN!F3foht)x_N$+9JoyQ?^nt zpaNA2sMtYEKMa(bDSGtLox_-cCSA27wGOM+lpvD<l>BYVgrgO#j85ul@k z-!U0g9C4`g^cuhJcA|Qibk`eyE_8!_W3=Q^JtXTh#3#^_vk*^>`U#2)$)6gYdYo5q zY;Hr;e>)kWq3(Gk=yA zwXgP`jZxDA!bGN?cQjo;pmN1tyWz_fJ$)l%#L*+%Tb1~y*HHl>Nssq$q&E+rQ-&Ob zp3bh9D>uYQXj^fhO(zl#^jEZv1nf;tRTW|%1kv<^R$y(I*xqPlS@+?APi151-~R2} z+G5d7lTK<~xe^~KKvV+4zf;2s>D9^?`YcS-kQjW`wDm=>BjihqE?Oiiu+V!(^KH9(GzR-{ttcTbtsJ%BZ*5%HNhfi zi(a&X^$eyA4US-vAP~`E zn;vcM<@T+||3LCcRp?ohx7#!L-#)$OE0w^>KTWK%_e_5!H<@xDnARmktcomm3$T%e zo#d`1thVQn-T26kZ#tGa=1!DrD+4av_~w+;qV~2SUByV)b(m*-UnS$SOY$k%$zK|6 zcIS^tYe-{H!-J#~t)FRgipuuE9A9fS*Et?I7wnnmu5%3XIu(7R9(|kZ?$V%v?iyF~`E&PGia~Lj z-a{sbAisE?|2r;tF?{**T?EJ73`eBtd$_!JZ5+B7S}cR6@FERial)o2yOM+K?5)_(<(R{#wgsQ<#4?4Ol2ssEGzerWb&-{8SSZpes2yl zz0ZeM*V%p-d8*@2sAvFX=&3CE6|eU}WseN2->HM}=t83F8B=Nu;cr#LuU)W87%gG* zchidBJGSV` z2Rf3uT^J>=k%xu6@%^^f4T_Nz^{CegY{vh^TYn5a8CQdO3JBPbxh`PzkCN~Zixx2;O13zBQIg?N%s1pRWQQe@vysTPUx&r|F*#0+Li3( zw|Z}>mT7Ll1M9SsN2stsq&+Jl$0&|2&*jrFEtpP1lt5k@7z@?8%NtOzLNDLxh^urR z=ve>rV?mQe&@9bzTY4QH1780DhTzb9~nIkQwPXjbnIC_ z{u^*8qDH$5ts$qeK1fr9bk;toK5peW%*qIP`f5{}zAfUu2CiR@u|ix}{WwEy&wh1^ z|JZ=QZG!^;u}R9gU4!Nm3tQDs?cE3@nLRdK$u(@zn56N+l`J7+axm65N)!lanz&+= zh@8QY9}vZYn?ei7v-;?ANASJ9S zY;>LMWMuv=^bm-v;XnCvUB;fXz4GREc2DbllccZ6>N{t1l*HW~)8lnVH;35A5M)iY zY@|QcN*EU%)NexkVud6<(7S1g0i#6Owra@Xmg9b*a#K|!7)gb8$v}na;-#xIi{LF8 zEx`1Bl&+#d4}Z=9AcSAX;$NtuI_l9HdZv7&`pv?G3(WVwr2c?}urDh8`OS1u*cl&? zV=r_|k7KsX?&WykS( z_Y{tgi(fin{l(S$_<=RQ7+R3LU(#D3dHBS#=9SX1HV+>iHWZ0ST;j452Xm5zO9xn4 zhM+??x{UMgJ-zxh(N1@UNxim7@8^?Nu8s;0E*tGjkLNj3W|nH6f5(JgKXzIWFWgD1;T*2|rj+4FNMdIFr?6G@1u-!6Abc}lD8{sGrLg{+@)@6Qq)6E1 zoot)yfUH#;pNRU_+^qiNBXvM=J3)Zml)?0`Vo75=y3n#rs4{EswYzeiW3w(+`_!lU zpFXYim`mQBfRVd?0(KbK<$4?{?fS9)7(E41Rb$f;b7V{;T^nFEy83uIVDwk8dGgFN zYYqmWF;u4yADZnlke4;1iBz=$zqq#-V%9eQUQT(wL(v}tr2Zy8fI76GTSgqEM>feQfEhej;NP?^YFr)TPnGl z5H!h)uSzOtNy=0N=8Us-1E369R5CzGu*u_=_}LQX+Dqa?;;^)s)j?>%S8C(G9r|%FAm+t|)oO)&+-$37h{q zzWV6ve&f1Cz?EulP+~RK@8jf*#7KGYYAV&;3Kyz|J~bhbi1RYZvWl81+%zVYU-oP; z3VKEsjYA3y-3e3S3_`$H3pK6vTu8_fw@?uJQ8BsxhvW$N?uGbe#BQokC&54V(RZl+ zS^9uKch=EzAN%n+VO=)h_DFNa!S658HTi}=`&!4H_Hu`LE3N6E*(R^dtm0BWV6WPk7vUrM6bvBcwuQ`}pI! zwSTAO8ApJ_g?T<`CkAttfcz1+!~IQYT3sjL2g}C2^Rb)6QI&k!Y`ojSbPw*GsF}z>j0}nBec~E}W$QU@zVp|}989lIJM+a6 zcs)Is-RrEOQW82vOlf?a+BXU;AqL_osFk&B-_JLNi3~2}e$dnqU~0H&B^mh>Qgo+B zm!t+riMey{s8@hm;(u3@=G)x%q`aO+-nS&FRqkMHx2%=j5F0pMOm*#zad!+RQICc2Uh8A@n@ z2X!{%X9#2KGu3=HCkZ}sQ;_MMNFo-7&0Uzgo0%selxmjIoi#MITzF-hCWIg`tw#Rr z!&9BmZMih#SkRGmGk5+DSt1-1#~l?6ti2N?-jyNxumo451oTnh&FFEXDJ`lawIRU? zLC-pTQLV9$MfQu?SIGWt%m^nI$-=084D5c``Tbnml6RCgZ8X zz(52d)k*og!-?9GnRDU_cAG^fVqz*KlkZ>y$Qu33HuvNxAL(a20Ba~Pnq6> zU8IJPBgZxti{aJ<3{|d?Ry&@o`bN{wzFa) zwVm_x=*R7+L6+!nl0zSkrTSpeaQe$D=BK1QGTwLY9h6VH&ha@z%)v${s8ho{86PBx zKftx&zyZOBcPWD+M@ogsiXa_JtZE7y!xI>kGQ;^D?>1D%)3}IrN!b^OnkH8T?Ct0l zMs*F&Xb^RyBt7eJ#IU8aE0wdrej!VmW}211S5WHH`0*V`rR`2!BErqGja$q_w2*_1 zU!uFFtpAIbeQ5Hrr!@0G|350`DyG@lu?7bsc$0xbj#-k0l3(APWX4}rBSg~_6BLFO zgcz#Pac!FaVSFE^h9f|_KXOi3N0BGu z$5H*#{)p}Nz(<^1Vy+~PtB@z(7)6aI)cOoOQZ{a*yZ7)+QW9Ai)q_4tU?Xv{ z?Tb?%qmJ~ z{0}b)t7~LFjn!tz4VPkC1cXwd`!CURzCeB!3j7T)EeNzc&LZ&hEHl-x*C7bR{IAV@ zz9sy~Qkrepi+;LGahZ6;#I(~=u1}R%RY(>Xkjr-5`oT(aw2HrY8eOVr9U~$tX@->i z7`cKHNOK&s(cIs{>)_7?oIbh0e4G_@e&pdy`55Dv>S$^9V*`w8>~fFfzkE5YKXO zTHlG*Llm~JTN0$#lncK^<1IW-3XmGgHB_$VxhvVFNUHkBOsF+(aj z5Z#-SVp1SkNy2?AA1#^{T|%|(frkrHDHJY8|lW|#Pu zM<^7L;rR{FtQ87wqvdOh%D1Z3EEcLYElu%2I%~)nyF~_@{mb6>*E4gel@y_o**f}4 zR|!4la+et?$ltJyKiFV1uBwFz+Y>`&jZKgEeOz$IOI6)ASVAvA#0-}ex1q;=@aB8z z6PFZH>np^Gzre`4n$VIl&>dRVt`OH%Cb|533I&qmv$xFSSkIkqJEW>Q`JKVlsNX}C zzv};ZT9K8I2_=*5F=gig@! z0h#k=29ndoD9X$H@JwH3;Y{I^p5`+RC{imBCMErTw#URgNe!WG4R5G?oM5hQo=gV_cpnMElh*T8vz|Mfl zoEh11Wxt5nNBuagu&;($`dN_R9e7IJ@SP6ym`!Q(L8EW2kqV@e=^4T7_IN8i$TPH6 z0Y*=$++QP$(RuMqXYj5D;>$O_7=WW#4PO@GI^u)J7?^sESfvIp|Ma*wQXi(a`HKaz z&RB5S3WXJWCS|zK3LEBq{QItg)-r36Agi-zRPvp2qzC}QsihzPStdHQdV|qO z45YMFVg^ffk$ofcXFGNJ%~=mo;*=i}Dz-UfMx$>+i&^6{ZFNI$6uWdIO=iYJaHQ0=EuzoL?n!1*#_h1&xecbl_eM-nd%Q`XVzLF~{S+5aNY= zStYpAqcsXe2^(Z4=0Fwp2?|6IoLl+F&}egva@|Pq!X4$u+kPEs9w+cQAdS`%lXX>` zj^M6Lw&JXWk9VxOY|z*iti*jvA`NdFjceyQu**B~?>K_cqy=&`{S7h#+qxLBJQI=Y zL>)SA{qC~S%-bXnq>UU)cG5E2gB+6~jNHhCVEaCGtsyE_?9>@X0oPTwOAdNoTEZwE z;iR=2DptMAh9v0=B3}eE)nXTPsX6cF2tYCmRv0e zu4HspFwNEvJstjiUaM&9)&aE~A3O_i^m@lu45r$lX7jA)&+iY_TYc!)p47GL7@D_S z-1_w`O8Co1UYX-G&%$)EF^twKNs-CoZWEt1cW$zXl!wdb=6^~W(nn+NH<+i71wA*K4GU`f!zwo+Z3~^?&hZ#HS^G6D-SOoGpQISzW`UYwh2hS?G~pE zGECHBA=CVCSR@jcvZ8-5@#s9GdAyb|V2(NdICd$mu1~IVmMu#WyINC$&-y;=(`5y7 zW}#H*o}NogPGc*HX6+emcB6)&nvpgsoy{((SJM%E0mu}dcpE^5QV*OnoZ75A^~#g( zCPh%yqbu$N(?w}TgMZ=Ikp%XPOmQ0u#ZJsF`| zTJnRAyuZypN8vuxMfDM?ivN-k;`cta#(>qJ2tWDLUbB?n5Q^3HHu_#>EIjaakajyg z0tIa3#wl*E^mag5_-2(DrhzPDL_{{|2WE=#w%UsL*xC}{;vE21#+XqgF>XPWa+9L+ zFyb$92DY1DH%ZN*XSP0B_IQFsvO=ZSgq}oST+Sn!RuhaA%CPu6DDzb3Q99xZ!dtOt z5;$=@cg!Zztzd4ig18b7hOtXZ?#GC~*{kYCQ9u18E^ratMF{2bCg0ev0<~*AMANcw0RNCEbDI zI*Wbp$3eed2{#FN*X4$|Lqj+8q3U6xA4zo9^EaWno^rF6s~<#b4}XZI86cPNM>$v9 zMKe0-!jQN#6^P}voDFWmWQhqHXK5P2kmEr_#YV{p{8kjope zQ%^tHwA5vwj+>?5<0+TYi3U4F*-cQo1!J9eJ5?Q1O{Y0-OpbBtc12?0mJ$9Ia5}h% zXEqkm7w+PM%NnT?5ljDE9-3%3RSC(TB+O>J@vPu88L&Ab$bH0zMW-#aAS0h!54lqYi%(o?4v1Abwquy^2Qk)utc_$~npDL9s*!9*DIUm> z$ZFoJxu1>Vj8uRWNHLAnf|`yQ(tlEM`|zS$cmCyiv)ra^GS;>9Ft|D)-@@P@`}O>a zkQ{{$2cw$9$JPt%VNPO&@I%(pIFrm>54 zW;-g^$91me)WeDLE^){$YoQYzH`#n?mzjkEPMC-@cN9j4shdBNxwXxK!VZk(Jg_ z7&aWDksQ-9BrZ`L$k_crQ0gFw3dpR`K>jKj=C~L*YLCu zdq1OSnj3u~upchywN1Bal$+ye1~^Edhf}rzf^CMRO!1aAN;uYyYl!Ppy`r)nHvW>$ zZMQs#PnM%bn~%#u{qU~{m?@fSrkc8a1H&VNy1L5vVOPqvnV+>i81<)@x06C9yfG8B z9F|R-Jw48-r4XU+gljg0m#VOZb1W=BK=#1HK#|zTT!Sc5)Tq!r$n|AOQEkpl=sDCV z>(XmYESnC(*>-_STm13Sfifcuj4tCdel1b(qwt&k?>ye?Cr=S=CahXu5TSaVH?dqC z|MJ{JL0?*jj-|N6+1Uzn&FK`fa8bR2M@%*lQj3h(?iOx>+zyd-`S!qy*7|$TkifH^ zX%yu7L1IJS=uezU7-+{+f-T1>Kw?W~byx7a)nJc7?kdN~M{5#xa4M1QuPiw>Mtw`M zc2pnz*udzJ161Ho(7C?WcZI%f(qvJZ0(|a}5xWPP3P#Kc-IIIfceb5VYM&B)C*L`H z4z=u5m^Bfwo&Ij1#Zg#dZ(~QNWJ(yW6GmmMy6~OXCfEyS6dMgvm9)iX)B~G4g?h%} z^3Y?}w_7!|*9=<0pDrcd5te#6VePrE#%!hX6By~mK`eML>ce2lXg#$6VVupcx{@lL z_ydpDRF6LuFfa0c?D@^XL(4W#3SqYb!NVJQ*H3=XYCy*9X8~SNo2|3!SCD_{$5^d- zR7R#U+<~2rP0n*IO8XtKWLPox=rUvI#_}aSuONXwqLKQF%X47MxzyBoXJk>rwqn~qF><6om{dXqhGcGI-gQj@=Xy*Ld3+FiXge#?_eEbf@`USn z$?V8^9n7=%)J&2)`AauU)^8*G8@JYA=H#5H!vhVPJR&fPg1p3=BsKcAYs?j#meY2& zcqEJ28dD`ns9)5Ez~sW5sh5Qoe``bW*iRcJ!_;VQ%i#Ku z%)nqFMnttL5VjeK(_vJV3zqew4BdPlGv3+>M?JZw=>HCBcrd|u;q~xCn{kPpHevTPeC7Jr$1kEB* z&`*3?Z0i0T>zgRt_2>kxU(76j)0_J+xwKZAe=nLI4C}~^#jQ|N+Jz;04Bw8KiA>qx z~~rPH_jbKxj1&Xq>NIEWOpK7-b2BmTs>i>ss$k4h6+%Z1nZ(X%Sh0Ag}Tok_Pk zVoid!&kh5q1ZL7P&I-LaVN@W!XNsDxuB$DiojEz8!cUH@%s*)HM!s9W!Tp0KH6!T+ znF|M>pnA-7iB(}4lF(i?_GBRcqz+%OB8D=*bSco$An4xd3QuDh{fpK@rl6dPz^|P> zZaOe23=k4rDvs-frWLs?%<|{2F;vkN~T!`j|gx{i#68uo3FKMMJ|?Pk4WUHlWRrE zBWnV}?!tXsMBzvhcyi7Cf;+K7B_;J{m0y!odG6FDBA2QmnI`D9xZ#dO_~%Vlv)EMb zBg@R+V0;U(QopcGS`cY8m1v!BNB`nEc|fnBO*`z*0Du%q3(D1ucpqO)i_`}lE^^=L1WL`(`i5qeLr z8kk-)7-1f-qKY-b!5};@_AbUOivZWE@YOnXsa~3y2XZ>=TM#+2-SNXR@22a|2aq-h z+uZq+IUsCO)*C2ZybJ4aHTyS~U0)G%O1CmPV*i>P^kNhi+5NW80+X42;x1OBdY#^3 z9J}O1egB=1MRF@E^+{iZVsKxnl&@}n0h7{HLi}y@S93SyW(S=97MyBPSy1(N~>DY9%*2AF3LX_ z_oVIOZy6WjG6fL2L%NJwWbz3n#zl$E@$+nh(KV^}YN|oe#rtP&YaHC9tQI@TfTa(3 z%=&A8er&s{HB{m2(W>Uf4}mr3cs&!4;rC_ZidcT-WjNj>AYlBp-gx+(p=rdRrJO&3 zD69=-1#Ed#q{(#lZ|E7s>k{%N5d1KS5l6p4N}K#qYuYo%6ad&RTqMjYS4mSYD92O! z2{TPdVQ7O1%V{>GWD%R=HcV0$RizbUQ)E{MJJHhC0a(&knR^H^FapFd@V3<=@>!e* z)lY1^0c#1W5JR)>%NlGR30Mi;Y7Nim3WR2Cfce8^oIz09c}iK8l1U2*2M?l|AAD(h5QcwH-!>M(G<=HFFZ0=$&W1ujG0eU_O8ELcs)YisU7KQ!v5&GgN3`l>5 z|KN9T(abtA>`g?NoP22J4fN$RfP!ZAl36Zm;eL;ofkBBFWE(Vhs4*$g*Z;y|p)`>C zb9dUjT8OEPAWFHtc;hAkTS3TY5?MGEOtvlw=!@-5Jm^C6gR%$yiWd~2OU>-o9AWK0>HuS60Yt#rk$ zbOWl3Yke&EUWq!j{$j4p-H<8VKKJv5o8U4-{W}qV8Pu&ZL}dY`kd~;-60UX!Py7}$ zQigS72^xfD;W+V1Id|BWqwFzMgl5YWLa2m^Hs#Of22KEwsaP*Fyejx93fnS82Vs?T z5F4Za1*#RO)F{UgrQm%`4i;0Lupi*rH3?7Ec#b9 zqRJD1k`Ssxs0>j<>}T;6tiWx{bjy@qi(!Nsf8aUp8ElX>?Ow!rT^@hI3)i$Aj~6w6 zwPh2CqycGw0o0MB^^_Fx;QdDV+1oKkO4$INFw@E4IR z3R4;-*@dXfb;QRgm)9KLDp>%JQs1SPK^XyN8r7G%g)&ZmlBf8gu?4DX$5@ASVB_@z ziVxZwh9S!Kc-u`He}*Fs=HlHgj$*4&Q~*28Kya#&4h+Lh!T7#$L{gD-UhUnRFQ$W+ruOF10z^2_Bd zW!T#Wr75Cd$maVh;RhOEV(7G229L-s9g~M%CRU@d(r^D9Wt^<|TZxW`o5DwDiJrxbD`f!l%$z!hvrF9oP%#bfjE&f z;Zcy)s2RCggsSd_gM@}37Gl}qCJub7Ka>#UpCgB%fnwt!rn2<#Z@*sCYHl&ZmPhBp z!+B*x7`xja(oCmd4urhp``+|ByQh*GM?WsP9ne9!fwjU)xy?Cy3n-`O zvOF;`tPK8UPdU?HA~X2c`D2crpjZU{d7h|?M_Iv*!p)U?hSs{Gdhn3$LQALMlz(kM ztV(2GIMg+n&6aQ9cUt2Nvd6!wUQ^D<8$kSPoTNV(Ltk}b?7SBtaGuy?LsA%F+`Ui! zXh;bb(UJ10cHymW8^I}$I8dMA4WOH-eP77%y=1mJDV@x7DQlTO zXl0kw3Hf!eN-J~5_30k5yPG=X@|Fh$(%?)`wDKLjMs*s~Un~V0Cxj&UM~Bi2bbR8h z(78mc<{nJd26_3)3~%I<{$|Bdl@f!DK>*oBcU=XO^@}=Y<$@X|I1CIG$hdDFP}Ng< z3a0b_kECmGsPk>ZCtJ(5v25G6?ONutZMznBI@z_bY#VE}u(&NQfA4-@e?aGXAKv$M zU)Oc#4Z(tE4#xRi3XRDfSudG`HhoVi!W_AH1SJZpc_Zs9+@kJ^2f84F?z1+lKLuBV zU+oQf8>$Q{WE14&YZhzFjXFQiT!%wi{9{)Jz93TZCMLqNz%qkOZ=NI(m&nOpOPO>( zEE$KUycaHI(*Ss%pjedq2h{v+wB1h^uFR}>i-Sr#7&tDYkUax$Ggo9neY*sl+Atf6 z*mVj`WPNQ>&~W8cbnDMyUy>)5ESYWmgO8yu65b3frP!cSbw33a!;RWp3{7Mg{-WqM zN)%yQ2X37+8Fgh2Be<`p2+|NS4!D2AnQ;5YrRN+#dz)qRub?fXGfJwBlw}(f77JHu zk&EWMv7E0YdPy#NXOhhxcvRr$IldQWpdq63q4T*-6oKG^ zEeoh_WR!qXCH7!tB6&h?srLONK^?hBfd~D>nc$aACz51Y>;3B~$q;Bz{Lq;sOTz5$ z<5aC_TO)Ln;nAiw%|{Cr%UH`}LHnbpg}aKy%p4{cu@pB-_)_WB#Pd%jy+12%x)6d( z$oOwrnMh+ZMybPFND2kg%~6bTY}kP+*%|#RCdf73x$}`I&?dJJuOpb#!RWHZwK3EU zW!e7NHWjb4j5mU}L=I3W!P_<>oB$<3Q=f1Bi{4Lf21T!F9w?UwYcEKx@N8WKYz_68 zHN4bu)G`sH*p}K}-LLRQ1_tG;Z1>8pVH3hee-m`ymb;W%B{iW^Guyuj0K+t=Xx5BO zEN*?_`)PCBUm)AFq>XGZ#7&lnf+8N|?&a%^w7Tv9d*^>xJV8~nX->JiAw6_KL!T_< zo@H=&t3#T*3slU$+otEM`Uqq_k?+`Iog;fj=axq%={U0B+Zx{B(>nZ#g5MOj@NUO2SF3D zw=wPp5(>~&DeHUMc|fkJePATxwSg((ef%b8SLe?Z^!in-bZH8Kk+0OrP^M$4f@{?! zp~?0cB?8S2ZSU$jhU1GFys7otx6(b2^qa6-h~4uM^4e$+Itzk8Nq9hPl-r-pyBvFr zhL6Rrrhof4-3^bpv~lO3I{mr-6n3960kiQ=Yx}8=tE#@-tn;Ad<}hYg+L!c=d=ge5 z7rWT7PO9+AwA$_s(S%E)o3Vk`I2<%QWYKbPl3(K-^8ZVy~uQ~N@YU7dH0;k>cyRXrFz4vzvO%%&iwS)lT3>UFnuLO<(I zpLEZwPIo|J4x^nwMdbCx#03VV7=iF{PL#aislH^Xv5igX=cN;x;#uo)w~VLeo(4~~ zRfivL>;k;*UlyoTdKG_`r?&UNdek7{Lju`)?-t+6lz6}7g2p2Us)@h3g4B7oU3-G3 zsOV>{8qNwL)OPhQdwkr;buVYbdPIqbhnU}fU^UQ9J>%%a!IK4x!xXN?n_>Pt9I}bG ztBcojk4D}0F9|=DFy0u8Ol2t$Q&IPb{2cdMJ*(szs_+|A`!?7YiL2t$IG_ilGOw1q z<04x{e{?FjZIrG3oqZmu+rH>hPBKB;$d6x9L8hVMFz-5LRo z^nVDo)i}N;(l(I*yho&F4X<$mE@(@t&(NQ-cl>?^ff-J)LBXBGnUVv=#x=Tc%-?Ms zy`Z7-_&FkrTI9ezat?Qsb1olN3^S*9#ecMq#y^gUq;Qc@i@CnlP(JDPsqnEr^F#@7 zzQ10AjW2U~F?*@0h(!xrgU7c7IZnFyh8Oo;W1Jkne?`5>4-f{}<@TIkXMVFsI4@s) zXSA3$*^zeS@$sU(>{ePNi6*FC8Zu#AXt^-`>!>FMiS^{n89m2ABaC>t5qT&ZWtmsnZ)2;rvh(*fwcyO5*wOsR7BrN*t=d!wAs~w2yTWsdT_u zRy8F>U^+8CG@9hHWpOJ@m(aP#`e(rS`WdZml_=~)8>mG{WOY^ywo8XG42rXs zG|TVm_+!4y+$+G;(HnW|AUd`^zPBg2tQ1nHo0o=z5w%EqmQqG*mJD>p zxnuu(v?S`UowgX>RhK0U*D3?sv$P6b=b-7rr1kICpKkVNrXt8$Utvg1UqoqbqP|O_ zPFB~?nF6o->Tl_#I!;*7I12d><`mC4I$f{&;0DPrSLogZzNi&L&>csV z`#Nu0%l*l`%c|5uF)13gV9W!$R7iK)tYmFViI z6G;*pZ2>qr9J(0VY^^aja$8ZO+^)Yf?<4#VF@rwr&9$aiSI*%hhJrOlr;v;Hb)!S! z{+)vwsO=lvJFXttTys*irAGLQljhoBrqd7;182^Lo)2;b$A!cW{?q znnhOwvuEYm%P7g$B0u@f?Z@T2kyzo8t|hXmj~z;jQS>EvcdRG?Ir!ztL47|N--zeS zdpt1Gb__y<(VNacmmc6RbP{{{I*Y-$eNH?oR1Wg}>ZwW9IdnMCAE za>KxP+tqD?g(;UE)()AM`Kx35xyc*`l;QOzl~T5FJZ_YKTLo0{H(TIKn=|jeAi*VC zRQ-bOFn<@2Df$Ev-Y6~>8B%_copEH1S0t>qWlLFjIv+{-a1)K<}u2sO#2N@@ToV>2@Mp&{W2bL z(YQH0!GjL@QneAoD{G35OBJU)1U>aBxIy_{J32E93i`QqjC;IRw%2=|mXEm1+d(q( zbWyqtM$(uyY&^L7c0kb!_#qJD*w_^`h=E{AC+E30hWc{Jx}!-{)+xt#yeO)OhtK%q zAqfpL%W>F`f$k7bIq*u0;i+rs+jn$t|n`H6tT7T0xi2~ai1VYs>-vEW~Bes1> zFB!OTowoWmRU5zWfx-LuHGK(f40h2)CPLxk@=+E4 zc9Mi4@W=uFY!SL}pj%lFMq6QXIJI4Utx~XLwy&`3WH*1?8lb<-%Qj#&J8GX(T+BgG zp`mqsr1PA5biQw4O;*_%(NT#7Gk>92;$nAs&?)na5ZNO1}g9fraa+ zV1YfjB|+TXI*?al3Ani_aQ*lYiBvb03d@?H#Ig7x*b;~~ zJpxHx5wbkgL?8afSw-Q@i82aL2RNxkEK9dcnm&$hzh8oxDrr7)ITeX+MrZ?SQ}G5j zAub62O(qD$L$S$38z-Ifn~p8YbOlTh6*e@agL-q2lw$pE(!W>SMfyMNY5z{K*0ML>a8hUAFh4jR>rjoO>sQ$g#%sTJ}5 z=Ti}CY#{^SghOhX@O8)A0}fv^kGm?~JRFFXP`jtQTs6)aG4A6vM_5_AN?fv0p_Kd5 z`PN6)g&2kNadpRQ>p;k^O@h5{ZM&`h?Bw-edFq>UQS6|g{C`SHZ>=dzxp4aJ zlR>GdQ~3_@bFl-e!W3xtz4Qu7W|V|9T(|}sh7J>51H({6vez?oZ zw}l?}y#|M>H}Ri__iQ1Nx_?Q+z@hXq1z-99odypHZdUVUI11uU+Q-haw@w^)y*HK3 zc^*m;o|!vQPXos!e>&ujy4oYA;PljjH;&y!!6kgNJkM+`3LzW%>kZTC!|6PXYi2EI z3(vX5n3I9iwx-n}`v=cV+Qxw@xWTpy;QY?NJBSem2Lo3;dbgjqqJWa|<3ML?a%)(g zq}fS^1MLcH(GkI`r|YJ8 z{fxO)vQ&9K07?+pgRPiWZuQ-sZ!%UevV$NL$Tv{dT(~xOu{lg0z zs0nC~CYG`6srL%HHuuq#YAhS&=a=_He^QP1T5cku`sHc-TB@eMoWUhyy8$`_aizGA zT7clHl+$hPpcwz&C5`2Vz^d#a?`$hulam%=C&CrtYUq*ob}ds}o$BiEeeYpYRpK_? zVJ2)bPxqBI;ny-1Mh;5xz`}O|sQ3Q{N&@(jflA>!vue+@F3nCqFTubTH~h8=U}`UD z=xJcu(A9Se>Jr|juF12)KgfJ(%7%--`fB=>O#cP_!sLM{j42`;2@xLP(Jyn$J$@xZ z(Zn9xmuf;|1cl=K4A;0KPu3`SI#KfsvxDfc-5Rb8H*>v|+QoL|Q?cSY#058!S`}A| zL29)VpR94HLi4PbZl$E(_4)5zz=DwtsVD#+CQv9GqzMqoY4FpFt?jYj?+rW__Vhiv zG7mGgT<`e==YwpfxmOie&8nXdC^w9(`aJq?JLmYGVjB3fCNb{@&yy?f=iiA*l*X6B zLGS-N8g3vbmRPiDFbF4_RkzEIGl#5hpuog@!8^7dV8N4r<^se(Vg|zS%%p3 zDa2gOU)kxPoQr7F<}P4&7Kwiezxnq(Q2|g4nVSYkoq?>jhC8aB)P~`9sc^f!Grx9x z)zlUZZi;kj$PJ#Emxh_IuCckIS%^rgmEq1>ym@(b>|?Lzi5Q|b6rvtG|GztRo#uGvB!iTI~L9Ru3xm6c>^$noduS~pLYji23eJ|gW;-TABf zlvYAy#LneJtk#D;Ij;%X4-Q~57w|{Gg9|9yL4(S}1{`>0t#Cl<80Vs&CuAhd@wL_z z(e*6WeB}~h{iF1U90g)#n>;vSG~IXVHWagI8ww9tkFsEH2CnF9;u}}BzhE{{82!7V z_spG(49kSR)4Jf$YByr`fN+vmJ}lcr9%60%eRG?!4E9$DGOzrVAGH?1<~9S(j1Y=E zHGh$b(TN2_n}psa{Bf6QQ;sK{hcI!cEVllB3}K=+=`kIi4-zAdg>6g z_3zqmrkg!i-e5Mrap+X|f7@EjuYNV#OLsl@dpD?*ds?)Z(M@W!5L+jld+=^17Ea$S$o;)}K({YjZwVN+ zf{lk*-%LUjm|S*DwA%)o%aX+CKq;I8ni$w{2Ce|oUPTP-hVrn^*6wo?%zI%RzrM|n z4mW}(M5lC}$KpR0xNEIXZuE`gXpE}jQa&_tku>)9ZBb0w@L4vqG#!OBA8PqNplR3o zI3$Lu8E?{$fpMONuuey-pD>obuAJ3Ovafmp*VMn?pNCIpfGY9sakL89%4f}t<*!RY zX2@)h`y~2nDeOO#bonQ&OYx>SC~{x3S(jYvvTkep1&E#@;;MU&cN%RhO~#$*zTDYZ(&&BAOCg~OfdybUacKM!^x|ns`Hj`)(W;5_dxLt=YaCm^HW+)um;EN+6KceGI zWL<_#@NWvH%sc5e*isYyAARYpnmgJ0IqoZBr11qM+}lZ%_P1YMY4psxitVO5QovRs zo|N>T=W0bm(I@IB-H}N(R*HV^>YV7hrX=xgM-gCzq>=;rHjDKaJ8@VU(sJZL1HKEU zFh97`iQlnt#=9yv0hIGId{lfJ%}&XDXTcIDCvwDBgUiog`xpPN%F!9IDCYbqK3%96 z^Hnmfth~!Y1yHD$_&|8{ySwj_)ctPHkS5v^cz`n_j=i@Uk}UAZM^^r5Y{VZuw*bu& z7v|b6D|lzC+n_syd^VYGY<2e2b@;4~MxVoOKXeE3 z^56qoq3_=>q<(6S>|{-f*SFeYF&*iykfPT1-VCNkrw+w-+iP!{42!+!14o;Bos2h< z(FCtf>e#HD7c*_h=DCFN@OjY>4{>29;5bp}`~M}OCZ9N;GWg3b)?T?{s^I=ye@9ST z(33(95*GxHq1*g)M|A4pfHcZDPw$2RSD2B-W&-1hZKo}i8`cr+c}xC~3m*+#7|&EC zK526A_IU@B0J7)HN?u{RNMue?noZ_mvv=;+HNU`EF3-GiB8+idURH^*?eVP3xrxNn zi7J>#$~oKzU8JBLT_gw%;{z#CZCbcU0))#D
%TL>y8N_%dR8#OycTHI)(NS9-FgR&-(GO&%vEpWMj~2%RjDD35UzJMo_r*f#sK$r1raF zaRj{&rOgC3#18krJsonGjOmxJGUMg~;X#flfP_=PZz(6TDpp@&@g>v`4zx_zl2XGc z-KF$u5T4qzT={3E!i@Skb;PfZluJN{^4rPg&?&(gJ0pgYR>DIfoS|$`0+u?CYhKn) zuZhU(5bw0^{c+M4up88tM#1W!h<@xUYj5qcl$NX6wq?nKeMXq&zc+0e8t;lYW%@6K z38-FN{i|LG2&xv)tzm=cgdyb_+tCpLa5SIkZ5$vNz7>6(TDYud`G>#nLTH29MR#@d zQ+%6Kp4*R~GRJzaSz3SN5qIZ+mu$QsA2Uj&WzIP$!qXOT_ zQB%C6F2~HG)L2-(-sg&H8?-WJ0J{?Hx%^=u0oQw7ByFiye*hkWs-kD=A4?p>PZ|_9 z=!H^O*nV>AbenODM01W)dsWga=8ENt5W|@gQPd>b&U$}f<uMbvk_?Ftwkov1J%OA7<5ULJ~TLiLs1eFqdhE>@*+ zqW$H)5hpL*Hhgy_doDVRWvc`WkGKy@WKKs7)lRW1si$gt+fDdYe?{uf%i6rkjz)U` zPvfG~T{Zv4X}h2GmG@2=3Ng{#7DC$w6$VyqzeZcz?xhXadSjcbo{vdCaWMe)UFuru z8t8)*z=Ke1l*e9U?G%0x;RXjn?8GI^-3Xtvh{GM4_+ZK zn@Q@l^7XrI37iB(8>{R`#!KH2*lz0%$$YIm$4Npmn(;jgH+phJ#kWSkiHq>69;5ZA z2RHu4*TzAvo2L?!O?a^?w&{s3Rx$`*#k8khAv+`lsUx#k$M=vDpxQnkrf~vJ47uTR zoM!3UDa&fh0rXc3o!KK<&Q7(1T~;4;x02Q|7moy+y|X`Epf1$ro-r9u#`Gw;R64ve z9(ZsZsVce5mrR6vB)S@WtiB5xn_sdoZz-Czb-DjCJ&E-Srux`(9+n6{0*%5_Ls`Sn zLJ^l}prh;X3kml1vtR}_Hda^&!ELw5X0HXk4K%U6yXzJ&l>Rpfg6$U*yegKXc45Cg z$NmNsbv79)-e44F=P~XOekuV%g>k7Z`E0|fwp`^}W-glQuE-7O!p=9>ahqiK1mWf@ z+?go9bX$RH6%yiS2q046n1k$vrx(`G8WRiu<$nz53aFFW+6F!F zhKI%yUvDg2!#V0oXl?_&Z;TF=-_>m23F>~?Uojp}@(+?}&Q^T-6mpuaCx>SKK!Gq# zk6g#@sw;EWeL1q}2l0L1TY~rC&V&N+iijwTC6<1ptY*Uk=6E3S3X6x&5@h+zAPTAN z{pCMGiyzjrvPt^*8N@bLa<(!CZ{?4!K3rmUUqS%uzYhlIE^&!oL4}dGx1Upduyn1_uFo?qMR~Bpl69gRMht0mk%kIZL zXl2sRPpftpq1O?cNr9}RokxD>S-QLz&2s{*=C&?}n!BWlld^~xQhl2`$SrC7R3hDMo~Sj@kFdP%iIm8oR41e`qnVH7{*2TfhD#Wj0-m z%9R}+1dQB39_5bvxS)9o8aU>AMwB83Y3%?y-(B76zDl zjBnGTt-|8^tXCCYPy77~mwU%rX@xw`mb(veX5y`dpP-pP@U3=Zj@5OXTV-;G0M`h% z`bb0o6!0>QzhetRUj!XuW#r-IaAwMYMP`}Tgay6!?A7ww4@HCQY|4}lywrSh=4dONkacooUyv%`3!@pf2$ykO9c^KSpKt)2S? zRzG7qseno|qw}5AKseF-Z#^5H(uO`8F<#$r@iszYqRNw|&-31bMQkql+v0g?OH%y` zhJ|I+c;lECarQpZZPS^H)t}uECWbKcsMIr@vD%Q$lb=rUEhe(~e7)@t(Xm`(&1UOx zZ5*b?8C54b8rXcNlUvTp-_DfZpe760h%32bOi5VSPs^#4O6@i1j`ePtdM5A=I;rsf zCKPLDtBulF?gj+wYO28bbHxM~O>5S1ow5!9t1BW&9w0uxEkys=44`QIGP# zR0F;pV=OliD8Gp=v(oC z8CK)*T3xUq4*voH(0ABy+|Wsjc-^C7N`Gvl$JzNvi=pc11ZAm4k9s8C9t;+~$FKQ7 z3~-v@E$Bm7PIRw&Z_U<)Rq$D{eyG04$DjJ?6}iNS#16ebR@Pwfk_awg-D~5})B22N zv)P9)+WhvNP7V;EB1Z@jg${<=!1< zoGQc2hH)a#a6 z$;K*r`C-X{@c8haUd341xxc>&+Te5@YbhnZMc^&*mD2;%I-OycuH~bNALaR$OX`t* zP=(=!i}lg%whlTnjAyY;rggcW8^iFDc?f0S+TO*Ub~JqQ^->naB}u>Y2l3(L|l9RZvi7V5!Y~P?fOIj zCwA1d$<7N$Vkc($z86aqW`DWky=6VIoX}-=O|J#Cyj_p-L(BGDG(n16>3q!P1amxi2 z{6U5ig-?r_5uC}TYSP07zuS||wox0crQwd!;nuk0Igg`-XLpz#^h|^chHlp5^2+nO z!ZkLmCOvx9oTYVT)@Ypsj~DC9jy2d0tdm%q`(oVS9Nf)>030GzUiycng-sefr7|}j zrR5ZUopc@#c6d0ES2y6qtoo&hTqZ$3Ru`1M*KQGTX>j?pLWx*c=F`dNVt~pV?-+C; z_*5e}T>GbY&g8|E7V;WuZDj7U$JF!Q(+QDO@7{n>EXU)n?5|+dLWOLI+KX6#@MRdo z1=7Zs%Q=xH52^*B_U8bM1-!>0;2*IEgSgyYITV*9}u#LFNIKzfo z;)3O8i2|_d&P?T_QZsmXF@Okx>>p4fzND}*K5-=@22gJzhR~+ybE0awOU4<|Nw>P+ zSxyV>QgX6(Sa`sCr;lXd60Bb+vQ+-sK=>inRZ@2%JI%c?`BCsxF>Wf`!1XTbcB+Q| zko(()B7Vvblq1yM5dPKrU5-S#ts!&qO8SHE2ztFohZiti5g5vR&aL{Cy*VOT_l z7H`?h7HZ&$@lQL|FFUJ6#1?h@Q!|7+4 z1l)}Y#aWJmo@+iBUV&Ac`T_L=g*k@LxHEBSYiR=Sqt{_YTyK(=KO1TZmR`<@7{%(T zQveW!wfc`Nm6e@{oezgbw8q!sPMJ)wU^F==%+5w={33My_|Q(?YQ8A9 zfT>F}+PX}sef=8U_R@tKtwY=Dx@nwPwR|`~L)wj+sIka%v-9?j&gBKf$haQqxfeDh zvS_hM$*&EeN`d|k5^<%g%o&1rPA-*xQE#juaala?MM$a6&>G@5^_I?e9ixssQPnju zZDrow%EaWlQpxh?isKgs5MSTJ)yKzb0X#@kaOUTY>H?OGx=!iL06W^oDBfoDnhrJ8Z7frU=KSfa{8lSfEY-Zm>dl(F2oG#N1B>F4A$NHA)Q%8 z97BKy`ZuWVs4Srzr7GQ}yHizD_ZowVMaLP-)w{4j${rf6KFgJv*)qFDxFIpQHmV=T zAYiu;1G@#f<{>@?jRqvsKOv_|Lh*gB{bYt)kXsReppmO|R{i+0h_b%WY6CGhG{+UH92l3E3@R zkG`f0a!a?_0&w>*a}6@EF~rrc!=eYrU3}zARH3}|W12x!c%ha}kzl#s50IbLP+gwP zWb6bi9nsSqk67Kj_b9j<@`GXTm0;;KM`GPZfC0Nl$*C% zopwbKf?v)R^vQ4JM3!31h*Wh=vk?e;$Y6L_76!!S;8NO8Sj{|IZkUBYM;n%*ylwm} zgi5NQA5c=)ZpXYnG?o=GY#CHY%%eiWNA2waI_Zq&GkvXBxy`ebKAlCyU(||w_pUG| zD}0D-h))!(LTZC}(br@SeUl`oW=Q5OV`I~R3M4ERAU z?!`f{EXOT>?C7Iek6^JM@2arE+NAt0zy70OFp@_tqT@#^TXTjnT@5@PyUNi# z5t5W&r;LD+)ajoNy}-^w37|`iWM?=N!2k1r=P}3r=^fy;8qM`F+j>WXOHE%uahf#o zlQj>+Xs8H>FO!Ez?5YPLDG}fefdoB}L~H1O69i7sLnhZ-A2W1HVynmn&p+}Tyum^oLr$D|Y7(wRCla`@7c zWfHeYQV#RV5@Cz`?wiE z7bJSz?7wVqGX86xrS7NJg$Ffh6!EIYQUFuebADW>lj}(9Fwqq5EUU)JC5#i^PGy8Q z$7Eu0`y$wOkJbI`>`fVl8k$$c;kgcgZ8U1Cj z>%nJfr-2W@z|jQ_9EHiTqRwz}9tP%hd!{N4rnc4eeF>tis~?+pGHlLj`$83La;C=W zc9G$}BZ+68KTsNHy(fC075wlwYuR)@80HoV$QZs4H$)wfi&RjFchZ#s9EGaSXxeaY zo8g3~`+rcw_YU40vhw`I4CPolP~`&KlpQv&Ea5dP4A*wm=}3RB;hbiF@lg9MWVKZ) zvfpI;uxMp$a|E0EP$j4(C zyvtT72+}l9Dsp_p%6KXAhvBW99i5$>3@0?}Oz~UsxQo^E&ENL0%if8tyI`D7T5nn& z?VyS?>q+!&pZ_uTnhsnpT7eQ4n0$Wm5u(}-HR5em zNBnATxZ9gqPZWrf)Yt}0nGeh@&^g2!#5Az^l{1^G%h0Bdom$-@c=>nkcW!8Q0%3FH zo{quCD8V*5zUXsGMv9C+ePu+~d{A*|_Gz6E$?j*H92XB9cYQ){G5U*{dv$I>;CR~e9rD3VE z>=F~8e5rl9(x)!_9ZHVw#Gv5%HQkH@1Gho5TU3=>F_I%I3fFI7QVMZZNiHM;h~zlh zh+B8WdjV?&m{8Z0U9%&P~hW*ZAfUjnppS)o|zk8SEjeLxo3UeJ`6f zIr}8cF+} z>6DsDMX7eBBRghrZb71oER}@4@E9GY?d@QDqk*U6K&JAQ~<~YBTJ#3smm!_ z8k^-yH*dO4h%V8bb}3Dv?1{EfgQQ*e6s;2^H`Gs6!l>-Jwc25;X!vb-F5F0YQNByA zwr+EPAT-I9-ZRQwBnpZJzqokyLHJ3}R7C&rg)%HAnAp?5s6c5XHg4sGXa4#0`sRpo%J@3(Q>;M8*vFNl-)>3` zX8zL_{^xMY#Pws8sTVTId4*BGa?gXm^aaK-nQmQjvWRb?aRUbg6I(kJv>e4=whqwG zpEg;wxdFfgHQJ%G6`I~Xu-K-Fd+k<*!aDB_oa|!FI`OQFeiHN5KzwsMgp8}MVY+|} z1xbFqi#{hi;s+=n36>=mib)(K9`G+VhfncKY)P#4i%%%?Fb3xe4Oh0-3$Q@ugs)gh zavpyXnT~CXY~wo+a;O%Qwv4vOjJ$&aX1MFWbRr^ls|w{e_cTc>2h=#eSVaX3qJMZn zs*=>Uy+f{~uIe0SjxC*GD*cfU?HyKLI_4cuefP(-yxqP$%Z-e(-fhL6l}EIFTy7v@ z!bIjHqbl7&%&Ggc#{ouT3Eac!5KupO*=Jjd#TsBl@<%pUCIwk1oKvy*e>ViHtopOf z9G8$C?-6b&Y+X)HB#pYi`m=RTugw6|FIJ)mAanOvV(aJ?&a2`=0TP$mJ7Dj9B;Y8jE!gMULzyU3Ra44C;oCva z3FqgSig!eoPk$Pe6+W5%W5RFBAcU`r&eBnbfw6H$+UTXPN z3~~tgHrvt|$+tB#YR)sd3Q2QKyw4L-Zkg@Ptefw{J6Qz*UxWgD(RbXs-&d`Rx-KbG zjid3;6o3<2%t=_Hn*vCkywke!#>CdZXi4PO`hqc>|KvK6KKsE!Uyk+9$*JBRF`>TV zdh5XH;L2eNOV7Khr-`B>N7DUljvNp?s1*uv9X69vxVaoW$P?Qd-p&>XY>9vV*bljb zsX|is(U1=)(+mj^0zs7z0_;K4dejNY+{U%2T$yd>%_Q3J^_}IV1>u<-)jVryq6Xj% z3;)AApKUA=@Cl$Q*hGVMh6Q-3MWvR%>%@x6QKN{Qy*F1Hh_sAIihjXn1_49HQ*VP7Xj2uUr6)dSOvVZ#IvOR^V zL>r?dv&6vT05(g5k2saKhj%IZdl~8ei59+#a^-Q9oUSp^ zjud{Hp^if)CE>!GPMD4G4z-c3_4WSHdxm29bmLc5abuI%iGVS+hnXk3{P73P<~otDc>( zKj4<`DNg71ej)rm0Rk%#M)$9xtV3!eIqO<5>u=%NMn*Jj66hlV9V|h0{*U^&5kF$0 zv2dYHQqIRd&K7z|QBP*x9p$p^nEvIvCf8w3nUgaVA>A-%#>w6%dmCq3$+V{*Kk-mO z<5Aol`noPVy2$J0r>*6~ERjwvp*-y-jj0cvEl?o3{M5`rOrh?t`6UuxGg{yF*BEwO z)mA=%adoeWZFNKRX9gV*)v9l^dLRw2a9C51FMYMHEiv;8EIp3dkRcK@6S8@z>?nO( zD9}d6$_`IWs!LKRYmeZR)U;$LZw@$a9T21e_Gz%qcGt_%12&DL(4c+5)WfGp4Na~W zi_-KZJNYKsKG<6WJ26oS+R%eKyQf?5v5QFT8Mdk%S-<*ouE5MVX@zvVUgKHWIH`ZW zC7}xy=!$Yqh`CPGu{E^LQHR&Q-#~luqEwM8+cQe_{VdjaF*}qK;B*Zdh&0j&xp*z} zc=`UP_|P@bPIxq0)*GtUrR-kuFj1hjdgVUrlfQ-QFcox@z-(=$a@@@yW0i%;=aP_g zai2>vM>$Y)RbmfQoKCwGnpAL{9)5Zz;eMOR@`WM%!&k)mLr^P`YYeN0<-OW1;u_TO z&foD|Pks`ajvL320B*XN&?C`ReZE@e+P)nl*7x>fTY_(^SUIgdX1tFdg$1DGrEoWk z+$ZM=0FFEuOra&n^utGs)`^87N20E&GZ}Df3}C0#+X{UO^o}cYbY9!6@p?JSireE5 z(R0P{q5e?1VJ&d4Z&pO>-rU^{ZWvNlV)De3oYP3~IaiKHFtir1`j!U@UDw{RT4~tP zbb{Ktag0}bg8^ZoD5u{fjqt#TYt6$KazIM1oLsh;CUp!ZoW1Rx4>2(Qx^jwep0I~2 z{;BwKGWt>cR!Mg=P?yy`iV=?|T&!NgHppDekeM%n10hwdU2AIrTIm({k=I-mHjX>%>L@;7X?PTGs6ut%ctD`xu8*uYzqC%0%rKTNK(-Z&|yjm6H)I`QEE1JS9BA%&#{?rlMZDM zE8NH!j4Pr`4aAyc&DO@J7UQh%%nwD`dS5Ml{QVbF3-7w~NA#JiHx_i8N-S&-30rV4 zXIs`BZsgNIe)iq=SwMSZSeLGC@w9Q=JSvfuS~~3^f>!6KE`6=uWM)uluySHDzez-w zK|k}OT9nYy7W9gXND^MXt)NjAuYUQ;e4T6+4PihRZ+Wtr*66gYxBo_RB8&HjL!UIX z+)8PYi4kYIJV~EVK*s~_BHlP%Ld5mOFgLgdt$T~yMMQ~MIgofpE&+L3B&lz#_I1Y~ zYY)Rilz#%PaU4s|w}}O$e4@O0JBId^93^IVhl0{pJp7}?KJcibEuV=&%GCaLqBd<8 z@>cxD#fO94kK`8;1K~%DvI_vRV#EZ5ECe`}uWLjs`JhnV7FWk*MiQA_y%jLyJrs|X z5XXcwScc(9I1NtAc5HJd+Xp9GZ|R*=u;Z#ewfhp7sk*7V8G=zl&CcZEB2xPvVxsGm zr$g}B`08LQpkpnUgsjXIA;?^!1u@uUNtD7|2`Y!2Av+ib7J@WoPYNh7u+P?%^i8xu- z(3Pk0!(6nV(isY*Vlx%3aG|wU^Oy_SCXkiG_lR$sPEEMGkBt#u-$!XLR*@IdG9vS<9@{+R9(s&fT@Lt3&)t=km=_mbzW5jk~#}!Abw2^Z2R$LoQ1ZI8qh7J+TszzWBOY?Rbbw|ukN}7qPF~S%hO_XR{;_?3_-`&Nvml$kDDDTuAM$THodHSkb2O zd`iCeHX||VaeCq1>~-x~^4?I~dhKrhBJWv^_GeQ=##64O!N?@<^<;c5wIUs*u{HK! z1L$HQBzAx7Up~iX>VOU#$y@^vySu`-$hBiCvuq6ksK1mI6~kMH`MlM}Uk9Q#k}S`H zA~`~4D@VVX#gEvuVY4;2CJkW>XEjmoe!AMZa0szgEqsgZ6JZ3_Os=Imd1-PoeY2SxQlGDW z7|_3#T(8wN1v+oa$cNXB`I()a=A6_9*V={QBu8(*MsyRRf^1k<>Ux7{jS2>-n;d0l zI|MlY>uS23aZ#{3KfI1O^PbJ9TqjnOQa6uLKPvR%w7xywZA+)~a<-O7qm$Os#`i$l zCU;ZXh@Nk;FXIVlk{;w0o^9V&oBh?o-zGqMFL_VY`L0kW#LIfBHg|_Z^l;zJL*FWo z1iW6oZn;a=cIXT}Azq8SjD@(aEWrZ;C)xl0FJTSB+WcUtSf;l9o77~Ca8Ayts(|$-RTSf0S%Y3?dMji}-I=u$9g2 z{5BC3<*G0=LWA4r=*e;{*R2^&KJl%s)uA&IG5MNFL;cy#k2oRub719bEveN^vXdXl z*l=R(aDAf^BkMrGyX~fztgX4qJ=ct4mxor?1xq*;P>?wSybe?iJ{L;Dzh*am0MhX| zQCngUE4w&mU}G{w1)j08?)gJ>o!D37fl2H1QZJ}gq_6hos=C~?R#wlLEHoNm{BdOo z_i|Emo=ynEBi3Td(Gk%;ke>wERrSyJjU#nQD=BJls7Q)9`_E%!_-MUjflrRMn&?gZPNDUc znw>r@VGXWqCy&2tYgRub9qQHhU~XTu)U2Jbd1;Gu&W%OGYSt@pPvg~P{SCc`%vmP6 zuLJC3WNn1fZ-s>g|9bu1%GeK>`OT(ibW|WHEqb$bY{v?bZdK+IzGw<$P(cyn8Qg!^ z^MSyHF0yoQo65Dc5|y_p`FyenqsiuBuBCO%>rHm>&gzX835)IufwlAxS}S+aIy;H4 zL;Q9C&#IG3*z8vqn;I1gn0Uabhh*zLarboo*Su#c&!~O=n*5F5M0k@<4GSgL_)j2f zie)c7SU6=vQmcJ4{hxibv1Fwr;Ml{;MXRUiC^=AkmNp z6|ZyCcS1W;*@Dq(q@Sf($efk`3v&vL^`hcc00A2tR12U_1j`HJj)YtA=CDt9{Z>VvqF)qu6qWUfhq zyMH_W<5ntBEiEp6Zk||6&p-ch)Li1VJ0V;_E>zg^Y61Zp391IrB)r$N265w=*d)-~j+q8m=l{<7LU#_eEE9n-Yy z?c3@H_xsJ=bMydCQQJ=qkI~C7xtfN%J!&fut{^vRAYKI!u%V!O08JtMad&KHmR45# z1ONb_zC8KDi|OZ{`D|&F8dJgQ{=7mUDt$w}Du%MUSDFWmsD0lmQrL6t1WTKVoTEc ziRmoaNn*c8Qu`%HBNEmDv>HL|>#K)?dfNYXv7O%aPeu>b&2Z>~J| zJbL|0UqS08h3K<`C7C#)2HQTzh9Q+}eP4x}vFS=IV5Ud1oRDIEaIfVw=3|Hl&g68? z`fc=@E1pSfM#pJT_yAMU)LC?c8c{>>Dp2bsyX32Vg$|sUp>~7n0@R&suHm&4<1{rk zN&o-=>dWcH1$xg{zeKk@{Gb$NnAs)_W9(NsiZEmLGHYyDbEfghr0pH)nATHsJ?397 z=3c;@`d2J}>Y10)6L(#x&JRe$hi0_m&=v&+FsOKC@$btl7#|s>jnixOtRY}!P+@?& zlkGJ;Haw)hy`Rv=83X_TP+z`v$F20?>#xy}PO0NB8&h5-$ zcgP4(%(%NEd8O&*Mb-44YCe;qC-yD+KKH*cgot4-o(W zfco;#kwf&w+is$p9=ebEN?#w>mFdBlw17=a|00B|IZ_VO7Z?^>(|k)#!AkKgup_-8 zx?=k|bk)xDXnG_#NK^Y-YG|8+9LOtRQ$ZpY0@eZ*1gI}XH0ClB$*RHV2jtmXasQNjqzELkYFdYB@ zfQ&e`Fh{rUdzkJ%xK|ak2kFr4N%gU#Xn~u!6x_)Xz^P;WG9nPOj`Ja~ablWwt=*^z z*R$7erP1E7Z{BE`$>3;!jL3Va90=H=K&1fcNPVtbVUabbPRATdK?>Gk?g|SU)ZXks z#ie;fRG={C3a6FTKK0c})QyTdiLxqkd1IUso^WQOv&=nTD0q&~avr=iB0z||uP7$+qjvj4tYai4jiIj66R*ZJi|ZG7;hm1SB{`kK43T1BeM`i>2Hoh}Wl z)7bC`jSr8~lsZk1j%)LS$n83-JvcHTBN`}RTNsT66#}Rm^}A;KilR+!bg^nVp-J;3 z$?9E2mQ&Z{hM(M$^6WC&vJ3gIbt#aEnSY_kb zq`9?`h9g183n&)Rn8jHDjl;i*QVoh|OSqzr7XiYF0ssobY}m*`)^s&v+-&?v!qf<7 z890c&Plee^-PZ`@Hx~e8O?$7eU_ohmxwr+bS?eBw$f}f;!Ut>(+YoPs>T^%ab%M#=tk*U6k5F2cHZ zv1{C%5YYMp15@XVs0=~d0#~$_2v}5~0Mv=lH~Im~c+)Cut3z-bKph<)9INEun7Q$< zV}rVCL@W;hM}83GmkTYWiMyY-pe?K2P;fuk76@2Wp8&K0))?R6wgKxH$P-*&Wr5Zg zNJZN&oGb-xi-LeOT)?9G1a$`2F91LWbpptP>@9t`QbA&H(deWVv@MHZ*p5XdQ2ck8D46()!2HDZ}VU_It zZ^<~Y00000Xg+8$7Y&X$u)xKTBRIG+4H2+7sQ>@~nhc(EpnBk{1RA&yas~(3O#@tr zaZ*9kz%>m30Lek!IXLm%h;g(5+=Pn&2iOlE3#>R$RqqFZlL-I-002-1Xoa;9ob-ah z0S_a<0$bIChF3iR0001RgSJ6-gc~t%q>Bc@wb}>nvM56Oz?uiI3O5c;Bxnn`)&T$j zR1-X*S_V#d3E-f|Ax03VGF^&s5&-}$grn{QAU?QXiU3EsMc`zY1P**$Vn!h@L&V}F z0svYF#47-eCGOo_zVDkTfn8eEN-Y2KFgLGuD*tY6I?Pl0cl7v4s2f5Se!u6c5n~08dzQds2x=fd6^cLQ97W) z;KWu0oQSlf5U_$QMJ!Gp0HDP{xB`%p;_jU`-KF-v!c(F;WOOcYIx7TD#vr5=u!2Ed zigDrq04)dN6@bcsyU`7fNF%^$tROfkgYy3a?>-*mgEB1E00000NkvXXu0mjfzXY`T literal 0 HcmV?d00001 From 8ce2b438f33f819ab9408029008c9939f22e61f3 Mon Sep 17 00:00:00 2001 From: Lena Sotnik Date: Mon, 25 Sep 2023 02:23:16 -0700 Subject: [PATCH 09/10] Commit with change of controller test and removed status codes --- .../greencity/controller/HabitController.java | 7 ++-- .../controller/HabitControllerTest.java | 36 ++++++++++++------- .../greencity/service/HabitServiceImpl.java | 4 +-- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/greencity/controller/HabitController.java b/core/src/main/java/greencity/controller/HabitController.java index 8fbf0758ff..b1e15cf720 100644 --- a/core/src/main/java/greencity/controller/HabitController.java +++ b/core/src/main/java/greencity/controller/HabitController.java @@ -39,7 +39,6 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.PutMapping; @@ -226,8 +225,7 @@ public ResponseEntity> findAllHabitsTags(@ApiIgnore @ValidLanguage @ApiResponses(value = { @ApiResponse(code = 201, message = HttpStatuses.CREATED, response = CustomHabitDtoResponse.class), @ApiResponse(code = 400, message = HttpStatuses.BAD_REQUEST), - @ApiResponse(code = 401, message = HttpStatuses.UNAUTHORIZED), - @ApiResponse(code = 404, message = HttpStatuses.NOT_FOUND), + @ApiResponse(code = 401, message = HttpStatuses.UNAUTHORIZED) }) @PostMapping(value = "/custom", consumes = {MediaType.APPLICATION_JSON_UTF8_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE}) @@ -278,12 +276,11 @@ public ResponseEntity> getFriendsAssignedToHabitProf @ApiResponse(code = 200, message = HttpStatuses.OK, response = CustomHabitDtoResponse.class), @ApiResponse(code = 400, message = HttpStatuses.BAD_REQUEST), @ApiResponse(code = 401, message = HttpStatuses.UNAUTHORIZED), - @ApiResponse(code = 403, message = HttpStatuses.FORBIDDEN), @ApiResponse(code = 404, message = HttpStatuses.NOT_FOUND) }) @PutMapping(value = "/update/{habitId}") public ResponseEntity updateCustomHabit(@PathVariable Long habitId, - @RequestBody @Valid CustomHabitDtoRequest request, @ApiIgnore Principal principal, + @RequestPart @Valid CustomHabitDtoRequest request, @ApiIgnore Principal principal, @ApiParam(value = "Image of habit") @ImageValidation @RequestPart(required = false) MultipartFile image) { return ResponseEntity.status(HttpStatus.OK) .body(habitService.updateCustomHabit(request, habitId, principal.getName(), image)); diff --git a/core/src/test/java/greencity/controller/HabitControllerTest.java b/core/src/test/java/greencity/controller/HabitControllerTest.java index 4a61091139..981ee60eff 100644 --- a/core/src/test/java/greencity/controller/HabitControllerTest.java +++ b/core/src/test/java/greencity/controller/HabitControllerTest.java @@ -9,13 +9,10 @@ import greencity.service.HabitService; import java.security.Principal; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; - -import java.util.Optional; +import java.util.*; import greencity.service.TagsService; +import lombok.SneakyThrows; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -36,7 +33,9 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; + +import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.validation.Validator; @@ -310,20 +309,33 @@ void getFriendsAssignedToHabitProfilePictures() throws Exception { } @Test - void updateCustomHabit() throws Exception { + @SneakyThrows + void updateCustomHabit() { Long habitId = 1L; + byte[] imageContent = "sampleImageData".getBytes(); + MockMultipartFile imageFile = new MockMultipartFile("image", imageContent); CustomHabitDtoRequest dto = ModelUtils.getAddCustomHabitDtoRequest(); + ObjectMapper objectMapper = new ObjectMapper(); objectMapper.findAndRegisterModules(); String requestedJson = objectMapper.writeValueAsString(dto); - - mockMvc.perform(put(habitLink + "/update/{habitId}", habitId) + MockMultipartFile jsonFile = new MockMultipartFile("request", "", + "application/json", requestedJson.getBytes()); + + MockMultipartHttpServletRequestBuilder builder = + MockMvcRequestBuilders.multipart(habitLink + "/update/{habitId}", habitId); + builder.with(request -> { + request.setMethod("PUT"); + return request; + }); + mockMvc.perform(builder + .file(jsonFile) + .file(imageFile) .principal(principal) - .contentType(MediaType.APPLICATION_JSON) - .content(requestedJson)) + .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()); - verify(habitService).updateCustomHabit(dto, habitId, principal.getName(), null); + verify(habitService).updateCustomHabit(dto, habitId, principal.getName(), imageFile); } } diff --git a/service/src/main/java/greencity/service/HabitServiceImpl.java b/service/src/main/java/greencity/service/HabitServiceImpl.java index a49758cdb3..6637384a86 100644 --- a/service/src/main/java/greencity/service/HabitServiceImpl.java +++ b/service/src/main/java/greencity/service/HabitServiceImpl.java @@ -367,7 +367,7 @@ public CustomHabitDtoResponse updateCustomHabit(CustomHabitDtoRequest habitDto, .orElseThrow(() -> new WrongEmailException(ErrorMessage.USER_NOT_FOUND_BY_EMAIL + userEmail)); Habit toUpdate = habitRepo.findById(habitId) .orElseThrow(() -> new NotFoundException(ErrorMessage.CUSTOM_HABIT_NOT_FOUND + habitId)); - checkAccessForAdminAndModerator(user, toUpdate); + checkAccessForAdminAndModeratorAndByUserId(user, toUpdate); enhanceHabitWithNewData(toUpdate, habitDto, user, image); Habit updatedHabit = habitRepo.save(toUpdate); return buildAddCustomHabitDtoResponse(updatedHabit, user.getId()); @@ -432,7 +432,7 @@ private void setCustomShoppingListItemToHabit(CustomHabitDtoRequest habitDto, Ha habit.setCustomShoppingListItems(customShoppingListItems); } - private void checkAccessForAdminAndModerator(User user, Habit habit) { + private void checkAccessForAdminAndModeratorAndByUserId(User user, Habit habit) { if (user.getRole() != Role.ROLE_ADMIN && user.getRole() != Role.ROLE_MODERATOR && !user.getId().equals(habit.getUserId())) { throw new UserHasNoPermissionToAccessException(ErrorMessage.USER_HAS_NO_PERMISSION); From f4530ac5eb99389fefb607038fc2b314676f36e3 Mon Sep 17 00:00:00 2001 From: Lena Sotnik Date: Mon, 25 Sep 2023 02:50:03 -0700 Subject: [PATCH 10/10] Some changes to remove asterisks --- core/src/test/java/greencity/ModelUtils.java | 5 ----- .../controller/HabitControllerTest.java | 6 ++++-- core/src/test/resources/test.jpg | Bin 36407 -> 0 bytes .../java/greencity/service/HabitService.java | 7 +++---- service/src/test/java/greencity/ModelUtils.java | 7 ++++++- .../greencity/service/HabitServiceImplTest.java | 5 ++++- 6 files changed, 17 insertions(+), 13 deletions(-) delete mode 100644 core/src/test/resources/test.jpg diff --git a/core/src/test/java/greencity/ModelUtils.java b/core/src/test/java/greencity/ModelUtils.java index fb4bcf7b5e..9c9844aa6f 100644 --- a/core/src/test/java/greencity/ModelUtils.java +++ b/core/src/test/java/greencity/ModelUtils.java @@ -768,9 +768,4 @@ public static FilterEventDto getNullFilterEventDto() { .tags(null) .build(); } - - public static Habit getHabitWithCustom() { - return Habit.builder().id(1L).image("image.png") - .complexity(1).isCustomHabit(true).tags(Set.of(getTag())).build(); - } } diff --git a/core/src/test/java/greencity/controller/HabitControllerTest.java b/core/src/test/java/greencity/controller/HabitControllerTest.java index 981ee60eff..44e077176e 100644 --- a/core/src/test/java/greencity/controller/HabitControllerTest.java +++ b/core/src/test/java/greencity/controller/HabitControllerTest.java @@ -9,7 +9,10 @@ import greencity.service.HabitService; import java.security.Principal; -import java.util.*; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Optional; import greencity.service.TagsService; import lombok.SneakyThrows; @@ -33,7 +36,6 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; diff --git a/core/src/test/resources/test.jpg b/core/src/test/resources/test.jpg deleted file mode 100644 index a790bbcc3cf13581e244201d5ee877ba9037dc33..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36407 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 diff --git a/service-api/src/main/java/greencity/service/HabitService.java b/service-api/src/main/java/greencity/service/HabitService.java index f5f01454f1..5a43d1614e 100644 --- a/service-api/src/main/java/greencity/service/HabitService.java +++ b/service-api/src/main/java/greencity/service/HabitService.java @@ -118,8 +118,7 @@ PageableDto getAllByDifferentParameters(UserVO userVO, Pageable pageab * @return {@link CustomHabitDtoResponse} instance. * @author Lilia Mokhnatska */ - CustomHabitDtoResponse addCustomHabit(CustomHabitDtoRequest addCustomHabitDtoRequest, - MultipartFile image, + CustomHabitDtoResponse addCustomHabit(CustomHabitDtoRequest addCustomHabitDtoRequest, MultipartFile image, String userEmail); /** @@ -141,6 +140,6 @@ CustomHabitDtoResponse addCustomHabit(CustomHabitDtoRequest addCustomHabitDtoReq * @return {@link CustomHabitDtoResponse} instance. * @author Olena Sotnik. */ - CustomHabitDtoResponse updateCustomHabit(CustomHabitDtoRequest customHabitDtoRequest, - Long habitId, String userEmail, MultipartFile image); + CustomHabitDtoResponse updateCustomHabit(CustomHabitDtoRequest customHabitDtoRequest, Long habitId, + String userEmail, MultipartFile image); } diff --git a/service/src/test/java/greencity/ModelUtils.java b/service/src/test/java/greencity/ModelUtils.java index 445e8151de..5d7843f511 100644 --- a/service/src/test/java/greencity/ModelUtils.java +++ b/service/src/test/java/greencity/ModelUtils.java @@ -194,7 +194,12 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import static greencity.enums.UserStatus.ACTIVATED; diff --git a/service/src/test/java/greencity/service/HabitServiceImplTest.java b/service/src/test/java/greencity/service/HabitServiceImplTest.java index 2a236a0b06..547d96f0dd 100644 --- a/service/src/test/java/greencity/service/HabitServiceImplTest.java +++ b/service/src/test/java/greencity/service/HabitServiceImplTest.java @@ -60,7 +60,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList;