Skip to content

Commit

Permalink
fix: detach entities when converting to dto
Browse files Browse the repository at this point in the history
  • Loading branch information
bbortt committed May 8, 2024
1 parent 50e43f5 commit f93073f
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.citrusframework.simulator.service;

import jakarta.persistence.EntityManager;
import jakarta.persistence.criteria.JoinType;
import org.citrusframework.simulator.model.MessageHeader;
import org.citrusframework.simulator.model.MessageHeader_;
Expand Down Expand Up @@ -44,9 +45,11 @@ public class MessageHeaderQueryService extends QueryService<MessageHeader> {

private static final Logger logger = LoggerFactory.getLogger(MessageHeaderQueryService.class);

private final EntityManager entityManager;
private final MessageHeaderRepository messageHeaderRepository;

public MessageHeaderQueryService(MessageHeaderRepository messageHeaderRepository) {
public MessageHeaderQueryService(EntityManager entityManager, MessageHeaderRepository messageHeaderRepository) {
this.entityManager = entityManager;
this.messageHeaderRepository = messageHeaderRepository;
}

Expand Down Expand Up @@ -75,7 +78,7 @@ public Page<MessageHeader> findByCriteria(MessageHeaderCriteria criteria, Pageab
logger.debug("find by criteria : {}, page: {}", criteria, page);
final Specification<MessageHeader> specification = createSpecification(criteria);
return messageHeaderRepository.findAll(specification, page)
.map(MessageHeaderService::restrictToDtoProperties);
.map(messageHeader -> MessageHeaderService.restrictToDtoProperties(messageHeader, entityManager));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@

package org.citrusframework.simulator.service;

import jakarta.persistence.EntityManager;
import org.citrusframework.simulator.model.Message;
import org.citrusframework.simulator.model.MessageHeader;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.Optional;

import static java.util.Objects.nonNull;

/**
* Service Interface for managing {@link MessageHeader}.
*/
Expand Down Expand Up @@ -58,11 +61,21 @@ public interface MessageHeaderService {
* relationships, because of a possible {@link org.hibernate.LazyInitializationException}).
*
* @param messageHeader The entity, which should be returned
* @param entityManager Entity manager that is currently managing the entity
* @return the entity with prepared {@link Message}
*/
static MessageHeader restrictToDtoProperties(MessageHeader messageHeader) {
Message message = messageHeader.getMessage();
messageHeader.setMessage(Message.builder().messageId(message.getMessageId()).citrusMessageId(message.getCitrusMessageId()).build());
static MessageHeader restrictToDtoProperties(MessageHeader messageHeader, EntityManager entityManager) {
var message = messageHeader.getMessage();

if (nonNull(message)) {
entityManager.detach(messageHeader);
messageHeader.setMessage(
Message.builder()
.messageId(message.getMessageId())
.citrusMessageId(message.getCitrusMessageId())
.build());
}

return messageHeader;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.citrusframework.simulator.service;

import jakarta.annotation.Nullable;
import jakarta.persistence.EntityManager;
import org.citrusframework.TestAction;
import org.citrusframework.TestCase;
import org.citrusframework.simulator.model.ScenarioAction;
Expand Down Expand Up @@ -86,13 +87,21 @@ public interface ScenarioActionService {
* because of a possible {@link org.hibernate.LazyInitializationException}).
*
* @param scenarioAction The entity, which should be returned
* @param entityManager Entity manager that is currently managing the entity
* @return the entity with prepared {@link ScenarioExecution}
*/
static ScenarioAction restrictToDtoProperties(ScenarioAction scenarioAction) {
static ScenarioAction restrictToDtoProperties(ScenarioAction scenarioAction, EntityManager entityManager) {
ScenarioExecution scenarioExecution = scenarioAction.getScenarioExecution();

if (nonNull(scenarioExecution)) {
scenarioAction.setScenarioExecution(ScenarioExecution.builder().executionId(scenarioExecution.getExecutionId()).scenarioName(scenarioExecution.getScenarioName()).build());
entityManager.detach(scenarioAction);
scenarioAction.setScenarioExecution(
ScenarioExecution.builder()
.executionId(scenarioExecution.getExecutionId())
.scenarioName(scenarioExecution.getScenarioName())
.build());
}

return scenarioAction;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.citrusframework.simulator.service.impl;

import jakarta.persistence.EntityManager;
import org.citrusframework.simulator.model.MessageHeader;
import org.citrusframework.simulator.repository.MessageHeaderRepository;
import org.citrusframework.simulator.service.MessageHeaderService;
Expand All @@ -37,9 +38,11 @@ public class MessageHeaderServiceImpl implements MessageHeaderService {

private static final Logger logger = LoggerFactory.getLogger(MessageHeaderServiceImpl.class);

private final EntityManager entityManager;
private final MessageHeaderRepository messageHeaderRepository;

public MessageHeaderServiceImpl(MessageHeaderRepository messageHeaderRepository) {
public MessageHeaderServiceImpl(EntityManager entityManager, MessageHeaderRepository messageHeaderRepository) {
this.entityManager = entityManager;
this.messageHeaderRepository = messageHeaderRepository;
}

Expand All @@ -54,14 +57,14 @@ public MessageHeader save(MessageHeader messageHeader) {
public Page<MessageHeader> findAll(Pageable pageable) {
logger.debug("Request to get all MessageHeaders with eager relationships");
return messageHeaderRepository.findAllWithEagerRelationships(pageable)
.map(MessageHeaderService::restrictToDtoProperties);
.map(messageHeader -> MessageHeaderService.restrictToDtoProperties(messageHeader, entityManager));
}

@Override
@Transactional(readOnly = true)
public Optional<MessageHeader> findOne(Long headerId) {
logger.debug("Request to get MessageHeader : {}", headerId);
return messageHeaderRepository.findOneWithEagerRelationships(headerId)
.map(MessageHeaderService::restrictToDtoProperties);
.map(messageHeader -> MessageHeaderService.restrictToDtoProperties(messageHeader, entityManager));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.citrusframework.simulator.service.impl;

import jakarta.annotation.Nullable;
import jakarta.persistence.EntityManager;
import org.apache.commons.lang3.StringUtils;
import org.citrusframework.TestAction;
import org.citrusframework.TestCase;
Expand Down Expand Up @@ -52,10 +53,12 @@ public class ScenarioActionServiceImpl implements ScenarioActionService {

private final TimeProvider timeProvider = new TimeProvider();

private final EntityManager entityManager;
private final ScenarioActionRepository scenarioActionRepository;
private final ScenarioExecutionService scenarioExecutionService;

public ScenarioActionServiceImpl(ScenarioActionRepository scenarioActionRepository, ScenarioExecutionService scenarioExecutionService) {
public ScenarioActionServiceImpl(EntityManager entityManager, ScenarioActionRepository scenarioActionRepository, ScenarioExecutionService scenarioExecutionService) {
this.entityManager = entityManager;
this.scenarioActionRepository = scenarioActionRepository;
this.scenarioExecutionService = scenarioExecutionService;
}
Expand All @@ -75,15 +78,15 @@ public ScenarioAction save(ScenarioAction scenarioAction) {
public Page<ScenarioAction> findAll(Pageable pageable) {
logger.debug("Request to get all ScenarioActions with eager relationships");
return scenarioActionRepository.findAllWithToOneRelationships(pageable)
.map(ScenarioActionService::restrictToDtoProperties);
.map(scenarioAction -> ScenarioActionService.restrictToDtoProperties(scenarioAction, entityManager));
}

@Override
@Transactional(readOnly = true)
public Optional<ScenarioAction> findOne(Long id) {
logger.debug("Request to get ScenarioAction with eager relationships: {}", id);
return scenarioActionRepository.findOneWithEagerRelationships(id)
.map(ScenarioActionService::restrictToDtoProperties);
.map(scenarioAction -> ScenarioActionService.restrictToDtoProperties(scenarioAction, entityManager));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package org.citrusframework.simulator.service;

import jakarta.persistence.EntityManager;
import org.citrusframework.simulator.model.Message;
import org.citrusframework.simulator.model.MessageHeader;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;

@ExtendWith({MockitoExtension.class})
class MessageHeaderServiceTest {

@Mock
private EntityManager entityManagerMock;

@Nested
class RestrictToDtoProperties {

@Test
void shouldFilterMessageDetails() {
var message = mock(Message.class);
doReturn(1234L).when(message).getMessageId();
doReturn("citrus-message-id").when(message).getCitrusMessageId();

var messageHeader = new MessageHeader();
messageHeader.setMessage(message);

var restrictedMessageHeader = MessageHeaderService.restrictToDtoProperties(messageHeader, entityManagerMock);

var restrictedMessage = restrictedMessageHeader.getMessage();
assertEquals(message.getMessageId(), restrictedMessage.getMessageId());
assertEquals(message.getCitrusMessageId(), restrictedMessage.getCitrusMessageId());

verify(message, never()).getHeaders();
verify(message, never()).getScenarioExecution();

verify(entityManagerMock).detach(messageHeader);
}

@Test
void shouldHandleNullMessage() {
var messageHeader = new MessageHeader();

var restrictedMessageHeader = MessageHeaderService.restrictToDtoProperties(messageHeader, entityManagerMock);

assertNull(restrictedMessageHeader.getMessage());

verifyNoInteractions(entityManagerMock);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.citrusframework.simulator.service;

import jakarta.persistence.EntityManager;
import org.citrusframework.simulator.model.ScenarioAction;
import org.citrusframework.simulator.model.ScenarioExecution;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;

@ExtendWith({MockitoExtension.class})
class ScenarioActionServiceTest {

@Mock
private EntityManager entityManagerMock;

@Nested
class RestrictToDtoProperties {

@Test
void shouldFilterMessageDetails() {
var scenarioExecution = mock(ScenarioExecution.class);
doReturn(1234L).when(scenarioExecution).getExecutionId();
doReturn("scenario-name").when(scenarioExecution).getScenarioName();

var scenarioAction = new ScenarioAction();
scenarioAction.setScenarioExecution(scenarioExecution);

var restrictedScenarioAction = ScenarioActionService.restrictToDtoProperties(scenarioAction, entityManagerMock);

var restrictedScenarioExecution = restrictedScenarioAction.getScenarioExecution();
assertEquals(scenarioExecution.getExecutionId(), restrictedScenarioExecution.getExecutionId());
assertEquals(scenarioExecution.getScenarioName(), restrictedScenarioExecution.getScenarioName());

verify(scenarioExecution, never()).getScenarioParameters();
verify(scenarioExecution, never()).getScenarioActions();
verify(scenarioExecution, never()).getScenarioMessages();

verify(entityManagerMock).detach(scenarioAction);
}

@Test
void shouldHandleNullMessage() {
var scenarioAction = new ScenarioAction();

var restrictedScenarioAction = ScenarioActionService.restrictToDtoProperties(scenarioAction, entityManagerMock);

assertNull(restrictedScenarioAction.getScenarioExecution());

verifyNoInteractions(entityManagerMock);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.citrusframework.simulator.service.impl;

import jakarta.persistence.EntityManager;
import org.citrusframework.simulator.model.Message;
import org.citrusframework.simulator.model.MessageHeader;
import org.citrusframework.simulator.repository.MessageHeaderRepository;
Expand All @@ -42,6 +43,9 @@
@ExtendWith(MockitoExtension.class)
class MessageHeaderServiceImplTest {

@Mock
private EntityManager entityManagerMock;

@Mock
private MessageHeaderRepository messageHeaderRepositoryMock;

Expand All @@ -59,7 +63,7 @@ void beforeEachSetup() {
messageHeader.setMessage(message);
messageHeaderWithMessage = spy(messageHeader);

fixture = new MessageHeaderServiceImpl(messageHeaderRepositoryMock);
fixture = new MessageHeaderServiceImpl(entityManagerMock, messageHeaderRepositoryMock);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.citrusframework.simulator.service.impl;

import jakarta.persistence.EntityManager;
import org.citrusframework.TestAction;
import org.citrusframework.TestCase;
import org.citrusframework.exceptions.CitrusRuntimeException;
Expand Down Expand Up @@ -47,7 +48,6 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentCaptor.captor;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doReturn;
Expand All @@ -60,6 +60,9 @@
@ExtendWith(MockitoExtension.class)
class ScenarioActionServiceImplTest {

@Mock
private EntityManager entityManagerMock;

@Mock
private ScenarioActionRepository scenarioActionRepositoryMock;

Expand Down Expand Up @@ -87,7 +90,7 @@ private static TestAction getTestActionWithName(String toBeReturned) {
}

@BeforeEach
void beforeEachSetup() {
void beforeEachSetup() {
ScenarioAction scenarioAction = new ScenarioAction();
ScenarioExecution scenarioExecution = ScenarioExecution.builder()
.executionId(1234L)
Expand All @@ -99,7 +102,7 @@ void beforeEachSetup() {
scenarioAction.setScenarioExecution(scenarioExecution);
scenarioActionWithScenarioExecution = spy(scenarioAction);

fixture = new ScenarioActionServiceImpl(scenarioActionRepositoryMock, scenarioExecutionServiceMock);
fixture = new ScenarioActionServiceImpl(entityManagerMock, scenarioActionRepositoryMock, scenarioExecutionServiceMock);
ReflectionTestUtils.setField(fixture, "timeProvider", timeProviderMock, TimeProvider.class);
}

Expand Down

0 comments on commit f93073f

Please sign in to comment.