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