mirror of
https://github.com/spring-projects/spring-petclinic.git
synced 2026-01-22 03:11:11 +00:00
Merge 8c8509b46e into 3a931080d4
This commit is contained in:
commit
913ab6dd61
56 changed files with 774 additions and 204 deletions
2
.github/dco.yml
vendored
2
.github/dco.yml
vendored
|
|
@ -1,2 +0,0 @@
|
|||
require:
|
||||
members: false
|
||||
31
.github/workflows/deploy-and-test-cluster.yml
vendored
31
.github/workflows/deploy-and-test-cluster.yml
vendored
|
|
@ -1,31 +0,0 @@
|
|||
name: Deploy and Test Cluster
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'k8s/**'
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'k8s/**'
|
||||
|
||||
jobs:
|
||||
deploy-and-test-cluster:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out the repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Create k8s Kind Cluster
|
||||
uses: helm/kind-action@v1
|
||||
|
||||
- name: Deploy application
|
||||
run: |
|
||||
kubectl apply -f k8s/
|
||||
|
||||
- name: Wait for Pods to be ready
|
||||
run: |
|
||||
kubectl wait --for=condition=ready pod -l app=demo-db --timeout=180s
|
||||
kubectl wait --for=condition=ready pod -l app=petclinic --timeout=180s
|
||||
|
||||
31
.github/workflows/gradle-build.yml
vendored
31
.github/workflows/gradle-build.yml
vendored
|
|
@ -1,31 +0,0 @@
|
|||
# This workflow will build a Java project with Gradle, and cache/restore any dependencies to improve the workflow execution time
|
||||
# For more information see: https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-java-with-gradle
|
||||
|
||||
name: Java CI with Gradle
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
java: [ '17' ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK ${{matrix.java}}
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: ${{matrix.java}}
|
||||
distribution: 'adopt'
|
||||
cache: maven
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
29
.github/workflows/maven-build.yml
vendored
29
.github/workflows/maven-build.yml
vendored
|
|
@ -1,29 +0,0 @@
|
|||
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
|
||||
# For more information see: https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-java-with-maven
|
||||
|
||||
name: Java CI with Maven
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
java: [ '17' ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK ${{matrix.java}}
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: ${{matrix.java}}
|
||||
distribution: 'adopt'
|
||||
cache: maven
|
||||
- name: Build with Maven Wrapper
|
||||
run: ./mvnw -B verify
|
||||
76
README.md
76
README.md
|
|
@ -1,3 +1,42 @@
|
|||
## Pet Attributes API Persistent Assignment
|
||||
|
||||
## Project Updates
|
||||
|
||||
- **Refactored / Rearranged Project Structure**
|
||||
[Commit Link](https://github.com/shrirangjoshi94/spring-petclinic/commit/569cc89c3ae4ebe50a432022cbf71e568ef5b4d5)
|
||||
|
||||
- **Save and Fetch Pet Attributes Feature**
|
||||
[Pull Request Link](https://github.com/shrirangjoshi94/spring-petclinic/pull/2)
|
||||
|
||||
---
|
||||
|
||||
### Development Notes
|
||||
|
||||
What I did was first restructured the project and merged the refactoring PR.
|
||||
Then I created a new PR to implement the save and fetch Pet Attributes feature.
|
||||
|
||||
|
||||
### 1. Save Attributes for a Pet
|
||||
**POST** `/pets/{petId}/attributes`
|
||||
Example request:
|
||||
`POST localhost:8080/pets/2/attributes`
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"temperament": "angry",
|
||||
"lengthCm": 401,
|
||||
"weightKg": 100
|
||||
}
|
||||
```
|
||||
|
||||
### 2. GET Attributes for a Pet
|
||||
**GET** `/pets/{petId}/attributes`
|
||||
Example request:
|
||||
`GET localhost:8080/pets/2/attributes`
|
||||
|
||||
----------------------------------------------------------
|
||||
|
||||
# Spring PetClinic Sample Application [](https://github.com/spring-projects/spring-petclinic/actions/workflows/maven-build.yml)[](https://github.com/spring-projects/spring-petclinic/actions/workflows/gradle-build.yml)
|
||||
|
||||
[](https://gitpod.io/#https://github.com/spring-projects/spring-petclinic) [](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=7517918)
|
||||
|
|
@ -126,40 +165,3 @@ The following items should be installed in your system:
|
|||
1. Navigate to the Petclinic
|
||||
|
||||
Visit [http://localhost:8080](http://localhost:8080) in your browser.
|
||||
|
||||
## Looking for something in particular?
|
||||
|
||||
|Spring Boot Configuration | Class or Java property files |
|
||||
|--------------------------|---|
|
||||
|The Main Class | [PetClinicApplication](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java) |
|
||||
|Properties Files | [application.properties](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources) |
|
||||
|Caching | [CacheConfiguration](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java) |
|
||||
|
||||
## Interesting Spring Petclinic branches and forks
|
||||
|
||||
The Spring Petclinic "main" branch in the [spring-projects](https://github.com/spring-projects/spring-petclinic)
|
||||
GitHub org is the "canonical" implementation based on Spring Boot and Thymeleaf. There are
|
||||
[quite a few forks](https://spring-petclinic.github.io/docs/forks.html) in the GitHub org
|
||||
[spring-petclinic](https://github.com/spring-petclinic). If you are interested in using a different technology stack to implement the Pet Clinic, please join the community there.
|
||||
|
||||
## Interaction with other open-source projects
|
||||
|
||||
One of the best parts about working on the Spring Petclinic application is that we have the opportunity to work in direct contact with many Open Source projects. We found bugs/suggested improvements on various topics such as Spring, Spring Data, Bean Validation and even Eclipse! In many cases, they've been fixed/implemented in just a few days.
|
||||
Here is a list of them:
|
||||
|
||||
| Name | Issue |
|
||||
|------|-------|
|
||||
| Spring JDBC: simplify usage of NamedParameterJdbcTemplate | [SPR-10256](https://github.com/spring-projects/spring-framework/issues/14889) and [SPR-10257](https://github.com/spring-projects/spring-framework/issues/14890) |
|
||||
| Bean Validation / Hibernate Validator: simplify Maven dependencies and backward compatibility |[HV-790](https://hibernate.atlassian.net/browse/HV-790) and [HV-792](https://hibernate.atlassian.net/browse/HV-792) |
|
||||
| Spring Data: provide more flexibility when working with JPQL queries | [DATAJPA-292](https://github.com/spring-projects/spring-data-jpa/issues/704) |
|
||||
|
||||
## Contributing
|
||||
|
||||
The [issue tracker](https://github.com/spring-projects/spring-petclinic/issues) is the preferred channel for bug reports, feature requests and submitting pull requests.
|
||||
|
||||
For pull requests, editor preferences are available in the [editor config](.editorconfig) for easy use in common text editors. Read more and download plugins at <https://editorconfig.org>. All commits must include a __Signed-off-by__ trailer at the end of each commit message to indicate that the contributor agrees to the Developer Certificate of Origin.
|
||||
For additional details, please refer to the blog post [Hello DCO, Goodbye CLA: Simplifying Contributions to Spring](https://spring.io/blog/2025/01/06/hello-dco-goodbye-cla-simplifying-contributions-to-spring).
|
||||
|
||||
## License
|
||||
|
||||
The Spring PetClinic sample application is released under version 2.0 of the [Apache License](https://www.apache.org/licenses/LICENSE-2.0).
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ package org.springframework.samples.petclinic;
|
|||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.samples.petclinic.model.BaseEntity;
|
||||
import org.springframework.samples.petclinic.model.Person;
|
||||
import org.springframework.samples.petclinic.vet.Vet;
|
||||
import org.springframework.samples.petclinic.common.model.BaseEntity;
|
||||
import org.springframework.samples.petclinic.common.model.Person;
|
||||
import org.springframework.samples.petclinic.vet.model.Vet;
|
||||
|
||||
public class PetClinicRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,11 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.owner;
|
||||
package org.springframework.samples.petclinic.common.formatter;
|
||||
|
||||
import org.springframework.format.Formatter;
|
||||
import org.springframework.samples.petclinic.owner.model.PetType;
|
||||
import org.springframework.samples.petclinic.owner.repository.OwnerRepository;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.model;
|
||||
package org.springframework.samples.petclinic.common.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.model;
|
||||
package org.springframework.samples.petclinic.common.model;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.model;
|
||||
package org.springframework.samples.petclinic.common.model;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The classes in this package represent utilities used by the domain.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.model;
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.owner;
|
||||
package org.springframework.samples.petclinic.owner.controller;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
|
@ -21,6 +21,8 @@ import java.util.Optional;
|
|||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.samples.petclinic.owner.model.Owner;
|
||||
import org.springframework.samples.petclinic.owner.repository.OwnerRepository;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
|
|
@ -44,7 +46,7 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
|||
* @author Wick Dynex
|
||||
*/
|
||||
@Controller
|
||||
class OwnerController {
|
||||
public class OwnerController {
|
||||
|
||||
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
|
||||
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package org.springframework.samples.petclinic.owner.controller;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.samples.petclinic.owner.dto.PetAttributesDTO;
|
||||
import org.springframework.samples.petclinic.owner.expection.PetNotFoundException;
|
||||
import org.springframework.samples.petclinic.owner.model.PetAttributes;
|
||||
import org.springframework.samples.petclinic.owner.repository.PetRepository;
|
||||
import org.springframework.samples.petclinic.owner.service.PetAttributesService;
|
||||
import org.springframework.samples.petclinic.owner.validation.PetIdExistsValidator;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/pets/{petId}/attributes")
|
||||
public class PetAttributesController {
|
||||
|
||||
private final PetAttributesService petAttributesService;
|
||||
|
||||
private PetIdExistsValidator petIdExistsValidator;
|
||||
|
||||
private PetRepository petRepository;
|
||||
|
||||
public PetAttributesController(PetIdExistsValidator petIdExistsValidator, PetAttributesService petAttributesService,
|
||||
PetRepository petRepository) {
|
||||
this.petIdExistsValidator = petIdExistsValidator;
|
||||
this.petAttributesService = petAttributesService;
|
||||
this.petRepository = petRepository;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<?> getPetAttributes(@PathVariable("petId") int petId) {
|
||||
if (!petRepository.existsById(petId)) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Pet not found");
|
||||
}
|
||||
|
||||
Optional<PetAttributes> optionalAttributes = petAttributesService.findByPetId(petId);
|
||||
|
||||
return optionalAttributes.<ResponseEntity<?>>map(ResponseEntity::ok)
|
||||
.orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).body("Attributes not found"));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity savePetAttributes(@PathVariable int petId, @Valid @RequestBody PetAttributesDTO dto,
|
||||
BindingResult bindingResult) {
|
||||
// Validate petId exists
|
||||
petIdExistsValidator.validate(petId, bindingResult);
|
||||
|
||||
if (bindingResult.hasErrors()) {
|
||||
return ResponseEntity.badRequest().body(bindingResult.getAllErrors());
|
||||
}
|
||||
|
||||
dto.setPetId(petId);
|
||||
|
||||
try {
|
||||
petAttributesService.savePetAttributes(dto);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body("Pet attributes saved");
|
||||
} catch (PetNotFoundException e) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -13,12 +13,17 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.owner;
|
||||
package org.springframework.samples.petclinic.owner.controller;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.samples.petclinic.owner.model.Owner;
|
||||
import org.springframework.samples.petclinic.owner.model.Pet;
|
||||
import org.springframework.samples.petclinic.owner.model.PetType;
|
||||
import org.springframework.samples.petclinic.owner.repository.OwnerRepository;
|
||||
import org.springframework.samples.petclinic.owner.validation.PetValidator;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
|
@ -42,7 +47,7 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
|||
*/
|
||||
@Controller
|
||||
@RequestMapping("/owners/{ownerId}")
|
||||
class PetController {
|
||||
public class PetController {
|
||||
|
||||
private static final String VIEWS_PETS_CREATE_OR_UPDATE_FORM = "pets/createOrUpdatePetForm";
|
||||
|
||||
|
|
@ -13,11 +13,15 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.owner;
|
||||
package org.springframework.samples.petclinic.owner.controller;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.samples.petclinic.owner.model.Owner;
|
||||
import org.springframework.samples.petclinic.owner.repository.OwnerRepository;
|
||||
import org.springframework.samples.petclinic.owner.model.Pet;
|
||||
import org.springframework.samples.petclinic.owner.model.Visit;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
|
|
@ -39,7 +43,7 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
|||
* @author Wick Dynex
|
||||
*/
|
||||
@Controller
|
||||
class VisitController {
|
||||
public class VisitController {
|
||||
|
||||
private final OwnerRepository owners;
|
||||
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package org.springframework.samples.petclinic.owner.dto;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class PetAttributesDTO {
|
||||
|
||||
@NotNull(message = "Pet ID is required")
|
||||
private int petId;
|
||||
|
||||
@NotBlank(message = "Temperament is required")
|
||||
@Size(max = 100, message = "Temperament must not exceed 100 characters")
|
||||
private String temperament;
|
||||
|
||||
@NotNull
|
||||
@DecimalMin(value = "0.1", inclusive = true, message = "Length must be at least 0.1 cm")
|
||||
@DecimalMax(value = "500.0", inclusive = true, message = "Length must be less than or equal to 500 cm")
|
||||
private BigDecimal lengthCm;
|
||||
|
||||
@NotNull
|
||||
@DecimalMin(value = "0.1", inclusive = true, message = "Weight must be at least 0.1 kg")
|
||||
@DecimalMax(value = "500.0", inclusive = true, message = "Weight must be less than or equal to 500 kg")
|
||||
private BigDecimal weightKg;
|
||||
|
||||
// Getters and setters
|
||||
|
||||
public int getPetId() {
|
||||
return petId;
|
||||
}
|
||||
|
||||
public void setPetId(int petId) {
|
||||
this.petId = petId;
|
||||
}
|
||||
|
||||
public String getTemperament() {
|
||||
return temperament;
|
||||
}
|
||||
|
||||
public void setTemperament(String temperament) {
|
||||
this.temperament = temperament;
|
||||
}
|
||||
|
||||
public BigDecimal getLengthCm() {
|
||||
return lengthCm;
|
||||
}
|
||||
|
||||
public void setLengthCm(BigDecimal lengthCm) {
|
||||
this.lengthCm = lengthCm;
|
||||
}
|
||||
|
||||
public BigDecimal getWeightKg() {
|
||||
return weightKg;
|
||||
}
|
||||
|
||||
public void setWeightKg(BigDecimal weightKg) {
|
||||
this.weightKg = weightKg;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package org.springframework.samples.petclinic.owner.expection;
|
||||
|
||||
public class PetNotFoundException extends RuntimeException {
|
||||
|
||||
public PetNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -13,13 +13,13 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.owner;
|
||||
package org.springframework.samples.petclinic.owner.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.samples.petclinic.model.Person;
|
||||
import org.springframework.samples.petclinic.common.model.Person;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.owner;
|
||||
package org.springframework.samples.petclinic.owner.model;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
|
|
@ -21,7 +21,7 @@ import java.util.LinkedHashSet;
|
|||
import java.util.Set;
|
||||
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.samples.petclinic.model.NamedEntity;
|
||||
import org.springframework.samples.petclinic.common.model.NamedEntity;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package org.springframework.samples.petclinic.owner.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Entity
|
||||
@Table(name = "pet_attributes")
|
||||
public class PetAttributes {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "pet_id", nullable = false)
|
||||
@JsonIgnore
|
||||
private Pet pet;
|
||||
|
||||
private String temperament;
|
||||
|
||||
@Column(name = "length_cm", precision = 5, scale = 2)
|
||||
private BigDecimal lengthCm;
|
||||
|
||||
@Column(name = "weight_kg", precision = 5, scale = 2)
|
||||
private BigDecimal weightKg;
|
||||
|
||||
@Column(name = "additional_attributes", columnDefinition = "json")
|
||||
private String additionalAttributes;
|
||||
|
||||
// --- Getters and Setters ---
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Pet getPet() {
|
||||
return pet;
|
||||
}
|
||||
|
||||
public void setPet(Pet pet) {
|
||||
this.pet = pet;
|
||||
}
|
||||
|
||||
public String getTemperament() {
|
||||
return temperament;
|
||||
}
|
||||
|
||||
public void setTemperament(String temperament) {
|
||||
this.temperament = temperament;
|
||||
}
|
||||
|
||||
public BigDecimal getLengthCm() {
|
||||
return lengthCm;
|
||||
}
|
||||
|
||||
public void setLengthCm(BigDecimal lengthCm) {
|
||||
this.lengthCm = lengthCm;
|
||||
}
|
||||
|
||||
public BigDecimal getWeightKg() {
|
||||
return weightKg;
|
||||
}
|
||||
|
||||
public void setWeightKg(BigDecimal weightKg) {
|
||||
this.weightKg = weightKg;
|
||||
}
|
||||
|
||||
public String getAdditionalAttributes() {
|
||||
return additionalAttributes;
|
||||
}
|
||||
|
||||
public void setAdditionalAttributes(String additionalAttributes) {
|
||||
this.additionalAttributes = additionalAttributes;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -13,9 +13,9 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.owner;
|
||||
package org.springframework.samples.petclinic.owner.model;
|
||||
|
||||
import org.springframework.samples.petclinic.model.NamedEntity;
|
||||
import org.springframework.samples.petclinic.common.model.NamedEntity;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
|
|
@ -13,12 +13,12 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.owner;
|
||||
package org.springframework.samples.petclinic.owner.model;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.samples.petclinic.model.BaseEntity;
|
||||
import org.springframework.samples.petclinic.common.model.BaseEntity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.owner;
|
||||
package org.springframework.samples.petclinic.owner.repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
|
@ -23,6 +23,8 @@ import org.springframework.data.domain.Page;
|
|||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.samples.petclinic.owner.model.Owner;
|
||||
import org.springframework.samples.petclinic.owner.model.PetType;
|
||||
|
||||
/**
|
||||
* Repository class for <code>Owner</code> domain objects All method names are compliant
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package org.springframework.samples.petclinic.owner.repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.samples.petclinic.owner.model.PetAttributes;
|
||||
|
||||
/**
|
||||
* Repository class for <code>PetAttributes</code> domain objects. Provides CRUD
|
||||
* operations and custom query methods related to a pet's attributes.
|
||||
*
|
||||
* @see org.springframework.samples.petclinic.owner.model.PetAttributes
|
||||
*/
|
||||
public interface PetAttributesRepository extends JpaRepository<PetAttributes, Integer> {
|
||||
|
||||
/**
|
||||
* Find pet attributes by pet ID.
|
||||
*
|
||||
* @param petId the ID of the pet
|
||||
* @return an Optional containing the PetAttributes if found
|
||||
*/
|
||||
Optional<PetAttributes> findByPetId(Integer petId);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package org.springframework.samples.petclinic.owner.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.samples.petclinic.owner.model.Pet;
|
||||
|
||||
public interface PetRepository extends JpaRepository<Pet, Integer> {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package org.springframework.samples.petclinic.owner.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.samples.petclinic.owner.dto.PetAttributesDTO;
|
||||
import org.springframework.samples.petclinic.owner.expection.PetNotFoundException;
|
||||
import org.springframework.samples.petclinic.owner.model.Pet;
|
||||
import org.springframework.samples.petclinic.owner.model.PetAttributes;
|
||||
import org.springframework.samples.petclinic.owner.repository.PetAttributesRepository;
|
||||
import org.springframework.samples.petclinic.owner.repository.PetRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class PetAttributesService {
|
||||
|
||||
private final PetAttributesRepository petAttributesRepository;
|
||||
|
||||
private final PetRepository petRepository;
|
||||
|
||||
@Autowired
|
||||
public PetAttributesService(PetAttributesRepository petAttributesRepository, PetRepository petRepository) {
|
||||
this.petAttributesRepository = petAttributesRepository;
|
||||
this.petRepository = petRepository;
|
||||
}
|
||||
|
||||
public Optional<PetAttributes> findByPetId(Integer petId) {
|
||||
return petAttributesRepository.findByPetId(petId);
|
||||
}
|
||||
|
||||
public void savePetAttributes(PetAttributesDTO dto) {
|
||||
Optional<Pet> pet = petRepository.findById(dto.getPetId());
|
||||
|
||||
if (pet.isEmpty()) {
|
||||
throw new PetNotFoundException("Pet not found");
|
||||
}
|
||||
|
||||
PetAttributes attributes = petAttributesRepository.findByPetId(dto.getPetId()).orElse(new PetAttributes());
|
||||
|
||||
attributes.setPet(pet.get());
|
||||
attributes.setTemperament(dto.getTemperament());
|
||||
attributes.setLengthCm(dto.getLengthCm());
|
||||
attributes.setWeightKg(dto.getWeightKg());
|
||||
petAttributesRepository.save(attributes);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package org.springframework.samples.petclinic.owner.validation;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.samples.petclinic.owner.repository.PetRepository;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
@Component
|
||||
public class PetIdExistsValidator implements Validator {
|
||||
|
||||
private final PetRepository petRepository;
|
||||
|
||||
@Autowired
|
||||
public PetIdExistsValidator(PetRepository petRepository) {
|
||||
this.petRepository = petRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
// This validator supports Integer class, for validating petId field
|
||||
return Integer.class.equals(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(Object target, Errors errors) {
|
||||
if (target == null) {
|
||||
errors.reject("petId.null", "Pet ID is required");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(target instanceof Integer)) {
|
||||
errors.reject("petId.type", "Pet ID must be an integer");
|
||||
return;
|
||||
}
|
||||
|
||||
Integer petId = (Integer) target;
|
||||
|
||||
if (petId <= 0) {
|
||||
errors.rejectValue("", "petId.positive", "Pet ID must be a positive number");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!petRepository.existsById(petId)) {
|
||||
errors.rejectValue("", "petId.notFound", "Pet with ID " + petId + " does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -13,8 +13,9 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.owner;
|
||||
package org.springframework.samples.petclinic.owner.validation;
|
||||
|
||||
import org.springframework.samples.petclinic.owner.model.Pet;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.Validator;
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.samples.petclinic.system;
|
||||
package org.springframework.samples.petclinic.system.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package org.springframework.samples.petclinic.system;
|
||||
package org.springframework.samples.petclinic.system.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.system;
|
||||
package org.springframework.samples.petclinic.system.controller;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
|
@ -26,7 +26,7 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||
* Also see how a view that resolves to "error" has been added ("error.html").
|
||||
*/
|
||||
@Controller
|
||||
class CrashController {
|
||||
public class CrashController {
|
||||
|
||||
@GetMapping("/oups")
|
||||
public String triggerException() {
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.samples.petclinic.system;
|
||||
package org.springframework.samples.petclinic.system.controller;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
|
@ -13,13 +13,16 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.vet;
|
||||
package org.springframework.samples.petclinic.vet.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.samples.petclinic.vet.model.Vet;
|
||||
import org.springframework.samples.petclinic.vet.repository.VetRepository;
|
||||
import org.springframework.samples.petclinic.vet.model.Vets;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
|
@ -33,7 +36,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
|||
* @author Arjen Poutsma
|
||||
*/
|
||||
@Controller
|
||||
class VetController {
|
||||
public class VetController {
|
||||
|
||||
private final VetRepository vetRepository;
|
||||
|
||||
|
|
@ -13,9 +13,9 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.vet;
|
||||
package org.springframework.samples.petclinic.vet.model;
|
||||
|
||||
import org.springframework.samples.petclinic.model.NamedEntity;
|
||||
import org.springframework.samples.petclinic.common.model.NamedEntity;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.vet;
|
||||
package org.springframework.samples.petclinic.vet.model;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
|
|
@ -21,8 +21,8 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.samples.petclinic.model.NamedEntity;
|
||||
import org.springframework.samples.petclinic.model.Person;
|
||||
import org.springframework.samples.petclinic.common.model.NamedEntity;
|
||||
import org.springframework.samples.petclinic.common.model.Person;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.vet;
|
||||
package org.springframework.samples.petclinic.vet.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
|
@ -13,13 +13,14 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.samples.petclinic.vet;
|
||||
package org.springframework.samples.petclinic.vet.repository;
|
||||
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.samples.petclinic.vet.model.Vet;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
|
|
@ -1,7 +1,22 @@
|
|||
# database init, supports mysql too
|
||||
database=h2
|
||||
spring.sql.init.schema-locations=classpath*:db/${database}/schema.sql
|
||||
spring.sql.init.data-locations=classpath*:db/${database}/data.sql
|
||||
#database=jdbc:mysql://localhost:3306/petclinic?useSSL=false&serverTimezone=UTC
|
||||
#spring.datasource.username=root
|
||||
#spring.datasource.password=testA@123
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
|
||||
# database init, supports mysql too
|
||||
database=mysql
|
||||
spring.datasource.url=${MYSQL_URL:jdbc:mysql://localhost/petclinic}
|
||||
spring.datasource.username=root
|
||||
spring.datasource.password=your-password
|
||||
# SQL is written to be idempotent so this is safe
|
||||
spring.sql.init.mode=always
|
||||
|
||||
# Hibernate Dialect (important for compatibility)
|
||||
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
|
||||
|
||||
spring.sql.init.schema-locations=classpath*:db/mysql/schema.sql
|
||||
spring.sql.init.data-locations=classpath*:db/mysql/data.sql
|
||||
|
||||
# Web
|
||||
spring.thymeleaf.mode=HTML
|
||||
|
|
|
|||
|
|
@ -53,3 +53,13 @@ CREATE TABLE IF NOT EXISTS visits (
|
|||
description VARCHAR(255),
|
||||
FOREIGN KEY (pet_id) REFERENCES pets(id)
|
||||
) engine=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS pet_attributes (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
pet_id INT UNSIGNED NOT NULL,
|
||||
temperament VARCHAR(100),
|
||||
length_cm DECIMAL(5,2),
|
||||
weight_kg DECIMAL(5,2),
|
||||
additional_attributes JSON,
|
||||
FOREIGN KEY (pet_id) REFERENCES pets(id)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import org.springframework.boot.web.client.RestTemplateBuilder;
|
|||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.samples.petclinic.vet.VetRepository;
|
||||
import org.springframework.samples.petclinic.vet.repository.VetRepository;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.aot.DisabledInAotMode;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import org.springframework.boot.web.client.RestTemplateBuilder;
|
|||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.samples.petclinic.vet.VetRepository;
|
||||
import org.springframework.samples.petclinic.vet.repository.VetRepository;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ import org.springframework.core.env.PropertySource;
|
|||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.samples.petclinic.vet.VetRepository;
|
||||
import org.springframework.samples.petclinic.vet.repository.VetRepository;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.testcontainers.DockerClientFactory;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import java.util.Set;
|
|||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.samples.petclinic.common.model.Person;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
|||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.samples.petclinic.owner.controller.OwnerController;
|
||||
import org.springframework.samples.petclinic.owner.model.Owner;
|
||||
import org.springframework.samples.petclinic.owner.model.Pet;
|
||||
import org.springframework.samples.petclinic.owner.model.PetType;
|
||||
import org.springframework.samples.petclinic.owner.model.Visit;
|
||||
import org.springframework.samples.petclinic.owner.repository.OwnerRepository;
|
||||
import org.springframework.test.context.aot.DisabledInAotMode;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,139 @@
|
|||
package org.springframework.samples.petclinic.owner;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.samples.petclinic.owner.controller.PetAttributesController;
|
||||
import org.springframework.samples.petclinic.owner.dto.PetAttributesDTO;
|
||||
import org.springframework.samples.petclinic.owner.expection.PetNotFoundException;
|
||||
import org.springframework.samples.petclinic.owner.model.PetAttributes;
|
||||
import org.springframework.samples.petclinic.owner.repository.PetRepository;
|
||||
import org.springframework.samples.petclinic.owner.service.PetAttributesService;
|
||||
import org.springframework.samples.petclinic.owner.validation.PetIdExistsValidator;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.validation.BindingResult;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
@WebMvcTest(PetAttributesController.class)
|
||||
public class PetAttributesControllerTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@MockBean
|
||||
private PetAttributesService petAttributesService;
|
||||
|
||||
@MockBean
|
||||
private PetIdExistsValidator petIdExistsValidator;
|
||||
|
||||
@MockBean
|
||||
private PetRepository petRepository;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Test
|
||||
void testGetPetAttributes_Found() throws Exception {
|
||||
int petId = 1;
|
||||
PetAttributes attributes = new PetAttributes();
|
||||
|
||||
Mockito.when(petRepository.existsById(petId)).thenReturn(true);
|
||||
Mockito.when(petAttributesService.findByPetId(petId)).thenReturn(Optional.of(attributes));
|
||||
|
||||
mockMvc.perform(get("/pets/{petId}/attributes", petId)).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPetAttributes_PetNotFound() throws Exception {
|
||||
int petId = 1;
|
||||
|
||||
Mockito.when(petRepository.existsById(petId)).thenReturn(false);
|
||||
|
||||
mockMvc.perform(get("/pets/{petId}/attributes", petId))
|
||||
.andExpect(status().isNotFound())
|
||||
.andExpect(content().string("Pet not found"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPetAttributes_AttributesNotFound() throws Exception {
|
||||
int petId = 1;
|
||||
|
||||
Mockito.when(petRepository.existsById(petId)).thenReturn(true);
|
||||
Mockito.when(petAttributesService.findByPetId(petId)).thenReturn(Optional.empty());
|
||||
|
||||
mockMvc.perform(get("/pets/{petId}/attributes", petId))
|
||||
.andExpect(status().isNotFound())
|
||||
.andExpect(content().string("Attributes not found"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSavePetAttributes_Success() throws Exception {
|
||||
int petId = 1;
|
||||
PetAttributesDTO dto = new PetAttributesDTO();
|
||||
dto.setTemperament("Calm");
|
||||
dto.setWeightKg(BigDecimal.valueOf(12.5));
|
||||
dto.setLengthCm(BigDecimal.valueOf(60.0));
|
||||
|
||||
Mockito.doNothing().when(petIdExistsValidator).validate(eq(petId), any(BindingResult.class));
|
||||
Mockito.doNothing().when(petAttributesService).savePetAttributes(any(PetAttributesDTO.class));
|
||||
|
||||
mockMvc
|
||||
.perform(post("/pets/{petId}/attributes", petId).contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(dto)))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(content().string("Pet attributes saved"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSavePetAttributes_PetNotFoundException() throws Exception {
|
||||
int petId = 1;
|
||||
PetAttributesDTO dto = new PetAttributesDTO();
|
||||
dto.setTemperament("Aggressive");
|
||||
dto.setWeightKg(BigDecimal.valueOf(12.5));
|
||||
dto.setLengthCm(BigDecimal.valueOf(60.0));
|
||||
|
||||
Mockito.doNothing().when(petIdExistsValidator).validate(eq(petId), any(BindingResult.class));
|
||||
Mockito.doThrow(new PetNotFoundException("Pet not found"))
|
||||
.when(petAttributesService)
|
||||
.savePetAttributes(any(PetAttributesDTO.class));
|
||||
|
||||
mockMvc
|
||||
.perform(post("/pets/{petId}/attributes", petId).contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(dto)))
|
||||
.andExpect(status().isNotFound())
|
||||
.andExpect(content().string("Pet not found"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSavePetAttributes_ValidationErrors() throws Exception {
|
||||
int petId = 1;
|
||||
PetAttributesDTO dto = new PetAttributesDTO();
|
||||
dto.setTemperament("Friendly");
|
||||
dto.setWeightKg(BigDecimal.valueOf(10.0));
|
||||
dto.setLengthCm(BigDecimal.valueOf(50.0));
|
||||
|
||||
// Simulate petId validation failure
|
||||
Mockito.doAnswer(invocation -> {
|
||||
BindingResult result = invocation.getArgument(1);
|
||||
result.reject("petId", "Validation failed");
|
||||
return null;
|
||||
}).when(petIdExistsValidator).validate(eq(petId), any(BindingResult.class));
|
||||
|
||||
mockMvc
|
||||
.perform(post("/pets/{petId}/attributes", petId).contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(dto)))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$[0].code").value("petId"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -24,6 +24,13 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.samples.petclinic.common.formatter.PetTypeFormatter;
|
||||
import org.springframework.samples.petclinic.owner.controller.PetController;
|
||||
import org.springframework.samples.petclinic.owner.controller.PetController;
|
||||
import org.springframework.samples.petclinic.owner.model.Owner;
|
||||
import org.springframework.samples.petclinic.owner.model.Pet;
|
||||
import org.springframework.samples.petclinic.owner.model.PetType;
|
||||
import org.springframework.samples.petclinic.owner.repository.OwnerRepository;
|
||||
import org.springframework.test.context.aot.DisabledInAotMode;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
|||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.samples.petclinic.common.formatter.PetTypeFormatter;
|
||||
import org.springframework.samples.petclinic.owner.model.PetType;
|
||||
import org.springframework.samples.petclinic.owner.repository.OwnerRepository;
|
||||
|
||||
/**
|
||||
* Test class for {@link PetTypeFormatter}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.samples.petclinic.owner.model.Pet;
|
||||
import org.springframework.samples.petclinic.owner.model.PetType;
|
||||
import org.springframework.samples.petclinic.owner.validation.PetValidator;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.MapBindingResult;
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.samples.petclinic.owner.controller.VisitController;
|
||||
import org.springframework.samples.petclinic.owner.model.Owner;
|
||||
import org.springframework.samples.petclinic.owner.model.Pet;
|
||||
import org.springframework.samples.petclinic.owner.repository.OwnerRepository;
|
||||
import org.springframework.test.context.aot.DisabledInAotMode;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabas
|
|||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.samples.petclinic.owner.Owner;
|
||||
import org.springframework.samples.petclinic.owner.OwnerRepository;
|
||||
import org.springframework.samples.petclinic.owner.Pet;
|
||||
import org.springframework.samples.petclinic.owner.PetType;
|
||||
import org.springframework.samples.petclinic.owner.Visit;
|
||||
import org.springframework.samples.petclinic.vet.Vet;
|
||||
import org.springframework.samples.petclinic.vet.VetRepository;
|
||||
import org.springframework.samples.petclinic.owner.model.Owner;
|
||||
import org.springframework.samples.petclinic.owner.repository.OwnerRepository;
|
||||
import org.springframework.samples.petclinic.owner.model.Pet;
|
||||
import org.springframework.samples.petclinic.owner.model.PetType;
|
||||
import org.springframework.samples.petclinic.owner.model.Visit;
|
||||
import org.springframework.samples.petclinic.vet.model.Vet;
|
||||
import org.springframework.samples.petclinic.vet.repository.VetRepository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
package org.springframework.samples.petclinic.service;
|
||||
|
||||
import org.springframework.orm.ObjectRetrievalFailureException;
|
||||
import org.springframework.samples.petclinic.model.BaseEntity;
|
||||
import org.springframework.samples.petclinic.common.model.BaseEntity;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ import java.util.Collection;
|
|||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @see org.springframework.samples.petclinic.model.BaseEntity
|
||||
* @see BaseEntity
|
||||
* @since 29.10.2003
|
||||
*/
|
||||
public abstract class EntityUtils {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
package org.springframework.samples.petclinic.service;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.*;
|
||||
import org.springframework.samples.petclinic.owner.dto.PetAttributesDTO;
|
||||
import org.springframework.samples.petclinic.owner.expection.PetNotFoundException;
|
||||
import org.springframework.samples.petclinic.owner.model.Pet;
|
||||
import org.springframework.samples.petclinic.owner.model.PetAttributes;
|
||||
import org.springframework.samples.petclinic.owner.repository.PetAttributesRepository;
|
||||
import org.springframework.samples.petclinic.owner.repository.PetRepository;
|
||||
import org.springframework.samples.petclinic.owner.service.PetAttributesService;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class PetAttributesServiceTest {
|
||||
|
||||
@Mock
|
||||
private PetAttributesRepository petAttributesRepository;
|
||||
|
||||
@Mock
|
||||
private PetRepository petRepository;
|
||||
|
||||
@InjectMocks
|
||||
private PetAttributesService petAttributesService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindByPetId_ReturnsAttributes() {
|
||||
PetAttributes attributes = new PetAttributes();
|
||||
when(petAttributesRepository.findByPetId(1)).thenReturn(Optional.of(attributes));
|
||||
Optional<PetAttributes> result = petAttributesService.findByPetId(1);
|
||||
assertTrue(result.isPresent());
|
||||
assertEquals(attributes, result.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindByPetId_ReturnsEmpty() {
|
||||
when(petAttributesRepository.findByPetId(1)).thenReturn(Optional.empty());
|
||||
Optional<PetAttributes> result = petAttributesService.findByPetId(1);
|
||||
assertFalse(result.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSavePetAttributes_Success_NewRecord() {
|
||||
Pet pet = new Pet();
|
||||
pet.setId(1);
|
||||
|
||||
PetAttributesDTO dto = new PetAttributesDTO();
|
||||
dto.setPetId(1);
|
||||
dto.setTemperament("Calm");
|
||||
dto.setLengthCm(BigDecimal.valueOf(40.0));
|
||||
dto.setWeightKg(BigDecimal.valueOf(8.5));
|
||||
|
||||
when(petRepository.findById(1)).thenReturn(Optional.of(pet));
|
||||
when(petAttributesRepository.findByPetId(1)).thenReturn(Optional.empty());
|
||||
|
||||
petAttributesService.savePetAttributes(dto);
|
||||
|
||||
// Expect a new PetAttributes to be created and saved
|
||||
ArgumentCaptor<PetAttributes> captor = ArgumentCaptor.forClass(PetAttributes.class);
|
||||
verify(petAttributesRepository).save(captor.capture());
|
||||
|
||||
PetAttributes saved = captor.getValue();
|
||||
assertEquals("Calm", saved.getTemperament());
|
||||
assertEquals(BigDecimal.valueOf(40.0), saved.getLengthCm());
|
||||
assertEquals(BigDecimal.valueOf(8.5), saved.getWeightKg());
|
||||
assertEquals(pet, saved.getPet());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSavePetAttributes_Success_UpdateExisting() {
|
||||
Pet pet = new Pet();
|
||||
pet.setId(1);
|
||||
|
||||
PetAttributes existing = new PetAttributes();
|
||||
existing.setPet(pet);
|
||||
|
||||
PetAttributesDTO dto = new PetAttributesDTO();
|
||||
dto.setPetId(1);
|
||||
dto.setTemperament("Playful");
|
||||
dto.setLengthCm(BigDecimal.valueOf(45.0));
|
||||
dto.setWeightKg(BigDecimal.valueOf(9.0));
|
||||
|
||||
when(petRepository.findById(1)).thenReturn(Optional.of(pet));
|
||||
when(petAttributesRepository.findByPetId(1)).thenReturn(Optional.of(existing));
|
||||
|
||||
petAttributesService.savePetAttributes(dto);
|
||||
|
||||
verify(petAttributesRepository).save(existing);
|
||||
assertEquals("Playful", existing.getTemperament());
|
||||
assertEquals(BigDecimal.valueOf(45.0), existing.getLengthCm());
|
||||
assertEquals(BigDecimal.valueOf(9.0), existing.getWeightKg());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSavePetAttributes_PetNotFound_ThrowsException() {
|
||||
PetAttributesDTO dto = new PetAttributesDTO();
|
||||
dto.setPetId(1);
|
||||
|
||||
when(petRepository.findById(1)).thenReturn(Optional.empty());
|
||||
|
||||
PetNotFoundException ex = assertThrows(PetNotFoundException.class, () -> {
|
||||
petAttributesService.savePetAttributes(dto);
|
||||
});
|
||||
|
||||
assertEquals("Pet not found", ex.getMessage());
|
||||
verify(petAttributesRepository, never()).save(any());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -41,7 +41,8 @@ import org.springframework.http.RequestEntity;
|
|||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
/**
|
||||
* Integration Test for {@link CrashController}.
|
||||
* Integration Test for
|
||||
* {@link org.springframework.samples.petclinic.system.controller.CrashController}.
|
||||
*
|
||||
* @author Alex Lutz
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -19,9 +19,11 @@ package org.springframework.samples.petclinic.system;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import org.springframework.samples.petclinic.system.controller.CrashController;
|
||||
|
||||
/**
|
||||
* Test class for {@link CrashController}
|
||||
* Test class for
|
||||
* {@link org.springframework.samples.petclinic.system.controller.CrashController}
|
||||
*
|
||||
* @author Colin But
|
||||
* @author Alex Lutz
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
|||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.samples.petclinic.vet.controller.VetController;
|
||||
import org.springframework.samples.petclinic.vet.model.Specialty;
|
||||
import org.springframework.samples.petclinic.vet.model.Vet;
|
||||
import org.springframework.samples.petclinic.vet.repository.VetRepository;
|
||||
import org.springframework.test.context.aot.DisabledInAotMode;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
package org.springframework.samples.petclinic.vet;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.samples.petclinic.vet.model.Vet;
|
||||
import org.springframework.util.SerializationUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue