From 0d0ae64123013b07821dbd95f204c4d25f98dd25 Mon Sep 17 00:00:00 2001 From: William Chalifoux Date: Mon, 9 Oct 2023 11:10:06 -0400 Subject: [PATCH 1/2] feat(VIST-CPC-821): view all visits by role type (#508) JIRA: [link to jira ticket](https://champlainsaintlambert.atlassian.net/browse/CPC-821) ## Context: Changing which visits a user has access to view depending on the highest role assigned to its account. ## Changes Here is what a logged user can view based on the highest role assigned to the account. - Admin can view all the visits(this was already the case) - Vet can view all visits that have its vetId for the visit - Customer/Owner can view all visits that include a petI for a pet belonging to the owner Changed the styling of the visits-list table. Fixed the sorting functionality for the table which was previously not working. Multiple filters can be applied at once and the filters are not case-sensitive. Sorting can be used in conjunction with filter(s). ## Before and After UI ### Before ![image](https://github.com/cgerard321/champlain_petclinic/assets/12865356/e1a6bd77-9ef4-4b1b-ad51-f65b79f807c3) ### After Visits-list from an admin account. image Visits-list from a vet account. image Visits-list from an owner account. image #### Sorting Sorting by visitId ascending image Sorting by visitId descending image Sorting by status descending status descending sorting Filter petId contains 4 and status contains CONfir. This demonstrates that multiple filters can be applied at once and that the filters are not case-sensitive. petId contains 4 and status contains CONfir Sorting by visitId ascending and filter petId contains 4 visitId ascending and petId contains 4 Sorting by visitId descending and filter petId contains 4 visitId descending and petId contains 4 ## Dev notes Had to fix an endpoint mapping which was inconsistent between the customer-service and the CustomerServiceClient in the API-gateway for the GetPetsByOwnerId method to work in the BFFApiGatewayController. Removed lots of unused imports and commented out some unused code, most of which was everything relating to the old DTO VisitDetails and its flux Visits that was used before we had visitRequestDTO and VisitResposeDTO. --- .../CustomersServiceClient.java | 2 +- .../VisitsServiceClient.java | 202 +++++++------- .../dtos/Visits/VisitDetails.java | 13 +- .../dtos/Visits/VisitResponseDTO.java | 5 - .../bffapigateway/dtos/Visits/Visits.java | 13 +- .../BFFApiGatewayController.java | 133 +++++----- .../visit-list/visit-list.controller.js | 122 ++++----- .../visit-list/visit-list.template.html | 124 ++++----- .../scripts/visits/visits.controller.js | 144 +++++----- .../BillServiceClientIntegrationTest.java | 11 - .../VisitsServiceClientIntegrationTest.java | 131 ++++------ .../ApiGatewayControllerTest.java | 247 +++++++++--------- ...atewayControllerFilterIntegrationTest.java | 44 +--- .../presentationlayer/PetController.java | 2 +- docker-compose.yml | 2 +- .../BusinessLayer/VisitServiceImpl.java | 2 - .../DataLayer/DataSetupService.java | 7 +- .../PresentationLayer/VisitController.java | 9 +- .../DomainClientLayer/PetsClientUnitTest.java | 8 +- .../DomainClientLayer/VetsClientUnitTest.java | 5 +- .../VisitControllerUnitTest.java | 33 +-- 21 files changed, 565 insertions(+), 694 deletions(-) diff --git a/api-gateway/src/main/java/com/petclinic/bffapigateway/domainclientlayer/CustomersServiceClient.java b/api-gateway/src/main/java/com/petclinic/bffapigateway/domainclientlayer/CustomersServiceClient.java index 8718b70086..4cda73203f 100755 --- a/api-gateway/src/main/java/com/petclinic/bffapigateway/domainclientlayer/CustomersServiceClient.java +++ b/api-gateway/src/main/java/com/petclinic/bffapigateway/domainclientlayer/CustomersServiceClient.java @@ -140,7 +140,7 @@ public Mono getPet(final String ownerId, final String petId) { public Flux getPetsByOwnerId(final String ownerId) { return webClientBuilder.build().get() - .uri(customersServiceUrl + "/owners/" + ownerId + "/pets") + .uri(customersServiceUrl + "/pet/owner/" + ownerId + "/pets") .retrieve() .bodyToFlux(PetResponseDTO.class); } diff --git a/api-gateway/src/main/java/com/petclinic/bffapigateway/domainclientlayer/VisitsServiceClient.java b/api-gateway/src/main/java/com/petclinic/bffapigateway/domainclientlayer/VisitsServiceClient.java index 97b6335b82..f9f89e1007 100755 --- a/api-gateway/src/main/java/com/petclinic/bffapigateway/domainclientlayer/VisitsServiceClient.java +++ b/api-gateway/src/main/java/com/petclinic/bffapigateway/domainclientlayer/VisitsServiceClient.java @@ -13,18 +13,10 @@ import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.client.WebClient; -import org.springframework.web.util.UriComponentsBuilder; import org.webjars.NotFoundException; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; - import java.io.IOException; -import java.net.URI; -import java.util.List; - -import static java.rmi.server.LogStream.log; -import static java.util.stream.Collectors.joining; - /** * @author Maciej Szarlinski * Copied from https://github.com/spring-petclinic/spring-petclinic-microservices @@ -33,7 +25,6 @@ @Component @Slf4j public class VisitsServiceClient { - private final WebClient webClient; @Autowired @@ -42,19 +33,16 @@ public VisitsServiceClient( @Value("${app.visits-service-new.port}") String visitsServicePort ) { this.webClient = WebClient.builder() - .baseUrl("http://" + visitsServiceHost + ":" + visitsServicePort) + .baseUrl("http://" + visitsServiceHost + ":" + visitsServicePort + "/visits") .build(); } public Flux getAllVisits(){ return this.webClient .get() - .uri("/visits") + .uri("") .retrieve() - .onStatus(HttpStatusCode::is4xxClientError, error -> { -// HttpStatusCode statusCode = error.statusCode(); - return Mono.error(new IllegalArgumentException("Something went wrong and got a 400 error")); - }) + .onStatus(HttpStatusCode::is4xxClientError, error -> Mono.error(new IllegalArgumentException("Something went wrong and we got a 400 error"))) .onStatus(HttpStatusCode::is5xxServerError, error -> Mono.error(new IllegalArgumentException("Something went wrong and we got a 500 error"))) .bodyToFlux(VisitResponseDTO.class); } @@ -62,84 +50,36 @@ public Flux getAllVisits(){ public Flux getVisitsForStatus(final String status){ return webClient .get() - .uri("/visits/status/{status}", status) + .uri("/status/{status}", status) .retrieve() .bodyToFlux(VisitResponseDTO.class); } - public Mono getVisitsForPets(final List petIds) { - return this.webClient - .get() - .uri("/pets/visits?petId={petId}", joinIds(petIds)) - .retrieve() - .bodyToMono(Visits.class); - } - public Flux getVisitsForPet(final String petId){ + public Flux getVisitsForPet(final String petId){ return webClient .get() - .uri("/visits/pets/{petId}", petId) + .uri("/pets/{petId}", petId) .retrieve() - .bodyToFlux(VisitDetails.class); - } - - public Flux getPreviousVisitsForPet(final String petId) { - return webClient - .get() - .uri("/visits/previous/{petId}", petId) - .retrieve() - .bodyToFlux(VisitDetails.class); + .bodyToFlux(VisitResponseDTO.class); } - - public Flux getVisitForPractitioner(final int practitionerId){ + public Flux getVisitByPractitionerId(final String practitionerId){ return webClient .get() - .uri("visits/vets/{practitionerId}", practitionerId) + .uri("/practitioner/{practitionerId}", practitionerId) .retrieve() - .bodyToFlux(VisitDetails.class); - } - - /* - public Flux getVisitsByPractitionerIdAndMonth(final int practitionerId, final String startDate, final String endDate) { - return webClient - .get() - .uri("/visits/calendar/{practitionerId}?dates={startDate},{endDate}", practitionerId, startDate, endDate) - .retrieve() - .bodyToFlux(VisitDetails.class); + .bodyToFlux(VisitResponseDTO.class); } - */ - public Flux getScheduledVisitsForPet(final String petId) { - return webClient - .get() - .uri("/visits/scheduled/{petId}", petId) - .retrieve() - .bodyToFlux(VisitDetails.class); - } public Mono getVisitByVisitId(String visitId) { return webClient .get() - .uri("/visit/{visitId}", visitId) + .uri("/{visitId}", visitId) .retrieve() .bodyToMono(VisitResponseDTO.class); } - public Mono updateVisitForPet(VisitDetails visit) { - URI uri = UriComponentsBuilder - .fromPath("/owners/*/pets/{petId}/visits/{visitId}") - .buildAndExpand(visit.getPetId(), visit.getVisitId()) - .toUri(); - - return webClient - .put() - .uri(uri) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .body(Mono.just(visit), VisitDetails.class) - .retrieve() - .onStatus(HttpStatusCode::isError, response -> Mono.error(new BadRequestException("Failed to update visit"))) - .bodyToMono(VisitDetails.class); - } public Mono updateStatusForVisitByVisitId(String visitId, String status) { @@ -151,61 +91,51 @@ public Mono updateStatusForVisitByVisitId(String visitId, Stri }; return webClient - .put() - .uri("/visits/"+ visitId +"/status/" + newStatus) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .retrieve() - .bodyToMono(VisitResponseDTO.class); + .put() + .uri("/"+ visitId +"/status/" + newStatus) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .retrieve() + .bodyToMono(VisitResponseDTO.class); } -/* public Mono createVisitForPet(VisitDetails visit) { - return webClient - .post() - .uri("/visits") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .body(Mono.just(visit), VisitDetails.class) - .retrieve() - .bodyToMono(VisitDetails.class); - }*/ -public Mono createVisitForPet(VisitRequestDTO visit) { - return webClient + public Mono createVisitForPet(VisitRequestDTO visit) { + return webClient .post() - .uri("/visits") + .uri("") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .body(Mono.just(visit), VisitRequestDTO.class) .retrieve() - .onStatus(response -> response.is4xxClientError(), response -> { + .onStatus(HttpStatusCode::is4xxClientError, response -> { HttpStatusCode httpStatus = response.statusCode(); return response.bodyToMono(String.class) - .flatMap(errorMessage -> { - try { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode errorNode = objectMapper.readTree(errorMessage); - String message = errorNode.get("message").asText(); - - if (httpStatus == HttpStatus.NOT_FOUND) { - return Mono.error(new NotFoundException(message)); - } else { - return Mono.error(new BadRequestException(message)); - } - } catch (IOException e) { - // Handle parsing error - return Mono.error(new BadRequestException("Bad Request")); + .flatMap(errorMessage -> { + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode errorNode = objectMapper.readTree(errorMessage); + String message = errorNode.get("message").asText(); + + if (httpStatus == HttpStatus.NOT_FOUND) { + return Mono.error(new NotFoundException(message)); + } else { + return Mono.error(new BadRequestException(message)); } - }); + } catch (IOException e) { + // Handle parsing error + return Mono.error(new BadRequestException("Bad Request")); + } + }); }) .bodyToMono(VisitResponseDTO.class); -} + } public Mono deleteVisitByVisitId(String visitId){ return webClient .delete() - .uri("/visits/{visitId}", visitId) + .uri("/{visitId}", visitId) .retrieve() .bodyToMono(Void.class); } - public Mono deleteAllCancelledVisits(){ return webClient .delete() @@ -214,12 +144,70 @@ public Mono deleteAllCancelledVisits(){ .bodyToMono(Void.class); } + /* + public Flux getPreviousVisitsForPet(final String petId) { + return webClient + .get() + .uri("/visits/previous/{petId}", petId) + .retrieve() + .bodyToFlux(VisitDetails.class); + } + + + public Flux getScheduledVisitsForPet(final String petId) { + return webClient + .get() + .uri("/visits/scheduled/{petId}", petId) + .retrieve() + .bodyToFlux(VisitDetails.class); + } +*/ +// public Mono updateVisitForPet(VisitDetails visit) { +// URI uri = UriComponentsBuilder +// .fromPath("/owners/*/pets/{petId}/visits/{visitId}") +// .buildAndExpand(visit.getPetId(), visit.getVisitId()) +// .toUri(); +/* + return webClient + .put() + .uri(uri) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .body(Mono.just(visit), VisitDetails.class) + .retrieve() + .onStatus(HttpStatusCode::isError, response -> Mono.error(new BadRequestException("Failed to update visit"))) + .bodyToMono(VisitDetails.class); + } private String joinIds(List petIds) { return petIds.stream().map(Object::toString).collect(joining(",")); } + public Mono getVisitsForPets(final List petIds) { + return this.webClient + .get() + .uri("/pets/visits?petId={petId}", joinIds(petIds)) + .retrieve() + .bodyToMono(Visits.class); + } + + public Flux getVisitsByPractitionerIdAndMonth(final int practitionerId, final String startDate, final String endDate) { + return webClient + .get() + .uri("/visits/calendar/{practitionerId}?dates={startDate},{endDate}", practitionerId, startDate, endDate) + .retrieve() + .bodyToFlux(VisitDetails.class); + } + + return webClient + .post() + .uri("/visits") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .body(Mono.just(visit), VisitDetails.class) + .retrieve() + .bodyToMono(VisitDetails.class); + + }*/ } diff --git a/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Visits/VisitDetails.java b/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Visits/VisitDetails.java index f8dc7c1154..8dc66bdd0f 100644 --- a/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Visits/VisitDetails.java +++ b/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Visits/VisitDetails.java @@ -1,5 +1,5 @@ package com.petclinic.bffapigateway.dtos.Visits; - +/* import com.fasterxml.jackson.annotation.JsonFormat; import lombok.AllArgsConstructor; import lombok.Builder; @@ -7,12 +7,12 @@ import lombok.NoArgsConstructor; import java.time.LocalDateTime; - -/** - * @author Maciej Szarlinski - * Copied from https://github.com/spring-petclinic/spring-petclinic-microservices +*/ +/* + @author Maciej Szarlinski + Copied from https://github.com/spring-petclinic/spring-petclinic-microservices */ - +/* @Data @NoArgsConstructor @AllArgsConstructor @@ -34,3 +34,4 @@ public class VisitDetails { private Status status = null; } + */ \ No newline at end of file diff --git a/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Visits/VisitResponseDTO.java b/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Visits/VisitResponseDTO.java index c006a0e20b..2a5a43a4ed 100644 --- a/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Visits/VisitResponseDTO.java +++ b/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Visits/VisitResponseDTO.java @@ -14,15 +14,10 @@ @Builder public class VisitResponseDTO { private String visitId; -/* private int year; - private int month; - private int day;*/ @JsonFormat(pattern = "yyyy-MM-dd HH:mm") private LocalDateTime visitDate; private String description; private String petId; private String practitionerId; -/* private int petId; - private int practitionerId;*/ private Status status; } \ No newline at end of file diff --git a/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Visits/Visits.java b/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Visits/Visits.java index 63e8a40c8b..a57cf9a035 100644 --- a/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Visits/Visits.java +++ b/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Visits/Visits.java @@ -1,19 +1,16 @@ package com.petclinic.bffapigateway.dtos.Visits; - +/* import lombok.Value; - import java.util.ArrayList; import java.util.List; - -/** +*/ +/* * @author Maciej Szarlinski * Copied from https://github.com/spring-petclinic/spring-petclinic-microservices */ - +/* @Value public class Visits { - private List items = new ArrayList<>(); - } - +*/ \ No newline at end of file diff --git a/api-gateway/src/main/java/com/petclinic/bffapigateway/presentationlayer/BFFApiGatewayController.java b/api-gateway/src/main/java/com/petclinic/bffapigateway/presentationlayer/BFFApiGatewayController.java index bbe57de2f5..b10ec36fe3 100644 --- a/api-gateway/src/main/java/com/petclinic/bffapigateway/presentationlayer/BFFApiGatewayController.java +++ b/api-gateway/src/main/java/com/petclinic/bffapigateway/presentationlayer/BFFApiGatewayController.java @@ -2,9 +2,7 @@ import com.petclinic.bffapigateway.domainclientlayer.*; -import com.petclinic.bffapigateway.dtos.Auth.Login; import com.petclinic.bffapigateway.dtos.Auth.*; -import com.petclinic.bffapigateway.dtos.Auth.UserPasswordLessDTO; import com.petclinic.bffapigateway.dtos.Bills.BillRequestDTO; import com.petclinic.bffapigateway.dtos.Bills.BillResponseDTO; import com.petclinic.bffapigateway.dtos.CustomerDTOs.OwnerRequestDTO; @@ -18,7 +16,6 @@ import com.petclinic.bffapigateway.dtos.Visits.VisitRequestDTO; import com.petclinic.bffapigateway.utils.Security.Annotations.IsUserSpecific; import com.petclinic.bffapigateway.utils.Security.Annotations.SecuredEndpoint; -import com.petclinic.bffapigateway.dtos.Visits.VisitDetails; import com.petclinic.bffapigateway.dtos.Visits.VisitResponseDTO; import com.petclinic.bffapigateway.utils.Security.Variables.Roles; import com.petclinic.bffapigateway.utils.VetsEntityDtoUtil; @@ -29,8 +26,6 @@ import org.springframework.http.*; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -158,13 +153,11 @@ public Mono> createPet(@RequestBody PetResponseDT .defaultIfEmpty(ResponseEntity.badRequest().build()); } - /*@GetMapping(value = "owners/{ownerId}/pets") public Flux getAllPetsFromOwnerId(@PathVariable String ownerId){ return customersServiceClient.getAllPets(ownerId); }*/ - @SecuredEndpoint(allowedRoles = {Roles.OWNER,Roles.ADMIN,Roles.VET}) @PatchMapping(value = "/pet/{petId}", produces = "application/json", consumes = "application/json") public Mono> patchPet(@RequestBody PetRequestDTO pet, @PathVariable String petId) { @@ -228,81 +221,44 @@ public Mono> updatePet(@RequestBody PetResponseDT } - /** - * Visits Methods - **/ + + /* Visits Methods */ @SecuredEndpoint(allowedRoles = {Roles.ADMIN}) @GetMapping(value = "visits", produces= MediaType.TEXT_EVENT_STREAM_VALUE) public Flux getAllVisits() { return visitsServiceClient.getAllVisits(); } - @GetMapping(value = "visits/previous/{petId}", produces= MediaType.TEXT_EVENT_STREAM_VALUE) - public Flux getPreviousVisitsForPet(@PathVariable final String petId) { - return visitsServiceClient.getPreviousVisitsForPet(petId); - } - - @GetMapping(value = "visits/scheduled/{petId}", produces= MediaType.TEXT_EVENT_STREAM_VALUE) - public Flux getScheduledVisitsForPet(@PathVariable final String petId) { - return visitsServiceClient.getScheduledVisitsForPet(petId); + @SecuredEndpoint(allowedRoles = {Roles.ADMIN,Roles.VET,Roles.OWNER}) + @IsUserSpecific(idToMatch = {"ownerId"}, bypassRoles = {Roles.ADMIN,Roles.VET}) + @GetMapping(value = "visits/owners/{ownerId}", produces= MediaType.TEXT_EVENT_STREAM_VALUE) + public Flux getVisitsByOwnerId(@PathVariable String ownerId){ +//not ideal since returns complete pet dto + return getPetsByOwnerId(ownerId).flatMap(petResponseDTO -> getVisitsForPet(petResponseDTO.getPetId())); } - @GetMapping(value = "visits/vets/{practitionerId}", produces= MediaType.TEXT_EVENT_STREAM_VALUE) - public Flux getVisitForPractitioner(@PathVariable int practitionerId){ - return visitsServiceClient.getVisitForPractitioner(practitionerId); + public Flux getVisitByPractitionerId(@PathVariable String practitionerId){ + return visitsServiceClient.getVisitByPractitionerId(practitionerId); } - /* - @GetMapping(value = "visits/calendar/{practitionerId}") - public Flux getVisitsByPractitionerIdAndMonth(@PathVariable("practitionerId") int practitionerId, - @RequestParam("dates") List dates) { - String startDate = dates.get(0); - String endDate = dates.get(1); - return visitsServiceClient.getVisitsByPractitionerIdAndMonth(practitionerId, startDate, endDate); - } - */ @GetMapping(value = "visits/pets/{petId}", produces= MediaType.TEXT_EVENT_STREAM_VALUE) - public Flux getVisitsForPet(final @PathVariable String petId){ + public Flux getVisitsForPet(@PathVariable String petId){ return visitsServiceClient.getVisitsForPet(petId); } @GetMapping(value = "visits/status/{status}", produces= MediaType.TEXT_EVENT_STREAM_VALUE) - public Flux getVisitsForStatus(final @PathVariable String status){ + public Flux getVisitsForStatus(@PathVariable String status){ return visitsServiceClient.getVisitsForStatus(status); } @GetMapping(value ="visits/{visitId}") - public Mono getVisitByVisitId(final @PathVariable String visitId){ + public Mono getVisitByVisitId(@PathVariable String visitId){ return visitsServiceClient.getVisitByVisitId(visitId); } - - /* - private Function addVisitsToOwner(OwnerResponseDTO owner) { - return visits -> { - owner.getPets() - .forEach(pet -> pet.getVisits() - .addAll(visits.getItems().stream() - .filter(v -> v.getPetId() == pet.getId()) - .collect(Collectors.toList())) - ); - return owner; - }; - } - */ - - - - @PostMapping(value = "visit/owners/5fe81e29-1f1d-4f9d-b249-8d3e0cc0b7dd/pets/9/visits", consumes = "application/json", produces = "application/json") - Mono> addVisit(@RequestBody VisitRequestDTO visit/* @PathVariable String ownerId, @PathVariable String petId*/) { - // visit.setPetId(petId); - return visitsServiceClient.createVisitForPet(visit).map(s -> ResponseEntity.status(HttpStatus.CREATED).body(s)) - .defaultIfEmpty(ResponseEntity.badRequest().build()); - } - @PutMapping(value = "owners/*/pets/{petId}/visits/{visitId}", consumes = "application/json", produces = "application/json") - Mono> updateVisit(@RequestBody VisitDetails visit, @PathVariable String petId, @PathVariable String visitId) { - visit.setPetId(petId); - visit.setVisitId(visitId); - return visitsServiceClient.updateVisitForPet(visit).map(s -> ResponseEntity.status(HttpStatus.OK).body(s)) + @PostMapping(value = "visit/owners/{ownerId}/pets/{petId}/visits", consumes = "application/json", produces = "application/json") + Mono> addVisit(@RequestBody VisitRequestDTO visit, @PathVariable String ownerId, @PathVariable String petId) { + // visit.setPetId(petId); + return visitsServiceClient.createVisitForPet(visit).map(ResponseEntity.status(HttpStatus.CREATED)::body) .defaultIfEmpty(ResponseEntity.badRequest().build()); } @@ -310,9 +266,8 @@ Mono> updateVisit(@RequestBody VisitDetails visit, Mono updateStatusForVisitByVisitId(@PathVariable String visitId, @PathVariable String status) { return visitsServiceClient.updateStatusForVisitByVisitId(visitId, status); } - @DeleteMapping (value = "visits/{visitId}") - public Mono> deleteVisitsByVisitId(final @PathVariable String visitId){ + public Mono> deleteVisitsByVisitId(@PathVariable String visitId){ return visitsServiceClient.deleteVisitByVisitId(visitId).then(Mono.just(ResponseEntity.noContent().build())) .defaultIfEmpty(ResponseEntity.notFound().build()); } @@ -321,9 +276,59 @@ public Mono> deleteVisitsByVisitId(final @PathVariable Stri public Mono> deleteAllCancelledVisits(){ return visitsServiceClient.deleteAllCancelledVisits().then(Mono.just(ResponseEntity.noContent().build())) .defaultIfEmpty(ResponseEntity.notFound().build()); + } + // @PutMapping(value = "owners/*/pets/{petId}/visits/{visitId}", consumes = "application/json", produces = "application/json") + /* + Mono> updateVisit(@RequestBody VisitDetails visit, @PathVariable String petId, @PathVariable String visitId) { + visit.setPetId(petId); + visit.setVisitId(visitId); + return visitsServiceClient.updateVisitForPet(visit).map(s -> ResponseEntity.status(HttpStatus.OK).body(s)) + .defaultIfEmpty(ResponseEntity.badRequest().build()); + } + + @GetMapping(value = "visits/previous/{petId}", produces= MediaType.TEXT_EVENT_STREAM_VALUE) + public Flux getPreviousVisitsForPet(@PathVariable final String petId) { + return visitsServiceClient.getPreviousVisitsForPet(petId); + } + @GetMapping(value = "visits/scheduled/{petId}", produces= MediaType.TEXT_EVENT_STREAM_VALUE) + public Flux getScheduledVisitsForPet(@PathVariable final String petId) { + return visitsServiceClient.getScheduledVisitsForPet(petId); + } + @GetMapping(value = "visits/calendar/{practitionerId}") + public Flux getVisitsByPractitionerIdAndMonth(@PathVariable("practitionerId") int practitionerId, @RequestParam("dates") List dates) { + String startDate = dates.get(0); + String endDate = dates.get(1); + return visitsServiceClient.getVisitsByPractitionerIdAndMonth(practitionerId, startDate, endDate); + } + private Function addVisitsToOwner(OwnerResponseDTO owner) { + return visits -> { + owner.getPets() + .forEach(pet -> pet.getVisits() + .addAll(visits.getItems().stream() + .filter(v -> v.getPetId() == pet.getId()) + .collect(Collectors.toList())) + ); + return owner; + }; + } +*/ + + @PostMapping(value = "visit/owners/5fe81e29-1f1d-4f9d-b249-8d3e0cc0b7dd/pets/9/visits", consumes = "application/json", produces = "application/json") + Mono> addVisit(@RequestBody VisitRequestDTO visit/* @PathVariable String ownerId, @PathVariable String petId*/) { + // visit.setPetId(petId); + return visitsServiceClient.createVisitForPet(visit).map(s -> ResponseEntity.status(HttpStatus.CREATED).body(s)) + .defaultIfEmpty(ResponseEntity.badRequest().build()); } +// @PutMapping(value = "owners/*/pets/{petId}/visits/{visitId}", consumes = "application/json", produces = "application/json") +// Mono> updateVisit(@RequestBody VisitDetails visit, @PathVariable String petId, @PathVariable String visitId) { +// visit.setPetId(petId); +// visit.setVisitId(visitId); +// return visitsServiceClient.updateVisitForPet(visit).map(s -> ResponseEntity.status(HttpStatus.OK).body(s)) +// .defaultIfEmpty(ResponseEntity.badRequest().build()); +// } + /* End of Visit Methods */ /** * End of Visit Methods @@ -847,4 +852,4 @@ public Flux getAllPetTypes() { .map(addVisitsToOwner(n)) );*/ } -} +} \ No newline at end of file diff --git a/api-gateway/src/main/resources/static/scripts/visit-list/visit-list.controller.js b/api-gateway/src/main/resources/static/scripts/visit-list/visit-list.controller.js index 357aecde01..7b5bfa81ee 100644 --- a/api-gateway/src/main/resources/static/scripts/visit-list/visit-list.controller.js +++ b/api-gateway/src/main/resources/static/scripts/visit-list/visit-list.controller.js @@ -1,12 +1,21 @@ 'use strict'; angular.module('visitList') - .controller('VisitListController', ['$http', '$scope', function ($http, $scope) { + .controller('VisitListController', ['$http', '$scope', '$window', 'orderByFilter', function ($http, $scope, $window, orderBy) { let self = this; // Lists holding visits for the tables to display self.upcomingVisits = [] self.previousVisits = [] - - let eventSource = new EventSource("api/gateway/visits") + let url + //sorted by order or most to least permissions + if ($window.localStorage.getItem("roles").includes("ADMIN")){ + url = "api/gateway/visits" + }else if ($window.localStorage.getItem("roles").includes("VET")) { + url = "api/gateway/visits/vets/"+$window.localStorage.getItem("practitionerIdAndMonth").split(",")[0] + }else if ($window.localStorage.getItem("roles").includes("OWNER")) { + url = "api/gateway/visits/owners/"+$window.localStorage.getItem("UUID") + }else{url = ""} + + let eventSource = new EventSource(url) eventSource.addEventListener('message', function (event){ $scope.$apply(function(){ console.log(event.data) @@ -22,115 +31,91 @@ angular.module('visitList') } } - function delayedReload() { - var loadingIndicator = document.getElementById('loadingIndicator'); - loadingIndicator.style.display = 'block'; - setTimeout(function() { - location.reload(); - }, 500); //delay by 1 second - } - $scope.confirmVisit = function (visitId, status){ - console.log(status) - switch(status){ - case "UPCOMING": status = "CONFIRMED" break; - case "CONFIRMED": status = "COMPLETED" break; - default: break; } console.log(status) - let putURL = 'api/gateway/visits/' + visitId + '/status/' + status; - $http.put(putURL, status) - .then(successCallback, errorCallback) + $http.put(putURL, status).then(successCallback, errorCallback) function successCallback(response) { - $scope.errors = []; - alert(visitId + " visit was confirmed successfully"); - console.log(response, 'res'); - delayedReload(); - } - - function errorCallback(error) { - alert($scope.errors); - console.log(error, 'Could not receive data'); + $scope.errors = [] + alert(visitId + " visit was confirmed successfully") + console.log(response, 'res') + delayedReload() } } $scope.cancelVisit = function (visitId, status){ - - let putURL = 'api/gateway/visits/' + visitId + '/status/' + status; - - $http.put(putURL, status) - .then(successCallback, errorCallback) + let putURL = 'api/gateway/visits/' + visitId + '/status/' + status + console.log(putURL) + $http.put(putURL, status).then(successCallback, errorCallback) function successCallback(response) { - $scope.errors = []; - alert(visitId + " visit was cancelled successfully"); - console.log(response, 'res'); - delayedReload(); - } - - function errorCallback(error) { - alert($scope.errors); - console.log(error, 'Could not receive data'); + $scope.errors = [] + alert(visitId + " visit was cancelled successfully") + console.log(response, 'res') + delayedReload() } } - $scope.deleteVisit = function (visitId) { - let varIsConf = confirm('You are about to delete visit ' + visitId + '. Is this what you want to do ? '); + let varIsConf = confirm('You are about to delete visit ' + visitId + '. Is this what you want to do ? ') if (varIsConf) { - - $http.delete('api/gateway/visits/' + visitId) - .then(successCallback, errorCallback) + $http.delete('api/gateway/visits/' + visitId).then(successCallback, errorCallback) function successCallback(response) { - $scope.errors = []; - alert(visitId + " visit was deleted successfully"); - console.log(response, 'res'); - delayedReload(); - } - - function errorCallback(error) { - alert(data.errors); - console.log(error, 'Could not receive data'); + $scope.errors = [] + alert(visitId + " visit was deleted successfully") + console.log(response, 'res') + delayedReload() } } - }; + } $scope.deleteAllCancelledVisits = function () { let varIsConf = confirm('You are about to delete all canceled visits. Is this what you want to do ? '); if (varIsConf) { - $http.delete('api/gateway/visits/cancelled') - .then(successCallback, errorCallback); - + $http.delete('api/gateway/visits/cancelled').then(successCallback, errorCallback); function successCallback(response) { $scope.errors = []; alert("All canceled visits were deleted successfully"); console.log(response, 'res'); delayedReload(); } - - function errorCallback(error) { - alert(data.errors); - console.log(error, 'Could not receive data'); - } } - }; + } - }]); + $scope.propertyName = 'visitId' + $scope.reverse = false + $scope.upcomingVisits = self.upcomingVisits + $scope.sortBy = function(propertyName){ + $scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false + $scope.propertyName = propertyName + } + function delayedReload() { + let loadingIndicator = document.getElementById('loadingIndicator') + loadingIndicator.style.display = 'block' + setTimeout(function() { + location.reload() + }, 1000) //delay by 1 second + } + function errorCallback(error) { + alert(error.errors) + console.log(error, 'Could not receive data') + } + }]) // // self.sortFetchedVisits = function() { // // let currentDate = getCurrentDate() @@ -150,5 +135,4 @@ angular.module('visitList') // // var yyyy = dateObj.getFullYear() // // return Date.parse(yyyy + '-' + mm + '-' + dd) // // } - // }]) - + // }]) \ No newline at end of file diff --git a/api-gateway/src/main/resources/static/scripts/visit-list/visit-list.template.html b/api-gateway/src/main/resources/static/scripts/visit-list/visit-list.template.html index 46544d5d25..2c1aac1a49 100644 --- a/api-gateway/src/main/resources/static/scripts/visit-list/visit-list.template.html +++ b/api-gateway/src/main/resources/static/scripts/visit-list/visit-list.template.html @@ -12,8 +12,9 @@ align-items: center; margin-bottom: 20px; } + .sortorder:after {content: '\25b2'; /* BLACK UP-POINTING TRIANGLE */} + .sortorder.reverse:after {content: '\25bc'; /* BLACK DOWN-POINTING TRIANGLE */} - .original-button { color: #fff; background-color: #19d256; @@ -23,8 +24,6 @@ width: 200px; height: 40px; font-weight: bold; - - } .original-button:hover { @@ -38,10 +37,9 @@ justify-content: flex-end; align-items: center; } - -
+
-

Upcoming Visits

+

Visits

-

Type something in the input field to search throughout the table:

- - - - - - - - - - +
- - - - - - - + + + + + + + @@ -83,72 +74,49 @@

Upcoming Visits

- - - - + + + + + + + + + + + + + + + + + + + + - - - + + + - - - - + - - - - - -
+ + +
{{v.visitId}}{{v.visitDate | date:'yyyy-MM-ddTHH:mm:ss'}}{{v.description}}
{{v.visitId}}{{v.visitDate | date:'yyyy-MM-ddTHH:mm:ss'}}{{v.description}} {{v.practitionerId}}{{v.petId}}{{v.status}}{{v.practitionerId}}{{v.petId}}{{v.status}} + + 'btn btn-success': v.status === 'UPCOMING', + 'btn btn-info': v.status === 'CONFIRMED', + 'btn btn-default': v.status === 'COMPLETED' || v.status === 'CANCELLED' + }" href="javascript:void(0)" ng-click="confirmVisit(v.visitId, v.status)"> {{ v.status === 'UPCOMING' ? 'Confirm Visit' : v.status === 'CONFIRMED' ? 'Complete Visit' : v.status === 'CANCELLED' ? 'Cannot Confirm Cancelled Visit' : 'Visit Completed!' }} {{ v.status === 'UPCOMING' ? 'Cancel Visit' : v.status === 'CONFIRMED' ? 'Cannot Cancel Confirmed Visit' : v.status === 'COMPLETED' ? 'Cannot Cancel Completed Visit' : 'Visit Cancelled!' }} Delete Visit

