forked from DevFW-CICD/spring-petclinic
Compare commits
33 commits
Author | SHA1 | Date | |
---|---|---|---|
2c6b644330 | |||
34ef850730 | |||
![]() |
22caee3d03 | ||
![]() |
42e2c74b0b | ||
![]() |
3b90fac983 | ||
![]() |
300597fc6c | ||
![]() |
40a41375e6 | ||
![]() |
214a8fb87f | ||
![]() |
a0ba075bd8 | ||
![]() |
91f55a4f71 | ||
![]() |
9f1cda1c08 | ||
![]() |
317562a170 | ||
![]() |
1cad4124b7 | ||
![]() |
668629d5bd | ||
![]() |
a3026bddbb | ||
![]() |
50866def72 | ||
![]() |
14af47d4e5 | ||
![]() |
fdc40a7048 | ||
![]() |
a50bfb65bb | ||
![]() |
dff45cf70c | ||
![]() |
90bbb98ea6 | ||
![]() |
bbb237928f | ||
![]() |
912de1648e | ||
![]() |
62dbfa8e9a | ||
![]() |
ae1bb8228c | ||
![]() |
fc442120ce | ||
![]() |
f8001e0add | ||
![]() |
608e2b6142 | ||
![]() |
6fffe61b93 | ||
![]() |
2daa3993ee | ||
![]() |
d90e284621 | ||
![]() |
f6f923bd39 | ||
![]() |
cabb74ed53 |
46 changed files with 739 additions and 243 deletions
11
.devcontainer/Dockerfile
Normal file
11
.devcontainer/Dockerfile
Normal file
|
@ -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'
|
4
.github/workflows/maven-build.yml
vendored
4
.github/workflows/maven-build.yml
vendored
|
@ -1,5 +1,5 @@
|
||||||
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
|
# 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
|
name: Java CI with Maven
|
||||||
|
|
||||||
|
@ -26,4 +26,4 @@ jobs:
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
cache: maven
|
cache: maven
|
||||||
- name: Build with Maven Wrapper
|
- name: Build with Maven Wrapper
|
||||||
run: ./mvnw -B package
|
run: ./mvnw -B verify
|
||||||
|
|
56
.gitignore
vendored
56
.gitignore
vendored
|
@ -1,17 +1,51 @@
|
||||||
target/*
|
HELP.md
|
||||||
bin/*
|
pom.xml.bak
|
||||||
build/*
|
target/
|
||||||
.gradle/*
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
.settings/*
|
!**/src/main/**/target/
|
||||||
.classpath
|
!**/src/test/**/target/
|
||||||
.project
|
.gradle
|
||||||
.factorypath
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
.attach_pid*
|
.attach_pid*
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
.idea
|
.idea
|
||||||
|
*.iws
|
||||||
*.iml
|
*.iml
|
||||||
/target
|
*.ipr
|
||||||
.sts4-cache/
|
out/
|
||||||
.vscode
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### SDK Man ###
|
||||||
|
.sdkmanrc
|
||||||
|
|
||||||
|
### CSS ###
|
||||||
_site/
|
_site/
|
||||||
*.css
|
*.css
|
||||||
!petclinic.css
|
!petclinic.css
|
||||||
|
|
18
.mvn/wrapper/maven-wrapper.properties
vendored
18
.mvn/wrapper/maven-wrapper.properties
vendored
|
@ -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
|
wrapperVersion=3.3.2
|
||||||
distributionType=only-script
|
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
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Spring PetClinic Sample Application [](https://github.com/spring-projects/spring-petclinic/actions/workflows/maven-build.yml)
|
# Spring PetClinic Sample Application [](https://github.com/spring-projects/spring-petclinic/actions/workflows/maven-build.yml)[](https://github.com/spring-projects/spring-petclinic/actions/workflows/gradle-build.yml)
|
||||||
|
|
||||||
[](https://gitpod.io/#https://github.com/spring-projects/spring-petclinic) [](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=7517918)
|
[](https://gitpod.io/#https://github.com/spring-projects/spring-petclinic) [](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=7517918)
|
||||||
|
|
||||||
|
@ -52,28 +52,28 @@ 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:
|
You can start MySQL or PostgreSQL locally with whatever installer works for your OS or use docker:
|
||||||
|
|
||||||
```bash
|
```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.1
|
||||||
```
|
```
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
```bash
|
```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)
|
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).
|
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
|
```bash
|
||||||
docker-compose --profile mysql up
|
docker compose up mysql
|
||||||
```
|
```
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose --profile postgres up
|
docker compose up postgres
|
||||||
```
|
```
|
||||||
|
|
||||||
## Test Applications
|
## Test Applications
|
24
build.gradle
24
build.gradle
|
@ -1,10 +1,10 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'org.springframework.boot' version '3.3.2'
|
id 'org.springframework.boot' version '3.4.0'
|
||||||
id 'io.spring.dependency-management' version '1.1.5'
|
id 'io.spring.dependency-management' version '1.1.6'
|
||||||
id 'org.graalvm.buildtools.native' version '0.10.2'
|
id 'org.graalvm.buildtools.native' version '0.10.3'
|
||||||
id 'org.cyclonedx.bom' version '1.8.2'
|
id 'org.cyclonedx.bom' version '1.10.0'
|
||||||
id 'io.spring.javaformat' version '0.0.41'
|
id 'io.spring.javaformat' version '0.0.43'
|
||||||
id "io.spring.nohttp" version "0.0.11"
|
id "io.spring.nohttp" version "0.0.11"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ apply plugin: 'io.spring.javaformat'
|
||||||
gradle.startParameter.excludedTaskNames += [ "checkFormatAot", "checkFormatAotTest" ]
|
gradle.startParameter.excludedTaskNames += [ "checkFormatAot", "checkFormatAotTest" ]
|
||||||
|
|
||||||
group = 'org.springframework.samples'
|
group = 'org.springframework.samples'
|
||||||
version = '3.3.0'
|
version = '3.4.0'
|
||||||
|
|
||||||
java {
|
java {
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
@ -25,18 +25,26 @@ repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ext.checkstyleVersion = "10.20.1"
|
||||||
|
ext.springJavaformatCheckstyleVersion = "0.0.43"
|
||||||
|
ext.webjarsLocatorLiteVersion = "1.0.1"
|
||||||
ext.webjarsFontawesomeVersion = "4.7.0"
|
ext.webjarsFontawesomeVersion = "4.7.0"
|
||||||
ext.webjarsBootstrapVersion = "5.3.3"
|
ext.webjarsBootstrapVersion = "5.3.3"
|
||||||
|
|
||||||
dependencies {
|
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-cache'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||||
|
implementation 'io.micrometer:micrometer-registry-prometheus'
|
||||||
implementation 'javax.cache:cache-api'
|
implementation 'javax.cache:cache-api'
|
||||||
implementation 'jakarta.xml.bind:jakarta.xml.bind-api'
|
implementation 'jakarta.xml.bind:jakarta.xml.bind-api'
|
||||||
runtimeOnly 'org.springframework.boot:spring-boot-starter-actuator'
|
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:bootstrap:${webjarsBootstrapVersion}"
|
||||||
runtimeOnly "org.webjars.npm:font-awesome:${webjarsFontawesomeVersion}"
|
runtimeOnly "org.webjars.npm:font-awesome:${webjarsFontawesomeVersion}"
|
||||||
runtimeOnly 'com.github.ben-manes.caffeine:caffeine'
|
runtimeOnly 'com.github.ben-manes.caffeine:caffeine'
|
||||||
|
@ -49,8 +57,8 @@ dependencies {
|
||||||
testImplementation 'org.springframework.boot:spring-boot-docker-compose'
|
testImplementation 'org.springframework.boot:spring-boot-docker-compose'
|
||||||
testImplementation 'org.testcontainers:junit-jupiter'
|
testImplementation 'org.testcontainers:junit-jupiter'
|
||||||
testImplementation 'org.testcontainers:mysql'
|
testImplementation 'org.testcontainers:mysql'
|
||||||
checkstyle 'io.spring.javaformat:spring-javaformat-checkstyle:0.0.41'
|
checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${springJavaformatCheckstyleVersion}"
|
||||||
checkstyle 'com.puppycrawl.tools:checkstyle:10.16.0'
|
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named('test') {
|
tasks.named('test') {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
services:
|
services:
|
||||||
mysql:
|
mysql:
|
||||||
image: mysql:8.4
|
image: mysql:9.1
|
||||||
ports:
|
ports:
|
||||||
- "3306:3306"
|
- "3306:3306"
|
||||||
environment:
|
environment:
|
||||||
|
@ -11,15 +11,11 @@ services:
|
||||||
- MYSQL_DATABASE=petclinic
|
- MYSQL_DATABASE=petclinic
|
||||||
volumes:
|
volumes:
|
||||||
- "./conf.d:/etc/mysql/conf.d:ro"
|
- "./conf.d:/etc/mysql/conf.d:ro"
|
||||||
profiles:
|
|
||||||
- mysql
|
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:16.3
|
image: postgres:17.0
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_PASSWORD=petclinic
|
- POSTGRES_PASSWORD=petclinic
|
||||||
- POSTGRES_USER=petclinic
|
- POSTGRES_USER=petclinic
|
||||||
- POSTGRES_DB=petclinic
|
- POSTGRES_DB=petclinic
|
||||||
profiles:
|
|
||||||
- postgres
|
|
||||||
|
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
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
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|
7
gradlew
vendored
7
gradlew
vendored
|
@ -15,6 +15,8 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
|
@ -55,7 +57,7 @@
|
||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (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.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
@ -84,7 +86,8 @@ done
|
||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
# 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.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
|
|
2
gradlew.bat
vendored
2
gradlew.bat
vendored
|
@ -13,6 +13,8 @@
|
||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
|
|
73
k8s/db.yml
Normal file
73
k8s/db.yml
Normal file
|
@ -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
|
64
k8s/petclinic.yml
Normal file
64
k8s/petclinic.yml
Normal file
|
@ -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
|
112
pom.xml
112
pom.xml
|
@ -1,17 +1,18 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.springframework.samples</groupId>
|
|
||||||
<artifactId>spring-petclinic</artifactId>
|
|
||||||
<version>3.3.0-SNAPSHOT</version>
|
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
<version>3.3.2</version>
|
<version>3.4.0</version>
|
||||||
|
<relativePath></relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
<groupId>org.springframework.samples</groupId>
|
||||||
|
<artifactId>spring-petclinic</artifactId>
|
||||||
|
<version>3.4.0-SNAPSHOT</version>
|
||||||
|
|
||||||
<name>petclinic</name>
|
<name>petclinic</name>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
@ -20,20 +21,22 @@
|
||||||
<java.version>17</java.version>
|
<java.version>17</java.version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<!-- Important for reproducible builds. Update using e.g. ./mvnw versions:set -DnewVersion=... -->
|
<!-- Important for reproducible builds. Update using e.g. ./mvnw versions:set
|
||||||
<project.build.outputTimestamp>2023-05-10T07:42:50Z</project.build.outputTimestamp>
|
-DnewVersion=... -->
|
||||||
|
<project.build.outputTimestamp>2024-11-28T14:37:52Z</project.build.outputTimestamp>
|
||||||
|
|
||||||
<!-- Web dependencies -->
|
<!-- Web dependencies -->
|
||||||
|
<webjars-locator.version>1.0.1</webjars-locator.version>
|
||||||
<webjars-bootstrap.version>5.3.3</webjars-bootstrap.version>
|
<webjars-bootstrap.version>5.3.3</webjars-bootstrap.version>
|
||||||
<webjars-font-awesome.version>4.7.0</webjars-font-awesome.version>
|
<webjars-font-awesome.version>4.7.0</webjars-font-awesome.version>
|
||||||
|
|
||||||
<checkstyle.version>10.16.0</checkstyle.version>
|
<checkstyle.version>10.20.1</checkstyle.version>
|
||||||
<jacoco.version>0.8.12</jacoco.version>
|
<jacoco.version>0.8.12</jacoco.version>
|
||||||
<libsass.version>0.2.29</libsass.version>
|
<libsass.version>0.2.29</libsass.version>
|
||||||
<lifecycle-mapping>1.0.0</lifecycle-mapping>
|
<lifecycle-mapping>1.0.0</lifecycle-mapping>
|
||||||
<maven-checkstyle.version>3.3.1</maven-checkstyle.version>
|
<maven-checkstyle.version>3.6.0</maven-checkstyle.version>
|
||||||
<nohttp-checkstyle.version>0.0.11</nohttp-checkstyle.version>
|
<nohttp-checkstyle.version>0.0.11</nohttp-checkstyle.version>
|
||||||
<spring-format.version>0.0.41</spring-format.version>
|
<spring-format.version>0.0.43</spring-format.version>
|
||||||
|
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
@ -68,6 +71,11 @@
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<!-- Workaround for AOT issue (https://github.com/spring-projects/spring-framework/pull/33949) -->
|
||||||
|
<groupId>io.projectreactor</groupId>
|
||||||
|
<artifactId>reactor-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Databases - Uses H2 by default -->
|
<!-- Databases - Uses H2 by default -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -97,6 +105,11 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Webjars -->
|
<!-- Webjars -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.webjars</groupId>
|
||||||
|
<artifactId>webjars-locator-lite</artifactId>
|
||||||
|
<version>${webjars-locator.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.webjars.npm</groupId>
|
<groupId>org.webjars.npm</groupId>
|
||||||
<artifactId>bootstrap</artifactId>
|
<artifactId>bootstrap</artifactId>
|
||||||
|
@ -139,6 +152,11 @@
|
||||||
<artifactId>jakarta.xml.bind-api</artifactId>
|
<artifactId>jakarta.xml.bind-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@ -155,8 +173,9 @@
|
||||||
<configuration>
|
<configuration>
|
||||||
<rules>
|
<rules>
|
||||||
<requireJavaVersion>
|
<requireJavaVersion>
|
||||||
<message>This build requires at least Java ${java.version}, update your JVM, and
|
<message>This build requires at least Java ${java.version},
|
||||||
run the build again</message>
|
update your JVM, and
|
||||||
|
run the build again</message>
|
||||||
<version>${java.version}</version>
|
<version>${java.version}</version>
|
||||||
</requireJavaVersion>
|
</requireJavaVersion>
|
||||||
</rules>
|
</rules>
|
||||||
|
@ -170,10 +189,10 @@
|
||||||
<version>${spring-format.version}</version>
|
<version>${spring-format.version}</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<phase>validate</phase>
|
|
||||||
<goals>
|
<goals>
|
||||||
<goal>validate</goal>
|
<goal>validate</goal>
|
||||||
</goals>
|
</goals>
|
||||||
|
<phase>validate</phase>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
@ -196,19 +215,17 @@
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>nohttp-checkstyle-validation</id>
|
<id>nohttp-checkstyle-validation</id>
|
||||||
|
<goals>
|
||||||
|
<goal>check</goal>
|
||||||
|
</goals>
|
||||||
<phase>validate</phase>
|
<phase>validate</phase>
|
||||||
<configuration>
|
<configuration>
|
||||||
<configLocation>src/checkstyle/nohttp-checkstyle.xml</configLocation>
|
<configLocation>src/checkstyle/nohttp-checkstyle.xml</configLocation>
|
||||||
<sourceDirectories>${basedir}</sourceDirectories>
|
<sourceDirectories>${basedir}</sourceDirectories>
|
||||||
<includes>**/*</includes>
|
<includes>**/*</includes>
|
||||||
<excludes>**/.git/**/*,**/.idea/**/*,**/target/**/,**/.flattened-pom.xml,**/*.class</excludes>
|
<excludes>**/.git/**/*,**/.idea/**/*,**/target/**/,**/.flattened-pom.xml,**/*.class</excludes>
|
||||||
<propertyExpansion>
|
<propertyExpansion>config_loc=${basedir}/src/checkstyle/</propertyExpansion>
|
||||||
config_loc=${basedir}/src/checkstyle/
|
|
||||||
</propertyExpansion>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
<goals>
|
|
||||||
<goal>check</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
@ -249,10 +266,10 @@
|
||||||
</execution>
|
</execution>
|
||||||
<execution>
|
<execution>
|
||||||
<id>report</id>
|
<id>report</id>
|
||||||
<phase>prepare-package</phase>
|
|
||||||
<goals>
|
<goals>
|
||||||
<goal>report</goal>
|
<goal>report</goal>
|
||||||
</goals>
|
</goals>
|
||||||
|
<phase>prepare-package</phase>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
@ -270,13 +287,13 @@
|
||||||
<!-- Spring Boot Actuator displays sbom-related information if a CycloneDX SBOM file is
|
<!-- Spring Boot Actuator displays sbom-related information if a CycloneDX SBOM file is
|
||||||
present at the classpath -->
|
present at the classpath -->
|
||||||
<plugin>
|
<plugin>
|
||||||
|
<?m2e ignore?>
|
||||||
<groupId>org.cyclonedx</groupId>
|
<groupId>org.cyclonedx</groupId>
|
||||||
<artifactId>cyclonedx-maven-plugin</artifactId>
|
<artifactId>cyclonedx-maven-plugin</artifactId>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<licenses>
|
<licenses>
|
||||||
<license>
|
<license>
|
||||||
<name>Apache License, Version 2.0</name>
|
<name>Apache License, Version 2.0</name>
|
||||||
|
@ -286,39 +303,38 @@
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
<id>spring-snapshots</id>
|
|
||||||
<name>Spring Snapshots</name>
|
|
||||||
<url>https://repo.spring.io/snapshot</url>
|
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>true</enabled>
|
<enabled>true</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
|
<id>spring-snapshots</id>
|
||||||
|
<name>Spring Snapshots</name>
|
||||||
|
<url>https://repo.spring.io/snapshot</url>
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>spring-milestones</id>
|
|
||||||
<name>Spring Milestones</name>
|
|
||||||
<url>https://repo.spring.io/milestone</url>
|
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>false</enabled>
|
<enabled>false</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/milestone</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<pluginRepositories>
|
<pluginRepositories>
|
||||||
<pluginRepository>
|
<pluginRepository>
|
||||||
<id>spring-snapshots</id>
|
|
||||||
<name>Spring Snapshots</name>
|
|
||||||
<url>https://repo.spring.io/snapshot</url>
|
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>true</enabled>
|
<enabled>true</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
|
<id>spring-snapshots</id>
|
||||||
|
<name>Spring Snapshots</name>
|
||||||
|
<url>https://repo.spring.io/snapshot</url>
|
||||||
</pluginRepository>
|
</pluginRepository>
|
||||||
<pluginRepository>
|
<pluginRepository>
|
||||||
<id>spring-milestones</id>
|
|
||||||
<name>Spring Milestones</name>
|
|
||||||
<url>https://repo.spring.io/milestone</url>
|
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>false</enabled>
|
<enabled>false</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/milestone</url>
|
||||||
</pluginRepository>
|
</pluginRepository>
|
||||||
</pluginRepositories>
|
</pluginRepositories>
|
||||||
|
|
||||||
|
@ -333,11 +349,11 @@
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>unpack</id>
|
<id>unpack</id>
|
||||||
<?m2e execute onConfiguration,onIncremental?>
|
|
||||||
<phase>generate-resources</phase>
|
|
||||||
<goals>
|
<goals>
|
||||||
<goal>unpack</goal>
|
<goal>unpack</goal>
|
||||||
</goals>
|
</goals>
|
||||||
|
<?m2e execute onConfiguration,onIncremental?>
|
||||||
|
<phase>generate-resources</phase>
|
||||||
<configuration>
|
<configuration>
|
||||||
<artifactItems>
|
<artifactItems>
|
||||||
<artifactItem>
|
<artifactItem>
|
||||||
|
@ -356,21 +372,20 @@
|
||||||
<groupId>com.gitlab.haynes</groupId>
|
<groupId>com.gitlab.haynes</groupId>
|
||||||
<artifactId>libsass-maven-plugin</artifactId>
|
<artifactId>libsass-maven-plugin</artifactId>
|
||||||
<version>${libsass.version}</version>
|
<version>${libsass.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<inputPath>${basedir}/src/main/scss/</inputPath>
|
||||||
|
<outputPath>${basedir}/src/main/resources/static/resources/css/</outputPath>
|
||||||
|
<includePath>${project.build.directory}/webjars/META-INF/resources/webjars/bootstrap/${webjars-bootstrap.version}/scss/</includePath>
|
||||||
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<phase>generate-resources</phase>
|
|
||||||
<?m2e execute onConfiguration,onIncremental?>
|
<?m2e execute onConfiguration,onIncremental?>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>compile</goal>
|
<goal>compile</goal>
|
||||||
</goals>
|
</goals>
|
||||||
|
<phase>generate-resources</phase>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
<configuration>
|
|
||||||
<inputPath>${basedir}/src/main/scss/</inputPath>
|
|
||||||
<outputPath>${basedir}/src/main/resources/static/resources/css/</outputPath>
|
|
||||||
<includePath>
|
|
||||||
${project.build.directory}/webjars/META-INF/resources/webjars/bootstrap/${webjars-bootstrap.version}/scss/</includePath>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
@ -404,7 +419,7 @@
|
||||||
</goals>
|
</goals>
|
||||||
</pluginExecutionFilter>
|
</pluginExecutionFilter>
|
||||||
<action>
|
<action>
|
||||||
<ignore />
|
<ignore></ignore>
|
||||||
</action>
|
</action>
|
||||||
</pluginExecution>
|
</pluginExecution>
|
||||||
<pluginExecution>
|
<pluginExecution>
|
||||||
|
@ -417,7 +432,7 @@
|
||||||
</goals>
|
</goals>
|
||||||
</pluginExecutionFilter>
|
</pluginExecutionFilter>
|
||||||
<action>
|
<action>
|
||||||
<ignore />
|
<ignore></ignore>
|
||||||
</action>
|
</action>
|
||||||
</pluginExecution>
|
</pluginExecution>
|
||||||
<pluginExecution>
|
<pluginExecution>
|
||||||
|
@ -430,7 +445,7 @@
|
||||||
</goals>
|
</goals>
|
||||||
</pluginExecutionFilter>
|
</pluginExecutionFilter>
|
||||||
<action>
|
<action>
|
||||||
<ignore />
|
<ignore></ignore>
|
||||||
</action>
|
</action>
|
||||||
</pluginExecution>
|
</pluginExecution>
|
||||||
</pluginExecutions>
|
</pluginExecutions>
|
||||||
|
@ -442,5 +457,4 @@
|
||||||
</build>
|
</build>
|
||||||
</profile>
|
</profile>
|
||||||
</profiles>
|
</profiles>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -18,11 +18,7 @@ package org.springframework.samples.petclinic;
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.ImportRuntimeHints;
|
import org.springframework.context.annotation.ImportRuntimeHints;
|
||||||
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PetClinic Spring Boot Application.
|
* PetClinic Spring Boot Application.
|
||||||
|
|
|
@ -28,7 +28,6 @@ public class PetClinicRuntimeHints implements RuntimeHintsRegistrar {
|
||||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||||
hints.resources().registerPattern("db/*"); // https://github.com/spring-projects/spring-boot/issues/32654
|
hints.resources().registerPattern("db/*"); // https://github.com/spring-projects/spring-boot/issues/32654
|
||||||
hints.resources().registerPattern("messages/*");
|
hints.resources().registerPattern("messages/*");
|
||||||
hints.resources().registerPattern("META-INF/resources/webjars/*");
|
|
||||||
hints.resources().registerPattern("mysql-default-conf");
|
hints.resources().registerPattern("mysql-default-conf");
|
||||||
hints.serialization().registerType(BaseEntity.class);
|
hints.serialization().registerType(BaseEntity.class);
|
||||||
hints.serialization().registerType(Person.class);
|
hints.serialization().registerType(Person.class);
|
||||||
|
|
|
@ -17,6 +17,7 @@ package org.springframework.samples.petclinic.model;
|
||||||
|
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.MappedSuperclass;
|
import jakarta.persistence.MappedSuperclass;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple JavaBean domain object adds a name property to <code>BaseEntity</code>. Used as
|
* Simple JavaBean domain object adds a name property to <code>BaseEntity</code>. Used as
|
||||||
|
@ -24,11 +25,13 @@ import jakarta.persistence.MappedSuperclass;
|
||||||
*
|
*
|
||||||
* @author Ken Krebs
|
* @author Ken Krebs
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
public class NamedEntity extends BaseEntity {
|
public class NamedEntity extends BaseEntity {
|
||||||
|
|
||||||
@Column(name = "name")
|
@Column(name = "name")
|
||||||
|
@NotBlank
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
|
|
@ -41,6 +41,7 @@ import jakarta.validation.constraints.NotBlank;
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @author Michael Isvy
|
* @author Michael Isvy
|
||||||
* @author Oliver Drotbohm
|
* @author Oliver Drotbohm
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "owners")
|
@Table(name = "owners")
|
||||||
|
@ -62,7 +63,7 @@ public class Owner extends Person {
|
||||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
@JoinColumn(name = "owner_id")
|
@JoinColumn(name = "owner_id")
|
||||||
@OrderBy("name")
|
@OrderBy("name")
|
||||||
private List<Pet> pets = new ArrayList<>();
|
private final List<Pet> pets = new ArrayList<>();
|
||||||
|
|
||||||
public String getAddress() {
|
public String getAddress() {
|
||||||
return this.address;
|
return this.address;
|
||||||
|
@ -101,7 +102,7 @@ public class Owner extends Person {
|
||||||
/**
|
/**
|
||||||
* Return the Pet with the given name, or null if none found for this Owner.
|
* Return the Pet with the given name, or null if none found for this Owner.
|
||||||
* @param name to test
|
* @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) {
|
public Pet getPet(String name) {
|
||||||
return getPet(name, false);
|
return getPet(name, false);
|
||||||
|
@ -110,7 +111,7 @@ public class Owner extends Person {
|
||||||
/**
|
/**
|
||||||
* Return the Pet with the given id, or null if none found for this Owner.
|
* Return the Pet with the given id, or null if none found for this Owner.
|
||||||
* @param id to test
|
* @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) {
|
public Pet getPet(Integer id) {
|
||||||
for (Pet pet : getPets()) {
|
for (Pet pet : getPets()) {
|
||||||
|
@ -127,10 +128,10 @@ public class Owner extends Person {
|
||||||
/**
|
/**
|
||||||
* Return the Pet with the given name, or null if none found for this Owner.
|
* Return the Pet with the given name, or null if none found for this Owner.
|
||||||
* @param name to test
|
* @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) {
|
public Pet getPet(String name, boolean ignoreNew) {
|
||||||
name = name.toLowerCase();
|
|
||||||
for (Pet pet : getPets()) {
|
for (Pet pet : getPets()) {
|
||||||
String compName = pet.getName();
|
String compName = pet.getName();
|
||||||
if (compName != null && compName.equalsIgnoreCase(name)) {
|
if (compName != null && compName.equalsIgnoreCase(name)) {
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Optional;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
@ -41,6 +41,7 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
* @author Ken Krebs
|
* @author Ken Krebs
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Michael Isvy
|
* @author Michael Isvy
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
class OwnerController {
|
class OwnerController {
|
||||||
|
@ -49,8 +50,8 @@ class OwnerController {
|
||||||
|
|
||||||
private final OwnerRepository owners;
|
private final OwnerRepository owners;
|
||||||
|
|
||||||
public OwnerController(OwnerRepository clinicService) {
|
public OwnerController(OwnerRepository owners) {
|
||||||
this.owners = clinicService;
|
this.owners = owners;
|
||||||
}
|
}
|
||||||
|
|
||||||
@InitBinder
|
@InitBinder
|
||||||
|
@ -60,13 +61,14 @@ class OwnerController {
|
||||||
|
|
||||||
@ModelAttribute("owner")
|
@ModelAttribute("owner")
|
||||||
public Owner findOwner(@PathVariable(name = "ownerId", required = false) Integer ownerId) {
|
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")
|
@GetMapping("/owners/new")
|
||||||
public String initCreationForm(Map<String, Object> model) {
|
public String initCreationForm() {
|
||||||
Owner owner = new Owner();
|
|
||||||
model.put("owner", owner);
|
|
||||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,13 +127,11 @@ class OwnerController {
|
||||||
private Page<Owner> findPaginatedForOwnersLastName(int page, String lastname) {
|
private Page<Owner> findPaginatedForOwnersLastName(int page, String lastname) {
|
||||||
int pageSize = 5;
|
int pageSize = 5;
|
||||||
Pageable pageable = PageRequest.of(page - 1, pageSize);
|
Pageable pageable = PageRequest.of(page - 1, pageSize);
|
||||||
return owners.findByLastName(lastname, pageable);
|
return owners.findByLastNameStartingWith(lastname, pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/owners/{ownerId}/edit")
|
@GetMapping("/owners/{ownerId}/edit")
|
||||||
public String initUpdateOwnerForm(@PathVariable("ownerId") int ownerId, Model model) {
|
public String initUpdateOwnerForm() {
|
||||||
Owner owner = this.owners.findById(ownerId);
|
|
||||||
model.addAttribute(owner);
|
|
||||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,6 +143,12 @@ class OwnerController {
|
||||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
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);
|
owner.setId(ownerId);
|
||||||
this.owners.save(owner);
|
this.owners.save(owner);
|
||||||
redirectAttributes.addFlashAttribute("message", "Owner Values Updated");
|
redirectAttributes.addFlashAttribute("message", "Owner Values Updated");
|
||||||
|
@ -157,7 +163,9 @@ class OwnerController {
|
||||||
@GetMapping("/owners/{ownerId}")
|
@GetMapping("/owners/{ownerId}")
|
||||||
public ModelAndView showOwner(@PathVariable("ownerId") int ownerId) {
|
public ModelAndView showOwner(@PathVariable("ownerId") int ownerId) {
|
||||||
ModelAndView mav = new ModelAndView("owners/ownerDetails");
|
ModelAndView mav = new ModelAndView("owners/ownerDetails");
|
||||||
Owner owner = this.owners.findById(ownerId);
|
Optional<Owner> 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);
|
mav.addObject(owner);
|
||||||
return mav;
|
return mav;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,13 @@
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.Repository;
|
|
||||||
import org.springframework.data.repository.query.Param;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,15 +35,15 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @author Michael Isvy
|
* @author Michael Isvy
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
public interface OwnerRepository extends Repository<Owner, Integer> {
|
public interface OwnerRepository extends JpaRepository<Owner, Integer> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve all {@link PetType}s from the data store.
|
* Retrieve all {@link PetType}s from the data store.
|
||||||
* @return a Collection of {@link PetType}s.
|
* @return a Collection of {@link PetType}s.
|
||||||
*/
|
*/
|
||||||
@Query("SELECT ptype FROM PetType ptype ORDER BY ptype.name")
|
@Query("SELECT ptype FROM PetType ptype ORDER BY ptype.name")
|
||||||
@Transactional(readOnly = true)
|
|
||||||
List<PetType> findPetTypes();
|
List<PetType> findPetTypes();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,31 +53,26 @@ public interface OwnerRepository extends Repository<Owner, Integer> {
|
||||||
* @return a Collection of matching {@link Owner}s (or an empty Collection if none
|
* @return a Collection of matching {@link Owner}s (or an empty Collection if none
|
||||||
* found)
|
* found)
|
||||||
*/
|
*/
|
||||||
|
Page<Owner> findByLastNameStartingWith(String lastName, Pageable pageable);
|
||||||
@Query("SELECT DISTINCT owner FROM Owner owner left join owner.pets WHERE owner.lastName LIKE :lastName% ")
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
Page<Owner> findByLastName(@Param("lastName") String lastName, Pageable pageable);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve an {@link Owner} from the data store by id.
|
* Retrieve an {@link Owner} from the data store by id.
|
||||||
|
* <p>
|
||||||
|
* 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}.
|
||||||
|
* </p>
|
||||||
* @param id the id to search for
|
* @param id the id to search for
|
||||||
* @return the {@link Owner} if found
|
* @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)
|
||||||
*/
|
*/
|
||||||
@Query("SELECT owner FROM Owner owner left join fetch owner.pets WHERE owner.id =:id")
|
Optional<Owner> findById(@Nonnull Integer 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
|
|
||||||
*/
|
|
||||||
void save(Owner owner);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all the owners from data store
|
* Returns all the owners from data store
|
||||||
**/
|
**/
|
||||||
@Query("SELECT owner FROM Owner owner")
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
Page<Owner> findAll(Pageable pageable);
|
Page<Owner> findAll(Pageable pageable);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
3
src/main/java/org/springframework/samples/petclinic/owner/Pet.java
Executable file → Normal file
3
src/main/java/org/springframework/samples/petclinic/owner/Pet.java
Executable file → Normal file
|
@ -39,6 +39,7 @@ import jakarta.persistence.Table;
|
||||||
* @author Ken Krebs
|
* @author Ken Krebs
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "pets")
|
@Table(name = "pets")
|
||||||
|
@ -55,7 +56,7 @@ public class Pet extends NamedEntity {
|
||||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
@JoinColumn(name = "pet_id")
|
@JoinColumn(name = "pet_id")
|
||||||
@OrderBy("visit_date ASC")
|
@OrderBy("visit_date ASC")
|
||||||
private Set<Visit> visits = new LinkedHashSet<>();
|
private final Set<Visit> visits = new LinkedHashSet<>();
|
||||||
|
|
||||||
public void setBirthDate(LocalDate birthDate) {
|
public void setBirthDate(LocalDate birthDate) {
|
||||||
this.birthDate = birthDate;
|
this.birthDate = birthDate;
|
||||||
|
|
|
@ -17,6 +17,7 @@ package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.ModelMap;
|
import org.springframework.ui.ModelMap;
|
||||||
|
@ -37,6 +38,7 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Ken Krebs
|
* @author Ken Krebs
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping("/owners/{ownerId}")
|
@RequestMapping("/owners/{ownerId}")
|
||||||
|
@ -57,11 +59,9 @@ class PetController {
|
||||||
|
|
||||||
@ModelAttribute("owner")
|
@ModelAttribute("owner")
|
||||||
public Owner findOwner(@PathVariable("ownerId") int ownerId) {
|
public Owner findOwner(@PathVariable("ownerId") int ownerId) {
|
||||||
|
Optional<Owner> optionalOwner = this.owners.findById(ownerId);
|
||||||
Owner owner = this.owners.findById(ownerId);
|
Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException(
|
||||||
if (owner == null) {
|
"Owner not found with id: " + ownerId + ". Please ensure the ID is correct "));
|
||||||
throw new IllegalArgumentException("Owner ID not found: " + ownerId);
|
|
||||||
}
|
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,10 +73,9 @@ class PetController {
|
||||||
return new Pet();
|
return new Pet();
|
||||||
}
|
}
|
||||||
|
|
||||||
Owner owner = this.owners.findById(ownerId);
|
Optional<Owner> optionalOwner = this.owners.findById(ownerId);
|
||||||
if (owner == null) {
|
Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException(
|
||||||
throw new IllegalArgumentException("Owner ID not found: " + ownerId);
|
"Owner not found with id: " + ownerId + ". Please ensure the ID is correct "));
|
||||||
}
|
|
||||||
return owner.getPet(petId);
|
return owner.getPet(petId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,51 +93,46 @@ class PetController {
|
||||||
public String initCreationForm(Owner owner, ModelMap model) {
|
public String initCreationForm(Owner owner, ModelMap model) {
|
||||||
Pet pet = new Pet();
|
Pet pet = new Pet();
|
||||||
owner.addPet(pet);
|
owner.addPet(pet);
|
||||||
model.put("pet", pet);
|
|
||||||
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/pets/new")
|
@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) {
|
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");
|
result.rejectValue("name", "duplicate", "already exists");
|
||||||
}
|
|
||||||
|
|
||||||
LocalDate currentDate = LocalDate.now();
|
LocalDate currentDate = LocalDate.now();
|
||||||
if (pet.getBirthDate() != null && pet.getBirthDate().isAfter(currentDate)) {
|
if (pet.getBirthDate() != null && pet.getBirthDate().isAfter(currentDate)) {
|
||||||
result.rejectValue("birthDate", "typeMismatch.birthDate");
|
result.rejectValue("birthDate", "typeMismatch.birthDate");
|
||||||
}
|
}
|
||||||
|
|
||||||
owner.addPet(pet);
|
|
||||||
if (result.hasErrors()) {
|
if (result.hasErrors()) {
|
||||||
model.put("pet", pet);
|
|
||||||
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
owner.addPet(pet);
|
||||||
this.owners.save(owner);
|
this.owners.save(owner);
|
||||||
redirectAttributes.addFlashAttribute("message", "New Pet has been Added");
|
redirectAttributes.addFlashAttribute("message", "New Pet has been Added");
|
||||||
return "redirect:/owners/{ownerId}";
|
return "redirect:/owners/{ownerId}";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/pets/{petId}/edit")
|
@GetMapping("/pets/{petId}/edit")
|
||||||
public String initUpdateForm(Owner owner, @PathVariable("petId") int petId, ModelMap model,
|
public String initUpdateForm() {
|
||||||
RedirectAttributes redirectAttributes) {
|
|
||||||
Pet pet = owner.getPet(petId);
|
|
||||||
model.put("pet", pet);
|
|
||||||
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/pets/{petId}/edit")
|
@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) {
|
RedirectAttributes redirectAttributes) {
|
||||||
|
|
||||||
String petName = pet.getName();
|
String petName = pet.getName();
|
||||||
|
|
||||||
// checking if the pet name already exist for the owner
|
// checking if the pet name already exist for the owner
|
||||||
if (StringUtils.hasText(petName)) {
|
if (StringUtils.hasText(petName)) {
|
||||||
Pet existingPet = owner.getPet(petName.toLowerCase(), false);
|
Pet existingPet = owner.getPet(petName, false);
|
||||||
if (existingPet != null && existingPet.getId() != pet.getId()) {
|
if (existingPet != null && !existingPet.getId().equals(pet.getId())) {
|
||||||
result.rejectValue("name", "duplicate", "already exists");
|
result.rejectValue("name", "duplicate", "already exists");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,7 +143,6 @@ class PetController {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.hasErrors()) {
|
if (result.hasErrors()) {
|
||||||
model.put("pet", pet);
|
|
||||||
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
0
src/main/java/org/springframework/samples/petclinic/owner/Visit.java
Executable file → Normal file
0
src/main/java/org/springframework/samples/petclinic/owner/Visit.java
Executable file → Normal file
|
@ -16,6 +16,7 @@
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
|
@ -35,6 +36,7 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Michael Isvy
|
* @author Michael Isvy
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
class VisitController {
|
class VisitController {
|
||||||
|
@ -60,7 +62,9 @@ class VisitController {
|
||||||
@ModelAttribute("visit")
|
@ModelAttribute("visit")
|
||||||
public Visit loadPetWithVisit(@PathVariable("ownerId") int ownerId, @PathVariable("petId") int petId,
|
public Visit loadPetWithVisit(@PathVariable("ownerId") int ownerId, @PathVariable("petId") int petId,
|
||||||
Map<String, Object> model) {
|
Map<String, Object> model) {
|
||||||
Owner owner = this.owners.findById(ownerId);
|
Optional<Owner> 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);
|
Pet pet = owner.getPet(petId);
|
||||||
model.put("pet", pet);
|
model.put("pet", pet);
|
||||||
|
|
0
src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java
Executable file → Normal file
0
src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java
Executable file → Normal file
|
@ -57,10 +57,6 @@ public class Vet extends Person {
|
||||||
return this.specialties;
|
return this.specialties;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setSpecialtiesInternal(Set<Specialty> specialties) {
|
|
||||||
this.specialties = specialties;
|
|
||||||
}
|
|
||||||
|
|
||||||
@XmlElement
|
@XmlElement
|
||||||
public List<Specialty> getSpecialties() {
|
public List<Specialty> getSpecialties() {
|
||||||
List<Specialty> sortedSpecs = new ArrayList<>(getSpecialtiesInternal());
|
List<Specialty> sortedSpecs = new ArrayList<>(getSpecialtiesInternal());
|
||||||
|
|
|
@ -37,8 +37,8 @@ class VetController {
|
||||||
|
|
||||||
private final VetRepository vetRepository;
|
private final VetRepository vetRepository;
|
||||||
|
|
||||||
public VetController(VetRepository clinicService) {
|
public VetController(VetRepository vetRepository) {
|
||||||
this.vetRepository = clinicService;
|
this.vetRepository = vetRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/vets.html")
|
@GetMapping("/vets.html")
|
||||||
|
|
|
@ -8,7 +8,7 @@ spring.thymeleaf.mode=HTML
|
||||||
|
|
||||||
# JPA
|
# JPA
|
||||||
spring.jpa.hibernate.ddl-auto=none
|
spring.jpa.hibernate.ddl-auto=none
|
||||||
spring.jpa.open-in-view=true
|
spring.jpa.open-in-view=false
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
spring.messages.basename=messages/messages
|
spring.messages.basename=messages/messages
|
||||||
|
|
9
src/main/resources/messages/messages_fa.properties
Normal file
9
src/main/resources/messages/messages_fa.properties
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
welcome=خوش آمدید
|
||||||
|
required=الزامی
|
||||||
|
notFound=یافت نشد
|
||||||
|
duplicate=قبلا استفاده شده
|
||||||
|
nonNumeric=باید عددی باشد
|
||||||
|
duplicateFormSubmission=ارسال تکراری فرم مجاز نیست
|
||||||
|
typeMismatch.date=تاریخ نامعتبر
|
||||||
|
typeMismatch.birthDate=تاریخ تولد نامعتبر
|
||||||
|
|
8
src/main/resources/messages/messages_pt.properties
Normal file
8
src/main/resources/messages/messages_pt.properties
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
welcome=Bem-vindo
|
||||||
|
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
|
9
src/main/resources/messages/messages_ru.properties
Normal file
9
src/main/resources/messages/messages_ru.properties
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
welcome=Добро пожаловать
|
||||||
|
required=необходимо
|
||||||
|
notFound=не найдено
|
||||||
|
duplicate=уже используется
|
||||||
|
nonNumeric=должно быть все числовое значение
|
||||||
|
duplicateFormSubmission=Дублирование формы не допускается
|
||||||
|
typeMismatch.date=неправильная даные
|
||||||
|
typeMismatch.birthDate=неправильная дата
|
||||||
|
|
9
src/main/resources/messages/messages_tr.properties
Normal file
9
src/main/resources/messages/messages_tr.properties
Normal file
|
@ -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
|
||||||
|
|
4
src/main/resources/templates/fragments/layout.html
Executable file → Normal file
4
src/main/resources/templates/fragments/layout.html
Executable file → Normal file
|
@ -17,7 +17,7 @@
|
||||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|
||||||
<link th:href="@{/webjars/font-awesome/4.7.0/css/font-awesome.min.css}" rel="stylesheet">
|
<link th:href="@{/webjars/font-awesome/css/font-awesome.min.css}" rel="stylesheet">
|
||||||
<link rel="stylesheet" th:href="@{/resources/css/petclinic.css}" />
|
<link rel="stylesheet" th:href="@{/resources/css/petclinic.css}" />
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
@ -87,7 +87,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script th:src="@{/webjars/bootstrap/5.3.3/dist/js/bootstrap.bundle.min.js}"></script>
|
<script th:src="@{/webjars/bootstrap/dist/js/bootstrap.bundle.min.js}"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ class MySqlIntegrationTests {
|
||||||
|
|
||||||
@ServiceConnection
|
@ServiceConnection
|
||||||
@Container
|
@Container
|
||||||
static MySQLContainer<?> container = new MySQLContainer<>("mysql:8.4");
|
static MySQLContainer<?> container = new MySQLContainer<>("mysql:9.1");
|
||||||
|
|
||||||
@LocalServerPort
|
@LocalServerPort
|
||||||
int port;
|
int port;
|
||||||
|
@ -58,7 +58,7 @@ class MySqlIntegrationTests {
|
||||||
private RestTemplateBuilder builder;
|
private RestTemplateBuilder builder;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testFindAll() throws Exception {
|
void testFindAll() {
|
||||||
vets.findAll();
|
vets.findAll();
|
||||||
vets.findAll(); // served from cache
|
vets.findAll(); // served from cache
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class MysqlTestApplication {
|
||||||
@Profile("mysql")
|
@Profile("mysql")
|
||||||
@Bean
|
@Bean
|
||||||
static MySQLContainer<?> container() {
|
static MySQLContainer<?> container() {
|
||||||
return new MySQLContainer<>("mysql:8.4");
|
return new MySQLContainer<>("mysql:9.1");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
|
@ -44,7 +44,7 @@ public class PetClinicIntegrationTests {
|
||||||
private RestTemplateBuilder builder;
|
private RestTemplateBuilder builder;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testFindAll() throws Exception {
|
void testFindAll() {
|
||||||
vets.findAll();
|
vets.findAll();
|
||||||
vets.findAll(); // served from cache
|
vets.findAll(); // served from cache
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.samples.petclinic;
|
package org.springframework.samples.petclinic;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
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 static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -48,7 +49,7 @@ import org.springframework.web.client.RestTemplate;
|
||||||
import org.testcontainers.DockerClientFactory;
|
import org.testcontainers.DockerClientFactory;
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "spring.docker.compose.skip.in-tests=false", //
|
@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")
|
@ActiveProfiles("postgres")
|
||||||
@DisabledInNativeImage
|
@DisabledInNativeImage
|
||||||
public class PostgresIntegrationTests {
|
public class PostgresIntegrationTests {
|
||||||
|
@ -71,7 +72,7 @@ public class PostgresIntegrationTests {
|
||||||
new SpringApplicationBuilder(PetClinicApplication.class) //
|
new SpringApplicationBuilder(PetClinicApplication.class) //
|
||||||
.profiles("postgres") //
|
.profiles("postgres") //
|
||||||
.properties( //
|
.properties( //
|
||||||
"spring.docker.compose.profiles.active=postgres" //
|
"spring.docker.compose.start.arguments=postgres" //
|
||||||
) //
|
) //
|
||||||
.listeners(new PropertiesLogger()) //
|
.listeners(new PropertiesLogger()) //
|
||||||
.run(args);
|
.run(args);
|
||||||
|
@ -114,7 +115,16 @@ public class PostgresIntegrationTests {
|
||||||
Arrays.sort(names);
|
Arrays.sort(names);
|
||||||
for (String name : names) {
|
for (String name : names) {
|
||||||
String resolved = environment.getProperty(name);
|
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)) {
|
if (resolved.equals(value)) {
|
||||||
log.info(name + "=" + resolved);
|
log.info(name + "=" + resolved);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,17 +20,18 @@ import org.assertj.core.util.Lists;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
||||||
import org.mockito.Mockito;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
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.Page;
|
||||||
import org.springframework.data.domain.PageImpl;
|
import org.springframework.data.domain.PageImpl;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.test.context.aot.DisabledInAotMode;
|
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.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.empty;
|
import static org.hamcrest.Matchers.empty;
|
||||||
import static org.hamcrest.Matchers.greaterThan;
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
|
@ -43,16 +44,16 @@ import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.BDDMockito.given;
|
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.get;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
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.*;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test class for {@link OwnerController}
|
* Test class for {@link OwnerController}
|
||||||
*
|
*
|
||||||
* @author Colin But
|
* @author Colin But
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@WebMvcTest(OwnerController.class)
|
@WebMvcTest(OwnerController.class)
|
||||||
@DisabledInNativeImage
|
@DisabledInNativeImage
|
||||||
|
@ -64,7 +65,7 @@ class OwnerControllerTests {
|
||||||
@Autowired
|
@Autowired
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
@MockBean
|
@MockitoBean
|
||||||
private OwnerRepository owners;
|
private OwnerRepository owners;
|
||||||
|
|
||||||
private Owner george() {
|
private Owner george() {
|
||||||
|
@ -90,12 +91,12 @@ class OwnerControllerTests {
|
||||||
void setup() {
|
void setup() {
|
||||||
|
|
||||||
Owner george = george();
|
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)));
|
.willReturn(new PageImpl<>(Lists.newArrayList(george)));
|
||||||
|
|
||||||
given(this.owners.findAll(any(Pageable.class))).willReturn(new PageImpl<>(Lists.newArrayList(george)));
|
given(this.owners.findAll(any(Pageable.class))).willReturn(new PageImpl<>(Lists.newArrayList(george)));
|
||||||
|
|
||||||
given(this.owners.findById(TEST_OWNER_ID)).willReturn(george);
|
given(this.owners.findById(TEST_OWNER_ID)).willReturn(Optional.of(george));
|
||||||
Visit visit = new Visit();
|
Visit visit = new Visit();
|
||||||
visit.setDate(LocalDate.now());
|
visit.setDate(LocalDate.now());
|
||||||
george.getPet("Max").getVisits().add(visit);
|
george.getPet("Max").getVisits().add(visit);
|
||||||
|
@ -143,14 +144,14 @@ class OwnerControllerTests {
|
||||||
@Test
|
@Test
|
||||||
void testProcessFindFormSuccess() throws Exception {
|
void testProcessFindFormSuccess() throws Exception {
|
||||||
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList(george(), new Owner()));
|
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList(george(), new Owner()));
|
||||||
Mockito.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"));
|
mockMvc.perform(get("/owners?page=1")).andExpect(status().isOk()).andExpect(view().name("owners/ownersList"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testProcessFindFormByLastName() throws Exception {
|
void testProcessFindFormByLastName() throws Exception {
|
||||||
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList(george()));
|
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList(george()));
|
||||||
Mockito.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"))
|
mockMvc.perform(get("/owners?page=1").param("lastName", "Franklin"))
|
||||||
.andExpect(status().is3xxRedirection())
|
.andExpect(status().is3xxRedirection())
|
||||||
.andExpect(view().name("redirect:/owners/" + TEST_OWNER_ID));
|
.andExpect(view().name("redirect:/owners/" + TEST_OWNER_ID));
|
||||||
|
@ -159,7 +160,7 @@ class OwnerControllerTests {
|
||||||
@Test
|
@Test
|
||||||
void testProcessFindFormNoOwnersFound() throws Exception {
|
void testProcessFindFormNoOwnersFound() throws Exception {
|
||||||
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList());
|
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList());
|
||||||
Mockito.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"))
|
mockMvc.perform(get("/owners?page=1").param("lastName", "Unknown Surname"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(model().attributeHasFieldErrors("owner", "lastName"))
|
.andExpect(model().attributeHasFieldErrors("owner", "lastName"))
|
||||||
|
@ -229,4 +230,24 @@ class OwnerControllerTests {
|
||||||
.andExpect(view().name("owners/ownerDetails"));
|
.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(Optional.of(owner));
|
||||||
|
|
||||||
|
mockMvc.perform(MockMvcRequestBuilders.post("/owners/{ownerId}/edit", pathOwnerId).flashAttr("owner", owner))
|
||||||
|
.andExpect(status().is3xxRedirection())
|
||||||
|
.andExpect(redirectedUrl("/owners/" + pathOwnerId + "/edit"))
|
||||||
|
.andExpect(flash().attributeExists("error"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
136
src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java
Executable file → Normal file
136
src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java
Executable file → Normal file
|
@ -18,16 +18,20 @@ package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import org.assertj.core.util.Lists;
|
import org.assertj.core.util.Lists;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
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.ComponentScan;
|
||||||
import org.springframework.context.annotation.FilterType;
|
import org.springframework.context.annotation.FilterType;
|
||||||
import org.springframework.test.context.aot.DisabledInAotMode;
|
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.MockMvc;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import static org.mockito.BDDMockito.given;
|
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.get;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
|
@ -39,6 +43,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
* Test class for the {@link PetController}
|
* Test class for the {@link PetController}
|
||||||
*
|
*
|
||||||
* @author Colin But
|
* @author Colin But
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@WebMvcTest(value = PetController.class,
|
@WebMvcTest(value = PetController.class,
|
||||||
includeFilters = @ComponentScan.Filter(value = PetTypeFormatter.class, type = FilterType.ASSIGNABLE_TYPE))
|
includeFilters = @ComponentScan.Filter(value = PetTypeFormatter.class, type = FilterType.ASSIGNABLE_TYPE))
|
||||||
|
@ -53,7 +58,7 @@ class PetControllerTests {
|
||||||
@Autowired
|
@Autowired
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
@MockBean
|
@MockitoBean
|
||||||
private OwnerRepository owners;
|
private OwnerRepository owners;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
|
@ -62,11 +67,17 @@ class PetControllerTests {
|
||||||
cat.setId(3);
|
cat.setId(3);
|
||||||
cat.setName("hamster");
|
cat.setName("hamster");
|
||||||
given(this.owners.findPetTypes()).willReturn(Lists.newArrayList(cat));
|
given(this.owners.findPetTypes()).willReturn(Lists.newArrayList(cat));
|
||||||
|
|
||||||
Owner owner = new Owner();
|
Owner owner = new Owner();
|
||||||
Pet pet = new Pet();
|
Pet pet = new Pet();
|
||||||
|
Pet dog = new Pet();
|
||||||
owner.addPet(pet);
|
owner.addPet(pet);
|
||||||
|
owner.addPet(dog);
|
||||||
pet.setId(TEST_PET_ID);
|
pet.setId(TEST_PET_ID);
|
||||||
given(this.owners.findById(TEST_OWNER_ID)).willReturn(owner);
|
dog.setId(TEST_PET_ID + 1);
|
||||||
|
pet.setName("petty");
|
||||||
|
dog.setName("doggy");
|
||||||
|
given(this.owners.findById(TEST_OWNER_ID)).willReturn(Optional.of(owner));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -87,25 +98,72 @@ class PetControllerTests {
|
||||||
.andExpect(view().name("redirect:/owners/{ownerId}"));
|
.andExpect(view().name("redirect:/owners/{ownerId}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Nested
|
||||||
void testProcessCreationFormHasErrors() throws Exception {
|
class ProcessCreationFormHasErrors {
|
||||||
mockMvc
|
|
||||||
.perform(post("/owners/{ownerId}/pets/new", TEST_OWNER_ID).param("name", "Betty")
|
@Test
|
||||||
.param("birthDate", "2015-02-12"))
|
void testProcessCreationFormWithBlankName() throws Exception {
|
||||||
.andExpect(model().attributeHasNoErrors("owner"))
|
mockMvc
|
||||||
.andExpect(model().attributeHasErrors("pet"))
|
.perform(post("/owners/{ownerId}/pets/new", TEST_OWNER_ID).param("name", "\t \n")
|
||||||
.andExpect(model().attributeHasFieldErrors("pet", "type"))
|
.param("birthDate", "2015-02-12"))
|
||||||
.andExpect(model().attributeHasFieldErrorCode("pet", "type", "required"))
|
.andExpect(model().attributeHasNoErrors("owner"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(model().attributeHasErrors("pet"))
|
||||||
.andExpect(view().name("pets/createOrUpdatePetForm"));
|
.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
|
@Test
|
||||||
|
@ -118,15 +176,33 @@ class PetControllerTests {
|
||||||
.andExpect(view().name("redirect:/owners/{ownerId}"));
|
.andExpect(view().name("redirect:/owners/{ownerId}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Nested
|
||||||
void testProcessUpdateFormHasErrors() throws Exception {
|
class ProcessUpdateFormHasErrors {
|
||||||
mockMvc
|
|
||||||
.perform(post("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID).param("name", "Betty")
|
@Test
|
||||||
.param("birthDate", "2015/02/12"))
|
void testProcessUpdateFormWithInvalidBirthDate() throws Exception {
|
||||||
.andExpect(model().attributeHasNoErrors("owner"))
|
mockMvc
|
||||||
.andExpect(model().attributeHasErrors("pet"))
|
.perform(post("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID).param("name", " ")
|
||||||
.andExpect(status().isOk())
|
.param("birthDate", "2015/02/12"))
|
||||||
.andExpect(view().name("pets/createOrUpdatePetForm"));
|
.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"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ class PetTypeFormatterTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldThrowParseException() throws ParseException {
|
void shouldThrowParseException() {
|
||||||
given(this.pets.findPetTypes()).willReturn(makePetTypes());
|
given(this.pets.findPetTypes()).willReturn(makePetTypes());
|
||||||
Assertions.assertThrows(ParseException.class, () -> {
|
Assertions.assertThrows(ParseException.class, () -> {
|
||||||
petTypeFormatter.parse("Fish", Locale.ENGLISH);
|
petTypeFormatter.parse("Fish", Locale.ENGLISH);
|
||||||
|
|
|
@ -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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -28,14 +28,17 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
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.aot.DisabledInAotMode;
|
||||||
|
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test class for {@link VisitController}
|
* Test class for {@link VisitController}
|
||||||
*
|
*
|
||||||
* @author Colin But
|
* @author Colin But
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@WebMvcTest(VisitController.class)
|
@WebMvcTest(VisitController.class)
|
||||||
@DisabledInNativeImage
|
@DisabledInNativeImage
|
||||||
|
@ -49,7 +52,7 @@ class VisitControllerTests {
|
||||||
@Autowired
|
@Autowired
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
@MockBean
|
@MockitoBean
|
||||||
private OwnerRepository owners;
|
private OwnerRepository owners;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
|
@ -58,7 +61,7 @@ class VisitControllerTests {
|
||||||
Pet pet = new Pet();
|
Pet pet = new Pet();
|
||||||
owner.addPet(pet);
|
owner.addPet(pet);
|
||||||
pet.setId(TEST_PET_ID);
|
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
|
@Test
|
||||||
|
|
|
@ -20,13 +20,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
|
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
|
||||||
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
|
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
|
||||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
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.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.samples.petclinic.owner.Owner;
|
import org.springframework.samples.petclinic.owner.Owner;
|
||||||
|
@ -36,7 +36,6 @@ import org.springframework.samples.petclinic.owner.PetType;
|
||||||
import org.springframework.samples.petclinic.owner.Visit;
|
import org.springframework.samples.petclinic.owner.Visit;
|
||||||
import org.springframework.samples.petclinic.vet.Vet;
|
import org.springframework.samples.petclinic.vet.Vet;
|
||||||
import org.springframework.samples.petclinic.vet.VetRepository;
|
import org.springframework.samples.petclinic.vet.VetRepository;
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,7 +66,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
* @author Michael Isvy
|
* @author Michael Isvy
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*/
|
*/
|
||||||
@DataJpaTest(includeFilters = @ComponentScan.Filter(Service.class))
|
@DataJpaTest
|
||||||
// Ensure that if the mysql profile is active we connect to the real database:
|
// Ensure that if the mysql profile is active we connect to the real database:
|
||||||
@AutoConfigureTestDatabase(replace = Replace.NONE)
|
@AutoConfigureTestDatabase(replace = Replace.NONE)
|
||||||
// @TestPropertySource("/application-postgres.properties")
|
// @TestPropertySource("/application-postgres.properties")
|
||||||
|
@ -83,16 +82,18 @@ class ClinicServiceTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldFindOwnersByLastName() {
|
void shouldFindOwnersByLastName() {
|
||||||
Page<Owner> owners = this.owners.findByLastName("Davis", pageable);
|
Page<Owner> owners = this.owners.findByLastNameStartingWith("Davis", pageable);
|
||||||
assertThat(owners).hasSize(2);
|
assertThat(owners).hasSize(2);
|
||||||
|
|
||||||
owners = this.owners.findByLastName("Daviss", pageable);
|
owners = this.owners.findByLastNameStartingWith("Daviss", pageable);
|
||||||
assertThat(owners).isEmpty();
|
assertThat(owners).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldFindSingleOwnerWithPet() {
|
void shouldFindSingleOwnerWithPet() {
|
||||||
Owner owner = this.owners.findById(1);
|
Optional<Owner> optionalOwner = this.owners.findById(1);
|
||||||
|
assertThat(optionalOwner).isPresent();
|
||||||
|
Owner owner = optionalOwner.get();
|
||||||
assertThat(owner.getLastName()).startsWith("Franklin");
|
assertThat(owner.getLastName()).startsWith("Franklin");
|
||||||
assertThat(owner.getPets()).hasSize(1);
|
assertThat(owner.getPets()).hasSize(1);
|
||||||
assertThat(owner.getPets().get(0).getType()).isNotNull();
|
assertThat(owner.getPets().get(0).getType()).isNotNull();
|
||||||
|
@ -102,7 +103,7 @@ class ClinicServiceTests {
|
||||||
@Test
|
@Test
|
||||||
@Transactional
|
@Transactional
|
||||||
void shouldInsertOwner() {
|
void shouldInsertOwner() {
|
||||||
Page<Owner> owners = this.owners.findByLastName("Schultz", pageable);
|
Page<Owner> owners = this.owners.findByLastNameStartingWith("Schultz", pageable);
|
||||||
int found = (int) owners.getTotalElements();
|
int found = (int) owners.getTotalElements();
|
||||||
|
|
||||||
Owner owner = new Owner();
|
Owner owner = new Owner();
|
||||||
|
@ -114,14 +115,16 @@ class ClinicServiceTests {
|
||||||
this.owners.save(owner);
|
this.owners.save(owner);
|
||||||
assertThat(owner.getId()).isNotZero();
|
assertThat(owner.getId()).isNotZero();
|
||||||
|
|
||||||
owners = this.owners.findByLastName("Schultz", pageable);
|
owners = this.owners.findByLastNameStartingWith("Schultz", pageable);
|
||||||
assertThat(owners.getTotalElements()).isEqualTo(found + 1);
|
assertThat(owners.getTotalElements()).isEqualTo(found + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Transactional
|
@Transactional
|
||||||
void shouldUpdateOwner() {
|
void shouldUpdateOwner() {
|
||||||
Owner owner = this.owners.findById(1);
|
Optional<Owner> optionalOwner = this.owners.findById(1);
|
||||||
|
assertThat(optionalOwner).isPresent();
|
||||||
|
Owner owner = optionalOwner.get();
|
||||||
String oldLastName = owner.getLastName();
|
String oldLastName = owner.getLastName();
|
||||||
String newLastName = oldLastName + "X";
|
String newLastName = oldLastName + "X";
|
||||||
|
|
||||||
|
@ -129,7 +132,9 @@ class ClinicServiceTests {
|
||||||
this.owners.save(owner);
|
this.owners.save(owner);
|
||||||
|
|
||||||
// retrieving new name from database
|
// 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);
|
assertThat(owner.getLastName()).isEqualTo(newLastName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +151,10 @@ class ClinicServiceTests {
|
||||||
@Test
|
@Test
|
||||||
@Transactional
|
@Transactional
|
||||||
void shouldInsertPetIntoDatabaseAndGenerateId() {
|
void shouldInsertPetIntoDatabaseAndGenerateId() {
|
||||||
Owner owner6 = this.owners.findById(6);
|
Optional<Owner> optionalOwner = this.owners.findById(6);
|
||||||
|
assertThat(optionalOwner).isPresent();
|
||||||
|
Owner owner6 = optionalOwner.get();
|
||||||
|
|
||||||
int found = owner6.getPets().size();
|
int found = owner6.getPets().size();
|
||||||
|
|
||||||
Pet pet = new Pet();
|
Pet pet = new Pet();
|
||||||
|
@ -159,7 +167,9 @@ class ClinicServiceTests {
|
||||||
|
|
||||||
this.owners.save(owner6);
|
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);
|
assertThat(owner6.getPets()).hasSize(found + 1);
|
||||||
// checks that id has been generated
|
// checks that id has been generated
|
||||||
pet = owner6.getPet("bowser");
|
pet = owner6.getPet("bowser");
|
||||||
|
@ -169,7 +179,10 @@ class ClinicServiceTests {
|
||||||
@Test
|
@Test
|
||||||
@Transactional
|
@Transactional
|
||||||
void shouldUpdatePetName() {
|
void shouldUpdatePetName() {
|
||||||
Owner owner6 = this.owners.findById(6);
|
Optional<Owner> optionalOwner = this.owners.findById(6);
|
||||||
|
assertThat(optionalOwner).isPresent();
|
||||||
|
Owner owner6 = optionalOwner.get();
|
||||||
|
|
||||||
Pet pet7 = owner6.getPet(7);
|
Pet pet7 = owner6.getPet(7);
|
||||||
String oldName = pet7.getName();
|
String oldName = pet7.getName();
|
||||||
|
|
||||||
|
@ -177,7 +190,9 @@ class ClinicServiceTests {
|
||||||
pet7.setName(newName);
|
pet7.setName(newName);
|
||||||
this.owners.save(owner6);
|
this.owners.save(owner6);
|
||||||
|
|
||||||
owner6 = this.owners.findById(6);
|
optionalOwner = this.owners.findById(6);
|
||||||
|
assertThat(optionalOwner).isPresent();
|
||||||
|
owner6 = optionalOwner.get();
|
||||||
pet7 = owner6.getPet(7);
|
pet7 = owner6.getPet(7);
|
||||||
assertThat(pet7.getName()).isEqualTo(newName);
|
assertThat(pet7.getName()).isEqualTo(newName);
|
||||||
}
|
}
|
||||||
|
@ -196,7 +211,10 @@ class ClinicServiceTests {
|
||||||
@Test
|
@Test
|
||||||
@Transactional
|
@Transactional
|
||||||
void shouldAddNewVisitForPet() {
|
void shouldAddNewVisitForPet() {
|
||||||
Owner owner6 = this.owners.findById(6);
|
Optional<Owner> optionalOwner = this.owners.findById(6);
|
||||||
|
assertThat(optionalOwner).isPresent();
|
||||||
|
Owner owner6 = optionalOwner.get();
|
||||||
|
|
||||||
Pet pet7 = owner6.getPet(7);
|
Pet pet7 = owner6.getPet(7);
|
||||||
int found = pet7.getVisits().size();
|
int found = pet7.getVisits().size();
|
||||||
Visit visit = new Visit();
|
Visit visit = new Visit();
|
||||||
|
@ -205,8 +223,6 @@ class ClinicServiceTests {
|
||||||
owner6.addVisit(pet7.getId(), visit);
|
owner6.addVisit(pet7.getId(), visit);
|
||||||
this.owners.save(owner6);
|
this.owners.save(owner6);
|
||||||
|
|
||||||
owner6 = this.owners.findById(6);
|
|
||||||
|
|
||||||
assertThat(pet7.getVisits()) //
|
assertThat(pet7.getVisits()) //
|
||||||
.hasSize(found + 1) //
|
.hasSize(found + 1) //
|
||||||
.allMatch(value -> value.getId() != null);
|
.allMatch(value -> value.getId() != null);
|
||||||
|
@ -214,7 +230,10 @@ class ClinicServiceTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldFindVisitsByPetId() {
|
void shouldFindVisitsByPetId() {
|
||||||
Owner owner6 = this.owners.findById(6);
|
Optional<Owner> optionalOwner = this.owners.findById(6);
|
||||||
|
assertThat(optionalOwner).isPresent();
|
||||||
|
Owner owner6 = optionalOwner.get();
|
||||||
|
|
||||||
Pet pet7 = owner6.getPet(7);
|
Pet pet7 = owner6.getPet(7);
|
||||||
Collection<Visit> visits = pet7.getVisits();
|
Collection<Visit> visits = pet7.getVisits();
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
// luck ((plain(st) UNIT test)! :)
|
// luck ((plain(st) UNIT test)! :)
|
||||||
class CrashControllerTests {
|
class CrashControllerTests {
|
||||||
|
|
||||||
CrashController testee = new CrashController();
|
final CrashController testee = new CrashController();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testTriggerException() {
|
void testTriggerException() {
|
||||||
|
|
|
@ -22,11 +22,11 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
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.PageImpl;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.context.aot.DisabledInAotMode;
|
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.MockMvc;
|
||||||
import org.springframework.test.web.servlet.ResultActions;
|
import org.springframework.test.web.servlet.ResultActions;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
@ -48,7 +48,7 @@ class VetControllerTests {
|
||||||
@Autowired
|
@Autowired
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
@MockBean
|
@MockitoBean
|
||||||
private VetRepository vets;
|
private VetRepository vets;
|
||||||
|
|
||||||
private Vet james() {
|
private Vet james() {
|
||||||
|
|
|
@ -156,8 +156,7 @@
|
||||||
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||||
<stringProp name="HTTPSampler.protocol"></stringProp>
|
<stringProp name="HTTPSampler.protocol"></stringProp>
|
||||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||||
<stringProp name="HTTPSampler.path">
|
<stringProp name="HTTPSampler.path">${CONTEXT_WEB}/webjars/bootstrap/dist/js/bootstrap.bundle.min.js</stringProp>
|
||||||
${CONTEXT_WEB}/webjars/bootstrap/5.3.3/dist/js/bootstrap.bundle.min.js</stringProp>
|
|
||||||
<stringProp name="HTTPSampler.method">GET</stringProp>
|
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||||
|
@ -420,8 +419,7 @@
|
||||||
<stringProp name="HTTPSampler.port"></stringProp>
|
<stringProp name="HTTPSampler.port"></stringProp>
|
||||||
<stringProp name="HTTPSampler.protocol"></stringProp>
|
<stringProp name="HTTPSampler.protocol"></stringProp>
|
||||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||||
<stringProp name="HTTPSampler.path">
|
<stringProp name="HTTPSampler.path">${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new</stringProp>
|
||||||
${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new</stringProp>
|
|
||||||
<stringProp name="HTTPSampler.method">GET</stringProp>
|
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||||
|
@ -458,8 +456,7 @@
|
||||||
<stringProp name="HTTPSampler.port"></stringProp>
|
<stringProp name="HTTPSampler.port"></stringProp>
|
||||||
<stringProp name="HTTPSampler.protocol"></stringProp>
|
<stringProp name="HTTPSampler.protocol"></stringProp>
|
||||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||||
<stringProp name="HTTPSampler.path">
|
<stringProp name="HTTPSampler.path">${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new</stringProp>
|
||||||
${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new</stringProp>
|
|
||||||
<stringProp name="HTTPSampler.method">POST</stringProp>
|
<stringProp name="HTTPSampler.method">POST</stringProp>
|
||||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||||
|
@ -540,4 +537,4 @@
|
||||||
</hashTree>
|
</hashTree>
|
||||||
</hashTree>
|
</hashTree>
|
||||||
</hashTree>
|
</hashTree>
|
||||||
</jmeterTestPlan>
|
</jmeterTestPlan>
|
||||||
|
|
Loading…
Reference in a new issue