mirror of
https://github.com/spring-projects/spring-petclinic.git
synced 2025-07-21 15:25:49 +00:00
jobruhnr
This commit is contained in:
parent
9244518ff9
commit
0e5c72d5ec
12 changed files with 94 additions and 103 deletions
|
@ -1,17 +1,17 @@
|
||||||
version: '3'
|
version: '3'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
pet-clinic:
|
# pet-clinic:
|
||||||
build: ./
|
# build: ./
|
||||||
healthcheck:
|
# healthcheck:
|
||||||
test: [ "CMD", "curl", "-f", "http://localhost:8082/" ]
|
# test: [ "CMD", "curl", "-f", "http://localhost:8082/" ]
|
||||||
interval: 20s
|
# interval: 20s
|
||||||
timeout: 10s
|
# timeout: 10s
|
||||||
retries: 4
|
# retries: 4
|
||||||
start_period: 5s
|
# start_period: 5s
|
||||||
ports:
|
# ports:
|
||||||
- "8082:8082"
|
# - "8082:8082"
|
||||||
entrypoint: java -jar app.jar
|
# entrypoint: java -jar app.jar
|
||||||
|
|
||||||
mysql:
|
mysql:
|
||||||
image: mysql:8.0
|
image: mysql:8.0
|
||||||
|
|
|
@ -4,28 +4,32 @@ receivers:
|
||||||
grpc:
|
grpc:
|
||||||
http:
|
http:
|
||||||
|
|
||||||
|
extensions:
|
||||||
|
basicauth/client:
|
||||||
|
client_auth:
|
||||||
|
username: 738734
|
||||||
|
password: glc_eyJvIjoiOTQzMDk3IiwibiI6Im90bHAtb3RscCIsImsiOiJEWnhpNTFsZVZWMHJYcTg5MTVqODJOOEgiLCJtIjp7InIiOiJ1cyJ9fQ==
|
||||||
|
|
||||||
exporters:
|
exporters:
|
||||||
jaeger:
|
otlphttp/grafana:
|
||||||
endpoint: "jaeger:14250"
|
endpoint: https://otlp-gateway-prod-us-east-0.grafana.net/otlp
|
||||||
|
auth:
|
||||||
|
authenticator: basicauth/client
|
||||||
tls:
|
tls:
|
||||||
insecure: true
|
insecure: false
|
||||||
|
|
||||||
otlp/digma:
|
otlp/digma:
|
||||||
endpoint: ${OTLP_EXPORTER_DIGMA_COLLECTOR_API}
|
endpoint: ${OTLP_EXPORTER_DIGMA_COLLECTOR_API}
|
||||||
tls:
|
tls:
|
||||||
insecure: true
|
insecure: true
|
||||||
prometheus:
|
|
||||||
endpoint: "0.0.0.0:8889"
|
|
||||||
|
|
||||||
processors:
|
processors:
|
||||||
batch:
|
batch:
|
||||||
|
|
||||||
service:
|
service:
|
||||||
|
extensions: [basicauth/client]
|
||||||
pipelines:
|
pipelines:
|
||||||
traces:
|
traces:
|
||||||
receivers: [otlp]
|
receivers: [otlp]
|
||||||
exporters: [otlp/digma, jaeger]
|
exporters: [otlphttp/grafana,otlp/digma]
|
||||||
processors: [batch]
|
|
||||||
metrics:
|
|
||||||
receivers: [otlp]
|
|
||||||
exporters: [prometheus]
|
|
||||||
processors: [batch]
|
processors: [batch]
|
|
@ -1,36 +1,6 @@
|
||||||
version: "3.6"
|
version: "3.6"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
jaeger:
|
|
||||||
image: jaegertracing/all-in-one:latest
|
|
||||||
container_name: jaeger
|
|
||||||
volumes:
|
|
||||||
- badger_data:/badger
|
|
||||||
ports:
|
|
||||||
- "16686:16686"
|
|
||||||
- "14250"
|
|
||||||
- "0.0.0.0:14268:14268"
|
|
||||||
environment:
|
|
||||||
- SPAN_STORAGE_TYPE=badger
|
|
||||||
- BADGER_EPHEMERAL=false
|
|
||||||
- BADGER_SPAN_STORE_TTL=2000h
|
|
||||||
- BADGER_DIRECTORY_VALUE=/badger/data
|
|
||||||
- BADGER_DIRECTORY_KEY=/badger/key
|
|
||||||
|
|
||||||
grafana:
|
|
||||||
container_name: grafana
|
|
||||||
image: grafana/grafana-oss:latest
|
|
||||||
ports:
|
|
||||||
- 3000:3000
|
|
||||||
|
|
||||||
prometheus:
|
|
||||||
container_name: prometheus
|
|
||||||
image: prom/prometheus:latest
|
|
||||||
volumes:
|
|
||||||
- ./prometheus.yaml:/etc/prometheus/prometheus.yml
|
|
||||||
ports:
|
|
||||||
- "9090:9090"
|
|
||||||
extra_hosts:
|
|
||||||
- "host.docker.internal:host-gateway"
|
|
||||||
|
|
||||||
collector:
|
collector:
|
||||||
image: otel/opentelemetry-collector-contrib
|
image: otel/opentelemetry-collector-contrib
|
||||||
|
@ -42,8 +12,6 @@ services:
|
||||||
- "0.0.0.0:8889:8889" # METRICS
|
- "0.0.0.0:8889:8889" # METRICS
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "host.docker.internal:host-gateway"
|
- "host.docker.internal:host-gateway"
|
||||||
depends_on:
|
|
||||||
- jaeger
|
|
||||||
environment:
|
environment:
|
||||||
- OTLP_EXPORTER_DIGMA_COLLECTOR_API=host.docker.internal:5050
|
- OTLP_EXPORTER_DIGMA_COLLECTOR_API=host.docker.internal:5050
|
||||||
|
|
||||||
|
|
5
pom.xml
5
pom.xml
|
@ -38,6 +38,11 @@
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jobrunr</groupId>
|
||||||
|
<artifactId>jobrunr-spring-boot-3-starter</artifactId>
|
||||||
|
<version>6.3.0</version>
|
||||||
|
</dependency>
|
||||||
<!-- Spring and Spring Boot dependencies -->
|
<!-- Spring and Spring Boot dependencies -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
|
|
@ -52,7 +52,6 @@ public class OwnerValidation {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// This function and classes were generated by ChatGPT
|
// This function and classes were generated by ChatGPT
|
||||||
public boolean ValidateUserAccess(String usr, String pswd, String sysCode) {
|
public boolean ValidateUserAccess(String usr, String pswd, String sysCode) {
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.json.JSONException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.samples.petclinic.adapters.PetVaccinationService;
|
import org.springframework.samples.petclinic.adapters.PetVaccinationService;
|
||||||
import org.springframework.samples.petclinic.adapters.VaccinnationRecord;
|
import org.springframework.samples.petclinic.adapters.VaccinnationRecord;
|
||||||
|
import org.springframework.samples.petclinic.owner.OwnerRepository;
|
||||||
import org.springframework.samples.petclinic.owner.Pet;
|
import org.springframework.samples.petclinic.owner.Pet;
|
||||||
import org.springframework.samples.petclinic.owner.PetVaccine;
|
import org.springframework.samples.petclinic.owner.PetVaccine;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
@ -13,18 +14,25 @@ import org.springframework.stereotype.Component;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class PetVaccinationStatusService {
|
public class PetVaccinationStatusService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OwnerRepository ownerRepositorys;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PetVaccinationService adapter;
|
private PetVaccinationService adapter;
|
||||||
|
|
||||||
|
|
||||||
@WithSpan
|
@WithSpan
|
||||||
public void UpdateVaccinationStatus(Pet[] pets) {
|
public void updateVaccinationStatus(List<Integer> petIds) {
|
||||||
|
|
||||||
|
|
||||||
|
for (Integer petId : petIds) {
|
||||||
|
var pet = ownerRepositorys.findById(petId).getPet(petId);
|
||||||
|
|
||||||
for (Pet pet : pets) {
|
|
||||||
try {
|
try {
|
||||||
var vaccinationRecords = this.adapter.allVaccines();
|
var vaccinationRecords = this.adapter.allVaccines();
|
||||||
for (VaccinnationRecord record : vaccinationRecords) {
|
for (VaccinnationRecord record : vaccinationRecords) {
|
||||||
|
|
|
@ -15,29 +15,24 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.annotations.WithSpan;
|
import io.opentelemetry.instrumentation.annotations.WithSpan;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
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;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.samples.petclinic.domain.OwnerValidation;
|
import org.springframework.samples.petclinic.domain.OwnerValidation;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
|
import org.springframework.web.ErrorResponse;
|
||||||
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 org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
import jakarta.validation.Valid;
|
import java.net.URI;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static io.opentelemetry.api.GlobalOpenTelemetry.getTracer;
|
import static io.opentelemetry.api.GlobalOpenTelemetry.getTracer;
|
||||||
|
|
||||||
|
@ -56,7 +51,6 @@ class OwnerController {
|
||||||
|
|
||||||
private OwnerValidation validator;
|
private OwnerValidation validator;
|
||||||
|
|
||||||
|
|
||||||
public OwnerController(OwnerRepository clinicService) {
|
public OwnerController(OwnerRepository clinicService) {
|
||||||
this.owners = clinicService;
|
this.owners = clinicService;
|
||||||
var otelTracer = getTracer("OwnerController");
|
var otelTracer = getTracer("OwnerController");
|
||||||
|
@ -64,17 +58,26 @@ class OwnerController {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@InitBinder
|
@InitBinder
|
||||||
public void setAllowedFields(WebDataBinder dataBinder) {
|
public void setAllowedFields(WebDataBinder dataBinder) {
|
||||||
dataBinder.setDisallowedFields("id");
|
dataBinder.setDisallowedFields("id");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ModelAttribute("owner")
|
@ModelAttribute("owner")
|
||||||
public Owner findOwner(@PathVariable(name = "ownerId", required = false) Integer ownerId) {
|
public Owner findOwner(@PathVariable(name = "ownerId", required = false) Integer ownerId) {
|
||||||
return ownerId == null ? new Owner() : this.owners.findById(ownerId);
|
return ownerId == null ? new Owner() : this.owners.findById(ownerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ExceptionHandler(NullPointerException.class)
|
||||||
|
// @ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
|
// public ErrorResponse onIllegaArgumentException(RuntimeException npe){
|
||||||
|
// return ErrorResponse.builder(npe,HttpStatus.NOT_FOUND, npe.getMessage())
|
||||||
|
// .title("Not found")
|
||||||
|
// .type(URI.create("https://api.bookmarks.com/errors/not-found"))
|
||||||
|
// .build();
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
@GetMapping("/owners/new")
|
@GetMapping("/owners/new")
|
||||||
public String initCreationForm(Map<String, Object> model) {
|
public String initCreationForm(Map<String, Object> model) {
|
||||||
|
@ -86,6 +89,8 @@ class OwnerController {
|
||||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@PostMapping("/owners/new")
|
@PostMapping("/owners/new")
|
||||||
public String processCreationForm(@Valid Owner owner, BindingResult result) {
|
public String processCreationForm(@Valid Owner owner, BindingResult result) {
|
||||||
if (result.hasErrors()) {
|
if (result.hasErrors()) {
|
||||||
|
@ -105,6 +110,8 @@ class OwnerController {
|
||||||
return "owners/findOwners";
|
return "owners/findOwners";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@GetMapping("/owners")
|
@GetMapping("/owners")
|
||||||
public String processFindForm(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result,
|
public String processFindForm(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result,
|
||||||
Model model) {
|
Model model) {
|
||||||
|
|
|
@ -57,6 +57,10 @@ public interface OwnerRepository extends Repository<Owner, Integer> {
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
Page<Owner> findByLastName(@Param("lastName") String lastName, Pageable pageable);
|
Page<Owner> findByLastName(@Param("lastName") String lastName, Pageable pageable);
|
||||||
|
|
||||||
|
@Query("SELECT DISTINCT owner FROM Owner owner LEFT JOIN FETCH owner.pets LEFT JOIN FETCH pets.visits")
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
Page<Owner> findByLastNameWithPetsAndVisits(@Param("lastName") String lastName, Pageable pageable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve an {@link Owner} from the data store by id.
|
* Retrieve an {@link Owner} from the data store by id.
|
||||||
* @param id the id to search for
|
* @param id the id to search for
|
||||||
|
|
|
@ -15,10 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
import org.jobrunr.scheduling.BackgroundJob;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.samples.petclinic.domain.PetVaccinationStatusService;
|
import org.springframework.samples.petclinic.domain.PetVaccinationStatusService;
|
||||||
|
@ -47,6 +50,7 @@ class PetController implements InitializingBean {
|
||||||
|
|
||||||
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 OwnerRepository owners;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -54,12 +58,10 @@ class PetController implements InitializingBean {
|
||||||
|
|
||||||
private ExecutorService executorService;
|
private ExecutorService executorService;
|
||||||
|
|
||||||
|
|
||||||
public PetController(OwnerRepository owners) {
|
public PetController(OwnerRepository owners) {
|
||||||
this.owners = owners;
|
this.owners = owners;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ModelAttribute("types")
|
@ModelAttribute("types")
|
||||||
public Collection<PetType> populatePetTypes() {
|
public Collection<PetType> populatePetTypes() {
|
||||||
return this.owners.findPetTypes();
|
return this.owners.findPetTypes();
|
||||||
|
@ -106,12 +108,13 @@ class PetController implements InitializingBean {
|
||||||
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.owners.save(owner);
|
this.owners.save(owner);
|
||||||
this.executorService.submit(() -> {
|
var pets = owner.getPets().toArray(Pet[]::new);
|
||||||
petVaccinationStatus.UpdateVaccinationStatus(owner.getPets().toArray(Pet[]::new));
|
// executorService.submit(
|
||||||
});
|
// () -> petVaccinationStatus.updateVaccinationStatus(pets));
|
||||||
|
|
||||||
|
var petIds = owner.getPets().stream().map(Pet::getId).toList();
|
||||||
|
BackgroundJob.enqueue(() -> petVaccinationStatus.updateVaccinationStatus(petIds) );
|
||||||
return "redirect:/owners/{ownerId}";
|
return "redirect:/owners/{ownerId}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,4 +142,5 @@ class PetController implements InitializingBean {
|
||||||
this.executorService = Executors.newFixedThreadPool(5);
|
this.executorService = Executors.newFixedThreadPool(5);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,4 @@ class WelcomeController {
|
||||||
return "welcome";
|
return "welcome";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
database=h2
|
database=h2
|
||||||
spring.sql.init.schema-locations=classpath*:db/${database}/schema.sql
|
spring.sql.init.schema-locations=classpath*:db/${database}/schema.sql
|
||||||
spring.sql.init.data-locations=classpath*:db/${database}/data.sql
|
spring.sql.init.data-locations=classpath*:db/${database}/data.sql
|
||||||
|
org.jobrunr.job-scheduler.enabled=true
|
||||||
|
org.jobrunr.background-job-server.enabled=true
|
||||||
|
org.jobrunr.dashboard.enabled=true
|
||||||
# Web
|
# Web
|
||||||
spring.thymeleaf.mode=HTML
|
spring.thymeleaf.mode=HTML
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,6 @@ public class OwnerControllerTests {
|
||||||
|
|
||||||
Owner owner = CreateOwner();
|
Owner owner = CreateOwner();
|
||||||
|
|
||||||
|
|
||||||
String newPetName = faker.dog().name();
|
String newPetName = faker.dog().name();
|
||||||
given().contentType("multipart/form-data")
|
given().contentType("multipart/form-data")
|
||||||
.multiPart("id", "")
|
.multiPart("id", "")
|
||||||
|
@ -85,12 +84,8 @@ public class OwnerControllerTests {
|
||||||
.then()
|
.then()
|
||||||
.statusCode(Matchers.not(Matchers.greaterThan(499)));
|
.statusCode(Matchers.not(Matchers.greaterThan(499)));
|
||||||
|
|
||||||
|
|
||||||
var updatedOwner = ownerRepository.findById(owner.getId());
|
var updatedOwner = ownerRepository.findById(owner.getId());
|
||||||
assertThat(updatedOwner.getPets())
|
assertThat(updatedOwner.getPets()).hasSize(2).extracting(Pet::getName).contains(newPetName);
|
||||||
.hasSize(2)
|
|
||||||
.extracting(Pet::getName)
|
|
||||||
.contains(newPetName);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,12 +94,9 @@ public class OwnerControllerTests {
|
||||||
|
|
||||||
Owner owner = CreateOwner();
|
Owner owner = CreateOwner();
|
||||||
|
|
||||||
var ownerLinkMatcher =
|
var ownerLinkMatcher = String.format("**.findAll { node -> node.@href=='/owners/%s'}", owner.getId());
|
||||||
String.format("**.findAll { node -> node.@href=='/owners/%s'}",
|
|
||||||
owner.getId());
|
|
||||||
|
|
||||||
given()
|
given().contentType(ContentType.JSON)
|
||||||
.contentType(ContentType.JSON)
|
|
||||||
.when()
|
.when()
|
||||||
.get("/owners")
|
.get("/owners")
|
||||||
.then()
|
.then()
|
||||||
|
|
Loading…
Reference in a new issue