Previous Visits

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
{{v.visitId}}{{visitsCtrl.visit.visitDate}}{{v.description}}{{v.practitionerId}}{{v.petId}}{{v.status}}Cancel VisitDelete Visit
\ No newline at end of file diff --git a/api-gateway/src/main/resources/static/scripts/visits/visits.controller.js b/api-gateway/src/main/resources/static/scripts/visits/visits.controller.js index fb94cb3d03..3bc6463a23 100755 --- a/api-gateway/src/main/resources/static/scripts/visits/visits.controller.js +++ b/api-gateway/src/main/resources/static/scripts/visits/visits.controller.js @@ -1,58 +1,58 @@ 'use strict'; angular.module('visits') - .controller('VisitsController', ['$http', '$state', '$stateParams', '$filter','$scope','$rootScope', function ($http, $state, $stateParams, $filter, $scope,$rootScope) { - var self = this; - var petId = $stateParams.petId || 0; - var postURL = "api/gateway/visit/owners/5fe81e29-1f1d-4f9d-b249-8d3e0cc0b7dd/pets/9/visits"; - var vetsUrl = "api/gateway/vets"; - var billsUrl = "api/gateway/bill"; - var visitId = 0; - self.practitionerId = 0; - self.date = new Date(); - self.desc = ""; - self.chosenDate = null; - self.chosenTime = null; + .controller('VisitsController', ['$http', '$state', '$stateParams', '$filter','$scope','$rootScope', '$window', function ($http, $state, $stateParams, $filter, $scope, $rootScope, $window) { + var self = this + var petId = $stateParams.petId || 0 + var postURL = "api/gateway/visit/owners/"+$window.localStorage.getItem("UUID")+"/pets/9/visits" + var vetsUrl = "api/gateway/vets" + var billsUrl = "api/gateway/bill" + var visitId = 0 + self.practitionerId = 0 + // self.date = new Date() + self.desc = "" + self.chosenDate = null + self.chosenTime = null - $http.get("api/gateway/visits/"+petId).then(function (resp) { - self.visits = resp.data; - self.sortFetchedVisits(); - }); + // $http.get("api/gateway/visits/"+petId).then(function (resp) { + // self.visits = resp.data; + // self.sortFetchedVisits(); + // }); $scope.$on('selectedDateChanged', function(event, selectedDate) { - console.log("Received selected date:", selectedDate); + console.log("Received selected date:", selectedDate) // Set dateChosen to the received selected date - self.chosenDate = selectedDate; - }); + self.chosenDate = selectedDate + }) // Function to... get the current date ;) function getCurrentDate() { - let dateObj = new Date(); - var dd = String(dateObj.getDate()).padStart(2, '0'); - var mm = String(dateObj.getMonth() + 1).padStart(2, '0'); - var yyyy = dateObj.getFullYear(); - console.log("got currentDate"); - return Date.parse(yyyy + '-' + mm + '-' + dd); + let dateObj = new Date() + var dd = String(dateObj.getDate()).padStart(2, '0') + var mm = String(dateObj.getMonth() + 1).padStart(2, '0') + var yyyy = dateObj.getFullYear() + console.log("got currentDate") + return Date.parse(yyyy + '-' + mm + '-' + dd) } //TODO: make a function to parse the date to localDate //---------------------------------------------------- // Container div for all alerts - let alertsContainer = $('#alertsContainer'); + let alertsContainer = $('#alertsContainer') // Function to delete last added alert function deleteAlertAfter(alertId, time) { setTimeout(function() { if(alertsContainer.children().length === 1 && alertsContainer.children(".alert:first-child").attr("id") === alertId) { - alertsContainer.children(".alert:first-child").remove(); + alertsContainer.children(".alert:first-child").remove() } - }, time); + }, time) } - let alertId = 0; + let alertId = 0 // Function to create alert function createAlert(alertType, alertMessage) { // Create an alert based on parameters @@ -60,35 +60,35 @@ angular.module('visits') "
" + "

" + alertMessage + "

" + "
" - ); + ) // If there is already an alert make place for new one if(alertsContainer.children().length > 1) { - alertsContainer.children(".alert:first-child").remove(); + alertsContainer.children(".alert:first-child").remove() } - console.log(alertsContainer.children().length); + console.log(alertsContainer.children().length) // Delete the alert after x amount of time (millis) - deleteAlertAfter("alert-" + alertId, 3000); + deleteAlertAfter("alert-" + alertId, 3000) } // Lists holding visits for the table to display - self.upcomingVisits = []; - self.previousVisits = []; + self.upcomingVisits = [] + self.previousVisits = [] self.sortFetchedVisits = function() { - let currentDate = getCurrentDate(); + let currentDate = getCurrentDate() $.each(self.visits, function(i, visit) { - let selectedVisitDate = Date.parse(visit.date); + let selectedVisitDate = Date.parse(visit.date) if(selectedVisitDate >= currentDate) { - self.upcomingVisits.push(visit); + self.upcomingVisits.push(visit) } else { - self.previousVisits.push(visit); + self.previousVisits.push(visit) } - }); + }) } /* self.getVisitsForPractitionerIdAndMonth = function() { @@ -125,61 +125,61 @@ angular.module('visits') $http.get(vetsUrl).then(function (resp) { self.vets = resp.data; - }); + }) self.loadVetInfo = function() { - let selectedVetsId = $("#selectedVet").val(); + let selectedVetsId = $("#selectedVet").val() - let foundVet = false; - let vetPhoneNumber = ""; - let vetEmailAddress = ""; - let vetWorkdays = ""; - let vetSpecialtiesObject = null; + let foundVet = false + let vetPhoneNumber = "" + let vetEmailAddress = "" + let vetWorkdays = "" + let vetSpecialtiesObject = null $.each(self.vets, function(i, vet) { if(selectedVetsId == vet.vetId) { - foundVet = true; - vetPhoneNumber = vet.phoneNumber; - vetEmailAddress = vet.email; - vetSpecialtiesObject = vet.specialties; - vetWorkdays = vet.workday; + foundVet = true + vetPhoneNumber = vet.phoneNumber + vetEmailAddress = vet.email + vetSpecialtiesObject = vet.specialties + vetWorkdays = vet.workday - return false; + return false } - }); + }) - let vetSpecialties = ""; + let vetSpecialties = "" $.each(vetSpecialtiesObject, function(i, specialty) { if(i < vetSpecialtiesObject.length - 1) { - vetSpecialties += specialty.name + ", "; + vetSpecialties += specialty.name + ", " } else { - vetSpecialties += specialty.name; + vetSpecialties += specialty.name } - }); + }) if(foundVet) { - $("#vetPhoneNumber").val(vetPhoneNumber); - $("#vetEmailAddress").val(vetEmailAddress); - $("#vetSpecialties").val(vetSpecialties); - $("#vetWorkdays").val(vetWorkdays).trigger("change"); + $("#vetPhoneNumber").val(vetPhoneNumber) + $("#vetEmailAddress").val(vetEmailAddress) + $("#vetSpecialties").val(vetSpecialties) + $("#vetWorkdays").val(vetWorkdays).trigger("change") } else { - $("#vetPhoneNumber").val(""); - $("#vetEmailAddress").val(""); - $("#vetSpecialties").val(""); - $("#vetWorkdays").val(""); + $("#vetPhoneNumber").val("") + $("#vetEmailAddress").val("") + $("#vetSpecialties").val("") + $("#vetWorkdays").val("") } } self.getPractitionerName = function (id){ - var practitionerName = ""; + var practitionerName = "" $.each(self.vets, function (i, vet){ if (vet.vetId == id){ - practitionerName = vet.firstName + " " + vet.lastName; - return false; + practitionerName = vet.firstName + " " + vet.lastName + return false } - }); - return practitionerName; - }; + }) + return practitionerName + } self.getVisitDate = function (id) { console.log("Getting date for visitId:", id); diff --git a/api-gateway/src/test/java/com/petclinic/bffapigateway/domainclientlayer/BillServiceClientIntegrationTest.java b/api-gateway/src/test/java/com/petclinic/bffapigateway/domainclientlayer/BillServiceClientIntegrationTest.java index 3e2f918030..79c342dd44 100644 --- a/api-gateway/src/test/java/com/petclinic/bffapigateway/domainclientlayer/BillServiceClientIntegrationTest.java +++ b/api-gateway/src/test/java/com/petclinic/bffapigateway/domainclientlayer/BillServiceClientIntegrationTest.java @@ -6,32 +6,21 @@ import com.petclinic.bffapigateway.dtos.Bills.BillRequestDTO; import com.petclinic.bffapigateway.dtos.Bills.BillResponseDTO; import com.petclinic.bffapigateway.dtos.Bills.BillStatus; -import com.petclinic.bffapigateway.dtos.Pets.PetResponseDTO; -import com.petclinic.bffapigateway.dtos.Visits.VisitDetails; -import com.petclinic.bffapigateway.dtos.Visits.VisitResponseDTO; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import org.junit.jupiter.api.*; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; - import java.io.IOException; -import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.Arrays; import java.util.List; import java.util.UUID; import java.util.function.Consumer; - import static org.junit.jupiter.api.Assertions.*; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; class BillServiceClientIntegrationTest { diff --git a/api-gateway/src/test/java/com/petclinic/bffapigateway/domainclientlayer/VisitsServiceClientIntegrationTest.java b/api-gateway/src/test/java/com/petclinic/bffapigateway/domainclientlayer/VisitsServiceClientIntegrationTest.java index 1d4727ebd2..8c41967c46 100755 --- a/api-gateway/src/test/java/com/petclinic/bffapigateway/domainclientlayer/VisitsServiceClientIntegrationTest.java +++ b/api-gateway/src/test/java/com/petclinic/bffapigateway/domainclientlayer/VisitsServiceClientIntegrationTest.java @@ -3,10 +3,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.petclinic.bffapigateway.dtos.Visits.*; -import com.petclinic.bffapigateway.dtos.Visits.VisitDetails; -import com.petclinic.bffapigateway.dtos.Visits.VisitRequestDTO; -import com.petclinic.bffapigateway.dtos.Visits.VisitResponseDTO; -import com.petclinic.bffapigateway.dtos.Visits.Visits; import com.petclinic.bffapigateway.utils.Security.Filters.JwtTokenFilter; import com.petclinic.bffapigateway.utils.Security.Filters.RoleFilter; import okhttp3.mockwebserver.MockResponse; @@ -25,16 +21,12 @@ import reactor.test.StepVerifier; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import java.time.LocalDateTime; - import java.io.IOException; import java.time.format.DateTimeFormatter; import java.util.Arrays; -import java.util.Collections; +import java.util.Objects; import java.util.UUID; -import java.util.function.Consumer; - import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.when; @WebFluxTest(value = VisitsServiceClient.class, excludeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {JwtTokenFilter.class, RoleFilter.class}), useDefaultFilters = false) @@ -47,7 +39,7 @@ class VisitsServiceClientIntegrationTest { private static final String PET_ID = "1"; - private static final String STATUS = "REQUESTED"; + private static final String STATUS = "UPCOMING"; @BeforeAll @@ -59,7 +51,7 @@ static void beforeAllSetUp() throws IOException{ } @BeforeEach void setUp() { - visitsServiceClient = new VisitsServiceClient("localhost", String.valueOf(server.getPort())); + visitsServiceClient = new VisitsServiceClient("localhost", "" + server.getPort()); } @AfterAll static void tearDown() throws IOException { @@ -79,6 +71,22 @@ void getAllVisits() throws JsonProcessingException { .expectNext(visitResponseDTO2) .verifyComplete(); } + @Test + void getAllVisits_400Error()throws IllegalArgumentException{ + server.enqueue(new MockResponse().setResponseCode(400).addHeader("Content-Type", "application/json")); + Flux visitResponseDTOFlux = visitsServiceClient.getAllVisits(); + StepVerifier.create(visitResponseDTOFlux) + .expectErrorMatches(throwable -> throwable instanceof IllegalArgumentException && Objects.equals(throwable.getMessage(), "Something went wrong and we got a 400 error")) + .verify(); + } + @Test + void getAllVisits_500Error()throws IllegalArgumentException{ + server.enqueue(new MockResponse().setResponseCode(500).addHeader("Content-Type", "application/json")); + Flux visitResponseDTOFlux = visitsServiceClient.getAllVisits(); + StepVerifier.create(visitResponseDTOFlux) + .expectErrorMatches(throwable -> throwable instanceof IllegalArgumentException && Objects.equals(throwable.getMessage(), "Something went wrong and we got a 500 error")) + .verify(); + } @Test void getVisitsForStatus() throws JsonProcessingException{ @@ -121,38 +129,45 @@ void createVisitForPet_Valid() throws JsonProcessingException { // Assert StepVerifier.create(resultMono) - .expectNextMatches(visitResponse -> visitResponse.getVisitId().equals(visitResponseDTO.getVisitId())) + .expectNextMatches(visitResponse -> Objects.equals(visitResponse.getVisitId(), visitResponseDTO.getVisitId())) .verifyComplete(); } - - - @Test - void getVisitsForPets_withAvailableVisitsService() { - prepareResponse(response -> response - .setHeader("Content-Type", "application/json") - .setBody("{\"items\":[{\"visitId\":\"773fa7b2-e04e-47b8-98e7-4adf7cfaaeee\"," + - "\"date\":\"2018-11-15\",\"description\":\"test visit\",\"petId\":\"1\"}]}")); - - Mono visits = visitsServiceClient.getVisitsForPets(Collections.singletonList(1)); + void getVisitsForPet() throws Exception { + VisitResponseDTO visitResponseDTO = new VisitResponseDTO("773fa7b2-e04e-47b8-98e7-4adf7cfaaeee", LocalDateTime.parse("2024-11-25 13:45", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")), "this is a dummy description", "2", "2", Status.UPCOMING); + server.enqueue(new MockResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(objectMapper.writeValueAsString(visitResponseDTO)).addHeader("Content-Type", "application/json")); - assertVisitDescriptionEquals(visits.block(), PET_ID,"test visit"); + Flux visits = visitsServiceClient.getVisitsForPet("2"); + StepVerifier.create(visits) + .expectNext(visitResponseDTO) + .verifyComplete(); } - @Test - void getVisitsForPet() { - prepareResponse(response -> response - .setHeader("Content-Type", "application/json") - .setBody("{\"visitId\":\"773fa7b2-e04e-47b8-98e7-4adf7cfaaeee\"," + - "\"date\":\"2018-11-15\",\"description\":\"test visit\",\"petId\":1," + - " \"practitionerId\":1}")); - - Flux visits = visitsServiceClient.getVisitsForPet("1"); + void getVisitById() throws Exception { + VisitResponseDTO visitResponseDTO = new VisitResponseDTO("773fa7b2-e04e-47b8-98e7-4adf7cfaaeee", LocalDateTime.parse("2024-11-25 13:45", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")), "this is a dummy description", "2", "2", Status.UPCOMING); + server.enqueue(new MockResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(objectMapper.writeValueAsString(visitResponseDTO)).addHeader("Content-Type", "application/json")); - assertVisitDescriptionEq(visits.blockFirst(), PET_ID,"test visit"); + Mono visitResponseDTOMono = visitsServiceClient.getVisitByVisitId("773fa7b2-e04e-47b8-98e7-4adf7cfaaeee"); + StepVerifier.create(visitResponseDTOMono) + .expectNextMatches(returnedVisitResponseDTO1 -> Objects.equals(returnedVisitResponseDTO1, visitResponseDTO)) + .verifyComplete(); } +// @Test +// void getVisitsForPets_withAvailableVisitsService() { +// prepareResponse(response -> response +// .setHeader("Content-Type", "application/json") +// .setBody("{\"items\":[{\"visitId\":\"773fa7b2-e04e-47b8-98e7-4adf7cfaaeee\"," + +// "\"date\":\"2018-11-15\",\"description\":\"test visit\",\"petId\":\"1\"}]}")); +// +// Mono visits = visitsServiceClient.getVisitsForPets(Collections.singletonList(1)); +// +// assertVisitDescriptionEquals(visits.block(), PET_ID,"test visit"); +// } + + +/* @Test void shouldDeleteVisitsForPet() throws JsonProcessingException { final VisitDetails visit = VisitDetails.builder() @@ -173,6 +188,7 @@ void shouldDeleteVisitsForPet() throws JsonProcessingException { assertNull(empty.block()); } + */ @Test void shouldCreateVisitForPet() throws JsonProcessingException { // Given @@ -215,35 +231,7 @@ void shouldCreateVisitForPet() throws JsonProcessingException { assertEquals(expectedVisitResponse.getStatus(), actualVisitResponse.getStatus()); } -// @Test -// void shouldCreateVisitsForPet() throws JsonProcessingException { -// -// final VisitDetails visit = VisitDetails.builder() -// .visitId(UUID.randomUUID().toString()) -// .petId(21) -// .practitionerId(2) -// .date("2021-12-7") -// .description("Cat is sick") -// .status(false) -// .build(); -// -// visitsServiceClient.createVisitForPet(visit); -// final String body = objectMapper.writeValueAsString(objectMapper.convertValue(visit, VisitDetails.class)); -// prepareResponse(response -> response -// .setHeader("Content-Type", "application/json") -// .setBody(body)); -// -// final VisitDetails petVisit = visitsServiceClient.getVisitsForPet(21).blockFirst(); -// -// assertEquals(visit.getVisitId(), petVisit.getVisitId()); -// assertEquals(visit.getPetId(), petVisit.getPetId()); -// assertEquals(visit.getPractitionerId(), petVisit.getPractitionerId()); -// assertEquals(visit.getDate(), petVisit.getDate()); -// assertEquals(visit.getDescription(), petVisit.getDescription()); -// assertEquals(visit.getStatus(), petVisit.getStatus()); -// } - - @Test +/* @Test void shouldUpdateVisitsForPet() throws JsonProcessingException { final VisitDetails visit = VisitDetails.builder() @@ -283,7 +271,7 @@ void shouldUpdateVisitsForPet() throws JsonProcessingException { assertEquals(visit2.getDescription(), petVisit.getDescription()); assertEquals(visit2.getStatus(), petVisit.getStatus()); - } + }*/ /* @Test @@ -419,12 +407,11 @@ void shouldUpdateStatusForVisitByVisitId() throws JsonProcessingException { // assertEquals(visit.getDescription(), scheduledVisits.getDescription()); // assertEquals(visit.getStatus(), scheduledVisits.getStatus()); // } - +/* private void assertVisitDescriptionEq(VisitDetails visits, String petId, String description) { assertEquals("773fa7b2-e04e-47b8-98e7-4adf7cfaaeee", visits.getVisitId()); assertEquals(description, visits.getDescription()); } - private void assertVisitDescriptionEquals(Visits visits, String petId, String description) { assertEquals(1, visits.getItems().size()); assertNotNull(visits.getItems().get(0)); @@ -436,19 +423,7 @@ private void prepareResponse(Consumer consumer) { MockResponse response = new MockResponse(); consumer.accept(response); server.enqueue(response); - } - - @Test - void getVisitById() throws Exception { - VisitResponseDTO visitResponseDTO = new VisitResponseDTO("773fa7b2-e04e-47b8-98e7-4adf7cfaaeee", LocalDateTime.parse("2024-11-25 13:45", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")), "this is a dummy description", "2", "2", Status.UPCOMING); - server.enqueue(new MockResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .setBody(objectMapper.writeValueAsString(visitResponseDTO)).addHeader("Content-Type", "application/json")); - - Mono visitResponseDTOMono = visitsServiceClient.getVisitByVisitId("773fa7b2-e04e-47b8-98e7-4adf7cfaaeee"); - StepVerifier.create(visitResponseDTOMono) - .expectNextMatches(returnedVisitResponseDTO1 -> returnedVisitResponseDTO1.getVisitId().equals("773fa7b2-e04e-47b8-98e7-4adf7cfaaeee")) - .verifyComplete(); - } + }*/ @Test void deleteAllCancelledVisits_shouldSucceed() { diff --git a/api-gateway/src/test/java/com/petclinic/bffapigateway/presentationlayer/ApiGatewayControllerTest.java b/api-gateway/src/test/java/com/petclinic/bffapigateway/presentationlayer/ApiGatewayControllerTest.java index be7774b926..572c0b2ffe 100755 --- a/api-gateway/src/test/java/com/petclinic/bffapigateway/presentationlayer/ApiGatewayControllerTest.java +++ b/api-gateway/src/test/java/com/petclinic/bffapigateway/presentationlayer/ApiGatewayControllerTest.java @@ -1,7 +1,5 @@ package com.petclinic.bffapigateway.presentationlayer; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import com.petclinic.bffapigateway.config.GlobalExceptionHandler; import com.petclinic.bffapigateway.domainclientlayer.*; import com.petclinic.bffapigateway.dtos.Auth.*; @@ -20,8 +18,6 @@ import com.petclinic.bffapigateway.dtos.Pets.PetTypeResponseDTO; import com.petclinic.bffapigateway.dtos.Vets.*; import com.petclinic.bffapigateway.dtos.Visits.Status; -import com.petclinic.bffapigateway.dtos.Visits.VisitDetails; -import com.petclinic.bffapigateway.dtos.Visits.VisitRequestDTO; import com.petclinic.bffapigateway.dtos.Visits.VisitResponseDTO; import com.petclinic.bffapigateway.exceptions.ExistingVetNotFoundException; import com.petclinic.bffapigateway.exceptions.GenericHttpException; @@ -41,11 +37,9 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; - import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.Resource; import org.springframework.http.*; - import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.test.context.ContextConfiguration; @@ -54,25 +48,16 @@ import org.springframework.web.reactive.function.BodyInserters; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; - -import org.springframework.web.server.ResponseStatusException; import org.webjars.NotFoundException; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; - -import javax.print.attribute.standard.Media; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import static org.assertj.core.api.Assertions.assertThat; -import static com.petclinic.bffapigateway.dtos.Inventory.InventoryType.internal; - import static org.junit.Assert.*; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; import static org.springframework.http.HttpStatus.*; @@ -94,7 +79,6 @@ classes = {JwtTokenFilter.class,RoleFilter.class}),useDefaultFilters = false) @AutoConfigureWebTestClient class ApiGatewayControllerTest { - @Autowired private ObjectMapper objectMapper; @Autowired private WebTestClient client; @MockBean private CustomersServiceClient customersServiceClient; @MockBean private VisitsServiceClient visitsServiceClient; @@ -2025,81 +2009,83 @@ void shouldCreateAVisitWithOwnerInfo(){ .jsonPath("$.description").isEqualTo("Charle's Richard cat has a paw infection.") .jsonPath("$.status").isEqualTo(false) .jsonPath("$.practitionerId").isEqualTo(1); - }*/ + } -// @Test -// void shouldDeleteAVisit() { -// VisitDetails visit = new VisitDetails(); -// OwnerDetails owner = new OwnerDetails(); -// owner.setId(1); -// visit.setVisitId(UUID.randomUUID().toString()); -// visit.setPetId(1); -// visit.setDate("2021-12-12"); -// visit.setDescription("Charle's Richard cat has a paw infection."); -// visit.setStatus(false); -// visit.setPractitionerId(1); -// -// -// when(visitsServiceClient.createVisitForPet(visit)) -// .thenReturn(Mono.just(visit)); -// -// client.post() -// .uri("/api/gateway/visit/owners/{ownerId}/pets/{petId}/visits", owner.getId(), visit.getPetId()) -// .body(Mono.just(visit), VisitDetails.class) -// .accept(MediaType.APPLICATION_JSON) -// .exchange() -// .expectStatus().isOk() -// .expectHeader().contentType(MediaType.APPLICATION_JSON) -// .expectBody() -// .jsonPath("$.visitId").isEqualTo(visit.getVisitId()) -// .jsonPath("$.petId").isEqualTo(1) -// .jsonPath("$.date").isEqualTo("2021-12-12") -// .jsonPath("$.description").isEqualTo("Charle's Richard cat has a paw infection.") -// .jsonPath("$.status").isEqualTo(false) -// .jsonPath("$.practitionerId").isEqualTo(1); -// -// client.delete() -// .uri("/api/gateway/visits/{visitId}", visit.getVisitId()) -// .accept(MediaType.APPLICATION_JSON) -// .exchange() -// .expectStatus() -// .isOk() -// .expectBody(); -// -// assertEquals(null, visitsServiceClient.getVisitsForPet(visit.getPetId())); -// } + @Test + void shouldDeleteAVisit() { + VisitDetails visit = new VisitDetails(); + OwnerDetails owner = new OwnerDetails(); + owner.setId(1); + visit.setVisitId(UUID.randomUUID().toString()); + visit.setPetId(1); + visit.setDate("2021-12-12"); + visit.setDescription("Charle's Richard cat has a paw infection."); + visit.setStatus(false); + visit.setPractitionerId(1); - /* @Test - void shouldUpdateAVisitsById() { - VisitDetails visitDetailsToUpdate = VisitDetails.builder() - .visitDate(LocalDateTime.parse("2022-11-25 13:45", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))) - .description("Charle's Richard dog has a paw infection.") - .petId("1") - .visitId("1") - .practitionerId(2) - .status(Status.UPCOMING) - .build(); - when(visitsServiceClient.updateVisitForPet(visitDetailsToUpdate)) - .thenReturn(Mono.just(visitDetailsToUpdate)); + when(visitsServiceClient.createVisitForPet(visit)) + .thenReturn(Mono.just(visit)); - client.put() - .uri("/api/gateway/owners/**//*pets/{petId}/visits/{visitId}", "1", "1") - /* .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.APPLICATION_JSON) - .bodyValue(visitDetailsToUpdate) + client.post() + .uri("/api/gateway/visit/owners/{ownerId}/pets/{petId}/visits", owner.getId(), visit.getPetId()) + .body(Mono.just(visit), VisitDetails.class) + .accept(MediaType.APPLICATION_JSON) .exchange() .expectStatus().isOk() .expectHeader().contentType(MediaType.APPLICATION_JSON) .expectBody() - .jsonPath("$.visitId").isEqualTo("1") - .jsonPath("$.petId").isEqualTo("1") - .jsonPath("$.description").isEqualTo("Charle's Richard dog has a paw infection.") - .jsonPath("$.status").isEqualTo(Status.UPCOMING.toString()) - .jsonPath("$.practitionerId").isEqualTo(2); - Mockito.verify(visitsServiceClient,times(1)).updateVisitForPet(visitDetailsToUpdate); - }*/ + .jsonPath("$.visitId").isEqualTo(visit.getVisitId()) + .jsonPath("$.petId").isEqualTo(1) + .jsonPath("$.date").isEqualTo("2021-12-12") + .jsonPath("$.description").isEqualTo("Charle's Richard cat has a paw infection.") + .jsonPath("$.status").isEqualTo(false) + .jsonPath("$.practitionerId").isEqualTo(1); + + client.delete() + .uri("/api/gateway/visits/{visitId}", visit.getVisitId()) + .accept(MediaType.APPLICATION_JSON) + .exchange() + .expectStatus() + .isOk() + .expectBody(); + + assertEquals(null, visitsServiceClient.getVisitsForPet(visit.getPetId())); + } + + */ + +// @Test +// void shouldUpdateAVisitsById() { +// VisitDetails visitDetailsToUpdate = VisitDetails.builder() +// .visitDate(LocalDateTime.parse("2022-11-25 13:45", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))) +// .description("Charle's Richard dog has a paw infection.") +// .petId("1") +// .visitId("1") +// .practitionerId(2) +// .status(Status.UPCOMING) +// .build(); +// +// when(visitsServiceClient.updateVisitForPet(visitDetailsToUpdate)) +// .thenReturn(Mono.just(visitDetailsToUpdate)); +// +// client.put() +// .uri("/api/gateway/owners/*/pets/{petId}/visits/{visitId}", "1", "1") +// .accept(MediaType.APPLICATION_JSON) +// .contentType(MediaType.APPLICATION_JSON) +// .bodyValue(visitDetailsToUpdate) +// .exchange() +// .expectStatus().isOk() +// .expectHeader().contentType(MediaType.APPLICATION_JSON) +// .expectBody() +// .jsonPath("$.visitId").isEqualTo("1") +// .jsonPath("$.petId").isEqualTo("1") +// .jsonPath("$.description").isEqualTo("Charle's Richard dog has a paw infection.") +// .jsonPath("$.status").isEqualTo(Status.UPCOMING.toString()) +// .jsonPath("$.practitionerId").isEqualTo(2); +// Mockito.verify(visitsServiceClient,times(1)).updateVisitForPet(visitDetailsToUpdate); +// } @Test void ShouldUpdateStatusForVisitByVisitId(){ @@ -2131,7 +2117,6 @@ void ShouldUpdateStatusForVisitByVisitId(){ Mockito.verify(visitsServiceClient, times(1)) .updateStatusForVisitByVisitId(anyString(), anyString()); } - @Test void shouldGetAllVisits() { VisitResponseDTO visitResponseDTO = new VisitResponseDTO("73b5c112-5703-4fb7-b7bc-ac8186811ae1", LocalDateTime.parse("2022-11-25 13:45", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")), "this is a dummy description", "2", "2", Status.UPCOMING); @@ -2149,14 +2134,41 @@ void shouldGetAllVisits() { Mockito.verify(visitsServiceClient,times(1)).getAllVisits(); } @Test + void getVisitsByOwnerId_shouldReturnOk(){ + //arrange + final String ownerId = "ownerId"; + PetResponseDTO petResponseDTO1 = PetResponseDTO.builder().petId("petId1").build(); + PetResponseDTO petResponseDTO2 = PetResponseDTO.builder().petId("petId2").build(); + VisitResponseDTO visitResponseDTO1 = VisitResponseDTO.builder().visitId("visitId1").petId("petId1").build(); + VisitResponseDTO visitResponseDTO2 = VisitResponseDTO.builder().visitId("visitId2").petId("petId1").build(); + VisitResponseDTO visitResponseDTO3 = VisitResponseDTO.builder().visitId("visitId3").petId("petId2").build(); + VisitResponseDTO visitResponseDTO4 = VisitResponseDTO.builder().visitId("visitId4").petId("petId2").build(); + VisitResponseDTO visitResponseDTO5 = VisitResponseDTO.builder().visitId("visitId5").petId("petId1").build(); + + Mockito.when(customersServiceClient.getPetsByOwnerId(anyString())).thenReturn(Flux.just(petResponseDTO1, petResponseDTO2)); + + Mockito.when(visitsServiceClient.getVisitsForPet(petResponseDTO1.getPetId())).thenReturn(Flux.just(visitResponseDTO1, visitResponseDTO2, visitResponseDTO5)); + Mockito.when(visitsServiceClient.getVisitsForPet(petResponseDTO2.getPetId())).thenReturn(Flux.just(visitResponseDTO3, visitResponseDTO4)); + + //act and assert + client + .get() + .uri("/api/gateway/visits/owners/{ownerId}",ownerId) + .accept(MediaType.valueOf(MediaType.TEXT_EVENT_STREAM_VALUE)) + .acceptCharset(StandardCharsets.UTF_8) + .exchange() + .expectStatus() + .isOk() + .expectHeader().valueEquals("Content-Type", "text/event-stream;charset=UTF-8") + .expectBodyList(VisitResponseDTO.class) + .value((list) -> { + Assertions.assertNotNull(list); + Assertions.assertEquals(5, list.size()); + }); + } + @Test void shouldGetAVisit() { - VisitDetails visit = new VisitDetails(); - visit.setVisitId(UUID.randomUUID().toString()); - visit.setPetId("1"); - visit.setVisitDate(LocalDateTime.parse("2022-11-25 13:45", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))); - visit.setDescription("Charle's Richard cat has a paw infection."); - visit.setStatus(Status.UPCOMING); - visit.setPractitionerId(1); + VisitResponseDTO visit = new VisitResponseDTO("73b5c112-5703-4fb7-b7bc-ac8186811ae1", LocalDateTime.parse("2022-11-25 13:45", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")), "this is a dummy description", "2", "2", Status.UPCOMING); when(visitsServiceClient.getVisitsForPet(visit.getPetId())) .thenReturn(Flux.just(visit)); @@ -2168,7 +2180,7 @@ void shouldGetAVisit() { .exchange() .expectStatus().isOk() .expectHeader().valueEquals("Content-Type", "text/event-stream;charset=UTF-8") - .expectBodyList(VisitDetails.class) + .expectBodyList(VisitResponseDTO.class) .value((list)-> { assertEquals(list.size(),1); assertEquals(list.get(0).getVisitId(),visit.getVisitId()); @@ -2179,7 +2191,7 @@ void shouldGetAVisit() { assertEquals(list.get(0).getPractitionerId(),visit.getPractitionerId()); }); } - +/* @Test void shouldGetAVisitForPractitioner(){ VisitDetails visit = new VisitDetails(); @@ -2211,7 +2223,7 @@ void shouldGetAVisitForPractitioner(){ assertEquals(list.get(0).getPractitionerId(),visit.getPractitionerId()); }); } - /* + @Test void shouldGetAVisitByPractitionerIdAndMonth(){ VisitDetails visit = new VisitDetails(); @@ -2273,7 +2285,7 @@ void getSingleVisit_Valid() { // .jsonPath("$.message").isEqualTo(expectedErrorMessage); // } - @Test + /*@Test @DisplayName("Should get the previous visits of a pet") void shouldGetPreviousVisitsOfAPet() { VisitDetails visit1 = new VisitDetails(); @@ -2320,9 +2332,9 @@ void shouldGetPreviousVisitsOfAPet() { assertEquals(list.get(1).getStatus(), visit2.getStatus()); assertEquals(list.get(1).getPractitionerId(), visit2.getPractitionerId()); }); - } + }*/ - @Test + /*@Test @DisplayName("Should return a bad request if the petId is invalid when trying to get the previous visits of a pet") void shouldGetBadRequestWhenInvalidPetIdToRetrievePreviousVisits() { final String invalidPetId = "-1"; @@ -2387,7 +2399,26 @@ void shouldGetScheduledVisitsOfAPet() { assertEquals(list.get(1).getStatus(), visit2.getStatus()); assertEquals(list.get(1).getPractitionerId(), visit2.getPractitionerId()); }); + + @Test + @DisplayName("Should return a bad request if the petId is invalid when trying to get the scheduled visits of a pet") + void shouldGetBadRequestWhenInvalidPetIdToRetrieveScheduledVisits() { + final String invalidPetId = "0"; + final String expectedErrorMessage = "error message"; + + when(visitsServiceClient.getScheduledVisitsForPet(invalidPetId)) + .thenThrow(new GenericHttpException(expectedErrorMessage, BAD_REQUEST)); + + client.get() + .uri("/api/gateway/visits/scheduled/{petId}", invalidPetId) + .exchange() + .expectStatus().isBadRequest() + .expectBody() + .jsonPath("$.statusCode").isEqualTo(BAD_REQUEST.value()) + .jsonPath("$.timestamp").exists() + .jsonPath("$.message").isEqualTo(expectedErrorMessage); } + }*/ @Test public void updateOwner_shouldSucceed() { @@ -2425,24 +2456,7 @@ public void updateOwner_shouldSucceed() { } - @Test - @DisplayName("Should return a bad request if the petId is invalid when trying to get the scheduled visits of a pet") - void shouldGetBadRequestWhenInvalidPetIdToRetrieveScheduledVisits() { - final String invalidPetId = "0"; - final String expectedErrorMessage = "error message"; - - when(visitsServiceClient.getScheduledVisitsForPet(invalidPetId)) - .thenThrow(new GenericHttpException(expectedErrorMessage, BAD_REQUEST)); - client.get() - .uri("/api/gateway/visits/scheduled/{petId}", invalidPetId) - .exchange() - .expectStatus().isBadRequest() - .expectBody() - .jsonPath("$.statusCode").isEqualTo(BAD_REQUEST.value()) - .jsonPath("$.timestamp").exists() - .jsonPath("$.message").isEqualTo(expectedErrorMessage); - } @Test void deleteVisitById_visitId_shouldSucceed(){ @@ -3175,10 +3189,3 @@ private EducationResponseDTO buildEducation(){ } } - - - - - - - diff --git a/api-gateway/src/test/java/com/petclinic/bffapigateway/presentationlayer/BFFApiGatewayControllerFilterIntegrationTest.java b/api-gateway/src/test/java/com/petclinic/bffapigateway/presentationlayer/BFFApiGatewayControllerFilterIntegrationTest.java index 98a7663dfc..760fd89285 100644 --- a/api-gateway/src/test/java/com/petclinic/bffapigateway/presentationlayer/BFFApiGatewayControllerFilterIntegrationTest.java +++ b/api-gateway/src/test/java/com/petclinic/bffapigateway/presentationlayer/BFFApiGatewayControllerFilterIntegrationTest.java @@ -3,9 +3,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.petclinic.bffapigateway.config.GlobalExceptionHandler; import com.petclinic.bffapigateway.domainclientlayer.*; -import com.petclinic.bffapigateway.dtos.Auth.Login; import com.petclinic.bffapigateway.dtos.Auth.TokenResponseDTO; -import com.petclinic.bffapigateway.dtos.CustomerDTOs.OwnerResponseDTO; import com.petclinic.bffapigateway.utils.Security.Filters.IsUserFilter; import com.petclinic.bffapigateway.utils.Security.Filters.JwtTokenFilter; import com.petclinic.bffapigateway.utils.Security.Filters.JwtTokenUtil; @@ -13,31 +11,21 @@ import org.junit.jupiter.api.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; -import org.reactivestreams.Publisher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.FilterType; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; - import java.nio.charset.StandardCharsets; import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; -import static reactor.core.publisher.Mono.when; @RunWith(SpringRunner.class) @ContextConfiguration(classes = { @@ -57,23 +45,15 @@ @AutoConfigureWebTestClient class BFFApiGatewayControllerFilterIntegrationTest { - @Autowired - private ObjectMapper objectMapper; + @Autowired private ObjectMapper objectMapper; @Autowired private WebTestClient client; - @MockBean - private CustomersServiceClient customersServiceClient; - @MockBean - private VisitsServiceClient visitsServiceClient; - @MockBean - private VetsServiceClient vetsServiceClient; - @MockBean - private AuthServiceClient authServiceClient; - @MockBean - private BillServiceClient billServiceClient; - @MockBean - private InventoryServiceClient inventoryServiceClient; - @MockBean - private JwtTokenUtil jwtTokenUtil; + @MockBean private CustomersServiceClient customersServiceClient; + @MockBean private VisitsServiceClient visitsServiceClient; + @MockBean private VetsServiceClient vetsServiceClient; + @MockBean private AuthServiceClient authServiceClient; + @MockBean private BillServiceClient billServiceClient; + @MockBean private InventoryServiceClient inventoryServiceClient; + @MockBean private JwtTokenUtil jwtTokenUtil; @Test void testGetAllCustomers_ShouldReturnUnauthorized() { @@ -89,8 +69,6 @@ void testGetAllCustomers_ShouldReturnUnauthorized() { @Test void testGetAllCustomers_ShouldReturnOk() { - - Mockito.when(jwtTokenUtil.getTokenFromRequest(any(ServerWebExchange.class))) .thenReturn("valid.token.signed"); @@ -119,7 +97,6 @@ void testGetAllCustomers_ShouldReturnOk() { //@Test void getUserByIdWithWrongAccount_ShouldFail(){ - Mockito.when(jwtTokenUtil.getTokenFromRequest(any(ServerWebExchange.class))) .thenReturn("valid.token.signed"); @@ -134,9 +111,6 @@ void getUserByIdWithWrongAccount_ShouldFail(){ Mockito.when(authServiceClient.validateToken(anyString())) .thenReturn(validationResponse); - - - client.get() .uri("/api/gateway/owners/UUID") .accept(MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE)) @@ -145,6 +119,4 @@ void getUserByIdWithWrongAccount_ShouldFail(){ .exchange() .expectStatus().isUnauthorized(); } - - } \ No newline at end of file diff --git a/customers-service-reactive/src/main/java/com/petclinic/customersservice/presentationlayer/PetController.java b/customers-service-reactive/src/main/java/com/petclinic/customersservice/presentationlayer/PetController.java index cfcebd34c3..0637a0aaba 100644 --- a/customers-service-reactive/src/main/java/com/petclinic/customersservice/presentationlayer/PetController.java +++ b/customers-service-reactive/src/main/java/com/petclinic/customersservice/presentationlayer/PetController.java @@ -24,7 +24,7 @@ public Mono getPetDTOByPetId(@PathVariable String petId) { .map(EntityDTOUtil::toPetResponseDTO); } - @GetMapping("/owner/{ownerId}") + @GetMapping("/owner/{ownerId}/pets") public Flux getPetsByOwnerId(@PathVariable String ownerId) { return petService.getPetsByOwnerId(ownerId); } diff --git a/docker-compose.yml b/docker-compose.yml index 0786f92305..0faf5ed615 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -313,7 +313,7 @@ services: - ME_CONFIG_MONGODB_SERVER=mongo2 - ME_CONFIG_MONGODB_ENABLE_ADMIN=true ports: - - 9001:8082 + - 9001:8081 depends_on: - mongo2 - visits-service-new diff --git a/visits-service-new/src/main/java/com/petclinic/visits/visitsservicenew/BusinessLayer/VisitServiceImpl.java b/visits-service-new/src/main/java/com/petclinic/visits/visitsservicenew/BusinessLayer/VisitServiceImpl.java index 0b92c36db5..59d7b75650 100644 --- a/visits-service-new/src/main/java/com/petclinic/visits/visitsservicenew/BusinessLayer/VisitServiceImpl.java +++ b/visits-service-new/src/main/java/com/petclinic/visits/visitsservicenew/BusinessLayer/VisitServiceImpl.java @@ -2,7 +2,6 @@ import com.petclinic.visits.visitsservicenew.DataLayer.Status; import com.petclinic.visits.visitsservicenew.DataLayer.VisitRepo; - import com.petclinic.visits.visitsservicenew.DomainClientLayer.PetResponseDTO; import com.petclinic.visits.visitsservicenew.DomainClientLayer.PetsClient; import com.petclinic.visits.visitsservicenew.DomainClientLayer.VetDTO; @@ -16,7 +15,6 @@ import org.springframework.stereotype.Service; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; - import java.time.LocalDateTime; diff --git a/visits-service-new/src/main/java/com/petclinic/visits/visitsservicenew/DataLayer/DataSetupService.java b/visits-service-new/src/main/java/com/petclinic/visits/visitsservicenew/DataLayer/DataSetupService.java index 55bae12383..2a8d024dc6 100644 --- a/visits-service-new/src/main/java/com/petclinic/visits/visitsservicenew/DataLayer/DataSetupService.java +++ b/visits-service-new/src/main/java/com/petclinic/visits/visitsservicenew/DataLayer/DataSetupService.java @@ -1,6 +1,5 @@ package com.petclinic.visits.visitsservicenew.DataLayer; - import lombok.RequiredArgsConstructor; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Service; @@ -9,21 +8,21 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; - @Service @RequiredArgsConstructor public class DataSetupService implements CommandLineRunner { private final VisitRepo visitRepo; @Override public void run(String... args) throws Exception { - Visit visit1 = buildVisit("visitId1", "2022-11-24 13:00", "this is a dummy description", "2", "69f852ca-625b-11ee-8c99-0242ac120002", Status.COMPLETED); Visit visit2 = buildVisit("visitId2", "2022-03-01 13:00", "Dog Needs Meds", "1", "69f85766-625b-11ee-8c99-0242ac120002", Status.COMPLETED); Visit visit3 = buildVisit("visitId3", "2020-07-19 13:00","Dog Needs Surgery After Meds", "1", "69f85bda-625b-11ee-8c99-0242ac120002", Status.COMPLETED); Visit visit4 = buildVisit("visitId4", "2022-12-24 13:00", "Dog Needs Physio-Therapy", "1", "69f85d2e-625b-11ee-8c99-0242ac120002", Status.UPCOMING); Visit visit5 = buildVisit("visitId5", "2023-12-24 13:00", "Cat Needs Check-Up", "4", "ac9adeb8-625b-11ee-8c99-0242ac120002", Status.UPCOMING); + Visit visit6 = buildVisit("visitId6", "2023-12-05 15:00", "Animal Needs Operation", "3", "ac9adeb8-625b-11ee-8c99-0242ac120002", Status.UPCOMING); + Visit visit7 = buildVisit("visitId7", "2022-05-20 09:00", "Cat Needs Check-Up", "4", "ac9adeb8-625b-11ee-8c99-0242ac120002", Status.CONFIRMED); - Flux.just(visit1, visit2, visit3, visit4, visit5).flatMap(x -> visitRepo.insert(Mono.just(x)).log(x.toString())).subscribe(); + Flux.just(visit1, visit2, visit3, visit4, visit5, visit6, visit7).flatMap(x -> visitRepo.insert(Mono.just(x)).log(x.toString())).subscribe(); } private Visit buildVisit(String visitId, String visitDate, String description, String petId, String practitionerId, Status status){ diff --git a/visits-service-new/src/main/java/com/petclinic/visits/visitsservicenew/PresentationLayer/VisitController.java b/visits-service-new/src/main/java/com/petclinic/visits/visitsservicenew/PresentationLayer/VisitController.java index 675af570a5..99a3933dbc 100644 --- a/visits-service-new/src/main/java/com/petclinic/visits/visitsservicenew/PresentationLayer/VisitController.java +++ b/visits-service-new/src/main/java/com/petclinic/visits/visitsservicenew/PresentationLayer/VisitController.java @@ -21,6 +21,10 @@ public Flux getAllVisits(){ return visitService.getAllVisits(); } + @GetMapping(value="practitioner/{practitionerId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + public Flux getVisitsByPractitionerId(@PathVariable String practitionerId) { + return visitService.getVisitsForPractitioner(practitionerId); + } @GetMapping(value="/pets/{petId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux getVisitsForPet(@PathVariable String petId){ return visitService.getVisitsForPet(petId); @@ -37,10 +41,7 @@ public Mono> getVisitByVisitId(@PathVariable St .map(ResponseEntity::ok) .defaultIfEmpty(ResponseEntity.notFound().build()); } - @GetMapping(value="practitioner/{practitionerId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - public Flux getVisitByPractitionerId(@PathVariable String practitionerId) { - return visitService.getVisitsForPractitioner(practitionerId); - } + /* @GetMapping(value="practitioner/{practitionerId}/{month}", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux getVisitsByPractitionerIdAndMonth(@PathVariable int practitionerId, @PathVariable int month){ diff --git a/visits-service-new/src/test/java/com/petclinic/visits/visitsservicenew/DomainClientLayer/PetsClientUnitTest.java b/visits-service-new/src/test/java/com/petclinic/visits/visitsservicenew/DomainClientLayer/PetsClientUnitTest.java index 78132f7676..8f43493ffc 100644 --- a/visits-service-new/src/test/java/com/petclinic/visits/visitsservicenew/DomainClientLayer/PetsClientUnitTest.java +++ b/visits-service-new/src/test/java/com/petclinic/visits/visitsservicenew/DomainClientLayer/PetsClientUnitTest.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.petclinic.visits.visitsservicenew.Exceptions.NotFoundException; -import com.petclinic.visits.visitsservicenew.PresentationLayer.VisitResponseDTO; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import org.junit.jupiter.api.AfterAll; @@ -16,14 +15,9 @@ import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; - import java.io.IOException; -import java.time.LocalDateTime; import java.util.Date; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.when; - @WebFluxTest(PetsClient.class) class PetsClientUnitTest { @@ -33,7 +27,7 @@ class PetsClientUnitTest { @MockBean private PetsClient petsClient; - private ObjectMapper objectMapper = new ObjectMapper(); + private final ObjectMapper objectMapper = new ObjectMapper(); private static MockWebServer mockBackEnd; diff --git a/visits-service-new/src/test/java/com/petclinic/visits/visitsservicenew/DomainClientLayer/VetsClientUnitTest.java b/visits-service-new/src/test/java/com/petclinic/visits/visitsservicenew/DomainClientLayer/VetsClientUnitTest.java index 65f6e75c5e..435b3dabe1 100644 --- a/visits-service-new/src/test/java/com/petclinic/visits/visitsservicenew/DomainClientLayer/VetsClientUnitTest.java +++ b/visits-service-new/src/test/java/com/petclinic/visits/visitsservicenew/DomainClientLayer/VetsClientUnitTest.java @@ -15,11 +15,8 @@ import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; - import java.io.IOException; -import static org.junit.jupiter.api.Assertions.*; - @WebFluxTest(VetsClient.class) class VetsClientUnitTest { @@ -29,7 +26,7 @@ class VetsClientUnitTest { @MockBean private VetsClient vetsClient; - private ObjectMapper objectMapper = new ObjectMapper(); + private final ObjectMapper objectMapper = new ObjectMapper(); private static MockWebServer mockBackEnd; diff --git a/visits-service-new/src/test/java/com/petclinic/visits/visitsservicenew/PresentationLayer/VisitControllerUnitTest.java b/visits-service-new/src/test/java/com/petclinic/visits/visitsservicenew/PresentationLayer/VisitControllerUnitTest.java index 0f0ee8f914..f9e50d24ec 100644 --- a/visits-service-new/src/test/java/com/petclinic/visits/visitsservicenew/PresentationLayer/VisitControllerUnitTest.java +++ b/visits-service-new/src/test/java/com/petclinic/visits/visitsservicenew/PresentationLayer/VisitControllerUnitTest.java @@ -34,19 +34,19 @@ class VisitControllerUnitTest { private WebTestClient webTestClient; - @MockBean - private VetsClient vetsClient; - - @MockBean - private PetsClient petsClient; +// @MockBean +// private VetsClient vetsClient; +// +// @MockBean +// private PetsClient petsClient; - String uuidVisit1 = UUID.randomUUID().toString(); +// String uuidVisit1 = UUID.randomUUID().toString(); String uuidVet = UUID.randomUUID().toString(); - String uuidPet = UUID.randomUUID().toString(); - String uuidPhoto = UUID.randomUUID().toString(); - String uuidOwner = UUID.randomUUID().toString(); +// String uuidPet = UUID.randomUUID().toString(); +// String uuidPhoto = UUID.randomUUID().toString(); +// String uuidOwner = UUID.randomUUID().toString(); - private final String STATUS = "COMPLETED"; +// private final String STATUS = "COMPLETED"; Set set= new HashSet<>(); @@ -65,7 +65,7 @@ class VisitControllerUnitTest { .active(true) .specialties(set) .build(); - +/* Date currentDate =new Date(); PetResponseDTO petResponseDTO = PetResponseDTO.builder() .petTypeId(uuidPet) @@ -74,8 +74,9 @@ class VisitControllerUnitTest { .photoId(uuidPhoto) .ownerId(uuidOwner) .build(); - Visit visit1 = buildVisit(uuidVisit1,"this is a dummy description",vet.getVetId()); + */ + private final VisitResponseDTO visitResponseDTO = buildVisitResponseDto(); private final VisitRequestDTO visitRequestDTO = buildVisitRequestDTO(vet.getVetId()); private final String Visit_UUID_OK = visitResponseDTO.getVisitId(); @@ -123,7 +124,7 @@ void getVisitByPractitionerId(){ when(visitService.getVisitsForPractitioner(anyString())).thenReturn(Flux.just(visitResponseDTO)); webTestClient.get() - .uri("/visits/practitioner/" + Practitioner_Id_OK) + .uri("/visits/practitioner/{practitionerId}", Practitioner_Id_OK) .accept(MediaType.TEXT_EVENT_STREAM) .exchange() .expectStatus().isOk() @@ -262,7 +263,7 @@ void deleteAllCancelledVisits_shouldSucceed(){ } @Test - void deleteAllCancelledVisits_shouldThrowRuntimeException() { + void deleteAllCancelledVisits_shouldThrowRuntimeException() throws RuntimeException { // Arrange Mockito.when(visitService.deleteAllCancelledVisits()) .thenReturn(Mono.error(new RuntimeException("Failed to delete cancelled visits"))); @@ -277,7 +278,7 @@ void deleteAllCancelledVisits_shouldThrowRuntimeException() { Mockito.verify(visitService, times(1)).deleteAllCancelledVisits(); } - +/* private Visit buildVisit(String uuid,String description, String vetId){ return Visit.builder() .visitId(uuid) @@ -288,7 +289,7 @@ private Visit buildVisit(String uuid,String description, String vetId){ .status(Status.UPCOMING) .build(); } - +*/ private VisitResponseDTO buildVisitResponseDto(){ return VisitResponseDTO.builder() From 5d0f254cbab2abb3cf080ab2132d44f7e0dad155 Mon Sep 17 00:00:00 2001 From: Timbro <57909059+AlexeiTimbro@users.noreply.github.com> Date: Mon, 9 Oct 2023 14:33:36 -0400 Subject: [PATCH 2/2] Feat/invt cpc 979 add sale price for clients in product list (#533) JIRA: link to jira ticket https://champlainsaintlambert.atlassian.net/browse/CPC-979 What is the ticket about and why are we making this change? This ticket is about adding an important field to our product list called the sale price. The sale price is important because previously we only had the normal price for a product that the pet clinic bought it for and we were missing the price we will be selling it at. We are making those changes because it's crucial information about a product. What are the various changes and what other modules do those changes affect? - The front end has been changed to accommodate the new field in the product list. - A couple of things about the backend have been changed: 1. RequestDTO 2. ResponseDTO 3. Product Entity 4. ServiceImpl methods - All the Testing that was using the DTOs, the Product entity and the serviceImpl has been changed to accommodate the new changes. - API Gateway testing fully accommodated with the new changes. ## Before and After UI (Required for UI-impacting PRs) Before: ![image](https://github.com/cgerard321/champlain_petclinic/assets/57909059/943b10f7-9cb9-47ad-9fec-a88bc58bf2ab) After: ![image](https://github.com/cgerard321/champlain_petclinic/assets/57909059/4e42e629-f35b-4b46-8834-6a32636929d2) --- .../dtos/Inventory/ProductRequestDTO.java | 2 ++ .../dtos/Inventory/ProductResponseDTO.java | 2 ++ .../inventory-list.template.html | 1 - .../inventory-product-list.template.html | 7 ++++ .../product-form/product-form.controller.js | 3 +- .../product-form/product-form.template.html | 6 ++++ .../product-update-form.controller.js | 3 +- .../product-update-form.template.html | 6 ++++ ...InventoryServiceClientIntegrationTest.java | 7 ++-- .../ApiGatewayControllerTest.java | 10 ++++-- .../ProductInventoryServiceImpl.java | 9 ++--- .../datalayer/Product/Product.java | 3 +- .../presentationlayer/ProductRequestDTO.java | 1 + .../presentationlayer/ProductResponseDTO.java | 1 + .../utils/DataBaseLoaderService.java | 9 +++-- .../ProductInventoryServiceUnitTest.java | 33 +++++++++++++++---- .../Product/ProductRepositoryTest.java | 17 +++++----- .../InventoryControllerIntegrationTest.java | 7 ++++ .../InventoryControllerUnitTest.java | 12 +++++++ 19 files changed, 110 insertions(+), 29 deletions(-) diff --git a/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Inventory/ProductRequestDTO.java b/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Inventory/ProductRequestDTO.java index a8e401d580..d1fb738735 100644 --- a/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Inventory/ProductRequestDTO.java +++ b/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Inventory/ProductRequestDTO.java @@ -14,4 +14,6 @@ public class ProductRequestDTO { private String productDescription; private Double productPrice; private Integer productQuantity; + private Double productSalePrice; + } diff --git a/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Inventory/ProductResponseDTO.java b/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Inventory/ProductResponseDTO.java index 762eb64a3e..02e7ccc9d8 100644 --- a/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Inventory/ProductResponseDTO.java +++ b/api-gateway/src/main/java/com/petclinic/bffapigateway/dtos/Inventory/ProductResponseDTO.java @@ -17,4 +17,6 @@ public class ProductResponseDTO { private String productDescription; private Double productPrice; private Integer productQuantity; + private Double productSalePrice; + } diff --git a/api-gateway/src/main/resources/static/scripts/inventory-list/inventory-list.template.html b/api-gateway/src/main/resources/static/scripts/inventory-list/inventory-list.template.html index c9a027f130..8fbe712eb3 100644 --- a/api-gateway/src/main/resources/static/scripts/inventory-list/inventory-list.template.html +++ b/api-gateway/src/main/resources/static/scripts/inventory-list/inventory-list.template.html @@ -65,7 +65,6 @@

Inventory

Delete - diff --git a/api-gateway/src/main/resources/static/scripts/inventory-product-list/inventory-product-list.template.html b/api-gateway/src/main/resources/static/scripts/inventory-product-list/inventory-product-list.template.html index 467039bee3..5c2bde164c 100644 --- a/api-gateway/src/main/resources/static/scripts/inventory-product-list/inventory-product-list.template.html +++ b/api-gateway/src/main/resources/static/scripts/inventory-product-list/inventory-product-list.template.html @@ -16,6 +16,7 @@

Inventory Products

Quantity Price Description + Sale Price @@ -34,6 +35,11 @@

Inventory Products

+ + + + + @@ -50,6 +56,7 @@

Inventory Products

{{product.productQuantity}} {{product.productPrice}} {{product.productDescription}} + {{product.productSalePrice}} diff --git a/api-gateway/src/main/resources/static/scripts/product-form/product-form.controller.js b/api-gateway/src/main/resources/static/scripts/product-form/product-form.controller.js index d081129e90..49c0f70b66 100644 --- a/api-gateway/src/main/resources/static/scripts/product-form/product-form.controller.js +++ b/api-gateway/src/main/resources/static/scripts/product-form/product-form.controller.js @@ -10,7 +10,8 @@ angular.module('productForm') productName: self.product.productName, productDescription: self.product.productDescription, productPrice: self.product.productPrice, - productQuantity: self.product.productQuantity + productQuantity: self.product.productQuantity, + productSalePrice: self.product.productSalePrice } var inventoryId = InventoryService.getInventoryId(); console.log("InventoryId: " + inventoryId); diff --git a/api-gateway/src/main/resources/static/scripts/product-form/product-form.template.html b/api-gateway/src/main/resources/static/scripts/product-form/product-form.template.html index 081eed2433..18e1adaf8b 100644 --- a/api-gateway/src/main/resources/static/scripts/product-form/product-form.template.html +++ b/api-gateway/src/main/resources/static/scripts/product-form/product-form.template.html @@ -26,6 +26,12 @@

New Product

+
+
+ + +
+
diff --git a/api-gateway/src/main/resources/static/scripts/product-update-form/product-update-form.controller.js b/api-gateway/src/main/resources/static/scripts/product-update-form/product-update-form.controller.js index 3b2b6f0779..d0a391c21e 100644 --- a/api-gateway/src/main/resources/static/scripts/product-update-form/product-update-form.controller.js +++ b/api-gateway/src/main/resources/static/scripts/product-update-form/product-update-form.controller.js @@ -24,7 +24,8 @@ angular.module('productUpdateForm') productName: self.product.productName, productDescription: self.product.productDescription, productPrice: self.product.productPrice, - productQuantity: self.product.productQuantity + productQuantity: self.product.productQuantity, + productSalePrice: self.product.productSalePrice } $http.put('/api/gateway/inventory/' + inventoryId + '/products/' + productId, data) diff --git a/api-gateway/src/main/resources/static/scripts/product-update-form/product-update-form.template.html b/api-gateway/src/main/resources/static/scripts/product-update-form/product-update-form.template.html index db4a0c56ba..c1633a1957 100644 --- a/api-gateway/src/main/resources/static/scripts/product-update-form/product-update-form.template.html +++ b/api-gateway/src/main/resources/static/scripts/product-update-form/product-update-form.template.html @@ -26,6 +26,12 @@

Update Product

+
+
+ + +
+
diff --git a/api-gateway/src/test/java/com/petclinic/bffapigateway/domainclientlayer/InventoryServiceClientIntegrationTest.java b/api-gateway/src/test/java/com/petclinic/bffapigateway/domainclientlayer/InventoryServiceClientIntegrationTest.java index b64c90fd21..96839fdacf 100644 --- a/api-gateway/src/test/java/com/petclinic/bffapigateway/domainclientlayer/InventoryServiceClientIntegrationTest.java +++ b/api-gateway/src/test/java/com/petclinic/bffapigateway/domainclientlayer/InventoryServiceClientIntegrationTest.java @@ -54,7 +54,9 @@ void getProductsInInventoryByInventoryIdAndProductsField() throws JsonProcessing "name", "desc", 10.00, - 2 + 2, + 15.99 + ); mockWebServer.enqueue(new MockResponse() @@ -79,7 +81,8 @@ void addProductsToInventory() throws JsonProcessingException { "name", "desc", 10.00, - 2 + 2, + 15.99 ); mockWebServer.enqueue(new MockResponse() diff --git a/api-gateway/src/test/java/com/petclinic/bffapigateway/presentationlayer/ApiGatewayControllerTest.java b/api-gateway/src/test/java/com/petclinic/bffapigateway/presentationlayer/ApiGatewayControllerTest.java index 572c0b2ffe..b48ad01dd9 100755 --- a/api-gateway/src/test/java/com/petclinic/bffapigateway/presentationlayer/ApiGatewayControllerTest.java +++ b/api-gateway/src/test/java/com/petclinic/bffapigateway/presentationlayer/ApiGatewayControllerTest.java @@ -2813,10 +2813,11 @@ void deleteAllProductInventory_shouldSucceed() { } //inventory tests + @Test void testUpdateProductInInventory() { // Create a sample ProductRequestDTO - ProductRequestDTO requestDTO = new ProductRequestDTO("Sample Product", "Sample Description", 10.0, 100); + ProductRequestDTO requestDTO = new ProductRequestDTO("Sample Product", "Sample Description", 10.0, 100, 15.99); // Define the expected response ProductResponseDTO expectedResponse = ProductResponseDTO.builder() @@ -2874,7 +2875,7 @@ public void deleteProductById_insideInventory(){ @DisplayName("Given valid inventoryId and valid productRequest Post and return productResponse") void testAddProductToInventory_ShouldSucceed() { // Create a sample ProductRequestDTO - ProductRequestDTO requestDTO = new ProductRequestDTO("Sample Product", "Sample Description", 10.0, 100); + ProductRequestDTO requestDTO = new ProductRequestDTO("Sample Product", "Sample Description", 10.0, 100, 15.99); // Define the expected response ProductResponseDTO expectedResponse = ProductResponseDTO.builder() @@ -2885,6 +2886,7 @@ void testAddProductToInventory_ShouldSucceed() { .productDescription(requestDTO.getProductDescription()) .productPrice(requestDTO.getProductPrice()) .productQuantity(requestDTO.getProductQuantity()) + .productSalePrice(requestDTO.getProductSalePrice()) .build(); // Mock the behavior of the inventoryServiceClient @@ -2906,6 +2908,7 @@ void testAddProductToInventory_ShouldSucceed() { assertEquals(requestDTO.getProductDescription(), dto.getProductDescription()); assertEquals(requestDTO.getProductPrice(), dto.getProductPrice()); assertEquals(requestDTO.getProductQuantity(), dto.getProductQuantity()); + assertEquals(requestDTO.getProductSalePrice(), dto.getProductSalePrice()); }); // Verify that the inventoryServiceClient method was called @@ -2917,7 +2920,7 @@ void testAddProductToInventory_ShouldSucceed() { @DisplayName("Given invalid inventoryId and valid productRequest Post and return NotFoundException") void testAddProductToInventory_InvalidInventoryId_ShouldReturnNotFoundException() { // Create a sample ProductRequestDTO - ProductRequestDTO requestDTO = new ProductRequestDTO("Sample Product", "Sample Description", 10.0, 100); + ProductRequestDTO requestDTO = new ProductRequestDTO("Sample Product", "Sample Description", 10.0, 100,15.99); // Define the expected response ProductResponseDTO expectedResponse = ProductResponseDTO.builder() @@ -2960,6 +2963,7 @@ private ProductResponseDTO buildProductDTO(){ .productDescription("Sedative Medication") .productPrice(100.00) .productQuantity(10) + .productSalePrice(15.99) .build(); } diff --git a/inventory-service/src/main/java/com/petclinic/inventoryservice/businesslayer/ProductInventoryServiceImpl.java b/inventory-service/src/main/java/com/petclinic/inventoryservice/businesslayer/ProductInventoryServiceImpl.java index f5bdf0ef67..3ea908b267 100644 --- a/inventory-service/src/main/java/com/petclinic/inventoryservice/businesslayer/ProductInventoryServiceImpl.java +++ b/inventory-service/src/main/java/com/petclinic/inventoryservice/businesslayer/ProductInventoryServiceImpl.java @@ -33,9 +33,9 @@ public Mono addProductToInventory(Mono pr .flatMap(requestDTO -> inventoryRepository.findInventoryByInventoryId(inventoryId) .switchIfEmpty(Mono.error(new NotFoundException("Inventory not found with id: " + inventoryId))) .flatMap(inventory -> { - if (requestDTO.getProductName() == null || requestDTO.getProductPrice() == null || requestDTO.getProductQuantity() == null) { + if (requestDTO.getProductName() == null || requestDTO.getProductPrice() == null || requestDTO.getProductQuantity() == null || requestDTO.getProductSalePrice() == null) { return Mono.error(new InvalidInputException("Product must have an inventory id, product name, product price, and product quantity.")); - } else if (requestDTO.getProductPrice() < 0 || requestDTO.getProductQuantity() < 0) { + } else if (requestDTO.getProductPrice() < 0 || requestDTO.getProductQuantity() < 0 || requestDTO.getProductSalePrice() < 0) { return Mono.error(new InvalidInputException("Product price and quantity must be greater than 0.")); } else { Product product = EntityDTOUtil.toProductEntity(requestDTO); @@ -94,9 +94,9 @@ public Mono updateProductInInventory(Mono .flatMap(requestDTO -> inventoryRepository.findInventoryByInventoryId(inventoryId) .switchIfEmpty(Mono.error(new NotFoundException("Inventory not found with id: " + inventoryId))) .flatMap(inventory -> { - if (requestDTO.getProductName() == null || requestDTO.getProductPrice() == null || requestDTO.getProductQuantity() == null) { + if (requestDTO.getProductName() == null || requestDTO.getProductPrice() == null || requestDTO.getProductQuantity() == null || requestDTO.getProductSalePrice() == null) { return Mono.error(new InvalidInputException("Product must have an inventory id, product name, product price, and product quantity.")); - } else if (requestDTO.getProductPrice() < 0 || requestDTO.getProductQuantity() < 0) { + } else if (requestDTO.getProductPrice() < 0 || requestDTO.getProductQuantity() < 0 || requestDTO.getProductSalePrice() < 0) { return Mono.error(new InvalidInputException("Product price and quantity must be greater than 0.")); } else { return productRepository.findProductByProductId(productId) @@ -105,6 +105,7 @@ public Mono updateProductInInventory(Mono existingProduct.setProductDescription(requestDTO.getProductDescription()); existingProduct.setProductPrice(requestDTO.getProductPrice()); existingProduct.setProductQuantity(requestDTO.getProductQuantity()); + existingProduct.setProductSalePrice(requestDTO.getProductSalePrice()); return productRepository.save(existingProduct) .map(EntityDTOUtil::toProductResponseDTO); diff --git a/inventory-service/src/main/java/com/petclinic/inventoryservice/datalayer/Product/Product.java b/inventory-service/src/main/java/com/petclinic/inventoryservice/datalayer/Product/Product.java index 1d616560cd..4fff0148df 100644 --- a/inventory-service/src/main/java/com/petclinic/inventoryservice/datalayer/Product/Product.java +++ b/inventory-service/src/main/java/com/petclinic/inventoryservice/datalayer/Product/Product.java @@ -15,6 +15,7 @@ public class Product { private String inventoryId; private String productName; private String productDescription; - private Double productPrice; private Integer productQuantity; + private Double productPrice; + private Double productSalePrice; } diff --git a/inventory-service/src/main/java/com/petclinic/inventoryservice/presentationlayer/ProductRequestDTO.java b/inventory-service/src/main/java/com/petclinic/inventoryservice/presentationlayer/ProductRequestDTO.java index 0f7088d677..1b516a3f59 100644 --- a/inventory-service/src/main/java/com/petclinic/inventoryservice/presentationlayer/ProductRequestDTO.java +++ b/inventory-service/src/main/java/com/petclinic/inventoryservice/presentationlayer/ProductRequestDTO.java @@ -12,4 +12,5 @@ public class ProductRequestDTO { private String productDescription; private Double productPrice; private Integer productQuantity; + private Double productSalePrice; } diff --git a/inventory-service/src/main/java/com/petclinic/inventoryservice/presentationlayer/ProductResponseDTO.java b/inventory-service/src/main/java/com/petclinic/inventoryservice/presentationlayer/ProductResponseDTO.java index efae06c866..38f9fbd2b4 100644 --- a/inventory-service/src/main/java/com/petclinic/inventoryservice/presentationlayer/ProductResponseDTO.java +++ b/inventory-service/src/main/java/com/petclinic/inventoryservice/presentationlayer/ProductResponseDTO.java @@ -15,4 +15,5 @@ public class ProductResponseDTO { private String productDescription; private Double productPrice; private Integer productQuantity; + private Double productSalePrice; } diff --git a/inventory-service/src/main/java/com/petclinic/inventoryservice/utils/DataBaseLoaderService.java b/inventory-service/src/main/java/com/petclinic/inventoryservice/utils/DataBaseLoaderService.java index aca902081d..a932c89c80 100644 --- a/inventory-service/src/main/java/com/petclinic/inventoryservice/utils/DataBaseLoaderService.java +++ b/inventory-service/src/main/java/com/petclinic/inventoryservice/utils/DataBaseLoaderService.java @@ -105,18 +105,23 @@ public void run(String... args) throws Exception { .productName("Benzodiazepines") .productId(UUID.randomUUID().toString()) .productPrice(100.00) - .inventoryId("1") + .inventoryId(inventory1.getInventoryId()) .productQuantity(10) .productDescription("Drugs for sleep") + .productSalePrice(10.00) .build(); + + Product product2 = Product.builder() .productName("Trazodone") .productId(UUID.randomUUID().toString()) .productPrice(150.00) - .inventoryId("1") + .inventoryId(inventory1.getInventoryId()) .productQuantity(10) .productDescription("Drugs for anxiety/stress") + .productSalePrice(10.00) .build(); + Flux.just(product1, product2) .flatMap(productRepository::insert) .log() diff --git a/inventory-service/src/test/java/com/petclinic/inventoryservice/businesslayer/ProductInventoryServiceUnitTest.java b/inventory-service/src/test/java/com/petclinic/inventoryservice/businesslayer/ProductInventoryServiceUnitTest.java index 794e0e4dae..14bdf096f9 100644 --- a/inventory-service/src/test/java/com/petclinic/inventoryservice/businesslayer/ProductInventoryServiceUnitTest.java +++ b/inventory-service/src/test/java/com/petclinic/inventoryservice/businesslayer/ProductInventoryServiceUnitTest.java @@ -46,6 +46,7 @@ class ProductInventoryServiceUnitTest { .productDescription("Sedative Medication") .productPrice(100.00) .productQuantity(10) + .productSalePrice(15.99) .build(); Product product = Product.builder() .productId("12345") @@ -54,6 +55,7 @@ class ProductInventoryServiceUnitTest { .productDescription("Sedative Medication") .productPrice(100.00) .productQuantity(10) + .productSalePrice(15.99) .build(); InventoryType inventoryType = InventoryType.builder() .id("1") @@ -68,11 +70,12 @@ class ProductInventoryServiceUnitTest { .inventoryDescription("Medication for procedures") .build(); ProductRequestDTO productRequestDTO = ProductRequestDTO.builder() - .productName("Benzodiazepines") - .productDescription("Sedative Medication") - .productPrice(100.00) - .productQuantity(10) - .build(); + .productName("Benzodiazepines") + .productDescription("Sedative Medication") + .productPrice(100.00) + .productQuantity(10) + .productSalePrice(15.99) + .build(); @Test @@ -82,6 +85,7 @@ void getAllProductsByInventoryId_andProductName_andProductPrice_andProductQuanti Double productPrice = 100.00; Integer productQuantity = 10; + when(productRepository .findAllProductsByInventoryIdAndProductNameAndProductPriceAndProductQuantity( inventoryId, @@ -385,6 +389,8 @@ void updateProductInInventory_ValidRequest_ShouldUpdateAndReturnProduct() { .productName("Updated Product Name") .productPrice(99.99) .productQuantity(20) + .productDescription("Description") + .productSalePrice(15.99) .build(); Inventory inventory = Inventory.builder() @@ -401,6 +407,7 @@ void updateProductInInventory_ValidRequest_ShouldUpdateAndReturnProduct() { .productName("Original Product Name") .productPrice(50.0) .productQuantity(10) + .productSalePrice(10.10) .build(); Product updatedProduct = Product.builder() @@ -410,6 +417,8 @@ void updateProductInInventory_ValidRequest_ShouldUpdateAndReturnProduct() { .productName("Updated Product Name") .productPrice(99.99) .productQuantity(20) + .productDescription("Description") + .productSalePrice(10.10) .build(); when(inventoryRepository.findInventoryByInventoryId(anyString())).thenReturn(Mono.just(inventory)); @@ -427,6 +436,8 @@ void updateProductInInventory_ValidRequest_ShouldUpdateAndReturnProduct() { assertEquals("Updated Product Name", responseDTO.getProductName()); assertEquals(99.99, responseDTO.getProductPrice()); assertEquals(20, responseDTO.getProductQuantity()); + assertEquals("Description",responseDTO.getProductDescription()); + assertEquals(10.10, responseDTO.getProductSalePrice()); return true; }) .verifyComplete(); @@ -440,8 +451,10 @@ void updateProductInInventory_ProductNotFound_ShouldThrowNotFoundException() { ProductRequestDTO productRequestDTO = ProductRequestDTO.builder() .productName("Updated Product Name") - .productPrice(99.99) + .productPrice(-99.99) .productQuantity(20) + .productDescription("Description") + .productSalePrice(10.10) .build(); when(inventoryRepository.findInventoryByInventoryId(anyString())).thenReturn(Mono.empty()); @@ -466,6 +479,8 @@ void updateProductInInventory_InvalidInput_ShouldThrowInvalidInputException() { .productName("Updated Product Name") .productPrice(-99.99) .productQuantity(20) + .productDescription("Description") + .productSalePrice(10.10) .build(); Inventory inventory = Inventory.builder() @@ -497,6 +512,8 @@ void updateProductInInventory_InvalidInputNull_ShouldThrowInvalidInputException( .productName(null) .productPrice(null) .productQuantity(null) + .productDescription(null) + .productSalePrice(null) .build(); // Mock the behavior of the inventoryRepository to return a dummy Inventory object @@ -596,6 +613,8 @@ void addProductToInventory_ShouldSucceed(){ assertEquals(productResponseDTO.getProductName(), productRequestDTO.getProductName()); assertEquals(productResponseDTO.getProductPrice(), productRequestDTO.getProductPrice()); assertEquals(productResponseDTO.getProductQuantity(), productRequestDTO.getProductQuantity()); + assertEquals(productResponseDTO.getProductDescription(),productRequestDTO.getProductDescription()); + assertEquals(productResponseDTO.getProductSalePrice(), productRequestDTO.getProductSalePrice()); }) .verifyComplete(); Mockito.verify(productRepository, Mockito.times(1)).save(any(Product.class)); @@ -627,6 +646,8 @@ void addProductToInventory_WithInvalidProductRequest_ShouldThrowInvalidInputExce .productDescription("Sedative Medication") .productPrice(-100.00) .productQuantity(10) + .productDescription("Description") + .productSalePrice(10.10) .build(); Mockito.when(inventoryRepository.findInventoryByInventoryId(inventoryId)) .thenReturn(Mono.just(inventory)); diff --git a/inventory-service/src/test/java/com/petclinic/inventoryservice/datalayer/Product/ProductRepositoryTest.java b/inventory-service/src/test/java/com/petclinic/inventoryservice/datalayer/Product/ProductRepositoryTest.java index 56d56310de..b89107e599 100644 --- a/inventory-service/src/test/java/com/petclinic/inventoryservice/datalayer/Product/ProductRepositoryTest.java +++ b/inventory-service/src/test/java/com/petclinic/inventoryservice/datalayer/Product/ProductRepositoryTest.java @@ -20,8 +20,8 @@ class ProductRepositoryTest { @BeforeEach public void setupDB() { - product1 = buildProduct("inventoryId_4", "productId_1", "Desc", "name", 100.00, 10); - product2 = buildProduct("inventoryId_4", "productId_2", "Desc", "name", 100.00, 10); + product1 = buildProduct("inventoryId_4", "productId_1", "Desc", "name", 100.00, 10, 15.99); + product2 = buildProduct("inventoryId_4", "productId_2", "Desc", "name", 100.00, 10, 15.99); Publisher setup1 = productRepository.deleteAll() .then(productRepository.save(product1)); @@ -41,7 +41,7 @@ public void setupDB() { @Test public void ShouldSaveSingleProduct(){ //arrange - Product newProduct = buildProduct("inventoryId_1", "sku_1", "product_1", "product_1", 10.0, 10); + Product newProduct = buildProduct("inventoryId_1", "sku_1", "product_1", "product_1", 10.0, 10, 15.99); Publisher setup = productRepository.save(newProduct); //Act and Assert StepVerifier @@ -53,7 +53,7 @@ public void ShouldSaveSingleProduct(){ @Test public void ShouldDeleteSingleProduct_byProductId(){ //arrange - Product product1 = buildProduct("inventoryId_1", "sku_1", "product_1", "product_1", 10.0, 10); + Product product1 = buildProduct("inventoryId_1", "sku_1", "product_1", "product_1", 10.0, 10, 15.99); Publisher setup = productRepository.deleteByProductId(product1.getProductId()); //act and assert StepVerifier @@ -109,8 +109,8 @@ public void shouldGetTwoProductsByInventoryIdAndProductName(){ @Test public void ShouldDeleteAllProducts() { // Arrange - Product product1 = buildProduct("inventoryId_1", "sku_1", "product_1", "product_1_desc", 10.0, 10); - Product product2 = buildProduct("inventoryId_2", "sku_2", "product_2", "product_2_desc", 15.0, 5); + Product product1 = buildProduct("inventoryId_1", "sku_1", "product_1", "product_1_desc", 10.0, 10, 15.99); + Product product2 = buildProduct("inventoryId_2", "sku_2", "product_2", "product_2_desc", 15.0, 5, 15.99); productRepository.save(product1).block(); productRepository.save(product2).block(); @@ -134,7 +134,7 @@ public void ShouldDeleteAllProducts() { public void testFindProductByProductId() { // Arrange String productIdToFind = "productId"; - Product newProduct = buildProduct("inventoryId_1", productIdToFind, "product_1", "product_1", 10.0, 10); + Product newProduct = buildProduct("inventoryId_1", productIdToFind, "product_1", "product_1", 10.0, 10, 15.99); productRepository.save(newProduct).block(); @@ -151,7 +151,7 @@ public void testFindProductByProductId() { .verifyComplete(); } - private Product buildProduct(String inventoryId, String productId, String productName, String productDescription, Double productPrice, Integer productQuantity) { + private Product buildProduct(String inventoryId, String productId, String productName, String productDescription, Double productPrice, Integer productQuantity, Double productSalePrice) { return Product.builder() .inventoryId(inventoryId) .productId(productId) @@ -159,6 +159,7 @@ private Product buildProduct(String inventoryId, String productId, String produc .productDescription(productDescription) .productPrice(productPrice) .productQuantity(productQuantity) + .productSalePrice(productSalePrice) .build(); } diff --git a/inventory-service/src/test/java/com/petclinic/inventoryservice/presentationlayer/InventoryControllerIntegrationTest.java b/inventory-service/src/test/java/com/petclinic/inventoryservice/presentationlayer/InventoryControllerIntegrationTest.java index 7aa55be974..f43b388977 100644 --- a/inventory-service/src/test/java/com/petclinic/inventoryservice/presentationlayer/InventoryControllerIntegrationTest.java +++ b/inventory-service/src/test/java/com/petclinic/inventoryservice/presentationlayer/InventoryControllerIntegrationTest.java @@ -77,6 +77,7 @@ public void dbSetup() { .productDescription("Sedative Medication") .productPrice(100.00) .productQuantity(10) + .productSalePrice(15.99) .build())) .thenMany(productRepository.save(Product.builder() .inventoryId("1") @@ -85,6 +86,7 @@ public void dbSetup() { .productDescription("Sedative Medication") .productPrice(100.00) .productQuantity(10) + .productSalePrice(15.99) .build())); StepVerifier @@ -99,6 +101,7 @@ public void dbSetup() { .productDescription("Sedative Medication") .productPrice(100.00) .productQuantity(10) + .productSalePrice(15.99) .build()); StepVerifier .create(productPublisher1) @@ -114,6 +117,7 @@ void addProductToInventory_WithInvalidInventoryIdAndValidBody_ShouldThrowNotFoun .productDescription("Sedative Medication") .productPrice(100.00) .productQuantity(10) + .productSalePrice(15.99) .build(); // Act and assert webTestClient @@ -512,6 +516,7 @@ void testUpdateProductInInventory_WithNonExistentProductId_ShouldReturnNotFound( .productDescription("Updated Sedative Medication") .productPrice(150.00) .productQuantity(20) + .productSalePrice(15.99) .build(); // Act and Assert @@ -585,6 +590,7 @@ void addProductToInventory_WithValidInventoryIdAndValidBody_ShouldSucceed() { .productDescription("Sedative Medication") .productPrice(100.00) .productQuantity(10) + .productSalePrice(15.99) .build(); // Act and assert webTestClient @@ -602,6 +608,7 @@ void addProductToInventory_WithValidInventoryIdAndValidBody_ShouldSucceed() { assertEquals(productRequestDTO.getProductDescription(), dto.getProductDescription()); assertEquals(productRequestDTO.getProductPrice(), dto.getProductPrice()); assertEquals(productRequestDTO.getProductQuantity(), dto.getProductQuantity()); + assertEquals(productRequestDTO.getProductSalePrice(), dto.getProductSalePrice()); }); } diff --git a/inventory-service/src/test/java/com/petclinic/inventoryservice/presentationlayer/InventoryControllerUnitTest.java b/inventory-service/src/test/java/com/petclinic/inventoryservice/presentationlayer/InventoryControllerUnitTest.java index 19e8123307..4a38c14da5 100644 --- a/inventory-service/src/test/java/com/petclinic/inventoryservice/presentationlayer/InventoryControllerUnitTest.java +++ b/inventory-service/src/test/java/com/petclinic/inventoryservice/presentationlayer/InventoryControllerUnitTest.java @@ -38,6 +38,7 @@ class InventoryControllerUnitTest { .productDescription("Sedative Medication") .productPrice(100.00) .productQuantity(10) + .productSalePrice(15.99) .build(); List productResponseDTOS = Arrays.asList( ProductResponseDTO.builder() @@ -48,6 +49,7 @@ class InventoryControllerUnitTest { .productDescription("Sedative Medication") .productPrice(100.00) .productQuantity(10) + .productSalePrice(15.99) .build(), ProductResponseDTO.builder() .id("1") @@ -57,6 +59,7 @@ class InventoryControllerUnitTest { .productDescription("Sedative Medication") .productPrice(100.00) .productQuantity(10) + .productSalePrice(15.99) .build() ); List typesDTOS = Arrays.asList( @@ -427,6 +430,7 @@ void updateProductInInventory_ValidRequest_ShouldSucceed() { .productDescription("Updated Description") .productPrice(200.00) .productQuantity(20) + .productSalePrice(15.99) .build(); ProductResponseDTO responseDTO = ProductResponseDTO.builder() @@ -436,6 +440,7 @@ void updateProductInInventory_ValidRequest_ShouldSucceed() { .productDescription("Updated Description") .productPrice(200.00) .productQuantity(20) + .productSalePrice(15.99) .build(); when(productInventoryService.updateProductInInventory(any(), eq(inventoryId), eq(productId))) @@ -458,6 +463,7 @@ void updateProductInInventory_ValidRequest_ShouldSucceed() { assertEquals(responseDTO.getProductDescription(), dto.getProductDescription()); assertEquals(responseDTO.getProductPrice(), dto.getProductPrice()); assertEquals(responseDTO.getProductQuantity(), dto.getProductQuantity()); + assertEquals(responseDTO.getProductSalePrice(), dto.getProductSalePrice()); }); verify(productInventoryService, times(1)) @@ -474,6 +480,7 @@ void updateProductInInventory_ProductNotFound_ShouldReturnNotFound() { .productDescription("Updated Description") .productPrice(200.00) .productQuantity(20) + .productSalePrice(15.99) .build(); when(productInventoryService.updateProductInInventory(any(), eq(inventoryId), eq(productId))) @@ -507,6 +514,7 @@ void updateProductInInventory_InvalidInput_ShouldReturnBadRequest() { .productDescription("Updated Description") .productPrice(200.00) .productQuantity(20) + .productSalePrice(15.99) .build(); when(productInventoryService.updateProductInInventory(any(), eq(inventoryId), eq(productId))) @@ -584,6 +592,7 @@ void addProductToInventory_ShouldCallServiceAddProduct() { .productDescription("New Description") .productPrice(200.00) .productQuantity(20) + .productSalePrice(15.99) .build(); ProductResponseDTO responseDTO = ProductResponseDTO.builder() @@ -593,6 +602,7 @@ void addProductToInventory_ShouldCallServiceAddProduct() { .productDescription("New Description") .productPrice(200.00) .productQuantity(20) + .productSalePrice(15.99) .build(); when(productInventoryService.addProductToInventory(any(), eq(inventoryId))) @@ -615,6 +625,7 @@ void addProductToInventory_ShouldCallServiceAddProduct() { assertEquals(responseDTO.getProductDescription(), dto.getProductDescription()); assertEquals(responseDTO.getProductPrice(), dto.getProductPrice()); assertEquals(responseDTO.getProductQuantity(), dto.getProductQuantity()); + assertEquals(responseDTO.getProductSalePrice(), dto.getProductSalePrice()); }); verify(productInventoryService, times(1)) @@ -630,6 +641,7 @@ void addProductToInventory_InvalidInput_ShouldReturnBadRequest() { .productDescription("New Description") .productPrice(200.00) .productQuantity(20) + .productSalePrice(15.99) .build(); when(productInventoryService.addProductToInventory(any(), eq(inventoryId)))