From cabb74ed53624ea815130ff8eccf68ab01bc2da3 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 13 Sep 2024 10:03:26 +0100 Subject: [PATCH 01/32] Update to Boot 3.3.3 Fixes #1658 --- pom.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c948f9e3..ee256750 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,8 @@ org.springframework.boot spring-boot-starter-parent - 3.3.2 + 3.3.3 + petclinic @@ -270,6 +271,7 @@ + org.cyclonedx cyclonedx-maven-plugin From f6f923bd39fe9a2e8001c9a45c8628a80d1aa62b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Tue, 24 Sep 2024 16:15:42 +0200 Subject: [PATCH 02/32] Upgrade to Spring Boot 3.3.4 --- build.gradle | 2 +- pom.xml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 397f898b..aa7c5124 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '3.3.2' + id 'org.springframework.boot' version '3.3.4' id 'io.spring.dependency-management' version '1.1.5' id 'org.graalvm.buildtools.native' version '0.10.2' id 'org.cyclonedx.bom' version '1.8.2' diff --git a/pom.xml b/pom.xml index ee256750..276d501f 100644 --- a/pom.xml +++ b/pom.xml @@ -10,8 +10,8 @@ org.springframework.boot spring-boot-starter-parent - 3.3.3 - + 3.3.4 + petclinic @@ -271,7 +271,7 @@ - + org.cyclonedx cyclonedx-maven-plugin From d90e284621d60378e2578afe2ec26084bc2237c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Tue, 24 Sep 2024 16:21:15 +0200 Subject: [PATCH 03/32] Disable JPA Open Session In View With the current codebase, it does not seems to be needed anymore, so we disable it to provide better performances. --- src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 5d3eeed3..6ed98565 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -8,7 +8,7 @@ spring.thymeleaf.mode=HTML # JPA spring.jpa.hibernate.ddl-auto=none -spring.jpa.open-in-view=true +spring.jpa.open-in-view=false # Internationalization spring.messages.basename=messages/messages From 2daa3993ee8dce8ec72cf96bc5ef7aee6e36f8fb Mon Sep 17 00:00:00 2001 From: ghost Date: Tue, 24 Sep 2024 04:44:04 +0200 Subject: [PATCH 04/32] Removed unused imports Made some variables as final --- .../samples/petclinic/PetClinicApplication.java | 4 ---- .../org/springframework/samples/petclinic/owner/Owner.java | 2 +- .../samples/petclinic/owner/OwnerController.java | 2 +- .../java/org/springframework/samples/petclinic/owner/Pet.java | 2 +- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java b/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java index 6299a76f..ac6e1503 100644 --- a/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java +++ b/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java @@ -18,11 +18,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ImportRuntimeHints; -import org.springframework.web.servlet.i18n.SessionLocaleResolver; - -import java.util.Locale; /** * PetClinic Spring Boot Application. 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 6a3f093d..f8f2871d 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/Owner.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/Owner.java @@ -62,7 +62,7 @@ public class Owner extends Person { @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinColumn(name = "owner_id") @OrderBy("name") - private List pets = new ArrayList<>(); + private final List pets = new ArrayList<>(); public String getAddress() { return this.address; 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 848d4f08..75512893 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java @@ -17,7 +17,7 @@ import java.util.List; import java.util.Map; -import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; diff --git a/src/main/java/org/springframework/samples/petclinic/owner/Pet.java b/src/main/java/org/springframework/samples/petclinic/owner/Pet.java index 0b0c08ac..2e5ea948 100755 --- a/src/main/java/org/springframework/samples/petclinic/owner/Pet.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/Pet.java @@ -55,7 +55,7 @@ public class Pet extends NamedEntity { @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinColumn(name = "pet_id") @OrderBy("visit_date ASC") - private Set visits = new LinkedHashSet<>(); + private final Set visits = new LinkedHashSet<>(); public void setBirthDate(LocalDate birthDate) { this.birthDate = birthDate; From 6fffe61b93dcfd040fdc83856fd6ec4d7781546d Mon Sep 17 00:00:00 2001 From: Patrick Baumgartner Date: Sat, 28 Sep 2024 08:58:17 +0200 Subject: [PATCH 05/32] Updated db containers, and dependencies --- build.gradle | 14 ++++++++------ docker-compose.yml | 4 ++-- pom.xml | 6 +++--- readme.md | 4 ++-- .../samples/petclinic/MySqlIntegrationTests.java | 2 +- .../samples/petclinic/MysqlTestApplication.java | 2 +- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/build.gradle b/build.gradle index aa7c5124..21baa9a2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,10 @@ plugins { id 'java' id 'org.springframework.boot' version '3.3.4' - id 'io.spring.dependency-management' version '1.1.5' - id 'org.graalvm.buildtools.native' version '0.10.2' - id 'org.cyclonedx.bom' version '1.8.2' - id 'io.spring.javaformat' version '0.0.41' + id 'io.spring.dependency-management' version '1.1.6' + id 'org.graalvm.buildtools.native' version '0.10.3' + id 'org.cyclonedx.bom' version '1.10.0' + id 'io.spring.javaformat' version '0.0.43' id "io.spring.nohttp" version "0.0.11" } @@ -25,6 +25,8 @@ repositories { mavenCentral() } +ext.checkstyleVersion = "10.18.1" +ext.springJavaformatCheckstyleVersion = "0.0.43" ext.webjarsFontawesomeVersion = "4.7.0" ext.webjarsBootstrapVersion = "5.3.3" @@ -49,8 +51,8 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-docker-compose' testImplementation 'org.testcontainers:junit-jupiter' testImplementation 'org.testcontainers:mysql' - checkstyle 'io.spring.javaformat:spring-javaformat-checkstyle:0.0.41' - checkstyle 'com.puppycrawl.tools:checkstyle:10.16.0' + checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${springJavaformatCheckstyleVersion}" + checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}" } tasks.named('test') { diff --git a/docker-compose.yml b/docker-compose.yml index aaebf7ca..7d41b57d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: mysql: - image: mysql:8.4 + image: mysql:9.0 ports: - "3306:3306" environment: @@ -14,7 +14,7 @@ services: profiles: - mysql postgres: - image: postgres:16.3 + image: postgres:17.0 ports: - "5432:5432" environment: diff --git a/pom.xml b/pom.xml index 276d501f..e3276816 100644 --- a/pom.xml +++ b/pom.xml @@ -28,13 +28,13 @@ 5.3.3 4.7.0 - 10.16.0 + 10.18.1 0.8.12 0.2.29 1.0.0 - 3.3.1 + 3.5.0 0.0.11 - 0.0.41 + 0.0.43 diff --git a/readme.md b/readme.md index 9dea4591..43d6bab4 100644 --- a/readme.md +++ b/readme.md @@ -52,13 +52,13 @@ A similar setup is provided for MySQL and PostgreSQL if a persistent database co You can start MySQL or PostgreSQL locally with whatever installer works for your OS or use docker: ```bash -docker run -e MYSQL_USER=petclinic -e MYSQL_PASSWORD=petclinic -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=petclinic -p 3306:3306 mysql:8.4 +docker run -e MYSQL_USER=petclinic -e MYSQL_PASSWORD=petclinic -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=petclinic -p 3306:3306 mysql:9.0 ``` or ```bash -docker run -e POSTGRES_USER=petclinic -e POSTGRES_PASSWORD=petclinic -e POSTGRES_DB=petclinic -p 5432:5432 postgres:16.3 +docker run -e POSTGRES_USER=petclinic -e POSTGRES_PASSWORD=petclinic -e POSTGRES_DB=petclinic -p 5432:5432 postgres:17.0 ``` Further documentation is provided for [MySQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt) diff --git a/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java index 08d0d712..300ac359 100644 --- a/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java @@ -46,7 +46,7 @@ class MySqlIntegrationTests { @ServiceConnection @Container - static MySQLContainer container = new MySQLContainer<>("mysql:8.4"); + static MySQLContainer container = new MySQLContainer<>("mysql:9.0"); @LocalServerPort int port; diff --git a/src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java b/src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java index 3c582d67..96b71ecc 100644 --- a/src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java +++ b/src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java @@ -36,7 +36,7 @@ public class MysqlTestApplication { @Profile("mysql") @Bean static MySQLContainer container() { - return new MySQLContainer<>("mysql:8.4"); + return new MySQLContainer<>("mysql:9.0"); } public static void main(String[] args) { From 608e2b614233106daf7e2b8775d977bf2da34d03 Mon Sep 17 00:00:00 2001 From: Patrick Baumgartner Date: Sat, 28 Sep 2024 09:38:06 +0200 Subject: [PATCH 06/32] Update maven and gradle wrapper --- .gitignore | 50 ++++++++++++++++++----- .mvn/wrapper/maven-wrapper.properties | 18 +++++++- gradle/wrapper/gradle-wrapper.jar | Bin 43453 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 7 +++- gradlew.bat | 2 + 6 files changed, 65 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index af0cb9bb..c92e0b4f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,47 @@ -target/* -bin/* -build/* -.gradle/* -.settings/* +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.attach_pid* +.apt_generated .classpath -.project .factorypath -.attach_pid* +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### .idea +*.iws *.iml -/target -.sts4-cache/ -.vscode +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### CSS ### _site/ *.css !petclinic.css diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index ce65fef7..654af46a 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,3 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. wrapperVersion=3.3.2 distributionType=only-script -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136f3d4ba8a0da8d277868979cfbc8ad796..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch delta 12612 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 HJ4oML2Lo<@i8BwbL^1~GkG`E7C$SEa_ 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b82aa23a..df97d72b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a42..f5feea6d 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 25da30db..9d21a218 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## From f8001e0addac3e8c90eea87815885c1e40519544 Mon Sep 17 00:00:00 2001 From: Patrick Baumgartner Date: Sat, 28 Sep 2024 10:04:43 +0200 Subject: [PATCH 07/32] Change chmod like other files --- .../java/org/springframework/samples/petclinic/owner/Pet.java | 0 .../java/org/springframework/samples/petclinic/owner/Visit.java | 0 .../samples/petclinic/system/CacheConfiguration.java | 0 src/main/resources/templates/fragments/layout.html | 0 .../samples/petclinic/owner/PetControllerTests.java | 0 5 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/main/java/org/springframework/samples/petclinic/owner/Pet.java mode change 100755 => 100644 src/main/java/org/springframework/samples/petclinic/owner/Visit.java mode change 100755 => 100644 src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java mode change 100755 => 100644 src/main/resources/templates/fragments/layout.html mode change 100755 => 100644 src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java diff --git a/src/main/java/org/springframework/samples/petclinic/owner/Pet.java b/src/main/java/org/springframework/samples/petclinic/owner/Pet.java old mode 100755 new mode 100644 diff --git a/src/main/java/org/springframework/samples/petclinic/owner/Visit.java b/src/main/java/org/springframework/samples/petclinic/owner/Visit.java old mode 100755 new mode 100644 diff --git a/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java b/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java old mode 100755 new mode 100644 diff --git a/src/main/resources/templates/fragments/layout.html b/src/main/resources/templates/fragments/layout.html old mode 100755 new mode 100644 diff --git a/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java b/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java old mode 100755 new mode 100644 From fc442120ce59aa8b5cbc1391510b430b9edd30d8 Mon Sep 17 00:00:00 2001 From: Guilherme Soares Date: Mon, 30 Sep 2024 20:14:33 +0100 Subject: [PATCH 08/32] fix(jmx): strip spaces before path --- src/test/jmeter/petclinic_test_plan.jmx | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/test/jmeter/petclinic_test_plan.jmx b/src/test/jmeter/petclinic_test_plan.jmx index b5c18cb5..aeb3bd2e 100644 --- a/src/test/jmeter/petclinic_test_plan.jmx +++ b/src/test/jmeter/petclinic_test_plan.jmx @@ -156,8 +156,7 @@ - - ${CONTEXT_WEB}/webjars/bootstrap/5.3.3/dist/js/bootstrap.bundle.min.js + ${CONTEXT_WEB}/webjars/bootstrap/5.3.3/dist/js/bootstrap.bundle.min.js GET true false @@ -420,8 +419,7 @@ - - ${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new + ${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new GET true false @@ -458,8 +456,7 @@ - - ${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new + ${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new POST true false @@ -540,4 +537,4 @@ - \ No newline at end of file + From ae1bb8228c66ae885eb8022ced9bf2d6ad839e92 Mon Sep 17 00:00:00 2001 From: Mousa Al Bateh Date: Thu, 3 Oct 2024 20:06:42 +0200 Subject: [PATCH 09/32] Minor code changes are the following: -Removed unused variables from few files. -Added null assertions in some tests. -Removed unnecessary throw exceptions. --- .../springframework/samples/petclinic/vet/Vet.java | 4 ---- .../samples/petclinic/MySqlIntegrationTests.java | 2 +- .../samples/petclinic/PetClinicIntegrationTests.java | 2 +- .../samples/petclinic/PostgresIntegrationTests.java | 12 +++++++++++- .../petclinic/owner/PetTypeFormatterTests.java | 2 +- .../petclinic/service/ClinicServiceTests.java | 2 -- .../petclinic/system/CrashControllerTests.java | 2 +- .../samples/petclinic/vet/VetTests.java | 1 + 8 files changed, 16 insertions(+), 11 deletions(-) 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 7a70155c..d8a1fc84 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/Vet.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/Vet.java @@ -57,10 +57,6 @@ protected Set getSpecialtiesInternal() { return this.specialties; } - protected void setSpecialtiesInternal(Set specialties) { - this.specialties = specialties; - } - @XmlElement public List getSpecialties() { List sortedSpecs = new ArrayList<>(getSpecialtiesInternal()); diff --git a/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java index 300ac359..31231d79 100644 --- a/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java @@ -58,7 +58,7 @@ class MySqlIntegrationTests { private RestTemplateBuilder builder; @Test - void testFindAll() throws Exception { + void testFindAll() { vets.findAll(); vets.findAll(); // served from cache } diff --git a/src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java index 6472a121..6d982069 100644 --- a/src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java @@ -44,7 +44,7 @@ public class PetClinicIntegrationTests { private RestTemplateBuilder builder; @Test - void testFindAll() throws Exception { + void testFindAll() { vets.findAll(); vets.findAll(); // served from cache } diff --git a/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java index 18945a57..51908132 100644 --- a/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java @@ -17,6 +17,7 @@ package org.springframework.samples.petclinic; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assumptions.assumeTrue; import java.util.Arrays; @@ -114,7 +115,16 @@ public void printProperties() { Arrays.sort(names); for (String name : names) { String resolved = environment.getProperty(name); - String value = source.getProperty(name).toString(); + + assertNotNull(resolved, "resolved environment property: " + name + " is null."); + + Object sourceProperty = source.getProperty(name); + + assertNotNull(sourceProperty, "source property was expecting an object but is null."); + + assertNotNull(sourceProperty.toString(), "source property toString() returned null."); + + String value = sourceProperty.toString(); if (resolved.equals(value)) { log.info(name + "=" + resolved); } 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 dabade7b..0295b478 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/PetTypeFormatterTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/PetTypeFormatterTests.java @@ -68,7 +68,7 @@ void shouldParse() throws ParseException { } @Test - void shouldThrowParseException() throws ParseException { + void shouldThrowParseException() { given(this.pets.findPetTypes()).willReturn(makePetTypes()); Assertions.assertThrows(ParseException.class, () -> { petTypeFormatter.parse("Fish", Locale.ENGLISH); 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 2cbf7af2..2f3d00ce 100644 --- a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java +++ b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java @@ -205,8 +205,6 @@ void shouldAddNewVisitForPet() { owner6.addVisit(pet7.getId(), visit); this.owners.save(owner6); - owner6 = this.owners.findById(6); - assertThat(pet7.getVisits()) // .hasSize(found + 1) // .allMatch(value -> value.getId() != null); 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 09773aec..cc2ad674 100644 --- a/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java @@ -30,7 +30,7 @@ // luck ((plain(st) UNIT test)! :) class CrashControllerTests { - CrashController testee = new CrashController(); + final CrashController testee = new CrashController(); @Test void testTriggerException() { diff --git a/src/test/java/org/springframework/samples/petclinic/vet/VetTests.java b/src/test/java/org/springframework/samples/petclinic/vet/VetTests.java index b29c6d10..ec1aacf5 100644 --- a/src/test/java/org/springframework/samples/petclinic/vet/VetTests.java +++ b/src/test/java/org/springframework/samples/petclinic/vet/VetTests.java @@ -18,6 +18,7 @@ import org.junit.jupiter.api.Test; import org.springframework.util.SerializationUtils; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.assertj.core.api.Assertions.assertThat; /** From 62dbfa8e9ae6486833a9b03ff63ba330349ae48f Mon Sep 17 00:00:00 2001 From: Antoine Rey Date: Wed, 9 Oct 2024 22:41:48 +0200 Subject: [PATCH 10/32] Remove the unnecessary includeFilters --- .../samples/petclinic/service/ClinicServiceTests.java | 4 +--- 1 file changed, 1 insertion(+), 3 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 2f3d00ce..92a70c38 100644 --- a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java +++ b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java @@ -26,7 +26,6 @@ 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.Owner; @@ -36,7 +35,6 @@ import org.springframework.samples.petclinic.owner.Visit; import org.springframework.samples.petclinic.vet.Vet; import org.springframework.samples.petclinic.vet.VetRepository; -import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** @@ -67,7 +65,7 @@ * @author Michael Isvy * @author Dave Syer */ -@DataJpaTest(includeFilters = @ComponentScan.Filter(Service.class)) +@DataJpaTest // Ensure that if the mysql profile is active we connect to the real database: @AutoConfigureTestDatabase(replace = Replace.NONE) // @TestPropertySource("/application-postgres.properties") From 912de1648e32012706eb8623af1e42f334d7964a Mon Sep 17 00:00:00 2001 From: ruabooe Date: Sun, 20 Oct 2024 13:14:47 +0300 Subject: [PATCH 11/32] feat: add russian --- src/main/resources/messages/messages_ru.properties | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/resources/messages/messages_ru.properties diff --git a/src/main/resources/messages/messages_ru.properties b/src/main/resources/messages/messages_ru.properties new file mode 100644 index 00000000..7e8d54d2 --- /dev/null +++ b/src/main/resources/messages/messages_ru.properties @@ -0,0 +1,9 @@ +welcome=Добро пожаловать +required=необходимо +notFound=не найдено +duplicate=уже используется +nonNumeric=должно быть все числовое значение +duplicateFormSubmission=Дублирование формы не допускается +typeMismatch.date=неправильная даные +typeMismatch.birthDate=неправильная дата + From bbb237928fa9ef24a09ce57ad01075e4b7eb78c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Bertholino?= Date: Sat, 19 Oct 2024 16:18:34 -0300 Subject: [PATCH 12/32] feat: Adds support for the Portuguese language. --- src/main/resources/messages/messages_pt.properties | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/resources/messages/messages_pt.properties diff --git a/src/main/resources/messages/messages_pt.properties b/src/main/resources/messages/messages_pt.properties new file mode 100644 index 00000000..501c12dd --- /dev/null +++ b/src/main/resources/messages/messages_pt.properties @@ -0,0 +1,8 @@ +welcome=Bem-vindo +required= necessrio +notFound=No foi encontrado +duplicate=J est em uso +nonNumeric=Deve ser tudo numrico +duplicateFormSubmission=O envio duplicado de formulrio no permitido +typeMismatch.date=Data invlida +typeMismatch.birthDate=Data de nascimento invlida From 90bbb98ea6068f13517207a3165f89f6dcbc0530 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Tue, 5 Nov 2024 08:24:50 +0000 Subject: [PATCH 13/32] Update readme to refer to test main classes --- readme.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 43d6bab4..dea07585 100644 --- a/readme.md +++ b/readme.md @@ -29,6 +29,14 @@ Or you can run it from Maven directly using the Spring Boot Maven plugin. If you > NOTE: If you prefer to use Gradle, you can build the app using `./gradlew build` and look for the jar file in `build/libs`. +At development time it is recommended to use the integration tests (whch have their own `main` method) to run the application. This will allow you to make changes to the content and see the changes immediately. You can run the integration tests from your IDE or using the command line. Because there are multiple main classes for different database configurations, you have to choose one, e.g: + +```bash +./mvnw spring-boot:test-run -Dspring-boot.run.main-class=org.springframework.samples.petclinic.PetClinicIntegrationTests +``` + +Or you can run it in your IDE directly from the test class of your choice. + ## Building a Container There is no `Dockerfile` in this project. You can build a container image (if you have a docker daemon) using the Spring Boot build plugin: @@ -44,7 +52,8 @@ Our issue tracker is available [here](https://github.com/spring-projects/spring- ## Database configuration In its default configuration, Petclinic uses an in-memory database (H2) which -gets populated at startup with data. The h2 console is exposed at `http://localhost:8080/h2-console`, +gets populated at startup with data. If you launch the app from the integration tests, the h2 console is automatically +exposed at `http://localhost:8080/h2-console`, and it is possible to inspect the content of the database using the `jdbc:h2:mem:` URL. The UUID is printed at startup to the console. A similar setup is provided for MySQL and PostgreSQL if a persistent database configuration is needed. Note that whenever the database type changes, the app needs to run with a different profile: `spring.profiles.active=mysql` for MySQL or `spring.profiles.active=postgres` for PostgreSQL. See the [Spring Boot documentation](https://docs.spring.io/spring-boot/how-to/properties-and-configuration.html#howto.properties-and-configuration.set-active-spring-profiles) for more detail on how to set the active profile. From dff45cf70cd578d0dea6fd38f9f28327b74b5c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Bertholino?= Date: Sun, 20 Oct 2024 15:20:52 -0300 Subject: [PATCH 14/32] fix: Temporarily removing accentuation from messages. --- src/main/resources/messages/messages_pt.properties | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/resources/messages/messages_pt.properties b/src/main/resources/messages/messages_pt.properties index 501c12dd..e9bc35a3 100644 --- a/src/main/resources/messages/messages_pt.properties +++ b/src/main/resources/messages/messages_pt.properties @@ -1,8 +1,8 @@ welcome=Bem-vindo -required= necessrio -notFound=No foi encontrado -duplicate=J est em uso -nonNumeric=Deve ser tudo numrico -duplicateFormSubmission=O envio duplicado de formulrio no permitido -typeMismatch.date=Data invlida -typeMismatch.birthDate=Data de nascimento invlida +required=E necessario +notFound=Nao foi encontrado +duplicate=Ja esta em uso +nonNumeric=Deve ser tudo numerico +duplicateFormSubmission=O envio duplicado de formulario nao e permitido +typeMismatch.date=Data invalida +typeMismatch.birthDate=Data de nascimento invalida From a50bfb65bb9ead5b8d6342bab3db9b2eb77f3f33 Mon Sep 17 00:00:00 2001 From: Patrick Baumgartner Date: Tue, 5 Nov 2024 09:45:11 +0100 Subject: [PATCH 15/32] Update Spring Boot release, adding Gradle build and cleanup - Update Spring Boot release, Checkstyle, Mysql. - Formatting pom.xml with sortpom-maven-plugin. - Rename README to standard file name. - Adding GitHub Action for Gradle. --- .github/workflows/gradle-build.yml | 31 +++++++ .github/workflows/maven-build.yml | 4 +- .gitignore | 4 + readme.md => README.md | 15 +--- build.gradle | 4 +- docker-compose.yml | 2 +- pom.xml | 86 +++++++++---------- .../petclinic/MySqlIntegrationTests.java | 2 +- .../petclinic/MysqlTestApplication.java | 2 +- .../samples/petclinic/vet/VetTests.java | 1 - 10 files changed, 85 insertions(+), 66 deletions(-) create mode 100644 .github/workflows/gradle-build.yml rename readme.md => README.md (92%) diff --git a/.github/workflows/gradle-build.yml b/.github/workflows/gradle-build.yml new file mode 100644 index 00000000..c24c121b --- /dev/null +++ b/.github/workflows/gradle-build.yml @@ -0,0 +1,31 @@ +# This workflow will build a Java project with Gradle, and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-java-with-gradle + +name: Java CI with Gradle + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + java: [ '17' ] + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK ${{matrix.java}} + uses: actions/setup-java@v4 + with: + java-version: ${{matrix.java}} + distribution: 'adopt' + cache: maven + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Build with Gradle + run: ./gradlew build diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index 4718a6ce..a1ec4dab 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -1,5 +1,5 @@ # 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 +# For more information see: https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-java-with-maven name: Java CI with Maven @@ -26,4 +26,4 @@ jobs: distribution: 'adopt' cache: maven - name: Build with Maven Wrapper - run: ./mvnw -B package + run: ./mvnw -B verify diff --git a/.gitignore b/.gitignore index c92e0b4f..d2767ad2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ HELP.md +pom.xml.bak target/ !.mvn/wrapper/maven-wrapper.jar !**/src/main/**/target/ @@ -41,6 +42,9 @@ out/ ### VS Code ### .vscode/ +### SDK Man ### +.sdkmanrc + ### CSS ### _site/ *.css diff --git a/readme.md b/README.md similarity index 92% rename from readme.md rename to README.md index dea07585..bb3fe977 100644 --- a/readme.md +++ b/README.md @@ -1,4 +1,4 @@ -# 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) +# 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)[![Build Status](https://github.com/spring-projects/spring-petclinic/actions/workflows/gradle-build.yml/badge.svg)](https://github.com/spring-projects/spring-petclinic/actions/workflows/gradle-build.yml) [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/spring-projects/spring-petclinic) [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=7517918) @@ -29,14 +29,6 @@ Or you can run it from Maven directly using the Spring Boot Maven plugin. If you > NOTE: If you prefer to use Gradle, you can build the app using `./gradlew build` and look for the jar file in `build/libs`. -At development time it is recommended to use the integration tests (whch have their own `main` method) to run the application. This will allow you to make changes to the content and see the changes immediately. You can run the integration tests from your IDE or using the command line. Because there are multiple main classes for different database configurations, you have to choose one, e.g: - -```bash -./mvnw spring-boot:test-run -Dspring-boot.run.main-class=org.springframework.samples.petclinic.PetClinicIntegrationTests -``` - -Or you can run it in your IDE directly from the test class of your choice. - ## Building a Container There is no `Dockerfile` in this project. You can build a container image (if you have a docker daemon) using the Spring Boot build plugin: @@ -52,8 +44,7 @@ Our issue tracker is available [here](https://github.com/spring-projects/spring- ## Database configuration In its default configuration, Petclinic uses an in-memory database (H2) which -gets populated at startup with data. If you launch the app from the integration tests, the h2 console is automatically -exposed at `http://localhost:8080/h2-console`, +gets populated at startup with data. The h2 console is exposed at `http://localhost:8080/h2-console`, and it is possible to inspect the content of the database using the `jdbc:h2:mem:` URL. The UUID is printed at startup to the console. A similar setup is provided for MySQL and PostgreSQL if a persistent database configuration is needed. Note that whenever the database type changes, the app needs to run with a different profile: `spring.profiles.active=mysql` for MySQL or `spring.profiles.active=postgres` for PostgreSQL. See the [Spring Boot documentation](https://docs.spring.io/spring-boot/how-to/properties-and-configuration.html#howto.properties-and-configuration.set-active-spring-profiles) for more detail on how to set the active profile. @@ -61,7 +52,7 @@ A similar setup is provided for MySQL and PostgreSQL if a persistent database co You can start MySQL or PostgreSQL locally with whatever installer works for your OS or use docker: ```bash -docker run -e MYSQL_USER=petclinic -e MYSQL_PASSWORD=petclinic -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=petclinic -p 3306:3306 mysql:9.0 +docker run -e MYSQL_USER=petclinic -e MYSQL_PASSWORD=petclinic -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=petclinic -p 3306:3306 mysql:9.1 ``` or diff --git a/build.gradle b/build.gradle index 21baa9a2..a5175dfb 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '3.3.4' + id 'org.springframework.boot' version '3.3.5' id 'io.spring.dependency-management' version '1.1.6' id 'org.graalvm.buildtools.native' version '0.10.3' id 'org.cyclonedx.bom' version '1.10.0' @@ -25,7 +25,7 @@ repositories { mavenCentral() } -ext.checkstyleVersion = "10.18.1" +ext.checkstyleVersion = "10.20.0" ext.springJavaformatCheckstyleVersion = "0.0.43" ext.webjarsFontawesomeVersion = "4.7.0" ext.webjarsBootstrapVersion = "5.3.3" diff --git a/docker-compose.yml b/docker-compose.yml index 7d41b57d..8fdaacbc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: mysql: - image: mysql:9.0 + image: mysql:9.1 ports: - "3306:3306" environment: diff --git a/pom.xml b/pom.xml index e3276816..3d7f28f6 100644 --- a/pom.xml +++ b/pom.xml @@ -1,18 +1,18 @@ - + 4.0.0 - org.springframework.samples - spring-petclinic - 3.3.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 3.3.4 - + 3.3.5 + + + org.springframework.samples + spring-petclinic + 3.3.0-SNAPSHOT + petclinic @@ -28,11 +28,11 @@ 5.3.3 4.7.0 - 10.18.1 + 10.20.0 0.8.12 0.2.29 1.0.0 - 3.5.0 + 3.6.0 0.0.11 0.0.43 @@ -171,10 +171,10 @@ ${spring-format.version} - validate validate + validate @@ -197,19 +197,17 @@ nohttp-checkstyle-validation + + check + validate src/checkstyle/nohttp-checkstyle.xml ${basedir} **/* **/.git/**/*,**/.idea/**/*,**/target/**/,**/.flattened-pom.xml,**/*.class - - config_loc=${basedir}/src/checkstyle/ - + config_loc=${basedir}/src/checkstyle/ - - check - @@ -250,10 +248,10 @@ report - prepare-package report + prepare-package @@ -271,14 +269,13 @@ - + org.cyclonedx cyclonedx-maven-plugin - Apache License, Version 2.0 @@ -288,39 +285,38 @@ - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot true + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot - spring-milestones - Spring Milestones - https://repo.spring.io/milestone false + spring-milestones + Spring Milestones + https://repo.spring.io/milestone - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot true + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot - spring-milestones - Spring Milestones - https://repo.spring.io/milestone false + spring-milestones + Spring Milestones + https://repo.spring.io/milestone @@ -335,11 +331,11 @@ unpack - - generate-resources unpack + + generate-resources @@ -358,21 +354,20 @@ com.gitlab.haynes libsass-maven-plugin ${libsass.version} + + ${basedir}/src/main/scss/ + ${basedir}/src/main/resources/static/resources/css/ + ${project.build.directory}/webjars/META-INF/resources/webjars/bootstrap/${webjars-bootstrap.version}/scss/ + - generate-resources compile + generate-resources - - ${basedir}/src/main/scss/ - ${basedir}/src/main/resources/static/resources/css/ - - ${project.build.directory}/webjars/META-INF/resources/webjars/bootstrap/${webjars-bootstrap.version}/scss/ - @@ -406,7 +401,7 @@ - + @@ -419,7 +414,7 @@ - + @@ -432,7 +427,7 @@ - + @@ -444,5 +439,4 @@ - diff --git a/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java index 31231d79..d67e449d 100644 --- a/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java @@ -46,7 +46,7 @@ class MySqlIntegrationTests { @ServiceConnection @Container - static MySQLContainer container = new MySQLContainer<>("mysql:9.0"); + static MySQLContainer container = new MySQLContainer<>("mysql:9.1"); @LocalServerPort int port; diff --git a/src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java b/src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java index 96b71ecc..8c7560a1 100644 --- a/src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java +++ b/src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java @@ -36,7 +36,7 @@ public class MysqlTestApplication { @Profile("mysql") @Bean static MySQLContainer container() { - return new MySQLContainer<>("mysql:9.0"); + return new MySQLContainer<>("mysql:9.1"); } public static void main(String[] args) { diff --git a/src/test/java/org/springframework/samples/petclinic/vet/VetTests.java b/src/test/java/org/springframework/samples/petclinic/vet/VetTests.java index ec1aacf5..b29c6d10 100644 --- a/src/test/java/org/springframework/samples/petclinic/vet/VetTests.java +++ b/src/test/java/org/springframework/samples/petclinic/vet/VetTests.java @@ -18,7 +18,6 @@ import org.junit.jupiter.api.Test; import org.springframework.util.SerializationUtils; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.assertj.core.api.Assertions.assertThat; /** From fdc40a7048843bc900d5a1b56a60350dffc93c9b Mon Sep 17 00:00:00 2001 From: YiXuan Ding <1328032567@qq.com> Date: Tue, 5 Nov 2024 15:27:56 +0800 Subject: [PATCH 16/32] Fix harmless bugs. - : use `equals` to replace `==` to compare `Integer` variable. - : remove redundant 'toLowerCase()' method and simplify pet lookup logic. - : rewrite method `getName()` comments. --- .../springframework/samples/petclinic/owner/Owner.java | 8 ++++---- .../samples/petclinic/owner/PetController.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) 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 f8f2871d..612d89d2 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/Owner.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/Owner.java @@ -101,7 +101,7 @@ public void addPet(Pet pet) { /** * Return the Pet with the given name, or null if none found for this Owner. * @param name to test - * @return a pet if pet name is already in use + * @return the Pet with the given name, or null if no such Pet exists for this Owner */ public Pet getPet(String name) { return getPet(name, false); @@ -110,7 +110,7 @@ public Pet getPet(String name) { /** * Return the Pet with the given id, or null if none found for this Owner. * @param id to test - * @return a pet if pet id is already in use + * @return the Pet with the given id, or null if no such Pet exists for this Owner */ public Pet getPet(Integer id) { for (Pet pet : getPets()) { @@ -127,10 +127,10 @@ public Pet getPet(Integer id) { /** * Return the Pet with the given name, or null if none found for this Owner. * @param name to test - * @return a pet if pet name is already in use + * @param ignoreNew whether to ignore new pets (pets that are not saved yet) + * @return the Pet with the given name, or null if no such Pet exists for this Owner */ public Pet getPet(String name, boolean ignoreNew) { - name = name.toLowerCase(); for (Pet pet : getPets()) { String compName = pet.getName(); if (compName != null && compName.equalsIgnoreCase(name)) { diff --git a/src/main/java/org/springframework/samples/petclinic/owner/PetController.java b/src/main/java/org/springframework/samples/petclinic/owner/PetController.java index 781fb580..6c37f057 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/PetController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/PetController.java @@ -138,7 +138,7 @@ public String processUpdateForm(@Valid Pet pet, BindingResult result, Owner owne // checking if the pet name already exist for the owner if (StringUtils.hasText(petName)) { Pet existingPet = owner.getPet(petName.toLowerCase(), false); - if (existingPet != null && existingPet.getId() != pet.getId()) { + if (existingPet != null && !existingPet.getId().equals(pet.getId())) { result.rejectValue("name", "duplicate", "already exists"); } } From 14af47d4e5b4e12e0ca28e5c2c8437528546c25d Mon Sep 17 00:00:00 2001 From: YiXuan Ding <1328032567@qq.com> Date: Wed, 6 Nov 2024 20:09:08 +0800 Subject: [PATCH 17/32] Refactor: - : delete logic `add owner to model` because of the comment `@ModelAttribute("owner")`. - : add logical judgment in ordet to avoid `owner` from `form` and `ownerId` from `url` mismatch. --- .../petclinic/owner/OwnerController.java | 15 +++++---- .../petclinic/owner/OwnerControllerTests.java | 33 +++++++++++++++---- 2 files changed, 34 insertions(+), 14 deletions(-) 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 75512893..490b1028 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java @@ -16,7 +16,6 @@ package org.springframework.samples.petclinic.owner; import java.util.List; -import java.util.Map; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -64,9 +63,7 @@ public Owner findOwner(@PathVariable(name = "ownerId", required = false) Integer } @GetMapping("/owners/new") - public String initCreationForm(Map model) { - Owner owner = new Owner(); - model.put("owner", owner); + public String initCreationForm() { return VIEWS_OWNER_CREATE_OR_UPDATE_FORM; } @@ -129,9 +126,7 @@ private Page findPaginatedForOwnersLastName(int page, String lastname) { } @GetMapping("/owners/{ownerId}/edit") - public String initUpdateOwnerForm(@PathVariable("ownerId") int ownerId, Model model) { - Owner owner = this.owners.findById(ownerId); - model.addAttribute(owner); + public String initUpdateOwnerForm() { return VIEWS_OWNER_CREATE_OR_UPDATE_FORM; } @@ -143,6 +138,12 @@ public String processUpdateOwnerForm(@Valid Owner owner, BindingResult result, @ return VIEWS_OWNER_CREATE_OR_UPDATE_FORM; } + if (owner.getId() != ownerId) { + result.rejectValue("id", "mismatch", "The owner ID in the form does not match the URL."); + redirectAttributes.addFlashAttribute("error", "Owner ID mismatch. Please try again."); + return "redirect:/owners/{ownerId}/edit"; + } + owner.setId(ownerId); this.owners.save(owner); redirectAttributes.addFlashAttribute("message", "Owner Values Updated"); 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 fc22fb01..f88c1f1d 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java @@ -20,7 +20,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledInNativeImage; -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; @@ -29,6 +28,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.test.context.aot.DisabledInAotMode; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import java.time.LocalDate; @@ -43,11 +43,10 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.when; 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} @@ -143,14 +142,14 @@ void testInitFindForm() throws Exception { @Test void testProcessFindFormSuccess() throws Exception { Page tasks = new PageImpl<>(Lists.newArrayList(george(), new Owner())); - Mockito.when(this.owners.findByLastName(anyString(), any(Pageable.class))).thenReturn(tasks); + 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())); - Mockito.when(this.owners.findByLastName(eq("Franklin"), any(Pageable.class))).thenReturn(tasks); + 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)); @@ -159,7 +158,7 @@ void testProcessFindFormByLastName() throws Exception { @Test void testProcessFindFormNoOwnersFound() throws Exception { Page tasks = new PageImpl<>(Lists.newArrayList()); - Mockito.when(this.owners.findByLastName(eq("Unknown Surname"), any(Pageable.class))).thenReturn(tasks); + 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")) @@ -229,4 +228,24 @@ void testShowOwner() throws Exception { .andExpect(view().name("owners/ownerDetails")); } + @Test + public void testProcessUpdateOwnerFormWithIdMismatch() throws Exception { + int pathOwnerId = 1; + + Owner owner = new Owner(); + owner.setId(2); + owner.setFirstName("John"); + owner.setLastName("Doe"); + owner.setAddress("Center Street"); + owner.setCity("New York"); + owner.setTelephone("0123456789"); + + when(owners.findById(pathOwnerId)).thenReturn(owner); + + mockMvc.perform(MockMvcRequestBuilders.post("/owners/{ownerId}/edit", pathOwnerId).flashAttr("owner", owner)) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/owners/" + pathOwnerId + "/edit")) + .andExpect(flash().attributeExists("error")); + } + } From 50866def72014963764577e1f737cdd6b9f9f691 Mon Sep 17 00:00:00 2001 From: YiXuan Ding <1328032567@qq.com> Date: Thu, 7 Nov 2024 17:37:12 +0800 Subject: [PATCH 18/32] Refactor the logic and add unit test -: add `@NotBlank` validation to pet's name. -: delete useless code and add unit test to check duplicate Pet name validation logic. -: add `Id` to pet in unit test. -: classify unit test. : adjust code format. --- .../samples/petclinic/model/NamedEntity.java | 2 + .../petclinic/owner/PetController.java | 19 +-- .../petclinic/owner/PetControllerTests.java | 132 ++++++++++++++---- 3 files changed, 114 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java b/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java index 7c2ccb2d..cf2ae364 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java +++ b/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java @@ -17,6 +17,7 @@ import jakarta.persistence.Column; import jakarta.persistence.MappedSuperclass; +import jakarta.validation.constraints.NotBlank; /** * Simple JavaBean domain object adds a name property to BaseEntity. Used as @@ -29,6 +30,7 @@ public class NamedEntity extends BaseEntity { @Column(name = "name") + @NotBlank private String name; public String getName() { diff --git a/src/main/java/org/springframework/samples/petclinic/owner/PetController.java b/src/main/java/org/springframework/samples/petclinic/owner/PetController.java index 6c37f057..cde8e59e 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/PetController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/PetController.java @@ -99,45 +99,41 @@ public String initCreationForm(Owner owner, ModelMap model) { } @PostMapping("/pets/new") - public String processCreationForm(Owner owner, @Valid Pet pet, BindingResult result, ModelMap model, + public String processCreationForm(Owner owner, @Valid Pet pet, BindingResult result, RedirectAttributes redirectAttributes) { - if (StringUtils.hasText(pet.getName()) && pet.isNew() && owner.getPet(pet.getName(), true) != null) { + + if (StringUtils.hasText(pet.getName()) && pet.isNew() && owner.getPet(pet.getName(), true) != null) result.rejectValue("name", "duplicate", "already exists"); - } LocalDate currentDate = LocalDate.now(); if (pet.getBirthDate() != null && pet.getBirthDate().isAfter(currentDate)) { result.rejectValue("birthDate", "typeMismatch.birthDate"); } - owner.addPet(pet); if (result.hasErrors()) { - model.put("pet", pet); return VIEWS_PETS_CREATE_OR_UPDATE_FORM; } + owner.addPet(pet); this.owners.save(owner); redirectAttributes.addFlashAttribute("message", "New Pet has been Added"); return "redirect:/owners/{ownerId}"; } @GetMapping("/pets/{petId}/edit") - public String initUpdateForm(Owner owner, @PathVariable("petId") int petId, ModelMap model, - RedirectAttributes redirectAttributes) { - Pet pet = owner.getPet(petId); - model.put("pet", pet); + public String initUpdateForm(Owner owner, @PathVariable("petId") int petId, RedirectAttributes redirectAttributes) { return VIEWS_PETS_CREATE_OR_UPDATE_FORM; } @PostMapping("/pets/{petId}/edit") - public String processUpdateForm(@Valid Pet pet, BindingResult result, Owner owner, ModelMap model, + public String processUpdateForm(Owner owner, @Valid Pet pet, BindingResult result, RedirectAttributes redirectAttributes) { String petName = pet.getName(); // checking if the pet name already exist for the owner if (StringUtils.hasText(petName)) { - Pet existingPet = owner.getPet(petName.toLowerCase(), false); + Pet existingPet = owner.getPet(petName, false); if (existingPet != null && !existingPet.getId().equals(pet.getId())) { result.rejectValue("name", "duplicate", "already exists"); } @@ -149,7 +145,6 @@ public String processUpdateForm(@Valid Pet pet, BindingResult result, Owner owne } if (result.hasErrors()) { - model.put("pet", pet); return VIEWS_PETS_CREATE_OR_UPDATE_FORM; } 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 73b83f9f..657df198 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java @@ -17,9 +17,12 @@ package org.springframework.samples.petclinic.owner; import org.assertj.core.util.Lists; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledInNativeImage; +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; @@ -28,6 +31,10 @@ import org.springframework.test.context.aot.DisabledInAotMode; import org.springframework.test.web.servlet.MockMvc; +import java.time.LocalDate; +import java.util.Optional; + +import static org.junit.Assert.assertNotNull; 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; @@ -62,10 +69,16 @@ void setup() { cat.setId(3); cat.setName("hamster"); given(this.owners.findPetTypes()).willReturn(Lists.newArrayList(cat)); + Owner owner = new Owner(); Pet pet = new Pet(); + Pet dog = new Pet(); owner.addPet(pet); + owner.addPet(dog); pet.setId(TEST_PET_ID); + dog.setId(TEST_PET_ID + 1); + pet.setName("petty"); + dog.setName("doggy"); given(this.owners.findById(TEST_OWNER_ID)).willReturn(owner); } @@ -87,25 +100,72 @@ void testProcessCreationFormSuccess() throws Exception { .andExpect(view().name("redirect:/owners/{ownerId}")); } - @Test - void testProcessCreationFormHasErrors() throws Exception { - mockMvc - .perform(post("/owners/{ownerId}/pets/new", TEST_OWNER_ID).param("name", "Betty") - .param("birthDate", "2015-02-12")) - .andExpect(model().attributeHasNoErrors("owner")) - .andExpect(model().attributeHasErrors("pet")) - .andExpect(model().attributeHasFieldErrors("pet", "type")) - .andExpect(model().attributeHasFieldErrorCode("pet", "type", "required")) - .andExpect(status().isOk()) - .andExpect(view().name("pets/createOrUpdatePetForm")); - } + @Nested + class ProcessCreationFormHasErrors { + + @Test + void testProcessCreationFormWithBlankName() throws Exception { + mockMvc + .perform(post("/owners/{ownerId}/pets/new", TEST_OWNER_ID).param("name", "\t \n") + .param("birthDate", "2015-02-12")) + .andExpect(model().attributeHasNoErrors("owner")) + .andExpect(model().attributeHasErrors("pet")) + .andExpect(model().attributeHasFieldErrors("pet", "name")) + .andExpect(model().attributeHasFieldErrorCode("pet", "name", "required")) + .andExpect(status().isOk()) + .andExpect(view().name("pets/createOrUpdatePetForm")); + } + + @Test + void testProcessCreationFormWithDuplicateName() throws Exception { + mockMvc + .perform(post("/owners/{ownerId}/pets/new", TEST_OWNER_ID).param("name", "petty") + .param("birthDate", "2015-02-12")) + .andExpect(model().attributeHasNoErrors("owner")) + .andExpect(model().attributeHasErrors("pet")) + .andExpect(model().attributeHasFieldErrors("pet", "name")) + .andExpect(model().attributeHasFieldErrorCode("pet", "name", "duplicate")) + .andExpect(status().isOk()) + .andExpect(view().name("pets/createOrUpdatePetForm")); + } + + @Test + void testProcessCreationFormWithMissingPetType() throws Exception { + mockMvc + .perform(post("/owners/{ownerId}/pets/new", TEST_OWNER_ID).param("name", "Betty") + .param("birthDate", "2015-02-12")) + .andExpect(model().attributeHasNoErrors("owner")) + .andExpect(model().attributeHasErrors("pet")) + .andExpect(model().attributeHasFieldErrors("pet", "type")) + .andExpect(model().attributeHasFieldErrorCode("pet", "type", "required")) + .andExpect(status().isOk()) + .andExpect(view().name("pets/createOrUpdatePetForm")); + } + + @Test + void testProcessCreationFormWithInvalidBirthDate() throws Exception { + LocalDate currentDate = LocalDate.now(); + String futureBirthDate = currentDate.plusMonths(1).toString(); + + mockMvc + .perform(post("/owners/{ownerId}/pets/new", TEST_OWNER_ID).param("name", "Betty") + .param("birthDate", futureBirthDate)) + .andExpect(model().attributeHasNoErrors("owner")) + .andExpect(model().attributeHasErrors("pet")) + .andExpect(model().attributeHasFieldErrors("pet", "birthDate")) + .andExpect(model().attributeHasFieldErrorCode("pet", "birthDate", "typeMismatch.birthDate")) + .andExpect(status().isOk()) + .andExpect(view().name("pets/createOrUpdatePetForm")); + } + + @Test + void testInitUpdateForm() throws Exception { + mockMvc.perform(get("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID)) + .andExpect(status().isOk()) + .andExpect(model().attributeExists("pet")) + .andExpect(view().name("pets/createOrUpdatePetForm")); + } - @Test - void testInitUpdateForm() throws Exception { - mockMvc.perform(get("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID)) - .andExpect(status().isOk()) - .andExpect(model().attributeExists("pet")) - .andExpect(view().name("pets/createOrUpdatePetForm")); } @Test @@ -118,15 +178,33 @@ void testProcessUpdateFormSuccess() throws Exception { .andExpect(view().name("redirect:/owners/{ownerId}")); } - @Test - void testProcessUpdateFormHasErrors() throws Exception { - mockMvc - .perform(post("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID).param("name", "Betty") - .param("birthDate", "2015/02/12")) - .andExpect(model().attributeHasNoErrors("owner")) - .andExpect(model().attributeHasErrors("pet")) - .andExpect(status().isOk()) - .andExpect(view().name("pets/createOrUpdatePetForm")); + @Nested + class ProcessUpdateFormHasErrors { + + @Test + void testProcessUpdateFormWithInvalidBirthDate() throws Exception { + mockMvc + .perform(post("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID).param("name", " ") + .param("birthDate", "2015/02/12")) + .andExpect(model().attributeHasNoErrors("owner")) + .andExpect(model().attributeHasErrors("pet")) + .andExpect(model().attributeHasFieldErrors("pet", "birthDate")) + .andExpect(model().attributeHasFieldErrorCode("pet", "birthDate", "typeMismatch")) + .andExpect(view().name("pets/createOrUpdatePetForm")); + } + + @Test + void testProcessUpdateFormWithBlankName() throws Exception { + mockMvc + .perform(post("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID).param("name", " ") + .param("birthDate", "2015-02-12")) + .andExpect(model().attributeHasNoErrors("owner")) + .andExpect(model().attributeHasErrors("pet")) + .andExpect(model().attributeHasFieldErrors("pet", "name")) + .andExpect(model().attributeHasFieldErrorCode("pet", "name", "required")) + .andExpect(view().name("pets/createOrUpdatePetForm")); + } + } } From a3026bddbb3ee267d64f3362554088a80e9398c7 Mon Sep 17 00:00:00 2001 From: Andrey Litvitski Date: Thu, 7 Nov 2024 17:33:58 +0300 Subject: [PATCH 19/32] Add Kubernetes support --- k8s/db.yml | 73 +++++++++++++++++++++++++++++++++++++++++++++++ k8s/petclinic.yml | 64 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 k8s/db.yml create mode 100644 k8s/petclinic.yml diff --git a/k8s/db.yml b/k8s/db.yml new file mode 100644 index 00000000..c230ddba --- /dev/null +++ b/k8s/db.yml @@ -0,0 +1,73 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: demo-db +type: servicebinding.io/postgresql +stringData: + type: "postgresql" + provider: "postgresql" + host: "demo-db" + port: "5432" + database: "petclinic" + username: "user" + password: "pass" + +--- +apiVersion: v1 +kind: Service +metadata: + name: demo-db +spec: + ports: + - port: 5432 + selector: + app: demo-db + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: demo-db + labels: + app: demo-db +spec: + selector: + matchLabels: + app: demo-db + template: + metadata: + labels: + app: demo-db + spec: + containers: + - image: postgres:17 + name: postgresql + env: + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: demo-db + key: username + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: demo-db + key: password + - name: POSTGRES_DB + valueFrom: + secretKeyRef: + name: demo-db + key: database + ports: + - containerPort: 5432 + name: postgresql + livenessProbe: + tcpSocket: + port: postgresql + readinessProbe: + tcpSocket: + port: postgresql + startupProbe: + tcpSocket: + port: postgresql diff --git a/k8s/petclinic.yml b/k8s/petclinic.yml new file mode 100644 index 00000000..a5677cd0 --- /dev/null +++ b/k8s/petclinic.yml @@ -0,0 +1,64 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: petclinic +spec: + type: NodePort + ports: + - port: 80 + targetPort: 8080 + selector: + app: petclinic + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: petclinic + labels: + app: petclinic +spec: + replicas: 1 + selector: + matchLabels: + app: petclinic + template: + metadata: + labels: + app: petclinic + spec: + containers: + - name: workload + image: dsyer/petclinic + env: + - name: SPRING_PROFILES_ACTIVE + value: postgres + - name: SERVICE_BINDING_ROOT + value: /bindings + - name: SPRING_APPLICATION_JSON + value: | + { + "management.endpoint.health.probes.add-additional-paths": true + } + ports: + - name: http + containerPort: 8080 + livenessProbe: + httpGet: + path: /livez + port: http + readinessProbe: + httpGet: + path: /readyz + port: http + volumeMounts: + - mountPath: /bindings/secret + name: binding + readOnly: true + volumes: + - name: binding + projected: + sources: + - secret: + name: demo-db From 668629d5bda4e6a87fb43425c7eb95ffa19b47c7 Mon Sep 17 00:00:00 2001 From: YiXuan Ding <1328032567@qq.com> Date: Fri, 8 Nov 2024 22:05:22 +0800 Subject: [PATCH 20/32] refactor `OwnerRepository`: -: use `JpaRepository` to replace `Repository` in `OwnerRepository` class. -: remove `save()` method. JpaRepository provides it by default. -: remove `@Query` because in `Owner` class, the `@OneToMany` annotiation achieved `fetch` in query. -: use `Optional` to recieve the result from `findById()`, and if is null, throw `IllegalArugmentExpection`. -: achieve the assert to judge return value in tests. -: add name to `@author` tag. --- .../samples/petclinic/model/NamedEntity.java | 1 + .../samples/petclinic/owner/Owner.java | 1 + .../petclinic/owner/OwnerController.java | 11 ++++- .../petclinic/owner/OwnerRepository.java | 30 ++++++++------ .../samples/petclinic/owner/Pet.java | 1 + .../petclinic/owner/PetController.java | 11 +++-- .../petclinic/owner/VisitController.java | 7 +++- .../petclinic/owner/OwnerControllerTests.java | 6 ++- .../petclinic/owner/PetControllerTests.java | 3 +- .../petclinic/owner/VisitControllerTests.java | 5 ++- .../petclinic/service/ClinicServiceTests.java | 41 +++++++++++++++---- 11 files changed, 85 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java b/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java index cf2ae364..012e8c4b 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java +++ b/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java @@ -25,6 +25,7 @@ * * @author Ken Krebs * @author Juergen Hoeller + * @author Wick Dynex */ @MappedSuperclass public class NamedEntity extends BaseEntity { 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 612d89d2..675b2140 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/Owner.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/Owner.java @@ -41,6 +41,7 @@ * @author Sam Brannen * @author Michael Isvy * @author Oliver Drotbohm + * @author Wick Dynex */ @Entity @Table(name = "owners") 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 490b1028..463a94c7 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java @@ -16,6 +16,7 @@ package org.springframework.samples.petclinic.owner; import java.util.List; +import java.util.Optional; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -40,6 +41,7 @@ * @author Ken Krebs * @author Arjen Poutsma * @author Michael Isvy + * @author Wick Dynex */ @Controller class OwnerController { @@ -59,7 +61,10 @@ public void setAllowedFields(WebDataBinder dataBinder) { @ModelAttribute("owner") public Owner findOwner(@PathVariable(name = "ownerId", required = false) Integer ownerId) { - return ownerId == null ? new Owner() : this.owners.findById(ownerId); + return ownerId == null ? new Owner() + : this.owners.findById(ownerId) + .orElseThrow(() -> new IllegalArgumentException("Owner not found with id: " + ownerId + + ". Please ensure the ID is correct " + "and the owner exists in the database.")); } @GetMapping("/owners/new") @@ -158,7 +163,9 @@ public String processUpdateOwnerForm(@Valid Owner owner, BindingResult result, @ @GetMapping("/owners/{ownerId}") public ModelAndView showOwner(@PathVariable("ownerId") int ownerId) { ModelAndView mav = new ModelAndView("owners/ownerDetails"); - Owner owner = this.owners.findById(ownerId); + Optional optionalOwner = this.owners.findById(ownerId); + Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException( + "Owner not found with id: " + ownerId + ". Please ensure the ID is correct ")); mav.addObject(owner); return mav; } 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 f4444943..7bbb91e9 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java @@ -16,12 +16,16 @@ package org.springframework.samples.petclinic.owner; import java.util.List; +import java.util.Optional; +import jakarta.annotation.Nonnull; +import jakarta.validation.constraints.NotNull; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.Repository; import org.springframework.data.repository.query.Param; +import org.springframework.lang.NonNullApi; import org.springframework.transaction.annotation.Transactional; /** @@ -34,8 +38,9 @@ * @author Juergen Hoeller * @author Sam Brannen * @author Michael Isvy + * @author Wick Dynex */ -public interface OwnerRepository extends Repository { +public interface OwnerRepository extends JpaRepository { /** * Retrieve all {@link PetType}s from the data store. @@ -52,25 +57,24 @@ 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 owner.pets WHERE owner.lastName LIKE :lastName% ") @Transactional(readOnly = true) Page findByLastName(@Param("lastName") String lastName, Pageable pageable); /** * Retrieve an {@link Owner} from the data store by id. + *

