diff --git a/pom.xml b/pom.xml index 2cade74..dbec20a 100644 --- a/pom.xml +++ b/pom.xml @@ -28,6 +28,7 @@ 21 + 1.5.5.Final @@ -55,6 +56,7 @@ org.projectlombok lombok + 1.18.30 provided @@ -65,7 +67,7 @@ com.querydsl querydsl-apt - 5.0.0 + 5.1.0 jakarta provided @@ -73,7 +75,22 @@ com.querydsl querydsl-jpa jakarta - 5.0.0 + 5.1.0 + + + org.mapstruct + mapstruct + ${org.mapstruct.version} + + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + provided + + + org.springframework.boot + spring-boot-starter-validation diff --git a/src/main/java/com/capgemini/training/appointmentbooking/common/datatype/AppointmentStatus.java b/src/main/java/com/capgemini/training/appointmentbooking/common/datatype/AppointmentStatus.java index 7f49c3a..53854ad 100644 --- a/src/main/java/com/capgemini/training/appointmentbooking/common/datatype/AppointmentStatus.java +++ b/src/main/java/com/capgemini/training/appointmentbooking/common/datatype/AppointmentStatus.java @@ -1,5 +1,5 @@ package com.capgemini.training.appointmentbooking.common.datatype; public enum AppointmentStatus { -SCHEDULED, CANCELLED, COMPLETED; + SCHEDULED, CANCELLED, COMPLETED } diff --git a/src/main/java/com/capgemini/training/appointmentbooking/common/exception/ConflictedAppointmentException.java b/src/main/java/com/capgemini/training/appointmentbooking/common/exception/ConflictedAppointmentException.java new file mode 100644 index 0000000..757abd6 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/common/exception/ConflictedAppointmentException.java @@ -0,0 +1,10 @@ +package com.capgemini.training.appointmentbooking.common.exception; + + +public class ConflictedAppointmentException extends RuntimeException { + + public ConflictedAppointmentException() { + super("The appointment conflicts with another scheduled appointment."); + } + +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/common/to/AppointmentBookingEto.java b/src/main/java/com/capgemini/training/appointmentbooking/common/to/AppointmentBookingEto.java new file mode 100644 index 0000000..d9b14e4 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/common/to/AppointmentBookingEto.java @@ -0,0 +1,12 @@ +package com.capgemini.training.appointmentbooking.common.to; + +import jakarta.validation.constraints.NotNull; +import lombok.Builder; + +import java.time.Instant; + + +@Builder +public record AppointmentBookingEto(@NotNull Long clientId, @NotNull Long treatmentId, @NotNull Long specialistId, + @NotNull Instant dateTime) { +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/common/to/AppointmentCto.java b/src/main/java/com/capgemini/training/appointmentbooking/common/to/AppointmentCto.java new file mode 100644 index 0000000..3559574 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/common/to/AppointmentCto.java @@ -0,0 +1,10 @@ +package com.capgemini.training.appointmentbooking.common.to; + +import jakarta.validation.Valid; +import lombok.Builder; + + +@Builder +public record AppointmentCto(@Valid AppointmentEto appointmentEto, @Valid ClientEto clientEto, + @Valid TreatmentCto treatmentCto) { +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/common/to/AppointmentEto.java b/src/main/java/com/capgemini/training/appointmentbooking/common/to/AppointmentEto.java new file mode 100644 index 0000000..a5a3262 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/common/to/AppointmentEto.java @@ -0,0 +1,12 @@ +package com.capgemini.training.appointmentbooking.common.to; + +import com.capgemini.training.appointmentbooking.common.datatype.AppointmentStatus; +import jakarta.validation.constraints.NotNull; +import lombok.Builder; + +import java.time.Instant; + + +@Builder +public record AppointmentEto(@NotNull Long id, @NotNull Instant dateTime, @NotNull AppointmentStatus status) { +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/common/to/ClientCto.java b/src/main/java/com/capgemini/training/appointmentbooking/common/to/ClientCto.java new file mode 100644 index 0000000..e9a1d6f --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/common/to/ClientCto.java @@ -0,0 +1,9 @@ +package com.capgemini.training.appointmentbooking.common.to; + +import jakarta.validation.Valid; +import lombok.Builder; + + +@Builder +public record ClientCto(@Valid ClientEto clientEto, @Valid UserEto userEto) { +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/common/to/ClientEto.java b/src/main/java/com/capgemini/training/appointmentbooking/common/to/ClientEto.java new file mode 100644 index 0000000..293b2b5 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/common/to/ClientEto.java @@ -0,0 +1,9 @@ +package com.capgemini.training.appointmentbooking.common.to; + +import jakarta.validation.constraints.NotNull; +import lombok.Builder; + + +@Builder +public record ClientEto(@NotNull Long id) { +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/common/to/SpecialistCto.java b/src/main/java/com/capgemini/training/appointmentbooking/common/to/SpecialistCto.java new file mode 100644 index 0000000..a6e63bd --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/common/to/SpecialistCto.java @@ -0,0 +1,9 @@ +package com.capgemini.training.appointmentbooking.common.to; + +import jakarta.validation.Valid; +import lombok.Builder; + + +@Builder +public record SpecialistCto(@Valid SpecialistEto specialistEto, @Valid UserEto userEto) { +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/common/to/SpecialistEto.java b/src/main/java/com/capgemini/training/appointmentbooking/common/to/SpecialistEto.java new file mode 100644 index 0000000..d6008e2 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/common/to/SpecialistEto.java @@ -0,0 +1,11 @@ +package com.capgemini.training.appointmentbooking.common.to; + +import com.capgemini.training.appointmentbooking.common.datatype.Specialization; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.Builder; + + +@Builder +public record SpecialistEto(@NotNull Long id, @Valid Specialization specialization) { +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/common/to/TreatmentCreationTo.java b/src/main/java/com/capgemini/training/appointmentbooking/common/to/TreatmentCreationTo.java new file mode 100644 index 0000000..3ff509d --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/common/to/TreatmentCreationTo.java @@ -0,0 +1,14 @@ +package com.capgemini.training.appointmentbooking.common.to; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Builder; + + +@Builder +public record TreatmentCreationTo(@NotNull @Size(min = 5, max = 20) String name, + @NotNull @Size(min = 5, max = 80) String description, + @Min(1) int durationMinutes, + @NotNull Long specialistId) { +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/common/to/TreatmentCto.java b/src/main/java/com/capgemini/training/appointmentbooking/common/to/TreatmentCto.java new file mode 100644 index 0000000..6e9fc5d --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/common/to/TreatmentCto.java @@ -0,0 +1,9 @@ +package com.capgemini.training.appointmentbooking.common.to; + +import jakarta.validation.Valid; +import lombok.Builder; + + +@Builder +public record TreatmentCto(@Valid TreatmentEto treatmentEto, @Valid SpecialistEto specialistEto) { +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/common/to/TreatmentEto.java b/src/main/java/com/capgemini/training/appointmentbooking/common/to/TreatmentEto.java new file mode 100644 index 0000000..0d5df76 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/common/to/TreatmentEto.java @@ -0,0 +1,11 @@ +package com.capgemini.training.appointmentbooking.common.to; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Builder; + + +@Builder +public record TreatmentEto(Long id, @NotNull @Size(min = 5, max = 80) String name, + @NotNull @Size(min = 5, max = 80) String description, int durationMinutes) { +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/common/to/UserEto.java b/src/main/java/com/capgemini/training/appointmentbooking/common/to/UserEto.java new file mode 100644 index 0000000..4d4ba6c --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/common/to/UserEto.java @@ -0,0 +1,14 @@ +package com.capgemini.training.appointmentbooking.common.to; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Builder; + + +@Builder +public record UserEto(Long id, @NotEmpty @Email String email, String passwordHash, + @NotNull @Size(min = 3, max = 20) String firstName, + @NotNull @Size(min = 3, max = 20) String lastName) { +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/dataaccess/repository/SpecialistRepository.java b/src/main/java/com/capgemini/training/appointmentbooking/dataaccess/repository/SpecialistRepository.java index 86925ef..b5473e4 100644 --- a/src/main/java/com/capgemini/training/appointmentbooking/dataaccess/repository/SpecialistRepository.java +++ b/src/main/java/com/capgemini/training/appointmentbooking/dataaccess/repository/SpecialistRepository.java @@ -27,7 +27,6 @@ default List findSpecialistByName(String firstName, String las .where(user.firstname.eq(firstName) .and(user.lastname.eq(lastName))) .fetch(); - } } diff --git a/src/main/java/com/capgemini/training/appointmentbooking/logic/FindAppointmentUc.java b/src/main/java/com/capgemini/training/appointmentbooking/logic/FindAppointmentUc.java new file mode 100644 index 0000000..71612bf --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/logic/FindAppointmentUc.java @@ -0,0 +1,22 @@ +package com.capgemini.training.appointmentbooking.logic; + +import com.capgemini.training.appointmentbooking.common.to.AppointmentCto; +import com.capgemini.training.appointmentbooking.dataaccess.repository.criteria.AppointmentCriteria; +import jakarta.validation.constraints.NotNull; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; + + +public interface FindAppointmentUc { + + Optional findById(@NotNull Long id); + + List findByCriteria(AppointmentCriteria criteria); + + List findAll(); + + boolean hasConflictingAppointment(@NotNull Long specialistId, @NotNull Instant dateTime); + +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/logic/FindTreatmentUc.java b/src/main/java/com/capgemini/training/appointmentbooking/logic/FindTreatmentUc.java new file mode 100644 index 0000000..04a1136 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/logic/FindTreatmentUc.java @@ -0,0 +1,19 @@ +package com.capgemini.training.appointmentbooking.logic; + +import com.capgemini.training.appointmentbooking.common.to.TreatmentCto; +import com.capgemini.training.appointmentbooking.dataaccess.repository.criteria.TreatmentCriteria; +import jakarta.validation.constraints.NotNull; + +import java.util.List; +import java.util.Optional; + + +public interface FindTreatmentUc { + + Optional findById(@NotNull Long id); + + List findAll(); + + List findByCriteria(TreatmentCriteria criteria); + +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/logic/ManageAppointmentUc.java b/src/main/java/com/capgemini/training/appointmentbooking/logic/ManageAppointmentUc.java new file mode 100644 index 0000000..3a0a304 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/logic/ManageAppointmentUc.java @@ -0,0 +1,17 @@ +package com.capgemini.training.appointmentbooking.logic; + +import com.capgemini.training.appointmentbooking.common.datatype.AppointmentStatus; +import com.capgemini.training.appointmentbooking.common.to.AppointmentBookingEto; +import com.capgemini.training.appointmentbooking.common.to.AppointmentCto; +import com.capgemini.training.appointmentbooking.common.to.AppointmentEto; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + + +public interface ManageAppointmentUc { + + AppointmentCto bookAppointment(@Valid AppointmentBookingEto appointmentBookingEto); + + AppointmentEto updateAppointmentStatus(@NotNull Long appointmentId, @NotNull AppointmentStatus appointmentStatus); + +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/logic/ManageTreatmentUc.java b/src/main/java/com/capgemini/training/appointmentbooking/logic/ManageTreatmentUc.java new file mode 100644 index 0000000..c3f9209 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/logic/ManageTreatmentUc.java @@ -0,0 +1,11 @@ +package com.capgemini.training.appointmentbooking.logic; + +import com.capgemini.training.appointmentbooking.common.to.TreatmentCreationTo; +import com.capgemini.training.appointmentbooking.common.to.TreatmentCto; +import jakarta.validation.Valid; + +public interface ManageTreatmentUc { + + TreatmentCto createTreatment(@Valid TreatmentCreationTo treatmentCreationTo); + +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/logic/config/MappingConfiguration.java b/src/main/java/com/capgemini/training/appointmentbooking/logic/config/MappingConfiguration.java new file mode 100644 index 0000000..ab8ada4 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/logic/config/MappingConfiguration.java @@ -0,0 +1,46 @@ +package com.capgemini.training.appointmentbooking.logic.config; + +import com.capgemini.training.appointmentbooking.logic.mapper.*; +import org.mapstruct.factory.Mappers; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class MappingConfiguration { + + @Bean + AppointmentMapper getAppointmentMapper() { + return Mappers.getMapper(AppointmentMapper.class); + } + + @Bean + AppointmentCtoMapper getAppointmentCtoMapper() { + return Mappers.getMapper(AppointmentCtoMapper.class); + } + + @Bean + ClientMapper getClientMapper() { + return Mappers.getMapper(ClientMapper.class); + } + + @Bean + SpecialistMapper getSpecialistMapper() { + return Mappers.getMapper(SpecialistMapper.class); + } + + @Bean + TreatmentMapper getTreatmentMapper() { + return Mappers.getMapper(TreatmentMapper.class); + } + + @Bean + TreatmentCtoMapper getTreatmentCtoMapper() { + return Mappers.getMapper(TreatmentCtoMapper.class); + } + + @Bean + UserMapper getUserMapper() { + return Mappers.getMapper(UserMapper.class); + } + +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/logic/impl/FindAppointmentUcImpl.java b/src/main/java/com/capgemini/training/appointmentbooking/logic/impl/FindAppointmentUcImpl.java new file mode 100644 index 0000000..973276c --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/logic/impl/FindAppointmentUcImpl.java @@ -0,0 +1,53 @@ +package com.capgemini.training.appointmentbooking.logic.impl; + +import com.capgemini.training.appointmentbooking.common.to.AppointmentCto; +import com.capgemini.training.appointmentbooking.dataaccess.repository.AppointmentRepository; +import com.capgemini.training.appointmentbooking.dataaccess.repository.criteria.AppointmentCriteria; +import com.capgemini.training.appointmentbooking.logic.FindAppointmentUc; +import com.capgemini.training.appointmentbooking.logic.mapper.AppointmentCtoMapper; +import jakarta.validation.constraints.NotNull; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + + +@Service +@Transactional(readOnly = true) +@Validated +public class FindAppointmentUcImpl implements FindAppointmentUc { + + private final AppointmentRepository appointmentRepository; + + private final AppointmentCtoMapper appointmentCtoMapper; + + public FindAppointmentUcImpl(AppointmentRepository appointmentRepository, AppointmentCtoMapper appointmentCtoMapper) { + this.appointmentRepository = appointmentRepository; + this.appointmentCtoMapper = appointmentCtoMapper; + } + + @Override + public Optional findById(@NotNull Long id) { + return appointmentRepository.findById(id).map(appointmentCtoMapper::toCto); + } + + @Override + public List findAll() { + return appointmentRepository.findAll().stream().map(appointmentCtoMapper::toCto).collect(Collectors.toList()); + } + + @Override + public List findByCriteria(AppointmentCriteria criteria) { + return appointmentRepository.findByCriteria(criteria).stream().map(appointmentCtoMapper::toCto).collect(Collectors.toList()); + } + + @Override + public boolean hasConflictingAppointment(@NotNull Long specialistId, @NotNull Instant dateTime) { + return appointmentRepository.hasConflictingAppointmentBySpecialistIdAndDateTime(specialistId, dateTime); + } + +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/logic/impl/FindTreatmentUcImpl.java b/src/main/java/com/capgemini/training/appointmentbooking/logic/impl/FindTreatmentUcImpl.java new file mode 100644 index 0000000..218f8f9 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/logic/impl/FindTreatmentUcImpl.java @@ -0,0 +1,47 @@ +package com.capgemini.training.appointmentbooking.logic.impl; + +import com.capgemini.training.appointmentbooking.common.to.TreatmentCto; +import com.capgemini.training.appointmentbooking.dataaccess.repository.TreatmentRepository; +import com.capgemini.training.appointmentbooking.dataaccess.repository.criteria.TreatmentCriteria; +import com.capgemini.training.appointmentbooking.logic.FindTreatmentUc; +import com.capgemini.training.appointmentbooking.logic.mapper.TreatmentCtoMapper; +import jakarta.validation.constraints.NotNull; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + + +@Service +@Transactional(readOnly = true) +@Validated +public class FindTreatmentUcImpl implements FindTreatmentUc { + + private final TreatmentRepository treatmentRepository; + + private final TreatmentCtoMapper treatmentCtoMapper; + + public FindTreatmentUcImpl(TreatmentRepository treatmentRepository, TreatmentCtoMapper treatmentCtoMapper) { + this.treatmentRepository = treatmentRepository; + this.treatmentCtoMapper = treatmentCtoMapper; + } + + @Override + public Optional findById(@NotNull Long id) { + return treatmentRepository.findById(id).map(treatmentCtoMapper::toCto); + } + + @Override + public List findByCriteria(TreatmentCriteria criteria) { + return treatmentRepository.findByCriteria(criteria).stream().map(treatmentCtoMapper::toCto).collect(Collectors.toList()); + } + + @Override + public List findAll() { + return treatmentRepository.findAll().stream().map(treatmentCtoMapper::toCto).toList(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/logic/impl/ManageAppointmentUcImpl.java b/src/main/java/com/capgemini/training/appointmentbooking/logic/impl/ManageAppointmentUcImpl.java new file mode 100644 index 0000000..1a84d35 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/logic/impl/ManageAppointmentUcImpl.java @@ -0,0 +1,93 @@ +package com.capgemini.training.appointmentbooking.logic.impl; + +import com.capgemini.training.appointmentbooking.common.datatype.AppointmentStatus; +import com.capgemini.training.appointmentbooking.common.exception.ConflictedAppointmentException; +import com.capgemini.training.appointmentbooking.common.to.AppointmentBookingEto; +import com.capgemini.training.appointmentbooking.common.to.AppointmentCto; +import com.capgemini.training.appointmentbooking.common.to.AppointmentEto; +import com.capgemini.training.appointmentbooking.dataaccess.entity.AppointmentEntity; +import com.capgemini.training.appointmentbooking.dataaccess.entity.ClientEntity; +import com.capgemini.training.appointmentbooking.dataaccess.entity.TreatmentEntity; +import com.capgemini.training.appointmentbooking.dataaccess.repository.AppointmentRepository; +import com.capgemini.training.appointmentbooking.dataaccess.repository.ClientRepository; +import com.capgemini.training.appointmentbooking.dataaccess.repository.TreatmentRepository; +import com.capgemini.training.appointmentbooking.logic.FindAppointmentUc; +import com.capgemini.training.appointmentbooking.logic.ManageAppointmentUc; +import com.capgemini.training.appointmentbooking.logic.mapper.AppointmentCtoMapper; +import com.capgemini.training.appointmentbooking.logic.mapper.AppointmentMapper; +import jakarta.persistence.EntityNotFoundException; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + + +@Service +@Transactional +@Validated +public class ManageAppointmentUcImpl implements ManageAppointmentUc { + + private final FindAppointmentUc findAppointmentUc; + + private final AppointmentRepository appointmentRepository; + + private final ClientRepository clientRepository; + + private final TreatmentRepository treatmentRepository; + + private final AppointmentMapper appointmentMapper; + + private final AppointmentCtoMapper appointmentCtoMapper; + + public ManageAppointmentUcImpl(FindAppointmentUc findAppointmentUc, AppointmentRepository appointmentRepository, ClientRepository clientRepository, + TreatmentRepository treatmentRepository, AppointmentMapper appointmentMapper, AppointmentCtoMapper appointmentCtoMapper) { + this.findAppointmentUc = findAppointmentUc; + this.appointmentRepository = appointmentRepository; + this.clientRepository = clientRepository; + this.treatmentRepository = treatmentRepository; + this.appointmentMapper = appointmentMapper; + this.appointmentCtoMapper = appointmentCtoMapper; + } + + @Override + public AppointmentCto bookAppointment(@Valid AppointmentBookingEto appointmentBookingEto) { + boolean hasConflict = findAppointmentUc.hasConflictingAppointment(appointmentBookingEto.specialistId(), appointmentBookingEto.dateTime()); + + if (hasConflict) { + throw new ConflictedAppointmentException(); + } + + AppointmentEntity appointmentEntity = buildAppointmentEntity(appointmentBookingEto); + appointmentEntity = appointmentRepository.saveAndFlush(appointmentEntity); + + return appointmentCtoMapper.toCto(appointmentEntity); + } + + @Override + public AppointmentEto updateAppointmentStatus(@NotNull Long appointmentId, @NotNull AppointmentStatus appointmentStatus) { + AppointmentEntity appointmentEntity = findAppointmentUc.findById(appointmentId) + .map(appointmentCtoMapper::toEntity) + .orElseThrow(() -> new EntityNotFoundException("Appointment not found for ID: " + appointmentId)); + + appointmentEntity.setStatus(appointmentStatus); + appointmentEntity = appointmentRepository.saveAndFlush(appointmentEntity); + + return appointmentMapper.toEto(appointmentEntity); + } + + private AppointmentEntity buildAppointmentEntity(@Valid AppointmentBookingEto appointmentBookingEto) { + AppointmentEntity entity = new AppointmentEntity(); + + ClientEntity clientEntity = clientRepository.getReferenceById(appointmentBookingEto.clientId()); + TreatmentEntity treatmentEntity = treatmentRepository.getReferenceById(appointmentBookingEto.treatmentId()); + + entity.setClient(clientEntity); + entity.setTreatment(treatmentEntity); + entity.setDateTime(appointmentBookingEto.dateTime()); + entity.setStatus(AppointmentStatus.SCHEDULED); + + return entity; + } + +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/logic/impl/ManageTreatmentUcImpl.java b/src/main/java/com/capgemini/training/appointmentbooking/logic/impl/ManageTreatmentUcImpl.java new file mode 100644 index 0000000..659d002 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/logic/impl/ManageTreatmentUcImpl.java @@ -0,0 +1,54 @@ +package com.capgemini.training.appointmentbooking.logic.impl; + +import com.capgemini.training.appointmentbooking.common.to.TreatmentCreationTo; +import com.capgemini.training.appointmentbooking.common.to.TreatmentCto; +import com.capgemini.training.appointmentbooking.dataaccess.entity.SpecialistEntity; +import com.capgemini.training.appointmentbooking.dataaccess.entity.TreatmentEntity; +import com.capgemini.training.appointmentbooking.dataaccess.repository.SpecialistRepository; +import com.capgemini.training.appointmentbooking.dataaccess.repository.TreatmentRepository; +import com.capgemini.training.appointmentbooking.logic.ManageTreatmentUc; +import com.capgemini.training.appointmentbooking.logic.mapper.TreatmentCtoMapper; +import jakarta.validation.Valid; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + + +@Service +@Transactional +@Validated +public class ManageTreatmentUcImpl implements ManageTreatmentUc { + + private final TreatmentRepository treatmentRepository; + + private final SpecialistRepository specialistRepository; + + private final TreatmentCtoMapper treatmentCtoMapper; + + public ManageTreatmentUcImpl(TreatmentRepository treatmentRepository, SpecialistRepository specialistRepository, TreatmentCtoMapper treatmentCtoMapper) { + this.treatmentRepository = treatmentRepository; + this.specialistRepository = specialistRepository; + this.treatmentCtoMapper = treatmentCtoMapper; + } + + @Override + public TreatmentCto createTreatment(@Valid TreatmentCreationTo treatmentCreationTo) { + TreatmentEntity treatmentEntity = buildTreatmentEntity(treatmentCreationTo); + treatmentEntity = treatmentRepository.saveAndFlush(treatmentEntity); + return treatmentCtoMapper.toCto(treatmentEntity); + } + + private TreatmentEntity buildTreatmentEntity(@Valid TreatmentCreationTo treatmentCreationTo) { + TreatmentEntity entity = new TreatmentEntity(); + + SpecialistEntity specialistEntity = specialistRepository.getReferenceById(treatmentCreationTo.specialistId()); + + entity.setName(treatmentCreationTo.name()); + entity.setDescription(treatmentCreationTo.description()); + entity.setDurationMinutes(treatmentCreationTo.durationMinutes()); + entity.setSpecialist(specialistEntity); + + return entity; + } + +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/AppointmentCtoMapper.java b/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/AppointmentCtoMapper.java new file mode 100644 index 0000000..0a23545 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/AppointmentCtoMapper.java @@ -0,0 +1,26 @@ +package com.capgemini.training.appointmentbooking.logic.mapper; + + +import com.capgemini.training.appointmentbooking.common.to.AppointmentCto; +import com.capgemini.training.appointmentbooking.dataaccess.entity.AppointmentEntity; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + + +@Mapper(uses = {AppointmentMapper.class, ClientMapper.class, TreatmentCtoMapper.class}) +public interface AppointmentCtoMapper { + + @Mapping(source = "appointmentEto.id", target = "id") + @Mapping(source = "appointmentEto.dateTime", target = "dateTime") + @Mapping(source = "appointmentEto.status", target = "status") + @Mapping(source = "clientEto", target = "client") + @Mapping(source = "treatmentCto", target = "treatment") + @Mapping(target = "version", ignore = true) + AppointmentEntity toEntity(AppointmentCto eto); + + @Mapping(source = "entity", target = "appointmentEto") + @Mapping(source = "entity.client", target = "clientEto") + @Mapping(source = "entity.treatment", target = "treatmentCto") + AppointmentCto toCto(AppointmentEntity entity); + +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/AppointmentMapper.java b/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/AppointmentMapper.java new file mode 100644 index 0000000..7110e89 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/AppointmentMapper.java @@ -0,0 +1,20 @@ +package com.capgemini.training.appointmentbooking.logic.mapper; + + +import com.capgemini.training.appointmentbooking.common.to.AppointmentEto; +import com.capgemini.training.appointmentbooking.dataaccess.entity.AppointmentEntity; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + + +@Mapper +public interface AppointmentMapper { + + @Mapping(target = "client", ignore = true) + @Mapping(target = "version", ignore = true) + @Mapping(target = "treatment", ignore = true) + AppointmentEntity toEntity(AppointmentEto eto); + + AppointmentEto toEto(AppointmentEntity entity); + +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/ClientMapper.java b/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/ClientMapper.java new file mode 100644 index 0000000..26654cf --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/ClientMapper.java @@ -0,0 +1,20 @@ +package com.capgemini.training.appointmentbooking.logic.mapper; + + +import com.capgemini.training.appointmentbooking.common.to.ClientEto; +import com.capgemini.training.appointmentbooking.dataaccess.entity.ClientEntity; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + + +@Mapper +public interface ClientMapper { + + @Mapping(target = "appointments", ignore = true) + @Mapping(target = "user", ignore = true) + @Mapping(target = "version", ignore = true) + ClientEntity toEntity(ClientEto clientEto); + + ClientEto toEto(ClientEntity clientEntity); + +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/SpecialistMapper.java b/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/SpecialistMapper.java new file mode 100644 index 0000000..415b979 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/SpecialistMapper.java @@ -0,0 +1,20 @@ +package com.capgemini.training.appointmentbooking.logic.mapper; + + +import com.capgemini.training.appointmentbooking.common.to.SpecialistEto; +import com.capgemini.training.appointmentbooking.dataaccess.entity.SpecialistEntity; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + + +@Mapper +public interface SpecialistMapper { + + @Mapping(target = "version", ignore = true) + @Mapping(target = "user", ignore = true) + @Mapping(target = "treatments", ignore = true) + SpecialistEntity toEntity(SpecialistEto eto); + + SpecialistEto toEto(SpecialistEntity entity); + +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/TreatmentCtoMapper.java b/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/TreatmentCtoMapper.java new file mode 100644 index 0000000..ab729e1 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/TreatmentCtoMapper.java @@ -0,0 +1,25 @@ +package com.capgemini.training.appointmentbooking.logic.mapper; + + +import com.capgemini.training.appointmentbooking.common.to.TreatmentCto; +import com.capgemini.training.appointmentbooking.dataaccess.entity.TreatmentEntity; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + + +@Mapper(uses = {TreatmentMapper.class, SpecialistMapper.class}) +public interface TreatmentCtoMapper { + + @Mapping(source = "treatmentEto.id", target = "id") + @Mapping(source = "treatmentEto.name", target = "name") + @Mapping(source = "treatmentEto.description", target = "description") + @Mapping(source = "treatmentEto.durationMinutes", target = "durationMinutes") + @Mapping(source = "specialistEto", target = "specialist") + @Mapping(target = "version", ignore = true) + TreatmentEntity toEntity(TreatmentCto entity); + + @Mapping(source = "entity", target = "treatmentEto") + @Mapping(source = "entity.specialist", target = "specialistEto") + TreatmentCto toCto(TreatmentEntity entity); + +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/TreatmentMapper.java b/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/TreatmentMapper.java new file mode 100644 index 0000000..e4bbfb3 --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/TreatmentMapper.java @@ -0,0 +1,19 @@ +package com.capgemini.training.appointmentbooking.logic.mapper; + + +import com.capgemini.training.appointmentbooking.common.to.TreatmentEto; +import com.capgemini.training.appointmentbooking.dataaccess.entity.TreatmentEntity; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + + +@Mapper +public interface TreatmentMapper { + + @Mapping(target = "specialist", ignore = true) + @Mapping(target = "version", ignore = true) + TreatmentEntity toEntity(TreatmentEto eto); + + TreatmentEto toEto(TreatmentEntity entity); + +} \ No newline at end of file diff --git a/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/UserMapper.java b/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/UserMapper.java new file mode 100644 index 0000000..d03f27a --- /dev/null +++ b/src/main/java/com/capgemini/training/appointmentbooking/logic/mapper/UserMapper.java @@ -0,0 +1,22 @@ +package com.capgemini.training.appointmentbooking.logic.mapper; + + +import com.capgemini.training.appointmentbooking.common.to.UserEto; +import com.capgemini.training.appointmentbooking.dataaccess.entity.UserEntity; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + + +@Mapper +public interface UserMapper { + + @Mapping(target = "firstname", source = "firstName") + @Mapping(target = "lastname", source = "lastName") + @Mapping(target = "version", ignore = true) + UserEntity toEntity(UserEto eto); + + @Mapping(target = "firstName", source = "firstname") + @Mapping(target = "lastName", source = "lastname") + UserEto toEto(UserEntity entity); + +} \ No newline at end of file diff --git a/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/AppointmentRepositoryTestIT.java b/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/AppointmentRepositoryTestIT.java index e9d2e3a..d512862 100644 --- a/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/AppointmentRepositoryTestIT.java +++ b/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/AppointmentRepositoryTestIT.java @@ -10,11 +10,14 @@ import jakarta.inject.Inject; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.transaction.annotation.Transactional; import java.time.Instant; import java.util.List; import java.util.Optional; + +@Transactional @DataJpaTest public class AppointmentRepositoryTestIT extends BaseTest { @@ -193,4 +196,5 @@ void shouldFindConflictedAppointment() { // then assertThat(conflict).isTrue(); } + } diff --git a/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/ClientRepositoryTestIT.java b/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/ClientRepositoryTestIT.java index 22e1777..607e6ea 100644 --- a/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/ClientRepositoryTestIT.java +++ b/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/ClientRepositoryTestIT.java @@ -5,9 +5,12 @@ import jakarta.inject.Inject; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.transaction.annotation.Transactional; import java.util.List; + +@Transactional @DataJpaTest public class ClientRepositoryTestIT extends BaseTest { @@ -32,4 +35,5 @@ void shouldFindClientsByName() { assertThat(client.getUser().getLastname()).isEqualTo(lastName).as("Expected client to have the specified last name"); }); } + } diff --git a/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/SpecialistRepositoryTestIT.java b/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/SpecialistRepositoryTestIT.java index b5aec30..09db21a 100644 --- a/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/SpecialistRepositoryTestIT.java +++ b/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/SpecialistRepositoryTestIT.java @@ -6,9 +6,12 @@ import jakarta.inject.Inject; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.transaction.annotation.Transactional; import java.util.List; + +@Transactional @DataJpaTest public class SpecialistRepositoryTestIT extends BaseTest { @@ -48,4 +51,5 @@ void shouldFindSpecialistsByName() { assertThat(specialist.getUser().getLastname()).isEqualTo(lastName).as("Expected specialist to have the specified last name"); }); } + } diff --git a/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/TreatmentRepositoryTestIT.java b/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/TreatmentRepositoryTestIT.java index fc53725..2c0d7cf 100644 --- a/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/TreatmentRepositoryTestIT.java +++ b/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/TreatmentRepositoryTestIT.java @@ -8,10 +8,13 @@ import jakarta.inject.Inject; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Optional; + +@Transactional @DataJpaTest public class TreatmentRepositoryTestIT extends BaseTest { @@ -165,4 +168,5 @@ void shouldSaveTreatment() { assertThat(savedSpecialist.getTreatments()).isNotNull(); }); } + } diff --git a/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/UserRepositoryTestIT.java b/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/UserRepositoryTestIT.java index 58483aa..b8e9c6b 100644 --- a/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/UserRepositoryTestIT.java +++ b/src/test/java/com/capgemini/training/appointmentbooking/dataaccess/repository/UserRepositoryTestIT.java @@ -6,9 +6,12 @@ import jakarta.inject.Inject; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.transaction.annotation.Transactional; import java.util.List; + +@Transactional @DataJpaTest public class UserRepositoryTestIT extends BaseTest { @@ -40,4 +43,5 @@ void shouldFindUsersByCriteria() { assertThat(user.getLastname()).isEqualTo(lastName).as("Expected user to have the specified last name"); }); } + } diff --git a/src/test/java/com/capgemini/training/appointmentbooking/logic/FindAppointmentUcTestIT.java b/src/test/java/com/capgemini/training/appointmentbooking/logic/FindAppointmentUcTestIT.java new file mode 100644 index 0000000..a5d111f --- /dev/null +++ b/src/test/java/com/capgemini/training/appointmentbooking/logic/FindAppointmentUcTestIT.java @@ -0,0 +1,78 @@ +package com.capgemini.training.appointmentbooking.logic; + +import com.capgemini.training.appointmentbooking.common.BaseTest; +import com.capgemini.training.appointmentbooking.common.datatype.AppointmentStatus; +import com.capgemini.training.appointmentbooking.common.to.AppointmentCto; +import com.capgemini.training.appointmentbooking.dataaccess.repository.criteria.AppointmentCriteria; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; + +@Transactional +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +class FindAppointmentUcTestIT extends BaseTest { + + @Inject + private FindAppointmentUc findAppointmentUc; + + @Test + void shouldFindById() { + // given + Long appointmentId = -10L; + + // when + Optional appointment = findAppointmentUc.findById(appointmentId); + + // then + assertThat(appointment).isPresent(); + appointment.ifPresent(a -> { + assertThat(a.appointmentEto().id()).isEqualTo(appointmentId); + assertThat(a.appointmentEto().dateTime()).isEqualTo(toInstant("2024-03-10 12:30:00")); + assertThat(a.appointmentEto().status()).isEqualTo(AppointmentStatus.SCHEDULED); + }); + } + + @Test + void shouldFindAppointmentsByCriteria() { + // given + AppointmentCriteria criteria = AppointmentCriteria + .builder() + .treatmentName("Konsultacja dentystyczna") + .build(); + + // when + List result = findAppointmentUc.findByCriteria(criteria); + + // then + assertThat(result).hasSize(2); + } + + @Test + void shouldFindAllAppointments() { + // given + // when + List result = findAppointmentUc.findAll(); + + // then + assertThat(result).hasSize(20); + } + + @Test + void shouldFindConflictedAppointment() { + // given + Long specialistId = -1L; + Instant dateTime = toInstant("2024-03-01 09:00:00"); + + // when + boolean hasConflictingAppointment = findAppointmentUc.hasConflictingAppointment(specialistId, dateTime); + + // then + assertThat(hasConflictingAppointment).isTrue(); + } + +} \ No newline at end of file diff --git a/src/test/java/com/capgemini/training/appointmentbooking/logic/FindTreatmentUcTestIT.java b/src/test/java/com/capgemini/training/appointmentbooking/logic/FindTreatmentUcTestIT.java new file mode 100644 index 0000000..c809023 --- /dev/null +++ b/src/test/java/com/capgemini/training/appointmentbooking/logic/FindTreatmentUcTestIT.java @@ -0,0 +1,68 @@ +package com.capgemini.training.appointmentbooking.logic; + +import com.capgemini.training.appointmentbooking.common.BaseTest; +import com.capgemini.training.appointmentbooking.common.to.TreatmentCto; +import com.capgemini.training.appointmentbooking.dataaccess.repository.TreatmentRepository; +import com.capgemini.training.appointmentbooking.dataaccess.repository.criteria.TreatmentCriteria; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Optional; + + +@Transactional +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +class FindTreatmentUcTestIT extends BaseTest { + + @Inject + private FindTreatmentUc findTreatmentUc; + + @Inject + private TreatmentRepository treatmentRepository; + + @Test + void shouldFindTreatment() { + // given + Long treatmentId = -1L; + + // when + Optional appointment = findTreatmentUc.findById(treatmentId); + + // then + assertThat(appointment).isPresent(); + appointment.ifPresent(a -> { + assertThat(a.treatmentEto().id()).isEqualTo(treatmentId); + assertThat(a.treatmentEto().name()).isEqualTo("Konsultacja dentystyczna"); + assertThat(a.treatmentEto().description()).isEqualTo("Konsultacja dentystyczna z diagnostyką i planem leczenia"); + assertThat(a.treatmentEto().durationMinutes()).isEqualTo(30); + }); + } + + @Test + void shouldFindAppointmentsByCriteria() { + // given + TreatmentCriteria criteria = TreatmentCriteria + .builder() + .name("Konsultacja dentystyczna") + .build(); + + // when + List result = findTreatmentUc.findByCriteria(criteria); + + // then + assertThat(result).hasSize(1); + } + + @Test + void shouldFindAllTreatments() { + // given & when + List result = findTreatmentUc.findAll(); + + // then + assertThat(result).hasSize(12); + } + +} \ No newline at end of file diff --git a/src/test/java/com/capgemini/training/appointmentbooking/logic/ManageAppointmentUcTestIT.java b/src/test/java/com/capgemini/training/appointmentbooking/logic/ManageAppointmentUcTestIT.java new file mode 100644 index 0000000..f5ab1ba --- /dev/null +++ b/src/test/java/com/capgemini/training/appointmentbooking/logic/ManageAppointmentUcTestIT.java @@ -0,0 +1,118 @@ +package com.capgemini.training.appointmentbooking.logic; + +import com.capgemini.training.appointmentbooking.common.BaseTest; +import com.capgemini.training.appointmentbooking.common.datatype.AppointmentStatus; +import com.capgemini.training.appointmentbooking.common.exception.ConflictedAppointmentException; +import com.capgemini.training.appointmentbooking.common.to.AppointmentBookingEto; +import com.capgemini.training.appointmentbooking.common.to.AppointmentCto; +import jakarta.inject.Inject; +import jakarta.validation.ConstraintViolationException; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.util.Optional; + + +@Transactional +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +class ManageAppointmentUcTestIT extends BaseTest { + + @Inject + private FindAppointmentUc findAppointmentUc; + + @Inject + private ManageAppointmentUc manageAppointmentUc; + + @Test + void shouldBookAppointment() { + // given + Long clientId = -1L; + Long specialistId = -2L; + Long treatmentId = -3L; + Instant dateTime = toInstant("2024-03-03 14:00:00"); + + AppointmentBookingEto appointmentBookingEto = AppointmentBookingEto + .builder() + .clientId(clientId) + .specialistId(specialistId) + .treatmentId(treatmentId) + .dateTime(dateTime) + .build(); + + // when + AppointmentCto result = manageAppointmentUc.bookAppointment(appointmentBookingEto); + + // then + Optional appointmentCto = findAppointmentUc.findById(result.appointmentEto().id()); + assertThat(appointmentCto).isPresent().hasValueSatisfying(a -> { + assertThat(a.appointmentEto().id()).isNotNull(); + assertThat(a.appointmentEto().dateTime()).isEqualTo(dateTime); + assertThat(a.appointmentEto().status()).isEqualTo(AppointmentStatus.SCHEDULED); + assertThat(a.clientEto().id()).isEqualTo(clientId); + assertThat(a.treatmentCto().specialistEto().id()).isEqualTo(specialistId); + assertThat(a.treatmentCto().treatmentEto().id()).isEqualTo(treatmentId); + }); + + } + + @Test + void shouldThrowConstraintViolationExceptionDuringAppointmentBooking() { + // given + Long clientId = null; + Long specialistId = -2L; + Long treatmentId = -3L; + Instant dateTime = toInstant("2024-03-03 14:00:00"); + + AppointmentBookingEto appointmentBookingEto = AppointmentBookingEto + .builder() + .clientId(clientId) + .specialistId(specialistId) + .treatmentId(treatmentId) + .dateTime(dateTime) + .build(); + + // then + assertThatThrownBy(() -> manageAppointmentUc.bookAppointment(appointmentBookingEto)) + .isInstanceOf(ConstraintViolationException.class) + .hasMessageContaining("bookAppointment.appointmentBookingEto.clientId: must not be null"); + } + + @Test + void shouldThrowAnExceptionDuringAppointmentBooking() { + // given + Long specialistId = -3L; + Instant dateTime = toInstant("2024-03-03 14:00:00"); + + AppointmentBookingEto appointmentBookingEto = AppointmentBookingEto + .builder() + .specialistId(specialistId) + .dateTime(dateTime) + .clientId(-1L) + .treatmentId(-1L) + .build(); + + // when & then + assertThatThrownBy(() -> manageAppointmentUc.bookAppointment(appointmentBookingEto)) + .isInstanceOf(ConflictedAppointmentException.class) + .hasMessageContaining("The appointment conflicts with another scheduled appointment."); + } + + @Test + void shouldAppointmentStatus() { + // given + Long appointmentId = -1L; + AppointmentStatus status = AppointmentStatus.COMPLETED; + + // when + manageAppointmentUc.updateAppointmentStatus(appointmentId, status); + + // then + Optional appointmentCto = findAppointmentUc.findById(appointmentId); + assertThat(appointmentCto).isPresent().hasValueSatisfying(a -> { + assertThat(a.appointmentEto().status()).isEqualTo(AppointmentStatus.COMPLETED); + }); + } + +} \ No newline at end of file diff --git a/src/test/java/com/capgemini/training/appointmentbooking/logic/ManageTreatmentUcTestIT.java b/src/test/java/com/capgemini/training/appointmentbooking/logic/ManageTreatmentUcTestIT.java new file mode 100644 index 0000000..f8116d9 --- /dev/null +++ b/src/test/java/com/capgemini/training/appointmentbooking/logic/ManageTreatmentUcTestIT.java @@ -0,0 +1,54 @@ +package com.capgemini.training.appointmentbooking.logic; + +import com.capgemini.training.appointmentbooking.common.BaseTest; +import com.capgemini.training.appointmentbooking.common.to.TreatmentCreationTo; +import com.capgemini.training.appointmentbooking.common.to.TreatmentCto; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + + +@Transactional +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +class ManageTreatmentUcTestIT extends BaseTest { + + @Inject + private FindTreatmentUc findTreatmentUc; + + @Inject + private ManageTreatmentUc manageTreatmentUc; + + @Test + void shouldCreateTreatment() { + // given + String name = "Wypełnianie ubytków"; + String description = "Wypełnianie ubytków próchnicowych w zębach za pomocą materiałów kompozytowych"; + int durationMinutes = 60; + Long specialistId = -1L; + + TreatmentCreationTo treatmentCreationTo = TreatmentCreationTo + .builder() + .name(name) + .description(description) + .durationMinutes(durationMinutes) + .specialistId(specialistId) + .build(); + + // when + TreatmentCto result = manageTreatmentUc.createTreatment(treatmentCreationTo); + + // then + Optional appointmentCto = findTreatmentUc.findById(result.treatmentEto().id()); + assertThat(appointmentCto).isPresent().hasValueSatisfying(a -> { + assertThat(a.treatmentEto().id()).isNotNull(); + assertThat(a.treatmentEto().name()).isEqualTo(name); + assertThat(a.treatmentEto().description()).isEqualTo(description); + assertThat(a.treatmentEto().durationMinutes()).isEqualTo(durationMinutes); + assertThat(a.specialistEto().id()).isEqualTo(specialistId); + }); + } + +} \ No newline at end of file diff --git a/src/test/java/com/capgemini/training/appointmentbooking/logic/impl/FindAppointmentUcImplTest.java b/src/test/java/com/capgemini/training/appointmentbooking/logic/impl/FindAppointmentUcImplTest.java new file mode 100644 index 0000000..11991de --- /dev/null +++ b/src/test/java/com/capgemini/training/appointmentbooking/logic/impl/FindAppointmentUcImplTest.java @@ -0,0 +1,111 @@ +package com.capgemini.training.appointmentbooking.logic.impl; + +import com.capgemini.training.appointmentbooking.common.BaseTest; +import com.capgemini.training.appointmentbooking.common.datatype.AppointmentStatus; +import com.capgemini.training.appointmentbooking.common.to.AppointmentCto; +import com.capgemini.training.appointmentbooking.dataaccess.entity.AppointmentEntity; +import com.capgemini.training.appointmentbooking.dataaccess.repository.AppointmentRepository; +import com.capgemini.training.appointmentbooking.dataaccess.repository.criteria.AppointmentCriteria; +import com.capgemini.training.appointmentbooking.logic.mapper.AppointmentCtoMapper; +import com.capgemini.training.appointmentbooking.logic.mapper.AppointmentMapper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mapstruct.factory.Mappers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + + +@ExtendWith(MockitoExtension.class) +class FindAppointmentUcImplTest extends BaseTest { + + @Mock + private AppointmentRepository appointmentRepository; + + @InjectMocks + private FindAppointmentUcImpl findAppointmentUc; + + @Spy + private static AppointmentMapper appointmentMapper = Mappers.getMapper(AppointmentMapper.class); + + @Spy + private static AppointmentCtoMapper appointmentCtoMapper = Mappers.getMapper(AppointmentCtoMapper.class); + + @Test + void shouldFindById() { + // given + Long appointmentId = -1L; + Instant dateTime = toInstant("2024-03-01 09:00:00"); + AppointmentStatus status = AppointmentStatus.SCHEDULED; + AppointmentEntity appointmentEntity = new AppointmentEntity(); + appointmentEntity.setId(appointmentId); + appointmentEntity.setDateTime(dateTime); + appointmentEntity.setStatus(status); + when(appointmentRepository.findById(appointmentId)).thenReturn(Optional.of(appointmentEntity)); + + // when + Optional appointment = findAppointmentUc.findById(appointmentId); + + // then + assertThat(appointment).isPresent(); + appointment.ifPresent(a -> { + assertThat(a.appointmentEto().id()).isEqualTo(appointmentId); + assertThat(a.appointmentEto().dateTime()).isEqualTo(dateTime); + assertThat(a.appointmentEto().status()).isEqualTo(AppointmentStatus.SCHEDULED); + }); + } + + @Test + void shouldFindAppointmentsByCriteria() { + // given + AppointmentCriteria criteria = AppointmentCriteria.builder().treatmentName("Konsultacja dentystyczna").build(); + AppointmentEntity appointmentEntity = new AppointmentEntity(); + appointmentEntity.setStatus(AppointmentStatus.COMPLETED); + when(appointmentRepository.findByCriteria(criteria)).thenReturn(List.of(appointmentEntity, appointmentEntity, appointmentEntity)); + + // when + List result = findAppointmentUc.findByCriteria(criteria); + + // then + assertThat(result).hasSize(3); + assertThat(result).allMatch(appointment -> appointment.appointmentEto().status() == AppointmentStatus.COMPLETED); + } + + @Test + void shouldFindAllAppointments() { + // given + AppointmentEntity appointmentEntity = new AppointmentEntity(); + appointmentEntity.setStatus(AppointmentStatus.COMPLETED); + when(appointmentRepository.findAll()).thenReturn(List.of(appointmentEntity, appointmentEntity, appointmentEntity)); + + // when + List result = findAppointmentUc.findAll(); + + // then + assertThat(result).hasSize(3); + assertThat(result).allMatch(appointment -> appointment.appointmentEto().status() == AppointmentStatus.COMPLETED); + } + + @Test + void shouldFindConflictedAppointment() { + // given + Long specialistId = -1L; + Instant dateTime = toInstant("2024-03-01 09:00:00"); + when(appointmentRepository.hasConflictingAppointmentBySpecialistIdAndDateTime(any(), any())).thenReturn(true); + + // when + boolean hasConflictingAppointment = findAppointmentUc.hasConflictingAppointment(specialistId, dateTime); + + // then + assertThat(hasConflictingAppointment).isTrue(); + } + +} \ No newline at end of file diff --git a/src/test/java/com/capgemini/training/appointmentbooking/logic/impl/ManageAppointmentUcImplTest.java b/src/test/java/com/capgemini/training/appointmentbooking/logic/impl/ManageAppointmentUcImplTest.java new file mode 100644 index 0000000..da1153a --- /dev/null +++ b/src/test/java/com/capgemini/training/appointmentbooking/logic/impl/ManageAppointmentUcImplTest.java @@ -0,0 +1,156 @@ +package com.capgemini.training.appointmentbooking.logic.impl; + +import com.capgemini.training.appointmentbooking.common.BaseTest; +import com.capgemini.training.appointmentbooking.common.datatype.AppointmentStatus; +import com.capgemini.training.appointmentbooking.common.exception.ConflictedAppointmentException; +import com.capgemini.training.appointmentbooking.common.to.AppointmentBookingEto; +import com.capgemini.training.appointmentbooking.common.to.AppointmentCto; +import com.capgemini.training.appointmentbooking.common.to.AppointmentEto; +import com.capgemini.training.appointmentbooking.dataaccess.entity.AppointmentEntity; +import com.capgemini.training.appointmentbooking.dataaccess.entity.ClientEntity; +import com.capgemini.training.appointmentbooking.dataaccess.entity.SpecialistEntity; +import com.capgemini.training.appointmentbooking.dataaccess.entity.TreatmentEntity; +import com.capgemini.training.appointmentbooking.dataaccess.repository.AppointmentRepository; +import com.capgemini.training.appointmentbooking.dataaccess.repository.ClientRepository; +import com.capgemini.training.appointmentbooking.dataaccess.repository.TreatmentRepository; +import com.capgemini.training.appointmentbooking.logic.mapper.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mapstruct.factory.Mappers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.Instant; +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + + +@ExtendWith(MockitoExtension.class) +class ManageAppointmentUcImplTest extends BaseTest { + + @Mock + private AppointmentRepository appointmentRepository; + + @Mock + private ClientRepository clientRepository; + + @Mock + private TreatmentRepository treatmentRepository; + + @Mock + private FindAppointmentUcImpl findAppointmentUc; + + @InjectMocks + private ManageAppointmentUcImpl manageAppointmentUc; + + @Spy + private static AppointmentMapper appointmentMapper = Mappers.getMapper(AppointmentMapper.class); + + @Spy + private static AppointmentCtoMapper appointmentCtoMapper = Mappers.getMapper(AppointmentCtoMapper.class); + + @Spy + private static TreatmentMapper treatmentMapper = Mappers.getMapper(TreatmentMapper.class); + + @Spy + private static TreatmentCtoMapper treatmentCtoMapper = Mappers.getMapper(TreatmentCtoMapper.class); + + @Spy + private static SpecialistMapper specialistMapper = Mappers.getMapper(SpecialistMapper.class); + + @Spy + private static ClientMapper clientMapper = Mappers.getMapper(ClientMapper.class); + + @Test + void shouldBookAppointment() { + // given + Long clientId = -1L; + Long specialistId = -2L; + Long treatmentId = -3L; + Instant dateTime = toInstant("2024-03-03 14:00:00"); + AppointmentStatus status = AppointmentStatus.SCHEDULED; + + AppointmentBookingEto appointmentBookingEto = AppointmentBookingEto + .builder() + .clientId(clientId) + .specialistId(specialistId) + .treatmentId(treatmentId) + .dateTime(dateTime) + .build(); + + AppointmentEntity appointmentEntity = buildAppointmentEntity(clientId, specialistId, treatmentId, dateTime, status); + + when(findAppointmentUc.hasConflictingAppointment(specialistId, dateTime)).thenReturn(false); + when(clientRepository.getReferenceById(appointmentBookingEto.clientId())).thenReturn(new ClientEntity()); + when(treatmentRepository.getReferenceById(appointmentBookingEto.treatmentId())).thenReturn((new TreatmentEntity())); + when(appointmentRepository.saveAndFlush(any())).thenReturn(appointmentEntity); + + // when + AppointmentCto result = manageAppointmentUc.bookAppointment(appointmentBookingEto); + + // then + assertThat(result.clientEto().id()).isEqualTo(clientId); + assertThat(result.treatmentCto().specialistEto().id()).isEqualTo(specialistId); + assertThat(result.treatmentCto().treatmentEto().id()).isEqualTo(treatmentId); + assertThat(result.appointmentEto().dateTime()).isEqualTo(dateTime); + assertThat(result.appointmentEto().status()).isEqualTo(status); + } + + @Test + void shouldThrowAnExceptionDuringAppointmentBooking() { + // given + Long specialistId = -3L; + Instant dateTime = toInstant("2024-03-03 14:00:00"); + + AppointmentBookingEto appointmentBookingEto = AppointmentBookingEto + .builder() + .specialistId(specialistId) + .dateTime(dateTime) + .build(); + + when(findAppointmentUc.hasConflictingAppointment(specialistId, dateTime)).thenReturn(true); + + // when & then + assertThatThrownBy(() -> manageAppointmentUc.bookAppointment(appointmentBookingEto)) + .isInstanceOf(ConflictedAppointmentException.class) + .hasMessageContaining("The appointment conflicts with another scheduled appointment."); + } + + private static AppointmentEntity buildAppointmentEntity(Long clientId, Long specialistId, Long treatmentId, Instant dateTime, AppointmentStatus status) { + AppointmentEntity appointmentEntity = new AppointmentEntity(); + ClientEntity clientEntity = new ClientEntity(); + clientEntity.setId(clientId); + appointmentEntity.setClient(clientEntity); + SpecialistEntity specialistEntity = new SpecialistEntity(); + specialistEntity.setId(specialistId); + TreatmentEntity treatmentEntity = new TreatmentEntity(); + treatmentEntity.setId(treatmentId); + treatmentEntity.setSpecialist(specialistEntity); + appointmentEntity.setTreatment(treatmentEntity); + appointmentEntity.setDateTime(dateTime); + appointmentEntity.setStatus(status); + return appointmentEntity; + } + + @Test + void shouldUpdateAppointmentStatus() { + // given + Long appointmentId = -1L; + AppointmentStatus status = AppointmentStatus.COMPLETED; + when(findAppointmentUc.findById(appointmentId)).thenReturn(Optional.of(AppointmentCto.builder().build())); + AppointmentEntity appointmentEntity = new AppointmentEntity(); + appointmentEntity.setStatus(status); + when(appointmentRepository.saveAndFlush(any())).thenReturn(appointmentEntity); + + // when + AppointmentEto result = manageAppointmentUc.updateAppointmentStatus(appointmentId, status); + + // then + assertThat(result.status()).isEqualTo(status); + } + +} \ No newline at end of file