This commit is contained in:
Roni Dover 2023-09-19 23:09:31 -07:00
parent 9244518ff9
commit 0e5c72d5ec
12 changed files with 94 additions and 103 deletions

View file

@ -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

View file

@ -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]

View file

@ -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

View file

@ -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>

View file

@ -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) {

View file

@ -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) {

View file

@ -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) {

View file

@ -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

View file

@ -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);
} }
} }

View file

@ -28,6 +28,4 @@ class WelcomeController {
return "welcome"; return "welcome";
} }
} }

View file

@ -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

View file

@ -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,18 +94,15 @@ 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()
.contentType(ContentType.HTML) .contentType(ContentType.HTML)
.statusCode(200) .statusCode(200)
.body(ownerLinkMatcher,Matchers.notNullValue()); .body(ownerLinkMatcher, Matchers.notNullValue());
} }