mirror of
https://github.com/spring-projects/spring-petclinic.git
synced 2025-07-22 15:55:49 +00:00
overall cleanup
Signed-off-by: Alireza <>
This commit is contained in:
parent
923e2b7aa3
commit
ee7f64bcb1
55 changed files with 731 additions and 9033 deletions
31
pom.xml
31
pom.xml
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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">
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>
|
<groupId>org.springframework.samples</groupId>
|
||||||
<artifactId>spring-petclinic</artifactId>
|
<artifactId>spring-petclinic</artifactId>
|
||||||
|
@ -32,6 +33,8 @@
|
||||||
<maven-checkstyle.version>3.2.2</maven-checkstyle.version>
|
<maven-checkstyle.version>3.2.2</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.39</spring-format.version>
|
<spring-format.version>0.0.39</spring-format.version>
|
||||||
|
<lombok.version>1.18.20</lombok.version>
|
||||||
|
<apache-lang3.version>3.12.0</apache-lang3.version>
|
||||||
|
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
@ -138,6 +141,19 @@
|
||||||
<artifactId>jakarta.xml.bind-api</artifactId>
|
<artifactId>jakarta.xml.bind-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
<version>${apache-lang3.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
@ -166,7 +182,8 @@
|
||||||
<configuration>
|
<configuration>
|
||||||
<rules>
|
<rules>
|
||||||
<requireJavaVersion>
|
<requireJavaVersion>
|
||||||
<message>This build requires at least Java ${java.version}, update your JVM, and run the build again</message>
|
<message>This build requires at least Java ${java.version}, update your JVM, and run the build again
|
||||||
|
</message>
|
||||||
<version>${java.version}</version>
|
<version>${java.version}</version>
|
||||||
</requireJavaVersion>
|
</requireJavaVersion>
|
||||||
</rules>
|
</rules>
|
||||||
|
@ -370,7 +387,9 @@
|
||||||
<configuration>
|
<configuration>
|
||||||
<inputPath>${basedir}/src/main/scss/</inputPath>
|
<inputPath>${basedir}/src/main/scss/</inputPath>
|
||||||
<outputPath>${basedir}/src/main/resources/static/resources/css/</outputPath>
|
<outputPath>${basedir}/src/main/resources/static/resources/css/</outputPath>
|
||||||
<includePath>${project.build.directory}/webjars/META-INF/resources/webjars/bootstrap/${webjars-bootstrap.version}/scss/</includePath>
|
<includePath>
|
||||||
|
${project.build.directory}/webjars/META-INF/resources/webjars/bootstrap/${webjars-bootstrap.version}/scss/
|
||||||
|
</includePath>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
@ -405,7 +424,7 @@
|
||||||
</goals>
|
</goals>
|
||||||
</pluginExecutionFilter>
|
</pluginExecutionFilter>
|
||||||
<action>
|
<action>
|
||||||
<ignore />
|
<ignore/>
|
||||||
</action>
|
</action>
|
||||||
</pluginExecution>
|
</pluginExecution>
|
||||||
<pluginExecution>
|
<pluginExecution>
|
||||||
|
@ -418,7 +437,7 @@
|
||||||
</goals>
|
</goals>
|
||||||
</pluginExecutionFilter>
|
</pluginExecutionFilter>
|
||||||
<action>
|
<action>
|
||||||
<ignore />
|
<ignore/>
|
||||||
</action>
|
</action>
|
||||||
</pluginExecution>
|
</pluginExecution>
|
||||||
<pluginExecution>
|
<pluginExecution>
|
||||||
|
@ -431,7 +450,7 @@
|
||||||
</goals>
|
</goals>
|
||||||
</pluginExecutionFilter>
|
</pluginExecutionFilter>
|
||||||
<action>
|
<action>
|
||||||
<ignore />
|
<ignore/>
|
||||||
</action>
|
</action>
|
||||||
</pluginExecution>
|
</pluginExecution>
|
||||||
</pluginExecutions>
|
</pluginExecutions>
|
||||||
|
|
10
readme.md
10
readme.md
|
@ -17,6 +17,9 @@ git clone https://github.com/spring-projects/spring-petclinic.git
|
||||||
cd spring-petclinic
|
cd spring-petclinic
|
||||||
./mvnw package
|
./mvnw package
|
||||||
java -jar target/*.jar
|
java -jar target/*.jar
|
||||||
|
|
||||||
|
Extras:
|
||||||
|
Also ,,enable annotation processing" in `Compiler > Annotation Processors` so that you can work with lombok properly
|
||||||
```
|
```
|
||||||
|
|
||||||
You can then access petclinic at http://localhost:8080/
|
You can then access petclinic at http://localhost:8080/
|
||||||
|
@ -84,7 +87,12 @@ At development time we recommend you use the test applications set up as `main()
|
||||||
|
|
||||||
## Compiling the CSS
|
## Compiling the CSS
|
||||||
|
|
||||||
There is a `petclinic.css` in `src/main/resources/static/resources/css`. It was generated from the `petclinic.scss` source, combined with the [Bootstrap](https://getbootstrap.com/) library. If you make changes to the `scss`, or upgrade Bootstrap, you will need to re-compile the CSS resources using the Maven profile "css", i.e. `./mvnw package -P css`. There is no build profile for Gradle to compile the CSS.
|
There is a `petclinic.css` in `src/main/resources/static/resources/css`.
|
||||||
|
It was generated from the `petclinic.scss` source, combined with the [Bootstrap](https://getbootstrap.com/) library.
|
||||||
|
If you make changes to the `scss`, or upgrade Bootstrap, you will need to re-compile the CSS resources using the Maven profile "css", i.e. `./mvnw package -P css`.
|
||||||
|
There is no build profile for Gradle to compile the CSS.
|
||||||
|
|
||||||
|
In case you have changes that are already inside `petclinic.scss` e.g use of bootstrap classes that are already included, there is no need to do the steps.
|
||||||
|
|
||||||
## Working with Petclinic in your IDE
|
## Working with Petclinic in your IDE
|
||||||
|
|
||||||
|
|
13
src/main/java/Utils/CollectionUtils.java
Normal file
13
src/main/java/Utils/CollectionUtils.java
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package Utils;
|
||||||
|
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public class CollectionUtils {
|
||||||
|
|
||||||
|
public static boolean isEmpty(@Nullable Collection<?> collection) {
|
||||||
|
return collection == null || collection.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -13,13 +13,16 @@
|
||||||
* 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.
|
||||||
*/
|
*/
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.Pet;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import org.springframework.format.annotation.DateTimeFormat;
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
import org.springframework.samples.petclinic.model.NamedEntity;
|
import org.springframework.samples.petclinic.model.NamedEntity;
|
||||||
|
|
||||||
|
@ -32,6 +35,7 @@ import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.OneToMany;
|
import jakarta.persistence.OneToMany;
|
||||||
import jakarta.persistence.OrderBy;
|
import jakarta.persistence.OrderBy;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
|
import org.springframework.samples.petclinic.owner.Visit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple business object representing a pet.
|
* Simple business object representing a pet.
|
||||||
|
@ -41,6 +45,8 @@ import jakarta.persistence.Table;
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
@Table(name = "pets")
|
@Table(name = "pets")
|
||||||
public class Pet extends NamedEntity {
|
public class Pet extends NamedEntity {
|
||||||
|
|
||||||
|
@ -52,33 +58,14 @@ public class Pet extends NamedEntity {
|
||||||
@JoinColumn(name = "type_id")
|
@JoinColumn(name = "type_id")
|
||||||
private PetType type;
|
private PetType type;
|
||||||
|
|
||||||
|
@Setter(AccessLevel.NONE)
|
||||||
@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 Set<Visit> visits = new LinkedHashSet<>();
|
||||||
|
|
||||||
public void setBirthDate(LocalDate birthDate) {
|
|
||||||
this.birthDate = birthDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalDate getBirthDate() {
|
|
||||||
return this.birthDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PetType getType() {
|
|
||||||
return this.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setType(PetType type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<Visit> getVisits() {
|
|
||||||
return this.visits;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addVisit(Visit visit) {
|
public void addVisit(Visit visit) {
|
||||||
getVisits().add(visit);
|
this.visits.add(visit);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -13,14 +13,17 @@
|
||||||
* 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.
|
||||||
*/
|
*/
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.Pet;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.samples.petclinic.Validation.PetDataBinderValidator;
|
||||||
|
import org.springframework.samples.petclinic.Validation.InputValidator;
|
||||||
|
import org.springframework.samples.petclinic.owner.Owner;
|
||||||
|
import org.springframework.samples.petclinic.owner.OwnerService;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.ModelMap;
|
import org.springframework.ui.ModelMap;
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
@ -39,39 +42,29 @@ import jakarta.validation.Valid;
|
||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping("/owners/{ownerId}")
|
@RequestMapping("/owners/{ownerId}")
|
||||||
|
@RequiredArgsConstructor
|
||||||
class PetController {
|
class PetController {
|
||||||
|
|
||||||
private static final String VIEWS_PETS_CREATE_OR_UPDATE_FORM = "pets/createOrUpdatePetForm";
|
private static final String VIEWS_PETS_CREATE_OR_UPDATE_FORM = "pets/createOrUpdatePetForm";
|
||||||
|
|
||||||
private final OwnerRepository owners;
|
private final OwnerService ownerService;
|
||||||
|
|
||||||
public PetController(OwnerRepository owners) {
|
private final InputValidator inputValidator;
|
||||||
this.owners = owners;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ModelAttribute("types")
|
@ModelAttribute("types")
|
||||||
public Collection<PetType> populatePetTypes() {
|
public Collection<PetType> populatePetTypes() {
|
||||||
return this.owners.findPetTypes();
|
return this.ownerService.findPetTypes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ModelAttribute("owner")
|
@ModelAttribute("owner")
|
||||||
public Owner findOwner(@PathVariable("ownerId") int ownerId) {
|
public Owner findOwner(@PathVariable("ownerId") int ownerId) {
|
||||||
|
return ownerService.findOwner(ownerId);
|
||||||
Owner owner = this.owners.findById(ownerId);
|
|
||||||
if (owner == null) {
|
|
||||||
throw new IllegalArgumentException("Owner ID not found: " + ownerId);
|
|
||||||
}
|
|
||||||
return owner;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ModelAttribute("pet")
|
@ModelAttribute("pet")
|
||||||
public Pet findPet(@PathVariable("ownerId") int ownerId,
|
public Pet findPet(@PathVariable("ownerId") int ownerId,
|
||||||
@PathVariable(name = "petId", required = false) Integer petId) {
|
@PathVariable(name = "petId", required = false) Integer petId) {
|
||||||
|
var owner = ownerService.findOwner(ownerId);
|
||||||
Owner owner = this.owners.findById(ownerId);
|
|
||||||
if (owner == null) {
|
|
||||||
throw new IllegalArgumentException("Owner ID not found: " + ownerId);
|
|
||||||
}
|
|
||||||
return petId == null ? new Pet() : owner.getPet(petId);
|
return petId == null ? new Pet() : owner.getPet(petId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +75,7 @@ class PetController {
|
||||||
|
|
||||||
@InitBinder("pet")
|
@InitBinder("pet")
|
||||||
public void initPetBinder(WebDataBinder dataBinder) {
|
public void initPetBinder(WebDataBinder dataBinder) {
|
||||||
dataBinder.setValidator(new PetValidator());
|
dataBinder.setValidator(new PetDataBinderValidator());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/pets/new")
|
@GetMapping("/pets/new")
|
||||||
|
@ -95,14 +88,9 @@ class PetController {
|
||||||
|
|
||||||
@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, ModelMap model) {
|
||||||
if (StringUtils.hasText(pet.getName()) && pet.isNew() && owner.getPet(pet.getName(), true) != null) {
|
|
||||||
result.rejectValue("name", "duplicate", "already exists");
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalDate currentDate = LocalDate.now();
|
this.inputValidator.validatePetDuplication(result, pet, owner);
|
||||||
if (pet.getBirthDate() != null && pet.getBirthDate().isAfter(currentDate)) {
|
this.inputValidator.validateDate(result, pet, "birthDate");
|
||||||
result.rejectValue("birthDate", "typeMismatch.birthDate");
|
|
||||||
}
|
|
||||||
|
|
||||||
owner.addPet(pet);
|
owner.addPet(pet);
|
||||||
if (result.hasErrors()) {
|
if (result.hasErrors()) {
|
||||||
|
@ -110,7 +98,7 @@ class PetController {
|
||||||
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.owners.save(owner);
|
this.ownerService.saveOwner(owner);
|
||||||
return "redirect:/owners/{ownerId}";
|
return "redirect:/owners/{ownerId}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,21 +111,8 @@ class PetController {
|
||||||
|
|
||||||
@PostMapping("/pets/{petId}/edit")
|
@PostMapping("/pets/{petId}/edit")
|
||||||
public String processUpdateForm(@Valid Pet pet, BindingResult result, Owner owner, ModelMap model) {
|
public String processUpdateForm(@Valid Pet pet, BindingResult result, Owner owner, ModelMap model) {
|
||||||
|
this.inputValidator.validatePetUpdateDuplication(result, pet, owner);
|
||||||
String petName = pet.getName();
|
this.inputValidator.validateDate(result, pet, "birthDate");
|
||||||
|
|
||||||
// checking if the pet name already exist for the owner
|
|
||||||
if (StringUtils.hasText(petName)) {
|
|
||||||
Pet existingPet = owner.getPet(petName.toLowerCase(), false);
|
|
||||||
if (existingPet != null && existingPet.getId() != pet.getId()) {
|
|
||||||
result.rejectValue("name", "duplicate", "already exists");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalDate currentDate = LocalDate.now();
|
|
||||||
if (pet.getBirthDate() != null && pet.getBirthDate().isAfter(currentDate)) {
|
|
||||||
result.rejectValue("birthDate", "typeMismatch.birthDate");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.hasErrors()) {
|
if (result.hasErrors()) {
|
||||||
model.put("pet", pet);
|
model.put("pet", pet);
|
||||||
|
@ -145,7 +120,7 @@ class PetController {
|
||||||
}
|
}
|
||||||
|
|
||||||
owner.addPet(pet);
|
owner.addPet(pet);
|
||||||
this.owners.save(owner);
|
this.ownerService.saveOwner(owner);
|
||||||
return "redirect:/owners/{ownerId}";
|
return "redirect:/owners/{ownerId}";
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
* 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.
|
||||||
*/
|
*/
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.Pet;
|
||||||
|
|
||||||
import org.springframework.samples.petclinic.model.NamedEntity;
|
import org.springframework.samples.petclinic.model.NamedEntity;
|
||||||
|
|
|
@ -13,14 +13,16 @@
|
||||||
* 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.
|
||||||
*/
|
*/
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.Pet;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.format.Formatter;
|
import org.springframework.format.Formatter;
|
||||||
|
import org.springframework.samples.petclinic.owner.OwnerRepository;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.Collection;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,29 +36,25 @@ import java.util.Locale;
|
||||||
* @author Michael Isvy
|
* @author Michael Isvy
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class PetTypeFormatter implements Formatter<PetType> {
|
public class PetTypeFormatter implements Formatter<PetType> {
|
||||||
|
|
||||||
private final OwnerRepository owners;
|
private final OwnerRepository owners;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public PetTypeFormatter(OwnerRepository owners) {
|
|
||||||
this.owners = owners;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String print(PetType petType, Locale locale) {
|
public String print(PetType petType, Locale locale) {
|
||||||
return petType.getName();
|
return petType.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// text = toString of PetType
|
||||||
@Override
|
@Override
|
||||||
public PetType parse(String text, Locale locale) throws ParseException {
|
public PetType parse(String text, Locale locale) throws ParseException {
|
||||||
Collection<PetType> findPetTypes = this.owners.findPetTypes();
|
List<PetType> petTypes = this.owners.findPetTypes();
|
||||||
for (PetType type : findPetTypes) {
|
|
||||||
if (type.getName().equals(text)) {
|
return petTypes.stream()
|
||||||
return type;
|
.filter(petType -> StringUtils.containsIgnoreCase(text, petType.getName()))
|
||||||
}
|
.findFirst()
|
||||||
}
|
.orElseThrow(() -> new ParseException("type not found: " + text, 0));
|
||||||
throw new ParseException("type not found: " + text, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package org.springframework.samples.petclinic.Pet;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public enum PetTypes {
|
||||||
|
|
||||||
|
CAT("cat", "pet.cat"), DOG("dog", "pet.dog"), LIZARD("lizard", "pet.lizard"), SNAKE("snake", "pet.snake"),
|
||||||
|
BIRD("bird", "pet.bird"), HAMSTER("hamster", "pet.hamster");
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
private final String frontendTranslationKey;
|
||||||
|
|
||||||
|
// TODO: Refactor so we will use this instead of taking all Types from db
|
||||||
|
public static List<String> getPetTypeNames() {
|
||||||
|
List<String> allPetTypeNames = new ArrayList<>();
|
||||||
|
Arrays.stream(values()).map(PetTypes::getValue).forEach(petTypesName -> allPetTypeNames.add(petTypesName));
|
||||||
|
return allPetTypeNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -30,6 +30,7 @@ public class PetClinicRuntimeHints implements RuntimeHintsRegistrar {
|
||||||
hints.resources().registerPattern("messages/*");
|
hints.resources().registerPattern("messages/*");
|
||||||
hints.resources().registerPattern("META-INF/resources/webjars/*");
|
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);
|
||||||
hints.serialization().registerType(Vet.class);
|
hints.serialization().registerType(Vet.class);
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
package org.springframework.samples.petclinic.Validation;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import org.springframework.samples.petclinic.Pet.Pet;
|
||||||
|
import org.springframework.samples.petclinic.owner.Owner;
|
||||||
|
import org.springframework.samples.petclinic.owner.Visit;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.validation.BindingResult;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class InputValidator {
|
||||||
|
|
||||||
|
public void validatePetDuplication(BindingResult result, Pet pet, Owner owner) {
|
||||||
|
|
||||||
|
requireNonNull(pet, "pet is null");
|
||||||
|
requireNonNull(pet, "owner is null");
|
||||||
|
|
||||||
|
boolean ownerHasExistingPet = owner.getPet(pet.getName(), true) != null;
|
||||||
|
if (StringUtils.hasText(pet.getName()) && pet.isNewEntry() && ownerHasExistingPet) {
|
||||||
|
result.rejectValue("name", "duplicate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validatePetUpdateDuplication(BindingResult result, Pet pet, Owner owner) {
|
||||||
|
|
||||||
|
requireNonNull(pet, "pet is null");
|
||||||
|
requireNonNull(pet, "owner is null");
|
||||||
|
|
||||||
|
var ownerHasExistingPet = owner.getPet(pet.getName(), false);
|
||||||
|
var notSameId = !Objects.equals(ownerHasExistingPet.getId(), pet.getId());
|
||||||
|
if (StringUtils.hasText(pet.getName()) && ownerHasExistingPet != null && notSameId) {
|
||||||
|
result.rejectValue("name", "duplicate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void validateDate(BindingResult result, @NotNull T obj, String fieldName) {
|
||||||
|
requireNonNull(obj, "pet is null");
|
||||||
|
|
||||||
|
// Refactor in case of changing to higher Java versions > 17 to use switch pattern
|
||||||
|
// matching
|
||||||
|
LocalDate date = null;
|
||||||
|
|
||||||
|
if (obj instanceof Pet pet) {
|
||||||
|
date = pet.getBirthDate();
|
||||||
|
}
|
||||||
|
else if (obj instanceof Visit visit) {
|
||||||
|
date = visit.getDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (date == null) {
|
||||||
|
result.rejectValue(fieldName, "typeMismatch.birthDate");
|
||||||
|
}
|
||||||
|
else if (date.isAfter(LocalDate.now())) {
|
||||||
|
result.rejectValue(fieldName, "typeMismatch.birthDate.future");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validateString(BindingResult result, @NotNull Visit visit, String fieldName) {
|
||||||
|
if (!StringUtils.hasText(visit.getDescription())) {
|
||||||
|
result.rejectValue(fieldName, "required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -13,8 +13,9 @@
|
||||||
* 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.
|
||||||
*/
|
*/
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.Validation;
|
||||||
|
|
||||||
|
import org.springframework.samples.petclinic.Pet.Pet;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.validation.Errors;
|
import org.springframework.validation.Errors;
|
||||||
import org.springframework.validation.Validator;
|
import org.springframework.validation.Validator;
|
||||||
|
@ -29,7 +30,7 @@ import org.springframework.validation.Validator;
|
||||||
* @author Ken Krebs
|
* @author Ken Krebs
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
*/
|
*/
|
||||||
public class PetValidator implements Validator {
|
public class PetDataBinderValidator implements Validator {
|
||||||
|
|
||||||
private static final String REQUIRED = "required";
|
private static final String REQUIRED = "required";
|
||||||
|
|
||||||
|
@ -42,12 +43,10 @@ public class PetValidator implements Validator {
|
||||||
errors.rejectValue("name", REQUIRED, REQUIRED);
|
errors.rejectValue("name", REQUIRED, REQUIRED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// type validation
|
if (pet.isNewEntry() && pet.getType() == null) {
|
||||||
if (pet.isNew() && pet.getType() == null) {
|
|
||||||
errors.rejectValue("type", REQUIRED, REQUIRED);
|
errors.rejectValue("type", REQUIRED, REQUIRED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// birth date validation
|
|
||||||
if (pet.getBirthDate() == null) {
|
if (pet.getBirthDate() == null) {
|
||||||
errors.rejectValue("birthDate", REQUIRED, REQUIRED);
|
errors.rejectValue("birthDate", REQUIRED, REQUIRED);
|
||||||
}
|
}
|
|
@ -21,6 +21,8 @@ import jakarta.persistence.GeneratedValue;
|
||||||
import jakarta.persistence.GenerationType;
|
import jakarta.persistence.GenerationType;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.MappedSuperclass;
|
import jakarta.persistence.MappedSuperclass;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple JavaBean domain object with an id property. Used as a base class for objects
|
* Simple JavaBean domain object with an id property. Used as a base class for objects
|
||||||
|
@ -29,6 +31,9 @@ import jakarta.persistence.MappedSuperclass;
|
||||||
* @author Ken Krebs
|
* @author Ken Krebs
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
public class BaseEntity implements Serializable {
|
public class BaseEntity implements Serializable {
|
||||||
|
|
||||||
|
@ -36,15 +41,7 @@ public class BaseEntity implements Serializable {
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
private Integer id;
|
private Integer id;
|
||||||
|
|
||||||
public Integer getId() {
|
public boolean isNewEntry() {
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(Integer id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isNew() {
|
|
||||||
return this.id == null;
|
return this.id == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 lombok.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
|
@ -26,19 +27,14 @@ import jakarta.persistence.MappedSuperclass;
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
*/
|
*/
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
public class NamedEntity extends BaseEntity {
|
public class NamedEntity extends BaseEntity {
|
||||||
|
|
||||||
@Column(name = "name")
|
@Column(name = "name")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
public String getName() {
|
// PetTypeFormatter#parse
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return this.getName();
|
return this.getName();
|
||||||
|
|
|
@ -18,6 +18,12 @@ 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;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple JavaBean domain object representing an person.
|
* Simple JavaBean domain object representing an person.
|
||||||
|
@ -25,6 +31,9 @@ import jakarta.validation.constraints.NotBlank;
|
||||||
* @author Ken Krebs
|
* @author Ken Krebs
|
||||||
*/
|
*/
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@ToString
|
||||||
public class Person extends BaseEntity {
|
public class Person extends BaseEntity {
|
||||||
|
|
||||||
@Column(name = "first_name")
|
@Column(name = "first_name")
|
||||||
|
@ -35,20 +44,24 @@ public class Person extends BaseEntity {
|
||||||
@NotBlank
|
@NotBlank
|
||||||
private String lastName;
|
private String lastName;
|
||||||
|
|
||||||
public String getFirstName() {
|
@Override
|
||||||
return this.firstName;
|
public boolean equals(Object object) {
|
||||||
|
|
||||||
|
if (this == object)
|
||||||
|
return true;
|
||||||
|
if (object == null || getClass() != object.getClass())
|
||||||
|
return false;
|
||||||
|
Person person = (Person) object;
|
||||||
|
|
||||||
|
return new EqualsBuilder().append(firstName, person.firstName)
|
||||||
|
.append(lastName, person.lastName)
|
||||||
|
.append(super.getId(), person.getId())
|
||||||
|
.isEquals();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFirstName(String firstName) {
|
@Override
|
||||||
this.firstName = firstName;
|
public int hashCode() {
|
||||||
}
|
return Objects.hash(firstName, lastName);
|
||||||
|
|
||||||
public String getLastName() {
|
|
||||||
return this.lastName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLastName(String lastName) {
|
|
||||||
this.lastName = lastName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,14 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
import Utils.CollectionUtils;
|
||||||
|
import lombok.*;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.core.style.ToStringCreator;
|
import org.springframework.core.style.ToStringCreator;
|
||||||
|
import org.springframework.samples.petclinic.Pet.Pet;
|
||||||
import org.springframework.samples.petclinic.model.Person;
|
import org.springframework.samples.petclinic.model.Person;
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
import jakarta.persistence.CascadeType;
|
import jakarta.persistence.CascadeType;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
|
@ -33,6 +35,8 @@ import jakarta.persistence.Table;
|
||||||
import jakarta.validation.constraints.Digits;
|
import jakarta.validation.constraints.Digits;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
import static java.util.function.Predicate.not;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple JavaBean domain object representing an owner.
|
* Simple JavaBean domain object representing an owner.
|
||||||
*
|
*
|
||||||
|
@ -44,6 +48,8 @@ import jakarta.validation.constraints.NotBlank;
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "owners")
|
@Table(name = "owners")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
public class Owner extends Person {
|
public class Owner extends Person {
|
||||||
|
|
||||||
@Column(name = "address")
|
@Column(name = "address")
|
||||||
|
@ -59,42 +65,19 @@ public class Owner extends Person {
|
||||||
@Digits(fraction = 0, integer = 10)
|
@Digits(fraction = 0, integer = 10)
|
||||||
private String telephone;
|
private String telephone;
|
||||||
|
|
||||||
|
@Setter(AccessLevel.NONE)
|
||||||
@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 List<Pet> pets = new ArrayList<>();
|
||||||
|
|
||||||
public String getAddress() {
|
|
||||||
return this.address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAddress(String address) {
|
|
||||||
this.address = address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCity() {
|
|
||||||
return this.city;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCity(String city) {
|
|
||||||
this.city = city;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTelephone() {
|
|
||||||
return this.telephone;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTelephone(String telephone) {
|
|
||||||
this.telephone = telephone;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Pet> getPets() {
|
|
||||||
return this.pets;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addPet(Pet pet) {
|
public void addPet(Pet pet) {
|
||||||
if (pet.isNew()) {
|
boolean petIsNewInList = false;
|
||||||
getPets().add(pet);
|
if (!CollectionUtils.isEmpty(this.pets)) {
|
||||||
|
petIsNewInList = this.pets.stream().anyMatch(value -> value.equals(pet));
|
||||||
|
}
|
||||||
|
if (pet.isNewEntry() || !petIsNewInList) {
|
||||||
|
this.pets.add(pet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,49 +92,28 @@ 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 petId to test
|
||||||
* @return a pet if pet id is already in use
|
* @return a pet if pet id is already in use
|
||||||
*/
|
*/
|
||||||
public Pet getPet(Integer id) {
|
public Pet getPet(Integer petId) {
|
||||||
for (Pet pet : getPets()) {
|
return this.pets.stream()
|
||||||
if (!pet.isNew()) {
|
.filter(not(Pet::isNewEntry))
|
||||||
Integer compId = pet.getId();
|
.filter(pet -> pet.getId().equals(petId))
|
||||||
if (compId.equals(id)) {
|
.findFirst()
|
||||||
return pet;
|
.orElse(null);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 petName to test
|
||||||
* @return a pet if pet name is already in use
|
* @return a pet if pet name is already in use
|
||||||
*/
|
*/
|
||||||
public Pet getPet(String name, boolean ignoreNew) {
|
public Pet getPet(String petName, boolean ignoreNew) {
|
||||||
name = name.toLowerCase();
|
return this.pets.stream()
|
||||||
for (Pet pet : getPets()) {
|
.filter(pet -> StringUtils.containsIgnoreCase(pet.getName(), petName))
|
||||||
String compName = pet.getName();
|
.filter(pet -> !ignoreNew || !pet.isNewEntry())
|
||||||
if (compName != null && compName.equalsIgnoreCase(name)) {
|
.findFirst()
|
||||||
if (!ignoreNew || !pet.isNew()) {
|
.orElse(null);
|
||||||
return pet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return new ToStringCreator(this).append("id", this.getId())
|
|
||||||
.append("new", this.isNew())
|
|
||||||
.append("lastName", this.getLastName())
|
|
||||||
.append("firstName", this.getFirstName())
|
|
||||||
.append("address", this.address)
|
|
||||||
.append("city", this.city)
|
|
||||||
.append("telephone", this.telephone)
|
|
||||||
.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -160,15 +122,20 @@ public class Owner extends Person {
|
||||||
* @param visit the visit to add, must not be {@literal null}.
|
* @param visit the visit to add, must not be {@literal null}.
|
||||||
*/
|
*/
|
||||||
public void addVisit(Integer petId, Visit visit) {
|
public void addVisit(Integer petId, Visit visit) {
|
||||||
|
Pet pet = Objects.requireNonNull(getPet(petId), "no pet found with given Id");
|
||||||
Assert.notNull(petId, "Pet identifier must not be null!");
|
|
||||||
Assert.notNull(visit, "Visit must not be null!");
|
|
||||||
|
|
||||||
Pet pet = getPet(petId);
|
|
||||||
|
|
||||||
Assert.notNull(pet, "Invalid Pet identifier!");
|
|
||||||
|
|
||||||
pet.addVisit(visit);
|
pet.addVisit(visit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new ToStringCreator(this).append("id", this.getId())
|
||||||
|
.append("new", this.isNewEntry())
|
||||||
|
.append("lastName", this.getLastName())
|
||||||
|
.append("firstName", this.getFirstName())
|
||||||
|
.append("address", this.address)
|
||||||
|
.append("city", this.city)
|
||||||
|
.append("telephone", this.telephone)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.springframework.samples.petclinic.owner;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
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;
|
||||||
|
@ -42,15 +43,16 @@ import jakarta.validation.Valid;
|
||||||
* @author Michael Isvy
|
* @author Michael Isvy
|
||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
|
@RequiredArgsConstructor
|
||||||
class OwnerController {
|
class OwnerController {
|
||||||
|
|
||||||
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
|
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
|
||||||
|
|
||||||
private final OwnerRepository owners;
|
private static final int PAGE_SIZE = 5;
|
||||||
|
|
||||||
public OwnerController(OwnerRepository clinicService) {
|
private final OwnerService ownerService;
|
||||||
this.owners = clinicService;
|
|
||||||
}
|
private final OwnerRepository owners;
|
||||||
|
|
||||||
@InitBinder
|
@InitBinder
|
||||||
public void setAllowedFields(WebDataBinder dataBinder) {
|
public void setAllowedFields(WebDataBinder dataBinder) {
|
||||||
|
@ -59,7 +61,7 @@ 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() : ownerService.findOwner(ownerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/owners/new")
|
@GetMapping("/owners/new")
|
||||||
|
@ -120,14 +122,13 @@ class OwnerController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Page<Owner> findPaginatedForOwnersLastName(int page, String lastname) {
|
private Page<Owner> findPaginatedForOwnersLastName(int page, String lastname) {
|
||||||
int pageSize = 5;
|
Pageable pageable = PageRequest.of(page - 1, PAGE_SIZE);
|
||||||
Pageable pageable = PageRequest.of(page - 1, pageSize);
|
|
||||||
return owners.findByLastName(lastname, pageable);
|
return owners.findByLastName(lastname, pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/owners/{ownerId}/edit")
|
@GetMapping("/owners/{ownerId}/edit")
|
||||||
public String initUpdateOwnerForm(@PathVariable("ownerId") int ownerId, Model model) {
|
public String initUpdateOwnerForm(@PathVariable("ownerId") int ownerId, Model model) {
|
||||||
Owner owner = this.owners.findById(ownerId);
|
Owner owner = ownerService.findOwner(ownerId);
|
||||||
model.addAttribute(owner);
|
model.addAttribute(owner);
|
||||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
@ -152,7 +153,7 @@ 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);
|
Owner owner = ownerService.findOwner(ownerId);
|
||||||
mav.addObject(owner);
|
mav.addObject(owner);
|
||||||
return mav;
|
return mav;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.springframework.data.domain.Pageable;
|
||||||
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.Repository;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.samples.petclinic.Pet.PetType;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.samples.petclinic.Pet.PetType;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class OwnerService {
|
||||||
|
|
||||||
|
private final OwnerRepository owners;
|
||||||
|
|
||||||
|
public Owner findOwner(Integer ownerId) {
|
||||||
|
Owner owner = this.owners.findById(ownerId);
|
||||||
|
Assert.notNull(owner, "Owner ID not found: " + ownerId);
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PetType> findPetTypes() {
|
||||||
|
return this.owners.findPetTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveOwner(@NotNull Owner owner) {
|
||||||
|
// add fancy logic here to make this method more useful
|
||||||
|
this.owners.save(owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,6 +17,9 @@ package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import org.springframework.format.annotation.DateTimeFormat;
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
import org.springframework.samples.petclinic.model.BaseEntity;
|
import org.springframework.samples.petclinic.model.BaseEntity;
|
||||||
|
|
||||||
|
@ -33,36 +36,19 @@ import jakarta.validation.constraints.NotBlank;
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "visits")
|
@Table(name = "visits")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
public class Visit extends BaseEntity {
|
public class Visit extends BaseEntity {
|
||||||
|
|
||||||
@Column(name = "visit_date")
|
@Column(name = "visit_date")
|
||||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||||
private LocalDate date;
|
private LocalDate date;
|
||||||
|
|
||||||
@NotBlank
|
@NotNull
|
||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance of Visit for the current date
|
|
||||||
*/
|
|
||||||
public Visit() {
|
public Visit() {
|
||||||
this.date = LocalDate.now();
|
this.date = LocalDate.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalDate getDate() {
|
|
||||||
return this.date;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDate(LocalDate date) {
|
|
||||||
this.date = date;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDescription() {
|
|
||||||
return this.description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDescription(String description) {
|
|
||||||
this.description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,14 +17,14 @@ package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.samples.petclinic.Pet.Pet;
|
||||||
|
import org.springframework.samples.petclinic.Validation.InputValidator;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.InitBinder;
|
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
|
|
||||||
|
@ -36,13 +36,12 @@ import jakarta.validation.Valid;
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
|
@RequiredArgsConstructor
|
||||||
class VisitController {
|
class VisitController {
|
||||||
|
|
||||||
private final OwnerRepository owners;
|
private final OwnerRepository owners;
|
||||||
|
|
||||||
public VisitController(OwnerRepository owners) {
|
private final InputValidator inputValidator;
|
||||||
this.owners = owners;
|
|
||||||
}
|
|
||||||
|
|
||||||
@InitBinder
|
@InitBinder
|
||||||
public void setAllowedFields(WebDataBinder dataBinder) {
|
public void setAllowedFields(WebDataBinder dataBinder) {
|
||||||
|
@ -82,12 +81,19 @@ class VisitController {
|
||||||
@PostMapping("/owners/{ownerId}/pets/{petId}/visits/new")
|
@PostMapping("/owners/{ownerId}/pets/{petId}/visits/new")
|
||||||
public String processNewVisitForm(@ModelAttribute Owner owner, @PathVariable int petId, @Valid Visit visit,
|
public String processNewVisitForm(@ModelAttribute Owner owner, @PathVariable int petId, @Valid Visit visit,
|
||||||
BindingResult result) {
|
BindingResult result) {
|
||||||
|
Assert.notNull(petId, "Pet identifier must not be null!");
|
||||||
|
Assert.notNull(visit, "Visit must not be null!");
|
||||||
|
|
||||||
|
this.inputValidator.validateDate(result, visit, "date");
|
||||||
|
this.inputValidator.validateString(result, visit, "description");
|
||||||
|
|
||||||
if (result.hasErrors()) {
|
if (result.hasErrors()) {
|
||||||
return "pets/createOrUpdateVisitForm";
|
return "pets/createOrUpdateVisitForm";
|
||||||
}
|
}
|
||||||
|
|
||||||
owner.addVisit(petId, visit);
|
owner.addVisit(petId, visit);
|
||||||
this.owners.save(owner);
|
this.owners.save(owner);
|
||||||
|
|
||||||
return "redirect:/owners/{ownerId}";
|
return "redirect:/owners/{ownerId}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -17,6 +17,7 @@ package org.springframework.samples.petclinic.vet;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
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;
|
||||||
|
@ -33,21 +34,20 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
|
@RequiredArgsConstructor
|
||||||
class VetController {
|
class VetController {
|
||||||
|
|
||||||
private final VetRepository vetRepository;
|
private final VetRepository vetRepository;
|
||||||
|
|
||||||
public VetController(VetRepository clinicService) {
|
|
||||||
this.vetRepository = clinicService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/vets.html")
|
@GetMapping("/vets.html")
|
||||||
public String showVetList(@RequestParam(defaultValue = "1") int page, Model model) {
|
public String showVetList(@RequestParam(defaultValue = "1") int page, Model model) {
|
||||||
|
|
||||||
// Here we are returning an object of type 'Vets' rather than a collection of Vet
|
// Here we are returning an object of type 'Vets' rather than a collection of Vet
|
||||||
// objects so it is simpler for Object-Xml mapping
|
// objects so it is simpler for Object-Xml mapping
|
||||||
Vets vets = new Vets();
|
Vets vets = new Vets();
|
||||||
Page<Vet> paginated = findPaginated(page);
|
Page<Vet> paginated = findPaginated(page);
|
||||||
vets.getVetList().addAll(paginated.toList());
|
vets.getVetList().addAll(paginated.toList());
|
||||||
|
|
||||||
return addPaginationModel(page, paginated, model);
|
return addPaginationModel(page, paginated, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,18 +60,20 @@ class VetController {
|
||||||
return "vets/vetList";
|
return "vets/vetList";
|
||||||
}
|
}
|
||||||
|
|
||||||
private Page<Vet> findPaginated(int page) {
|
private Page<Vet> findPaginated(int currentPageNumber) {
|
||||||
int pageSize = 5;
|
int pageSize = 5;
|
||||||
Pageable pageable = PageRequest.of(page - 1, pageSize);
|
Pageable pageable = PageRequest.of(currentPageNumber - 1, pageSize);
|
||||||
return vetRepository.findAll(pageable);
|
return vetRepository.findAll(pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping({ "/vets" })
|
@GetMapping({ "/vets" })
|
||||||
public @ResponseBody Vets showResourcesVetList() {
|
public @ResponseBody Vets showResourcesVetList() {
|
||||||
|
|
||||||
// Here we are returning an object of type 'Vets' rather than a collection of Vet
|
// Here we are returning an object of type 'Vets' rather than a collection of Vet
|
||||||
// objects so it is simpler for JSon/Object mapping
|
// objects so it is simpler for JSon/Object mapping
|
||||||
Vets vets = new Vets();
|
Vets vets = new Vets();
|
||||||
vets.getVetList().addAll(this.vetRepository.findAll());
|
vets.getVetList().addAll(this.vetRepository.findAll());
|
||||||
|
|
||||||
return vets;
|
return vets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# database init
|
||||||
database=postgres
|
database=postgres
|
||||||
spring.datasource.url=${POSTGRES_URL:jdbc:postgresql://localhost/petclinic}
|
spring.datasource.url=${POSTGRES_URL:jdbc:postgresql://localhost/petclinic}
|
||||||
spring.datasource.username=${POSTGRES_USER:petclinic}
|
spring.datasource.username=${POSTGRES_USER:petclinic}
|
||||||
|
|
|
@ -21,5 +21,9 @@ logging.level.org.springframework=INFO
|
||||||
# logging.level.org.springframework.web=DEBUG
|
# logging.level.org.springframework.web=DEBUG
|
||||||
# logging.level.org.springframework.context.annotation=TRACE
|
# logging.level.org.springframework.context.annotation=TRACE
|
||||||
|
|
||||||
|
# SQL Logging
|
||||||
|
# spring.jpa.show-sql=true
|
||||||
|
# spring.jpa.properties.hibernate.format_sql=true
|
||||||
|
|
||||||
# Maximum time static resources should be cached
|
# Maximum time static resources should be cached
|
||||||
spring.web.resources.cache.cachecontrol.max-age=12h
|
spring.web.resources.cache.cachecontrol.max-age=12h
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
welcome=Welcome
|
welcome=Welcome
|
||||||
required=is required
|
required=is required
|
||||||
notFound=has not been found
|
notFound=has not been found
|
||||||
duplicate=is already in use
|
duplication=is already in use
|
||||||
nonNumeric=must be all numeric
|
nonNumeric=must be all numeric
|
||||||
duplicateFormSubmission=Duplicate form submission is not allowed
|
duplicateFormSubmission=Duplicate form submission is not allowed
|
||||||
typeMismatch.date=invalid date
|
typeMismatch.date=invalid date
|
||||||
typeMismatch.birthDate=invalid date
|
typeMismatch.birthDate=empty date
|
||||||
|
typeMismatch.birthDate.future=date is in the future
|
||||||
|
|
|
@ -4,6 +4,7 @@ notFound=wurde nicht gefunden
|
||||||
duplicate=ist bereits vergeben
|
duplicate=ist bereits vergeben
|
||||||
nonNumeric=darf nur numerisch sein
|
nonNumeric=darf nur numerisch sein
|
||||||
duplicateFormSubmission=Wiederholtes Absenden des Formulars ist nicht erlaubt
|
duplicateFormSubmission=Wiederholtes Absenden des Formulars ist nicht erlaubt
|
||||||
typeMismatch.date=ung<EFBFBD>ltiges Datum
|
typeMismatch.date=ungültiges Datum
|
||||||
typeMismatch.birthDate=ung<EFBFBD>ltiges Datum
|
typeMismatch.birthDate=leeres Datum
|
||||||
|
typeMismatch.birthDate.future=Das Datum liegt in der Zukunft
|
||||||
|
|
||||||
|
|
|
@ -5,5 +5,5 @@ duplicate=Ya se encuentra en uso
|
||||||
nonNumeric=Sólo debe contener numeros
|
nonNumeric=Sólo debe contener numeros
|
||||||
duplicateFormSubmission=No se permite el envío de formularios duplicados
|
duplicateFormSubmission=No se permite el envío de formularios duplicados
|
||||||
typeMismatch.date=Fecha invalida
|
typeMismatch.date=Fecha invalida
|
||||||
typeMismatch.birthDate=Fecha invalida
|
typeMismatch.birthDate=fecha vacía
|
||||||
|
typeMismatch.birthDate.future=la fecha es en el futuro
|
||||||
|
|
|
@ -5,4 +5,5 @@ duplicate=이미 존재합니다
|
||||||
nonNumeric=모두 숫자로 입력해야 합니다
|
nonNumeric=모두 숫자로 입력해야 합니다
|
||||||
duplicateFormSubmission=중복 제출은 허용되지 않습니다
|
duplicateFormSubmission=중복 제출은 허용되지 않습니다
|
||||||
typeMismatch.date=잘못된 날짜입니다
|
typeMismatch.date=잘못된 날짜입니다
|
||||||
typeMismatch.birthDate=잘못된 날짜입니다
|
typeMismatch.birthDate=잘못된 날짜입니다 //TODO change to: empty date
|
||||||
|
typeMismatch.birthDate.future=date is in the future
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,19 +5,20 @@
|
||||||
<div th:with="valid=${!#fields.hasErrors(name)}"
|
<div th:with="valid=${!#fields.hasErrors(name)}"
|
||||||
th:class="${'form-group' + (valid ? '' : ' has-error')}"
|
th:class="${'form-group' + (valid ? '' : ' has-error')}"
|
||||||
class="form-group">
|
class="form-group">
|
||||||
<label class="col-sm-2 control-label" th:text="${label}">Label</label>
|
<label class="col-sm-2 control-label asterisk" th:text="${label}">Label</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-5 mb-3">
|
||||||
<div th:switch="${type}">
|
<div th:switch="${type}">
|
||||||
<input th:case="'text'" class="form-control" type="text" th:field="*{__${name}__}" />
|
<input th:case="'text'" class="form-control" type="text" th:field="*{__${name}__}" />
|
||||||
<input th:case="'date'" class="form-control" type="date" th:field="*{__${name}__}"/>
|
<input th:case="'date'" class="form-control" type="date" th:field="*{__${name}__}"/>
|
||||||
</div>
|
</div>
|
||||||
<span th:if="${valid}"
|
<span th:if="${valid}"
|
||||||
class="fa fa-ok form-control-feedback"
|
class="fa fa-ok form-control-feedback"
|
||||||
aria-hidden="true"></span>
|
aria-hidden="true">
|
||||||
|
</span>
|
||||||
<th:block th:if="${!valid}">
|
<th:block th:if="${!valid}">
|
||||||
<span
|
<span
|
||||||
class="fa fa-remove form-control-feedback"
|
class="fa fa-remove form-control-feedback mb-4"
|
||||||
aria-hidden="true"></span>
|
aria-hidden="true"/>
|
||||||
<span class="help-inline" th:errors="*{__${name}__}">Error</span>
|
<span class="help-inline" th:errors="*{__${name}__}">Error</span>
|
||||||
</th:block>
|
</th:block>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -80,7 +80,12 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 text-center">
|
<div class="col-12 text-center">
|
||||||
<img src="../static/images/spring-logo.svg" th:src="@{/resources/images/spring-logo.svg}" alt="VMware Tanzu Logo" class="logo">
|
<img
|
||||||
|
src="../static/images/spring-logo.svg"
|
||||||
|
th:src="@{/resources/images/spring-logo.svg}"
|
||||||
|
alt="VMware Tanzu Logo"
|
||||||
|
class="logo"
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<th:block th:fragment="select (label, name, items)">
|
<th:block th:fragment="select (label, name, items)">
|
||||||
<div th:with="valid=${!#fields.hasErrors(name)}"
|
<div th:with="valid=${!#fields.hasErrors(name)}"
|
||||||
th:class="${'form-group' + (valid ? '' : ' has-error')}"
|
th:class="${'form-group' + (valid ? '' : ' has-error')}"
|
||||||
class="form-group">
|
class="form-group mb-3">
|
||||||
<label class="col-sm-2 control-label" th:text="${label}">Label</label>
|
<label class="col-sm-2 control-label" th:text="${label}">Label</label>
|
||||||
|
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<button
|
<button
|
||||||
th:with="text=${owner['new']} ? 'Add Owner' : 'Update Owner'"
|
th:with="text=${owner['newEntry']} ? 'Add Owner' : 'Update Owner'"
|
||||||
class="btn btn-primary" type="submit" th:text="${text}">Add
|
class="btn btn-primary" type="submit" th:text="${text}">Add
|
||||||
Owner</button>
|
Owner</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
<label class="col-sm-2 control-label">Last name </label>
|
<label class="col-sm-2 control-label">Last name </label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input class="form-control" th:field="*{lastName}" size="30"
|
<input class="form-control" th:field="*{lastName}" size="30"
|
||||||
maxlength="80" /> <span class="help-inline"><div
|
maxlength="80" /> <span class="help-inline">
|
||||||
th:if="${#fields.hasAnyErrors()}">
|
<div th:if="${#fields.hasAnyErrors()}">
|
||||||
<p th:each="err : ${#fields.allErrors()}" th:text="${err}">Error</p>
|
<p th:each="err : ${#fields.allErrors()}" th:text="${err}">Error</p>
|
||||||
</div></span>
|
</div></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<h2>
|
<h2>
|
||||||
<th:block th:if="${pet['new']}">New </th:block>
|
<th:block th:if="${pet['newEntry']}">New </th:block>
|
||||||
Pet
|
Pet
|
||||||
</h2>
|
</h2>
|
||||||
<form th:object="${pet}" class="form-horizontal" method="post">
|
<form th:object="${pet}" class="form-horizontal" method="post">
|
||||||
<input type="hidden" name="id" th:value="*{id}" />
|
<input type="hidden" name="id" th:value="*{id}" />
|
||||||
<div class="form-group has-feedback">
|
<div class="form-group has-feedback">
|
||||||
<div class="form-group">
|
<div class="form-group mb-3">
|
||||||
<label class="col-sm-2 control-label">Owner</label>
|
<label class="col-sm-2 control-label">Owner</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<span th:text="${owner?.firstName + ' ' + owner?.lastName}" />
|
<span th:text="${owner?.firstName + ' ' + owner?.lastName}" />
|
||||||
|
@ -26,11 +26,12 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<button
|
<button
|
||||||
th:with="text=${pet['new']} ? 'Add Pet' : 'Update Pet'"
|
th:with="text=${pet['newEntry']} ? 'Add Pet' : 'Update Pet'"
|
||||||
class="btn btn-primary" type="submit" th:text="${text}">Add
|
class="btn btn-primary" type="submit" th:text="${text}">Add
|
||||||
Pet</button>
|
Pet</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div th:text="${error != null ? error: ''}"/>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<h2>
|
<h2>
|
||||||
<th:block th:if="${visit['new']}">New </th:block>
|
<th:block th:if="${visit['newEntry']}">New </th:block>
|
||||||
Visit
|
Visit
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
<th>Date</th>
|
<th>Date</th>
|
||||||
<th>Description</th>
|
<th>Description</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr th:if="${!visit['new']}" th:each="visit : ${pet.visits}">
|
<tr th:if="${!visit['newEntry']}" th:each="visit : ${pet.visits}">
|
||||||
<td th:text="${#temporals.format(visit.date, 'yyyy-MM-dd')}"></td>
|
<td th:text="${#temporals.format(visit.date, 'yyyy-MM-dd')}"></td>
|
||||||
<td th:text=" ${visit.description}"></td>
|
<td th:text=" ${visit.description}"></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
|
||||||
<html xmlns:th="https://www.thymeleaf.org" th:replace="~{fragments/layout :: layout (~{::body},'home')}">
|
<html
|
||||||
|
xmlns:th="https://www.thymeleaf.org"
|
||||||
|
th:replace="~{fragments/layout :: layout (~{::body},'home')}"
|
||||||
|
>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<h2 th:text="#{welcome}">Welcome</h2>
|
<h2 th:text="#{welcome}">Welcome</h2>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<img class="img-responsive" src="../static/resources/images/pets.png" th:src="@{/resources/images/pets.png}"/>
|
<img
|
||||||
|
class="img-responsive"
|
||||||
|
src="../static/resources/images/pets.png"
|
||||||
|
th:src="@{/resources/images/pets.png}"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,14 @@ $pagination-active-bg: $spring-brown;
|
||||||
$pagination-active-border: $spring-green;
|
$pagination-active-border: $spring-green;
|
||||||
$table-border-color: $spring-brown;
|
$table-border-color: $spring-brown;
|
||||||
|
|
||||||
|
$asterisk: "\f069";
|
||||||
|
|
||||||
|
.asterisk{
|
||||||
|
&:after {
|
||||||
|
content: "*";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.table > thead > tr > th {
|
.table > thead > tr > th {
|
||||||
background-color: lighten($spring-brown, 3%);
|
background-color: lighten($spring-brown, 3%);
|
||||||
color: $spring-light-grey;
|
color: $spring-light-grey;
|
||||||
|
@ -75,10 +83,10 @@ $table-border-color: $spring-brown;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus,
|
&:focus,
|
||||||
&:active,
|
|
||||||
&.active,
|
&.active,
|
||||||
.open .dropdown-toggle {
|
.open .dropdown-toggle {
|
||||||
background-color: $spring-brown;
|
background-color: transparent;
|
||||||
|
color: $spring-green;
|
||||||
border-color: $spring-brown;
|
border-color: $spring-brown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,9 @@ class MySqlIntegrationTests {
|
||||||
@Test
|
@Test
|
||||||
void testOwnerDetails() {
|
void testOwnerDetails() {
|
||||||
RestTemplate template = builder.rootUri("http://localhost:" + port).build();
|
RestTemplate template = builder.rootUri("http://localhost:" + port).build();
|
||||||
ResponseEntity<String> result = template.exchange(RequestEntity.get("/owners/1").build(), String.class);
|
var request = RequestEntity.get("/owners/1").build();
|
||||||
|
|
||||||
|
ResponseEntity<String> result = template.exchange(request, String.class);
|
||||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.Pet;
|
||||||
|
|
||||||
import org.assertj.core.util.Lists;
|
import org.assertj.core.util.Lists;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
@ -25,6 +25,10 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
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.samples.petclinic.Validation.InputValidator;
|
||||||
|
import org.springframework.samples.petclinic.owner.Owner;
|
||||||
|
import org.springframework.samples.petclinic.owner.OwnerRepository;
|
||||||
|
import org.springframework.samples.petclinic.owner.OwnerService;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
|
@ -54,6 +58,12 @@ class PetControllerTests {
|
||||||
@MockBean
|
@MockBean
|
||||||
private OwnerRepository owners;
|
private OwnerRepository owners;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private InputValidator inputValidator;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private OwnerService ownerService;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() {
|
||||||
PetType cat = new PetType();
|
PetType cat = new PetType();
|
||||||
|
@ -64,7 +74,8 @@ class PetControllerTests {
|
||||||
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(ownerService.findOwner(TEST_OWNER_ID)).willReturn(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
|
@ -14,14 +14,12 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.Pet;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
@ -32,6 +30,8 @@ import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.samples.petclinic.TestUtils.PetTypeTestUtil;
|
||||||
|
import org.springframework.samples.petclinic.owner.OwnerRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test class for {@link PetTypeFormatter}
|
* Test class for {@link PetTypeFormatter}
|
||||||
|
@ -54,44 +54,31 @@ class PetTypeFormatterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testPrint() {
|
void testPrint() {
|
||||||
PetType petType = new PetType();
|
var hamster = PetTypes.HAMSTER.getValue();
|
||||||
petType.setName("Hamster");
|
var petType = PetTypeTestUtil.createPetType(hamster);
|
||||||
String petTypeName = this.petTypeFormatter.print(petType, Locale.ENGLISH);
|
String petTypeName = this.petTypeFormatter.print(petType, Locale.ENGLISH);
|
||||||
assertThat(petTypeName).isEqualTo("Hamster");
|
assertThat(petTypeName).isEqualTo(hamster);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldParse() throws ParseException {
|
void shouldParse() throws ParseException {
|
||||||
given(this.pets.findPetTypes()).willReturn(makePetTypes());
|
mockFindPetTypes();
|
||||||
PetType petType = petTypeFormatter.parse("Bird", Locale.ENGLISH);
|
var bird = PetTypes.BIRD.getValue();
|
||||||
assertThat(petType.getName()).isEqualTo("Bird");
|
PetType petType = petTypeFormatter.parse(bird, Locale.ENGLISH);
|
||||||
|
assertThat(petType.getName()).isEqualTo(bird);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldThrowParseException() throws ParseException {
|
void shouldThrowParseException() throws ParseException {
|
||||||
given(this.pets.findPetTypes()).willReturn(makePetTypes());
|
mockFindPetTypes();
|
||||||
Assertions.assertThrows(ParseException.class, () -> {
|
Assertions.assertThrows(ParseException.class, () -> {
|
||||||
petTypeFormatter.parse("Fish", Locale.ENGLISH);
|
petTypeFormatter.parse("Fish", Locale.ENGLISH);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void mockFindPetTypes() {
|
||||||
* Helper method to produce some sample pet types just for test purpose
|
List<PetType> petTypes = PetTypeTestUtil.createPetTypes(PetTypes.BIRD, PetTypes.DOG);
|
||||||
* @return {@link Collection} of {@link PetType}
|
given(this.pets.findPetTypes()).willReturn(petTypes);
|
||||||
*/
|
|
||||||
private List<PetType> makePetTypes() {
|
|
||||||
List<PetType> petTypes = new ArrayList<>();
|
|
||||||
petTypes.add(new PetType() {
|
|
||||||
{
|
|
||||||
setName("Dog");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
petTypes.add(new PetType() {
|
|
||||||
{
|
|
||||||
setName("Bird");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return petTypes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -52,7 +52,9 @@ public class PetClinicIntegrationTests {
|
||||||
@Test
|
@Test
|
||||||
void testOwnerDetails() {
|
void testOwnerDetails() {
|
||||||
RestTemplate template = builder.rootUri("http://localhost:" + port).build();
|
RestTemplate template = builder.rootUri("http://localhost:" + port).build();
|
||||||
ResponseEntity<String> result = template.exchange(RequestEntity.get("/owners/1").build(), String.class);
|
var request = RequestEntity.get("/owners/1").build();
|
||||||
|
|
||||||
|
ResponseEntity<String> result = template.exchange(request, String.class);
|
||||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,8 +47,8 @@ import org.springframework.test.context.ActiveProfiles;
|
||||||
import org.springframework.web.client.RestTemplate;
|
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,
|
||||||
"spring.docker.compose.profiles.active=postgres" })
|
properties = { "spring.docker.compose.skip.in-tests=false", "spring.docker.compose.profiles.active=postgres" })
|
||||||
@ActiveProfiles("postgres")
|
@ActiveProfiles("postgres")
|
||||||
@DisabledInNativeImage
|
@DisabledInNativeImage
|
||||||
public class PostgresIntegrationTests {
|
public class PostgresIntegrationTests {
|
||||||
|
@ -68,12 +68,9 @@ public class PostgresIntegrationTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
new SpringApplicationBuilder(PetClinicApplication.class) //
|
new SpringApplicationBuilder(PetClinicApplication.class).profiles("postgres")
|
||||||
.profiles("postgres") //
|
.properties("spring.docker.compose.profiles.active=postgres")
|
||||||
.properties( //
|
.listeners(new PropertiesLogger())
|
||||||
"spring.docker.compose.profiles.active=postgres" //
|
|
||||||
) //
|
|
||||||
.listeners(new PropertiesLogger()) //
|
|
||||||
.run(args);
|
.run(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +83,9 @@ public class PostgresIntegrationTests {
|
||||||
@Test
|
@Test
|
||||||
void testOwnerDetails() {
|
void testOwnerDetails() {
|
||||||
RestTemplate template = builder.rootUri("http://localhost:" + port).build();
|
RestTemplate template = builder.rootUri("http://localhost:" + port).build();
|
||||||
ResponseEntity<String> result = template.exchange(RequestEntity.get("/owners/1").build(), String.class);
|
var request = RequestEntity.get("/owners/1").build();
|
||||||
|
|
||||||
|
ResponseEntity<String> result = template.exchange(request, String.class);
|
||||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
package org.springframework.samples.petclinic.TestUtils;
|
||||||
|
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.springframework.samples.petclinic.Pet.Pet;
|
||||||
|
import org.springframework.samples.petclinic.owner.Owner;
|
||||||
|
|
||||||
|
import static org.springframework.samples.petclinic.TestUtils.PetTestUtil.createPet;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class OwnerTestUtil {
|
||||||
|
|
||||||
|
public static Owner createOwner() {
|
||||||
|
return createOwner(createPet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Owner createOwner(Pet pet) {
|
||||||
|
var owner = new Owner();
|
||||||
|
owner.setId(1);
|
||||||
|
owner.setFirstName("firstName");
|
||||||
|
owner.setLastName("lastname");
|
||||||
|
owner.setAddress("110 W. Liberty St.");
|
||||||
|
owner.setCity("Madison");
|
||||||
|
owner.setTelephone("6085551023");
|
||||||
|
owner.addPet(pet);
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO create custom Builder with Lombok (should also include Id from BaseEntity)
|
||||||
|
public static Owner createOwner(Integer ownerId, String firstName, String lastname, Pet pet) {
|
||||||
|
var owner = new Owner();
|
||||||
|
owner.setId(ownerId);
|
||||||
|
owner.setFirstName(firstName);
|
||||||
|
owner.setLastName(lastname);
|
||||||
|
owner.setAddress("110 W. Liberty St.");
|
||||||
|
owner.setCity("Madison");
|
||||||
|
owner.setTelephone("6085551023");
|
||||||
|
owner.addPet(pet);
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package org.springframework.samples.petclinic.TestUtils;
|
||||||
|
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.springframework.samples.petclinic.Pet.Pet;
|
||||||
|
import org.springframework.samples.petclinic.Pet.PetType;
|
||||||
|
import org.springframework.samples.petclinic.Pet.PetTypes;
|
||||||
|
import org.springframework.samples.petclinic.owner.Visit;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
import static org.springframework.samples.petclinic.TestUtils.VisitTestUtil.createVisit;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class PetTestUtil {
|
||||||
|
|
||||||
|
public static Pet createPet() {
|
||||||
|
var petType = PetTypeTestUtil.createPetType(PetTypes.CAT.getValue());
|
||||||
|
return createPet("Max", petType, createVisit());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Pet createPet(int petId) {
|
||||||
|
var petType = PetTypeTestUtil.createPetType(PetTypes.CAT.getValue());
|
||||||
|
return createPet(petId, "Max", petType, createVisit());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Pet createPet(String name, PetType petType) {
|
||||||
|
return createPet(name, petType, createVisit());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Pet createPet(String name, PetType petType, Visit visit) {
|
||||||
|
return createPet(1, name, petType, visit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Pet createPet(int petId, String name, PetType petType, Visit visit) {
|
||||||
|
var pet = new Pet();
|
||||||
|
pet.setId(petId);
|
||||||
|
pet.setType(petType);
|
||||||
|
pet.setName(name);
|
||||||
|
pet.setBirthDate(LocalDate.now());
|
||||||
|
pet.addVisit(visit);
|
||||||
|
return pet;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package org.springframework.samples.petclinic.TestUtils;
|
||||||
|
|
||||||
|
import org.springframework.samples.petclinic.Pet.PetType;
|
||||||
|
import org.springframework.samples.petclinic.Pet.PetTypes;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class PetTypeTestUtil {
|
||||||
|
|
||||||
|
public static PetType createPetType(String petTypeName) {
|
||||||
|
return createPetType(1, petTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PetType createPetType(int id, String petTypeName) {
|
||||||
|
var petType = new PetType();
|
||||||
|
petType.setName(petTypeName);
|
||||||
|
petType.setId(id);
|
||||||
|
return petType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<PetType> createPetTypes(PetTypes... petTypes) {
|
||||||
|
return Stream.of(petTypes).map(PetTypes::getValue).map(petTypeName -> {
|
||||||
|
var newpetType = new PetType();
|
||||||
|
newpetType.setName(petTypeName);
|
||||||
|
return newpetType;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package org.springframework.samples.petclinic.TestUtils;
|
||||||
|
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.springframework.samples.petclinic.Pet.Pet;
|
||||||
|
import org.springframework.samples.petclinic.owner.Owner;
|
||||||
|
import org.springframework.samples.petclinic.vet.Specialty;
|
||||||
|
import org.springframework.samples.petclinic.vet.Vet;
|
||||||
|
|
||||||
|
import static org.springframework.samples.petclinic.TestUtils.PetTestUtil.createPet;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class VetTestUtil {
|
||||||
|
|
||||||
|
public static Vet createVet() {
|
||||||
|
var vet = new Vet();
|
||||||
|
vet.setFirstName("James");
|
||||||
|
vet.setLastName("Carter");
|
||||||
|
vet.setId(1);
|
||||||
|
return vet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vet createVetWithSpecialty() {
|
||||||
|
var vet = createVet();
|
||||||
|
vet.setId(vet.getId() + 1);
|
||||||
|
vet.addSpecialty(createSpecialty());
|
||||||
|
return vet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Specialty createSpecialty() {
|
||||||
|
return createSpecialty("radiology");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Specialty createSpecialty(String name) {
|
||||||
|
var specialty = new Specialty();
|
||||||
|
specialty.setId(1);
|
||||||
|
specialty.setName(name);
|
||||||
|
return specialty;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package org.springframework.samples.petclinic.TestUtils;
|
||||||
|
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.springframework.samples.petclinic.owner.Visit;
|
||||||
|
import org.springframework.samples.petclinic.vet.Specialty;
|
||||||
|
import org.springframework.samples.petclinic.vet.Vet;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class VisitTestUtil {
|
||||||
|
|
||||||
|
public static Visit createVisit() {
|
||||||
|
return createVisit(LocalDate.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Visit createVisit(LocalDate localDate) {
|
||||||
|
var visit = new Visit();
|
||||||
|
visit.setId(1);
|
||||||
|
visit.setDate(localDate);
|
||||||
|
visit.setDescription("Just a visit");
|
||||||
|
return visit;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -24,13 +24,14 @@ 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.springframework.samples.petclinic.TestUtils.OwnerTestUtil.createOwner;
|
||||||
|
import static org.springframework.samples.petclinic.TestUtils.PetTestUtil.createPet;
|
||||||
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.model;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.assertj.core.util.Lists;
|
import org.assertj.core.util.Lists;
|
||||||
|
@ -46,6 +47,10 @@ 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.samples.petclinic.Pet.Pet;
|
||||||
|
import org.springframework.samples.petclinic.Pet.PetType;
|
||||||
|
import org.springframework.samples.petclinic.Pet.PetTypes;
|
||||||
|
import org.springframework.samples.petclinic.TestUtils.PetTypeTestUtil;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,47 +62,36 @@ import org.springframework.test.web.servlet.MockMvc;
|
||||||
@DisabledInNativeImage
|
@DisabledInNativeImage
|
||||||
class OwnerControllerTests {
|
class OwnerControllerTests {
|
||||||
|
|
||||||
private static final int TEST_OWNER_ID = 1;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
private OwnerRepository owners;
|
private OwnerRepository owners;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private OwnerService ownerService;
|
||||||
|
|
||||||
|
private record TestData(int ownerId, String ownerFirstname, String ownerLastname, String petName, PetType petType) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private final TestData testData = new TestData(1, "George", "Franklin", "Max",
|
||||||
|
PetTypeTestUtil.createPetType(PetTypes.DOG.getValue()));
|
||||||
|
|
||||||
private Owner george() {
|
private Owner george() {
|
||||||
Owner george = new Owner();
|
var pet = createPet(testData.petName, testData.petType);
|
||||||
george.setId(TEST_OWNER_ID);
|
var owner = createOwner(testData.ownerId, testData.ownerFirstname, testData.ownerLastname, pet);
|
||||||
george.setFirstName("George");
|
return owner;
|
||||||
george.setLastName("Franklin");
|
|
||||||
george.setAddress("110 W. Liberty St.");
|
|
||||||
george.setCity("Madison");
|
|
||||||
george.setTelephone("6085551023");
|
|
||||||
Pet max = new Pet();
|
|
||||||
PetType dog = new PetType();
|
|
||||||
dog.setName("dog");
|
|
||||||
max.setType(dog);
|
|
||||||
max.setName("Max");
|
|
||||||
max.setBirthDate(LocalDate.now());
|
|
||||||
george.addPet(max);
|
|
||||||
max.setId(1);
|
|
||||||
return george;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() {
|
||||||
|
|
||||||
Owner george = george();
|
Owner george = george();
|
||||||
given(this.owners.findByLastName(eq("Franklin"), any(Pageable.class)))
|
given(this.owners.findByLastName(eq(testData.ownerLastname), any(Pageable.class)))
|
||||||
.willReturn(new PageImpl<Owner>(Lists.newArrayList(george)));
|
.willReturn(new PageImpl<Owner>(Lists.newArrayList(george)));
|
||||||
|
|
||||||
given(this.owners.findAll(any(Pageable.class))).willReturn(new PageImpl<Owner>(Lists.newArrayList(george)));
|
given(this.owners.findAll(any(Pageable.class))).willReturn(new PageImpl<Owner>(Lists.newArrayList(george)));
|
||||||
|
given(ownerService.findOwner(testData.ownerId)).willReturn(george);
|
||||||
given(this.owners.findById(TEST_OWNER_ID)).willReturn(george);
|
|
||||||
Visit visit = new Visit();
|
|
||||||
visit.setDate(LocalDate.now());
|
|
||||||
george.getPet("Max").getVisits().add(visit);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -148,10 +142,10 @@ class OwnerControllerTests {
|
||||||
@Test
|
@Test
|
||||||
void testProcessFindFormByLastName() throws Exception {
|
void testProcessFindFormByLastName() throws Exception {
|
||||||
Page<Owner> tasks = new PageImpl<Owner>(Lists.newArrayList(george()));
|
Page<Owner> tasks = new PageImpl<Owner>(Lists.newArrayList(george()));
|
||||||
Mockito.when(this.owners.findByLastName(eq("Franklin"), any(Pageable.class))).thenReturn(tasks);
|
Mockito.when(this.owners.findByLastName(eq(testData.ownerLastname), any(Pageable.class))).thenReturn(tasks);
|
||||||
mockMvc.perform(get("/owners?page=1").param("lastName", "Franklin"))
|
mockMvc.perform(get("/owners?page=1").param("lastName", testData.ownerLastname))
|
||||||
.andExpect(status().is3xxRedirection())
|
.andExpect(status().is3xxRedirection())
|
||||||
.andExpect(view().name("redirect:/owners/" + TEST_OWNER_ID));
|
.andExpect(view().name("redirect:/owners/" + testData.ownerId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -168,11 +162,11 @@ class OwnerControllerTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testInitUpdateOwnerForm() throws Exception {
|
void testInitUpdateOwnerForm() throws Exception {
|
||||||
mockMvc.perform(get("/owners/{ownerId}/edit", TEST_OWNER_ID))
|
mockMvc.perform(get("/owners/{ownerId}/edit", testData.ownerId))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(model().attributeExists("owner"))
|
.andExpect(model().attributeExists("owner"))
|
||||||
.andExpect(model().attribute("owner", hasProperty("lastName", is("Franklin"))))
|
.andExpect(model().attribute("owner", hasProperty("lastName", is(testData.ownerLastname))))
|
||||||
.andExpect(model().attribute("owner", hasProperty("firstName", is("George"))))
|
.andExpect(model().attribute("owner", hasProperty("firstName", is(testData.ownerFirstname))))
|
||||||
.andExpect(model().attribute("owner", hasProperty("address", is("110 W. Liberty St."))))
|
.andExpect(model().attribute("owner", hasProperty("address", is("110 W. Liberty St."))))
|
||||||
.andExpect(model().attribute("owner", hasProperty("city", is("Madison"))))
|
.andExpect(model().attribute("owner", hasProperty("city", is("Madison"))))
|
||||||
.andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023"))))
|
.andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023"))))
|
||||||
|
@ -182,7 +176,7 @@ class OwnerControllerTests {
|
||||||
@Test
|
@Test
|
||||||
void testProcessUpdateOwnerFormSuccess() throws Exception {
|
void testProcessUpdateOwnerFormSuccess() throws Exception {
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID).param("firstName", "Joe")
|
.perform(post("/owners/{ownerId}/edit", testData.ownerId).param("firstName", "Joe")
|
||||||
.param("lastName", "Bloggs")
|
.param("lastName", "Bloggs")
|
||||||
.param("address", "123 Caramel Street")
|
.param("address", "123 Caramel Street")
|
||||||
.param("city", "London")
|
.param("city", "London")
|
||||||
|
@ -193,7 +187,7 @@ class OwnerControllerTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testProcessUpdateOwnerFormUnchangedSuccess() throws Exception {
|
void testProcessUpdateOwnerFormUnchangedSuccess() throws Exception {
|
||||||
mockMvc.perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID))
|
mockMvc.perform(post("/owners/{ownerId}/edit", testData.ownerId))
|
||||||
.andExpect(status().is3xxRedirection())
|
.andExpect(status().is3xxRedirection())
|
||||||
.andExpect(view().name("redirect:/owners/{ownerId}"));
|
.andExpect(view().name("redirect:/owners/{ownerId}"));
|
||||||
}
|
}
|
||||||
|
@ -201,7 +195,7 @@ class OwnerControllerTests {
|
||||||
@Test
|
@Test
|
||||||
void testProcessUpdateOwnerFormHasErrors() throws Exception {
|
void testProcessUpdateOwnerFormHasErrors() throws Exception {
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID).param("firstName", "Joe")
|
.perform(post("/owners/{ownerId}/edit", testData.ownerId).param("firstName", "Joe")
|
||||||
.param("lastName", "Bloggs")
|
.param("lastName", "Bloggs")
|
||||||
.param("address", "")
|
.param("address", "")
|
||||||
.param("telephone", ""))
|
.param("telephone", ""))
|
||||||
|
@ -214,10 +208,10 @@ class OwnerControllerTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testShowOwner() throws Exception {
|
void testShowOwner() throws Exception {
|
||||||
mockMvc.perform(get("/owners/{ownerId}", TEST_OWNER_ID))
|
mockMvc.perform(get("/owners/{ownerId}", testData.ownerId))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(model().attribute("owner", hasProperty("lastName", is("Franklin"))))
|
.andExpect(model().attribute("owner", hasProperty("lastName", is(testData.ownerLastname))))
|
||||||
.andExpect(model().attribute("owner", hasProperty("firstName", is("George"))))
|
.andExpect(model().attribute("owner", hasProperty("firstName", is(testData.ownerFirstname))))
|
||||||
.andExpect(model().attribute("owner", hasProperty("address", is("110 W. Liberty St."))))
|
.andExpect(model().attribute("owner", hasProperty("address", is("110 W. Liberty St."))))
|
||||||
.andExpect(model().attribute("owner", hasProperty("city", is("Madison"))))
|
.andExpect(model().attribute("owner", hasProperty("city", is("Madison"))))
|
||||||
.andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023"))))
|
.andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023"))))
|
||||||
|
@ -237,7 +231,7 @@ class OwnerControllerTests {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void describeTo(Description description) {
|
public void describeTo(Description description) {
|
||||||
description.appendText("Max did not have any visits");
|
description.appendText(testData.petName + " did not have any visits");
|
||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
.andExpect(view().name("owners/ownerDetails"));
|
.andExpect(view().name("owners/ownerDetails"));
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.springframework.samples.petclinic.TestUtils.OwnerTestUtil.createOwner;
|
||||||
|
import static org.springframework.samples.petclinic.TestUtils.PetTestUtil.createPet;
|
||||||
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.model;
|
||||||
|
@ -29,8 +31,13 @@ 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.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.boot.test.mock.mockito.SpyBean;
|
||||||
|
import org.springframework.samples.petclinic.Validation.InputValidator;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test class for {@link VisitController}
|
* Test class for {@link VisitController}
|
||||||
*
|
*
|
||||||
|
@ -50,12 +57,12 @@ class VisitControllerTests {
|
||||||
@MockBean
|
@MockBean
|
||||||
private OwnerRepository owners;
|
private OwnerRepository owners;
|
||||||
|
|
||||||
|
@SpyBean
|
||||||
|
private InputValidator inputValidator;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void init() {
|
void init() {
|
||||||
Owner owner = new Owner();
|
var owner = createOwner(createPet(TEST_PET_ID));
|
||||||
Pet pet = new Pet();
|
|
||||||
owner.addPet(pet);
|
|
||||||
pet.setId(TEST_PET_ID);
|
|
||||||
given(this.owners.findById(TEST_OWNER_ID)).willReturn(owner);
|
given(this.owners.findById(TEST_OWNER_ID)).willReturn(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,4 +93,42 @@ class VisitControllerTests {
|
||||||
.andExpect(view().name("pets/createOrUpdateVisitForm"));
|
.andExpect(view().name("pets/createOrUpdateVisitForm"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProcessUpdateFormHasError_For_EmptyDescription() throws Exception {
|
||||||
|
mockMvc
|
||||||
|
.perform(post("/owners/{ownerId}/pets/{petId}/visits/new", TEST_OWNER_ID, TEST_PET_ID)
|
||||||
|
.param("description", "")
|
||||||
|
.param("date", "2015-02-12"))
|
||||||
|
.andExpect(model().attributeHasErrors("visit"))
|
||||||
|
.andExpect(model().attributeHasFieldErrorCode("visit", "description", "required"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(view().name("pets/createOrUpdateVisitForm"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProcessUpdateFormHasErrors_For_FutureDate() throws Exception {
|
||||||
|
|
||||||
|
var dateInFuture = LocalDate.now().plusYears(1);
|
||||||
|
mockMvc
|
||||||
|
.perform(post("/owners/{ownerId}/pets/{petId}/visits/new", TEST_OWNER_ID, TEST_PET_ID)
|
||||||
|
.param("description", "hi")
|
||||||
|
.param("date", String.valueOf(dateInFuture)))
|
||||||
|
.andExpect(model().attributeHasErrors("visit"))
|
||||||
|
.andExpect(model().attributeHasFieldErrorCode("visit", "date", "typeMismatch.birthDate.future"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(view().name("pets/createOrUpdateVisitForm"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProcessUpdateFormHasErrors_For_EmptyDate() throws Exception {
|
||||||
|
mockMvc
|
||||||
|
.perform(post("/owners/{ownerId}/pets/{petId}/visits/new", TEST_OWNER_ID, TEST_PET_ID)
|
||||||
|
.param("description", "hi")
|
||||||
|
.param("date", (String) null))
|
||||||
|
.andExpect(model().attributeHasErrors("visit"))
|
||||||
|
.andExpect(model().attributeHasFieldErrorCode("visit", "date", "typeMismatch.birthDate"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(view().name("pets/createOrUpdateVisitForm"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,10 +29,11 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
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.Pet.Pet;
|
||||||
|
import org.springframework.samples.petclinic.Pet.PetType;
|
||||||
|
import org.springframework.samples.petclinic.Pet.PetTypes;
|
||||||
import org.springframework.samples.petclinic.owner.Owner;
|
import org.springframework.samples.petclinic.owner.Owner;
|
||||||
import org.springframework.samples.petclinic.owner.OwnerRepository;
|
import org.springframework.samples.petclinic.owner.OwnerRepository;
|
||||||
import org.springframework.samples.petclinic.owner.Pet;
|
|
||||||
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;
|
||||||
|
@ -96,7 +97,7 @@ class ClinicServiceTests {
|
||||||
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();
|
||||||
assertThat(owner.getPets().get(0).getType().getName()).isEqualTo("cat");
|
assertThat(owner.getPets().get(0).getType().getName()).isEqualTo(PetTypes.CAT.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -111,6 +112,7 @@ class ClinicServiceTests {
|
||||||
owner.setAddress("4, Evans Street");
|
owner.setAddress("4, Evans Street");
|
||||||
owner.setCity("Wollongong");
|
owner.setCity("Wollongong");
|
||||||
owner.setTelephone("4444444444");
|
owner.setTelephone("4444444444");
|
||||||
|
|
||||||
this.owners.save(owner);
|
this.owners.save(owner);
|
||||||
assertThat(owner.getId().longValue()).isNotEqualTo(0);
|
assertThat(owner.getId().longValue()).isNotEqualTo(0);
|
||||||
|
|
||||||
|
@ -138,9 +140,9 @@ class ClinicServiceTests {
|
||||||
Collection<PetType> petTypes = this.owners.findPetTypes();
|
Collection<PetType> petTypes = this.owners.findPetTypes();
|
||||||
|
|
||||||
PetType petType1 = EntityUtils.getById(petTypes, PetType.class, 1);
|
PetType petType1 = EntityUtils.getById(petTypes, PetType.class, 1);
|
||||||
assertThat(petType1.getName()).isEqualTo("cat");
|
assertThat(petType1.getName()).isEqualTo(PetTypes.CAT.getValue());
|
||||||
PetType petType4 = EntityUtils.getById(petTypes, PetType.class, 4);
|
PetType petType4 = EntityUtils.getById(petTypes, PetType.class, 4);
|
||||||
assertThat(petType4.getName()).isEqualTo("snake");
|
assertThat(petType4.getName()).isEqualTo(PetTypes.SNAKE.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -201,6 +203,7 @@ class ClinicServiceTests {
|
||||||
int found = pet7.getVisits().size();
|
int found = pet7.getVisits().size();
|
||||||
Visit visit = new Visit();
|
Visit visit = new Visit();
|
||||||
visit.setDescription("test");
|
visit.setDescription("test");
|
||||||
|
;
|
||||||
|
|
||||||
owner6.addVisit(pet7.getId(), visit);
|
owner6.addVisit(pet7.getId(), visit);
|
||||||
this.owners.save(owner6);
|
this.owners.save(owner6);
|
||||||
|
|
|
@ -51,10 +51,13 @@ class CrashControllerIntegrationTests {
|
||||||
|
|
||||||
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class,
|
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class,
|
||||||
DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
|
DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
|
||||||
|
|
||||||
static class TestConfiguration {
|
static class TestConfiguration {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String BASE_URL = "http://localhost:";
|
||||||
|
|
||||||
@Value(value = "${local.server.port}")
|
@Value(value = "${local.server.port}")
|
||||||
private int port;
|
private int port;
|
||||||
|
|
||||||
|
@ -63,8 +66,7 @@ class CrashControllerIntegrationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testTriggerExceptionJson() {
|
void testTriggerExceptionJson() {
|
||||||
ResponseEntity<Map<String, Object>> resp = rest.exchange(
|
ResponseEntity<Map<String, Object>> resp = rest.exchange(RequestEntity.get(BASE_URL + port + "/oups").build(),
|
||||||
RequestEntity.get("http://localhost:" + port + "/oups").build(),
|
|
||||||
new ParameterizedTypeReference<Map<String, Object>>() {
|
new ParameterizedTypeReference<Map<String, Object>>() {
|
||||||
});
|
});
|
||||||
assertThat(resp).isNotNull();
|
assertThat(resp).isNotNull();
|
||||||
|
@ -81,7 +83,7 @@ class CrashControllerIntegrationTests {
|
||||||
void testTriggerExceptionHtml() {
|
void testTriggerExceptionHtml() {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setAccept(List.of(MediaType.TEXT_HTML));
|
headers.setAccept(List.of(MediaType.TEXT_HTML));
|
||||||
ResponseEntity<String> resp = rest.exchange("http://localhost:" + port + "/oups", HttpMethod.GET,
|
ResponseEntity<String> resp = rest.exchange(BASE_URL + port + "/oups", HttpMethod.GET,
|
||||||
new HttpEntity<>(headers), String.class);
|
new HttpEntity<>(headers), String.class);
|
||||||
assertThat(resp).isNotNull();
|
assertThat(resp).isNotNull();
|
||||||
assertThat(resp.getStatusCode().is5xxServerError());
|
assertThat(resp.getStatusCode().is5xxServerError());
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.samples.petclinic.system;
|
package org.springframework.samples.petclinic.system;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
@ -31,16 +32,12 @@ import org.junit.jupiter.api.Test;
|
||||||
// luck ((plain(st) UNIT test)! :)
|
// luck ((plain(st) UNIT test)! :)
|
||||||
class CrashControllerTests {
|
class CrashControllerTests {
|
||||||
|
|
||||||
CrashController testee = new CrashController();
|
CrashController crashController = new CrashController();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testTriggerException() throws Exception {
|
void testTriggerException() throws Exception {
|
||||||
RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
|
assertThatThrownBy(() -> crashController.triggerException()).isInstanceOf(RuntimeException.class)
|
||||||
testee.triggerException();
|
.hasMessage("Expected: controller used to showcase what happens when an exception is thrown");
|
||||||
});
|
|
||||||
|
|
||||||
assertEquals("Expected: controller used to showcase what happens when an exception is thrown",
|
|
||||||
thrown.getMessage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.springframework.samples.petclinic.TestUtils.VetTestUtil.createVet;
|
||||||
|
import static org.springframework.samples.petclinic.TestUtils.VetTestUtil.createVetWithSpecialty;
|
||||||
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.result.MockMvcResultMatchers.*;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||||
|
|
||||||
|
@ -49,31 +51,13 @@ class VetControllerTests {
|
||||||
@MockBean
|
@MockBean
|
||||||
private VetRepository vets;
|
private VetRepository vets;
|
||||||
|
|
||||||
private Vet james() {
|
|
||||||
Vet james = new Vet();
|
|
||||||
james.setFirstName("James");
|
|
||||||
james.setLastName("Carter");
|
|
||||||
james.setId(1);
|
|
||||||
return james;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Vet helen() {
|
|
||||||
Vet helen = new Vet();
|
|
||||||
helen.setFirstName("Helen");
|
|
||||||
helen.setLastName("Leary");
|
|
||||||
helen.setId(2);
|
|
||||||
Specialty radiology = new Specialty();
|
|
||||||
radiology.setId(1);
|
|
||||||
radiology.setName("radiology");
|
|
||||||
helen.addSpecialty(radiology);
|
|
||||||
return helen;
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() {
|
||||||
given(this.vets.findAll()).willReturn(Lists.newArrayList(james(), helen()));
|
var vet = createVet();
|
||||||
|
var vetWithSpecialty = createVetWithSpecialty();
|
||||||
|
given(this.vets.findAll()).willReturn(Lists.newArrayList(vet, vetWithSpecialty));
|
||||||
given(this.vets.findAll(any(Pageable.class)))
|
given(this.vets.findAll(any(Pageable.class)))
|
||||||
.willReturn(new PageImpl<Vet>(Lists.newArrayList(james(), helen())));
|
.willReturn(new PageImpl<Vet>(Lists.newArrayList(vet, vetWithSpecialty)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.util.SerializationUtils;
|
import org.springframework.util.SerializationUtils;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.springframework.samples.petclinic.TestUtils.VetTestUtil.createVet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
|
@ -27,15 +28,11 @@ class VetTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSerialization() {
|
void testSerialization() {
|
||||||
Vet vet = new Vet();
|
var vet = createVet();
|
||||||
vet.setFirstName("Zaphod");
|
|
||||||
vet.setLastName("Beeblebrox");
|
|
||||||
vet.setId(123);
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
Vet other = (Vet) SerializationUtils.deserialize(SerializationUtils.serialize(vet));
|
var other = (Vet) SerializationUtils.deserialize(SerializationUtils.serialize(vet));
|
||||||
assertThat(other.getFirstName()).isEqualTo(vet.getFirstName());
|
assertThat(other.equals(vet));
|
||||||
assertThat(other.getLastName()).isEqualTo(vet.getLastName());
|
|
||||||
assertThat(other.getId()).isEqualTo(vet.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue