Skip to content

Commit

Permalink
Merge pull request #2493 from pierrehenri-dauvergne/shanoir-issue#2482
Browse files Browse the repository at this point in the history
shanoir-issue#2482: call subject delete in preclinical and datasets at the same time
  • Loading branch information
julien-louis authored Dec 2, 2024
2 parents 2efa56a + 3a5c35c commit 73d8150
Show file tree
Hide file tree
Showing 13 changed files with 58 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -312,21 +312,15 @@ public void createDatasetAcquisition(final String studyStr) {


/**
* Receives a shanoirEvent as a json object, concerning a subject deletion
* @param eventAsString the task as a json string.
*/
@RabbitListener(bindings = @QueueBinding(
key = ShanoirEventType.DELETE_SUBJECT_EVENT,
value = @Queue(value = RabbitMQConfiguration.DELETE_SUBJECT_QUEUE, durable = "true"),
exchange = @Exchange(value = RabbitMQConfiguration.EVENTS_EXCHANGE, ignoreDeclarationExceptions = "true",
autoDelete = "false", durable = "true", type=ExchangeTypes.TOPIC)), containerFactory = "singleConsumerFactory"
)
* Receives a shanoirEvent as a json object, concerning a subject deletion
* @param subjectIdAsString a string of the subject's id
*/
@RabbitListener(queues = RabbitMQConfiguration.DELETE_SUBJECT_QUEUE, containerFactory = "singleConsumerFactory")
@Transactional
public void deleteSubject(String eventAsString) throws AmqpRejectAndDontRequeueException {
public void deleteSubject(String subjectIdAsString) throws AmqpRejectAndDontRequeueException {
SecurityContextUtil.initAuthenticationContext("ROLE_ADMIN");
try {
ShanoirEvent event = objectMapper.readValue(eventAsString, ShanoirEvent.class);
Long subjectId = Long.valueOf(event.getObjectId());
Long subjectId = Long.valueOf(subjectIdAsString);
Set<Long> studyIds = new HashSet<>();

// Inverse order to remove copied examination before its source (if copied)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,10 @@ public void deleteById(final Long id, ShanoirEvent event) throws ShanoirExceptio
}
}
for (DatasetAcquisition dsAcq : dsAcqs) {
event.setMessage("Delete examination - acquisition with id : " + dsAcq.getId());
eventService.publishEvent(event);
if (event != null) {
event.setMessage("Delete examination - acquisition with id : " + dsAcq.getId());
eventService.publishEvent(event);
}
this.datasetAcquisitionService.deleteById(dsAcq.getId(), event);
}
if (event != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class StudyServiceImpl implements StudyService {

@Autowired
private DatasetRepository dsRepository;

@Override
public Study findById(final Long id) {
return repository.findById(id).orElse(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ export class SubjectStudyListComponent extends AbstractInput<SubjectStudy[]> imp
removeSubjectStudy(subjectStudy: SubjectStudy):void {
if (!this.warningDisplayed) {
this.confirmDialogService.confirm('Deleting subject',
'Warning: If this subject is only linked to this study, it will be completely deleted from the database.')
'Warning: If this subject is only linked to this study, it will be completely deleted from the database. This means each examination, acquisition and dataset of this subject will be deleted. Are you sure ?')
.then(userChoice => {
if (userChoice) {
this.removeSubjectStudyOk(subjectStudy);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,24 +313,26 @@ public static Queue studiesSubjectStudyStudyCardTagQueue() {
return new Queue(STUDIES_SUBJECT_STUDY_STUDY_CARD_TAG, true);
}

@Bean
public static Queue deleteSubjectQueue() { return new Queue(DELETE_SUBJECT_QUEUE, true); }

@Bean
public static Queue importerQueue() {
return new Queue(IMPORTER_QUEUE_DATASET, true);
}

@Bean
public FanoutExchange fanout() {
return new FanoutExchange(STUDY_USER_EXCHANGE, true, false);
return new FanoutExchange(STUDY_USER_EXCHANGE, true, false);
}

@Bean
public TopicExchange topicExchange() {
return new TopicExchange(EVENTS_EXCHANGE);
}

@Bean
public FanoutExchange fanoutSubjectExchange() {
return new FanoutExchange(STUDY_USER_EXCHANGE, true, false);
return new FanoutExchange(STUDY_USER_EXCHANGE, true, false);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import org.shanoir.ng.preclinical.pathologies.subject_pathologies.SubjectPathologyService;
import org.shanoir.ng.preclinical.subjects.model.AnimalSubject;
import org.shanoir.ng.preclinical.subjects.repository.AnimalSubjectRepository;
import org.shanoir.ng.preclinical.subjects.service.AnimalSubjectService;
import org.shanoir.ng.preclinical.therapies.subject_therapies.SubjectTherapyService;
import org.shanoir.ng.shared.configuration.RabbitMQConfiguration;
Expand All @@ -21,13 +20,9 @@
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Component
public class RabbitMQPreclinicalService {

Expand All @@ -36,7 +31,7 @@ public class RabbitMQPreclinicalService {
private ObjectMapper objectMapper;

@Autowired
private AnimalSubjectService subjectService;
private AnimalSubjectService animalSubjectService;

@Autowired
private SubjectPathologyService subjectPathologyService;
Expand All @@ -51,23 +46,16 @@ public class RabbitMQPreclinicalService {

/**
* Receives a shanoirEvent as a json object, concerning a subject deletion
* @param eventAsString the task as a json string.
* @param subjectIdAsStr the subject's id to delete, as string
*/
@RabbitListener(bindings = @QueueBinding(
key = ShanoirEventType.DELETE_SUBJECT_EVENT,
value = @Queue(value = RabbitMQConfiguration.DELETE_SUBJECT_QUEUE, durable = "true"),
exchange = @Exchange(value = RabbitMQConfiguration.EVENTS_EXCHANGE, ignoreDeclarationExceptions = "true",
autoDelete = "false", durable = "true", type= ExchangeTypes.TOPIC))
)
@RabbitListener(queues = RabbitMQConfiguration.DELETE_ANIMAL_SUBJECT_QUEUE)
@Transactional
public void deleteAnimalSubject(String eventAsString) throws AmqpRejectAndDontRequeueException {
public void deleteAnimalSubject(String subjectIdAsStr) throws AmqpRejectAndDontRequeueException {
SecurityContextUtil.initAuthenticationContext("ADMIN_ROLE");
try {
Long subjectId = Long.valueOf(subjectIdAsStr);

ShanoirEvent event = objectMapper.readValue(eventAsString, ShanoirEvent.class);
Long subjectId = Long.valueOf(event.getObjectId());

AnimalSubject animalSubject = subjectService.getBySubjectId(subjectId);
AnimalSubject animalSubject = animalSubjectService.getBySubjectId(subjectId);

if(animalSubject == null){
return;
Expand All @@ -76,12 +64,10 @@ public void deleteAnimalSubject(String eventAsString) throws AmqpRejectAndDontRe

subjectPathologyService.deleteByAnimalSubject(animalSubject);
subjectTherapyService.deleteByAnimalSubject(animalSubject);
subjectService.deleteBySubjectId(subjectId);
animalSubjectService.deleteBySubjectId(subjectId);

LOG.info("Animal subject [{}] has been deleted following deletion of subject [{}]", id, subjectId);

eventService.publishEvent(new ShanoirEvent(ShanoirEventType.DELETE_PRECLINICAL_SUBJECT_EVENT, subjectId.toString(), KeycloakUtil.getTokenUserId(), "", ShanoirEvent.SUCCESS));

} catch (Exception e) {
LOG.error("Something went wrong deserializing the event. {}", e.getMessage());
throw new AmqpRejectAndDontRequeueException(RABBIT_MQ_ERROR + e.getMessage(), e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,41 +190,6 @@ public Long createOrUpdateSubject(String subjectAsString) {
}
}

/**
* Receives a shanoirEvent as a json object, concerning a preclinical subject deletion
* @param eventAsString the task as a json string.
*/
@RabbitListener(bindings = @QueueBinding(
key = ShanoirEventType.DELETE_PRECLINICAL_SUBJECT_EVENT,
value = @Queue(value = RabbitMQConfiguration.DELETE_ANIMAL_SUBJECT_QUEUE, durable = "true"),
exchange = @Exchange(value = RabbitMQConfiguration.EVENTS_EXCHANGE, ignoreDeclarationExceptions = "true",
autoDelete = "false", durable = "true", type= ExchangeTypes.TOPIC)), containerFactory = "multipleConsumersFactory"
)

@Transactional
public void deletePreclinicalSubject(String eventAsString) throws AmqpRejectAndDontRequeueException {
SecurityContextUtil.initAuthenticationContext("ADMIN_ROLE");
try {

ShanoirEvent event = mapper.readValue(eventAsString, ShanoirEvent.class);

Long id = Long.valueOf(event.getObjectId());

Optional<Subject> subject = subjectRepository.findById(id);

if(subject.isEmpty() || !subject.get().isPreclinical()){
return;
}

subjectService.deleteById(subject.get().getId());
LOG.info("Subject [{}] has been deleted following deletion of preclinical subject [{}]", subject.get().getId(), id);


} catch (Exception e) {
LOG.error("Something went wrong deserializing the event", e);
throw new AmqpRejectAndDontRequeueException(e);
}
}

private boolean studyListContains(List<SubjectStudy> subjectStudyList, Long studyId) {
for (SubjectStudy sustu : subjectStudyList) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,11 +366,6 @@ public Study update(Study study) throws ShanoirException {
studyDb = studyRepository.save(studyDb);
}

// Actually delete subjects
for (Subject subjectToDelete : toBeDeleted) {
subjectService.deleteById(subjectToDelete.getId());
}

if (studyDb.getTags() != null) {
studyDb.getTags().removeIf(tag -> tagsToDelete.contains(tag.getId()));
studyDb = studyRepository.save(studyDb);
Expand All @@ -382,7 +377,12 @@ public Study update(Study study) throws ShanoirException {

String error = this.updateStudyName(studyMapper.studyToStudyDTODetailed(studyDb));

if(error != null && !error.isEmpty()){
// Actually delete subjects
for (Subject subjectToDelete : toBeDeleted) {
subjectService.deleteById(subjectToDelete.getId());
}

if (error != null && !error.isEmpty()) {
LOG.error("Study [" + studyDb.getId() + "] couldn't be sync with datasets microservice : {}", error);
throw new ShanoirException(error);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.List;

import org.shanoir.ng.subject.model.Subject;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
Expand All @@ -30,7 +31,11 @@ public interface StudyExaminationRepository extends CrudRepository<StudyExaminat

public Iterable<StudyExamination> findByCenterId(Long centerId);

public void deleteBySubject(Subject subject);
public void deleteBySubject(Subject subject);

@Modifying
@Query("DELETE FROM StudyExamination se WHERE se.subject.id = :subjectId")
public void deleteBySubjectId(Long subjectId);

int countByStudyId(@Param("studyId") Long studyId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

package org.shanoir.ng.subject.controler;


import org.shanoir.ng.shared.configuration.RabbitMQConfiguration;
import io.swagger.v3.oas.annotations.Parameter;
import org.shanoir.ng.shared.core.model.IdName;
import org.shanoir.ng.shared.error.FieldErrorMap;
Expand All @@ -30,6 +32,7 @@
import org.shanoir.ng.subject.service.SubjectService;
import org.shanoir.ng.subject.service.SubjectUniqueConstraintManager;
import org.shanoir.ng.utils.KeycloakUtil;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
Expand Down Expand Up @@ -63,13 +66,17 @@ public class SubjectApiController implements SubjectApi {
@Autowired
private StudyService studyService;

@Autowired
private RabbitTemplate rabbitTemplate;

@Override
public ResponseEntity<Void> deleteSubject(
@Parameter(description = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId) {
try {
// Delete all associated bids folders
subjectService.deleteById(subjectId);
eventService.publishEvent(new ShanoirEvent(ShanoirEventType.DELETE_SUBJECT_EVENT, subjectId.toString(), KeycloakUtil.getTokenUserId(), "", ShanoirEvent.SUCCESS));
rabbitTemplate.convertAndSend(RabbitMQConfiguration.DELETE_SUBJECT_QUEUE, subjectId.toString());

return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (EntityNotFoundException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,15 @@ public void deleteById(final Long id) throws EntityNotFoundException {
if (subject.isEmpty()) {
throw new EntityNotFoundException(Subject.class, id);
}

// Delete all associated study_examination
studyExaminationRepository.deleteBySubject(subject.get());
studyExaminationRepository.deleteBySubjectId(id);
subjectRepository.deleteById(id);
// Propagate deletion
eventService.publishEvent(new ShanoirEvent(ShanoirEventType.DELETE_SUBJECT_EVENT, id.toString(), KeycloakUtil.getTokenUserId(), "", ShanoirEvent.SUCCESS));
if (subject.get().isPreclinical())
rabbitTemplate.convertAndSend(RabbitMQConfiguration.DELETE_ANIMAL_SUBJECT_QUEUE, id.toString());

rabbitTemplate.convertAndSend(RabbitMQConfiguration.DELETE_SUBJECT_QUEUE, id.toString());

}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.shanoir.ng.shared.error.FieldErrorMap;
import org.shanoir.ng.shared.event.ShanoirEventService;
Expand All @@ -43,6 +44,7 @@
import org.shanoir.ng.subjectstudy.dto.SubjectStudyDTO;
import org.shanoir.ng.utils.ModelsUtil;
import org.shanoir.ng.utils.usermock.WithMockKeycloakUser;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
Expand Down Expand Up @@ -79,10 +81,13 @@ public class SubjectApiControllerTest {

@MockBean
private SubjectMapper subjectMapperMock;


@MockBean
private RabbitTemplate rabbitTemplate;

@MockBean
private SubjectUniqueConstraintManager uniqueConstraintManager;

@MockBean
private ShanoirEventService eventService;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,7 @@ public class SubjectServiceTest {

@Mock
private ObjectMapper objectMapper;

@Mock
private ShanoirEventService eventService;


@Mock
private StudyExaminationRepository studyExaminationRepository;
Expand All @@ -105,7 +103,6 @@ public void deleteByIdTest() throws EntityNotFoundException {
subjectService.deleteById(SUBJECT_ID);

Mockito.verify(subjectRepository, Mockito.times(1)).deleteById(Mockito.anyLong());
Mockito.verify(eventService, Mockito.times(1)).publishEvent(Mockito.any(ShanoirEvent.class));
}

@Test
Expand Down

0 comments on commit 73d8150

Please sign in to comment.