+ * This method returns an {@link Optional} containing the {@link Owner} if found. If + * no {@link Owner} is found with the provided id, it will return an empty + * {@link Optional}. + *

* @param id the id to search for - * @return the {@link Owner} if found - */ - @Query("SELECT owner FROM Owner owner left join fetch owner.pets WHERE owner.id =:id") - @Transactional(readOnly = true) - Owner findById(@Param("id") Integer id); - - /** - * Save an {@link Owner} to the data store, either inserting or updating it. - * @param owner the {@link Owner} to save + * @return an {@link Optional} containing the {@link Owner} if found, or an empty + * {@link Optional} if not found. + * @throws IllegalArgumentException if the id is null (assuming null is not a valid + * input for id) */ - void save(Owner owner); + Optional findById(@Nonnull Integer id); /** * Returns all the owners from data store diff --git a/src/main/java/org/springframework/samples/petclinic/owner/Pet.java b/src/main/java/org/springframework/samples/petclinic/owner/Pet.java index 2e5ea948..03fd26c9 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/Pet.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/Pet.java @@ -39,6 +39,7 @@ * @author Ken Krebs * @author Juergen Hoeller * @author Sam Brannen + * @author Wick Dynex */ @Entity @Table(name = "pets") diff --git a/src/main/java/org/springframework/samples/petclinic/owner/PetController.java b/src/main/java/org/springframework/samples/petclinic/owner/PetController.java index cde8e59e..f5dc7a35 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/PetController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/PetController.java @@ -17,6 +17,7 @@ import java.time.LocalDate; import java.util.Collection; +import java.util.Optional; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; @@ -37,6 +38,7 @@ * @author Juergen Hoeller * @author Ken Krebs * @author Arjen Poutsma + * @author Wick Dynex */ @Controller @RequestMapping("/owners/{ownerId}") @@ -57,8 +59,9 @@ public Collection populatePetTypes() { @ModelAttribute("owner") public Owner findOwner(@PathVariable("ownerId") int ownerId) { - - Owner owner = this.owners.findById(ownerId); + Optional optionalOwner = this.owners.findById(ownerId); + Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException( + "Owner not found with id: " + ownerId + ". Please ensure the ID is correct ")); if (owner == null) { throw new IllegalArgumentException("Owner ID not found: " + ownerId); } @@ -73,7 +76,9 @@ public Pet findPet(@PathVariable("ownerId") int ownerId, return new Pet(); } - Owner owner = this.owners.findById(ownerId); + Optional optionalOwner = this.owners.findById(ownerId); + Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException( + "Owner not found with id: " + ownerId + ". Please ensure the ID is correct ")); if (owner == null) { throw new IllegalArgumentException("Owner ID not found: " + ownerId); } 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 6c7a8dd1..542d4718 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java @@ -16,6 +16,8 @@ package org.springframework.samples.petclinic.owner; import java.util.Map; +import java.util.Optional; +import java.util.OptionalInt; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; @@ -35,6 +37,7 @@ * @author Arjen Poutsma * @author Michael Isvy * @author Dave Syer + * @author Wick Dynex */ @Controller class VisitController { @@ -60,7 +63,9 @@ public void setAllowedFields(WebDataBinder dataBinder) { @ModelAttribute("visit") public Visit loadPetWithVisit(@PathVariable("ownerId") int ownerId, @PathVariable("petId") int petId, Map model) { - Owner owner = this.owners.findById(ownerId); + Optional optionalOwner = owners.findById(ownerId); + Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException( + "Owner not found with id: " + ownerId + ". Please ensure the ID is correct ")); Pet pet = owner.getPet(petId); model.put("pet", pet); 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 f88c1f1d..40a40bc5 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java @@ -31,6 +31,7 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import java.time.LocalDate; +import java.util.Optional; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.greaterThan; @@ -52,6 +53,7 @@ * Test class for {@link OwnerController} * * @author Colin But + * @author Wick Dynex */ @WebMvcTest(OwnerController.class) @DisabledInNativeImage @@ -94,7 +96,7 @@ void setup() { given(this.owners.findAll(any(Pageable.class))).willReturn(new PageImpl<>(Lists.newArrayList(george))); - given(this.owners.findById(TEST_OWNER_ID)).willReturn(george); + given(this.owners.findById(TEST_OWNER_ID)).willReturn(Optional.of(george)); Visit visit = new Visit(); visit.setDate(LocalDate.now()); george.getPet("Max").getVisits().add(visit); @@ -240,7 +242,7 @@ public void testProcessUpdateOwnerFormWithIdMismatch() throws Exception { owner.setCity("New York"); owner.setTelephone("0123456789"); - when(owners.findById(pathOwnerId)).thenReturn(owner); + when(owners.findById(pathOwnerId)).thenReturn(Optional.of(owner)); mockMvc.perform(MockMvcRequestBuilders.post("/owners/{ownerId}/edit", pathOwnerId).flashAttr("owner", owner)) .andExpect(status().is3xxRedirection()) 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 657df198..14ae3257 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java @@ -46,6 +46,7 @@ * Test class for the {@link PetController} * * @author Colin But + * @author Wick Dynex */ @WebMvcTest(value = PetController.class, includeFilters = @ComponentScan.Filter(value = PetTypeFormatter.class, type = FilterType.ASSIGNABLE_TYPE)) @@ -79,7 +80,7 @@ void setup() { dog.setId(TEST_PET_ID + 1); pet.setName("petty"); dog.setName("doggy"); - given(this.owners.findById(TEST_OWNER_ID)).willReturn(owner); + given(this.owners.findById(TEST_OWNER_ID)).willReturn(Optional.of(owner)); } @Test 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 3565d927..8f44aace 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java @@ -32,10 +32,13 @@ import org.springframework.test.context.aot.DisabledInAotMode; import org.springframework.test.web.servlet.MockMvc; +import java.util.Optional; + /** * Test class for {@link VisitController} * * @author Colin But + * @author Wick Dynex */ @WebMvcTest(VisitController.class) @DisabledInNativeImage @@ -58,7 +61,7 @@ void init() { Pet pet = new Pet(); owner.addPet(pet); pet.setId(TEST_PET_ID); - given(this.owners.findById(TEST_OWNER_ID)).willReturn(owner); + given(this.owners.findById(TEST_OWNER_ID)).willReturn(Optional.of(owner)); } @Test 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 92a70c38..39a444e2 100644 --- a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java +++ b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java @@ -20,6 +20,7 @@ import java.time.LocalDate; import java.util.Collection; +import java.util.Optional; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -90,7 +91,9 @@ void shouldFindOwnersByLastName() { @Test void shouldFindSingleOwnerWithPet() { - Owner owner = this.owners.findById(1); + Optional optionalOwner = this.owners.findById(1); + assertThat(optionalOwner).isPresent(); + Owner owner = optionalOwner.get(); assertThat(owner.getLastName()).startsWith("Franklin"); assertThat(owner.getPets()).hasSize(1); assertThat(owner.getPets().get(0).getType()).isNotNull(); @@ -119,7 +122,9 @@ void shouldInsertOwner() { @Test @Transactional void shouldUpdateOwner() { - Owner owner = this.owners.findById(1); + Optional optionalOwner = this.owners.findById(1); + assertThat(optionalOwner).isPresent(); + Owner owner = optionalOwner.get(); String oldLastName = owner.getLastName(); String newLastName = oldLastName + "X"; @@ -127,7 +132,9 @@ void shouldUpdateOwner() { this.owners.save(owner); // retrieving new name from database - owner = this.owners.findById(1); + optionalOwner = this.owners.findById(1); + assertThat(optionalOwner).isPresent(); + owner = optionalOwner.get(); assertThat(owner.getLastName()).isEqualTo(newLastName); } @@ -144,7 +151,10 @@ void shouldFindAllPetTypes() { @Test @Transactional void shouldInsertPetIntoDatabaseAndGenerateId() { - Owner owner6 = this.owners.findById(6); + Optional optionalOwner = this.owners.findById(6); + assertThat(optionalOwner).isPresent(); + Owner owner6 = optionalOwner.get(); + int found = owner6.getPets().size(); Pet pet = new Pet(); @@ -157,7 +167,9 @@ void shouldInsertPetIntoDatabaseAndGenerateId() { this.owners.save(owner6); - owner6 = this.owners.findById(6); + optionalOwner = this.owners.findById(6); + assertThat(optionalOwner).isPresent(); + owner6 = optionalOwner.get(); assertThat(owner6.getPets()).hasSize(found + 1); // checks that id has been generated pet = owner6.getPet("bowser"); @@ -167,7 +179,10 @@ void shouldInsertPetIntoDatabaseAndGenerateId() { @Test @Transactional void shouldUpdatePetName() { - Owner owner6 = this.owners.findById(6); + Optional optionalOwner = this.owners.findById(6); + assertThat(optionalOwner).isPresent(); + Owner owner6 = optionalOwner.get(); + Pet pet7 = owner6.getPet(7); String oldName = pet7.getName(); @@ -175,7 +190,9 @@ void shouldUpdatePetName() { pet7.setName(newName); this.owners.save(owner6); - owner6 = this.owners.findById(6); + optionalOwner = this.owners.findById(6); + assertThat(optionalOwner).isPresent(); + owner6 = optionalOwner.get(); pet7 = owner6.getPet(7); assertThat(pet7.getName()).isEqualTo(newName); } @@ -194,7 +211,10 @@ void shouldFindVets() { @Test @Transactional void shouldAddNewVisitForPet() { - Owner owner6 = this.owners.findById(6); + Optional optionalOwner = this.owners.findById(6); + assertThat(optionalOwner).isPresent(); + Owner owner6 = optionalOwner.get(); + Pet pet7 = owner6.getPet(7); int found = pet7.getVisits().size(); Visit visit = new Visit(); @@ -210,7 +230,10 @@ void shouldAddNewVisitForPet() { @Test void shouldFindVisitsByPetId() { - Owner owner6 = this.owners.findById(6); + Optional optionalOwner = this.owners.findById(6); + assertThat(optionalOwner).isPresent(); + Owner owner6 = optionalOwner.get(); + Pet pet7 = owner6.getPet(7); Collection visits = pet7.getVisits(); From 1cad4124b7685d3e66ac376f44874a3d5319cb06 Mon Sep 17 00:00:00 2001 From: YiXuan Ding <1328032567@qq.com> Date: Sun, 10 Nov 2024 20:51:23 +0800 Subject: [PATCH 21/32] Refactor code logic : remove useless logic cod. : detele useless annotation which is provided by Jpa. : refactor implement of `findByLastName`, use Jpa to simplify query. --- .../samples/petclinic/owner/OwnerController.java | 2 +- .../samples/petclinic/owner/OwnerRepository.java | 8 +------- .../samples/petclinic/owner/PetController.java | 9 +-------- .../samples/petclinic/owner/OwnerControllerTests.java | 8 ++++---- .../samples/petclinic/service/ClinicServiceTests.java | 8 ++++---- 5 files changed, 11 insertions(+), 24 deletions(-) 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 463a94c7..44c85a62 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java @@ -127,7 +127,7 @@ private String addPaginationModel(int page, Model model, Page paginated) private Page findPaginatedForOwnersLastName(int page, String lastname) { int pageSize = 5; Pageable pageable = PageRequest.of(page - 1, pageSize); - return owners.findByLastName(lastname, pageable); + return owners.findByLastNameStartingWith(lastname, pageable); } @GetMapping("/owners/{ownerId}/edit") 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 7bbb91e9..63fae196 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java @@ -19,13 +19,11 @@ import java.util.Optional; import jakarta.annotation.Nonnull; -import jakarta.validation.constraints.NotNull; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import org.springframework.lang.NonNullApi; import org.springframework.transaction.annotation.Transactional; /** @@ -57,9 +55,7 @@ public interface OwnerRepository extends JpaRepository { * @return a Collection of matching {@link Owner}s (or an empty Collection if none * found) */ - @Query("SELECT DISTINCT owner FROM Owner owner left join owner.pets WHERE owner.lastName LIKE :lastName% ") - @Transactional(readOnly = true) - Page findByLastName(@Param("lastName") String lastName, Pageable pageable); + Page findByLastNameStartingWith(String lastName, Pageable pageable); /** * Retrieve an {@link Owner} from the data store by id. @@ -79,8 +75,6 @@ public interface OwnerRepository extends JpaRepository { /** * Returns 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/PetController.java b/src/main/java/org/springframework/samples/petclinic/owner/PetController.java index f5dc7a35..fcf431bf 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/PetController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/PetController.java @@ -62,9 +62,6 @@ public Owner findOwner(@PathVariable("ownerId") int ownerId) { Optional optionalOwner = this.owners.findById(ownerId); Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException( "Owner not found with id: " + ownerId + ". Please ensure the ID is correct ")); - if (owner == null) { - throw new IllegalArgumentException("Owner ID not found: " + ownerId); - } return owner; } @@ -79,9 +76,6 @@ public Pet findPet(@PathVariable("ownerId") int ownerId, Optional optionalOwner = this.owners.findById(ownerId); Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException( "Owner not found with id: " + ownerId + ". Please ensure the ID is correct ")); - if (owner == null) { - throw new IllegalArgumentException("Owner ID not found: " + ownerId); - } return owner.getPet(petId); } @@ -99,7 +93,6 @@ public void initPetBinder(WebDataBinder dataBinder) { public String initCreationForm(Owner owner, ModelMap model) { Pet pet = new Pet(); owner.addPet(pet); - model.put("pet", pet); return VIEWS_PETS_CREATE_OR_UPDATE_FORM; } @@ -126,7 +119,7 @@ public String processCreationForm(Owner owner, @Valid Pet pet, BindingResult res } @GetMapping("/pets/{petId}/edit") - public String initUpdateForm(Owner owner, @PathVariable("petId") int petId, RedirectAttributes redirectAttributes) { + public String initUpdateForm() { return VIEWS_PETS_CREATE_OR_UPDATE_FORM; } 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 40a40bc5..c285f39f 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java @@ -91,7 +91,7 @@ private Owner george() { void setup() { Owner george = george(); - given(this.owners.findByLastName(eq("Franklin"), any(Pageable.class))) + given(this.owners.findByLastNameStartingWith(eq("Franklin"), any(Pageable.class))) .willReturn(new PageImpl<>(Lists.newArrayList(george))); given(this.owners.findAll(any(Pageable.class))).willReturn(new PageImpl<>(Lists.newArrayList(george))); @@ -144,14 +144,14 @@ void testInitFindForm() throws Exception { @Test void testProcessFindFormSuccess() throws Exception { Page tasks = new PageImpl<>(Lists.newArrayList(george(), new Owner())); - when(this.owners.findByLastName(anyString(), any(Pageable.class))).thenReturn(tasks); + when(this.owners.findByLastNameStartingWith(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())); - when(this.owners.findByLastName(eq("Franklin"), any(Pageable.class))).thenReturn(tasks); + when(this.owners.findByLastNameStartingWith(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)); @@ -160,7 +160,7 @@ void testProcessFindFormByLastName() throws Exception { @Test void testProcessFindFormNoOwnersFound() throws Exception { Page tasks = new PageImpl<>(Lists.newArrayList()); - when(this.owners.findByLastName(eq("Unknown Surname"), any(Pageable.class))).thenReturn(tasks); + when(this.owners.findByLastNameStartingWith(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")) 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 39a444e2..17360278 100644 --- a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java +++ b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java @@ -82,10 +82,10 @@ class ClinicServiceTests { @Test void shouldFindOwnersByLastName() { - Page owners = this.owners.findByLastName("Davis", pageable); + Page owners = this.owners.findByLastNameStartingWith("Davis", pageable); assertThat(owners).hasSize(2); - owners = this.owners.findByLastName("Daviss", pageable); + owners = this.owners.findByLastNameStartingWith("Daviss", pageable); assertThat(owners).isEmpty(); } @@ -103,7 +103,7 @@ void shouldFindSingleOwnerWithPet() { @Test @Transactional void shouldInsertOwner() { - Page owners = this.owners.findByLastName("Schultz", pageable); + Page owners = this.owners.findByLastNameStartingWith("Schultz", pageable); int found = (int) owners.getTotalElements(); Owner owner = new Owner(); @@ -115,7 +115,7 @@ void shouldInsertOwner() { this.owners.save(owner); assertThat(owner.getId()).isNotZero(); - owners = this.owners.findByLastName("Schultz", pageable); + owners = this.owners.findByLastNameStartingWith("Schultz", pageable); assertThat(owners.getTotalElements()).isEqualTo(found + 1); } From 317562a170f63463e419ee5be4c0959bb88e1843 Mon Sep 17 00:00:00 2001 From: Andrey Litvitski Date: Mon, 11 Nov 2024 23:30:06 +0300 Subject: [PATCH 22/32] Implement K8S deployment testing using Kind in GitHub Actions --- .github/workflows/deploy-and-test-cluster.yml | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/deploy-and-test-cluster.yml diff --git a/.github/workflows/deploy-and-test-cluster.yml b/.github/workflows/deploy-and-test-cluster.yml new file mode 100644 index 00000000..7353a604 --- /dev/null +++ b/.github/workflows/deploy-and-test-cluster.yml @@ -0,0 +1,31 @@ +name: Deploy and Test Cluster + +on: + push: + branches: [main] + paths: + - 'k8s/**' + pull_request: + branches: [main] + paths: + - 'k8s/**' + +jobs: + deploy-and-test-cluster: + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v2 + + - name: Create k8s Kind Cluster + uses: helm/kind-action@v1 + + - name: Deploy application + run: | + kubectl apply -f k8s/ + + - name: Wait for Pods to be ready + run: | + kubectl wait --for=condition=ready pod -l app=demo-db --timeout=180s + kubectl wait --for=condition=ready pod -l app=petclinic --timeout=180s + From 9f1cda1c08e6563c71edec907460af8f2e21d7db Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 25 Nov 2024 11:04:49 +0000 Subject: [PATCH 23/32] Update to Boot 3.4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3d7f28f6..a50e4064 100644 --- a/pom.xml +++ b/pom.xml @@ -5,13 +5,13 @@ org.springframework.boot spring-boot-starter-parent - 3.3.5 + 3.4.0 org.springframework.samples spring-petclinic - 3.3.0-SNAPSHOT + 3.4.0-SNAPSHOT petclinic From 91f55a4f7134409a832435de2c6df231102d5898 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Wed, 27 Nov 2024 08:32:30 +0000 Subject: [PATCH 24/32] Versionless webjars (again) Spring Boot 3.4 supports the webjars-locator-lite which in turn supports native images, so we are back to versionless URLs for webjars assets in templates. --- pom.xml | 849 +++++++++--------- .../petclinic/PetClinicRuntimeHints.java | 1 - .../resources/templates/fragments/layout.html | 4 +- src/test/jmeter/petclinic_test_plan.jmx | 2 +- 4 files changed, 436 insertions(+), 420 deletions(-) diff --git a/pom.xml b/pom.xml index a50e4064..bd7f22bc 100644 --- a/pom.xml +++ b/pom.xml @@ -1,442 +1,459 @@ - - 4.0.0 + + 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.4.0 - - - - org.springframework.samples - spring-petclinic - 3.4.0-SNAPSHOT - - petclinic - - - - - 17 - UTF-8 - UTF-8 - - 2023-05-10T07:42:50Z + + org.springframework.boot + spring-boot-starter-parent + 3.4.0 + + - - 5.3.3 - 4.7.0 + org.springframework.samples + spring-petclinic + 3.4.0-SNAPSHOT - 10.20.0 - 0.8.12 - 0.2.29 - 1.0.0 - 3.6.0 - 0.0.11 - 0.0.43 + petclinic - + - - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-starter-cache - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-validation - - - org.springframework.boot - spring-boot-starter-thymeleaf - - - org.springframework.boot - spring-boot-starter-test - test - + + 17 + UTF-8 + UTF-8 + + 2023-05-10T07:42:50Z - - - com.h2database - h2 - runtime - - - com.mysql - mysql-connector-j - runtime - - - org.postgresql - postgresql - runtime - + + 1.0.1 + 5.3.3 + 4.7.0 - - - javax.cache - cache-api - - - com.github.ben-manes.caffeine - caffeine - + 10.20.0 + 0.8.12 + 0.2.29 + 1.0.0 + 3.6.0 + 0.0.11 + 0.0.43 - - - org.webjars.npm - bootstrap - ${webjars-bootstrap.version} - - - org.webjars.npm - font-awesome - ${webjars-font-awesome.version} - + - - org.springframework.boot - spring-boot-devtools - test - - - org.springframework.boot - spring-boot-testcontainers - test - - - org.springframework.boot - spring-boot-docker-compose - test - - - org.testcontainers - junit-jupiter - test - - - org.testcontainers - mysql - test - + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-cache + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-test + test + + + + io.projectreactor + reactor-core + - - jakarta.xml.bind - jakarta.xml.bind-api - + + + com.h2database + h2 + runtime + + + com.mysql + mysql-connector-j + runtime + + + org.postgresql + postgresql + runtime + - + + + javax.cache + cache-api + + + com.github.ben-manes.caffeine + caffeine + - - - - org.apache.maven.plugins - maven-enforcer-plugin - - - enforce-java - - enforce - - - - - This build requires at least Java ${java.version}, update your JVM, and - run the build again - ${java.version} - - - - - - - - io.spring.javaformat - spring-javaformat-maven-plugin - ${spring-format.version} - - - - validate - - validate - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - ${maven-checkstyle.version} - - - com.puppycrawl.tools - checkstyle - ${checkstyle.version} - - - io.spring.nohttp - nohttp-checkstyle - ${nohttp-checkstyle.version} - - - - - nohttp-checkstyle-validation - - check - - validate - - src/checkstyle/nohttp-checkstyle.xml - ${basedir} - **/* - **/.git/**/*,**/.idea/**/*,**/target/**/,**/.flattened-pom.xml,**/*.class - config_loc=${basedir}/src/checkstyle/ - - - - - - org.graalvm.buildtools - native-maven-plugin - - - org.springframework.boot - spring-boot-maven-plugin - - - - - build-info - - - - ${project.build.sourceEncoding} - ${project.reporting.outputEncoding} - ${java.version} - ${java.version} - - - - - - - org.jacoco - jacoco-maven-plugin - ${jacoco.version} - - - - prepare-agent - - - - report - - report - - prepare-package - - - + + + org.webjars + webjars-locator-lite + ${webjars-locator.version} + + + org.webjars.npm + bootstrap + ${webjars-bootstrap.version} + + + org.webjars.npm + font-awesome + ${webjars-font-awesome.version} + - - - io.github.git-commit-id - git-commit-id-maven-plugin - - false - false - - - - - - org.cyclonedx - cyclonedx-maven-plugin - + + org.springframework.boot + spring-boot-devtools + test + + + org.springframework.boot + spring-boot-testcontainers + test + + + org.springframework.boot + spring-boot-docker-compose + test + + + org.testcontainers + junit-jupiter + test + + + org.testcontainers + mysql + test + - - - - - Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0 - - + + jakarta.xml.bind + jakarta.xml.bind-api + - - - - true - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - - - false - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - - - - - true - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - - - false - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - + - - - css - + - - org.apache.maven.plugins - maven-dependency-plugin - - - unpack - - unpack - - - generate-resources - - - - org.webjars.npm - bootstrap - ${webjars-bootstrap.version} - - - ${project.build.directory}/webjars - - - - - - - com.gitlab.haynes - libsass-maven-plugin - ${libsass.version} - - ${basedir}/src/main/scss/ - ${basedir}/src/main/resources/static/resources/css/ - ${project.build.directory}/webjars/META-INF/resources/webjars/bootstrap/${webjars-bootstrap.version}/scss/ - - - - - - compile - - generate-resources - - - - - - - - m2e - - - m2e.version - - - - - - - org.eclipse.m2e - lifecycle-mapping - ${lifecycle-mapping} - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - [1,) + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-java + + enforce + + + + + This build requires at least Java ${java.version}, + update your JVM, and + run the build again + ${java.version} + + + + + + + + io.spring.javaformat + spring-javaformat-maven-plugin + ${spring-format.version} + + + + validate + + validate + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${maven-checkstyle.version} + + + com.puppycrawl.tools + checkstyle + ${checkstyle.version} + + + io.spring.nohttp + nohttp-checkstyle + ${nohttp-checkstyle.version} + + + + + nohttp-checkstyle-validation + + check + + validate + + src/checkstyle/nohttp-checkstyle.xml + ${basedir} + **/* + + **/.git/**/*,**/.idea/**/*,**/target/**/,**/.flattened-pom.xml,**/*.class + config_loc=${basedir}/src/checkstyle/ + + + + + + org.graalvm.buildtools + native-maven-plugin + + + org.springframework.boot + spring-boot-maven-plugin + + + - check + build-info - - - - - - - - org.springframework.boot - spring-boot-maven-plugin - [1,) + + + ${project.build.sourceEncoding} + ${project.reporting.outputEncoding} + ${java.version} + ${java.version} + + + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + - build-info + prepare-agent - - - - - - - - io.spring.javaformat - spring-javaformat-maven-plugin - [0,) + + + report - validate + report - - - - - - - - + prepare-package + + + + + + + io.github.git-commit-id + git-commit-id-maven-plugin + + false + false + + + + + + org.cyclonedx + cyclonedx-maven-plugin - - - - - - + + + + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0 + + + + + + + true + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + + + false + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + + + + true + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + + + false + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + + + + css + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack + + unpack + + + generate-resources + + + + org.webjars.npm + bootstrap + ${webjars-bootstrap.version} + + + ${project.build.directory}/webjars + + + + + + + com.gitlab.haynes + libsass-maven-plugin + ${libsass.version} + + ${basedir}/src/main/scss/ + ${basedir}/src/main/resources/static/resources/css/ + + ${project.build.directory}/webjars/META-INF/resources/webjars/bootstrap/${webjars-bootstrap.version}/scss/ + + + + + + compile + + generate-resources + + + + + + + + m2e + + + m2e.version + + + + + + + + org.eclipse.m2e + lifecycle-mapping + ${lifecycle-mapping} + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + [1,) + + check + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + [1,) + + build-info + + + + + + + + + io.spring.javaformat + spring-javaformat-maven-plugin + [0,) + + validate + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/org/springframework/samples/petclinic/PetClinicRuntimeHints.java b/src/main/java/org/springframework/samples/petclinic/PetClinicRuntimeHints.java index 7acecf46..2975923d 100644 --- a/src/main/java/org/springframework/samples/petclinic/PetClinicRuntimeHints.java +++ b/src/main/java/org/springframework/samples/petclinic/PetClinicRuntimeHints.java @@ -28,7 +28,6 @@ public class PetClinicRuntimeHints implements RuntimeHintsRegistrar { public void registerHints(RuntimeHints hints, ClassLoader classLoader) { hints.resources().registerPattern("db/*"); // https://github.com/spring-projects/spring-boot/issues/32654 hints.resources().registerPattern("messages/*"); - hints.resources().registerPattern("META-INF/resources/webjars/*"); hints.resources().registerPattern("mysql-default-conf"); hints.serialization().registerType(BaseEntity.class); hints.serialization().registerType(Person.class); diff --git a/src/main/resources/templates/fragments/layout.html b/src/main/resources/templates/fragments/layout.html index 3e9bc398..eb54d28a 100644 --- a/src/main/resources/templates/fragments/layout.html +++ b/src/main/resources/templates/fragments/layout.html @@ -17,7 +17,7 @@ - + @@ -87,7 +87,7 @@ - + diff --git a/src/test/jmeter/petclinic_test_plan.jmx b/src/test/jmeter/petclinic_test_plan.jmx index aeb3bd2e..89c7bf21 100644 --- a/src/test/jmeter/petclinic_test_plan.jmx +++ b/src/test/jmeter/petclinic_test_plan.jmx @@ -156,7 +156,7 @@ - ${CONTEXT_WEB}/webjars/bootstrap/5.3.3/dist/js/bootstrap.bundle.min.js + ${CONTEXT_WEB}/webjars/bootstrap/dist/js/bootstrap.bundle.min.js GET true false From a0ba075bd86d3940e6bbcb74612103a611aa2da4 Mon Sep 17 00:00:00 2001 From: Patrick Baumgartner Date: Wed, 27 Nov 2024 10:47:52 +0100 Subject: [PATCH 25/32] Futher updates for Spring Boot 3.4 --- build.gradle | 11 +- pom.xml | 856 +++++++++--------- .../petclinic/owner/OwnerRepository.java | 1 - .../petclinic/owner/VisitController.java | 1 - .../petclinic/owner/OwnerControllerTests.java | 4 +- .../petclinic/owner/PetControllerTests.java | 7 +- .../petclinic/owner/VisitControllerTests.java | 4 +- .../petclinic/vet/VetControllerTests.java | 4 +- 8 files changed, 442 insertions(+), 446 deletions(-) diff --git a/build.gradle b/build.gradle index a5175dfb..17c3763e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '3.3.5' + id 'org.springframework.boot' version '3.4.0' id 'io.spring.dependency-management' version '1.1.6' id 'org.graalvm.buildtools.native' version '0.10.3' id 'org.cyclonedx.bom' version '1.10.0' @@ -15,7 +15,7 @@ apply plugin: 'io.spring.javaformat' gradle.startParameter.excludedTaskNames += [ "checkFormatAot", "checkFormatAotTest" ] group = 'org.springframework.samples' -version = '3.3.0' +version = '3.4.0' java { sourceCompatibility = JavaVersion.VERSION_17 @@ -25,12 +25,16 @@ repositories { mavenCentral() } -ext.checkstyleVersion = "10.20.0" +ext.checkstyleVersion = "10.20.1" ext.springJavaformatCheckstyleVersion = "0.0.43" +ext.webjarsLocatorLiteVersion = "1.0.1" ext.webjarsFontawesomeVersion = "4.7.0" ext.webjarsBootstrapVersion = "5.3.3" dependencies { + // Workaround for AOT issue (https://github.com/spring-projects/spring-framework/pull/33949) --> + implementation 'io.projectreactor:reactor-core' + implementation 'org.springframework.boot:spring-boot-starter-cache' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' @@ -39,6 +43,7 @@ dependencies { implementation 'javax.cache:cache-api' implementation 'jakarta.xml.bind:jakarta.xml.bind-api' runtimeOnly 'org.springframework.boot:spring-boot-starter-actuator' + runtimeOnly "org.webjars:webjars-locator-lite:${webjarsLocatorLiteVersion}" runtimeOnly "org.webjars.npm:bootstrap:${webjarsBootstrapVersion}" runtimeOnly "org.webjars.npm:font-awesome:${webjarsFontawesomeVersion}" runtimeOnly 'com.github.ben-manes.caffeine:caffeine' diff --git a/pom.xml b/pom.xml index bd7f22bc..8cd1b7d8 100644 --- a/pom.xml +++ b/pom.xml @@ -1,459 +1,455 @@ - - 4.0.0 + + 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.4.0 - - + + org.springframework.boot + spring-boot-starter-parent + 3.4.0 + + - org.springframework.samples - spring-petclinic - 3.4.0-SNAPSHOT + org.springframework.samples + spring-petclinic + 3.4.0-SNAPSHOT - petclinic + petclinic - + - - 17 - UTF-8 - UTF-8 - + 17 + UTF-8 + UTF-8 + - 2023-05-10T07:42:50Z + 2023-05-10T07:42:50Z - - 1.0.1 - 5.3.3 - 4.7.0 + + 1.0.1 + 5.3.3 + 4.7.0 - 10.20.0 - 0.8.12 - 0.2.29 - 1.0.0 - 3.6.0 - 0.0.11 - 0.0.43 + 10.20.1 + 0.8.12 + 0.2.29 + 1.0.0 + 3.6.0 + 0.0.11 + 0.0.43 - + - - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-starter-cache - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-validation - - - org.springframework.boot - spring-boot-starter-thymeleaf - - - org.springframework.boot - spring-boot-starter-test - test - - - - io.projectreactor - reactor-core - + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-cache + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-test + test + + + + io.projectreactor + reactor-core + - - - com.h2database - h2 - runtime - - - com.mysql - mysql-connector-j - runtime - - - org.postgresql - postgresql - runtime - + + + com.h2database + h2 + runtime + + + com.mysql + mysql-connector-j + runtime + + + org.postgresql + postgresql + runtime + - - - javax.cache - cache-api - - - com.github.ben-manes.caffeine - caffeine - + + + javax.cache + cache-api + + + com.github.ben-manes.caffeine + caffeine + - - - org.webjars - webjars-locator-lite - ${webjars-locator.version} - - - org.webjars.npm - bootstrap - ${webjars-bootstrap.version} - - - org.webjars.npm - font-awesome - ${webjars-font-awesome.version} - + + + org.webjars + webjars-locator-lite + ${webjars-locator.version} + + + org.webjars.npm + bootstrap + ${webjars-bootstrap.version} + + + org.webjars.npm + font-awesome + ${webjars-font-awesome.version} + - - org.springframework.boot - spring-boot-devtools - test - - - org.springframework.boot - spring-boot-testcontainers - test - - - org.springframework.boot - spring-boot-docker-compose - test - - - org.testcontainers - junit-jupiter - test - - - org.testcontainers - mysql - test - + + org.springframework.boot + spring-boot-devtools + test + + + org.springframework.boot + spring-boot-testcontainers + test + + + org.springframework.boot + spring-boot-docker-compose + test + + + org.testcontainers + junit-jupiter + test + + + org.testcontainers + mysql + test + - - jakarta.xml.bind - jakarta.xml.bind-api - + + jakarta.xml.bind + jakarta.xml.bind-api + - + - - - - org.apache.maven.plugins - maven-enforcer-plugin - - - enforce-java - - enforce - - - - - This build requires at least Java ${java.version}, + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-java + + enforce + + + + + This build requires at least Java ${java.version}, update your JVM, and run the build again - ${java.version} - - - - - - - - io.spring.javaformat - spring-javaformat-maven-plugin - ${spring-format.version} - - - - validate - - validate - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - ${maven-checkstyle.version} - - - com.puppycrawl.tools - checkstyle - ${checkstyle.version} - - - io.spring.nohttp - nohttp-checkstyle - ${nohttp-checkstyle.version} - - - - - nohttp-checkstyle-validation - - check - - validate - - src/checkstyle/nohttp-checkstyle.xml - ${basedir} - **/* - - **/.git/**/*,**/.idea/**/*,**/target/**/,**/.flattened-pom.xml,**/*.class - config_loc=${basedir}/src/checkstyle/ - - - - - - org.graalvm.buildtools - native-maven-plugin - - - org.springframework.boot - spring-boot-maven-plugin - - - - - build-info - - - - ${project.build.sourceEncoding} - ${project.reporting.outputEncoding} - ${java.version} - ${java.version} - - - - - - - org.jacoco - jacoco-maven-plugin - ${jacoco.version} - - - - prepare-agent - - - - report - - report - - prepare-package - - - + + build-info + + + + ${project.build.sourceEncoding} + ${project.reporting.outputEncoding} + ${java.version} + ${java.version} + + + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + + prepare-agent + + + + report + + report + + prepare-package + + + - - - io.github.git-commit-id - git-commit-id-maven-plugin - - false - false - - - - - - org.cyclonedx - cyclonedx-maven-plugin - + + + org.cyclonedx + cyclonedx-maven-plugin + - - - - - Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0 - - + + + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0 + + - - - - true - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - - - false - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - - - - - true - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - - - false - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - + + + + true + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + + + false + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + + + + true + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + + + false + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + - - - css - - - - org.apache.maven.plugins - maven-dependency-plugin - - - unpack - - unpack - - - generate-resources - - - - org.webjars.npm - bootstrap - ${webjars-bootstrap.version} - - - ${project.build.directory}/webjars - - - - + + + css + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack + + unpack + + + generate-resources + + + + org.webjars.npm + bootstrap + ${webjars-bootstrap.version} + + + ${project.build.directory}/webjars + + + + - - com.gitlab.haynes - libsass-maven-plugin - ${libsass.version} - - ${basedir}/src/main/scss/ - ${basedir}/src/main/resources/static/resources/css/ - - ${project.build.directory}/webjars/META-INF/resources/webjars/bootstrap/${webjars-bootstrap.version}/scss/ - - - - - - compile - - generate-resources - - - - - - - - m2e - - - m2e.version - - - - - - - - org.eclipse.m2e - lifecycle-mapping - ${lifecycle-mapping} - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - [1,) - - check - - - - - - - - - org.springframework.boot - spring-boot-maven-plugin - [1,) - - build-info - - - - - - - - - io.spring.javaformat - spring-javaformat-maven-plugin - [0,) - - validate - - - - - - - - - - - - - - - - \ No newline at end of file + + org.eclipse.m2e + lifecycle-mapping + ${lifecycle-mapping} + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + [1,) + + check + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + [1,) + + build-info + + + + + + + + + io.spring.javaformat + spring-javaformat-maven-plugin + [0,) + + validate + + + + + + + + + + + + + + + + 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 63fae196..b91d80d8 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java @@ -23,7 +23,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; /** 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 542d4718..b546f609 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java @@ -17,7 +17,6 @@ import java.util.Map; import java.util.Optional; -import java.util.OptionalInt; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; 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 c285f39f..426ca5c2 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java @@ -22,11 +22,11 @@ import org.junit.jupiter.api.condition.DisabledInNativeImage; 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.Pageable; import org.springframework.test.context.aot.DisabledInAotMode; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -65,7 +65,7 @@ class OwnerControllerTests { @Autowired private MockMvc mockMvc; - @MockBean + @MockitoBean private OwnerRepository owners; private Owner george() { 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 14ae3257..9a6134cb 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java @@ -17,24 +17,21 @@ package org.springframework.samples.petclinic.owner; import org.assertj.core.util.Lists; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledInNativeImage; -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.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; import org.springframework.test.context.aot.DisabledInAotMode; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; import java.time.LocalDate; import java.util.Optional; -import static org.junit.Assert.assertNotNull; 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; @@ -61,7 +58,7 @@ class PetControllerTests { @Autowired private MockMvc mockMvc; - @MockBean + @MockitoBean private OwnerRepository owners; @BeforeEach 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 8f44aace..e42e7503 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java @@ -28,8 +28,8 @@ import org.junit.jupiter.api.condition.DisabledInNativeImage; 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.test.context.aot.DisabledInAotMode; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; import java.util.Optional; @@ -52,7 +52,7 @@ class VisitControllerTests { @Autowired private MockMvc mockMvc; - @MockBean + @MockitoBean private OwnerRepository owners; @BeforeEach 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 20c3f46c..5fffeea4 100644 --- a/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java @@ -22,11 +22,11 @@ import org.junit.jupiter.api.condition.DisabledInNativeImage; 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.context.aot.DisabledInAotMode; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -48,7 +48,7 @@ class VetControllerTests { @Autowired private MockMvc mockMvc; - @MockBean + @MockitoBean private VetRepository vets; private Vet james() { From 214a8fb87f5fb7124010b31f04f9591b9a0aa9e4 Mon Sep 17 00:00:00 2001 From: YiXuan Ding <1328032567@qq.com> Date: Tue, 12 Nov 2024 18:57:52 +0800 Subject: [PATCH 26/32] : rename the DI variable name in constructor. --- .../samples/petclinic/owner/OwnerController.java | 4 ++-- .../springframework/samples/petclinic/vet/VetController.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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 44c85a62..fa350645 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java @@ -50,8 +50,8 @@ class OwnerController { private final OwnerRepository owners; - public OwnerController(OwnerRepository clinicService) { - this.owners = clinicService; + public OwnerController(OwnerRepository owners) { + this.owners = owners; } @InitBinder 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 3240814a..29fcecc7 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/VetController.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/VetController.java @@ -37,8 +37,8 @@ class VetController { private final VetRepository vetRepository; - public VetController(VetRepository clinicService) { - this.vetRepository = clinicService; + public VetController(VetRepository vetRepository) { + this.vetRepository = vetRepository; } @GetMapping("/vets.html") From 40a41375e6b55d5cf246c483d63fa9380ccb9ff1 Mon Sep 17 00:00:00 2001 From: YiXuan Ding <1328032567@qq.com> Date: Tue, 12 Nov 2024 17:33:46 +0800 Subject: [PATCH 27/32] Add new test file -: remove `@Transactional`. -: create unit-test file related to `PetValidator`. -: move pet objects initialization to `@BeforeEach` setup. --- .../petclinic/owner/OwnerRepository.java | 1 - .../petclinic/owner/PetValidatorTests.java | 117 ++++++++++++++++++ 2 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/springframework/samples/petclinic/owner/PetValidatorTests.java 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 b91d80d8..5d7a40fb 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java @@ -44,7 +44,6 @@ public interface OwnerRepository extends JpaRepository { * @return a Collection of {@link PetType}s. */ @Query("SELECT ptype FROM PetType ptype ORDER BY ptype.name") - @Transactional(readOnly = true) List findPetTypes(); /** diff --git a/src/test/java/org/springframework/samples/petclinic/owner/PetValidatorTests.java b/src/test/java/org/springframework/samples/petclinic/owner/PetValidatorTests.java new file mode 100644 index 00000000..1a153bcb --- /dev/null +++ b/src/test/java/org/springframework/samples/petclinic/owner/PetValidatorTests.java @@ -0,0 +1,117 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * 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. + */ + +package org.springframework.samples.petclinic.owner; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledInNativeImage; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.validation.Errors; +import org.springframework.validation.MapBindingResult; + +import java.time.LocalDate; +import java.util.HashMap; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test class for {@link PetValidator} + * + * @author Wick Dynex + */ +@ExtendWith(MockitoExtension.class) +@DisabledInNativeImage +public class PetValidatorTests { + + private PetValidator petValidator; + + private Pet pet; + + private PetType petType; + + private Errors errors; + + private static final String petName = "Buddy"; + + private static final String petTypeName = "Dog"; + + private static final LocalDate petBirthDate = LocalDate.of(1990, 1, 1); + + @BeforeEach + void setUp() { + petValidator = new PetValidator(); + pet = new Pet(); + petType = new PetType(); + errors = new MapBindingResult(new HashMap<>(), "pet"); + } + + @Test + void testValidate() { + petType.setName(petTypeName); + pet.setName(petName); + pet.setType(petType); + pet.setBirthDate(petBirthDate); + + petValidator.validate(pet, errors); + + assertFalse(errors.hasErrors()); + } + + @Nested + class ValidateHasErrors { + + @Test + void testValidateWithInvalidPetName() { + petType.setName(petTypeName); + pet.setName(""); + pet.setType(petType); + pet.setBirthDate(petBirthDate); + + petValidator.validate(pet, errors); + + assertTrue(errors.hasFieldErrors("name")); + } + + @Test + void testValidateWithInvalidPetType() { + pet.setName(petName); + pet.setType(null); + pet.setBirthDate(petBirthDate); + + petValidator.validate(pet, errors); + + assertTrue(errors.hasFieldErrors("type")); + } + + @Test + void testValidateWithInvalidBirthDate() { + petType.setName(petTypeName); + pet.setName(petName); + pet.setType(petType); + pet.setBirthDate(null); + + petValidator.validate(pet, errors); + + assertTrue(errors.hasFieldErrors("birthDate")); + } + + } + +} From 300597fc6cc765a8f167c2daff33cf165abd17b9 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 14 Oct 2024 15:29:20 +0100 Subject: [PATCH 28/32] Update tomestamp for reproducible builds --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8cd1b7d8..9b9b815d 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ UTF-8 - 2023-05-10T07:42:50Z + 2024-11-28T14:37:52Z 1.0.1 From 3b90fac983bafce882f6f5ef2bffdb7a177a3590 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 14 Oct 2024 15:29:49 +0100 Subject: [PATCH 29/32] Fix occasional stale volume in postgres tests Fixes #1522 --- README.md | 6 +++--- docker-compose.yml | 4 ---- .../samples/petclinic/PostgresIntegrationTests.java | 4 ++-- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index bb3fe977..f1594013 100644 --- a/README.md +++ b/README.md @@ -64,16 +64,16 @@ docker run -e POSTGRES_USER=petclinic -e POSTGRES_PASSWORD=petclinic -e POSTGRES Further documentation is provided for [MySQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt) and [PostgreSQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/postgres/petclinic_db_setup_postgres.txt). -Instead of vanilla `docker` you can also use the provided `docker-compose.yml` file to start the database containers. Each one has a profile just like the Spring profile: +Instead of vanilla `docker` you can also use the provided `docker-compose.yml` file to start the database containers. Each one has a service named after the Spring profile: ```bash -docker-compose --profile mysql up +docker compose up mysql ``` or ```bash -docker-compose --profile postgres up +docker compose up postgres ``` ## Test Applications diff --git a/docker-compose.yml b/docker-compose.yml index 8fdaacbc..47579bba 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,8 +11,6 @@ services: - MYSQL_DATABASE=petclinic volumes: - "./conf.d:/etc/mysql/conf.d:ro" - profiles: - - mysql postgres: image: postgres:17.0 ports: @@ -21,5 +19,3 @@ services: - POSTGRES_PASSWORD=petclinic - POSTGRES_USER=petclinic - POSTGRES_DB=petclinic - profiles: - - postgres diff --git a/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java index 51908132..0b9e4f93 100644 --- a/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java @@ -49,7 +49,7 @@ import org.testcontainers.DockerClientFactory; @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "spring.docker.compose.skip.in-tests=false", // - "spring.docker.compose.profiles.active=postgres" }) + "spring.docker.compose.start.arguments=--force-recreate,--renew-anon-volumes,postgres" }) @ActiveProfiles("postgres") @DisabledInNativeImage public class PostgresIntegrationTests { @@ -72,7 +72,7 @@ public static void main(String[] args) { new SpringApplicationBuilder(PetClinicApplication.class) // .profiles("postgres") // .properties( // - "spring.docker.compose.profiles.active=postgres" // + "spring.docker.compose.start.arguments=postgres" // ) // .listeners(new PropertiesLogger()) // .run(args); From 42e2c74b0bcccd3f547b33ce9856a615c3a821b8 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Thu, 28 Nov 2024 14:45:59 +0000 Subject: [PATCH 30/32] Add a Dockerfile for dev environments other than codespaces --- .devcontainer/Dockerfile | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .devcontainer/Dockerfile diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..f8102bca --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,11 @@ +# Not actually used by the devcontainer, but it is used by gitpod +ARG VARIANT=17-bullseye +FROM mcr.microsoft.com/vscode/devcontainers/java:0-${VARIANT} +ARG NODE_VERSION="none" +RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi +ARG USER=vscode +VOLUME /home/$USER/.m2 +VOLUME /home/$USER/.gradle +ARG JAVA_VERSION=17.0.7-ms +RUN sudo mkdir /home/$USER/.m2 /home/$USER/.gradle && sudo chown $USER:$USER /home/$USER/.m2 /home/$USER/.gradle +RUN bash -lc '. /usr/local/sdkman/bin/sdkman-init.sh && sdk install java $JAVA_VERSION && sdk use java $JAVA_VERSION' \ No newline at end of file From 22caee3d036b6ec34d6045dc7354185919a9d4fc Mon Sep 17 00:00:00 2001 From: feelgood1987 Date: Sat, 30 Nov 2024 17:03:09 +0300 Subject: [PATCH 31/32] feat: add Persian and Turkish localization files for application messages --- src/main/resources/messages/messages_fa.properties | 9 +++++++++ src/main/resources/messages/messages_tr.properties | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100644 src/main/resources/messages/messages_fa.properties create mode 100644 src/main/resources/messages/messages_tr.properties diff --git a/src/main/resources/messages/messages_fa.properties b/src/main/resources/messages/messages_fa.properties new file mode 100644 index 00000000..a68a21c7 --- /dev/null +++ b/src/main/resources/messages/messages_fa.properties @@ -0,0 +1,9 @@ +welcome=خوش آمدید +required=الزامی +notFound=یافت نشد +duplicate=قبلا استفاده شده +nonNumeric=باید عددی باشد +duplicateFormSubmission=ارسال تکراری فرم مجاز نیست +typeMismatch.date=تاریخ نامعتبر +typeMismatch.birthDate=تاریخ تولد نامعتبر + diff --git a/src/main/resources/messages/messages_tr.properties b/src/main/resources/messages/messages_tr.properties new file mode 100644 index 00000000..1020566a --- /dev/null +++ b/src/main/resources/messages/messages_tr.properties @@ -0,0 +1,9 @@ +welcome=hoş geldiniz +required=gerekli +notFound=bulunamadı +duplicate=zaten kullanılıyor +nonNumeric=sadece sayısal olmalıdır +duplicateFormSubmission=Formun tekrar gönderilmesine izin verilmez +typeMismatch.date=geçersiz tarih +typeMismatch.birthDate=geçersiz tarih + From 175ea8429a9ab942e7863cf0ae85e4825f071169 Mon Sep 17 00:00:00 2001 From: Antoine Rey Date: Sun, 8 Dec 2024 19:31:53 +0100 Subject: [PATCH 32/32] Rename artefactId to spring-petclinic-ai --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eb878ed8..e77cdea4 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.springframework.samples - spring-petclinic + spring-petclinic-ai 3.4.0-SNAPSHOT petclinic