From 09e07869ac8cea1d08bd62802d5e0dad97827b87 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Thu, 29 Apr 2021 07:11:20 +0100 Subject: [PATCH 001/129] Update Spring Boot to 2.4.5 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index cdd6c4522..a9fc1eca4 100644 --- a/pom.xml +++ b/pom.xml @@ -5,12 +5,12 @@ 4.0.0 org.springframework.samples spring-petclinic - 2.4.2 + 2.4.5 org.springframework.boot spring-boot-starter-parent - 2.4.2 + 2.4.5 petclinic From 39d60e276108e605d6aebc89058de9081af61a09 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 27 Apr 2021 10:46:28 +0800 Subject: [PATCH 002/129] Configure an inline m2e lifecycle mapping for wro4j-maven-plugin Signed-off-by: Jinbo Wang --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index a9fc1eca4..6737a2391 100644 --- a/pom.xml +++ b/pom.xml @@ -241,6 +241,7 @@ generate-resources + run From af1857cda1753022ee887cc6a4b204095649da14 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Tue, 4 May 2021 11:37:26 +0100 Subject: [PATCH 003/129] Add javaformat plugin to Eclipse workaround --- pom.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pom.xml b/pom.xml index 6737a2391..628c88461 100644 --- a/pom.xml +++ b/pom.xml @@ -361,6 +361,19 @@ + + + io.spring.javaformat + spring-javaformat-maven-plugin + [0,) + + validate + + + + + + From d0e4e7731bdfcd3bea8735fd6944f22e327d376b Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 10 May 2021 17:45:23 +0100 Subject: [PATCH 004/129] Only attempt to login to docker on main --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6557f3076..ab3854078 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,6 @@ jdk: oraclejdk8 services: - docker before_script: - - docker login -u springbuildmaster -p "$DOCKERHUB_PASSWORD" + - '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "main" ] && docker login -u springbuildmaster -p "$DOCKERHUB_PASSWORD"' script: - ./mvnw spring-boot:build-image -D spring-boot.build-image.imageName=springio/petclinic && [ "${TRAVIS_BRANCH}" = "main" ] && docker push springio/petclinic From 525dc6a2d058cd1126cbcf223239164e53498a49 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 10 May 2021 17:45:23 +0100 Subject: [PATCH 005/129] Only attempt to login to docker on main --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ab3854078..c76db46b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,6 @@ jdk: oraclejdk8 services: - docker before_script: - - '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "main" ] && docker login -u springbuildmaster -p "$DOCKERHUB_PASSWORD"' + - '[ "${TRAVIS_PULL_REQUEST}" != "false" ] || [ "${TRAVIS_BRANCH}" != "main" ] || docker login -u springbuildmaster -p "$DOCKERHUB_PASSWORD"' script: - - ./mvnw spring-boot:build-image -D spring-boot.build-image.imageName=springio/petclinic && [ "${TRAVIS_BRANCH}" = "main" ] && docker push springio/petclinic + - ./mvnw spring-boot:build-image -D spring-boot.build-image.imageName=springio/petclinic && ([ "${TRAVIS_BRANCH}" != "main" ] || docker push springio/petclinic) From 0d8a80da65315dc898ec07a0c38948288cf2e2b3 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Wed, 12 May 2021 10:41:09 +0100 Subject: [PATCH 006/129] Clarify that a JDK is needed --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index ff6d2be15..fe2ff72b3 100644 --- a/readme.md +++ b/readme.md @@ -48,7 +48,7 @@ Further documentation is provided [here](https://github.com/spring-projects/spri ### Prerequisites The following items should be installed in your system: -* Java 8 or newer. +* Java 8 or newer (full JDK not a JRE). * git command line tool (https://help.github.com/articles/set-up-git) * Your preferred IDE * Eclipse with the m2e plugin. Note: when m2e is available, there is an m2 icon in `Help -> About` dialog. If m2e is From 8954fa9585b02ba035662ab1385dce20a8c15efc Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Wed, 12 May 2021 10:45:14 +0100 Subject: [PATCH 007/129] Add note about autocrlf on windows --- readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.md b/readme.md index fe2ff72b3..c31c8a162 100644 --- a/readme.md +++ b/readme.md @@ -24,6 +24,8 @@ Or you can run it from Maven directly using the Spring Boot Maven plugin. If you ./mvnw spring-boot:run ``` +> NOTE: Windows users should set `git config core.autocrlf true` to avoid format assertions failing the build (use `--global` to set that flag globally). + ## In case you find a bug/suggested improvement for Spring Petclinic Our issue tracker is available here: https://github.com/spring-projects/spring-petclinic/issues From b91e21378c218189892e964732918e1c93d9a8e2 Mon Sep 17 00:00:00 2001 From: Frank Migliorino Date: Tue, 24 Sep 2019 10:15:18 -0400 Subject: [PATCH 008/129] Fix docs for setting profiles Add using -Dspring-boot.run.jvmArguments to the notes for how to set up using MySQL. --- src/main/resources/db/mysql/petclinic_db_setup_mysql.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt b/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt index 29bb601fe..f7ae6777c 100644 --- a/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt +++ b/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt @@ -24,7 +24,11 @@ because the petclinic user is already set up if you use the provided `docker-compose.yaml`. 3) Run the app with `spring.profiles.active=mysql` (e.g. as a System property via the command - line, but any way that sets that property in a Spring Boot app should work). + line, but any way that sets that property in a Spring Boot app should work). For example use + + mvn spring-boot:run -Dspring-boot.run.profiles=mysql + + To activate the profile on the command line. N.B. the "petclinic" database has to exist for the app to work with the JDBC URL value as it is configured by default. This condition is taken care of automatically by the From e7c879ed3abe10e446ff103887ad665ca6acf04e Mon Sep 17 00:00:00 2001 From: lsap <35534440+lsap@users.noreply.github.com> Date: Fri, 14 May 2021 17:00:22 +0300 Subject: [PATCH 009/129] Update petclinic_db_setup_mysql.txt Hey Team, thanks in advance! --- src/main/resources/db/mysql/petclinic_db_setup_mysql.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt b/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt index f7ae6777c..8b39c07a4 100644 --- a/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt +++ b/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt @@ -21,7 +21,7 @@ 2) (Once only) create the PetClinic database and user by executing the "db/mysql/user.sql" scripts. You can connect to the database running in the docker container using `mysql -u root -h localhost --protocol tcp`, but you don't need to run the script there - because the petclinic user is already set up if you use the provided `docker-compose.yaml`. + because the petclinic user is already set up if you use the provided `docker-compose.yml`. 3) Run the app with `spring.profiles.active=mysql` (e.g. as a System property via the command line, but any way that sets that property in a Spring Boot app should work). For example use From 51499570615823fd6770ca5a2c7bc783f2ecc047 Mon Sep 17 00:00:00 2001 From: Daniel Franco Date: Sun, 22 Aug 2021 22:48:44 +0100 Subject: [PATCH 010/129] Upgrade to Maven 3.8.2 See gh-805 --- .mvn/wrapper/maven-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 2743cab67..4be234949 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,3 +1,3 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar From 11f1234b424fe2c55e5c02ad5683913090966ffa Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 26 Aug 2021 16:31:33 +0200 Subject: [PATCH 011/129] Upgrade to Spring Boot 2.5.4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 628c88461..fdc24c4c3 100644 --- a/pom.xml +++ b/pom.xml @@ -5,12 +5,12 @@ 4.0.0 org.springframework.samples spring-petclinic - 2.4.5 + 2.5.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 2.4.5 + 2.5.4 petclinic From 25ba1621a9d7440df013f7737ce070f44014ecb1 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Tue, 14 Sep 2021 17:33:24 +0100 Subject: [PATCH 012/129] Add delimiter to list of pets --- src/main/resources/templates/owners/ownersList.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/templates/owners/ownersList.html b/src/main/resources/templates/owners/ownersList.html index cf489b59b..55cbe0a6a 100644 --- a/src/main/resources/templates/owners/ownersList.html +++ b/src/main/resources/templates/owners/ownersList.html @@ -24,7 +24,7 @@ - + From 8ad9c05f74a96f46dd4211766fbb0bc78d48f94d Mon Sep 17 00:00:00 2001 From: simrin051 Date: Mon, 13 Sep 2021 20:11:55 +0530 Subject: [PATCH 013/129] Add pagination for owners and vets lists in HTML Fixes #91 --- .../samples/petclinic/model/BaseEntity.java | 3 +- .../samples/petclinic/owner/Owner.java | 3 +- .../petclinic/owner/OwnerController.java | 49 ++++++++--- .../petclinic/owner/OwnerRepository.java | 16 +++- .../petclinic/owner/PetRepository.java | 4 +- .../samples/petclinic/owner/PetType.java | 4 +- .../petclinic/owner/PetTypeFormatter.java | 8 +- .../petclinic/owner/VisitController.java | 13 +-- .../petclinic/system/CacheConfiguration.java | 4 +- .../samples/petclinic/vet/Specialty.java | 5 +- .../samples/petclinic/vet/Vet.java | 18 +---- .../samples/petclinic/vet/VetController.java | 30 ++++++- .../samples/petclinic/vet/VetRepository.java | 18 ++++- .../samples/petclinic/vet/Vets.java | 5 +- .../samples/petclinic/visit/Visit.java | 7 +- .../petclinic/visit/VisitRepository.java | 4 +- .../templates/owners/ownersList.html | 81 +++++++++++++------ .../resources/templates/vets/vetList.html | 66 ++++++++++----- .../petclinic/model/ValidatorTests.java | 11 ++- .../petclinic/owner/OwnerControllerTests.java | 53 ++++++++---- .../petclinic/owner/PetControllerTests.java | 12 ++- .../owner/PetTypeFormatterTests.java | 12 +-- .../petclinic/owner/VisitControllerTests.java | 12 ++- .../petclinic/service/ClinicServiceTests.java | 35 ++++---- .../petclinic/service/EntityUtils.java | 4 +- .../system/CrashControllerTests.java | 5 +- .../petclinic/vet/VetControllerTests.java | 33 +++++--- 27 files changed, 323 insertions(+), 192 deletions(-) diff --git a/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java b/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java index 4cb9ffc0c..3b47a954c 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java +++ b/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java @@ -15,12 +15,11 @@ */ package org.springframework.samples.petclinic.model; -import java.io.Serializable; - import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.MappedSuperclass; +import java.io.Serializable; /** * Simple JavaBean domain object with an id property. Used as a base class for objects diff --git a/src/main/java/org/springframework/samples/petclinic/owner/Owner.java b/src/main/java/org/springframework/samples/petclinic/owner/Owner.java index 61083bc8d..7f2ef905e 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/Owner.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/Owner.java @@ -24,6 +24,7 @@ import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.validation.constraints.Digits; @@ -59,7 +60,7 @@ public class Owner extends Person { @Digits(fraction = 0, integer = 10) private String telephone; - @OneToMany(cascade = CascadeType.ALL, mappedBy = "owner") + @OneToMany(cascade = CascadeType.ALL, mappedBy = "owner", fetch = FetchType.EAGER) private Set pets; public String getAddress() { diff --git a/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java b/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java index 79aa4cd9b..781184ae2 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java @@ -15,6 +15,14 @@ */ package org.springframework.samples.petclinic.owner; +import java.util.List; +import java.util.Map; + +import javax.validation.Valid; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.samples.petclinic.visit.VisitRepository; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -24,12 +32,9 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.ModelAndView; -import javax.validation.Valid; -import java.util.Collection; -import java.util.Map; - /** * @author Juergen Hoeller * @author Ken Krebs @@ -43,7 +48,7 @@ class OwnerController { private final OwnerRepository owners; - private VisitRepository visits; + private final VisitRepository visits; public OwnerController(OwnerRepository clinicService, VisitRepository visits) { this.owners = clinicService; @@ -80,7 +85,8 @@ class OwnerController { } @GetMapping("/owners") - public String processFindForm(Owner owner, BindingResult result, Map model) { + public String processFindForm(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result, + Model model) { // allow parameterless GET request for /owners to return all records if (owner.getLastName() == null) { @@ -88,24 +94,43 @@ class OwnerController { } // find owners by last name - Collection results = this.owners.findByLastName(owner.getLastName()); - if (results.isEmpty()) { + String lastName = owner.getLastName(); + Page ownersResults = findPaginatedForOwnersLastName(page, lastName); + if (ownersResults.isEmpty()) { // no owners found result.rejectValue("lastName", "notFound", "not found"); return "owners/findOwners"; } - else if (results.size() == 1) { + else if (ownersResults.getTotalElements() == 1) { // 1 owner found - owner = results.iterator().next(); + owner = ownersResults.iterator().next(); return "redirect:/owners/" + owner.getId(); } else { // multiple owners found - model.put("selections", results); - return "owners/ownersList"; + lastName = owner.getLastName(); + return addPaginationModel(page, model, lastName, ownersResults); } } + private String addPaginationModel(int page, Model model, String lastName, Page paginated) { + model.addAttribute("listOwners", paginated); + List listOwners = paginated.getContent(); + model.addAttribute("currentPage", page); + model.addAttribute("totalPages", paginated.getTotalPages()); + model.addAttribute("totalItems", paginated.getTotalElements()); + model.addAttribute("listOwners", listOwners); + return "owners/ownersList"; + } + + private Page findPaginatedForOwnersLastName(int page, String lastname) { + + int pageSize = 5; + Pageable pageable = PageRequest.of(page - 1, pageSize); + return owners.findByLastName(lastname, pageable); + + } + @GetMapping("/owners/{ownerId}/edit") public String initUpdateOwnerForm(@PathVariable("ownerId") int ownerId, Model model) { Owner owner = this.owners.findById(ownerId); diff --git a/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java b/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java index 0613e928a..091510122 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java @@ -15,8 +15,8 @@ */ package org.springframework.samples.petclinic.owner; -import java.util.Collection; - +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; import org.springframework.data.repository.query.Param; @@ -42,9 +42,10 @@ public interface OwnerRepository extends Repository { * @return a Collection of matching {@link Owner}s (or an empty Collection if none * found) */ - @Query("SELECT DISTINCT owner FROM Owner owner left join fetch owner.pets WHERE owner.lastName LIKE :lastName%") + + @Query("SELECT DISTINCT owner FROM Owner owner left join owner.pets WHERE owner.lastName LIKE :lastName% ") @Transactional(readOnly = true) - Collection findByLastName(@Param("lastName") String lastName); + Page findByLastName(@Param("lastName") String lastName, Pageable pageable); /** * Retrieve an {@link Owner} from the data store by id. @@ -61,4 +62,11 @@ public interface OwnerRepository extends Repository { */ void save(Owner owner); + /** + * Returnes all the owners from data store + **/ + @Query("SELECT owner FROM Owner owner") + @Transactional(readOnly = true) + Page findAll(Pageable pageable); + } diff --git a/src/main/java/org/springframework/samples/petclinic/owner/PetRepository.java b/src/main/java/org/springframework/samples/petclinic/owner/PetRepository.java index 9d25b095b..069f9f37c 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/PetRepository.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/PetRepository.java @@ -15,12 +15,12 @@ */ package org.springframework.samples.petclinic.owner; -import java.util.List; - import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + /** * Repository class for Pet domain objects All method names are compliant * with Spring Data naming conventions so this interface can easily be extended for Spring diff --git a/src/main/java/org/springframework/samples/petclinic/owner/PetType.java b/src/main/java/org/springframework/samples/petclinic/owner/PetType.java index 6f0aa58d3..3cb9fc1d0 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/PetType.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/PetType.java @@ -15,11 +15,11 @@ */ package org.springframework.samples.petclinic.owner; +import org.springframework.samples.petclinic.model.NamedEntity; + import javax.persistence.Entity; import javax.persistence.Table; -import org.springframework.samples.petclinic.model.NamedEntity; - /** * @author Juergen Hoeller Can be Cat, Dog, Hamster... */ diff --git a/src/main/java/org/springframework/samples/petclinic/owner/PetTypeFormatter.java b/src/main/java/org/springframework/samples/petclinic/owner/PetTypeFormatter.java index 4940bcb38..c97107f2e 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/PetTypeFormatter.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/PetTypeFormatter.java @@ -15,14 +15,14 @@ */ package org.springframework.samples.petclinic.owner; -import java.text.ParseException; -import java.util.Collection; -import java.util.Locale; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.format.Formatter; import org.springframework.stereotype.Component; +import java.text.ParseException; +import java.util.Collection; +import java.util.Locale; + /** * Instructs Spring MVC on how to parse and print elements of type 'PetType'. Starting * from Spring 3.0, Formatters have come as an improvement in comparison to legacy diff --git a/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java b/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java index 375980312..135497f47 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java @@ -15,20 +15,15 @@ */ package org.springframework.samples.petclinic.owner; -import java.util.Map; - -import javax.validation.Valid; - import org.springframework.samples.petclinic.visit.Visit; import org.springframework.samples.petclinic.visit.VisitRepository; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.WebDataBinder; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.InitBinder; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.Map; /** * @author Juergen Hoeller diff --git a/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java b/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java index 0a96582c9..c1da28994 100755 --- a/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java +++ b/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java @@ -16,13 +16,13 @@ package org.springframework.samples.petclinic.system; -import javax.cache.configuration.MutableConfiguration; - import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import javax.cache.configuration.MutableConfiguration; + /** * Cache configuration intended for caches providing the JCache API. This configuration * creates the used cache for the application and enables statistics that become diff --git a/src/main/java/org/springframework/samples/petclinic/vet/Specialty.java b/src/main/java/org/springframework/samples/petclinic/vet/Specialty.java index ea8ec6ded..3bd2fbe78 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/Specialty.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/Specialty.java @@ -15,12 +15,11 @@ */ package org.springframework.samples.petclinic.vet; -import java.io.Serializable; +import org.springframework.samples.petclinic.model.NamedEntity; import javax.persistence.Entity; import javax.persistence.Table; - -import org.springframework.samples.petclinic.model.NamedEntity; +import java.io.Serializable; /** * Models a {@link Vet Vet's} specialty (for example, dentistry). diff --git a/src/main/java/org/springframework/samples/petclinic/vet/Vet.java b/src/main/java/org/springframework/samples/petclinic/vet/Vet.java index 014becfce..7f6193102 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/Vet.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/Vet.java @@ -15,24 +15,14 @@ */ package org.springframework.samples.petclinic.vet; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.JoinColumn; -import javax.persistence.JoinTable; -import javax.persistence.ManyToMany; -import javax.persistence.Table; -import javax.xml.bind.annotation.XmlElement; - import org.springframework.beans.support.MutableSortDefinition; import org.springframework.beans.support.PropertyComparator; import org.springframework.samples.petclinic.model.Person; +import javax.persistence.*; +import javax.xml.bind.annotation.XmlElement; +import java.util.*; + /** * Simple JavaBean domain object representing a veterinarian. * diff --git a/src/main/java/org/springframework/samples/petclinic/vet/VetController.java b/src/main/java/org/springframework/samples/petclinic/vet/VetController.java index fb5e321ba..170c2e876 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/VetController.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/VetController.java @@ -15,11 +15,17 @@ */ package org.springframework.samples.petclinic.vet; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; -import java.util.Map; +import java.util.List; /** * @author Juergen Hoeller @@ -37,15 +43,31 @@ class VetController { } @GetMapping("/vets.html") - public String showVetList(Map model) { + public String showVetList(@RequestParam(defaultValue = "1") int page, Model model) { // Here we are returning an object of type 'Vets' rather than a collection of Vet // objects so it is simpler for Object-Xml mapping Vets vets = new Vets(); - vets.getVetList().addAll(this.vets.findAll()); - model.put("vets", vets); + Page paginated = findPaginated(page); + vets.getVetList().addAll(paginated.toList()); + return addPaginationModel(page, paginated, model); + + } + + private String addPaginationModel(int page, Page paginated, Model model) { + List listVets = paginated.getContent(); + model.addAttribute("currentPage", page); + model.addAttribute("totalPages", paginated.getTotalPages()); + model.addAttribute("totalItems", paginated.getTotalElements()); + model.addAttribute("listVets", listVets); return "vets/vetList"; } + private Page findPaginated(int page) { + int pageSize = 5; + Pageable pageable = PageRequest.of(page - 1, pageSize); + return vets.findAll(pageable); + } + @GetMapping({ "/vets" }) public @ResponseBody Vets showResourcesVetList() { // Here we are returning an object of type 'Vets' rather than a collection of Vet diff --git a/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java b/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java index 549b1c229..65af2b86b 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java @@ -15,13 +15,15 @@ */ package org.springframework.samples.petclinic.vet; -import java.util.Collection; - import org.springframework.cache.annotation.Cacheable; import org.springframework.dao.DataAccessException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.repository.Repository; import org.springframework.transaction.annotation.Transactional; +import java.util.Collection; + /** * Repository class for Vet domain objects All method names are compliant * with Spring Data naming conventions so this interface can easily be extended for Spring @@ -43,4 +45,16 @@ public interface VetRepository extends Repository { @Cacheable("vets") Collection findAll() throws DataAccessException; + /** + * Retrieve all Vets from data store in Pages + * @param pageable + * @return + * @throws DataAccessException + */ + @Transactional(readOnly = true) + @Cacheable("vets") + Page findAll(Pageable pageable) throws DataAccessException; + + ; + } diff --git a/src/main/java/org/springframework/samples/petclinic/vet/Vets.java b/src/main/java/org/springframework/samples/petclinic/vet/Vets.java index cea665629..961e5c096 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/Vets.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/Vets.java @@ -15,11 +15,10 @@ */ package org.springframework.samples.petclinic.vet; -import java.util.ArrayList; -import java.util.List; - import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import java.util.ArrayList; +import java.util.List; /** * Simple domain object representing a list of veterinarians. Mostly here to be used for diff --git a/src/main/java/org/springframework/samples/petclinic/visit/Visit.java b/src/main/java/org/springframework/samples/petclinic/visit/Visit.java index df9f25fe0..239d60597 100755 --- a/src/main/java/org/springframework/samples/petclinic/visit/Visit.java +++ b/src/main/java/org/springframework/samples/petclinic/visit/Visit.java @@ -15,15 +15,14 @@ */ package org.springframework.samples.petclinic.visit; -import java.time.LocalDate; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.samples.petclinic.model.BaseEntity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; import javax.validation.constraints.NotEmpty; - -import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.samples.petclinic.model.BaseEntity; +import java.time.LocalDate; /** * Simple JavaBean domain object representing a visit. diff --git a/src/main/java/org/springframework/samples/petclinic/visit/VisitRepository.java b/src/main/java/org/springframework/samples/petclinic/visit/VisitRepository.java index d5a3334c6..4af5079a0 100644 --- a/src/main/java/org/springframework/samples/petclinic/visit/VisitRepository.java +++ b/src/main/java/org/springframework/samples/petclinic/visit/VisitRepository.java @@ -15,12 +15,12 @@ */ package org.springframework.samples.petclinic.visit; -import java.util.List; - import org.springframework.dao.DataAccessException; import org.springframework.data.repository.Repository; import org.springframework.samples.petclinic.model.BaseEntity; +import java.util.List; + /** * Repository class for Visit domain objects All method names are compliant * with Spring Data naming conventions so this interface can easily be extended for Spring diff --git a/src/main/resources/templates/owners/ownersList.html b/src/main/resources/templates/owners/ownersList.html index 55cbe0a6a..9d5a9aaab 100644 --- a/src/main/resources/templates/owners/ownersList.html +++ b/src/main/resources/templates/owners/ownersList.html @@ -2,32 +2,61 @@ - + -

Owners

+

Owners

- - - - - - - - - - - - - - - - -
NameAddressCityTelephonePets
- - - - -
- - + + + + + + + + + + + + + + + + +
NameAddressCityTelephonePets
+ + + + +
+
+ Pages: + [ + + [[${i}]] + [[${i}]] + + + + + + + + + + + + + + + + + + +
+ + diff --git a/src/main/resources/templates/vets/vetList.html b/src/main/resources/templates/vets/vetList.html index 4fc961793..26432ed8f 100644 --- a/src/main/resources/templates/vets/vetList.html +++ b/src/main/resources/templates/vets/vetList.html @@ -1,27 +1,57 @@ + th:replace="~{fragments/layout :: layout (~{::body},'vets')}"> -

Veterinarians

+

Veterinarians

- - - - - - - - - - - - - -
NameSpecialties
none
+ + + + + + + + + + + + + +
NameSpecialties
none
+ +
+ Pages: + [ + + [[${i}]] + [[${i}]] + + + + + + + + + + + + + + + + + + +
diff --git a/src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java b/src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java index 8d754900d..5fee8191a 100644 --- a/src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java +++ b/src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java @@ -16,16 +16,15 @@ package org.springframework.samples.petclinic.model; -import java.util.Locale; -import java.util.Set; - -import javax.validation.ConstraintViolation; -import javax.validation.Validator; - import org.junit.jupiter.api.Test; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; +import javax.validation.ConstraintViolation; +import javax.validation.Validator; +import java.util.Locale; +import java.util.Set; + import static org.assertj.core.api.Assertions.assertThat; /** diff --git a/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java b/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java index 1d6249c5d..ce6395d7b 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java @@ -16,32 +16,34 @@ package org.springframework.samples.petclinic.owner; -import java.time.LocalDate; -import java.util.Collections; -import java.util.List; - import org.assertj.core.util.Lists; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.samples.petclinic.visit.Visit; import org.springframework.samples.petclinic.visit.VisitRepository; import org.springframework.test.web.servlet.MockMvc; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.hasProperty; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; +import java.time.LocalDate; +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.Matchers.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.BDDMockito.given; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; /** * Test class for {@link OwnerController} @@ -81,10 +83,17 @@ class OwnerControllerTests { max.setName("Max"); max.setBirthDate(LocalDate.now()); george.setPetsInternal(Collections.singleton(max)); + + given(this.owners.findByLastName(eq("Franklin"), any(Pageable.class))) + .willReturn(new PageImpl(Lists.newArrayList(george))); + + given(this.owners.findAll(any(Pageable.class))).willReturn(new PageImpl(Lists.newArrayList(george))); + given(this.owners.findById(TEST_OWNER_ID)).willReturn(george); Visit visit = new Visit(); visit.setDate(LocalDate.now()); given(this.visits.findByPetId(max.getId())).willReturn(Collections.singletonList(visit)); + } @Test @@ -118,23 +127,35 @@ class OwnerControllerTests { @Test void testProcessFindFormSuccess() throws Exception { - given(this.owners.findByLastName("")).willReturn(Lists.newArrayList(george, new Owner())); - mockMvc.perform(get("/owners")).andExpect(status().isOk()).andExpect(view().name("owners/ownersList")); + Page tasks = Mockito.mock(Page.class); + Mockito.when(this.owners.findByLastName(anyString(), org.mockito.Matchers.isA(Pageable.class))) + .thenReturn(tasks); + mockMvc.perform(get("/owners?page=1")).andExpect(status().isOk()).andExpect(view().name("owners/ownersList")); } @Test void testProcessFindFormByLastName() throws Exception { - given(this.owners.findByLastName(george.getLastName())).willReturn(Lists.newArrayList(george)); - mockMvc.perform(get("/owners").param("lastName", "Franklin")).andExpect(status().is3xxRedirection()) + + Page tasks = new PageImpl(Lists.newArrayList(george)); + String lastName = george.getLastName(); + Mockito.when(this.owners.findByLastName(eq("Franklin"), any(Pageable.class))).thenReturn(tasks); + mockMvc.perform(get("/owners?page=1").param("lastName", "Franklin")).andExpect(status().is3xxRedirection()) .andExpect(view().name("redirect:/owners/" + TEST_OWNER_ID)); } @Test void testProcessFindFormNoOwnersFound() throws Exception { - mockMvc.perform(get("/owners").param("lastName", "Unknown Surname")).andExpect(status().isOk()) + int pageNumber = 0; + int pageSize = 1, size = 0; + Pageable pageable = PageRequest.of(pageNumber, pageSize); + Page tasks = new PageImpl(Lists.newArrayList()); + Mockito.when(this.owners.findByLastName(eq("Unknown Surname"), org.mockito.Matchers.isA(Pageable.class))) + .thenReturn(tasks); + mockMvc.perform(get("/owners?page=1").param("lastName", "Unknown Surname")).andExpect(status().isOk()) .andExpect(model().attributeHasFieldErrors("owner", "lastName")) .andExpect(model().attributeHasFieldErrorCode("owner", "lastName", "notFound")) .andExpect(view().name("owners/findOwners")); + } @Test diff --git a/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java b/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java index 47c444a78..5efd55fad 100755 --- a/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java @@ -16,13 +16,6 @@ package org.springframework.samples.petclinic.owner; -import static org.mockito.BDDMockito.given; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; - import org.assertj.core.util.Lists; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -33,6 +26,11 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; import org.springframework.test.web.servlet.MockMvc; +import static org.mockito.BDDMockito.given; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + /** * Test class for the {@link PetController} * diff --git a/src/test/java/org/springframework/samples/petclinic/owner/PetTypeFormatterTests.java b/src/test/java/org/springframework/samples/petclinic/owner/PetTypeFormatterTests.java index adb96b69d..1c0c01b7e 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/PetTypeFormatterTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/PetTypeFormatterTests.java @@ -16,12 +16,6 @@ package org.springframework.samples.petclinic.owner; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Locale; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -29,6 +23,12 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Locale; + import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; diff --git a/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java b/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java index 84bee72df..a22317bfe 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java @@ -16,13 +16,6 @@ package org.springframework.samples.petclinic.owner; -import static org.mockito.BDDMockito.given; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -31,6 +24,11 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.samples.petclinic.visit.VisitRepository; import org.springframework.test.web.servlet.MockMvc; +import static org.mockito.BDDMockito.given; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + /** * Test class for {@link VisitController} * diff --git a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java index a7f3d9d24..6a133ea23 100644 --- a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java +++ b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java @@ -16,20 +16,13 @@ package org.springframework.samples.petclinic.service; -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.LocalDate; -import java.util.Collection; - import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.ComponentScan; -import org.springframework.samples.petclinic.owner.Owner; -import org.springframework.samples.petclinic.owner.OwnerRepository; -import org.springframework.samples.petclinic.owner.Pet; -import org.springframework.samples.petclinic.owner.PetRepository; -import org.springframework.samples.petclinic.owner.PetType; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.samples.petclinic.owner.*; import org.springframework.samples.petclinic.vet.Vet; import org.springframework.samples.petclinic.vet.VetRepository; import org.springframework.samples.petclinic.visit.Visit; @@ -37,6 +30,11 @@ import org.springframework.samples.petclinic.visit.VisitRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDate; +import java.util.Collection; + +import static org.assertj.core.api.Assertions.assertThat; + /** * Integration test of the Service and the Repository layer. *

@@ -48,8 +46,7 @@ import org.springframework.transaction.annotation.Transactional; * time between test execution. *

  • Dependency Injection of test fixture instances, meaning that we * don't need to perform application context lookups. See the use of - * {@link Autowired @Autowired} on the {@link - * ClinicServiceTests#clinicService clinicService} instance variable, which uses + * {@link Autowired @Autowired} on the instance variable, which uses * autowiring by type. *
  • Transaction management, meaning each test method is executed in * its own transaction, which is automatically rolled back by default. Thus, even if tests @@ -81,12 +78,14 @@ class ClinicServiceTests { @Autowired protected VetRepository vets; + Pageable pageable; + @Test void shouldFindOwnersByLastName() { - Collection owners = this.owners.findByLastName("Davis"); + Page owners = this.owners.findByLastName("Davis", pageable); assertThat(owners).hasSize(2); - owners = this.owners.findByLastName("Daviss"); + owners = this.owners.findByLastName("Daviss", pageable); assertThat(owners).isEmpty(); } @@ -102,8 +101,8 @@ class ClinicServiceTests { @Test @Transactional void shouldInsertOwner() { - Collection owners = this.owners.findByLastName("Schultz"); - int found = owners.size(); + Page owners = this.owners.findByLastName("Schultz", pageable); + int found = (int) owners.getTotalElements(); Owner owner = new Owner(); owner.setFirstName("Sam"); @@ -114,8 +113,8 @@ class ClinicServiceTests { this.owners.save(owner); assertThat(owner.getId().longValue()).isNotEqualTo(0); - owners = this.owners.findByLastName("Schultz"); - assertThat(owners.size()).isEqualTo(found + 1); + owners = this.owners.findByLastName("Schultz", pageable); + assertThat(owners.getTotalElements()).isEqualTo(found + 1); } @Test diff --git a/src/test/java/org/springframework/samples/petclinic/service/EntityUtils.java b/src/test/java/org/springframework/samples/petclinic/service/EntityUtils.java index ca8bc41fc..7b7a5e64a 100644 --- a/src/test/java/org/springframework/samples/petclinic/service/EntityUtils.java +++ b/src/test/java/org/springframework/samples/petclinic/service/EntityUtils.java @@ -16,11 +16,11 @@ package org.springframework.samples.petclinic.service; -import java.util.Collection; - import org.springframework.orm.ObjectRetrievalFailureException; import org.springframework.samples.petclinic.model.BaseEntity; +import java.util.Collection; + /** * Utility methods for handling entities. Separate from the BaseEntity class mainly * because of dependency on the ORM-associated ObjectRetrievalFailureException. diff --git a/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java b/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java index 6bafc7499..06e714085 100644 --- a/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java @@ -23,10 +23,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; /** * Test class for {@link CrashController} diff --git a/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java b/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java index fd537bee2..e0b4964be 100644 --- a/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java @@ -16,27 +16,28 @@ package org.springframework.samples.petclinic.vet; -import static org.mockito.BDDMockito.given; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; - import org.assertj.core.util.Lists; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; /** * Test class for the {@link VetController} */ + @WebMvcTest(VetController.class) class VetControllerTests { @@ -46,13 +47,17 @@ class VetControllerTests { @MockBean private VetRepository vets; + private Vet james; + + private Vet helen; + @BeforeEach void setup() { - Vet james = new Vet(); + james = new Vet(); james.setFirstName("James"); james.setLastName("Carter"); james.setId(1); - Vet helen = new Vet(); + helen = new Vet(); helen.setFirstName("Helen"); helen.setLastName("Leary"); helen.setId(2); @@ -61,12 +66,16 @@ class VetControllerTests { radiology.setName("radiology"); helen.addSpecialty(radiology); given(this.vets.findAll()).willReturn(Lists.newArrayList(james, helen)); + given(this.vets.findAll(any(Pageable.class))).willReturn(new PageImpl(Lists.newArrayList(james, helen))); + } @Test void testShowVetListHtml() throws Exception { - mockMvc.perform(get("/vets.html")).andExpect(status().isOk()).andExpect(model().attributeExists("vets")) - .andExpect(view().name("vets/vetList")); + + mockMvc.perform(MockMvcRequestBuilders.get("/vets.html?page=1")).andExpect(status().isOk()) + .andExpect(model().attributeExists("listVets")).andExpect(view().name("vets/vetList")); + } @Test From e11a594ec210344faa4931f29678ed164bb546ac Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 1 Oct 2021 15:09:25 +0100 Subject: [PATCH 014/129] Tidy up test --- .../petclinic/owner/OwnerControllerTests.java | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java b/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java index ce6395d7b..28120afa1 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java @@ -16,6 +16,24 @@ package org.springframework.samples.petclinic.owner; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; + +import java.time.LocalDate; +import java.util.Collections; +import java.util.List; + import org.assertj.core.util.Lists; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; @@ -27,24 +45,11 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.samples.petclinic.visit.Visit; import org.springframework.samples.petclinic.visit.VisitRepository; import org.springframework.test.web.servlet.MockMvc; -import java.time.LocalDate; -import java.util.Collections; -import java.util.List; - -import static org.hamcrest.Matchers.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.BDDMockito.given; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - /** * Test class for {@link OwnerController} * @@ -127,17 +132,14 @@ class OwnerControllerTests { @Test void testProcessFindFormSuccess() throws Exception { - Page tasks = Mockito.mock(Page.class); - Mockito.when(this.owners.findByLastName(anyString(), org.mockito.Matchers.isA(Pageable.class))) - .thenReturn(tasks); + Page tasks = new PageImpl(Lists.newArrayList(george, new Owner())); + Mockito.when(this.owners.findByLastName(anyString(), any(Pageable.class))).thenReturn(tasks); mockMvc.perform(get("/owners?page=1")).andExpect(status().isOk()).andExpect(view().name("owners/ownersList")); } @Test void testProcessFindFormByLastName() throws Exception { - Page tasks = new PageImpl(Lists.newArrayList(george)); - String lastName = george.getLastName(); Mockito.when(this.owners.findByLastName(eq("Franklin"), any(Pageable.class))).thenReturn(tasks); mockMvc.perform(get("/owners?page=1").param("lastName", "Franklin")).andExpect(status().is3xxRedirection()) .andExpect(view().name("redirect:/owners/" + TEST_OWNER_ID)); @@ -145,12 +147,8 @@ class OwnerControllerTests { @Test void testProcessFindFormNoOwnersFound() throws Exception { - int pageNumber = 0; - int pageSize = 1, size = 0; - Pageable pageable = PageRequest.of(pageNumber, pageSize); Page tasks = new PageImpl(Lists.newArrayList()); - Mockito.when(this.owners.findByLastName(eq("Unknown Surname"), org.mockito.Matchers.isA(Pageable.class))) - .thenReturn(tasks); + Mockito.when(this.owners.findByLastName(eq("Unknown Surname"), any(Pageable.class))).thenReturn(tasks); mockMvc.perform(get("/owners?page=1").param("lastName", "Unknown Surname")).andExpect(status().isOk()) .andExpect(model().attributeHasFieldErrors("owner", "lastName")) .andExpect(model().attributeHasFieldErrorCode("owner", "lastName", "notFound")) From e57ef71e54f88d4e129b7e7c2fde4b3b38e86459 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Tue, 5 Oct 2021 13:36:50 +0100 Subject: [PATCH 015/129] Ensure real database is used if mysql profile active --- .../petclinic/service/ClinicServiceTests.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java index 6a133ea23..cab001f11 100644 --- a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java +++ b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java @@ -16,13 +16,24 @@ package org.springframework.samples.petclinic.service; +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.LocalDate; +import java.util.Collection; + import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.ComponentScan; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.samples.petclinic.owner.*; +import org.springframework.samples.petclinic.owner.Owner; +import org.springframework.samples.petclinic.owner.OwnerRepository; +import org.springframework.samples.petclinic.owner.Pet; +import org.springframework.samples.petclinic.owner.PetRepository; +import org.springframework.samples.petclinic.owner.PetType; import org.springframework.samples.petclinic.vet.Vet; import org.springframework.samples.petclinic.vet.VetRepository; import org.springframework.samples.petclinic.visit.Visit; @@ -30,11 +41,6 @@ import org.springframework.samples.petclinic.visit.VisitRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDate; -import java.util.Collection; - -import static org.assertj.core.api.Assertions.assertThat; - /** * Integration test of the Service and the Repository layer. *

    @@ -64,6 +70,8 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Dave Syer */ @DataJpaTest(includeFilters = @ComponentScan.Filter(Service.class)) +// Ensure that if the mysql profile is active we connect to the real database: +@AutoConfigureTestDatabase(replace = Replace.NONE) class ClinicServiceTests { @Autowired From 0a1b3dd1e5e20e8bb441fdffba0f8ddc295f4e53 Mon Sep 17 00:00:00 2001 From: Thomas Risberg Date: Tue, 5 Oct 2021 10:57:08 -0400 Subject: [PATCH 016/129] Add license file --- LICENSE.txt | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 000000000..ff7737963 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 8bfe9e68bf22342c51aa9f4f3acea878b8036953 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Tue, 19 Oct 2021 10:55:07 +0100 Subject: [PATCH 017/129] Tidy imports --- .../springframework/samples/petclinic/vet/VetController.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/springframework/samples/petclinic/vet/VetController.java b/src/main/java/org/springframework/samples/petclinic/vet/VetController.java index 170c2e876..71348f222 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/VetController.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/VetController.java @@ -15,18 +15,17 @@ */ package org.springframework.samples.petclinic.vet; +import java.util.List; + import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; -import java.util.List; - /** * @author Juergen Hoeller * @author Mark Fisher From af9a0a423e8e7237631ac8e3292594b669ff3246 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Tue, 19 Oct 2021 15:34:07 +0100 Subject: [PATCH 018/129] Update to Boot 2.5.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fdc24c4c3..23c84b4b3 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.springframework.boot spring-boot-starter-parent - 2.5.4 + 2.5.5 petclinic From 01621077cbf76aa873eab64f0f0f50aaf6a04b90 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Thu, 21 Oct 2021 10:36:58 +0100 Subject: [PATCH 019/129] Add note on Java version --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index c31c8a162..4a1406bcf 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,7 @@ See the presentation here ## Running petclinic locally -Petclinic is a [Spring Boot](https://spring.io/guides/gs/spring-boot) application built using [Maven](https://spring.io/guides/gs/maven/). You can build a jar file and run it from the command line: +Petclinic is a [Spring Boot](https://spring.io/guides/gs/spring-boot) application built using [Maven](https://spring.io/guides/gs/maven/). You can build a jar file and run it from the command line (it should work just as well with Java 8, 11 or 17): ``` From 0016ae911248ab850d7d2492947ae50aeb9af7aa Mon Sep 17 00:00:00 2001 From: Antoine Rey Date: Sun, 3 Oct 2021 16:40:55 +0200 Subject: [PATCH 020/129] Add GitHub actions maven-build.yml --- .github/workflows/maven-build.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/maven-build.yml diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml new file mode 100644 index 000000000..775ef5521 --- /dev/null +++ b/.github/workflows/maven-build.yml @@ -0,0 +1,26 @@ +# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Java CI with Maven + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'adopt' + cache: maven + - name: Build with Maven + run: mvn -B package --file pom.xml From 198f5e288c16d5137ab4b7f7fb5c642d300592b1 Mon Sep 17 00:00:00 2001 From: Antoine Rey Date: Sun, 3 Oct 2021 16:48:32 +0200 Subject: [PATCH 021/129] Replace Travis badge by GitHub actions one --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 4a1406bcf..72c817828 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -# Spring PetClinic Sample Application [![Build Status](https://travis-ci.org/spring-projects/spring-petclinic.png?branch=main)](https://travis-ci.org/spring-projects/spring-petclinic/) +# Spring PetClinic Sample Application [![Build Status](https://github.com/spring-projects/spring-petclinic/actions/workflows/maven-build.yml/badge.svg)](https://github.com/spring-projects/spring-petclinic/actions/workflows/maven-build.yml) ## Understanding the Spring Petclinic application with a few diagrams See the presentation here From 30d0e2a8561cb5e8b211b7abf8ea82d37f47ea56 Mon Sep 17 00:00:00 2001 From: Antoine Rey Date: Tue, 5 Oct 2021 18:27:21 +0200 Subject: [PATCH 022/129] Use the provided maven wrapper --- .github/workflows/maven-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index 775ef5521..f7ad11424 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -22,5 +22,5 @@ jobs: java-version: '11' distribution: 'adopt' cache: maven - - name: Build with Maven - run: mvn -B package --file pom.xml + - name: Build with Maven Wrapper + run: ./mvnw -B package From bc35c402a2f16451a395ec24ca75d54ff79bb91f Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 29 Oct 2021 13:14:59 +0000 Subject: [PATCH 023/129] Remove travis config file --- .travis.yml | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c76db46b0..000000000 --- a/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -dist: trusty -language: java -jdk: oraclejdk8 -services: - - docker -before_script: - - '[ "${TRAVIS_PULL_REQUEST}" != "false" ] || [ "${TRAVIS_BRANCH}" != "main" ] || docker login -u springbuildmaster -p "$DOCKERHUB_PASSWORD"' -script: - - ./mvnw spring-boot:build-image -D spring-boot.build-image.imageName=springio/petclinic && ([ "${TRAVIS_BRANCH}" != "main" ] || docker push springio/petclinic) From b45f5be82006156dd33717e168d752bbe105b725 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 19 Nov 2021 07:54:21 +0000 Subject: [PATCH 024/129] Update to Spring Boot 2.5.6 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 23c84b4b3..05d97371a 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.springframework.boot spring-boot-starter-parent - 2.5.5 + 2.5.6 petclinic From 3eba970fffd78f2e48dc5dcba004870e54193ec5 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 19 Nov 2021 13:34:47 +0000 Subject: [PATCH 025/129] Fix page links --- src/main/resources/templates/owners/ownersList.html | 2 +- src/main/resources/templates/vets/vetList.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/templates/owners/ownersList.html b/src/main/resources/templates/owners/ownersList.html index 9d5a9aaab..6791d0da9 100644 --- a/src/main/resources/templates/owners/ownersList.html +++ b/src/main/resources/templates/owners/ownersList.html @@ -32,7 +32,7 @@ Pages: [ - [[${i}]] + [[${i}]] [[${i}]] diff --git a/src/main/resources/templates/vets/vetList.html b/src/main/resources/templates/vets/vetList.html index 26432ed8f..031fd43f5 100644 --- a/src/main/resources/templates/vets/vetList.html +++ b/src/main/resources/templates/vets/vetList.html @@ -28,7 +28,7 @@ Pages: [ - [[${i}]] + [[${i}]] [[${i}]] From 4f2f8fab73b35ed79fae3fa249d08011984e3745 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 19 Nov 2021 15:30:56 +0000 Subject: [PATCH 026/129] Remove redundant interface --- .../springframework/samples/petclinic/vet/Specialty.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/springframework/samples/petclinic/vet/Specialty.java b/src/main/java/org/springframework/samples/petclinic/vet/Specialty.java index 3bd2fbe78..3a7347f45 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/Specialty.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/Specialty.java @@ -15,11 +15,10 @@ */ package org.springframework.samples.petclinic.vet; -import org.springframework.samples.petclinic.model.NamedEntity; - import javax.persistence.Entity; import javax.persistence.Table; -import java.io.Serializable; + +import org.springframework.samples.petclinic.model.NamedEntity; /** * Models a {@link Vet Vet's} specialty (for example, dentistry). @@ -28,6 +27,6 @@ import java.io.Serializable; */ @Entity @Table(name = "specialties") -public class Specialty extends NamedEntity implements Serializable { +public class Specialty extends NamedEntity { } From 1095a15f0e42cf5ebf0df942022c62fcf4c82e86 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 19 Nov 2021 11:49:45 +0000 Subject: [PATCH 027/129] Convert to SCSS and Bootstrap 5 --- .gitignore | 2 + npm | 4 + package-lock.json | 151 +++++++++++ package.json | 12 + pom.xml | 74 +++--- .../nohttp-checkstyle-suppressions.xml | 2 + src/main/less/petclinic.less | 239 ------------------ .../templates/fragments/inputField.html | 4 +- .../resources/templates/fragments/layout.html | 50 ++-- .../templates/fragments/selectField.html | 6 +- .../owners/createOrUpdateOwnerForm.html | 2 +- .../templates/owners/findOwners.html | 7 +- .../templates/owners/ownerDetails.html | 4 +- .../templates/owners/ownersList.html | 16 +- .../templates/pets/createOrUpdatePetForm.html | 4 +- .../pets/createOrUpdateVisitForm.html | 2 +- .../resources/templates/vets/vetList.html | 16 +- .../{less/header.less => scss/header.scss} | 0 src/main/scss/petclinic.scss | 211 ++++++++++++++++ .../responsive.less => scss/responsive.scss} | 2 +- .../typography.less => scss/typography.scss} | 0 src/main/wro/wro.properties | 4 - src/main/wro/wro.xml | 6 - 23 files changed, 477 insertions(+), 341 deletions(-) create mode 100755 npm create mode 100644 package-lock.json create mode 100644 package.json delete mode 100644 src/main/less/petclinic.less rename src/main/{less/header.less => scss/header.scss} (100%) create mode 100644 src/main/scss/petclinic.scss rename src/main/{less/responsive.less => scss/responsive.scss} (98%) rename src/main/{less/typography.less => scss/typography.scss} (100%) delete mode 100644 src/main/wro/wro.properties delete mode 100644 src/main/wro/wro.xml diff --git a/.gitignore b/.gitignore index 191769767..182603cab 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ target/* .sts4-cache/ .vscode _site/ +node/ +node_modules/ diff --git a/npm b/npm new file mode 100755 index 000000000..e5e7ed560 --- /dev/null +++ b/npm @@ -0,0 +1,4 @@ +#!/bin/sh +cd $(dirname $0) +PATH="$PWD/node/":$PATH +node "node/node_modules/npm/bin/npm-cli.js" "$@" diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..e5642e723 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,151 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@popperjs/core": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.2.tgz", + "integrity": "sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ==" + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "3.0.0", + "picomatch": "2.3.0" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bootstrap": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz", + "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==" + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "7.0.1" + } + }, + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "requires": { + "anymatch": "3.1.2", + "braces": "3.0.2", + "fsevents": "2.3.2", + "glob-parent": "5.1.2", + "is-binary-path": "2.1.0", + "is-glob": "4.0.3", + "normalize-path": "3.0.0", + "readdirp": "3.6.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "5.0.1" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "4.0.3" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "2.2.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "2.3.0" + } + }, + "sass": { + "version": "1.43.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.43.4.tgz", + "integrity": "sha512-/ptG7KE9lxpGSYiXn7Ar+lKOv37xfWsZRtFYal2QHNigyVQDx685VFT/h7ejVr+R8w7H4tmUgtulsKl5YpveOg==", + "dev": true, + "requires": { + "chokidar": "3.5.2" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "7.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..ac649454e --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "dependencies": { + "bootstrap": "^5.1.3", + "@popperjs/core": "^2.10.1" + }, + "devDependencies": { + "sass": "^1.43.4" + }, + "scripts": { + "build": "sass --style expanded --source-map --embed-sources --no-error-css src/main/scss/:target/classes/static/resources/css/" + } +} diff --git a/pom.xml b/pom.xml index 05d97371a..077586634 100644 --- a/pom.xml +++ b/pom.xml @@ -22,14 +22,17 @@ UTF-8 - 3.3.6 - 1.11.4 - 2.2.4 + 5.1.3 + 4.7.0 + 3.4.1 + 1.13.0 1.8.0 0.8.5 + v8.11.1 0.0.4.RELEASE 0.0.25 + 1.12.0 @@ -100,18 +103,23 @@ org.webjars jquery - ${webjars-jquery.version} + ${jquery.version} org.webjars jquery-ui - ${webjars-jquery-ui.version} + ${jquery-ui.version} - org.webjars + org.webjars.npm bootstrap ${webjars-bootstrap.version} + + org.webjars.npm + font-awesome + ${font-awesome.version} + @@ -235,38 +243,42 @@ - ro.isdc.wro4j - wro4j-maven-plugin - ${wro4j.version} + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} - generate-resources - + install node and npm - run + install-node-and-npm + + ${node.version} + ${npm.version} + + + + npm install + + npm + + + install + + + + build + + npm + + + run-script build + + generate-resources - - ro.isdc.wro.maven.plugin.manager.factory.ConfigurableWroManagerFactory - ${project.build.directory}/classes/static/resources/css - ${basedir}/src/main/wro/wro.xml - ${basedir}/src/main/wro/wro.properties - ${basedir}/src/main/less - - - - org.webjars - bootstrap - ${webjars-bootstrap.version} - - - org.mockito - mockito-core - ${mockito.version} - - + diff --git a/src/checkstyle/nohttp-checkstyle-suppressions.xml b/src/checkstyle/nohttp-checkstyle-suppressions.xml index 1b40e8b3f..73edefd2e 100644 --- a/src/checkstyle/nohttp-checkstyle-suppressions.xml +++ b/src/checkstyle/nohttp-checkstyle-suppressions.xml @@ -3,6 +3,8 @@ "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN" "https://checkstyle.org/dtds/suppressions_1_2.dtd"> + + diff --git a/src/main/less/petclinic.less b/src/main/less/petclinic.less deleted file mode 100644 index 7c88ec091..000000000 --- a/src/main/less/petclinic.less +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2016 the original author or authors. - * - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -@icon-font-path: "../../webjars/bootstrap/fonts/"; - -@spring-green: #6db33f; -@spring-dark-green: #5fa134; -@spring-brown: #34302D; -@spring-grey: #838789; -@spring-light-grey: #f1f1f1; - -@body-bg: @spring-light-grey; -@text-color: @spring-brown; -@link-color: @spring-dark-green; -@link-hover-color: @spring-dark-green; - -@navbar-default-link-color: @spring-light-grey; -@navbar-default-link-active-color: @spring-light-grey; -@navbar-default-link-hover-color: @spring-light-grey; -@navbar-default-link-hover-bg: @spring-green; -@navbar-default-toggle-icon-bar-bg: @spring-light-grey; -@navbar-default-toggle-hover-bg: transparent; -@navbar-default-link-active-bg: @spring-green; - -@border-radius-base: 0; -@border-radius-large: 0; -@border-radius-small: 0; - -@btn-default-color: @spring-light-grey; -@btn-default-bg: @spring-brown; -@btn-default-border: @spring-green; - -@nav-tabs-active-link-hover-color: @spring-light-grey; -@nav-tabs-active-link-hover-bg: @spring-brown; -@nav-tabs-active-link-hover-border-color: @spring-brown; -@nav-tabs-border-color: @spring-brown; - -@pagination-active-bg: @spring-brown; -@pagination-active-border: @spring-green; -@table-border-color: @spring-brown; - -.table > thead > tr > th { - background-color: lighten(@spring-brown, 3%); - color: @spring-light-grey; -} - -.table-filter { - background-color: @spring-brown; - padding: 9px 12px; -} - -.nav > li > a { - color: @spring-grey; -} - -.btn-default { - border-width: 2px; - transition: border 0.15s; - -webkit-transition: border 0.15s; - -moz-transition: border 0.15s; - -o-transition: border 0.15s; - -ms-transition: border 0.15s; - - &:hover, - &:focus, - &:active, - &.active, - .open .dropdown-toggle& { - background-color: @spring-brown; - border-color: @spring-brown; - } -} - - -.container .text-muted { - margin: 20px 0; -} - -code { - font-size: 80%; -} - -.xd-container { - margin-top: 40px; - margin-bottom: 100px; - padding-left: 5px; - padding-right: 5px; -} - -h1 { - margin-bottom: 15px -} - -.index-page--subtitle { - font-size: 16px; - line-height: 24px; - margin: 0 0 30px; -} - -.form-horizontal button.btn-inverse { - margin-left: 32px; -} - -#job-params-modal .modal-dialog { - width: 90%; - margin-left:auto; - margin-right:auto; -} - -[ng-cloak].splash { - display: block !important; -} -[ng-cloak] { - display: none; -} - -.splash { - background: @spring-green; - color: @spring-brown; - display: none; -} - -.error-page { - margin-top: 100px; - text-align: center; -} - -.error-page .error-title { - font-size: 24px; - line-height: 24px; - margin: 30px 0 0; -} - -table td { - vertical-align: middle; -} - -table td .progress { - margin-bottom: 0; -} - -table td.action-column { - width: 1px; -} - -.help-block { - color: lighten(@text-color, 50%); // lighten the text some for contrast -} - -.xd-containers { - font-size: 15px; -} - -.cluster-view > table td { - vertical-align: top; -} - -.cluster-view .label, .cluster-view .column-block { - display: block; -} - -.cluster-view .input-group-addon { - width: 0%; -} - -.cluster-view { - margin-bottom: 0; -} - -.deployment-status-deployed { - .label-success; -} - -.deployment-status-incomplete { - .label-warning; -} - -.deployment-status-failed { - .label-danger; -} - -.deployment-status-deploying { - .label-info -} -.deployment-status-na { -} - -.container-details-table th { - background-color: lighten(@spring-brown, 3%); - color: @spring-light-grey; -} - -.status-help-content-table td { - color: @spring-brown; -} - -.alert-success { - .alert-variant(fade(@alert-success-bg, 70%); @alert-success-border; @alert-success-text); -} -.alert-info { - .alert-variant(fade(@alert-info-bg, 70%); @alert-info-border; @alert-info-text); -} -.alert-warning { - .alert-variant(fade(@alert-warning-bg, 70%); @alert-warning-border; @alert-warning-text); -} -.alert-danger { - .alert-variant(fade(@alert-danger-bg, 70%); @alert-danger-border; @alert-danger-text); -} - -.myspinner { - animation-name: spinner; - animation-duration: 2s; - animation-iteration-count: infinite; - animation-timing-function: linear; - - -webkit-transform-origin: 49% 50%; - -webkit-animation-name: spinner; - -webkit-animation-duration: 2s; - -webkit-animation-iteration-count: infinite; - -webkit-animation-timing-function: linear; -} - -hr { - border-top: 1px dotted @spring-brown; -} - -@import "typography.less"; -@import "header.less"; -@import "responsive.less"; diff --git a/src/main/resources/templates/fragments/inputField.html b/src/main/resources/templates/fragments/inputField.html index c3373bea0..e2062a16c 100644 --- a/src/main/resources/templates/fragments/inputField.html +++ b/src/main/resources/templates/fragments/inputField.html @@ -14,11 +14,11 @@ pattern="(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))"/> Error diff --git a/src/main/resources/templates/fragments/layout.html b/src/main/resources/templates/fragments/layout.html index f3b207483..f3d352275 100755 --- a/src/main/resources/templates/fragments/layout.html +++ b/src/main/resources/templates/fragments/layout.html @@ -17,60 +17,52 @@ + -