diff options
Diffstat (limited to 'api/src/main/java/med')
27 files changed, 488 insertions, 2 deletions
diff --git a/api/src/main/java/med/voll/api/controller/AppointmentController.java b/api/src/main/java/med/voll/api/controller/AppointmentController.java new file mode 100644 index 0000000..e5a2ba4 --- /dev/null +++ b/api/src/main/java/med/voll/api/controller/AppointmentController.java @@ -0,0 +1,34 @@ +package med.voll.api.controller; + +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import jakarta.validation.Valid; +import med.voll.api.domain.appointment.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.util.UriComponentsBuilder; + +@RestController +@RequestMapping("/appointments") +@SecurityRequirement(name = "bearer-key") +public class AppointmentController { + @Autowired + private AppointmentsSchedule appointmentsSchedule; + + @PostMapping + @Transactional + public ResponseEntity register(@RequestBody @Valid AppointmentRegistrationData data, UriComponentsBuilder uriBuilder) { + var dto = appointmentsSchedule.schedule(data); + return ResponseEntity.ok(dto); + } + + @DeleteMapping + @Transactional + public ResponseEntity cancel(@RequestBody @Valid AppointmentDeletionData data) { + appointmentsSchedule.cancel(data); + return ResponseEntity.noContent().build(); + } + + +} diff --git a/api/src/main/java/med/voll/api/controller/AuthenticationController.java b/api/src/main/java/med/voll/api/controller/AuthenticationController.java index b7dcf27..f86fc02 100644 --- a/api/src/main/java/med/voll/api/controller/AuthenticationController.java +++ b/api/src/main/java/med/voll/api/controller/AuthenticationController.java @@ -14,8 +14,9 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; -@Controller +@RestController @RequestMapping("/login") public class AuthenticationController { diff --git a/api/src/main/java/med/voll/api/controller/DoctorController.java b/api/src/main/java/med/voll/api/controller/DoctorController.java index a262d34..f87da56 100644 --- a/api/src/main/java/med/voll/api/controller/DoctorController.java +++ b/api/src/main/java/med/voll/api/controller/DoctorController.java @@ -1,5 +1,6 @@ package med.voll.api.controller; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; import jakarta.validation.Valid; import med.voll.api.domain.doctor.DoctorListingData; import med.voll.api.domain.doctor.Doctor; @@ -17,6 +18,7 @@ import org.springframework.web.util.UriComponentsBuilder; @RestController @RequestMapping("/doctors") +@SecurityRequirement(name = "bearer-key") public class DoctorController { @Autowired diff --git a/api/src/main/java/med/voll/api/controller/PatientController.java b/api/src/main/java/med/voll/api/controller/PatientController.java index bd774a1..5675cfb 100644 --- a/api/src/main/java/med/voll/api/controller/PatientController.java +++ b/api/src/main/java/med/voll/api/controller/PatientController.java @@ -1,5 +1,6 @@ package med.voll.api.controller; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; import jakarta.validation.Valid; import med.voll.api.domain.patient.PatientListingData; import med.voll.api.domain.patient.Patient; @@ -17,6 +18,7 @@ import org.springframework.web.util.UriComponentsBuilder; @RestController @RequestMapping("/patients") +@SecurityRequirement(name = "bearer-key") public class PatientController { @Autowired PatientRepository patientRepository; diff --git a/api/src/main/java/med/voll/api/domain/appointment/Appointment.java b/api/src/main/java/med/voll/api/domain/appointment/Appointment.java new file mode 100644 index 0000000..a48c15f --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/appointment/Appointment.java @@ -0,0 +1,47 @@ +package med.voll.api.domain.appointment; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import med.voll.api.domain.doctor.Doctor; +import med.voll.api.domain.patient.Patient; + +import java.time.LocalDateTime; + +@Table(name = "appointments") +@Entity(name = "Appointment") +@Getter +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(of = "id") +public class Appointment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private LocalDateTime date; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "doctor_id") + private Doctor doctor; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "patient_id") + private Patient patient; + private Boolean active = true; + + @Column(name = "reason_for_cancellation") + @Enumerated(EnumType.STRING) + private ReasonForCancellation reasonForCancellation; + + public Appointment(Long id, LocalDateTime date, Doctor doctor, Patient patient) { + this.id = id; + this.date = date; + this.doctor = doctor; + this.patient = patient; + } + + public void cancel(ReasonForCancellation reasonForCancellation) { + this.reasonForCancellation = reasonForCancellation; + this.active = false; + } +} diff --git a/api/src/main/java/med/voll/api/domain/appointment/AppointmentDeletionData.java b/api/src/main/java/med/voll/api/domain/appointment/AppointmentDeletionData.java new file mode 100644 index 0000000..81dc28e --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/appointment/AppointmentDeletionData.java @@ -0,0 +1,11 @@ +package med.voll.api.domain.appointment; + +import jakarta.validation.constraints.NotNull; + +public record AppointmentDeletionData( + @NotNull + Long idAppointment, + @NotNull + ReasonForCancellation reasonForCancellation +) { +} diff --git a/api/src/main/java/med/voll/api/domain/appointment/AppointmentListingData.java b/api/src/main/java/med/voll/api/domain/appointment/AppointmentListingData.java new file mode 100644 index 0000000..4070a4c --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/appointment/AppointmentListingData.java @@ -0,0 +1,10 @@ +package med.voll.api.domain.appointment; + +import java.time.LocalDateTime; + +public record AppointmentListingData(Long id, LocalDateTime date, Long idDoctor, Long idPatient) { + + public AppointmentListingData(Appointment appointment) { + this(appointment.getId(), appointment.getDate(), appointment.getDoctor().getId(), appointment.getPatient().getId()); + } +} diff --git a/api/src/main/java/med/voll/api/domain/appointment/AppointmentRegistrationData.java b/api/src/main/java/med/voll/api/domain/appointment/AppointmentRegistrationData.java new file mode 100644 index 0000000..fa62bde --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/appointment/AppointmentRegistrationData.java @@ -0,0 +1,17 @@ +package med.voll.api.domain.appointment; + +import jakarta.validation.constraints.Future; +import jakarta.validation.constraints.NotNull; +import med.voll.api.domain.doctor.Specialty; + +import java.time.LocalDateTime; + +public record AppointmentRegistrationData( + Long idDoctor, + @NotNull(message = "Patient is required") + Long idPatient, + @NotNull + @Future + LocalDateTime date, + Specialty specialty) { +} diff --git a/api/src/main/java/med/voll/api/domain/appointment/AppointmentRepository.java b/api/src/main/java/med/voll/api/domain/appointment/AppointmentRepository.java new file mode 100644 index 0000000..a56e3c7 --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/appointment/AppointmentRepository.java @@ -0,0 +1,18 @@ +package med.voll.api.domain.appointment; + +import med.voll.api.domain.doctor.Doctor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; + +@Repository +public interface AppointmentRepository extends JpaRepository<Appointment, Long> { + Page<Appointment> findAllByActiveTrue(Pageable pagination); + + boolean existsByDoctorIdAndDateAndActiveIsTrue(Long idDoctor, LocalDateTime date); + + boolean existsByPatientIdAndDateBetween(Long idPatient, LocalDateTime earliestTime, LocalDateTime latestTime); +} diff --git a/api/src/main/java/med/voll/api/domain/appointment/AppointmentUpdateData.java b/api/src/main/java/med/voll/api/domain/appointment/AppointmentUpdateData.java new file mode 100644 index 0000000..c13712c --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/appointment/AppointmentUpdateData.java @@ -0,0 +1,19 @@ +package med.voll.api.domain.appointment; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +import java.time.LocalDateTime; + +public record AppointmentUpdateData( + @NotNull + Long id, + Integer year, + Integer month, + Integer day, + Integer hour, + Integer minute, + String doctor, + String patient +) { +} diff --git a/api/src/main/java/med/voll/api/domain/appointment/AppointmentsSchedule.java b/api/src/main/java/med/voll/api/domain/appointment/AppointmentsSchedule.java new file mode 100644 index 0000000..fd7d4e8 --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/appointment/AppointmentsSchedule.java @@ -0,0 +1,75 @@ +package med.voll.api.domain.appointment; + +import jakarta.validation.ValidationException; +import med.voll.api.domain.appointment.validations.cancellation.AppointmentCancellationValidator; +import med.voll.api.domain.appointment.validations.scheduling.AppointmentSchedulingValidator; +import med.voll.api.domain.doctor.Doctor; +import med.voll.api.domain.doctor.DoctorRepository; +import med.voll.api.domain.patient.PatientRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class AppointmentsSchedule { + @Autowired + private AppointmentRepository appointmentRepository; + @Autowired + private DoctorRepository doctorRepository; + @Autowired + private PatientRepository patientRepository; + @Autowired + private List<AppointmentSchedulingValidator> validators; + + @Autowired + private List<AppointmentCancellationValidator> cancellationValidators; + + public AppointmentListingData schedule(AppointmentRegistrationData data) { + if (!patientRepository.existsById(data.idPatient())) { + throw new ValidationException("Patient id doesn't exist"); + } + + if (data.idDoctor() != null && !doctorRepository.existsById(data.idDoctor())) { + throw new ValidationException("Doctor id doesn't exist"); + } + + validators.forEach(v -> v.validate(data)); + + var doctor = chooseDoctor(data); + if (doctor == null) { + throw new ValidationException("There are no available doctors on this date"); + } + var patient = patientRepository.getReferenceById(data.idPatient()); + + var appointment = new Appointment(null, data.date(), doctor, patient, true, null); + + appointmentRepository.save(appointment); + + return new AppointmentListingData(appointment); + } + + public void cancel(AppointmentDeletionData data) { + if (!appointmentRepository.existsById(data.idAppointment())) { + throw new ValidationException("Invalid appointment id"); + } + + cancellationValidators.forEach(v -> v.validate(data)); + + var appointment = appointmentRepository.getReferenceById(data.idAppointment()); + appointment.cancel(data.reasonForCancellation()); + } + + private Doctor chooseDoctor(AppointmentRegistrationData data) { + if(data.idDoctor() != null) { + return doctorRepository.getReferenceById(data.idDoctor()); + } + + if (data.specialty() == null) { + throw new ValidationException("Specialty is mandatory when a doctor is not chosen"); + } + + return doctorRepository.chooseRandomDoctorAvailable(data.specialty(), data.date()); + } + +} diff --git a/api/src/main/java/med/voll/api/domain/appointment/ReasonForCancellation.java b/api/src/main/java/med/voll/api/domain/appointment/ReasonForCancellation.java new file mode 100644 index 0000000..db6e98a --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/appointment/ReasonForCancellation.java @@ -0,0 +1,8 @@ +package med.voll.api.domain.appointment; + +public enum ReasonForCancellation { + PATIENT_GAVE_UP, + LONG_WAITING_TIME, + DOCTOR_CANCELED, + OTHER_REASON +} diff --git a/api/src/main/java/med/voll/api/domain/appointment/validations/cancellation/AppointmentCancellationValidator.java b/api/src/main/java/med/voll/api/domain/appointment/validations/cancellation/AppointmentCancellationValidator.java new file mode 100644 index 0000000..6c9e0cc --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/appointment/validations/cancellation/AppointmentCancellationValidator.java @@ -0,0 +1,7 @@ +package med.voll.api.domain.appointment.validations.cancellation; + +import med.voll.api.domain.appointment.AppointmentDeletionData; + +public interface AppointmentCancellationValidator { + void validate(AppointmentDeletionData data); +} diff --git a/api/src/main/java/med/voll/api/domain/appointment/validations/cancellation/ValidatesTimeInAdvance.java b/api/src/main/java/med/voll/api/domain/appointment/validations/cancellation/ValidatesTimeInAdvance.java new file mode 100644 index 0000000..03688ce --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/appointment/validations/cancellation/ValidatesTimeInAdvance.java @@ -0,0 +1,27 @@ +package med.voll.api.domain.appointment.validations.cancellation; + +import jakarta.validation.ValidationException; +import med.voll.api.domain.appointment.AppointmentDeletionData; +import med.voll.api.domain.appointment.AppointmentRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.time.LocalDateTime; + +@Component("ValidatesTimeInAdvanceCancellation") +public class ValidatesTimeInAdvance implements AppointmentCancellationValidator { + @Autowired + private AppointmentRepository appointmentRepository; + + @Override + public void validate(AppointmentDeletionData data) { + var appointment = appointmentRepository.getReferenceById(data.idAppointment()); + var now = LocalDateTime.now(); + var differenceInHours = Duration.between(now, appointment.getDate()).toHours(); + + if (differenceInHours < 24) { + throw new ValidationException("Appointments can only be cancelled with at least 24 hours in advance!"); + } + } +} diff --git a/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/AppointmentSchedulingValidator.java b/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/AppointmentSchedulingValidator.java new file mode 100644 index 0000000..99f6128 --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/AppointmentSchedulingValidator.java @@ -0,0 +1,7 @@ +package med.voll.api.domain.appointment.validations.scheduling; + +import med.voll.api.domain.appointment.AppointmentRegistrationData; + +public interface AppointmentSchedulingValidator { + void validate(AppointmentRegistrationData data); +} diff --git a/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesActiveDoctor.java b/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesActiveDoctor.java new file mode 100644 index 0000000..59bb761 --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesActiveDoctor.java @@ -0,0 +1,26 @@ +package med.voll.api.domain.appointment.validations.scheduling; + +import jakarta.validation.ValidationException; +import med.voll.api.domain.appointment.AppointmentRegistrationData; +import med.voll.api.domain.doctor.DoctorRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ValidatesActiveDoctor implements AppointmentSchedulingValidator { + + @Autowired + private DoctorRepository doctorRepository; + + public void validate(AppointmentRegistrationData data) { + // random doctor + if (data.idDoctor() == null) { + return; + } + + var doctorIsActive = doctorRepository.findActiveById(data.idDoctor()); + if (!doctorIsActive) { + throw new ValidationException("Appointment can't be scheduled with an inactive doctor"); + } + } +} diff --git a/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesActivePatient.java b/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesActivePatient.java new file mode 100644 index 0000000..439877f --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesActivePatient.java @@ -0,0 +1,21 @@ +package med.voll.api.domain.appointment.validations.scheduling; + +import jakarta.validation.ValidationException; +import med.voll.api.domain.appointment.AppointmentRegistrationData; +import med.voll.api.domain.patient.PatientRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ValidatesActivePatient implements AppointmentSchedulingValidator { + + @Autowired + private PatientRepository patientRepository; + + public void validate(AppointmentRegistrationData data) { + var patientIsActive = patientRepository.findActiveById(data.idPatient()); + if (!patientIsActive) { + throw new ValidationException("Appointment can't be scheduled with an inactive patient"); + } + } +} diff --git a/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesDateAndTime.java b/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesDateAndTime.java new file mode 100644 index 0000000..beee6c6 --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesDateAndTime.java @@ -0,0 +1,21 @@ +package med.voll.api.domain.appointment.validations.scheduling; + +import jakarta.validation.ValidationException; +import med.voll.api.domain.appointment.AppointmentRegistrationData; +import org.springframework.stereotype.Component; + +import java.time.DayOfWeek; + +@Component +public class ValidatesDateAndTime implements AppointmentSchedulingValidator { + public void validate(AppointmentRegistrationData data) { + var appointmentDate = data.date(); + var sunday = appointmentDate.getDayOfWeek().equals(DayOfWeek.SUNDAY); + var beforeTheClinicOpens = appointmentDate.getHour() < 7; + var afterTheClinicCloses = appointmentDate.getHour() > 18; + + if (sunday || beforeTheClinicOpens || afterTheClinicCloses) { + throw new ValidationException("Appointment outside opening hours or date"); + } + } +} diff --git a/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesDoctorWithOtherAppointmentAtTheSameTime.java b/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesDoctorWithOtherAppointmentAtTheSameTime.java new file mode 100644 index 0000000..fa94d81 --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesDoctorWithOtherAppointmentAtTheSameTime.java @@ -0,0 +1,21 @@ +package med.voll.api.domain.appointment.validations.scheduling; + +import jakarta.validation.ValidationException; +import med.voll.api.domain.appointment.AppointmentRegistrationData; +import med.voll.api.domain.appointment.AppointmentRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ValidatesDoctorWithOtherAppointmentAtTheSameTime implements AppointmentSchedulingValidator { + + @Autowired + private AppointmentRepository appointmentRepository; + + public void validate(AppointmentRegistrationData data) { + var doctorHasAnotherAppointmentAtTheSameTime = appointmentRepository.existsByDoctorIdAndDateAndActiveIsTrue(data.idDoctor(), data.date()); + if (doctorHasAnotherAppointmentAtTheSameTime) { + throw new ValidationException("This doctor already has an appointment at this time and date"); + } + } +} diff --git a/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesPatientDoesntHaveAnotherAppointmentToday.java b/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesPatientDoesntHaveAnotherAppointmentToday.java new file mode 100644 index 0000000..a412bfb --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesPatientDoesntHaveAnotherAppointmentToday.java @@ -0,0 +1,24 @@ +package med.voll.api.domain.appointment.validations.scheduling; + +import jakarta.validation.ValidationException; +import med.voll.api.domain.appointment.AppointmentRegistrationData; +import med.voll.api.domain.appointment.AppointmentRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ValidatesPatientDoesntHaveAnotherAppointmentToday implements AppointmentSchedulingValidator { + + @Autowired + private AppointmentRepository appointmentRepository; + + public void validate(AppointmentRegistrationData data) { + var earliestTime = data.date().withHour(7); + var latestTime = data.date().withHour(18); + var patientHasAnotherAppointmentToday = appointmentRepository.existsByPatientIdAndDateBetween(data.idPatient(), earliestTime, latestTime); + + if (patientHasAnotherAppointmentToday) { + throw new ValidationException("Patient already has an scheduled appointment today"); + } + } +} diff --git a/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesTimeInAdvance.java b/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesTimeInAdvance.java new file mode 100644 index 0000000..442f4f9 --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/appointment/validations/scheduling/ValidatesTimeInAdvance.java @@ -0,0 +1,21 @@ +package med.voll.api.domain.appointment.validations.scheduling; + +import jakarta.validation.ValidationException; +import med.voll.api.domain.appointment.AppointmentRegistrationData; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.time.LocalDateTime; + +@Component("ValidatesTimeInAdvanceScheduling") +public class ValidatesTimeInAdvance implements AppointmentSchedulingValidator { + public void validate(AppointmentRegistrationData data) { + var appointmentDate = data.date(); + var now = LocalDateTime.now(); + var differenceInMinutes = Duration.between(now, appointmentDate).toMinutes(); + + if (differenceInMinutes < 30) { + throw new ValidationException("Appointment should be scheduled half an hour in advance"); + } + } +} diff --git a/api/src/main/java/med/voll/api/domain/doctor/DoctorRepository.java b/api/src/main/java/med/voll/api/domain/doctor/DoctorRepository.java index 1efd0af..8cd6c46 100644 --- a/api/src/main/java/med/voll/api/domain/doctor/DoctorRepository.java +++ b/api/src/main/java/med/voll/api/domain/doctor/DoctorRepository.java @@ -3,9 +3,29 @@ package med.voll.api.domain.doctor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; + @Repository public interface DoctorRepository extends JpaRepository<Doctor, Long> { Page<Doctor> findAllByActiveTrue(Pageable pagination); + + //order by rand() limit 1 + @Query(""" + SELECT d from Doctor d where d.active = true + and d.specialty = :specialty + and d.id not in ( + select a.doctor.id from Appointment a where a.date = :date + and a.active = true + ) + """) + Doctor chooseRandomDoctorAvailable(Specialty specialty, LocalDateTime date); + + @Query(""" + select d.active from Doctor d + where d.id = :id + """) + Boolean findActiveById(Long id); } diff --git a/api/src/main/java/med/voll/api/domain/patient/PatientRepository.java b/api/src/main/java/med/voll/api/domain/patient/PatientRepository.java index e550ac2..dbdcd87 100644 --- a/api/src/main/java/med/voll/api/domain/patient/PatientRepository.java +++ b/api/src/main/java/med/voll/api/domain/patient/PatientRepository.java @@ -3,9 +3,16 @@ package med.voll.api.domain.patient; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; @Repository public interface PatientRepository extends JpaRepository<Patient, Long> { Page<Patient> findAllByActiveTrue(Pageable pagination); + + @Query(""" + select p.active from Patient p + where p.id = :id + """) + boolean findActiveById(Long id); } diff --git a/api/src/main/java/med/voll/api/infra/exception/ErrorTreatment.java b/api/src/main/java/med/voll/api/infra/exception/ErrorTreatment.java index 9e49fd3..f2d40cc 100644 --- a/api/src/main/java/med/voll/api/infra/exception/ErrorTreatment.java +++ b/api/src/main/java/med/voll/api/infra/exception/ErrorTreatment.java @@ -1,6 +1,7 @@ package med.voll.api.infra.exception; import jakarta.persistence.EntityNotFoundException; +import jakarta.validation.ValidationException; import org.springframework.http.ResponseEntity; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -20,6 +21,10 @@ public class ErrorTreatment { var errors = ex.getFieldErrors(); return ResponseEntity.badRequest().body(errors.stream().map(ValidationErrorData::new).toList()); } + @ExceptionHandler(ValidationException.class) + public ResponseEntity treatBusinessRule(ValidationException ex) { + return ResponseEntity.badRequest().body(ex.getMessage()); + } private record ValidationErrorData(String field, String message) { public ValidationErrorData(FieldError error) { diff --git a/api/src/main/java/med/voll/api/infra/security/SecurityConfigurations.java b/api/src/main/java/med/voll/api/infra/security/SecurityConfigurations.java index 5055162..46038c6 100644 --- a/api/src/main/java/med/voll/api/infra/security/SecurityConfigurations.java +++ b/api/src/main/java/med/voll/api/infra/security/SecurityConfigurations.java @@ -27,6 +27,7 @@ public class SecurityConfigurations { .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and().authorizeHttpRequests() .requestMatchers(HttpMethod.POST, "/login").permitAll() + .requestMatchers("/v3/api-docs/**", "/swagger-ui.html", "/swagger-ui/**").permitAll() .anyRequest().authenticated() .and().addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class) .build(); diff --git a/api/src/main/java/med/voll/api/infra/security/TokenService.java b/api/src/main/java/med/voll/api/infra/security/TokenService.java index 63dda73..bf19a3d 100644 --- a/api/src/main/java/med/voll/api/infra/security/TokenService.java +++ b/api/src/main/java/med/voll/api/infra/security/TokenService.java @@ -45,6 +45,6 @@ public class TokenService { } private Instant expirationDate() { - return LocalDateTime.now().plusHours(2).toInstant(ZoneOffset.of("-03:00")); + return LocalDateTime.now().plusHours(12).toInstant(ZoneOffset.of("-03:00")); } } diff --git a/api/src/main/java/med/voll/api/infra/springdoc/SpringDocConfigurations.java b/api/src/main/java/med/voll/api/infra/springdoc/SpringDocConfigurations.java new file mode 100644 index 0000000..c575160 --- /dev/null +++ b/api/src/main/java/med/voll/api/infra/springdoc/SpringDocConfigurations.java @@ -0,0 +1,34 @@ +package med.voll.api.infra.springdoc; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.security.SecurityScheme; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SpringDocConfigurations { + + @Bean + public OpenAPI customOpenAPI() { + return new OpenAPI() + .components(new Components() + .addSecuritySchemes("bearer-key", + new SecurityScheme() + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT"))) + .info(new Info() + .title("Voll.med API") + .description("Voll.med Rest API application, containing CRUD functionalities for doctors and patients, in addition to scheduling and canceling appointments") + .contact(new Contact() + .name("Backend Team") + .email("backend@voll.med")) + .license(new License() + .name("Apache 2.0") + .url("http://voll.med/api/license"))); + } +} |
