Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature add medical record #88

Merged
merged 11 commits into from
Apr 6, 2024
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package cat.udl.eps.softarch.demo.domain;

import com.fasterxml.jackson.annotation.JsonIdentityReference;
import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
Expand All @@ -14,7 +16,7 @@
public class MedicalRecord extends UriEntity<Long> {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@GeneratedValue()
private Long id;

@NotBlank
Expand All @@ -27,5 +29,7 @@ public class MedicalRecord extends UriEntity<Long> {
private ZonedDateTime date;

@ManyToOne
@JsonIdentityReference(alwaysAsId = true) // Only serialize the pet ID
@JsonInclude(JsonInclude.Include.NON_NULL) // Include pet only if it's not null
private Pet pet;
}
1 change: 1 addition & 0 deletions src/main/java/cat/udl/eps/softarch/demo/domain/Pet.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class Pet extends UriEntity<Long> {
@GeneratedValue
Long id;


String name;
boolean isAdopted;
String color;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package cat.udl.eps.softarch.demo.exceptions;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(code = HttpStatus.FORBIDDEN, reason = "Unauthorized access")
public class UnauthorizedAccessException extends RuntimeException {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package cat.udl.eps.softarch.demo.handler;

import cat.udl.eps.softarch.demo.domain.MedicalRecord;
import cat.udl.eps.softarch.demo.exceptions.UnauthorizedAccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.rest.core.annotation.HandleAfterSave;
import org.springframework.data.rest.core.annotation.HandleBeforeCreate;
import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

@Component
@RepositoryEventHandler
public class MedicalRecordEventHandler {

private static final Logger logger = LoggerFactory.getLogger(MedicalRecordEventHandler.class);

// Authorities
private static final String ROLE_SHELTER_VOLUNTEER = "ROLE_SHELTER_VOLUNTEER";
private static final String ROLE_ADMIN = "ROLE_ADMIN";

/**
* Handles actions before creating a medical record.
* @param medicalRecord the medical record to be created
* @throws UnauthorizedAccessException if the user is not authorized to create a medical record
*/
@HandleBeforeCreate
public void handleMedicalRecordBeforeCreate(MedicalRecord medicalRecord) throws UnauthorizedAccessException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

if (!isAuthorized(authentication)) {
String userName = authentication != null ? authentication.getName() : "anonymous";
String errorMessage = String.format("Unauthorized attempt to create a medical record by user: %s", userName);
logger.error(errorMessage);
throw new UnauthorizedAccessException();
}

logger.info("Authorized creation of a new medical record by user: {}", authentication.getName());
}

/**
* Handles actions after saving a medical record.
* @param medicalRecord the saved medical record
*/
@HandleAfterSave
public void handleMedicalRecordPostSave(MedicalRecord medicalRecord) {
logger.info("Medical record for pet {} saved successfully", medicalRecord.getPet().getName());
}

/**
* Checks if the authenticated user is authorized to perform the action.
* @param authentication the authentication context
* @return true if the user is authorized, false otherwise
*/
private boolean isAuthorized(Authentication authentication) {
if (authentication == null || !authentication.isAuthenticated()) {
return false;
}

List<String> requiredAuthorities = Arrays.asList(ROLE_SHELTER_VOLUNTEER, ROLE_ADMIN);

return authentication.getAuthorities().stream()
.anyMatch(grantedAuthority -> requiredAuthorities.contains(grantedAuthority.getAuthority()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package cat.udl.eps.softarch.demo.repository;

import cat.udl.eps.softarch.demo.domain.MedicalRecord;
import cat.udl.eps.softarch.demo.domain.Pet;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

import java.util.List;

@RepositoryRestResource
public interface MedicalRecordRepository extends CrudRepository < MedicalRecord, Long > , PagingAndSortingRepository < MedicalRecord, Long > {

List < MedicalRecord > findByPet(@Param("pet") Pet pet);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package cat.udl.eps.softarch.demo.steps;

import cat.udl.eps.softarch.demo.domain.MedicalRecord;
import cat.udl.eps.softarch.demo.domain.Pet;
import cat.udl.eps.softarch.demo.repository.PetRepository;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;

import java.nio.charset.StandardCharsets;
import java.time.ZonedDateTime;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;


public class AddMedicalRecordStepDefs {

@Autowired
private StepDefs stepDefs;

@Autowired
PetRepository petRepository;


@Given("a pet exists in the system")
public void aPetExistsInTheSystem() {
var pet = new Pet();

petRepository.save(pet); // Save the pet to the database
}

@When("I add a new medical record for a pet with issue {string}, description {string}, and date {string}")
public void iAddANewMedicalRecordForAPetWithIssueDescriptionAndDate(String issue, String description, String date) throws Throwable {
MedicalRecord newRecord = new MedicalRecord();
newRecord.setDescription(description);
newRecord.setIssue(issue);
newRecord.setDate(ZonedDateTime.parse(date));
newRecord.setPet(petRepository.findAll().iterator().next());

// Mock a POST request to /medicalRecords
stepDefs.result = stepDefs.mockMvc.perform(
post("/medicalRecords")
.contentType(MediaType.APPLICATION_JSON)
.content(stepDefs.mapper.writeValueAsString(newRecord))
.characterEncoding(StandardCharsets.UTF_8)
.with(AuthenticationStepDefs.authenticate()))
.andDo(print());

}



@When("I add a new medical record for a pet with issue {string}, description {string} and no date")
public void iAddANewMedicalRecordForAPetWithIssueDescriptionAndNoDate(String issue, String description) throws Throwable {
MedicalRecord recordWithoutDate = new MedicalRecord();
recordWithoutDate.setDescription(description);
recordWithoutDate.setIssue(issue);
//No data added
recordWithoutDate.setPet(petRepository.findAll().iterator().next());

stepDefs.result = stepDefs.mockMvc.perform(
post("/medicalRecords")
.contentType(MediaType.APPLICATION_JSON)
.content(stepDefs.mapper.writeValueAsString(recordWithoutDate))
.characterEncoding(StandardCharsets.UTF_8)
.with(AuthenticationStepDefs.authenticate()))
.andDo(print());
}

@When("I try to add a medical record for a pet")
public void iTryToAddAMedicalRecordForAPet() throws Exception {
MedicalRecord medicalRecord = new MedicalRecord();
medicalRecord.setDescription("Description");
medicalRecord.setIssue("Issue");
medicalRecord.setDate(ZonedDateTime.now());
medicalRecord.setPet(petRepository.findAll().iterator().next());

stepDefs.result = stepDefs.mockMvc.perform(
post("/medicalRecords")
.contentType(MediaType.APPLICATION_JSON)
.content(stepDefs.mapper.writeValueAsString(medicalRecord))
.characterEncoding(StandardCharsets.UTF_8)
.with(AuthenticationStepDefs.authenticate())
)
.andDo(print());
}
}
42 changes: 42 additions & 0 deletions src/test/resources/features/AddMedicalRecord.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
Feature: Add Medical Record
In order to maintain an accurate health history for pets
As a shelter volunteer
I want to add medical records to pets' profiles

Background:
Given a pet exists in the system
Given There is a registered user with username "user" and password "password" and email "[email protected]"
Given There is a registered admin with name "admin" and password "password" and email "[email protected]"
Given There is a registered volunteer with name "volunteer" and password "password" and email "[email protected]"


Scenario: Add a valid medical record to an existing pet as Volunteer
Given I login as "volunteer" with password "password"
When I add a new medical record for a pet with issue "Allergy", description "Seasonal allergy", and date "2024-03-07T14:00:00Z"
Then The response code is 201

Scenario: Add medical record with empty issue as Volunteer
Given I login as "volunteer" with password "password"
When I add a new medical record for a pet with issue "", description "Missing vaccine", and date "2024-03-07T14:00:00Z"
Then The response code is 400

Scenario: Add medical record with empty description as Volunteer
Given I login as "volunteer" with password "password"
When I add a new medical record for a pet with issue "Vaccination", description "", and date "2024-03-07T14:00:00Z"
Then The response code is 400

Scenario: Add medical record without date as Volunteer
Given I login as "volunteer" with password "password"
When I add a new medical record for a pet with issue "Injury", description "Minor cut on paw" and no date
Then The response code is 400

Scenario: Attempt to add a medical record as a normal user
Given I login as "user" with password "password"
When I try to add a medical record for a pet
Then The response code is 403
And The error message is "Unauthorized access"

Scenario: Attempt to add a medical record as an admin
Given I login as "admin" with password "password"
When I try to add a medical record for a pet
Then The response code is 201
Loading