From e360294037c781015d23f234befcd92d88076737 Mon Sep 17 00:00:00 2001 From: Linus Westling Date: Mon, 20 Apr 2026 09:03:36 +0200 Subject: [PATCH 1/5] Issue 60 done --- .../application/service/CaseService.java | 24 +++++++++-- .../presentation/dto/CreateCaseForm.java | 2 - .../presentation/web/UiController.java | 21 +++++++--- src/main/resources/templates/cases/new.html | 5 ++- .../application/service/CaseServiceTest.java | 40 +++++++++++++++++++ 5 files changed, 81 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/example/projektarendehantering/application/service/CaseService.java b/src/main/java/org/example/projektarendehantering/application/service/CaseService.java index 58265ff..46000d0 100644 --- a/src/main/java/org/example/projektarendehantering/application/service/CaseService.java +++ b/src/main/java/org/example/projektarendehantering/application/service/CaseService.java @@ -71,12 +71,19 @@ public CaseDTO createCase(Actor actor, CaseDTO caseDTO) { if (!canCreate(actor)) { throw new NotAuthorizedException("Not allowed to create cases"); } - if (caseDTO.getPatientId() == null) { + UUID requestedPatientId = caseDTO.getPatientId(); + if (isPatient(actor)) { + if (requestedPatientId != null && !actor.userId().equals(requestedPatientId)) { + throw new NotAuthorizedException("Patients can only create cases for themselves"); + } + requestedPatientId = actor.userId(); + } + if (requestedPatientId == null) { throw new BadRequestException("patientId is required"); } CaseEntity entity = caseMapper.toEntity(caseDTO); entity.setId(UUID.randomUUID()); - PatientEntity patient = patientRepository.findById(caseDTO.getPatientId()) + PatientEntity patient = patientRepository.findById(requestedPatientId) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Patient not found")); entity.setPatient(patient); if (isDoctor(actor) || isManager(actor)) { @@ -182,6 +189,11 @@ public List getAllCases(Actor actor) { .map(caseMapper::toDTO) .collect(Collectors.toList()); } + if (isPatient(actor)) { + return caseRepository.findAllByPatient_IdAndStatusNot(actor.userId(), CaseStatus.CLOSED).stream() + .map(caseMapper::toDTO) + .collect(Collectors.toList()); + } throw new NotAuthorizedException("Not allowed to list cases"); } @@ -254,6 +266,7 @@ private void requireCanRead(Actor actor, CaseEntity entity) { if (isManager(actor)) return; if (isDoctor(actor) && actor.userId().equals(entity.getOwnerId())) return; if (isNurse(actor) && actor.userId().equals(entity.getHandlerId())) return; + if (isPatient(actor) && entity.getPatient() != null && actor.userId().equals(entity.getPatient().getId())) return; throw new NotAuthorizedException("Not allowed to read this case"); } @@ -276,13 +289,14 @@ private void requireCanDelete(Actor actor, CaseEntity entity) { } private boolean canCreate(Actor actor) { - return isManager(actor) || isDoctor(actor); + return isManager(actor) || isDoctor(actor) || isPatient(actor); } private boolean canRead(Actor actor, CaseEntity entity) { if (isManager(actor)) return true; if (isDoctor(actor) && actor.userId().equals(entity.getOwnerId())) return true; if (isNurse(actor) && actor.userId().equals(entity.getHandlerId())) return true; + if (isPatient(actor) && entity.getPatient() != null && actor.userId().equals(entity.getPatient().getId())) return true; return false; } @@ -298,6 +312,10 @@ private boolean isNurse(Actor actor) { return actor.role() == Role.NURSE; } + private boolean isPatient(Actor actor) { + return actor.role() == Role.PATIENT; + } + private void recordStatusChange(Actor actor, UUID caseId, CaseStatus from, CaseStatus to) { String statusChange = (from != null ? from.name() : "NEW") + " -> " + to.name(); AuditEventEntity event = AuditEventEntity.builder() diff --git a/src/main/java/org/example/projektarendehantering/presentation/dto/CreateCaseForm.java b/src/main/java/org/example/projektarendehantering/presentation/dto/CreateCaseForm.java index 159681f..9f40374 100644 --- a/src/main/java/org/example/projektarendehantering/presentation/dto/CreateCaseForm.java +++ b/src/main/java/org/example/projektarendehantering/presentation/dto/CreateCaseForm.java @@ -1,7 +1,6 @@ package org.example.projektarendehantering.presentation.dto; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.AllArgsConstructor; import lombok.Builder; @@ -24,7 +23,6 @@ public class CreateCaseForm { @Size(max = 4000, message = "Description must be under 4000 characters") private String description; - @NotNull(message = "Patient is required") private UUID patientId; } diff --git a/src/main/java/org/example/projektarendehantering/presentation/web/UiController.java b/src/main/java/org/example/projektarendehantering/presentation/web/UiController.java index 3611853..7b1e1f4 100644 --- a/src/main/java/org/example/projektarendehantering/presentation/web/UiController.java +++ b/src/main/java/org/example/projektarendehantering/presentation/web/UiController.java @@ -8,6 +8,8 @@ import org.example.projektarendehantering.presentation.dto.CaseDTO; import org.example.projektarendehantering.presentation.dto.CreateCaseForm; import org.example.projektarendehantering.presentation.dto.UpdateCaseForm; +import org.example.projektarendehantering.common.Actor; +import org.example.projektarendehantering.common.Role; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -21,7 +23,6 @@ import jakarta.validation.Valid; import org.springframework.web.server.ResponseStatusException; -import java.security.Principal; import java.util.UUID; @Controller @@ -64,23 +65,33 @@ public String listClosedCases(Model model) { @GetMapping("/ui/cases/new") public String newCase(Model model) { + Actor actor = securityActorAdapter.currentUser(); model.addAttribute("createCaseForm", new CreateCaseForm()); - model.addAttribute("patients", patientService.getAllPatients()); + if (actor.role() != Role.PATIENT) { + model.addAttribute("patients", patientService.getAllPatients()); + } return "cases/new"; } @PostMapping("/ui/cases/new") - public String createCase(@Valid @ModelAttribute("createCaseForm") CreateCaseForm form, BindingResult result) { + public String createCase(@Valid @ModelAttribute("createCaseForm") CreateCaseForm form, BindingResult result, Model model) { + Actor actor = securityActorAdapter.currentUser(); + if (actor.role() != Role.PATIENT && form.getPatientId() == null) { + result.rejectValue("patientId", "required", "Patient is required"); + } if (result.hasErrors()) { + if (actor.role() != Role.PATIENT) { + model.addAttribute("patients", patientService.getAllPatients()); + } return "cases/new"; } CaseDTO caseDTO = new CaseDTO(); caseDTO.setTitle(form.getTitle()); caseDTO.setDescription(form.getDescription()); - caseDTO.setPatientId(form.getPatientId()); + caseDTO.setPatientId(actor.role() == Role.PATIENT ? actor.userId() : form.getPatientId()); - caseService.createCase(securityActorAdapter.currentUser(), caseDTO); + caseService.createCase(actor, caseDTO); return "redirect:/ui/cases"; } diff --git a/src/main/resources/templates/cases/new.html b/src/main/resources/templates/cases/new.html index 37a04ac..572680b 100644 --- a/src/main/resources/templates/cases/new.html +++ b/src/main/resources/templates/cases/new.html @@ -12,7 +12,7 @@

Create case

-