From c624d498ae145428a1a3d8b6061e17526bf19ad4 Mon Sep 17 00:00:00 2001 From: Annika Holmqvist Date: Fri, 27 Mar 2026 15:31:22 +0100 Subject: [PATCH 1/4] Add DTOs for requests and responses in the MedicalRecord module closes #9 --- .../medicalrecord/AssignVetRequest.java | 8 +++ .../CreateMedicalRecordRequest.java | 11 ++++ .../UpdateMedicalRecordRequest.java | 7 +++ .../medicalrecord/UpdateStatusRequest.java | 8 +++ .../medicalrecord/MedicalRecordResponse.java | 52 +++++++++++++++++++ .../MedicalRecordSummaryResponse.java | 30 +++++++++++ 6 files changed, 116 insertions(+) create mode 100644 src/main/java/org/example/vet1177/dto/request/medicalrecord/AssignVetRequest.java create mode 100644 src/main/java/org/example/vet1177/dto/request/medicalrecord/CreateMedicalRecordRequest.java create mode 100644 src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateMedicalRecordRequest.java create mode 100644 src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateStatusRequest.java create mode 100644 src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordResponse.java create mode 100644 src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordSummaryResponse.java diff --git a/src/main/java/org/example/vet1177/dto/request/medicalrecord/AssignVetRequest.java b/src/main/java/org/example/vet1177/dto/request/medicalrecord/AssignVetRequest.java new file mode 100644 index 0000000..02f65d1 --- /dev/null +++ b/src/main/java/org/example/vet1177/dto/request/medicalrecord/AssignVetRequest.java @@ -0,0 +1,8 @@ +package org.example.vet1177.dto.request.medicalrecord; + +import java.util.UUID; + +// Tilldela handläggare +public record AssignVetRequest( + UUID vetId +) {} \ No newline at end of file diff --git a/src/main/java/org/example/vet1177/dto/request/medicalrecord/CreateMedicalRecordRequest.java b/src/main/java/org/example/vet1177/dto/request/medicalrecord/CreateMedicalRecordRequest.java new file mode 100644 index 0000000..90f6926 --- /dev/null +++ b/src/main/java/org/example/vet1177/dto/request/medicalrecord/CreateMedicalRecordRequest.java @@ -0,0 +1,11 @@ +package org.example.vet1177.dto.request.medicalrecord; + +import java.util.UUID; + +// Skapa nytt ärende +public record CreateMedicalRecordRequest( + String title, + String description, + UUID petId, + UUID clinicId +) {} \ No newline at end of file diff --git a/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateMedicalRecordRequest.java b/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateMedicalRecordRequest.java new file mode 100644 index 0000000..82d506f --- /dev/null +++ b/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateMedicalRecordRequest.java @@ -0,0 +1,7 @@ +package org.example.vet1177.dto.request.medicalrecord; + +// Uppdatera titel och beskrivning +public record UpdateMedicalRecordRequest( + String title, + String description +) {} \ No newline at end of file diff --git a/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateStatusRequest.java b/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateStatusRequest.java new file mode 100644 index 0000000..c8d8c0c --- /dev/null +++ b/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateStatusRequest.java @@ -0,0 +1,8 @@ +package org.example.vet1177.dto.request.medicalrecord; + +import org.example.vet1177.entities.RecordStatus; + +// Uppdatera status +public record UpdateStatusRequest( + RecordStatus status +) {} \ No newline at end of file diff --git a/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordResponse.java b/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordResponse.java new file mode 100644 index 0000000..a75febb --- /dev/null +++ b/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordResponse.java @@ -0,0 +1,52 @@ +package org.example.vet1177.dto.response.medicalrecord; + +import org.example.vet1177.entities.MedicalRecord; +import org.example.vet1177.entities.RecordStatus; + +import java.time.Instant; +import java.util.UUID; + +//fullständigt svar för getByID: +public record MedicalRecordResponse( + UUID id, + String title, + String description, + RecordStatus status, + UUID petId, + String petName, + String petSpecies, + UUID ownerId, + String ownerName, + UUID clinicId, + String clinicName, + UUID assignedVetId, + String assignedVetName, + UUID createdById, + String createdByName, + Instant createdAt, + Instant updatedAt, + Instant closedAt +) { + public static MedicalRecordResponse from(MedicalRecord record) { + return new MedicalRecordResponse( + record.getId(), + record.getTitle(), + record.getDescription(), + record.getStatus(), + record.getPet().getId(), + record.getPet().getName(), + record.getPet().getSpecies(), + record.getOwner().getId(), + record.getOwner().getName(), + record.getClinic().getId(), + record.getClinic().getName(), + record.getAssignedVet() != null ? record.getAssignedVet().getId() : null, + record.getAssignedVet() != null ? record.getAssignedVet().getName() : null, + record.getCreatedBy().getId(), + record.getCreatedBy().getName(), + record.getCreatedAt(), + record.getUpdatedAt(), + record.getClosedAt() + ); + } +} \ No newline at end of file diff --git a/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordSummaryResponse.java b/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordSummaryResponse.java new file mode 100644 index 0000000..89daff4 --- /dev/null +++ b/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordSummaryResponse.java @@ -0,0 +1,30 @@ +package org.example.vet1177.dto.response.medicalrecord; + +import org.example.vet1177.entities.MedicalRecord; +import org.example.vet1177.entities.RecordStatus; + +import java.time.Instant; +import java.util.UUID; + +// Förenklat svar som används i listor +public record MedicalRecordSummaryResponse( + UUID id, + String title, + RecordStatus status, + String petName, + String ownerName, + String assignedVetName, + Instant createdAt +) { + public static MedicalRecordSummaryResponse from(MedicalRecord record) { + return new MedicalRecordSummaryResponse( + record.getId(), + record.getTitle(), + record.getStatus(), + record.getPet().getName(), + record.getOwner().getName(), + record.getAssignedVet() != null ? record.getAssignedVet().getName() : null, + record.getCreatedAt() + ); + } +} From 5ea38d1858fb1965f7a14e372032d452ae35c043 Mon Sep 17 00:00:00 2001 From: Annika Holmqvist Date: Fri, 27 Mar 2026 17:04:33 +0100 Subject: [PATCH 2/4] Add validation annotations to DTOs in MedicalRecord module and update dependencies Closes #9 --- pom.xml | 4 ++++ .../dto/request/medicalrecord/AssignVetRequest.java | 5 +++++ .../medicalrecord/CreateMedicalRecordRequest.java | 13 +++++++++++++ .../medicalrecord/UpdateMedicalRecordRequest.java | 7 +++++++ .../request/medicalrecord/UpdateStatusRequest.java | 3 +++ 5 files changed, 32 insertions(+) diff --git a/pom.xml b/pom.xml index 5d37f08..85d43dc 100644 --- a/pom.xml +++ b/pom.xml @@ -73,6 +73,10 @@ spring-boot-docker-compose runtime + + org.springframework.boot + spring-boot-starter-validation + diff --git a/src/main/java/org/example/vet1177/dto/request/medicalrecord/AssignVetRequest.java b/src/main/java/org/example/vet1177/dto/request/medicalrecord/AssignVetRequest.java index 02f65d1..3992be2 100644 --- a/src/main/java/org/example/vet1177/dto/request/medicalrecord/AssignVetRequest.java +++ b/src/main/java/org/example/vet1177/dto/request/medicalrecord/AssignVetRequest.java @@ -1,8 +1,13 @@ package org.example.vet1177.dto.request.medicalrecord; + + +import jakarta.validation.constraints.NotNull; + import java.util.UUID; // Tilldela handläggare public record AssignVetRequest( + @NotNull(message = "Veterinär måste anges") UUID vetId ) {} \ No newline at end of file diff --git a/src/main/java/org/example/vet1177/dto/request/medicalrecord/CreateMedicalRecordRequest.java b/src/main/java/org/example/vet1177/dto/request/medicalrecord/CreateMedicalRecordRequest.java index 90f6926..6db18b9 100644 --- a/src/main/java/org/example/vet1177/dto/request/medicalrecord/CreateMedicalRecordRequest.java +++ b/src/main/java/org/example/vet1177/dto/request/medicalrecord/CreateMedicalRecordRequest.java @@ -1,11 +1,24 @@ package org.example.vet1177.dto.request.medicalrecord; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + import java.util.UUID; // Skapa nytt ärende public record CreateMedicalRecordRequest( + @NotBlank(message = "Titel får inte vara tom") + @Size(max = 500, message = "Titel får max vara 500 tecken") String title, + + @Size(max = 5000, message = "Beskrivning får max vara 5000 tecken") String description, + + @NotNull(message = "Djur måste anges") UUID petId, + + @NotNull(message = "Klinik måste anges") UUID clinicId + ) {} \ No newline at end of file diff --git a/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateMedicalRecordRequest.java b/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateMedicalRecordRequest.java index 82d506f..7437acb 100644 --- a/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateMedicalRecordRequest.java +++ b/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateMedicalRecordRequest.java @@ -1,7 +1,14 @@ package org.example.vet1177.dto.request.medicalrecord; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + // Uppdatera titel och beskrivning public record UpdateMedicalRecordRequest( + @NotBlank(message = "Titel får inte vara tom") + @Size(max = 500, message = "Titel får max vara 500 tecken") String title, + + @Size(max = 5000, message = "Beskrivning får max vara 5000 tecken") String description ) {} \ No newline at end of file diff --git a/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateStatusRequest.java b/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateStatusRequest.java index c8d8c0c..d3a53ad 100644 --- a/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateStatusRequest.java +++ b/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateStatusRequest.java @@ -1,8 +1,11 @@ package org.example.vet1177.dto.request.medicalrecord; + +import jakarta.validation.constraints.NotNull; import org.example.vet1177.entities.RecordStatus; // Uppdatera status public record UpdateStatusRequest( + @NotNull(message = "Status måste anges") RecordStatus status ) {} \ No newline at end of file From 6161c499709645bebde42a2f2f4ab6705da5291b Mon Sep 17 00:00:00 2001 From: Annika Holmqvist Date: Fri, 27 Mar 2026 17:57:13 +0100 Subject: [PATCH 3/4] Add EntityGraph annotations to optimize MedicalRecord queries - Applied `@EntityGraph` to relevant repository methods for fetching associated entities. - Added `findById` method with `@EntityGraph`. --- .../org/example/vet1177/entities/Comment.java | 2 +- .../repository/MedicalRecordRepository.java | 24 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/example/vet1177/entities/Comment.java b/src/main/java/org/example/vet1177/entities/Comment.java index 8fc8008..7ed2059 100644 --- a/src/main/java/org/example/vet1177/entities/Comment.java +++ b/src/main/java/org/example/vet1177/entities/Comment.java @@ -55,5 +55,5 @@ public Comment() {} public Instant getCreatedAt() { return createdAt; } public Instant getUpdatedAt() { return updatedAt; } - public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; } + } \ No newline at end of file diff --git a/src/main/java/org/example/vet1177/repository/MedicalRecordRepository.java b/src/main/java/org/example/vet1177/repository/MedicalRecordRepository.java index 621224f..f2c47fa 100644 --- a/src/main/java/org/example/vet1177/repository/MedicalRecordRepository.java +++ b/src/main/java/org/example/vet1177/repository/MedicalRecordRepository.java @@ -2,30 +2,34 @@ import org.example.vet1177.entities.MedicalRecord; import org.example.vet1177.entities.RecordStatus; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; import java.util.UUID; @Repository public interface MedicalRecordRepository extends JpaRepository { - // Hämta alla ärenden för ett specifikt djur + @EntityGraph(attributePaths = { + "pet", "owner", "clinic", "assignedVet", "createdBy" + }) + Optional findById(UUID id); + + @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet"}) List findByPetId(UUID petId); - // Hämta alla ärenden för en ägare + @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet"}) List findByOwnerId(UUID ownerId); - // Hämta alla ärenden tilldelade en specifik vet - List findByAssignedVetId(UUID vetId); - - // Hämta alla ärenden för en klinik + @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet"}) List findByClinicId(UUID clinicId); - // Hämta ärenden filtrerat på status - List findByStatus(RecordStatus status); - - // Kombinerat — ärenden för en klinik med specifik status (användbart för ABAC) + @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet"}) List findByClinicIdAndStatus(UUID clinicId, RecordStatus status); + + List findByAssignedVetId(UUID vetId); + List findByStatus(RecordStatus status); } \ No newline at end of file From bfa792e37febc5a0d738dde83bb5dcceea158151 Mon Sep 17 00:00:00 2001 From: Annika Holmqvist Date: Sun, 29 Mar 2026 12:44:18 +0200 Subject: [PATCH 4/4] Enhance MedicalRecordService and optimize repository queries - Extended `@EntityGraph` with `createdBy` for detailed entity fetching. - Replaced runtime exceptions with specific exceptions (`BusinessRuleException`, `ResourceNotFoundException`). - Prevent updates to finalized records with additional business rule validation. - Improved error handling for record closing logic. --- .../vet1177/repository/MedicalRecordRepository.java | 8 ++++---- .../vet1177/services/MedicalRecordService.java | 13 ++++++++++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/example/vet1177/repository/MedicalRecordRepository.java b/src/main/java/org/example/vet1177/repository/MedicalRecordRepository.java index f2c47fa..88507aa 100644 --- a/src/main/java/org/example/vet1177/repository/MedicalRecordRepository.java +++ b/src/main/java/org/example/vet1177/repository/MedicalRecordRepository.java @@ -18,16 +18,16 @@ public interface MedicalRecordRepository extends JpaRepository findById(UUID id); - @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet"}) + @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet", "createdBy"}) List findByPetId(UUID petId); - @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet"}) + @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet", "createdBy"}) List findByOwnerId(UUID ownerId); - @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet"}) + @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet", "createdBy"}) List findByClinicId(UUID clinicId); - @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet"}) + @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet", "createdBy"}) List findByClinicIdAndStatus(UUID clinicId, RecordStatus status); List findByAssignedVetId(UUID vetId); diff --git a/src/main/java/org/example/vet1177/services/MedicalRecordService.java b/src/main/java/org/example/vet1177/services/MedicalRecordService.java index 2924400..65b0aea 100644 --- a/src/main/java/org/example/vet1177/services/MedicalRecordService.java +++ b/src/main/java/org/example/vet1177/services/MedicalRecordService.java @@ -1,6 +1,8 @@ package org.example.vet1177.services; import org.example.vet1177.entities.*; +import org.example.vet1177.exception.BusinessRuleException; +import org.example.vet1177.exception.ResourceNotFoundException; import org.example.vet1177.repository.MedicalRecordRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -46,7 +48,7 @@ public MedicalRecord create( @Transactional(readOnly = true) public MedicalRecord getById(UUID id) { return medicalRecordRepository.findById(id) - .orElseThrow(() -> new RuntimeException("Ärende hittades inte: " + id)); + .orElseThrow(() -> new ResourceNotFoundException("MedicalRecord", id)); // ← rad 49 } @Transactional(readOnly = true) @@ -73,6 +75,11 @@ public List getByClinicAndStatus(UUID clinicId, RecordStatus stat public MedicalRecord update(UUID id, String title, String description, User updatedBy) { MedicalRecord record = getById(id); + + if (record.getStatus().isFinal()) { // ← mellan rad 75-76 + throw new BusinessRuleException("Stängda ärenden kan inte uppdateras"); + } + record.setTitle(title); record.setDescription(description); record.setUpdatedBy(updatedBy); @@ -104,8 +111,8 @@ public MedicalRecord updateStatus(UUID recordId, RecordStatus newStatus, User up public MedicalRecord close(UUID recordId, User closedBy) { MedicalRecord record = getById(recordId); - if (record.getStatus() == RecordStatus.CLOSED) { - throw new RuntimeException("Ärendet är redan stängt"); + if (record.getStatus().isFinal()) { // ← rad 108 + throw new BusinessRuleException("Ärendet är redan stängt"); } record.setStatus(RecordStatus.CLOSED);