Skip to content

Commit

Permalink
Merge branch 'main' into Feature-Validate-Adoption
Browse files Browse the repository at this point in the history
  • Loading branch information
Codinab authored Apr 6, 2024
2 parents 569329c + bc78bb9 commit a47bc30
Show file tree
Hide file tree
Showing 19 changed files with 721 additions and 13 deletions.
6 changes: 6 additions & 0 deletions EntitiesModel.puml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ class MedicalRecord {
date : Date
}

class FavouritedPets {
id : Long
petId : Long
}


Pet "1" -- "*" MedicalRecord : has >

Expand All @@ -104,4 +109,5 @@ Client "*" -> "*" Shelter : donate >
Adoption "*" -> "1" Pet :adopted >
Adoption "*" -> "1" User :adopt <
ShelterVolunteer "1.*" -> "*" Shelter : works >
User "1" -> "*" FavouritedPets : hasFavourited >
@enduml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package cat.udl.eps.softarch.demo.controller;

import cat.udl.eps.softarch.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class UserController {

@Autowired
UserRepository userRepository;

@PreAuthorize("hasRole('ROLE_ADMIN')")
@PostMapping("/users/{username}/lock")
public ResponseEntity<Object> lockUser(@PathVariable String username) {
// If the user is present, lock it and save it. If not, return 404.
return this.userRepository.findById(username)
.map(user -> {
user.lock();
this.userRepository.save(user);
return ResponseEntity.ok().build();
})
.orElse(ResponseEntity.notFound().build());
}

@PreAuthorize("hasRole('ROLE_ADMIN')")
@PostMapping("/users/{username}/unlock")
public ResponseEntity<Object> unlockUser(@PathVariable String username) {
// If the user is present, unlock it and save it. If not, return 404.
return this.userRepository.findById(username)
.map(user -> {
user.unlock();
this.userRepository.save(user);
return ResponseEntity.ok().build();
})
.orElse(ResponseEntity.notFound().build());
}
}
18 changes: 18 additions & 0 deletions src/main/java/cat/udl/eps/softarch/demo/domain/FavouritedPets.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package cat.udl.eps.softarch.demo.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;

@EqualsAndHashCode(callSuper = true)
@Entity
@Data
public class FavouritedPets extends UriEntity<Long> {
@Id
@NotNull
Long id;

Long petId;
}
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
Expand Up @@ -3,10 +3,7 @@
import java.time.ZonedDateTime;
import com.fasterxml.jackson.annotation.JsonIdentityReference;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
Expand All @@ -24,6 +21,9 @@ public class ShelterCertificate extends UriEntity<Long> {
@NotNull
private ZonedDateTime expirationDate;

@NotNull
private Boolean validated;

@JsonIdentityReference(alwaysAsId = true)
@ManyToOne
private Shelter shelterServed;
Expand Down
21 changes: 15 additions & 6 deletions src/main/java/cat/udl/eps/softarch/demo/domain/User.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package cat.udl.eps.softarch.demo.domain;

import com.fasterxml.jackson.annotation.JsonProperty;

import com.fasterxml.jackson.annotation.JsonValue;
import jakarta.persistence.*;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;

import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.validator.constraints.Length;
Expand Down Expand Up @@ -43,10 +41,21 @@ public class User extends UriEntity<String> implements UserDetails {
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private boolean passwordReset;

@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private boolean locked;

public void encodePassword() {
this.password = passwordEncoder.encode(this.password);
}

public void lock() {
this.locked = true;
}

public void unlock() {
this.locked = false;
}

@Override
public String getUsername() { return id; }

Expand All @@ -64,7 +73,7 @@ public boolean isAccountNonExpired() {

@Override
public boolean isAccountNonLocked() {
return true;
return !locked;
}

@Override
Expand All @@ -77,7 +86,7 @@ public boolean isEnabled() {
return true;
}

@ManyToMany
public List<Pet> favouritedPets;
@OneToMany(fetch = FetchType.EAGER)
public List<FavouritedPets> favouritedPets;

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(code = HttpStatus.FORBIDDEN, reason = "Unauthorized access")
public class UnauthorizedAccessException extends RuntimeException {}
public class UnauthorizedAccessException extends RuntimeException {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
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.HandleBeforeSave;
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() // Ensure this handler is for MedicalRecord entity
public class MedicalRecordEventHandler {

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

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 {
checkAuthorization();
logger.info("Authorized creation of a new medical record by user: {}", getCurrentUsername());
}

/**
* Handles actions before saving (updating) a medical record.
*/
@HandleBeforeSave
public void handleMedicalRecordBeforeSave(MedicalRecord medicalRecord) throws UnauthorizedAccessException {
checkAuthorization();
logger.info("Authorized save of medical record by user: {}", getCurrentUsername());
}

/**
* 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());
}

private void checkAuthorization() throws UnauthorizedAccessException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (!isAuthorized(authentication)) {
throw new UnauthorizedAccessException();
}
}

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

private String getCurrentUsername() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication != null ? authentication.getName() : "anonymous";
}
}
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
Expand Up @@ -3,7 +3,9 @@
import cat.udl.eps.softarch.demo.domain.ShelterCertificate;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import cat.udl.eps.softarch.demo.domain.Shelter;
import org.springframework.data.repository.query.Param;

public interface ShelterCertificateRepository extends CrudRepository<ShelterCertificate, Long>, PagingAndSortingRepository<ShelterCertificate, Long> {

ShelterCertificate findByShelterServed(@Param("shelterServed") Shelter shelter);
}
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());
}
}
Loading

0 comments on commit a47bc30

Please sign in to comment.