diff --git a/java/pom.xml b/java/pom.xml index 6a6f1a92..c4bd0874 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -22,11 +22,13 @@ 1.0.2.2-RELEASE 1.0.3 + 1.0.0 11 1.18.0 - 1.6.1 + 1.6.9 2.6.4 + 3.6.0 3.6.0 2.13.0 4.1.0 @@ -106,6 +108,12 @@ ${stacks.core.commons.version} + + com.amido.stacks.modules + stacks-azure-cosmos + ${stacks.azure.cosmos.version} + + org.springframework.boot spring-boot-starter-actuator @@ -228,15 +236,6 @@ provided - - org.springframework.boot spring-boot-starter-test diff --git a/java/sonar-project.properties b/java/sonar-project.properties index f5691ee6..34693065 100644 --- a/java/sonar-project.properties +++ b/java/sonar-project.properties @@ -6,7 +6,6 @@ # sonar.login=${env.SONAR_TOKEN} # sonar.organization=${env.SONAR_ORGANIZATION} sonar.sourceEncoding=UTF-8 - ################################### # Branch and PR Analysis Variables # These are all supplied by the Pipeline @@ -20,7 +19,6 @@ sonar.sourceEncoding=UTF-8 # sonar.pullrequest.github.repository=${env.SONAR_GITHUB_REPO} # sonar.pullrequest.base=${env.SONAR_TARGET_BRANCH} ################################### - sonar.sources=src/main sonar.java.binaries=target sonar.java.libraries=.m2 @@ -28,6 +26,5 @@ sonar.junit.reportPaths=target/surefire-reports sonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml sonar.language=java sonar.java.source=11 - sonar.cpd.exclusions=**/model/* -sonar.coverage.exclusions=**/AuthControllerImpl*,**/ApplicationConfig* +sonar.coverage.exclusions=**/AuthControllerImpl*,**/ApplicationConfig*,**/exception/** diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/CategoryController.java b/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/CategoryController.java index 4cbc63ab..bf650902 100644 --- a/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/CategoryController.java +++ b/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/CategoryController.java @@ -48,7 +48,8 @@ ResponseEntity createCategory( @Valid @RequestBody CreateCategoryRequest body, @Parameter(hidden = true) @RequestAttribute("CorrelationId") String correlationId) { - return new ResponseEntity<>(categoryService.create(body, correlationId), HttpStatus.CREATED); + return new ResponseEntity<>( + categoryService.create(menuId, body, correlationId), HttpStatus.CREATED); } @PutMapping("/{categoryId}") @@ -82,6 +83,7 @@ ResponseEntity deleteCategory( UUID categoryId, @Parameter(hidden = true) @RequestAttribute("CorrelationId") String correlationId) { + categoryService.delete(menuId, categoryId, correlationId); return new ResponseEntity<>(OK); } } diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/ItemController.java b/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/ItemController.java index c21ab46b..ae2e3163 100644 --- a/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/ItemController.java +++ b/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/ItemController.java @@ -70,7 +70,7 @@ ResponseEntity updateItem( @Parameter(hidden = true) @RequestAttribute("CorrelationId") String correlationId) { return new ResponseEntity<>( - itemService.update(menuId, categoryId, body, correlationId), HttpStatus.OK); + itemService.update(menuId, categoryId, itemId, body, correlationId), HttpStatus.OK); } @DeleteMapping("/{itemId}") @@ -87,6 +87,7 @@ ResponseEntity deleteItem( @Parameter(description = "Item id", required = true) @PathVariable("itemId") UUID itemId, @Parameter(hidden = true) @RequestAttribute("CorrelationId") String correlationId) { + itemService.delete(menuId, categoryId, itemId, correlationId); return new ResponseEntity<>(OK); } } diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/MenuController.java b/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/MenuController.java index 8fffba5e..bc649a3a 100644 --- a/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/MenuController.java +++ b/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/MenuController.java @@ -1,14 +1,10 @@ package com.amido.stacks.workloads.menu.api.v1; -import static org.springframework.http.HttpStatus.OK; - import com.amido.stacks.core.api.annotations.CreateAPIResponses; import com.amido.stacks.core.api.annotations.DeleteAPIResponses; import com.amido.stacks.core.api.annotations.ReadAPIResponses; import com.amido.stacks.core.api.annotations.SearchAPIResponses; import com.amido.stacks.core.api.annotations.UpdateAPIResponses; -import com.amido.stacks.core.api.dto.response.ResourceCreatedResponse; -import com.amido.stacks.core.api.dto.response.ResourceUpdatedResponse; import com.amido.stacks.workloads.menu.api.v1.dto.request.CreateMenuRequest; import com.amido.stacks.workloads.menu.api.v1.dto.request.UpdateMenuRequest; import com.amido.stacks.workloads.menu.api.v1.dto.response.MenuDTO; @@ -50,11 +46,11 @@ public class MenuController { description = "Adds a menu", operationId = "CreateMenu") @CreateAPIResponses - ResponseEntity createMenu( - @Valid @RequestBody CreateMenuRequest body, + ResponseEntity createMenu( + @Valid @RequestBody CreateMenuRequest dto, @Parameter(hidden = true) @RequestAttribute("CorrelationId") String correlationId) { - return new ResponseEntity<>(menuService.create(body, correlationId), HttpStatus.CREATED); + return new ResponseEntity<>(menuService.create(dto, correlationId), HttpStatus.CREATED); } @GetMapping @@ -75,10 +71,11 @@ ResponseEntity searchMenu( @RequestParam(value = "searchTerm", required = false) String searchTerm, @RequestParam(value = "restaurantId", required = false) UUID restaurantId, @RequestParam(value = "pageSize", required = false, defaultValue = "20") Integer pageSize, - @RequestParam(value = "pageNumber", required = false, defaultValue = "1") - Integer pageNumber) { + @RequestParam(value = "pageNumber", required = false, defaultValue = "1") Integer pageNumber, + @Parameter(hidden = true) @RequestAttribute("CorrelationId") String correlationId) { - return ResponseEntity.ok(menuService.search(searchTerm, restaurantId, pageSize, pageNumber)); + return ResponseEntity.ok( + menuService.search(searchTerm, restaurantId, pageSize, pageNumber, correlationId)); } @GetMapping(value = "/{id}") @@ -108,12 +105,12 @@ ResponseEntity getMenu( summary = "Update a menu", description = "Update a menu with new information") @UpdateAPIResponses - ResponseEntity updateMenu( + ResponseEntity updateMenu( @Parameter(description = "Menu id", required = true) @PathVariable("id") UUID menuId, @Valid @RequestBody UpdateMenuRequest body, @Parameter(hidden = true) @RequestAttribute("CorrelationId") String correlationId) { - return new ResponseEntity<>(menuService.update(body, correlationId), HttpStatus.OK); + return new ResponseEntity<>(menuService.update(menuId, body, correlationId), HttpStatus.OK); } @DeleteMapping(value = "/{id}") @@ -127,6 +124,7 @@ ResponseEntity deleteMenu( @Parameter(description = "Menu id", required = true) @PathVariable("id") UUID menuId, @Parameter(hidden = true) @RequestAttribute("CorrelationId") String correlationId) { - return new ResponseEntity<>(OK); + menuService.delete(menuId, correlationId); + return new ResponseEntity<>(HttpStatus.OK); } } diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/dto/request/GenerateTokenRequest.java b/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/dto/request/GenerateTokenRequest.java deleted file mode 100644 index 973b8369..00000000 --- a/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/dto/request/GenerateTokenRequest.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.amido.stacks.workloads.menu.api.v1.dto.request; - -import com.fasterxml.jackson.annotation.JsonProperty; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@AllArgsConstructor -public class GenerateTokenRequest { - - @JsonProperty("client_id") - @NotBlank - private String client_id = null; - - @JsonProperty("client_secret") - @NotBlank - private String client_secret = null; - - @JsonProperty("audience") - @NotNull - private String audience = null; - - @JsonProperty("grant_type") - @NotNull - private String grant_type = null; -} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/dto/request/UpdateMenuRequest.java b/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/dto/request/UpdateMenuRequest.java index 0be7ee9c..b92a80e7 100644 --- a/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/dto/request/UpdateMenuRequest.java +++ b/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/dto/request/UpdateMenuRequest.java @@ -3,13 +3,11 @@ import com.fasterxml.jackson.annotation.JsonProperty; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; -import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor -@AllArgsConstructor public class UpdateMenuRequest { @JsonProperty("name") diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/dto/response/ItemDTO.java b/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/dto/response/ItemDTO.java index 53642a3a..6a399a83 100644 --- a/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/dto/response/ItemDTO.java +++ b/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/dto/response/ItemDTO.java @@ -2,13 +2,11 @@ import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor -@AllArgsConstructor @Schema(name = "Item") public class ItemDTO { diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/dto/response/SearchMenuResultItem.java b/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/dto/response/SearchMenuResultItem.java index e03cefb8..77684b33 100644 --- a/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/dto/response/SearchMenuResultItem.java +++ b/java/src/main/java/com/amido/stacks/workloads/menu/api/v1/dto/response/SearchMenuResultItem.java @@ -2,13 +2,11 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.util.UUID; -import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor -@AllArgsConstructor public class SearchMenuResultItem { @JsonProperty("id") diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/domain/Category.java b/java/src/main/java/com/amido/stacks/workloads/menu/domain/Category.java index defe4f81..9e967172 100644 --- a/java/src/main/java/com/amido/stacks/workloads/menu/domain/Category.java +++ b/java/src/main/java/com/amido/stacks/workloads/menu/domain/Category.java @@ -2,7 +2,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -18,16 +17,4 @@ public class Category { private String description; @Builder.Default private List items = new ArrayList<>(); - - public Category addOrUpdateItem(Item item) { - if (this.items == null) { - this.items = new ArrayList<>(); - } - this.items = - this.items.stream() - .filter(c -> !c.getId().equals(item.getId())) - .collect(Collectors.toList()); - this.items.add(item); - return this; - } } diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/domain/Menu.java b/java/src/main/java/com/amido/stacks/workloads/menu/domain/Menu.java index 775189dd..ded01d5d 100644 --- a/java/src/main/java/com/amido/stacks/workloads/menu/domain/Menu.java +++ b/java/src/main/java/com/amido/stacks/workloads/menu/domain/Menu.java @@ -2,7 +2,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -24,16 +23,4 @@ public class Menu { @Builder.Default private List categories = new ArrayList<>(); private Boolean enabled; - - public Menu addOrUpdateCategory(Category category) { - if (this.categories == null) { - this.categories = new ArrayList<>(); - } - this.categories = - this.categories.stream() - .filter(c -> !c.getId().equals(category.getId())) - .collect(Collectors.toList()); - this.categories.add(category); - return this; - } } diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/exception/CategoryAlreadyExistsException.java b/java/src/main/java/com/amido/stacks/workloads/menu/exception/CategoryAlreadyExistsException.java new file mode 100644 index 00000000..3fe29666 --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/exception/CategoryAlreadyExistsException.java @@ -0,0 +1,19 @@ +package com.amido.stacks.workloads.menu.exception; + +import java.util.UUID; + +public class CategoryAlreadyExistsException extends RuntimeException { + + private static final int EXCEPTION_CODE = 11409; + + public CategoryAlreadyExistsException(UUID menuId, String name) { + super( + String.format( + "A category with the name '%s' already exists for the menu with id '%s'.", + name, menuId)); + } + + public int getExceptionCode() { + return EXCEPTION_CODE; + } +} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/exception/CategoryDoesNotExistException.java b/java/src/main/java/com/amido/stacks/workloads/menu/exception/CategoryDoesNotExistException.java new file mode 100644 index 00000000..2e9583d3 --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/exception/CategoryDoesNotExistException.java @@ -0,0 +1,19 @@ +package com.amido.stacks.workloads.menu.exception; + +import java.util.UUID; + +public class CategoryDoesNotExistException extends RuntimeException { + + private static final int EXCEPTION_CODE = 11404; + + public CategoryDoesNotExistException(UUID menuId, UUID categoryId) { + super( + String.format( + "A category with the id '%s' does not exist for menu with id '%s'.", + categoryId, menuId)); + } + + public int getExceptionCode() { + return EXCEPTION_CODE; + } +} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/exception/ItemAlreadyExistsException.java b/java/src/main/java/com/amido/stacks/workloads/menu/exception/ItemAlreadyExistsException.java new file mode 100644 index 00000000..2f94fc43 --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/exception/ItemAlreadyExistsException.java @@ -0,0 +1,20 @@ +package com.amido.stacks.workloads.menu.exception; + +import java.util.UUID; + +public class ItemAlreadyExistsException extends RuntimeException { + + private static final int EXCEPTION_CODE = 12409; + + public ItemAlreadyExistsException(UUID menuId, UUID categoryId, String name) { + super( + String.format( + "An item with the name '%s' already exists for the category '%s' in menu with " + + "id '%s'.", + name, categoryId, menuId)); + } + + public int getExceptionCode() { + return EXCEPTION_CODE; + } +} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/exception/ItemDoesNotExistsException.java b/java/src/main/java/com/amido/stacks/workloads/menu/exception/ItemDoesNotExistsException.java new file mode 100644 index 00000000..a495bfce --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/exception/ItemDoesNotExistsException.java @@ -0,0 +1,19 @@ +package com.amido.stacks.workloads.menu.exception; + +import java.util.UUID; + +public class ItemDoesNotExistsException extends RuntimeException { + + private static final int EXCEPTION_CODE = 12404; + + public ItemDoesNotExistsException(UUID menuId, UUID categoryId, UUID itemId) { + super( + String.format( + "An item with the id '%s' does not exists for category with the id '%s' and for menu with id '%s'.", + itemId, categoryId, menuId)); + } + + public int getExceptionCode() { + return EXCEPTION_CODE; + } +} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/exception/MenuAlreadyExistsException.java b/java/src/main/java/com/amido/stacks/workloads/menu/exception/MenuAlreadyExistsException.java new file mode 100644 index 00000000..fea0ccda --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/exception/MenuAlreadyExistsException.java @@ -0,0 +1,19 @@ +package com.amido.stacks.workloads.menu.exception; + +import java.util.UUID; + +public class MenuAlreadyExistsException extends RuntimeException { + + private static final int EXCEPTION_CODE = 10409; + + public MenuAlreadyExistsException(UUID restaurantId, String name) { + super( + String.format( + "A Menu with the name '%s' already exists for the restaurant with id '%s'.", + name, restaurantId)); + } + + public int getExceptionCode() { + return EXCEPTION_CODE; + } +} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/exception/MenuApiException.java b/java/src/main/java/com/amido/stacks/workloads/menu/exception/MenuApiException.java new file mode 100644 index 00000000..2090e963 --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/exception/MenuApiException.java @@ -0,0 +1,14 @@ +package com.amido.stacks.workloads.menu.exception; + +public class MenuApiException extends RuntimeException { + + private static final int EXCEPTION_CODE = 10000; + + public MenuApiException(String message) { + super(message); + } + + public int getExceptionCode() { + return EXCEPTION_CODE; + } +} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/exception/MenuNotFoundException.java b/java/src/main/java/com/amido/stacks/workloads/menu/exception/MenuNotFoundException.java new file mode 100644 index 00000000..441e8479 --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/exception/MenuNotFoundException.java @@ -0,0 +1,16 @@ +package com.amido.stacks.workloads.menu.exception; + +import java.util.UUID; + +public class MenuNotFoundException extends RuntimeException { + + private static final int EXCEPTION_CODE = 10404; + + public MenuNotFoundException(UUID menuId) { + super(String.format("A menu with id '%s' does not exist.", menuId)); + } + + public int getExceptionCode() { + return EXCEPTION_CODE; + } +} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/exception/handler/MenuApiExceptionHandler.java b/java/src/main/java/com/amido/stacks/workloads/menu/exception/handler/MenuApiExceptionHandler.java new file mode 100644 index 00000000..c2d5d145 --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/exception/handler/MenuApiExceptionHandler.java @@ -0,0 +1,62 @@ +package com.amido.stacks.workloads.menu.exception.handler; + +import com.amido.stacks.core.api.dto.ErrorResponse; +import com.amido.stacks.workloads.menu.exception.CategoryAlreadyExistsException; +import com.amido.stacks.workloads.menu.exception.CategoryDoesNotExistException; +import com.amido.stacks.workloads.menu.exception.ItemAlreadyExistsException; +import com.amido.stacks.workloads.menu.exception.ItemDoesNotExistsException; +import com.amido.stacks.workloads.menu.exception.MenuAlreadyExistsException; +import com.amido.stacks.workloads.menu.exception.MenuNotFoundException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +@RequestMapping(produces = "application/json") +public class MenuApiExceptionHandler { + + @ResponseBody + @ExceptionHandler(MenuAlreadyExistsException.class) + @ResponseStatus(HttpStatus.CONFLICT) + ErrorResponse menuAlreadyExistsExceptionHandler(MenuAlreadyExistsException ex) { + return new ErrorResponse(ex.getExceptionCode(), 0, "", ex.getMessage()); + } + + @ResponseBody + @ExceptionHandler(MenuNotFoundException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + ErrorResponse menuNotFoundExceptionHandler(MenuNotFoundException ex) { + return new ErrorResponse(ex.getExceptionCode(), 0, "", ex.getMessage()); + } + + @ResponseBody + @ExceptionHandler(CategoryAlreadyExistsException.class) + @ResponseStatus(HttpStatus.CONFLICT) + ErrorResponse categoryAlreadyExistsExceptionHandler(CategoryAlreadyExistsException ex) { + return new ErrorResponse(ex.getExceptionCode(), 0, "", ex.getMessage()); + } + + @ResponseBody + @ExceptionHandler(CategoryDoesNotExistException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + ErrorResponse categoryNotFoundExceptionHandler(CategoryDoesNotExistException ex) { + return new ErrorResponse(ex.getExceptionCode(), 0, "", ex.getMessage()); + } + + @ResponseBody + @ExceptionHandler(ItemAlreadyExistsException.class) + @ResponseStatus(HttpStatus.CONFLICT) + ErrorResponse itemAlreadyExistsExceptionHandler(ItemAlreadyExistsException ex) { + return new ErrorResponse(ex.getExceptionCode(), 0, "", ex.getMessage()); + } + + @ResponseBody + @ExceptionHandler(ItemDoesNotExistsException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + ErrorResponse itemDontNotExistsExceptionHandler(ItemDoesNotExistsException ex) { + return new ErrorResponse(ex.getExceptionCode(), 0, "", ex.getMessage()); + } +} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/CreateCategoryMapper.java b/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/CreateCategoryMapper.java new file mode 100644 index 00000000..7c6db8e9 --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/CreateCategoryMapper.java @@ -0,0 +1,13 @@ +package com.amido.stacks.workloads.menu.mappers.wrappers; + +import com.amido.stacks.core.mapping.BaseMapper; +import com.amido.stacks.workloads.menu.api.v1.dto.request.CreateCategoryRequest; +import com.amido.stacks.workloads.menu.api.v1.dto.response.CategoryDTO; +import org.mapstruct.Mapper; +import org.mapstruct.NullValueCheckStrategy; + +@Mapper( + componentModel = "spring", + uses = {}, + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS) +public interface CreateCategoryMapper extends BaseMapper {} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/CreateItemMapper.java b/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/CreateItemMapper.java new file mode 100644 index 00000000..26c5706c --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/CreateItemMapper.java @@ -0,0 +1,13 @@ +package com.amido.stacks.workloads.menu.mappers.wrappers; + +import com.amido.stacks.core.mapping.BaseMapper; +import com.amido.stacks.workloads.menu.api.v1.dto.request.CreateItemRequest; +import com.amido.stacks.workloads.menu.api.v1.dto.response.ItemDTO; +import org.mapstruct.Mapper; +import org.mapstruct.NullValueCheckStrategy; + +@Mapper( + componentModel = "spring", + uses = {}, + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS) +public interface CreateItemMapper extends BaseMapper {} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/CreateMenuMapper.java b/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/CreateMenuMapper.java new file mode 100644 index 00000000..b22cf321 --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/CreateMenuMapper.java @@ -0,0 +1,23 @@ +package com.amido.stacks.workloads.menu.mappers.wrappers; + +import com.amido.stacks.core.mapping.BaseMapper; +import com.amido.stacks.workloads.menu.api.v1.dto.request.CreateMenuRequest; +import com.amido.stacks.workloads.menu.api.v1.dto.response.MenuDTO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.NullValueCheckStrategy; + +@Mapper( + componentModel = "spring", + uses = {}, + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS) +public interface CreateMenuMapper extends BaseMapper { + + @Override + @Mapping(source = "restaurantId", target = "tenantId") + CreateMenuRequest toDto(MenuDTO command); + + @Override + @Mapping(source = "tenantId", target = "restaurantId") + MenuDTO fromDto(CreateMenuRequest request); +} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/UpdateCategoryMapper.java b/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/UpdateCategoryMapper.java new file mode 100644 index 00000000..33f0c251 --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/UpdateCategoryMapper.java @@ -0,0 +1,13 @@ +package com.amido.stacks.workloads.menu.mappers.wrappers; + +import com.amido.stacks.core.mapping.BaseMapper; +import com.amido.stacks.workloads.menu.api.v1.dto.request.UpdateCategoryRequest; +import com.amido.stacks.workloads.menu.api.v1.dto.response.CategoryDTO; +import org.mapstruct.Mapper; +import org.mapstruct.NullValueCheckStrategy; + +@Mapper( + componentModel = "spring", + uses = {}, + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS) +public interface UpdateCategoryMapper extends BaseMapper {} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/UpdateItemMapper.java b/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/UpdateItemMapper.java new file mode 100644 index 00000000..0b7b1c97 --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/UpdateItemMapper.java @@ -0,0 +1,13 @@ +package com.amido.stacks.workloads.menu.mappers.wrappers; + +import com.amido.stacks.core.mapping.BaseMapper; +import com.amido.stacks.workloads.menu.api.v1.dto.request.UpdateItemRequest; +import com.amido.stacks.workloads.menu.api.v1.dto.response.ItemDTO; +import org.mapstruct.Mapper; +import org.mapstruct.NullValueCheckStrategy; + +@Mapper( + componentModel = "spring", + uses = {}, + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS) +public interface UpdateItemMapper extends BaseMapper {} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/UpdateMenuMapper.java b/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/UpdateMenuMapper.java new file mode 100644 index 00000000..6034db81 --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/mappers/wrappers/UpdateMenuMapper.java @@ -0,0 +1,13 @@ +package com.amido.stacks.workloads.menu.mappers.wrappers; + +import com.amido.stacks.core.mapping.BaseMapper; +import com.amido.stacks.workloads.menu.api.v1.dto.request.UpdateMenuRequest; +import com.amido.stacks.workloads.menu.api.v1.dto.response.MenuDTO; +import org.mapstruct.Mapper; +import org.mapstruct.NullValueCheckStrategy; + +@Mapper( + componentModel = "spring", + uses = {}, + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS) +public interface UpdateMenuMapper extends BaseMapper {} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/repository/MenuRepository.java b/java/src/main/java/com/amido/stacks/workloads/menu/repository/MenuRepository.java new file mode 100644 index 00000000..aabd549d --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/repository/MenuRepository.java @@ -0,0 +1,56 @@ +package com.amido.stacks.workloads.menu.repository; + +import com.amido.stacks.workloads.menu.domain.Menu; +import com.azure.spring.data.cosmos.repository.CosmosRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Repository; + +@Repository +public interface MenuRepository extends CosmosRepository { + + @Override + Menu save(Menu menu); + + /** + * Query is constructed OOTB- out of the box, executed and results are fetched based param + * restaurantId. Pagination and sorting is done by spring data JPA. + * + * @param restaurantId tenantID/RestaurantId + * @return page of menu + */ + Page findAllByRestaurantId(String restaurantId, Pageable pageable); + + /** + * Query is constructed OOTB - out of the box, executed and results are fetched based param + * searchTerm. Pagination and sorting is done by spring data JPA. + * + * @param searchTerm Menu name + * @param pageable pagination + * @return page of menu + */ + Page findAllByNameContaining(String searchTerm, Pageable pageable); + + /** + * Query is constructed OOTB - out of the box, executed and results are fetched based param + * restaurantId and searchTerm. Pagination and sorting is done by spring data JPA. + * + * @param restaurantId tenantID/RestaurantId + * @param searchTerm Menu name + * @param pageable pagination + * @return page of menu + */ + Page findAllByRestaurantIdAndNameContaining( + String restaurantId, String searchTerm, Pageable pageable); + + /** + * Query is constructed OOTB - out of the box, executed and results are fetched based param + * restaurantId and searchTerm. Pagination and sorting is done by spring data JPA. + * + * @param restaurantId + * @param name + * @param pageable + * @return + */ + Page findAllByRestaurantIdAndName(String restaurantId, String name, Pageable pageable); +} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/service/data/CosmosMenuQueryService.java b/java/src/main/java/com/amido/stacks/workloads/menu/service/data/CosmosMenuQueryService.java new file mode 100644 index 00000000..faa2e74f --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/service/data/CosmosMenuQueryService.java @@ -0,0 +1,76 @@ +package com.amido.stacks.workloads.menu.service.data; + +import com.amido.stacks.workloads.menu.domain.Menu; +import com.amido.stacks.workloads.menu.repository.MenuRepository; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class CosmosMenuQueryService implements MenuQueryService { + + private static final String NAME = "name"; + + private final MenuRepository menuRepository; + + @Override + public Optional findById(UUID id) { + return menuRepository.findById(id.toString()); + } + + @Override + public List findAll(int pageNumber, int pageSize) { + + Page page = + menuRepository.findAll(PageRequest.of(0, pageSize, Sort.by(Sort.Direction.ASC, NAME))); + + // This is specific and needed due to the way in which CosmosDB handles pagination + // using a continuationToken and a limitation in the Swagger Specification. + // See https://github.com/Azure/azure-sdk-for-java/issues/12726 + int currentPage = 0; + while (currentPage < pageNumber && page.hasNext()) { + currentPage++; + Pageable nextPageable = page.nextPageable(); + page = menuRepository.findAll(nextPageable); + } + return page.getContent(); + } + + @Override + public List findAllByRestaurantId(UUID restaurantId, Integer pageSize, Integer pageNumber) { + + return menuRepository + .findAllByRestaurantId( + restaurantId.toString(), PageRequest.of(0, pageSize, Sort.by(Sort.Direction.ASC, NAME))) + .getContent(); + } + + @Override + public List findAllByNameContaining( + String searchTerm, Integer pageSize, Integer pageNumber) { + + return menuRepository + .findAllByNameContaining( + searchTerm, PageRequest.of(0, pageSize, Sort.by(Sort.Direction.ASC, NAME))) + .getContent(); + } + + @Override + public List findAllByRestaurantIdAndNameContaining( + UUID restaurantId, String searchTerm, Integer pageSize, Integer pageNumber) { + + return menuRepository + .findAllByRestaurantIdAndNameContaining( + restaurantId.toString(), + searchTerm, + PageRequest.of(0, pageSize, Sort.by(Sort.Direction.ASC, NAME))) + .getContent(); + } +} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/service/data/MenuQueryService.java b/java/src/main/java/com/amido/stacks/workloads/menu/service/data/MenuQueryService.java new file mode 100644 index 00000000..7ab88441 --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/service/data/MenuQueryService.java @@ -0,0 +1,61 @@ +package com.amido.stacks.workloads.menu.service.data; + +import com.amido.stacks.workloads.menu.domain.Menu; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public interface MenuQueryService { + + /** + * Retrieve list of all available Menus + * + * @param pageNumber pageNo + * @param pageSize page Size + * @return List of MenuDTO + */ + List findAll(int pageNumber, int pageSize); + + /** + * Retrieve MenuDTO by MenuDTO Id + * + * @param id menu Id + * @return Optional MenuDTO + */ + Optional findById(UUID id); + + /** + * Retrieve MenuDTO by Restaurant Id Pagination and sorting is done by spring data JPA. + * + * @param restaurantId restaurant id + * @param pageSize page size + * @param pageNumber page no + * @return List of MenuDTO + */ + public List findAllByRestaurantId(UUID restaurantId, Integer pageSize, Integer pageNumber); + + /** + * Retrieve MenuDTO's by matching the name (Contains operation) Pagination and sorting is done by + * spring data JPA. + * + * @param searchTerm menu name + * @param pageSize page size + * @param pageNumber page no + * @return List of MenuDTO + */ + public List findAllByNameContaining( + String searchTerm, Integer pageSize, Integer pageNumber); + + /** + * Retrieve MenuDTO's by matching the name and the restaurantId (Contains operation) Pagination + * and sorting is done by spring data JPA. + * + * @param restaurantId restaurant id + * @param searchTerm menu name + * @param pageSize page size + * @param pageNumber page no + * @return List of MenuDTO + */ + List findAllByRestaurantIdAndNameContaining( + UUID restaurantId, String searchTerm, Integer pageSize, Integer pageNumber); +} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/service/v1/CategoryService.java b/java/src/main/java/com/amido/stacks/workloads/menu/service/v1/CategoryService.java index 772ded52..5cf8c064 100644 --- a/java/src/main/java/com/amido/stacks/workloads/menu/service/v1/CategoryService.java +++ b/java/src/main/java/com/amido/stacks/workloads/menu/service/v1/CategoryService.java @@ -1,9 +1,22 @@ package com.amido.stacks.workloads.menu.service.v1; +import static java.util.UUID.randomUUID; + import com.amido.stacks.core.api.dto.response.ResourceCreatedResponse; import com.amido.stacks.core.api.dto.response.ResourceUpdatedResponse; import com.amido.stacks.workloads.menu.api.v1.dto.request.CreateCategoryRequest; import com.amido.stacks.workloads.menu.api.v1.dto.request.UpdateCategoryRequest; +import com.amido.stacks.workloads.menu.api.v1.dto.response.CategoryDTO; +import com.amido.stacks.workloads.menu.domain.Category; +import com.amido.stacks.workloads.menu.domain.Menu; +import com.amido.stacks.workloads.menu.exception.MenuNotFoundException; +import com.amido.stacks.workloads.menu.mappers.CategoryMapper; +import com.amido.stacks.workloads.menu.mappers.wrappers.CreateCategoryMapper; +import com.amido.stacks.workloads.menu.mappers.wrappers.UpdateCategoryMapper; +import com.amido.stacks.workloads.menu.repository.MenuRepository; +import com.amido.stacks.workloads.menu.service.data.MenuQueryService; +import com.amido.stacks.workloads.menu.service.v1.utility.MenuHelperService; +import java.util.Optional; import java.util.UUID; import javax.validation.Valid; import lombok.RequiredArgsConstructor; @@ -13,12 +26,74 @@ @RequiredArgsConstructor public class CategoryService { - public ResourceCreatedResponse create(@Valid CreateCategoryRequest body, String correlationId) { - return new ResourceCreatedResponse(UUID.randomUUID()); + private final MenuRepository menuRepository; + + private final MenuQueryService menuQueryService; + + private final MenuHelperService menuHelperService; + + private final CategoryMapper categoryMapper; + + private final CreateCategoryMapper createCategoryMapper; + + private final UpdateCategoryMapper updateCategoryMapper; + + public ResourceCreatedResponse create( + UUID menuId, @Valid CreateCategoryRequest body, String correlationId) { + + Menu menu = getMenu(menuId); + + UUID categoryId = randomUUID(); + + menuHelperService.verifyCategoryNameNotAlreadyExisting(menu, categoryId, body.getName()); + + CategoryDTO categoryDTO = createCategoryMapper.fromDto(body); + categoryDTO.setId(categoryId.toString()); + + menuHelperService.addOrUpdateCategory(menu, categoryMapper.fromDto(categoryDTO)); + menuRepository.save(menu); + + return new ResourceCreatedResponse(categoryId); } public ResourceUpdatedResponse update( UUID menuId, UUID categoryId, @Valid UpdateCategoryRequest body, String correlationId) { - return new ResourceUpdatedResponse(UUID.randomUUID()); + + Menu menu = getMenu(menuId); + + Category category = menuHelperService.checkCategoryExistsById(menu, categoryId); + + menuHelperService.verifyCategoryNameNotAlreadyExisting(menu, categoryId, body.getName()); + + CategoryDTO updatedDTO = updateCategoryMapper.fromDto(body); + updatedDTO.setId(categoryId.toString()); + + Category updatedCategory = categoryMapper.fromDto(updatedDTO); + updatedCategory.setItems(category.getItems()); + + menuHelperService.addOrUpdateCategory(menu, updatedCategory); + menuRepository.save(menu); + + return new ResourceUpdatedResponse(categoryId); + } + + public void delete(UUID menuId, UUID categoryId, String correlationId) { + + Menu menu = getMenu(menuId); + + menuHelperService.checkCategoryExistsById(menu, categoryId); + + menuHelperService.removeCategory(menu, categoryId); + menuRepository.save(menu); + } + + private Menu getMenu(UUID menuId) { + Optional optMenu = menuQueryService.findById(menuId); + + if (optMenu.isEmpty()) { + throw new MenuNotFoundException(menuId); + } + + return optMenu.get(); } } diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/service/v1/ItemService.java b/java/src/main/java/com/amido/stacks/workloads/menu/service/v1/ItemService.java index 46d25bce..a11eee85 100644 --- a/java/src/main/java/com/amido/stacks/workloads/menu/service/v1/ItemService.java +++ b/java/src/main/java/com/amido/stacks/workloads/menu/service/v1/ItemService.java @@ -4,20 +4,102 @@ import com.amido.stacks.core.api.dto.response.ResourceUpdatedResponse; import com.amido.stacks.workloads.menu.api.v1.dto.request.CreateItemRequest; import com.amido.stacks.workloads.menu.api.v1.dto.request.UpdateItemRequest; +import com.amido.stacks.workloads.menu.api.v1.dto.response.ItemDTO; +import com.amido.stacks.workloads.menu.domain.Category; +import com.amido.stacks.workloads.menu.domain.Item; +import com.amido.stacks.workloads.menu.domain.Menu; +import com.amido.stacks.workloads.menu.exception.MenuNotFoundException; +import com.amido.stacks.workloads.menu.mappers.ItemMapper; +import com.amido.stacks.workloads.menu.mappers.wrappers.CreateItemMapper; +import com.amido.stacks.workloads.menu.mappers.wrappers.UpdateItemMapper; +import com.amido.stacks.workloads.menu.repository.MenuRepository; +import com.amido.stacks.workloads.menu.service.data.MenuQueryService; +import com.amido.stacks.workloads.menu.service.v1.utility.MenuHelperService; +import java.util.Optional; import java.util.UUID; import javax.validation.Valid; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @Service +@RequiredArgsConstructor public class ItemService { + private final MenuRepository menuRepository; + + private final MenuQueryService menuQueryService; + + private final MenuHelperService menuHelperService; + + private final CreateItemMapper createItemMapper; + + private final UpdateItemMapper updateItemMapper; + + private final ItemMapper itemMapper; + public ResourceCreatedResponse create( UUID menuId, UUID categoryId, @Valid CreateItemRequest body, String correlationId) { - return new ResourceCreatedResponse(UUID.randomUUID()); + + Menu menu = getMenu(menuId); + + Category category = menuHelperService.checkCategoryExistsById(menu, categoryId); + + UUID itemId = UUID.randomUUID(); + + menuHelperService.verifyItemNameNotAlreadyExisting(menuId, category, itemId, body.getName()); + + ItemDTO itemDTO = createItemMapper.fromDto(body); + itemDTO.setId(itemId.toString()); + + menuHelperService.addOrUpdateItem(category, itemMapper.fromDto(itemDTO)); + menuRepository.save(menu); + + return new ResourceCreatedResponse(itemId); } public ResourceUpdatedResponse update( - UUID menuId, UUID categoryId, @Valid UpdateItemRequest body, String correlationId) { - return new ResourceUpdatedResponse(UUID.randomUUID()); + UUID menuId, + UUID categoryId, + UUID itemId, + @Valid UpdateItemRequest body, + String correlationId) { + + Menu menu = getMenu(menuId); + + Category category = menuHelperService.checkCategoryExistsById(menu, categoryId); + + Item item = menuHelperService.checkItemExistsById(menuId, category, itemId); + + menuHelperService.verifyItemNameNotAlreadyExisting(menuId, category, itemId, body.getName()); + + ItemDTO itemDTO = updateItemMapper.fromDto(body); + itemDTO.setId(item.getId()); + + menuHelperService.addOrUpdateItem(category, itemMapper.fromDto(itemDTO)); + menuRepository.save(menu); + + return new ResourceUpdatedResponse(itemId); + } + + public void delete(UUID menuId, UUID categoryId, UUID itemId, String correlationId) { + + Menu menu = getMenu(menuId); + + Category category = menuHelperService.checkCategoryExistsById(menu, categoryId); + + menuHelperService.checkItemExistsById(menuId, category, itemId); + + menuHelperService.removeItem(category, itemId); + menuRepository.save(menu); + } + + private Menu getMenu(UUID menuId) { + Optional optMenu = menuQueryService.findById(menuId); + + if (optMenu.isEmpty()) { + throw new MenuNotFoundException(menuId); + } + + return optMenu.get(); } } diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/service/v1/MenuService.java b/java/src/main/java/com/amido/stacks/workloads/menu/service/v1/MenuService.java index 36f57b67..115250a5 100644 --- a/java/src/main/java/com/amido/stacks/workloads/menu/service/v1/MenuService.java +++ b/java/src/main/java/com/amido/stacks/workloads/menu/service/v1/MenuService.java @@ -1,84 +1,134 @@ package com.amido.stacks.workloads.menu.service.v1; -import com.amido.stacks.core.api.dto.response.ResourceCreatedResponse; -import com.amido.stacks.core.api.dto.response.ResourceUpdatedResponse; +import static java.util.Objects.nonNull; +import static java.util.UUID.fromString; +import static java.util.UUID.randomUUID; +import static org.apache.commons.lang3.StringUtils.isNotEmpty; + import com.amido.stacks.workloads.menu.api.v1.dto.request.CreateMenuRequest; import com.amido.stacks.workloads.menu.api.v1.dto.request.UpdateMenuRequest; import com.amido.stacks.workloads.menu.api.v1.dto.response.MenuDTO; import com.amido.stacks.workloads.menu.api.v1.dto.response.SearchMenuResult; -import com.amido.stacks.workloads.menu.domain.Category; -import com.amido.stacks.workloads.menu.domain.Item; import com.amido.stacks.workloads.menu.domain.Menu; +import com.amido.stacks.workloads.menu.exception.MenuAlreadyExistsException; +import com.amido.stacks.workloads.menu.exception.MenuNotFoundException; import com.amido.stacks.workloads.menu.mappers.MenuMapper; import com.amido.stacks.workloads.menu.mappers.SearchMenuResultItemMapper; -import java.util.ArrayList; +import com.amido.stacks.workloads.menu.mappers.wrappers.CreateMenuMapper; +import com.amido.stacks.workloads.menu.mappers.wrappers.UpdateMenuMapper; +import com.amido.stacks.workloads.menu.repository.MenuRepository; +import com.amido.stacks.workloads.menu.service.data.MenuQueryService; import java.util.List; +import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; import javax.validation.Valid; -import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class MenuService { - @Getter private final MenuMapper menuMapper; + private final MenuRepository menuRepository; + + private final MenuQueryService menuQueryService; + + private final MenuMapper menuMapper; + + private final CreateMenuMapper createMenuMapper; + + private final UpdateMenuMapper updateMenuMapper; private final SearchMenuResultItemMapper searchMenuResultItemMapper; - public ResourceCreatedResponse create(@Valid CreateMenuRequest body, String correlationId) { + public MenuDTO create(@Valid CreateMenuRequest dto, String correlationId) { + + verifyMenuNotAlreadyExisting(dto.getTenantId(), dto.getName()); + + MenuDTO menuDTO = createMenuMapper.fromDto(dto); + menuDTO.setId(randomUUID()); - return new ResourceCreatedResponse(UUID.randomUUID()); + return (menuMapper.toDto(menuRepository.save(menuMapper.fromDto(menuDTO)))); } public SearchMenuResult search( - String searchTerm, UUID restaurantId, Integer pageSize, Integer pageNumber) { - - List menuList = new ArrayList<>(); - - final String menuId = "d5785e28-306b-4e23-add0-3f9092d395f8"; - - Menu mockMenu; - if (restaurantId == null) { - mockMenu = - new Menu( - menuId, - "58a1df85-6bdc-412a-a118-0f0e394c1342", - "name", - "description", - new ArrayList<>(), - true); + String searchTerm, + UUID restaurantId, + Integer pageSize, + Integer pageNumber, + String correlationId) { + + List menuList; + + if (isNotEmpty(searchTerm) && nonNull(restaurantId)) { + menuList = + this.menuQueryService.findAllByRestaurantIdAndNameContaining( + restaurantId, searchTerm, pageSize, pageNumber); + } else if (isNotEmpty(searchTerm)) { + menuList = this.menuQueryService.findAllByNameContaining(searchTerm, pageSize, pageNumber); + } else if (nonNull(restaurantId)) { + menuList = this.menuQueryService.findAllByRestaurantId(restaurantId, pageSize, pageNumber); } else { - mockMenu = - new Menu(menuId, restaurantId.toString(), "name", "description", new ArrayList<>(), true); + menuList = this.menuQueryService.findAll(pageNumber, pageSize); } - menuList.add(mockMenu); - return new SearchMenuResult( pageSize, pageNumber, menuList.stream().map(searchMenuResultItemMapper::toDto).collect(Collectors.toList())); } - public MenuDTO get(UUID id, String correlationId) { + public MenuDTO get(UUID menuId, String correlationId) { + + Optional optMenu = menuQueryService.findById(menuId); + + if (optMenu.isPresent()) { + return menuMapper.toDto(optMenu.get()); + } + + throw new MenuNotFoundException(menuId); + } + + public MenuDTO update(UUID menuId, @Valid UpdateMenuRequest dto, String correlationId) { + + Optional optMenu = menuQueryService.findById(menuId); - final String restaurantId = "58a1df85-6bdc-412a-a118-0f0e394c1342"; - final String categoryId = "2c43dbda-7d4d-46fb-b246-bec2bd348ca1"; - final String itemId = "7e46a698-080b-45e6-a529-2c196d00791c"; + if (optMenu.isPresent()) { - Menu menu = - new Menu(id.toString(), restaurantId, "name", "description", new ArrayList<>(), true); - Item item = new Item(itemId, "item name", "item description", 5.99d, true); - Category category = new Category(categoryId, "cat name", "cat description", List.of(item)); - menu.addOrUpdateCategory(category); + MenuDTO menuDTO = updateMenuMapper.fromDto(dto); + menuDTO.setId(menuId); + menuDTO.setRestaurantId(fromString(optMenu.get().getRestaurantId())); - return menuMapper.toDto(menu); + menuRepository.save(menuMapper.fromDto(menuDTO)); + + return get(menuId, correlationId); + } + + throw new MenuNotFoundException(menuId); } - public ResourceUpdatedResponse update(@Valid UpdateMenuRequest body, String correlationId) { - return new ResourceUpdatedResponse(UUID.randomUUID()); + public void verifyMenuNotAlreadyExisting(UUID restaurantId, String name) { + Page existing = + menuRepository.findAllByRestaurantIdAndName( + restaurantId.toString(), name, PageRequest.of(0, 1)); + + if (!existing.getContent().isEmpty() + && existing.get().anyMatch(m -> m.getName().equals(name))) { + throw new MenuAlreadyExistsException(restaurantId, name); + } + } + + public void delete(UUID menuId, String correlationId) { + + Optional optMenu = menuQueryService.findById(menuId); + + if (optMenu.isEmpty()) { + throw new MenuNotFoundException(menuId); + } + + menuRepository.delete(optMenu.get()); } } diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/service/v1/utility/MenuHelperService.java b/java/src/main/java/com/amido/stacks/workloads/menu/service/v1/utility/MenuHelperService.java new file mode 100644 index 00000000..e3239877 --- /dev/null +++ b/java/src/main/java/com/amido/stacks/workloads/menu/service/v1/utility/MenuHelperService.java @@ -0,0 +1,106 @@ +package com.amido.stacks.workloads.menu.service.v1.utility; + +import com.amido.stacks.workloads.menu.domain.Category; +import com.amido.stacks.workloads.menu.domain.Item; +import com.amido.stacks.workloads.menu.domain.Menu; +import com.amido.stacks.workloads.menu.exception.CategoryAlreadyExistsException; +import com.amido.stacks.workloads.menu.exception.CategoryDoesNotExistException; +import com.amido.stacks.workloads.menu.exception.ItemAlreadyExistsException; +import com.amido.stacks.workloads.menu.exception.ItemDoesNotExistsException; +import com.amido.stacks.workloads.menu.repository.MenuRepository; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class MenuHelperService { + + private final MenuRepository menuRepository; + + public void addOrUpdateCategory(Menu menu, Category category) { + if (menu.getCategories() == null) { + menu.setCategories(new ArrayList<>()); + } + + List newCategories = + menu.getCategories().stream() + .filter(c -> !c.getId().equals(category.getId())) + .collect(Collectors.toList()); + + newCategories.add(category); + + menu.setCategories(newCategories); + } + + public void addOrUpdateItem(Category category, Item item) { + + if (category.getItems() == null) { + category.setItems(new ArrayList<>()); + } + + List newItems = + category.getItems().stream() + .filter(c -> !c.getId().equals(item.getId())) + .collect(Collectors.toList()); + + newItems.add(item); + + category.setItems(newItems); + } + + public Category checkCategoryExistsById(Menu menu, UUID categoryId) { + Optional optCategory = + menu.getCategories().stream() + .filter(c -> categoryId.toString().equals(c.getId())) + .findFirst(); + + if (optCategory.isEmpty()) { + throw new CategoryDoesNotExistException(UUID.fromString(menu.getId()), categoryId); + } + + return optCategory.get(); + } + + public Item checkItemExistsById(UUID menuId, Category category, UUID itemId) { + Optional optItem = + category.getItems().stream().filter(i -> itemId.toString().equals(i.getId())).findFirst(); + + if (optItem.isEmpty()) { + throw new ItemDoesNotExistsException(menuId, UUID.fromString(category.getId()), itemId); + } + + return optItem.get(); + } + + public void verifyCategoryNameNotAlreadyExisting(Menu menu, UUID categoryId, String name) { + Optional optCategory = + menu.getCategories().stream().filter(c -> c.getName().equals(name)).findFirst(); + + if (optCategory.isPresent() && !optCategory.get().getId().equals(categoryId.toString())) { + throw new CategoryAlreadyExistsException(UUID.fromString(menu.getId()), name); + } + } + + public void verifyItemNameNotAlreadyExisting( + UUID menuId, Category category, UUID itemId, String name) { + Optional optItem = + category.getItems().stream().filter(c -> c.getName().equals(name)).findFirst(); + + if (optItem.isPresent() && !optItem.get().getId().equals(itemId.toString())) { + throw new ItemAlreadyExistsException(menuId, UUID.fromString(category.getId()), name); + } + } + + public void removeItem(Category category, UUID itemId) { + category.getItems().removeIf(i -> i.getId().equals(itemId.toString())); + } + + public void removeCategory(Menu menu, UUID categoryId) { + menu.getCategories().removeIf(c -> c.getId().equals(categoryId.toString())); + } +} diff --git a/java/src/main/java/com/amido/stacks/workloads/menu/service/v2/MenuServiceV2.java b/java/src/main/java/com/amido/stacks/workloads/menu/service/v2/MenuServiceV2.java index a1614641..3e472f47 100644 --- a/java/src/main/java/com/amido/stacks/workloads/menu/service/v2/MenuServiceV2.java +++ b/java/src/main/java/com/amido/stacks/workloads/menu/service/v2/MenuServiceV2.java @@ -1,11 +1,13 @@ package com.amido.stacks.workloads.menu.service.v2; import com.amido.stacks.workloads.menu.api.v1.dto.response.MenuDTO; -import com.amido.stacks.workloads.menu.domain.Menu; import com.amido.stacks.workloads.menu.mappers.MenuMapper; import com.amido.stacks.workloads.menu.mappers.SearchMenuResultItemMapper; +import com.amido.stacks.workloads.menu.mappers.wrappers.CreateMenuMapper; +import com.amido.stacks.workloads.menu.mappers.wrappers.UpdateMenuMapper; +import com.amido.stacks.workloads.menu.repository.MenuRepository; +import com.amido.stacks.workloads.menu.service.data.MenuQueryService; import com.amido.stacks.workloads.menu.service.v1.MenuService; -import java.util.ArrayList; import java.util.UUID; import org.springframework.stereotype.Service; @@ -13,19 +15,24 @@ public class MenuServiceV2 extends MenuService { public MenuServiceV2( - MenuMapper menuMapper, SearchMenuResultItemMapper searchMenuResultItemMapper) { - super(menuMapper, searchMenuResultItemMapper); + MenuRepository menuRepository, + MenuQueryService menuQueryService, + MenuMapper menuMapper, + CreateMenuMapper createMenuMapper, + UpdateMenuMapper updateMenuMapper, + SearchMenuResultItemMapper searchMenuResultItemMapper) { + super( + menuRepository, + menuQueryService, + menuMapper, + createMenuMapper, + updateMenuMapper, + searchMenuResultItemMapper); } @Override public MenuDTO get(UUID id, String correlationId) { - String restaurantId = "3930ddff-82ce-4f7e-b910-b0709b276cf0"; - - Menu menu = - new Menu( - id.toString(), restaurantId, "0 Menu", "0 Menu Description", new ArrayList<>(), true); - - return getMenuMapper().toDto(menu); + return super.get(id, correlationId); } } diff --git a/java/src/main/resources/application.yml b/java/src/main/resources/application.yml index 524dda51..60f769a9 100644 --- a/java/src/main/resources/application.yml +++ b/java/src/main/resources/application.yml @@ -33,6 +33,10 @@ springdoc: path: /swagger/oas-json azure: + cosmos: + uri: https://localhost:8081 + database: Stacks + key: ${COSMOSDB_KEY} application-insights: instrumentation-key: xxxxxx enabled: true diff --git a/java/src/main/resources/lombok.config b/java/src/main/resources/lombok.config new file mode 100644 index 00000000..7a21e880 --- /dev/null +++ b/java/src/main/resources/lombok.config @@ -0,0 +1 @@ +lombok.addLombokGeneratedAnnotation = true diff --git a/java/src/test/java/com/amido/stacks/workloads/actuator/ActuatorTest.java b/java/src/test/java/com/amido/stacks/workloads/actuator/ActuatorTest.java index db8f210d..6e43486e 100644 --- a/java/src/test/java/com/amido/stacks/workloads/actuator/ActuatorTest.java +++ b/java/src/test/java/com/amido/stacks/workloads/actuator/ActuatorTest.java @@ -3,6 +3,11 @@ import static com.amido.stacks.workloads.util.TestHelper.getBaseURL; import static org.assertj.core.api.BDDAssertions.then; +import com.amido.stacks.workloads.Application; +import com.amido.stacks.workloads.menu.repository.MenuRepository; +import com.azure.spring.autoconfigure.cosmos.CosmosAutoConfiguration; +import com.azure.spring.autoconfigure.cosmos.CosmosHealthConfiguration; +import com.azure.spring.autoconfigure.cosmos.CosmosRepositoriesAutoConfiguration; import java.util.Map; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -10,13 +15,21 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.HttpStatus; import org.springframework.test.context.TestPropertySource; -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + classes = Application.class) @TestPropertySource(properties = {"management.port=0"}) -@EnableAutoConfiguration +@EnableAutoConfiguration( + exclude = { + CosmosRepositoriesAutoConfiguration.class, + CosmosAutoConfiguration.class, + CosmosHealthConfiguration.class + }) @Tag("Component") class ActuatorTest { @@ -25,6 +38,8 @@ class ActuatorTest { @Autowired private TestRestTemplate testRestTemplate; + @MockBean private MenuRepository menuRepository; + @Test void shouldReturn200WhenSendingRequestToHealthEndpoint() { diff --git a/java/src/test/java/com/amido/stacks/workloads/menu/api/v1/CategoryControllerTest.java b/java/src/test/java/com/amido/stacks/workloads/menu/api/v1/CategoryControllerTest.java index 687b048d..8c329e74 100644 --- a/java/src/test/java/com/amido/stacks/workloads/menu/api/v1/CategoryControllerTest.java +++ b/java/src/test/java/com/amido/stacks/workloads/menu/api/v1/CategoryControllerTest.java @@ -8,22 +8,38 @@ import static com.amido.stacks.workloads.util.TestHelper.getRequestHttpEntity; import static java.util.UUID.fromString; import static org.assertj.core.api.BDDAssertions.then; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.NOT_FOUND; import static org.springframework.http.HttpStatus.OK; import com.amido.stacks.core.api.dto.ErrorResponse; import com.amido.stacks.core.api.dto.response.ResourceCreatedResponse; import com.amido.stacks.core.api.dto.response.ResourceUpdatedResponse; +import com.amido.stacks.workloads.Application; import com.amido.stacks.workloads.menu.api.v1.dto.request.CreateCategoryRequest; import com.amido.stacks.workloads.menu.api.v1.dto.request.UpdateCategoryRequest; import com.amido.stacks.workloads.menu.domain.Category; import com.amido.stacks.workloads.menu.domain.Menu; +import com.amido.stacks.workloads.menu.repository.MenuRepository; +import com.amido.stacks.workloads.menu.service.data.MenuQueryService; +import com.amido.stacks.workloads.menu.service.v1.CategoryService; +import com.amido.stacks.workloads.menu.service.v1.utility.MenuHelperService; +import com.azure.spring.autoconfigure.cosmos.CosmosAutoConfiguration; +import com.azure.spring.autoconfigure.cosmos.CosmosHealthConfiguration; +import com.azure.spring.autoconfigure.cosmos.CosmosRepositoriesAutoConfiguration; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.UUID; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.http.HttpEntity; @@ -31,8 +47,15 @@ import org.springframework.http.HttpStatus; import org.springframework.test.context.ActiveProfiles; -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@EnableAutoConfiguration +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + classes = {Application.class}) +@EnableAutoConfiguration( + exclude = { + CosmosRepositoriesAutoConfiguration.class, + CosmosAutoConfiguration.class, + CosmosHealthConfiguration.class + }) @Tag("Integration") @ActiveProfiles("test") public class CategoryControllerTest { @@ -47,8 +70,16 @@ public class CategoryControllerTest { @Autowired private TestRestTemplate testRestTemplate; + @Autowired private CategoryService categoryService; + + @Autowired private MenuHelperService menuHelperService; + + @MockBean private MenuRepository menuRepository; + + @MockBean private MenuQueryService menuQueryService; + @Test - void testInvalidMenuIdWilThrowBadRequest() { + void testInvalidMenuIdWillThrowBadRequest() { // Given CreateCategoryRequest request = new CreateCategoryRequest("test Category Name", ""); @@ -72,6 +103,9 @@ void testAddCategory() { CreateCategoryRequest request = new CreateCategoryRequest("test Category Name", "test Category Description"); + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.of(menu)); + when(menuRepository.save(any(Menu.class))).thenReturn(menu); + // When var response = this.testRestTemplate.postForEntity( @@ -90,10 +124,13 @@ void testUpdateCategorySuccess() { // Given Menu menu = createMenu(0); Category category = createCategory(0); - menu.addOrUpdateCategory(category); + menuHelperService.addOrUpdateCategory(menu, category); UpdateCategoryRequest request = new UpdateCategoryRequest("new Category", "new Description"); + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.of(menu)); + when(menuRepository.save(any(Menu.class))).thenReturn(menu); + // When String requestUrl = String.format( @@ -114,6 +151,38 @@ void testUpdateCategorySuccess() { then(response.getStatusCode()).isEqualTo(OK); } + @Test + void testUpdateCategoryFailure() { + // Given + Menu menu = createMenu(0); + Category category = createCategory(0); + menuHelperService.addOrUpdateCategory(menu, category); + + UpdateCategoryRequest request = new UpdateCategoryRequest("new Category", "new Description"); + + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.empty()); + when(menuRepository.save(any(Menu.class))).thenReturn(menu); + + // When + String requestUrl = + String.format( + UPDATE_CATEGORY, + getBaseURL(port), + fromString(menu.getId()), + fromString(category.getId())); + + var response = + this.testRestTemplate.exchange( + requestUrl, + HttpMethod.PUT, + new HttpEntity<>(request, getRequestHttpEntity()), + ResourceUpdatedResponse.class); + + // Then + then(response).isNotNull(); + then(response.getStatusCode()).isEqualTo(NOT_FOUND); + } + @Test void testUpdateCategoryWhenMultipleCategoriesExists() { // Given @@ -123,6 +192,9 @@ void testUpdateCategoryWhenMultipleCategoriesExists() { UpdateCategoryRequest request = new UpdateCategoryRequest("new Category", "new Description"); + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.of(menu)); + when(menuRepository.save(any(Menu.class))).thenReturn(menu); + // When String requestUrl = String.format(UPDATE_CATEGORY, getBaseURL(port), menu.getId(), categories.get(0).getId()); @@ -149,6 +221,9 @@ void testUpdateOnlyCategoryDescription() { UpdateCategoryRequest request = new UpdateCategoryRequest(categoryList.get(1).getName(), "new Description"); + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.of(menu)); + when(menuRepository.save(any(Menu.class))).thenReturn(menu); + // When String requestUrl = String.format( @@ -173,7 +248,12 @@ void testDeleteCategorySuccess() { // Given Menu menu = createMenu(1); Category category = createCategory(0); - menu.setCategories(List.of(category)); + List categories = new ArrayList<>(); + categories.add(category); + menu.setCategories(categories); + + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.of(menu)); + doNothing().when(menuRepository).delete(any(Menu.class)); // When String requestUrl = @@ -194,8 +274,11 @@ void testDeleteCategoryWithAnItem() { // Given Menu menu = createMenu(1); Category category = createCategory(0); - category.addOrUpdateItem(createItem(0)); - menu.addOrUpdateCategory(category); + menuHelperService.addOrUpdateItem(category, createItem(0)); + menuHelperService.addOrUpdateCategory(menu, category); + + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.of(menu)); + doNothing().when(menuRepository).delete(any(Menu.class)); // When String requestUrl = @@ -218,6 +301,9 @@ void testDeleteACategoryFromList() { List categories = createCategories(2); menu.setCategories(categories); + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.of(menu)); + doNothing().when(menuRepository).delete(any(Menu.class)); + // When String requestUrl = String.format(DELETE_CATEGORY, getBaseURL(port), menu.getId(), categories.get(0).getId()); diff --git a/java/src/test/java/com/amido/stacks/workloads/menu/api/v1/ItemControllerTest.java b/java/src/test/java/com/amido/stacks/workloads/menu/api/v1/ItemControllerTest.java index 422b76c3..57685d90 100644 --- a/java/src/test/java/com/amido/stacks/workloads/menu/api/v1/ItemControllerTest.java +++ b/java/src/test/java/com/amido/stacks/workloads/menu/api/v1/ItemControllerTest.java @@ -8,25 +8,38 @@ import static com.amido.stacks.workloads.util.TestHelper.getRequestHttpEntity; import static java.util.UUID.randomUUID; import static org.assertj.core.api.BDDAssertions.then; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.NOT_FOUND; import static org.springframework.http.HttpStatus.OK; import com.amido.stacks.core.api.dto.ErrorResponse; import com.amido.stacks.core.api.dto.response.ResourceCreatedResponse; import com.amido.stacks.core.api.dto.response.ResourceUpdatedResponse; +import com.amido.stacks.workloads.Application; import com.amido.stacks.workloads.menu.api.v1.dto.request.CreateItemRequest; import com.amido.stacks.workloads.menu.api.v1.dto.request.UpdateItemRequest; import com.amido.stacks.workloads.menu.domain.Category; import com.amido.stacks.workloads.menu.domain.Item; import com.amido.stacks.workloads.menu.domain.Menu; +import com.amido.stacks.workloads.menu.repository.MenuRepository; +import com.amido.stacks.workloads.menu.service.data.MenuQueryService; +import com.amido.stacks.workloads.menu.service.v1.CategoryService; +import com.amido.stacks.workloads.menu.service.v1.utility.MenuHelperService; +import com.azure.spring.autoconfigure.cosmos.CosmosAutoConfiguration; +import com.azure.spring.autoconfigure.cosmos.CosmosHealthConfiguration; +import com.azure.spring.autoconfigure.cosmos.CosmosRepositoriesAutoConfiguration; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.UUID; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.http.HttpEntity; @@ -35,8 +48,15 @@ import org.springframework.http.ResponseEntity; import org.springframework.test.context.ActiveProfiles; -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@EnableAutoConfiguration +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + classes = {Application.class}) +@EnableAutoConfiguration( + exclude = { + CosmosRepositoriesAutoConfiguration.class, + CosmosAutoConfiguration.class, + CosmosHealthConfiguration.class + }) @Tag("Integration") @ActiveProfiles("test") public class ItemControllerTest { @@ -51,17 +71,28 @@ public class ItemControllerTest { @Autowired private TestRestTemplate testRestTemplate; + @Autowired private MenuHelperService menuHelperService; + + @Autowired private CategoryService categoryService; + + @MockBean private MenuRepository menuRepository; + + @MockBean private MenuQueryService menuQueryService; + @Test void testAddItem() { // Given Menu menu = createMenu(1); Category category = new Category(randomUUID().toString(), "cat name", "cat description", new ArrayList<>()); - menu.addOrUpdateCategory(category); + menuHelperService.addOrUpdateCategory(menu, category); CreateItemRequest request = new CreateItemRequest("Some Name", "Some Description", 13.56d, true); + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.of(menu)); + when(menuRepository.save(any(Menu.class))).thenReturn(menu); + // When var response = this.testRestTemplate.postForEntity( @@ -77,7 +108,7 @@ void testAddItem() { } @Test - void testInvalidCategoryIdWilThrowBadRequest() { + void testInvalidCategoryIdWillThrowBadRequest() { // Given CreateItemRequest request = @@ -101,8 +132,8 @@ void testUpdateItemSuccess() { Menu menu = createMenu(0); Category category = createCategory(0); Item item = createItem(0); - category.addOrUpdateItem(item); - menu.addOrUpdateCategory(category); + menuHelperService.addOrUpdateItem(category, item); + menuHelperService.addOrUpdateCategory(menu, category); UpdateItemRequest request = new UpdateItemRequest("Some Name", "Some Description", 13.56d, true); @@ -111,6 +142,9 @@ void testUpdateItemSuccess() { String requestUrl = String.format(UPDATE_ITEM, getBaseURL(port), menu.getId(), category.getId(), item.getId()); + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.of(menu)); + when(menuRepository.save(any(Menu.class))).thenReturn(menu); + var response = this.testRestTemplate.exchange( requestUrl, @@ -123,6 +157,37 @@ void testUpdateItemSuccess() { then(response.getStatusCode()).isEqualTo(OK); } + @Test + void testUpdateItemFailure() { + // Given + Menu menu = createMenu(0); + Category category = createCategory(0); + Item item = createItem(0); + menuHelperService.addOrUpdateItem(category, item); + menuHelperService.addOrUpdateCategory(menu, category); + + UpdateItemRequest request = + new UpdateItemRequest("Some Name", "Some Description", 13.56d, true); + + // When + String requestUrl = + String.format(UPDATE_ITEM, getBaseURL(port), menu.getId(), category.getId(), item.getId()); + + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.empty()); + when(menuRepository.save(any(Menu.class))).thenReturn(menu); + + var response = + this.testRestTemplate.exchange( + requestUrl, + HttpMethod.PUT, + new HttpEntity<>(request, getRequestHttpEntity()), + ResourceUpdatedResponse.class); + + // Then + then(response).isNotNull(); + then(response.getStatusCode()).isEqualTo(NOT_FOUND); + } + @Test void testUpdateItemDescription() { // Given @@ -130,7 +195,7 @@ void testUpdateItemDescription() { Category category = createCategory(0); List items = createItems(2); category.setItems(items); - menu.addOrUpdateCategory(category); + menuHelperService.addOrUpdateCategory(menu, category); UpdateItemRequest request = new UpdateItemRequest(items.get(0).getName(), "Some Description2", 13.56d, true); @@ -140,6 +205,9 @@ void testUpdateItemDescription() { String.format( UPDATE_ITEM, getBaseURL(port), menu.getId(), category.getId(), items.get(0).getId()); + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.of(menu)); + when(menuRepository.save(any(Menu.class))).thenReturn(menu); + var response = this.testRestTemplate.exchange( requestUrl, @@ -158,13 +226,16 @@ void testDeleteItemSuccess() { Menu menu = createMenu(1); Category category = createCategory(0); Item item = new Item(UUID.randomUUID().toString(), "New Item", "Item description", 12.2d, true); - category.addOrUpdateItem(item); - menu.addOrUpdateCategory(category); + menuHelperService.addOrUpdateItem(category, item); + menuHelperService.addOrUpdateCategory(menu, category); // When String requestUrl = String.format(DELETE_ITEM, getBaseURL(port), menu.getId(), category.getId(), item.getId()); + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.of(menu)); + when(menuRepository.save(any(Menu.class))).thenReturn(menu); + var response = this.testRestTemplate.exchange( requestUrl, diff --git a/java/src/test/java/com/amido/stacks/workloads/menu/api/v1/MenuControllerTest.java b/java/src/test/java/com/amido/stacks/workloads/menu/api/v1/MenuControllerTest.java index 6e0c7ac4..fe3674dc 100644 --- a/java/src/test/java/com/amido/stacks/workloads/menu/api/v1/MenuControllerTest.java +++ b/java/src/test/java/com/amido/stacks/workloads/menu/api/v1/MenuControllerTest.java @@ -8,12 +8,17 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.core.IsNull.notNullValue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; +import static org.springframework.http.HttpStatus.NOT_FOUND; import static org.springframework.http.HttpStatus.OK; import com.amido.stacks.core.api.dto.response.ResourceCreatedResponse; import com.amido.stacks.core.api.dto.response.ResourceUpdatedResponse; +import com.amido.stacks.workloads.Application; import com.amido.stacks.workloads.menu.api.v1.dto.request.CreateMenuRequest; -import com.amido.stacks.workloads.menu.api.v1.dto.request.UpdateMenuRequest; import com.amido.stacks.workloads.menu.api.v1.dto.response.MenuDTO; import com.amido.stacks.workloads.menu.api.v1.dto.response.SearchMenuResult; import com.amido.stacks.workloads.menu.api.v1.dto.response.SearchMenuResultItem; @@ -24,10 +29,18 @@ import com.amido.stacks.workloads.menu.mappers.ItemMapper; import com.amido.stacks.workloads.menu.mappers.MenuMapper; import com.amido.stacks.workloads.menu.mappers.SearchMenuResultItemMapper; +import com.amido.stacks.workloads.menu.repository.MenuRepository; +import com.amido.stacks.workloads.menu.service.data.MenuQueryService; +import com.amido.stacks.workloads.menu.service.v1.MenuService; +import com.amido.stacks.workloads.menu.service.v1.utility.MenuHelperService; +import com.azure.spring.autoconfigure.cosmos.CosmosAutoConfiguration; +import com.azure.spring.autoconfigure.cosmos.CosmosHealthConfiguration; +import com.azure.spring.autoconfigure.cosmos.CosmosRepositoriesAutoConfiguration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; import org.junit.jupiter.api.Tag; @@ -35,16 +48,26 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.test.context.ActiveProfiles; -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@EnableAutoConfiguration +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + classes = {Application.class}) +@EnableAutoConfiguration( + exclude = { + CosmosRepositoriesAutoConfiguration.class, + CosmosAutoConfiguration.class, + CosmosHealthConfiguration.class + }) @Tag("Integration") @ActiveProfiles("test") public class MenuControllerTest { @@ -62,6 +85,10 @@ public class MenuControllerTest { @Autowired private TestRestTemplate testRestTemplate; + @Autowired private MenuService menuService; + + @Autowired private MenuHelperService menuHelperService; + @Autowired private MenuMapper menuMapper; @Autowired private CategoryMapper categoryMapper; @@ -70,6 +97,10 @@ public class MenuControllerTest { @Autowired private SearchMenuResultItemMapper searchMenuResultItemMapper; + @MockBean private MenuRepository menuRepository; + + @MockBean private MenuQueryService menuQueryService; + @Test void testCreateNewMenu() { // Given @@ -78,6 +109,11 @@ void testCreateNewMenu() { new CreateMenuRequest( m.getName(), m.getDescription(), UUID.fromString(m.getRestaurantId()), m.getEnabled()); + when(menuRepository.findAllByRestaurantIdAndName( + eq(m.getRestaurantId()), eq(m.getName()), any(Pageable.class))) + .thenReturn(new PageImpl<>(Collections.emptyList())); + when(menuRepository.save(any(Menu.class))).thenReturn(m); + // When var response = this.testRestTemplate.postForEntity( @@ -87,6 +123,31 @@ void testCreateNewMenu() { then(response.getStatusCode()).isEqualTo(HttpStatus.CREATED); } + @Test + void testCreateNewMenuFails() { + // Given + Menu m = createMenu(1); + CreateMenuRequest request = + new CreateMenuRequest( + m.getName(), m.getDescription(), UUID.fromString(m.getRestaurantId()), m.getEnabled()); + + List existing = new ArrayList<>(); + existing.add(m); + + when(menuRepository.findAllByRestaurantIdAndName( + eq(m.getRestaurantId()), eq(m.getName()), any(Pageable.class))) + .thenReturn(new PageImpl<>(existing)); + when(menuRepository.save(any(Menu.class))).thenReturn(m); + + // When + var response = + this.testRestTemplate.postForEntity( + getBaseURL(port) + CREATE_MENU, request, ResourceCreatedResponse.class); + + // Then + then(response.getStatusCode()).isEqualTo(HttpStatus.CONFLICT); + } + @Test public void listMenusAndPagination() { @@ -95,6 +156,9 @@ public void listMenusAndPagination() { int pageNumber = 5; int pageSize = 6; + when(menuQueryService.findAll(any(Integer.class), any(Integer.class))) + .thenReturn(new ArrayList<>()); + // When var response = this.testRestTemplate.getForEntity( @@ -131,6 +195,10 @@ public void listMenusFilteredByRestaurantId() { SearchMenuResult expectedResponse = new SearchMenuResult(DEFAULT_PAGE_SIZE, DEFAULT_PAGE_NUMBER, expectedMenuList); + when(menuQueryService.findAllByRestaurantId( + eq(UUID.fromString(restaurantId)), any(Integer.class), any(Integer.class))) + .thenReturn(menuList); + // When var result = this.testRestTemplate.getForEntity( @@ -142,6 +210,83 @@ public void listMenusFilteredByRestaurantId() { then(result.getBody().getResults()).containsAll(expectedResponse.getResults()); } + @Test + public void listMenusFilteredByRestaurantName() { + + // Given + final String menuId = "d5785e28-306b-4e23-add0-3f9092d395f8"; + final String restaurantId = "58a1df85-6bdc-412a-a118-0f0e394c1342"; + + List menuList = createMenus(3); + Menu match = new Menu(menuId, restaurantId, "name", "description", new ArrayList<>(), true); + match.setRestaurantId(restaurantId); + menuList.add(match); + List matching = Collections.singletonList(match); + + List expectedMenuList = + matching.stream() + .map(m -> searchMenuResultItemMapper.toDto(m)) + .collect(Collectors.toList()); + + SearchMenuResult expectedResponse = + new SearchMenuResult(DEFAULT_PAGE_SIZE, DEFAULT_PAGE_NUMBER, expectedMenuList); + + when(menuQueryService.findAllByNameContaining( + eq(match.getName()), any(Integer.class), any(Integer.class))) + .thenReturn(menuList); + + // When + var result = + this.testRestTemplate.getForEntity( + String.format("%s/v1/menu?searchTerm=%s", getBaseURL(port), match.getName()), + SearchMenuResult.class); + // Then + then(result.getBody().getPageNumber()).isEqualTo(expectedResponse.getPageNumber()); + then(result.getBody().getPageSize()).isEqualTo(expectedResponse.getPageSize()); + then(result.getBody().getResults()).containsAll(expectedResponse.getResults()); + } + + @Test + public void listMenusFilteredByRestaurantNameAndId() { + + // Given + final String menuId = "d5785e28-306b-4e23-add0-3f9092d395f8"; + final String restaurantId = "58a1df85-6bdc-412a-a118-0f0e394c1342"; + + List menuList = createMenus(3); + Menu match = new Menu(menuId, restaurantId, "name", "description", new ArrayList<>(), true); + match.setRestaurantId(restaurantId); + menuList.add(match); + List matching = Collections.singletonList(match); + + List expectedMenuList = + matching.stream() + .map(m -> searchMenuResultItemMapper.toDto(m)) + .collect(Collectors.toList()); + + SearchMenuResult expectedResponse = + new SearchMenuResult(DEFAULT_PAGE_SIZE, DEFAULT_PAGE_NUMBER, expectedMenuList); + + when(menuQueryService.findAllByRestaurantIdAndNameContaining( + eq(UUID.fromString(restaurantId)), + eq(match.getName()), + any(Integer.class), + any(Integer.class))) + .thenReturn(menuList); + + // When + var result = + this.testRestTemplate.getForEntity( + String.format( + "%s/v1/menu?restaurantId=%s&searchTerm=%s", + getBaseURL(port), restaurantId, match.getName()), + SearchMenuResult.class); + // Then + then(result.getBody().getPageNumber()).isEqualTo(expectedResponse.getPageNumber()); + then(result.getBody().getPageSize()).isEqualTo(expectedResponse.getPageSize()); + then(result.getBody().getResults()).containsAll(expectedResponse.getResults()); + } + @Test public void getMenuById() { // Given @@ -154,10 +299,12 @@ public void getMenuById() { Item item = new Item(itemId, "item name", "item description", 5.99d, true); Category category = new Category(categoryId, "cat name", "cat description", Arrays.asList(item)); - menu.addOrUpdateCategory(category); + menuHelperService.addOrUpdateCategory(menu, category); MenuDTO expectedResponse = menuMapper.toDto(menu); + when(menuQueryService.findById(UUID.fromString(menuId))).thenReturn(Optional.of(menu)); + // When var response = this.testRestTemplate.getForEntity( @@ -167,6 +314,22 @@ public void getMenuById() { then(response.getBody()).isEqualTo(expectedResponse); } + @Test + public void getMenuByIdNotFound() { + // Given + final String menuId = "d5785e28-306b-4e23-add0-3f9092d395f8"; + + when(menuQueryService.findById(UUID.fromString(menuId))).thenReturn(Optional.empty()); + + // When + var response = + this.testRestTemplate.getForEntity( + String.format("%s/v1/menu/%s", getBaseURL(port), menuId), MenuDTO.class); + + // Then + then(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + } + @Test public void listMenusWithDefaultPagination() { @@ -187,7 +350,12 @@ void testUpdateSuccess() { // Given Menu menu = createMenu(0); - UpdateMenuRequest request = new UpdateMenuRequest("new name", "new description", false); + MenuDTO request = new MenuDTO(); + request.setName("new name"); + request.setDescription("new description"); + request.setEnabled(false); + + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.of(menu)); // When var response = @@ -202,11 +370,38 @@ void testUpdateSuccess() { then(response.getStatusCode()).isEqualTo(HttpStatus.OK); } + @Test + void testUpdateFailure() { + // Given + Menu menu = createMenu(0); + + MenuDTO request = new MenuDTO(); + request.setName("new name"); + request.setDescription("new description"); + request.setEnabled(false); + + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.empty()); + + // When + var response = + this.testRestTemplate.exchange( + String.format(UPDATE_MENU, getBaseURL(port), menu.getId()), + HttpMethod.PUT, + new HttpEntity<>(request, getRequestHttpEntity()), + ResourceUpdatedResponse.class); + + // Then + then(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + } + @Test void testDeleteMenuSuccess() { // Given Menu menu = createMenu(1); + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.of(menu)); + doNothing().when(menuRepository).delete(any(Menu.class)); + var response = this.testRestTemplate.exchange( String.format(DELETE_MENU, getBaseURL(port), menu.getId()), @@ -216,4 +411,23 @@ void testDeleteMenuSuccess() { // Then then(response.getStatusCode()).isEqualTo(OK); } + + @Test + void testDeleteMenuFailure() { + // Given + Menu menu = createMenu(1); + + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.empty()); + doNothing().when(menuRepository).delete(any(Menu.class)); + + var response = + this.testRestTemplate.exchange( + String.format(DELETE_MENU, getBaseURL(port), menu.getId()), + HttpMethod.DELETE, + new HttpEntity<>(getRequestHttpEntity()), + Void.class); + + // Then + then(response.getStatusCode()).isEqualTo(NOT_FOUND); + } } diff --git a/java/src/test/java/com/amido/stacks/workloads/menu/api/v2/MenuControllerV2Test.java b/java/src/test/java/com/amido/stacks/workloads/menu/api/v2/MenuControllerV2Test.java index f78959b6..ca7df17d 100644 --- a/java/src/test/java/com/amido/stacks/workloads/menu/api/v2/MenuControllerV2Test.java +++ b/java/src/test/java/com/amido/stacks/workloads/menu/api/v2/MenuControllerV2Test.java @@ -3,21 +3,36 @@ import static com.amido.stacks.workloads.menu.domain.MenuHelper.createMenu; import static com.amido.stacks.workloads.util.TestHelper.getBaseURL; import static org.assertj.core.api.BDDAssertions.then; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; import com.amido.stacks.workloads.menu.api.v1.dto.response.MenuDTO; import com.amido.stacks.workloads.menu.domain.Menu; import com.amido.stacks.workloads.menu.mappers.MenuMapper; +import com.amido.stacks.workloads.menu.repository.MenuRepository; +import com.amido.stacks.workloads.menu.service.data.MenuQueryService; +import com.azure.spring.autoconfigure.cosmos.CosmosAutoConfiguration; +import com.azure.spring.autoconfigure.cosmos.CosmosHealthConfiguration; +import com.azure.spring.autoconfigure.cosmos.CosmosRepositoriesAutoConfiguration; +import java.util.Optional; +import java.util.UUID; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.test.context.ActiveProfiles; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@EnableAutoConfiguration +@EnableAutoConfiguration( + exclude = { + CosmosRepositoriesAutoConfiguration.class, + CosmosAutoConfiguration.class, + CosmosHealthConfiguration.class + }) @Tag("Integration") @ActiveProfiles("test") public class MenuControllerV2Test { @@ -30,6 +45,10 @@ public class MenuControllerV2Test { @Autowired private MenuMapper menuMapper; + @MockBean private MenuRepository menuRepository; + + @MockBean private MenuQueryService menuQueryService; + @Test void getMenuById() { // Given @@ -38,6 +57,9 @@ void getMenuById() { menu.setRestaurantId(restaurantId); MenuDTO expectedResponse = menuMapper.toDto(menu); + when(menuQueryService.findById(UUID.fromString(menu.getId()))).thenReturn(Optional.of(menu)); + when(menuRepository.save(any(Menu.class))).thenReturn(menu); + // When var response = this.testRestTemplate.getForEntity( diff --git a/java/src/test/java/com/amido/stacks/workloads/menu/domain/MenuHelper.java b/java/src/test/java/com/amido/stacks/workloads/menu/domain/MenuHelper.java index beaf6179..4e057c73 100644 --- a/java/src/test/java/com/amido/stacks/workloads/menu/domain/MenuHelper.java +++ b/java/src/test/java/com/amido/stacks/workloads/menu/domain/MenuHelper.java @@ -4,7 +4,6 @@ import java.util.List; import java.util.UUID; -/** @author ArathyKrishna */ public class MenuHelper { public static List createMenus(int count) { @@ -21,7 +20,7 @@ public static Menu createMenu(int counter) { UUID.randomUUID().toString(), counter + " Menu", counter + " Menu Description", - new ArrayList(), + new ArrayList<>(), true); } } diff --git a/java/src/test/java/com/amido/stacks/workloads/menu/service/data/CosmosMenuQueryServiceTest.java b/java/src/test/java/com/amido/stacks/workloads/menu/service/data/CosmosMenuQueryServiceTest.java new file mode 100644 index 00000000..1cca0742 --- /dev/null +++ b/java/src/test/java/com/amido/stacks/workloads/menu/service/data/CosmosMenuQueryServiceTest.java @@ -0,0 +1,169 @@ +package com.amido.stacks.workloads.menu.service.data; + +import static org.assertj.core.api.BDDAssertions.then; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +import com.amido.stacks.workloads.menu.domain.Menu; +import com.amido.stacks.workloads.menu.domain.MenuHelper; +import com.amido.stacks.workloads.menu.repository.MenuRepository; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; + +@Tag("Unit") +public class CosmosMenuQueryServiceTest { + + @Test + void findById() { + MenuRepository repository = mock(MenuRepository.class); + MenuQueryService menuQueryService = new CosmosMenuQueryService(repository); + + Menu menu = MenuHelper.createMenu(1); + + given(repository.findById(anyString())).willReturn(Optional.of(menu)); + + Optional optMenu = menuQueryService.findById(UUID.randomUUID()); + + then(optMenu).isNotEmpty(); + then(optMenu.get()).isEqualTo(menu); + } + + @Test + void findAll() { + + MenuRepository repository = mock(MenuRepository.class); + MenuQueryService menuQueryService = new CosmosMenuQueryService(repository); + + Pageable pageable = mock(Pageable.class); + + List results = MenuHelper.createMenus(2); + Page page1 = new PageImpl<>(results, pageable, 2); + Page page2 = new PageImpl<>(results, pageable, 2); + + // Given + given(repository.findAll(any(Pageable.class))).willReturn(page1); + given(repository.findAll(eq(pageable))).willReturn(page2); + + // When + List actualResults = menuQueryService.findAll(2, 5); + + // Then + then(actualResults).isEqualTo(results); + } + + @Test + void findAllNextPage() { + + MenuRepository repository = mock(MenuRepository.class); + MenuQueryService menuQueryService = new CosmosMenuQueryService(repository); + + Pageable pageable = mock(Pageable.class); + + List results = MenuHelper.createMenus(2); + Page page1 = new PageImpl<>(results, pageable, 2); + Page page2 = new PageImpl<>(results, pageable, 2); + + Page mockPage1 = (Page) mock(Page.class); + + // Given + given(repository.findAll(any(Pageable.class))).willReturn(page1); + given(repository.findAll(eq(pageable))).willReturn(page2); + + // When + List actualResults = menuQueryService.findAll(2, 5); + + // Then + then(actualResults).isEqualTo(results); + } + + @Test + void findAllByRestaurantId() { + + MenuRepository repository = mock(MenuRepository.class); + MenuQueryService menuQueryService = new CosmosMenuQueryService(repository); + + Pageable pageable = mock(Pageable.class); + + List results = MenuHelper.createMenus(2); + Page page1 = new PageImpl<>(results, pageable, 2); + Page page2 = new PageImpl<>(results, pageable, 2); + + UUID restaurantId = UUID.randomUUID(); + + // Given + given(repository.findAllByRestaurantId(eq(restaurantId.toString()), any(Pageable.class))) + .willReturn(page1); + given(repository.findAll(eq(pageable))).willReturn(page2); + + // When + List actualResults = menuQueryService.findAllByRestaurantId(restaurantId, 2, 5); + + // Then + then(actualResults).isEqualTo(results); + } + + @Test + void findAllByNameContaining() { + + MenuRepository repository = mock(MenuRepository.class); + MenuQueryService menuQueryService = new CosmosMenuQueryService(repository); + + Pageable pageable = mock(Pageable.class); + + List results = MenuHelper.createMenus(2); + Page page1 = new PageImpl<>(results, pageable, 2); + Page page2 = new PageImpl<>(results, pageable, 2); + + String searchTerm = "SICB"; + + // Given + given(repository.findAllByNameContaining(eq(searchTerm), any(Pageable.class))) + .willReturn(page1); + given(repository.findAll(eq(pageable))).willReturn(page2); + + // When + List actualResults = menuQueryService.findAllByNameContaining(searchTerm, 2, 5); + + // Then + then(actualResults).isEqualTo(results); + } + + @Test + void findAllByRestaurantIdAndNameContaining() { + + MenuRepository repository = mock(MenuRepository.class); + MenuQueryService menuQueryService = new CosmosMenuQueryService(repository); + + Pageable pageable = mock(Pageable.class); + + List results = MenuHelper.createMenus(2); + Page page1 = new PageImpl<>(results, pageable, 2); + Page page2 = new PageImpl<>(results, pageable, 2); + + String searchTerm = "SICB"; + UUID restaurantId = UUID.randomUUID(); + + // Given + given( + repository.findAllByRestaurantIdAndNameContaining( + eq(restaurantId.toString()), eq(searchTerm), any(Pageable.class))) + .willReturn(page1); + given(repository.findAll(eq(pageable))).willReturn(page2); + + // When + List actualResults = + menuQueryService.findAllByRestaurantIdAndNameContaining(restaurantId, searchTerm, 2, 5); + + // Then + then(actualResults).isEqualTo(results); + } +}