DeclaredExperienceServiceImpl.java
package fr.avenirsesr.portfolio.student.progress.declared.experience.domain.service;
import static fr.avenirsesr.portfolio.common.validation.domain.constraints.FieldMaxLengths.*;
import static fr.avenirsesr.portfolio.common.validation.domain.utils.FieldValidationUtils.*;
import fr.avenirsesr.portfolio.association.domain.data.AssociationSearchResultData;
import fr.avenirsesr.portfolio.association.domain.model.Association;
import fr.avenirsesr.portfolio.association.domain.model.EAssociationContextType;
import fr.avenirsesr.portfolio.association.domain.model.EAssociationType;
import fr.avenirsesr.portfolio.association.domain.port.input.AssociationService;
import fr.avenirsesr.portfolio.association.domain.service.AssociationSearchHelper;
import fr.avenirsesr.portfolio.common.data.domain.model.AvenirsBaseModel;
import fr.avenirsesr.portfolio.common.data.domain.model.PageCriteria;
import fr.avenirsesr.portfolio.common.data.domain.model.PagedResult;
import fr.avenirsesr.portfolio.common.security.domain.exception.UserNotAuthorizedException;
import fr.avenirsesr.portfolio.shared.domain.port.input.LoggedInUserService;
import fr.avenirsesr.portfolio.student.progress.declared.experience.domain.data.DeclaredExperienceAssociationsData;
import fr.avenirsesr.portfolio.student.progress.declared.experience.domain.exception.DeclaredExperienceNotFoundException;
import fr.avenirsesr.portfolio.student.progress.declared.experience.domain.model.DeclaredExperience;
import fr.avenirsesr.portfolio.student.progress.declared.experience.domain.model.enums.EExperienceType;
import fr.avenirsesr.portfolio.student.progress.declared.experience.domain.port.input.DeclaredExperienceService;
import fr.avenirsesr.portfolio.student.progress.declared.experience.domain.port.output.repository.DeclaredExperienceRepository;
import fr.avenirsesr.portfolio.trace.domain.data.TraceAssociationData;
import fr.avenirsesr.portfolio.trace.domain.exception.TraceNotFoundException;
import fr.avenirsesr.portfolio.trace.domain.model.Trace;
import fr.avenirsesr.portfolio.trace.domain.port.input.TraceService;
import fr.avenirsesr.portfolio.user.domain.model.Student;
import fr.avenirsesr.portfolio.user.domain.port.input.StudentService;
import java.time.LocalDate;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequiredArgsConstructor
public class DeclaredExperienceServiceImpl implements DeclaredExperienceService {
private final LoggedInUserService loggedInUserService;
private final AssociationService associationService;
private final AssociationSearchHelper associationSearchHelper;
private final TraceService traceService;
private final DeclaredExperienceRepository experienceRepository;
private final StudentService studentService;
@Override
public DeclaredExperience create(
UUID studentId,
String title,
EExperienceType experienceType,
String organization,
String activitySector,
String location,
String description,
String sourceOfInformation,
String summary,
String externalLink,
LocalDate startDate,
LocalDate endDate) {
Student student = studentService.getStudentById(studentId);
return create(
student,
title,
experienceType,
organization,
activitySector,
location,
description,
sourceOfInformation,
summary,
externalLink,
startDate,
endDate);
}
@Override
public DeclaredExperience create(
String title,
EExperienceType experienceType,
String organization,
String activitySector,
String location,
String description,
String sourceOfInformation,
String summary,
String externalLink,
LocalDate startDate,
LocalDate endDate) {
return create(
loggedInUserService.getLoggedInStudent(),
title,
experienceType,
organization,
activitySector,
location,
description,
sourceOfInformation,
summary,
externalLink,
startDate,
endDate);
}
private DeclaredExperience create(
Student student,
String title,
EExperienceType experienceType,
String organization,
String activitySector,
String location,
String description,
String sourceOfInformation,
String summary,
String externalLink,
LocalDate startDate,
LocalDate endDate) {
log.info("DeclaredExperience creation for {}", student);
checkDeclaredExperienceDataValidity(
title,
organization,
activitySector,
location,
description,
sourceOfInformation,
summary,
externalLink,
startDate,
endDate);
var experience =
DeclaredExperience.create(
student,
title,
experienceType,
organization,
activitySector,
location,
description,
sourceOfInformation,
summary,
externalLink,
startDate,
endDate);
experience = experienceRepository.save(experience);
log.info("{} has been created", experience);
return experience;
}
@Override
public DeclaredExperience update(
UUID experienceId,
String title,
EExperienceType experienceType,
String organization,
String activitySector,
String location,
String description,
String sourceOfInformation,
String summary,
String externalLink,
LocalDate startDate,
LocalDate endDate) {
Student student = loggedInUserService.getLoggedInStudent();
log.info("Update experienceId {} by {}", experienceId, student);
var experience =
experienceRepository
.findById(experienceId)
.orElseThrow(DeclaredExperienceNotFoundException::new);
if (!experience.getStudent().equals(student)) {
throw new UserNotAuthorizedException();
}
checkDeclaredExperienceDataValidity(
title,
organization,
activitySector,
location,
description,
sourceOfInformation,
summary,
externalLink,
startDate,
endDate);
experience.setTitle(title);
experience.setExperienceType(experienceType);
experience.setOrganization(organization);
experience.setActivitySector(activitySector);
experience.setLocation(location);
experience.setDescription(description);
experience.setSourceOfInformation(sourceOfInformation);
experience.setSummary(summary);
experience.setExternalLink(externalLink);
experience.setStartDate(startDate);
experience.setEndDate(endDate);
experience = experienceRepository.save(experience);
log.info("{} updated successfully", experience);
return experience;
}
private void checkDeclaredExperienceDataValidity(
String title,
String organization,
String activitySector,
String location,
String description,
String sourceOfInformation,
String summary,
String externalLink,
LocalDate startDate,
LocalDate endDate) {
requireNotBlankAndMaxLength("title", title, TITLE_LENGTH);
requireNotBlankAndMaxLength("organization", organization, ORGANIZATION_LENGTH);
validateOptionalTextMaxLength("activitySector", activitySector, ACTIVITY_SECTOR_LENGTH);
validateOptionalTextMaxLength("location", location, LOCATION_LENGTH);
validateOptionalTextMaxLength(
"sourceOfInformation", sourceOfInformation, SOURCE_OF_INFORMATION_LENGTH);
validateOptionalTextMaxLength("description", description, DESCRIPTION_LENGTH);
validateOptionalTextMaxLength("summary", summary, SUMMARY_LENGTH);
requireNotNull("startDate", startDate);
validateDateOrder(startDate, endDate);
validateUrl(externalLink);
}
@Override
public void delete(List<UUID> experienceIds) {
Student student = loggedInUserService.getLoggedInStudent();
log.info("Deleting experienceIds {} by {}", experienceIds, student);
var experiences = experienceRepository.findAllById(experienceIds);
if (experiences.stream().anyMatch(experience -> !experience.getStudent().equals(student))) {
throw new UserNotAuthorizedException();
}
if (!new HashSet<>(experiences.stream().map(AvenirsBaseModel::getId).toList())
.containsAll(experienceIds)) {
throw new DeclaredExperienceNotFoundException();
}
associationService.deleteAllOf(experienceIds, DeclaredExperience.class);
experienceRepository.removeAllFromDatabase(experiences);
log.info("ExperienceIds {} successfully deleted", experienceIds);
}
@Override
public DeclaredExperience get(UUID experienceId) {
Student student = loggedInUserService.getLoggedInStudent();
log.info("Get experienceId {} by {}", experienceId, student);
var experience =
experienceRepository
.findById(experienceId)
.orElseThrow(DeclaredExperienceNotFoundException::new);
if (!experience.getStudent().equals(student)) {
throw new UserNotAuthorizedException();
}
return experience;
}
@Override
public PagedResult<DeclaredExperience> getView(PageCriteria pageCriteria) {
Student student = loggedInUserService.getLoggedInStudent();
log.info("Get experience view by {}", student);
return experienceRepository.findAllByStudent(student, pageCriteria);
}
@Override
public List<DeclaredExperience> findAllByIds(List<UUID> experienceIds) {
return experienceRepository.findAllById(experienceIds);
}
@Override
public PagedResult<DeclaredExperience> search(String keyword, PageCriteria pageCriteria) {
Student student = loggedInUserService.getLoggedInStudent();
return experienceRepository.findAllByStudent(student, pageCriteria, keyword);
}
private TraceAssociationData traceAssociationMapper(Association association, List<Trace> traces) {
return new TraceAssociationData(
association.getId(),
traces.stream()
.filter(t -> t.getId().equals(association.getId1()))
.findAny()
.orElseThrow(TraceNotFoundException::new));
}
@Override
public PagedResult<AssociationSearchResultData> searchDeclaredExperiencesForAssociation(
UUID excludeAssociatedWithElementId,
EAssociationContextType contextType,
String keyword,
PageCriteria pageCriteria) {
var experiences = search(keyword, pageCriteria);
if (contextType == null) {
return associationSearchHelper.searchForAssociation(
null,
null,
null,
null,
experiences,
AvenirsBaseModel::getId,
DeclaredExperience::getTitle,
de -> de.getExperienceType().name(),
de -> false);
}
EAssociationType associationType = getAssociationType(contextType);
return associationSearchHelper.searchForAssociation(
excludeAssociatedWithElementId,
contextType.toClass(),
associationType,
Association::getId2,
experiences,
AvenirsBaseModel::getId,
DeclaredExperience::getTitle,
de -> de.getExperienceType().name(),
de -> false);
}
@Override
public DeclaredExperienceAssociationsData getAssociations(UUID experienceId) {
var student = loggedInUserService.getLoggedInStudent();
var experience =
experienceRepository
.findById(experienceId)
.orElseThrow(DeclaredExperienceNotFoundException::new);
if (!experience.getStudent().equals(student)) {
throw new UserNotAuthorizedException();
}
var associations =
associationService.getAllOf(
experience.getId(),
DeclaredExperience.class,
EAssociationType.getAllBy(DeclaredExperience.class));
var traceAssociations =
associations.stream()
.filter(a -> a.getAssociationType() == EAssociationType.TRACE_DECLARED_EXPERIENCE)
.toList();
var traces =
traceService.findAllTracesById(associations.stream().map(Association::getId1).toList());
return new DeclaredExperienceAssociationsData(
traceAssociations.stream().map(a -> traceAssociationMapper(a, traces)).toList());
}
private EAssociationType getAssociationType(EAssociationContextType contextType) {
return switch (contextType) {
case TRACE -> EAssociationType.TRACE_DECLARED_EXPERIENCE;
case DECLARED_ACTIVITY, DECLARED_SKILL, DECLARED_EXPERIENCE ->
throw new UnsupportedOperationException();
};
}
}