Feature add optional name to thing created (#888)

* First implementation pushed because of debugging purpose

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Add name field and tests regarding name field functionality in THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* SonarQube realted changes in name field functionality in THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Add name field and tests regarding name field functionality in UPDATE_ATTRIBUTES

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Adapt documentation due to name field in THING_CREATED and UPDATE_ATTRIBUTES

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Add integration tests regarding name field functionality in THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Reformat after finding format bug regarding THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Reformat after finding the real format bug regarding THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Reformat regarding THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Use constant in THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Format in THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Resolving peer review comments regarding THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Resolving peer review comments (organize imports) regarding THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Refactoring regarding THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Refactoring due to peer review

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Refactoring due to peer review

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Excluding UPDATE_ATTRIBUTES changes and provide functionality of updating the name property in THING_CREATED message

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Refactoring due to peer review

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Refactoring due to peer review

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Fix SonarQube finding

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Merge master into current branch

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Fix peer review findings

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>
This commit is contained in:
Ammar Bikic
2019-10-24 12:13:35 +02:00
committed by Dominic Schabel
parent b50a82fc0f
commit af0f7ceb5a
10 changed files with 525 additions and 388 deletions

View File

@@ -52,12 +52,22 @@ Message Properties | Description
content_type | The content type of the payload | String | true content_type | The content type of the payload | String | true
reply_to | Exchange to reply to. The default is sp.direct.exchange which is bound to the sp_direct_queue | String | false reply_to | Exchange to reply to. The default is sp.direct.exchange which is bound to the sp_direct_queue | String | false
Example headers Example headers and payload:
Header | MessageProperties Header | MessageProperties
----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------
type=THING\_CREATED <br /> tenant=tenant123 <br /> thingId=abc <br /> sender=Lwm2m | content\_type=application/json <br /> reply_to (optional) =sp.connector.replyTo type=THING\_CREATED <br /> tenant=tenant123 <br /> thingId=abc <br /> sender=Lwm2m | content\_type=application/json <br /> reply_to (optional) =sp.connector.replyTo
Payload Template (optional):
```json
{
"name": "String"
}
```
The "name" property specifies the name of the thing, which by default is the thing ID. This property is optional.
### THING_REMOVED ### THING_REMOVED

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.amqp;
import static org.eclipse.hawkbit.repository.RepositoryConstants.MAX_ACTION_COUNT; import static org.eclipse.hawkbit.repository.RepositoryConstants.MAX_ACTION_COUNT;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED; import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
import static org.springframework.util.StringUtils.hasText;
import java.io.Serializable; import java.io.Serializable;
import java.net.URI; import java.net.URI;
@@ -26,6 +27,7 @@ import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.amqp.api.MessageType; import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
import org.eclipse.hawkbit.dmf.json.model.DmfActionUpdateStatus; import org.eclipse.hawkbit.dmf.json.model.DmfActionUpdateStatus;
import org.eclipse.hawkbit.dmf.json.model.DmfAttributeUpdate; import org.eclipse.hawkbit.dmf.json.model.DmfAttributeUpdate;
import org.eclipse.hawkbit.dmf.json.model.DmfCreateThing;
import org.eclipse.hawkbit.dmf.json.model.DmfUpdateMode; import org.eclipse.hawkbit.dmf.json.model.DmfUpdateMode;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
@@ -51,6 +53,7 @@ import org.springframework.amqp.AmqpRejectAndDontRequeueException;
import org.springframework.amqp.core.Message; import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.MessageConversionException;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.messaging.handler.annotation.Header; import org.springframework.messaging.handler.annotation.Header;
import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.AnonymousAuthenticationToken;
@@ -84,6 +87,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
private static final String THING_ID_NULL = "ThingId is null"; private static final String THING_ID_NULL = "ThingId is null";
private static final String EMPTY_MESSAGE_BODY = "\"\"";
/** /**
* Constructor. * Constructor.
* *
@@ -122,7 +127,6 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
* the message type * the message type
* @param tenant * @param tenant
* the contentType of the message * the contentType of the message
*
* @return a message if <null> no message is send back to sender * @return a message if <null> no message is send back to sender
*/ */
@RabbitListener(queues = "${hawkbit.dmf.rabbitmq.receiverQueue:dmf_receiver}", containerFactory = "listenerContainerFactory") @RabbitListener(queues = "${hawkbit.dmf.rabbitmq.receiverQueue:dmf_receiver}", containerFactory = "listenerContainerFactory")
@@ -198,12 +202,14 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
} }
/** /**
* Method to create a new target or to find the target if it already exists. * Method to create a new target or to find the target if it already exists and
* update its poll time, status and optionally its name.
* *
* @param targetID * @param message
* the ID of the target/thing * the message that contains replyTo property and optionally the name
* @param ip * in body
* the ip of the target/thing * @param virtualHost
* the virtual host
*/ */
private void registerTarget(final Message message, final String virtualHost) { private void registerTarget(final Message message, final String virtualHost) {
final String thingId = getStringHeaderKey(message, MessageHeaderKey.THING_ID, THING_ID_NULL); final String thingId = getStringHeaderKey(message, MessageHeaderKey.THING_ID, THING_ID_NULL);
@@ -215,14 +221,29 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
try { try {
final URI amqpUri = IpUtil.createAmqpUri(virtualHost, replyTo); final URI amqpUri = IpUtil.createAmqpUri(virtualHost, replyTo);
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(thingId, amqpUri); final Target target;
if (isOptionalMessageBodyEmpty(message)) {
target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(thingId, amqpUri);
} else {
checkContentTypeJson(message);
target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(thingId, amqpUri, convertMessage(message, DmfCreateThing.class).getName());
}
LOG.debug("Target {} reported online state.", thingId); LOG.debug("Target {} reported online state.", thingId);
sendUpdateCommandToTarget(target); sendUpdateCommandToTarget(target);
} catch (EntityAlreadyExistsException e) { } catch (final EntityAlreadyExistsException e) {
throw new AmqpRejectAndDontRequeueException("Target already registered, message will be ignored!", e); throw new AmqpRejectAndDontRequeueException(
"Tried to register previously registered target, message will be ignored!", e);
} }
} }
private static boolean isOptionalMessageBodyEmpty(final Message message) {
// empty byte array message body is serialized to double-quoted string
// by message converter and should also be considered as empty
return isMessageBodyEmpty(message) || EMPTY_MESSAGE_BODY.equals(new String(message.getBody()));
}
private void sendUpdateCommandToTarget(final Target target) { private void sendUpdateCommandToTarget(final Target target) {
if (isMultiAssignmentsEnabled()) { if (isMultiAssignmentsEnabled()) {
sendCurrentActionsAsMultiActionToTarget(target); sendCurrentActionsAsMultiActionToTarget(target);

View File

@@ -69,7 +69,7 @@ public class BaseAmqpService {
return rabbitTemplate.getMessageConverter(); return rabbitTemplate.getMessageConverter();
} }
private static boolean isMessageBodyEmpty(final Message message) { protected static boolean isMessageBodyEmpty(final Message message) {
return message.getBody() == null || message.getBody().length == 0; return message.getBody() == null || message.getBody().length == 0;
} }

View File

@@ -35,6 +35,7 @@ import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
import org.eclipse.hawkbit.dmf.json.model.DmfActionStatus; import org.eclipse.hawkbit.dmf.json.model.DmfActionStatus;
import org.eclipse.hawkbit.dmf.json.model.DmfActionUpdateStatus; import org.eclipse.hawkbit.dmf.json.model.DmfActionUpdateStatus;
import org.eclipse.hawkbit.dmf.json.model.DmfAttributeUpdate; import org.eclipse.hawkbit.dmf.json.model.DmfAttributeUpdate;
import org.eclipse.hawkbit.dmf.json.model.DmfCreateThing;
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadResponse; import org.eclipse.hawkbit.dmf.json.model.DmfDownloadResponse;
import org.eclipse.hawkbit.dmf.json.model.DmfUpdateMode; import org.eclipse.hawkbit.dmf.json.model.DmfUpdateMode;
import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ArtifactManagement;
@@ -70,6 +71,7 @@ import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConversionException;
import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@@ -82,8 +84,8 @@ import io.qameta.allure.Story;
@Story("AmqpMessage Handler Service Test") @Story("AmqpMessage Handler Service Test")
public class AmqpMessageHandlerServiceTest { public class AmqpMessageHandlerServiceTest {
private static final String FAIL_MESSAGE_AMQP_REJECT_REASON = AmqpRejectAndDontRequeueException.class.getSimpleName() private static final String FAIL_MESSAGE_AMQP_REJECT_REASON = AmqpRejectAndDontRequeueException.class
+ " was expected, "; .getSimpleName() + " was expected, ";
private static final String SHA1 = "12345"; private static final String SHA1 = "12345";
private static final String VIRTUAL_HOST = "vHost"; private static final String VIRTUAL_HOST = "vHost";
@@ -136,6 +138,9 @@ public class AmqpMessageHandlerServiceTest {
@Captor @Captor
private ArgumentCaptor<String> targetIdCaptor; private ArgumentCaptor<String> targetIdCaptor;
@Captor
private ArgumentCaptor<URI> uriCaptor;
@Captor @Captor
private ArgumentCaptor<UpdateMode> modeCaptor; private ArgumentCaptor<UpdateMode> modeCaptor;
@@ -176,23 +181,60 @@ public class AmqpMessageHandlerServiceTest {
@Description("Tests the creation of a target/thing by calling the same method that incoming RabbitMQ messages would access.") @Description("Tests the creation of a target/thing by calling the same method that incoming RabbitMQ messages would access.")
public void createThing() { public void createThing() {
final String knownThingId = "1"; final String knownThingId = "1";
final MessageProperties messageProperties = createMessageProperties(MessageType.THING_CREATED);
messageProperties.setHeader(MessageHeaderKey.THING_ID, "1");
final Message message = messageConverter.toMessage(new byte[0], messageProperties);
final Target targetMock = mock(Target.class); final Target targetMock = mock(Target.class);
final ArgumentCaptor<String> targetIdCaptor = ArgumentCaptor.forClass(String.class); targetIdCaptor = ArgumentCaptor.forClass(String.class);
final ArgumentCaptor<URI> uriCaptor = ArgumentCaptor.forClass(URI.class); uriCaptor = ArgumentCaptor.forClass(URI.class);
when(controllerManagementMock.findOrRegisterTargetIfItDoesNotExist(targetIdCaptor.capture(), when(controllerManagementMock.findOrRegisterTargetIfItDoesNotExist(targetIdCaptor.capture(),
uriCaptor.capture())).thenReturn(targetMock); uriCaptor.capture())).thenReturn(targetMock);
when(controllerManagementMock.findOldestActiveActionByTarget(any())).thenReturn(Optional.empty()); when(controllerManagementMock.findOldestActiveActionByTarget(any())).thenReturn(Optional.empty());
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, VIRTUAL_HOST); amqpMessageHandlerService.onMessage(createMessage(new byte[0], getThingCreatedMessageProperties(knownThingId)),
MessageType.THING_CREATED.name(), TENANT, VIRTUAL_HOST);
// verify // verify
assertThat(targetIdCaptor.getValue()).as("Thing id is wrong").isEqualTo(knownThingId); assertThat(targetIdCaptor.getValue()).as("Thing id is wrong").isEqualTo(knownThingId);
assertThat(uriCaptor.getValue().toString()).as("Uri is not right").isEqualTo("amqp://"+VIRTUAL_HOST+"/MyTest"); assertThat(uriCaptor.getValue().toString()).as("Uri is not right")
.isEqualTo("amqp://" + VIRTUAL_HOST + "/MyTest");
}
@Test
@Description("Tests the creation of a target/thing with specified name by calling the same method that incoming RabbitMQ messages would access.")
public void createThingWithName() {
final String knownThingId = "2";
final DmfCreateThing targetProperties = new DmfCreateThing();
targetProperties.setName("NonDefaultTargetName");
final Target targetMock = mock(Target.class);
targetIdCaptor = ArgumentCaptor.forClass(String.class);
uriCaptor = ArgumentCaptor.forClass(URI.class);
final ArgumentCaptor<String> targetNameCaptor = ArgumentCaptor.forClass(String.class);
when(controllerManagementMock.findOrRegisterTargetIfItDoesNotExist(targetIdCaptor.capture(),
uriCaptor.capture(), targetNameCaptor.capture())).thenReturn(targetMock);
when(controllerManagementMock.findOldestActiveActionByTarget(any())).thenReturn(Optional.empty());
amqpMessageHandlerService.onMessage(
createMessage(targetProperties, getThingCreatedMessageProperties(knownThingId)),
MessageType.THING_CREATED.name(), TENANT, "vHost");
assertThat(targetIdCaptor.getValue()).as("Thing id is wrong").isEqualTo(knownThingId);
assertThat(uriCaptor.getValue().toString()).as("Uri is not right").isEqualTo("amqp://vHost/MyTest");
assertThat(targetNameCaptor.getValue()).as("Thing name is not right").isEqualTo(targetProperties.getName());
}
@Test
@Description("Tests not allowed body in message")
public void createThingWithWrongBody() {
final String knownThingId = "3";
assertThatExceptionOfType(MessageConversionException.class)
.as("MessageConversionException was excepeted due to wrong body")
.isThrownBy(() -> amqpMessageHandlerService.onMessage(
createMessage("Not allowed Body".getBytes(), getThingCreatedMessageProperties(knownThingId)),
MessageType.THING_CREATED.name(), TENANT, VIRTUAL_HOST));
} }
@Test @Test
@@ -308,15 +350,13 @@ public class AmqpMessageHandlerServiceTest {
final Message message = new Message(new byte[0], messageProperties); final Message message = new Message(new byte[0], messageProperties);
assertThatExceptionOfType(AmqpRejectAndDontRequeueException.class) assertThatExceptionOfType(AmqpRejectAndDontRequeueException.class)
.as(FAIL_MESSAGE_AMQP_REJECT_REASON + "due to unknown message type") .as(FAIL_MESSAGE_AMQP_REJECT_REASON + "due to unknown message type").isThrownBy(
.isThrownBy(() -> amqpMessageHandlerService.onMessage(message, "unknownMessageType", TENANT, () -> amqpMessageHandlerService.onMessage(message, "unknownMessageType", TENANT, VIRTUAL_HOST));
VIRTUAL_HOST));
messageProperties.setHeader(MessageHeaderKey.TOPIC, "wrongTopic"); messageProperties.setHeader(MessageHeaderKey.TOPIC, "wrongTopic");
assertThatExceptionOfType(AmqpRejectAndDontRequeueException.class) assertThatExceptionOfType(AmqpRejectAndDontRequeueException.class)
.as(FAIL_MESSAGE_AMQP_REJECT_REASON + "due to unknown topic") .as(FAIL_MESSAGE_AMQP_REJECT_REASON + "due to unknown topic").isThrownBy(() -> amqpMessageHandlerService
.isThrownBy(() -> amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, .onMessage(message, MessageType.EVENT.name(), TENANT, VIRTUAL_HOST));
VIRTUAL_HOST));
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.CANCEL_DOWNLOAD.name()); messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.CANCEL_DOWNLOAD.name());
assertThatExceptionOfType(AmqpRejectAndDontRequeueException.class) assertThatExceptionOfType(AmqpRejectAndDontRequeueException.class)
@@ -554,4 +594,13 @@ public class AmqpMessageHandlerServiceTest {
VIRTUAL_HOST)); VIRTUAL_HOST));
} }
private MessageProperties getThingCreatedMessageProperties(String thingId) {
final MessageProperties messageProperties = createMessageProperties(MessageType.THING_CREATED);
messageProperties.setHeader(MessageHeaderKey.THING_ID, thingId);
return messageProperties;
}
private Message createMessage(Object object, MessageProperties messageProperties) {
return messageConverter.toMessage(object, messageProperties);
}
} }

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2019 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.dmf.json.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* JSON representation of the Attribute THING_CREATED message.
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class DmfCreateThing {
@JsonProperty
private String name;
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
}

View File

@@ -46,8 +46,8 @@ import org.springframework.security.access.prepost.PreAuthorize;
public interface ControllerManagement { public interface ControllerManagement {
/** /**
* Adds an {@link ActionStatus} for a cancel {@link Action} including * Adds an {@link ActionStatus} for a cancel {@link Action} including potential
* potential state changes for the target and the {@link Action} itself. * state changes for the target and the {@link Action} itself.
* *
* @param create * @param create
* to be added * to be added
@@ -57,8 +57,8 @@ public interface ControllerManagement {
* if a given entity already exists * if a given entity already exists
* *
* @throws QuotaExceededException * @throws QuotaExceededException
* if more than the allowed number of status entries or messages * if more than the allowed number of status entries or messages per
* per entry are inserted * entry are inserted
* @throws EntityNotFoundException * @throws EntityNotFoundException
* if given action does not exist * if given action does not exist
* @throws ConstraintViolationException * @throws ConstraintViolationException
@@ -93,8 +93,8 @@ public interface ControllerManagement {
@NotNull Collection<Long> moduleId); @NotNull Collection<Long> moduleId);
/** /**
* Simple addition of a new {@link ActionStatus} entry to the {@link Action} * Simple addition of a new {@link ActionStatus} entry to the {@link Action} .
* . No state changes. * No state changes.
* *
* @param create * @param create
* to add to the action * to add to the action
@@ -102,8 +102,8 @@ public interface ControllerManagement {
* @return created {@link ActionStatus} entity * @return created {@link ActionStatus} entity
* *
* @throws QuotaExceededException * @throws QuotaExceededException
* if more than the allowed number of status entries or messages * if more than the allowed number of status entries or messages per
* per entry are inserted * entry are inserted
* @throws EntityNotFoundException * @throws EntityNotFoundException
* if given action does not exist * if given action does not exist
* @throws ConstraintViolationException * @throws ConstraintViolationException
@@ -124,8 +124,8 @@ public interface ControllerManagement {
* @throws EntityAlreadyExistsException * @throws EntityAlreadyExistsException
* if a given entity already exists * if a given entity already exists
* @throws QuotaExceededException * @throws QuotaExceededException
* if more than the allowed number of status entries or messages * if more than the allowed number of status entries or messages per
* per entry are inserted * entry are inserted
* @throws EntityNotFoundException * @throws EntityNotFoundException
* if action status not exist * if action status not exist
* @throws ConstraintViolationException * @throws ConstraintViolationException
@@ -140,7 +140,7 @@ public interface ControllerManagement {
* {@link Target}. * {@link Target}.
* *
* For performance reasons this method does not throw * For performance reasons this method does not throw
* {@link EntityNotFoundException} in case target with given controllerId * {@link EntityNotFoundException} in case target with given controlelrId
* does not exist but will return an {@link Optional#empty()} instead. * does not exist but will return an {@link Optional#empty()} instead.
* *
* @param controllerId * @param controllerId
@@ -152,8 +152,8 @@ public interface ControllerManagement {
Optional<Action> findOldestActiveActionByTarget(@NotEmpty String controllerId); Optional<Action> findOldestActiveActionByTarget(@NotEmpty String controllerId);
/** /**
* Retrieves all active actions which are assigned to the target with the * Retrieves all active actions which are assigned to the target with the given
* given controller ID. * controller ID.
* *
* @param pageable * @param pageable
* pagination parameter * pagination parameter
@@ -165,8 +165,7 @@ public interface ControllerManagement {
Page<Action> findActiveActionsByTarget(@NotNull Pageable pageable, @NotEmpty String controllerId); Page<Action> findActiveActionsByTarget(@NotNull Pageable pageable, @NotEmpty String controllerId);
/** /**
* Get the {@link Action} entity for given actionId with all lazy * Get the {@link Action} entity for given actionId with all lazy attributes.
* attributes.
* *
* @param actionId * @param actionId
* to be id of the action * to be id of the action
@@ -176,8 +175,7 @@ public interface ControllerManagement {
Optional<Action> findActionWithDetails(long actionId); Optional<Action> findActionWithDetails(long actionId);
/** /**
* Retrieves all the {@link ActionStatus} entries of the given * Retrieves all the {@link ActionStatus} entries of the given {@link Action}.
* {@link Action}.
* *
* @param pageReq * @param pageReq
* pagination parameter * pagination parameter
@@ -192,11 +190,10 @@ public interface ControllerManagement {
Page<ActionStatus> findActionStatusByAction(@NotNull Pageable pageReq, long actionId); Page<ActionStatus> findActionStatusByAction(@NotNull Pageable pageReq, long actionId);
/** /**
* Register new target in the repository (plug-and-play) and in case it * Register new target in the repository (plug-and-play) and in case it already
* already exists updates {@link Target#getAddress()} and * exists updates {@link Target#getAddress()} and
* {@link Target#getLastTargetQuery()} and switches if * {@link Target#getLastTargetQuery()} and switches if
* {@link TargetUpdateStatus#UNKNOWN} to * {@link TargetUpdateStatus#UNKNOWN} to {@link TargetUpdateStatus#REGISTERED}.
* {@link TargetUpdateStatus#REGISTERED}.
* *
* @param controllerId * @param controllerId
* reference * reference
@@ -208,14 +205,31 @@ public interface ControllerManagement {
Target findOrRegisterTargetIfItDoesNotExist(@NotEmpty String controllerId, @NotNull URI address); Target findOrRegisterTargetIfItDoesNotExist(@NotEmpty String controllerId, @NotNull URI address);
/** /**
* Retrieves last {@link Action} for a download of an artifact of given * Register new target in the repository (plug-and-play) and in case it already
* module and target if exists and is not canceled. * exists updates {@link Target#getAddress()} and
* {@link Target#getLastTargetQuery()} and {@link Target#getName()} and switches if
* {@link TargetUpdateStatus#UNKNOWN} to {@link TargetUpdateStatus#REGISTERED}.
*
* @param controllerId
* reference
* @param address
* the client IP address of the target, might be {@code null}
* @param name
* the name of the target
* @return target reference
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Target findOrRegisterTargetIfItDoesNotExist(@NotEmpty String controllerId, @NotNull URI address, String name);
/**
* Retrieves last {@link Action} for a download of an artifact of given module
* and target if exists and is not canceled.
* *
* @param controllerId * @param controllerId
* to look for * to look for
* @param moduleId * @param moduleId
* of the the {@link SoftwareModule} that should be assigned to * of the the {@link SoftwareModule} that should be assigned to the
* the target * target
* @return last {@link Action} for given combination * @return last {@link Action} for given combination
* *
* @throws EntityNotFoundException * @throws EntityNotFoundException
@@ -253,18 +267,18 @@ public interface ControllerManagement {
int getMaintenanceWindowPollCount(); int getMaintenanceWindowPollCount();
/** /**
* Returns polling time based on the maintenance window for an action. * Returns polling time based on the maintenance window for an action. Server
* Server will reduce the polling interval as the start time for maintenance * will reduce the polling interval as the start time for maintenance window
* window approaches, so that at least these many attempts are made between * approaches, so that at least these many attempts are made between current
* current polling until start of maintenance window. Poll time keeps * polling until start of maintenance window. Poll time keeps reducing with
* reducing with MinPollingTime as lower limit * MinPollingTime as lower limit
* {@link TenantConfigurationKey#MIN_POLLING_TIME_INTERVAL}. After the start * {@link TenantConfigurationKey#MIN_POLLING_TIME_INTERVAL}. After the start of
* of maintenance window, it resets to default * maintenance window, it resets to default
* {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}. * {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
* *
* @param actionId * @param actionId
* id the {@link Action} for which polling time is calculated * id the {@link Action} for which polling time is calculated based
* based on it having maintenance window or not * on it having maintenance window or not
* *
* @return current {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}. * @return current {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
*/ */
@@ -272,20 +286,20 @@ public interface ControllerManagement {
String getPollingTimeForAction(long actionId); String getPollingTimeForAction(long actionId);
/** /**
* Checks if a given target has currently or has even been assigned to the * Checks if a given target has currently or has even been assigned to the given
* given artifact through the action history list. This can e.g. indicate if * artifact through the action history list. This can e.g. indicate if a target
* a target is allowed to download a given artifact because it has currently * is allowed to download a given artifact because it has currently assigned or
* assigned or had ever been assigned to the target and so it's visible to a * had ever been assigned to the target and so it's visible to a specific target
* specific target e.g. for downloading. * e.g. for downloading.
* *
* @param controllerId * @param controllerId
* the ID of the target to check * the ID of the target to check
* @param sha1Hash * @param sha1Hash
* of the artifact to verify if the given target had even been * of the artifact to verify if the given target had even been
* assigned to * assigned to
* @return {@code true} if the given target has currently or had ever a * @return {@code true} if the given target has currently or had ever a relation
* relation to the given artifact through the action history, * to the given artifact through the action history, otherwise
* otherwise {@code false} * {@code false}
* *
* @throws EntityNotFoundException * @throws EntityNotFoundException
* if target with given ID does not exist * if target with given ID does not exist
@@ -294,20 +308,20 @@ public interface ControllerManagement {
boolean hasTargetArtifactAssigned(@NotEmpty String controllerId, @NotEmpty String sha1Hash); boolean hasTargetArtifactAssigned(@NotEmpty String controllerId, @NotEmpty String sha1Hash);
/** /**
* Checks if a given target has currently or has even been assigned to the * Checks if a given target has currently or has even been assigned to the given
* given artifact through the action history list. This can e.g. indicate if * artifact through the action history list. This can e.g. indicate if a target
* a target is allowed to download a given artifact because it has currently * is allowed to download a given artifact because it has currently assigned or
* assigned or had ever been assigned to the target and so it's visible to a * had ever been assigned to the target and so it's visible to a specific target
* specific target e.g. for downloading. * e.g. for downloading.
* *
* @param targetId * @param targetId
* the ID of the target to check * the ID of the target to check
* @param sha1Hash * @param sha1Hash
* of the artifact to verify if the given target had even been * of the artifact to verify if the given target had even been
* assigned to * assigned to
* @return {@code true} if the given target has currently or had ever a * @return {@code true} if the given target has currently or had ever a relation
* relation to the given artifact through the action history, * to the given artifact through the action history, otherwise
* otherwise {@code false} * {@code false}
* *
* @throws EntityNotFoundException * @throws EntityNotFoundException
* if target with given ID does not exist * if target with given ID does not exist
@@ -316,8 +330,8 @@ public interface ControllerManagement {
boolean hasTargetArtifactAssigned(long targetId, @NotEmpty String sha1Hash); boolean hasTargetArtifactAssigned(long targetId, @NotEmpty String sha1Hash);
/** /**
* Registers retrieved status for given {@link Target} and {@link Action} if * Registers retrieved status for given {@link Target} and {@link Action} if it
* it does not exist yet. * does not exist yet.
* *
* @param actionId * @param actionId
* to the handle status for * to the handle status for
@@ -371,9 +385,8 @@ public interface ControllerManagement {
Optional<Target> getByControllerId(@NotEmpty String controllerId); Optional<Target> getByControllerId(@NotEmpty String controllerId);
/** /**
* Finds {@link Target} based on given ID returns found Target without * Finds {@link Target} based on given ID returns found Target without details,
* details, i.e. NO {@link Target#getTags()} and {@link Target#getActions()} * i.e. NO {@link Target#getTags()} and {@link Target#getActions()} possible.
* possible.
* *
* @param targetId * @param targetId
* to look for. * to look for.
@@ -385,21 +398,20 @@ public interface ControllerManagement {
Optional<Target> get(long targetId); Optional<Target> get(long targetId);
/** /**
* Retrieves the specified number of messages from action history of the * Retrieves the specified number of messages from action history of the given
* given {@link Action} based on messageCount. Regardless of the value of * {@link Action} based on messageCount. Regardless of the value of
* messageCount, in order to restrict resource utilisation by controllers, * messageCount, in order to restrict resource utilisation by controllers,
* maximum number of messages that are retrieved from database is limited by * maximum number of messages that are retrieved from database is limited by
* {@link RepositoryConstants#MAX_ACTION_HISTORY_MSG_COUNT}. messageCount * {@link RepositoryConstants#MAX_ACTION_HISTORY_MSG_COUNT}. messageCount less
* less then zero, retrieves the maximum allowed number of action status * then zero, retrieves the maximum allowed number of action status messages
* messages from history; messageCount equal zero, does not retrieve any * from history; messageCount equal zero, does not retrieve any message; and
* message; and messageCount larger then zero, retrieves the specified * messageCount larger then zero, retrieves the specified number of messages,
* number of messages, limited by maximum allowed number. A controller sends * limited by maximum allowed number. A controller sends the feedback for an
* the feedback for an {@link ActionStatus} as a list of messages; while * {@link ActionStatus} as a list of messages; while returning the messages,
* returning the messages, even though the messages from multiple * even though the messages from multiple {@link ActionStatus} are retrieved in
* {@link ActionStatus} are retrieved in descending order by the reported * descending order by the reported time ({@link ActionStatus#getOccurredAt()}),
* time ({@link ActionStatus#getOccurredAt()}), i.e. latest ActionStatus * i.e. latest ActionStatus first, the sub-ordering of messages from within
* first, the sub-ordering of messages from within single * single {@link ActionStatus} is unspecified.
* {@link ActionStatus} is unspecified.
* *
* @param actionId * @param actionId
* to be filtered on * to be filtered on
@@ -412,10 +424,10 @@ public interface ControllerManagement {
List<String> getActionHistoryMessages(long actionId, int messageCount); List<String> getActionHistoryMessages(long actionId, int messageCount);
/** /**
* Cancels given {@link Action} for this {@link Target}. However, it might * Cancels given {@link Action} for this {@link Target}. However, it might be
* be possible that the controller will continue to work on the * possible that the controller will continue to work on the cancellation. The
* cancellation. The controller needs to acknowledge or reject the * controller needs to acknowledge or reject the cancellation using
* cancellation using {@link DdiRootController#postCancelActionFeedback}. * {@link DdiRootController#postCancelActionFeedback}.
* *
* @param actionId * @param actionId
* to be canceled * to be canceled
@@ -442,8 +454,7 @@ public interface ControllerManagement {
void updateActionExternalRef(long actionId, @NotEmpty String externalRef); void updateActionExternalRef(long actionId, @NotEmpty String externalRef);
/** /**
* Retrieves list of {@link Action}s which matches the provided * Retrieves list of {@link Action}s which matches the provided externalRefs.
* externalRefs.
* *
* @param externalRefs * @param externalRefs
* for which the actions need to be fetched. * for which the actions need to be fetched.

View File

@@ -97,6 +97,7 @@ import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallback;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -238,11 +239,11 @@ public class JpaControllerManagement implements ControllerManagement {
* Constructor. * Constructor.
* *
* @param defaultEventInterval * @param defaultEventInterval
* default timer value to use for interval between events. * default timer value to use for interval between events. This puts
* This puts an upper bound for the timer value * an upper bound for the timer value
* @param minimumEventInterval * @param minimumEventInterval
* for loading {@link DistributionSet#getModules()}. This * for loading {@link DistributionSet#getModules()}. This puts a
* puts a lower bound to the timer value * lower bound to the timer value
* @param timeUnit * @param timeUnit
* representing the unit of time to be used for timer. * representing the unit of time to be used for timer.
*/ */
@@ -257,16 +258,15 @@ public class JpaControllerManagement implements ControllerManagement {
} }
/** /**
* This method calculates the time interval until the next event based * This method calculates the time interval until the next event based on the
* on the desired number of events before the time when interval is * desired number of events before the time when interval is reset to default.
* reset to default. The return value is bounded by * The return value is bounded by {@link EventTimer#defaultEventInterval} and
* {@link EventTimer#defaultEventInterval} and
* {@link EventTimer#minimumEventInterval}. * {@link EventTimer#minimumEventInterval}.
* *
* @param eventCount * @param eventCount
* number of events desired until the interval is reset to * number of events desired until the interval is reset to default.
* default. This is not guaranteed as the interval between * This is not guaranteed as the interval between events cannot be
* events cannot be less than the minimum interval * less than the minimum interval
* @param timerResetTime * @param timerResetTime
* time when exponential forwarding should reset to default * time when exponential forwarding should reset to default
* *
@@ -386,16 +386,25 @@ public class JpaControllerManagement implements ControllerManagement {
@Transactional(isolation = Isolation.READ_COMMITTED) @Transactional(isolation = Isolation.READ_COMMITTED)
@Retryable(include = ConcurrencyFailureException.class, exclude = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) @Retryable(include = ConcurrencyFailureException.class, exclude = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address) { public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address) {
return findOrRegisterTargetIfItDoesNotExist(controllerId, address, null);
}
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
@Retryable(include = ConcurrencyFailureException.class, exclude = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address,
final String name) {
final Specification<JpaTarget> spec = (targetRoot, query, cb) -> cb final Specification<JpaTarget> spec = (targetRoot, query, cb) -> cb
.equal(targetRoot.get(JpaTarget_.controllerId), controllerId); .equal(targetRoot.get(JpaTarget_.controllerId), controllerId);
return targetRepository.findOne(spec).map(target -> updateTargetStatus(target, address)) return targetRepository.findOne(spec).map(target -> updateTarget(target, address, name))
.orElseGet(() -> createTarget(controllerId, address)); .orElseGet(() -> createTarget(controllerId, address, name));
} }
private Target createTarget(final String controllerId, final URI address) { private Target createTarget(final String controllerId, final URI address, String name) {
final Target result = targetRepository.save((JpaTarget) entityFactory.target().create() final Target result = targetRepository.save((JpaTarget) entityFactory.target().create()
.controllerId(controllerId).description("Plug and Play target: " + controllerId).name(controllerId) .controllerId(controllerId).description("Plug and Play target: " + controllerId).name((StringUtils.hasText(name) ? name : controllerId))
.status(TargetUpdateStatus.REGISTERED).lastTargetQuery(System.currentTimeMillis()) .status(TargetUpdateStatus.REGISTERED).lastTargetQuery(System.currentTimeMillis())
.address(Optional.ofNullable(address).map(URI::toString).orElse(null)).build()); .address(Optional.ofNullable(address).map(URI::toString).orElse(null)).build());
@@ -458,9 +467,8 @@ public class JpaControllerManagement implements ControllerManagement {
/** /**
* Sets {@link Target#getLastTargetQuery()} by native SQL in order to avoid * Sets {@link Target#getLastTargetQuery()} by native SQL in order to avoid
* raising opt lock revision as this update is not mission critical and in * raising opt lock revision as this update is not mission critical and in fact
* fact only written by {@link ControllerManagement}, i.e. the target * only written by {@link ControllerManagement}, i.e. the target itself.
* itself.
*/ */
private void setLastTargetQuery(final String tenant, final long currentTimeMillis, final List<String> chunk) { private void setLastTargetQuery(final String tenant, final long currentTimeMillis, final List<String> chunk) {
final Map<String, String> paramMapping = Maps.newHashMapWithExpectedSize(chunk.size()); final Map<String, String> paramMapping = Maps.newHashMapWithExpectedSize(chunk.size());
@@ -488,39 +496,40 @@ public class JpaControllerManagement implements ControllerManagement {
} }
/** /**
* Stores target directly to DB in case either {@link Target#getAddress()} * Stores target directly to DB in case either {@link Target#getAddress()} or
* or {@link Target#getUpdateStatus()} changes or the buffer queue is full. * {@link Target#getUpdateStatus()} or {@link Target#getName()} changes or the buffer queue is full.
* *
*/ */
private Target updateTargetStatus(final JpaTarget toUpdate, final URI address) { private Target updateTarget(final JpaTarget toUpdate, final URI address, final String name) {
boolean storeEager = isStoreEager(toUpdate, address); if (isStoreEager(toUpdate, address, name) || !queue.offer(new TargetPoll(toUpdate))) {
if (isAddressChanged(toUpdate.getAddress(), address)) {
if (TargetUpdateStatus.UNKNOWN == toUpdate.getUpdateStatus()) {
toUpdate.setUpdateStatus(TargetUpdateStatus.REGISTERED);
storeEager = true;
}
if (storeEager || !queue.offer(new TargetPoll(toUpdate))) {
toUpdate.setAddress(address.toString()); toUpdate.setAddress(address.toString());
}
if (isNameChanged(toUpdate.getName(), name)) {
toUpdate.setName(name);
}
if (isStatusUnknown(toUpdate.getUpdateStatus())) {
toUpdate.setUpdateStatus(TargetUpdateStatus.REGISTERED);
}
toUpdate.setLastTargetQuery(System.currentTimeMillis()); toUpdate.setLastTargetQuery(System.currentTimeMillis());
afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher() afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher()
.publishEvent(new TargetPollEvent(toUpdate, eventPublisherHolder.getApplicationId()))); .publishEvent(new TargetPollEvent(toUpdate, eventPublisherHolder.getApplicationId())));
return targetRepository.save(toUpdate); return targetRepository.save(toUpdate);
} }
return toUpdate; return toUpdate;
} }
private boolean isStoreEager(final JpaTarget toUpdate, final URI address, final String name) {
private boolean isStoreEager(final JpaTarget toUpdate, final URI address) { return repositoryProperties.isEagerPollPersistence() || isAddressChanged(toUpdate.getAddress(), address)
if (repositoryProperties.isEagerPollPersistence()) { || isNameChanged(toUpdate.getName(), name) || isStatusUnknown(toUpdate.getUpdateStatus());
return true;
} else if (toUpdate.getAddress() == null) {
return true;
} else {
return !toUpdate.getAddress().equals(address);
} }
private boolean isAddressChanged(final URI addressToUpdate, final URI address) {
return addressToUpdate == null || !addressToUpdate.equals(address);
}
private boolean isNameChanged(final String nameToUpdate, final String name) {
return StringUtils.hasText(name) && !nameToUpdate.equals(name);
}
private boolean isStatusUnknown(final TargetUpdateStatus statusToUpdate) {
return TargetUpdateStatus.UNKNOWN == statusToUpdate;
} }
@Override @Override
@@ -596,8 +605,8 @@ public class JpaControllerManagement implements ControllerManagement {
* ActionStatus updates are allowed mainly if the action is active. If the * ActionStatus updates are allowed mainly if the action is active. If the
* action is not active we accept further status updates if permitted so by * action is not active we accept further status updates if permitted so by
* repository configuration. In this case, only the values: Status.ERROR and * repository configuration. In this case, only the values: Status.ERROR and
* Status.FINISHED are allowed. In the case of a DOWNLOAD_ONLY action, we * Status.FINISHED are allowed. In the case of a DOWNLOAD_ONLY action, we accept
* accept status updates only once. * status updates only once.
*/ */
private boolean isUpdatingActionStatusAllowed(final JpaAction action, final JpaActionStatus actionStatus) { private boolean isUpdatingActionStatusAllowed(final JpaAction action, final JpaActionStatus actionStatus) {
@@ -732,8 +741,8 @@ public class JpaControllerManagement implements ControllerManagement {
final UpdateMode mode) { final UpdateMode mode) {
/* /*
* Constraints on attribute keys & values are not validated by * Constraints on attribute keys & values are not validated by EclipseLink.
* EclipseLink. Hence, they are validated here. * Hence, they are validated here.
*/ */
if (data.entrySet().stream().anyMatch(e -> !isAttributeEntryValid(e))) { if (data.entrySet().stream().anyMatch(e -> !isAttributeEntryValid(e))) {
throw new InvalidTargetAttributeException(); throw new InvalidTargetAttributeException();
@@ -744,7 +753,6 @@ public class JpaControllerManagement implements ControllerManagement {
// get the modifiable attribute map // get the modifiable attribute map
final Map<String, String> controllerAttributes = target.getControllerAttributes(); final Map<String, String> controllerAttributes = target.getControllerAttributes();
final UpdateMode updateMode = mode != null ? mode : UpdateMode.MERGE; final UpdateMode updateMode = mode != null ? mode : UpdateMode.MERGE;
switch (updateMode) { switch (updateMode) {
case REMOVE: case REMOVE:
@@ -811,8 +819,8 @@ public class JpaControllerManagement implements ControllerManagement {
} }
/** /**
* Registers retrieved status for given {@link Target} and {@link Action} if * Registers retrieved status for given {@link Target} and {@link Action} if it
* it does not exist yet. * does not exist yet.
* *
* @param actionId * @param actionId
* to the handle status for * to the handle status for

View File

@@ -511,6 +511,20 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
assertThat(targetRepository.count()).as("Only 1 target should be registered").isEqualTo(1L); assertThat(targetRepository.count()).as("Only 1 target should be registered").isEqualTo(1L);
} }
@Test
@Description("Register a controller with name which does not exist and update its name")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 1) })
public void findOrRegisterTargetIfItDoesNotExistWithName() {
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST, "TestName");
final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
"ChangedTestName");
assertThat(target.getId()).as("Target should be the equals").isEqualTo(sameTarget.getId());
assertThat(target.getName()).as("Taget names should be different").isNotEqualTo(sameTarget.getName());
assertThat(sameTarget.getName()).as("Taget name should be changed").isEqualTo("ChangedTestName");
assertThat(targetRepository.count()).as("Only 1 target should be registred").isEqualTo(1L);
}
@Test @Test
@Description("Tries to register a target with an invalid controller id") @Description("Tries to register a target with an invalid controller id")
public void findOrRegisterTargetIfItDoesNotExistThrowsExceptionForInvalidControllerIdParam() { public void findOrRegisterTargetIfItDoesNotExistThrowsExceptionForInvalidControllerIdParam() {
@@ -580,6 +594,28 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
} }
} }
@Test
@Description("Register a controller which does not exist, then update the controller twice, first time by providing a name property and second time without a new name")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 3) ,
@Expect(type = TargetUpdatedEvent.class, count = 1)})
public void findOrRegisterTargetIfItDoesNotExistDoesUpdateNameOnExistingTargetProperly() {
String controllerId = "12345";
String targetName = "UpdatedName";
final Target newTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, LOCALHOST);
assertThat(newTarget.getName()).isEqualTo(controllerId);
Target firstTimeUpdatedTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, LOCALHOST, targetName);
assertThat(firstTimeUpdatedTarget.getName()).isEqualTo(targetName);
//Name should not change to default (name=targetId) if target is updated without new name provided
Target secondTimeUpdatedTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, LOCALHOST);
assertThat(secondTimeUpdatedTarget.getName()).isEqualTo(targetName);
}
@Test @Test
@Description("Register a controller which does not exist, if a EntityAlreadyExistsException is raised, the " @Description("Register a controller which does not exist, if a EntityAlreadyExistsException is raised, the "
+ "exception is rethrown and no further retries will be attempted") + "exception is rethrown and no further retries will be attempted")

View File

@@ -117,8 +117,8 @@ public class TestdataFactory {
public static final String SM_TYPE_RT = "runtime"; public static final String SM_TYPE_RT = "runtime";
/** /**
* Key of test "application" {@link SoftwareModuleType} : optional software * Key of test "application" {@link SoftwareModuleType} : optional software in
* in {@link #DS_TYPE_DEFAULT}. * {@link #DS_TYPE_DEFAULT}.
*/ */
public static final String SM_TYPE_APP = "application"; public static final String SM_TYPE_APP = "application";
@@ -160,14 +160,13 @@ public class TestdataFactory {
/** /**
* Creates {@link DistributionSet} in repository including three * Creates {@link DistributionSet} in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link DistributionSet#isRequiredMigrationStep()} <code>false</code>. * {@link DistributionSet#isRequiredMigrationStep()} <code>false</code>.
* *
* @param prefix * @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name, * for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description. * vendor and description.
*
* @return {@link DistributionSet} entity. * @return {@link DistributionSet} entity.
*/ */
public DistributionSet createDistributionSet(final String prefix) { public DistributionSet createDistributionSet(final String prefix) {
@@ -176,8 +175,8 @@ public class TestdataFactory {
/** /**
* Creates {@link DistributionSet} in repository including three * Creates {@link DistributionSet} in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link DistributionSet#isRequiredMigrationStep()} <code>false</code>. * {@link DistributionSet#isRequiredMigrationStep()} <code>false</code>.
* *
* @return {@link DistributionSet} entity. * @return {@link DistributionSet} entity.
@@ -188,13 +187,12 @@ public class TestdataFactory {
/** /**
* Creates {@link DistributionSet} in repository including three * Creates {@link DistributionSet} in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link DistributionSet#isRequiredMigrationStep()} <code>false</code>. * {@link DistributionSet#isRequiredMigrationStep()} <code>false</code>.
* *
* @param modules * @param modules
* of {@link DistributionSet#getModules()} * of {@link DistributionSet#getModules()}
*
* @return {@link DistributionSet} entity. * @return {@link DistributionSet} entity.
*/ */
public DistributionSet createDistributionSet(final Collection<SoftwareModule> modules) { public DistributionSet createDistributionSet(final Collection<SoftwareModule> modules) {
@@ -203,8 +201,8 @@ public class TestdataFactory {
/** /**
* Creates {@link DistributionSet} in repository including three * Creates {@link DistributionSet} in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link DistributionSet#isRequiredMigrationStep()} <code>false</code>. * {@link DistributionSet#isRequiredMigrationStep()} <code>false</code>.
* *
* @param modules * @param modules
@@ -212,7 +210,6 @@ public class TestdataFactory {
* @param prefix * @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name, * for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description. * vendor and description.
*
* @return {@link DistributionSet} entity. * @return {@link DistributionSet} entity.
*/ */
public DistributionSet createDistributionSet(final Collection<SoftwareModule> modules, final String prefix) { public DistributionSet createDistributionSet(final Collection<SoftwareModule> modules, final String prefix) {
@@ -221,15 +218,14 @@ public class TestdataFactory {
/** /**
* Creates {@link DistributionSet} in repository including three * Creates {@link DistributionSet} in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION}. * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION}.
* *
* @param prefix * @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name, * for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description. * vendor and description.
* @param isRequiredMigrationStep * @param isRequiredMigrationStep
* for {@link DistributionSet#isRequiredMigrationStep()} * for {@link DistributionSet#isRequiredMigrationStep()}
*
* @return {@link DistributionSet} entity. * @return {@link DistributionSet} entity.
*/ */
public DistributionSet createDistributionSet(final String prefix, final boolean isRequiredMigrationStep) { public DistributionSet createDistributionSet(final String prefix, final boolean isRequiredMigrationStep) {
@@ -238,8 +234,8 @@ public class TestdataFactory {
/** /**
* Creates {@link DistributionSet} in repository including three * Creates {@link DistributionSet} in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link DistributionSet#isRequiredMigrationStep()} <code>false</code>. * {@link DistributionSet#isRequiredMigrationStep()} <code>false</code>.
* *
* @param prefix * @param prefix
@@ -247,7 +243,6 @@ public class TestdataFactory {
* vendor and description. * vendor and description.
* @param tags * @param tags
* {@link DistributionSet#getTags()} * {@link DistributionSet#getTags()}
*
* @return {@link DistributionSet} entity. * @return {@link DistributionSet} entity.
*/ */
public DistributionSet createDistributionSet(final String prefix, final Collection<DistributionSetTag> tags) { public DistributionSet createDistributionSet(final String prefix, final Collection<DistributionSetTag> tags) {
@@ -256,19 +251,17 @@ public class TestdataFactory {
/** /**
* Creates {@link DistributionSet} in repository including three * Creates {@link DistributionSet} in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* , {@link #SM_TYPE_APP}. * {@link #SM_TYPE_APP}.
* *
* @param prefix * @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name, * for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description. * vendor and description.
* @param version * @param version
* {@link DistributionSet#getVersion()} and * {@link DistributionSet#getVersion()} and
* {@link SoftwareModule#getVersion()} extended by a random * {@link SoftwareModule#getVersion()} extended by a random number.
* number.
* @param isRequiredMigrationStep * @param isRequiredMigrationStep
* for {@link DistributionSet#isRequiredMigrationStep()} * for {@link DistributionSet#isRequiredMigrationStep()}
*
* @return {@link DistributionSet} entity. * @return {@link DistributionSet} entity.
*/ */
public DistributionSet createDistributionSet(final String prefix, final String version, public DistributionSet createDistributionSet(final String prefix, final String version,
@@ -326,13 +319,11 @@ public class TestdataFactory {
* vendor and description. * vendor and description.
* @param version * @param version
* {@link DistributionSet#getVersion()} and * {@link DistributionSet#getVersion()} and
* {@link SoftwareModule#getVersion()} extended by a random * {@link SoftwareModule#getVersion()} extended by a random number.
* number.
* @param isRequiredMigrationStep * @param isRequiredMigrationStep
* for {@link DistributionSet#isRequiredMigrationStep()} * for {@link DistributionSet#isRequiredMigrationStep()}
* @param modules * @param modules
* for {@link DistributionSet#getModules()} * for {@link DistributionSet#getModules()}
*
* @return {@link DistributionSet} entity. * @return {@link DistributionSet} entity.
*/ */
public DistributionSet createDistributionSet(final String prefix, final String version, public DistributionSet createDistributionSet(final String prefix, final String version,
@@ -347,8 +338,8 @@ public class TestdataFactory {
/** /**
* Creates {@link DistributionSet} in repository including three * Creates {@link DistributionSet} in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* , {@link #SM_TYPE_APP}. * {@link #SM_TYPE_APP}.
* *
* @param prefix * @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name, * for {@link SoftwareModule}s and {@link DistributionSet}s name,
@@ -359,7 +350,6 @@ public class TestdataFactory {
* number.updat * number.updat
* @param tags * @param tags
* {@link DistributionSet#getTags()} * {@link DistributionSet#getTags()}
*
* @return {@link DistributionSet} entity. * @return {@link DistributionSet} entity.
*/ */
public DistributionSet createDistributionSet(final String prefix, final String version, public DistributionSet createDistributionSet(final String prefix, final String version,
@@ -375,14 +365,13 @@ public class TestdataFactory {
/** /**
* Creates {@link DistributionSet}s in repository including three * Creates {@link DistributionSet}s in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an iterative
* iterative number and {@link DistributionSet#isRequiredMigrationStep()} * number and {@link DistributionSet#isRequiredMigrationStep()}
* <code>false</code>. * <code>false</code>.
* *
* @param number * @param number
* of {@link DistributionSet}s to create * of {@link DistributionSet}s to create
*
* @return {@link List} of {@link DistributionSet} entities * @return {@link List} of {@link DistributionSet} entities
*/ */
public List<DistributionSet> createDistributionSets(final int number) { public List<DistributionSet> createDistributionSets(final int number) {
@@ -391,8 +380,7 @@ public class TestdataFactory {
} }
/** /**
* Create a list of {@link DistributionSet}s without modules, i.e. * Create a list of {@link DistributionSet}s without modules, i.e. incomplete.
* incomplete.
* *
* @param number * @param number
* of {@link DistributionSet}s to create * of {@link DistributionSet}s to create
@@ -412,9 +400,9 @@ public class TestdataFactory {
/** /**
* Creates {@link DistributionSet}s in repository including three * Creates {@link DistributionSet}s in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an iterative
* iterative number and {@link DistributionSet#isRequiredMigrationStep()} * number and {@link DistributionSet#isRequiredMigrationStep()}
* <code>false</code>. * <code>false</code>.
* *
* @param prefix * @param prefix
@@ -422,7 +410,6 @@ public class TestdataFactory {
* vendor and description. * vendor and description.
* @param number * @param number
* of {@link DistributionSet}s to create * of {@link DistributionSet}s to create
*
* @return {@link List} of {@link DistributionSet} entities * @return {@link List} of {@link DistributionSet} entities
*/ */
public List<DistributionSet> createDistributionSets(final String prefix, final int number) { public List<DistributionSet> createDistributionSets(final String prefix, final int number) {
@@ -444,7 +431,6 @@ public class TestdataFactory {
* {@link DistributionSet#getName()} * {@link DistributionSet#getName()}
* @param version * @param version
* {@link DistributionSet#getVersion()} * {@link DistributionSet#getVersion()}
*
* @return {@link DistributionSet} entity * @return {@link DistributionSet} entity
*/ */
public DistributionSet createDistributionSetWithNoSoftwareModules(final String name, final String version) { public DistributionSet createDistributionSetWithNoSoftwareModules(final String name, final String version) {
@@ -454,12 +440,11 @@ public class TestdataFactory {
} }
/** /**
* Creates {@link Artifact}s for given {@link SoftwareModule} with a small * Creates {@link Artifact}s for given {@link SoftwareModule} with a small text
* text payload. * payload.
* *
* @param moduleId * @param moduleId
* the {@link Artifact}s belong to. * the {@link Artifact}s belong to.
*
* @return {@link Artifact} entity. * @return {@link Artifact} entity.
*/ */
public List<Artifact> createArtifacts(final Long moduleId) { public List<Artifact> createArtifacts(final Long moduleId) {
@@ -473,18 +458,15 @@ public class TestdataFactory {
} }
/** /**
* Create an {@link Artifact} for given {@link SoftwareModule} with a small * Create an {@link Artifact} for given {@link SoftwareModule} with a small text
* text payload. * payload.
* *
* @param artifactData * @param artifactData
* the {@link Artifact} Inputstream * the {@link Artifact} Inputstream
*
* @param moduleId * @param moduleId
* the {@link Artifact} belongs to * the {@link Artifact} belongs to
*
* @param filename * @param filename
* that was provided during upload. * that was provided during upload.
*
* @return {@link Artifact} entity. * @return {@link Artifact} entity.
*/ */
public Artifact createArtifact(final String artifactData, final Long moduleId, final String filename) { public Artifact createArtifact(final String artifactData, final Long moduleId, final String filename) {
@@ -494,36 +476,32 @@ public class TestdataFactory {
} }
/** /**
* Create an {@link Artifact} for given {@link SoftwareModule} with a small * Create an {@link Artifact} for given {@link SoftwareModule} with a small text
* text payload. * payload.
* *
* @param artifactData * @param artifactData
* the {@link Artifact} Inputstream * the {@link Artifact} Inputstream
*
* @param moduleId * @param moduleId
* the {@link Artifact} belongs to * the {@link Artifact} belongs to
*
* @param filename * @param filename
* that was provided during upload. * that was provided during upload.
*
* @param fileSize * @param fileSize
* the file size * the file size
*
* @return {@link Artifact} entity. * @return {@link Artifact} entity.
*/ */
public Artifact createArtifact(final byte[] artifactData, final Long moduleId, final String filename, final int fileSize) { public Artifact createArtifact(final byte[] artifactData, final Long moduleId, final String filename,
return artifactManagement final int fileSize) {
.create(new ArtifactUpload(new ByteArrayInputStream(artifactData), moduleId, filename, false, fileSize)); return artifactManagement.create(
new ArtifactUpload(new ByteArrayInputStream(artifactData), moduleId, filename, false, fileSize));
} }
/** /**
* Creates {@link SoftwareModule} with {@link #DEFAULT_VENDOR} and * Creates {@link SoftwareModule} with {@link #DEFAULT_VENDOR} and
* {@link #DEFAULT_VERSION} and random generated * {@link #DEFAULT_VERSION} and random generated {@link Target#getDescription()}
* {@link Target#getDescription()} in the repository. * in the repository.
* *
* @param typeKey * @param typeKey
* of the {@link SoftwareModuleType} * of the {@link SoftwareModuleType}
*
* @return persisted {@link SoftwareModule}. * @return persisted {@link SoftwareModule}.
*/ */
public SoftwareModule createSoftwareModule(final String typeKey) { public SoftwareModule createSoftwareModule(final String typeKey) {
@@ -531,11 +509,9 @@ public class TestdataFactory {
} }
/** /**
* Creates {@link SoftwareModule} of type * Creates {@link SoftwareModule} of type {@value Constants#SMT_DEFAULT_APP_KEY}
* {@value Constants#SMT_DEFAULT_APP_KEY} with {@link #DEFAULT_VENDOR} and * with {@link #DEFAULT_VENDOR} and {@link #DEFAULT_VERSION} and random
* {@link #DEFAULT_VERSION} and random generated * generated {@link Target#getDescription()} in the repository.
* {@link Target#getDescription()} in the repository.
*
* *
* @return persisted {@link SoftwareModule}. * @return persisted {@link SoftwareModule}.
*/ */
@@ -544,15 +520,12 @@ public class TestdataFactory {
} }
/** /**
* Creates {@link SoftwareModule} of type * Creates {@link SoftwareModule} of type {@value Constants#SMT_DEFAULT_APP_KEY}
* {@value Constants#SMT_DEFAULT_APP_KEY} with {@link #DEFAULT_VENDOR} and * with {@link #DEFAULT_VENDOR} and {@link #DEFAULT_VERSION} and random
* {@link #DEFAULT_VERSION} and random generated * generated {@link Target#getDescription()} in the repository.
* {@link Target#getDescription()} in the repository.
* *
* @param prefix * @param prefix
* added to name and version * added to name and version
*
*
* @return persisted {@link SoftwareModule}. * @return persisted {@link SoftwareModule}.
*/ */
public SoftwareModule createSoftwareModuleApp(final String prefix) { public SoftwareModule createSoftwareModuleApp(final String prefix) {
@@ -560,11 +533,9 @@ public class TestdataFactory {
} }
/** /**
* Creates {@link SoftwareModule} of type * Creates {@link SoftwareModule} of type {@value Constants#SMT_DEFAULT_OS_KEY}
* {@value Constants#SMT_DEFAULT_OS_KEY} with {@link #DEFAULT_VENDOR} and * with {@link #DEFAULT_VENDOR} and {@link #DEFAULT_VERSION} and random
* {@link #DEFAULT_VERSION} and random generated * generated {@link Target#getDescription()} in the repository.
* {@link Target#getDescription()} in the repository.
*
* *
* @return persisted {@link SoftwareModule}. * @return persisted {@link SoftwareModule}.
*/ */
@@ -573,15 +544,12 @@ public class TestdataFactory {
} }
/** /**
* Creates {@link SoftwareModule} of type * Creates {@link SoftwareModule} of type {@value Constants#SMT_DEFAULT_OS_KEY}
* {@value Constants#SMT_DEFAULT_OS_KEY} with {@link #DEFAULT_VENDOR} and * with {@link #DEFAULT_VENDOR} and {@link #DEFAULT_VERSION} and random
* {@link #DEFAULT_VERSION} and random generated * generated {@link Target#getDescription()} in the repository.
* {@link Target#getDescription()} in the repository.
* *
* @param prefix * @param prefix
* added to name and version * added to name and version
*
*
* @return persisted {@link SoftwareModule}. * @return persisted {@link SoftwareModule}.
*/ */
public SoftwareModule createSoftwareModuleOs(final String prefix) { public SoftwareModule createSoftwareModuleOs(final String prefix) {
@@ -590,14 +558,13 @@ public class TestdataFactory {
/** /**
* Creates {@link SoftwareModule} with {@link #DEFAULT_VENDOR} and * Creates {@link SoftwareModule} with {@link #DEFAULT_VENDOR} and
* {@link #DEFAULT_VERSION} and random generated * {@link #DEFAULT_VERSION} and random generated {@link Target#getDescription()}
* {@link Target#getDescription()} in the repository. * in the repository.
* *
* @param typeKey * @param typeKey
* of the {@link SoftwareModuleType} * of the {@link SoftwareModuleType}
* @param prefix * @param prefix
* added to name and version * added to name and version
*
* @return persisted {@link SoftwareModule}. * @return persisted {@link SoftwareModule}.
*/ */
public SoftwareModule createSoftwareModule(final String typeKey, final String prefix) { public SoftwareModule createSoftwareModule(final String typeKey, final String prefix) {
@@ -619,27 +586,42 @@ public class TestdataFactory {
* @return persisted {@link Target} * @return persisted {@link Target}
*/ */
public Target createTarget(final String controllerId) { public Target createTarget(final String controllerId) {
final Target target = targetManagement.create(entityFactory.target().create().controllerId(controllerId)); return createTarget(controllerId, controllerId);
}
/**
* @param controllerId
* of the target
* @param targetName
* name of the target
* @return persisted {@link Target}
*/
public Target createTarget(final String controllerId, final String targetName) {
final Target target = targetManagement
.create(entityFactory.target().create().controllerId(controllerId).name(targetName));
assertTargetProperlyCreated(target);
return target;
}
private void assertTargetProperlyCreated(Target target) {
assertThat(target.getCreatedBy()).isNotNull(); assertThat(target.getCreatedBy()).isNotNull();
assertThat(target.getCreatedAt()).isNotNull(); assertThat(target.getCreatedAt()).isNotNull();
assertThat(target.getLastModifiedBy()).isNotNull(); assertThat(target.getLastModifiedBy()).isNotNull();
assertThat(target.getLastModifiedAt()).isNotNull(); assertThat(target.getLastModifiedAt()).isNotNull();
assertThat(target.getUpdateStatus()).isEqualTo(TargetUpdateStatus.UNKNOWN); assertThat(target.getUpdateStatus()).isEqualTo(TargetUpdateStatus.UNKNOWN);
return target;
} }
/** /**
* Creates {@link DistributionSet}s in repository including three * Creates {@link DistributionSet}s in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an iterative
* iterative number and {@link DistributionSet#isRequiredMigrationStep()} * number and {@link DistributionSet#isRequiredMigrationStep()}
* <code>false</code>. * <code>false</code>.
* *
* In addition it updates the created {@link DistributionSet}s and * In addition it updates the created {@link DistributionSet}s and
* {@link SoftwareModule}s to ensure that * {@link SoftwareModule}s to ensure that {@link BaseEntity#getLastModifiedAt()}
* {@link BaseEntity#getLastModifiedAt()} and * and {@link BaseEntity#getLastModifiedBy()} is filled.
* {@link BaseEntity#getLastModifiedBy()} is filled.
* *
* @return persisted {@link DistributionSet}. * @return persisted {@link DistributionSet}.
*/ */
@@ -657,8 +639,8 @@ public class TestdataFactory {
/** /**
* @return {@link DistributionSetType} with key {@link #DS_TYPE_DEFAULT} and * @return {@link DistributionSetType} with key {@link #DS_TYPE_DEFAULT} and
* {@link SoftwareModuleType}s {@link #SM_TYPE_OS}, * {@link SoftwareModuleType}s {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
* {@link #SM_TYPE_RT} , {@link #SM_TYPE_APP}. * , {@link #SM_TYPE_APP}.
*/ */
public DistributionSetType findOrCreateDefaultTestDsType() { public DistributionSetType findOrCreateDefaultTestDsType() {
final List<SoftwareModuleType> mand = new ArrayList<>(); final List<SoftwareModuleType> mand = new ArrayList<>();
@@ -679,7 +661,6 @@ public class TestdataFactory {
* {@link DistributionSetType#getKey()} * {@link DistributionSetType#getKey()}
* @param dsTypeName * @param dsTypeName
* {@link DistributionSetType#getName()} * {@link DistributionSetType#getName()}
*
* @return persisted {@link DistributionSetType} * @return persisted {@link DistributionSetType}
*/ */
public DistributionSetType findOrCreateDistributionSetType(final String dsTypeKey, final String dsTypeName) { public DistributionSetType findOrCreateDistributionSetType(final String dsTypeKey, final String dsTypeName) {
@@ -700,7 +681,6 @@ public class TestdataFactory {
* {@link DistributionSetType#getMandatoryModuleTypes()} * {@link DistributionSetType#getMandatoryModuleTypes()}
* @param optional * @param optional
* {@link DistributionSetType#getOptionalModuleTypes()} * {@link DistributionSetType#getOptionalModuleTypes()}
*
* @return persisted {@link DistributionSetType} * @return persisted {@link DistributionSetType}
*/ */
public DistributionSetType findOrCreateDistributionSetType(final String dsTypeKey, final String dsTypeName, public DistributionSetType findOrCreateDistributionSetType(final String dsTypeKey, final String dsTypeName,
@@ -714,12 +694,11 @@ public class TestdataFactory {
/** /**
* Finds {@link SoftwareModuleType} in repository with given * Finds {@link SoftwareModuleType} in repository with given
* {@link SoftwareModuleType#getKey()} or creates if it does not exist yet * {@link SoftwareModuleType#getKey()} or creates if it does not exist yet with
* with {@link SoftwareModuleType#getMaxAssignments()} = 1. * {@link SoftwareModuleType#getMaxAssignments()} = 1.
* *
* @param key * @param key
* {@link SoftwareModuleType#getKey()} * {@link SoftwareModuleType#getKey()}
*
* @return persisted {@link SoftwareModuleType} * @return persisted {@link SoftwareModuleType}
*/ */
public SoftwareModuleType findOrCreateSoftwareModuleType(final String key) { public SoftwareModuleType findOrCreateSoftwareModuleType(final String key) {
@@ -734,7 +713,6 @@ public class TestdataFactory {
* {@link SoftwareModuleType#getKey()} * {@link SoftwareModuleType#getKey()}
* @param maxAssignments * @param maxAssignments
* {@link SoftwareModuleType#getMaxAssignments()} * {@link SoftwareModuleType#getMaxAssignments()}
*
* @return persisted {@link SoftwareModuleType} * @return persisted {@link SoftwareModuleType}
*/ */
public SoftwareModuleType findOrCreateSoftwareModuleType(final String key, final int maxAssignments) { public SoftwareModuleType findOrCreateSoftwareModuleType(final String key, final int maxAssignments) {
@@ -754,7 +732,6 @@ public class TestdataFactory {
* {@link DistributionSet#getType()} * {@link DistributionSet#getType()}
* @param modules * @param modules
* {@link DistributionSet#getModules()} * {@link DistributionSet#getModules()}
*
* @return the created {@link DistributionSet} * @return the created {@link DistributionSet}
*/ */
public DistributionSet createDistributionSet(final String name, final String version, public DistributionSet createDistributionSet(final String name, final String version,
@@ -777,7 +754,6 @@ public class TestdataFactory {
* {@link DistributionSet#getModules()} * {@link DistributionSet#getModules()}
* @param requiredMigrationStep * @param requiredMigrationStep
* {@link DistributionSet#isRequiredMigrationStep()} * {@link DistributionSet#isRequiredMigrationStep()}
*
* @return the created {@link DistributionSet} * @return the created {@link DistributionSet}
*/ */
public DistributionSet generateDistributionSet(final String name, final String version, public DistributionSet generateDistributionSet(final String name, final String version,
@@ -799,7 +775,6 @@ public class TestdataFactory {
* {@link DistributionSet#getType()} * {@link DistributionSet#getType()}
* @param modules * @param modules
* {@link DistributionSet#getModules()} * {@link DistributionSet#getModules()}
*
* @return the created {@link DistributionSet} * @return the created {@link DistributionSet}
*/ */
public DistributionSet generateDistributionSet(final String name, final String version, public DistributionSet generateDistributionSet(final String name, final String version,
@@ -812,7 +787,6 @@ public class TestdataFactory {
* *
* @param name * @param name
* {@link DistributionSet#getName()} * {@link DistributionSet#getName()}
*
* @return the generated {@link DistributionSet} * @return the generated {@link DistributionSet}
*/ */
public DistributionSet generateDistributionSet(final String name) { public DistributionSet generateDistributionSet(final String name) {
@@ -821,13 +795,11 @@ public class TestdataFactory {
} }
/** /**
* Creates {@link Target}s in repository and with * Creates {@link Target}s in repository and with {@link #DEFAULT_CONTROLLER_ID}
* {@link #DEFAULT_CONTROLLER_ID} as prefix for * as prefix for {@link Target#getControllerId()}.
* {@link Target#getControllerId()}.
* *
* @param number * @param number
* of {@link Target}s to create * of {@link Target}s to create
*
* @return {@link List} of {@link Target} entities * @return {@link List} of {@link Target} entities
*/ */
public List<Target> createTargets(final int number) { public List<Target> createTargets(final int number) {
@@ -863,8 +835,7 @@ public class TestdataFactory {
/** /**
* Builds {@link Target} objects with given prefix for * Builds {@link Target} objects with given prefix for
* {@link Target#getControllerId()} followed by a number suffix starting * {@link Target#getControllerId()} followed by a number suffix starting with 0.
* with 0.
* *
* @param numberOfTargets * @param numberOfTargets
* of {@link Target}s to generate * of {@link Target}s to generate
@@ -958,7 +929,6 @@ public class TestdataFactory {
* *
* @param number * @param number
* of {@link DistributionSetTag}s * of {@link DistributionSetTag}s
*
* @return the persisted {@link DistributionSetTag}s * @return the persisted {@link DistributionSetTag}s
*/ */
public List<DistributionSetTag> createDistributionSetTags(final int number) { public List<DistributionSetTag> createDistributionSetTags(final int number) {
@@ -980,8 +950,7 @@ public class TestdataFactory {
} }
/** /**
* Append {@link ActionStatus} to all {@link Action}s of given * Append {@link ActionStatus} to all {@link Action}s of given {@link Target}s.
* {@link Target}s.
* *
* @param targets * @param targets
* to add {@link ActionStatus} * to add {@link ActionStatus}
@@ -989,7 +958,6 @@ public class TestdataFactory {
* to add * to add
* @param message * @param message
* to add * to add
*
* @return updated {@link Action}. * @return updated {@link Action}.
*/ */
public List<Action> sendUpdateActionStatusToTargets(final Collection<Target> targets, final Status status, public List<Action> sendUpdateActionStatusToTargets(final Collection<Target> targets, final Status status,
@@ -998,8 +966,7 @@ public class TestdataFactory {
} }
/** /**
* Append {@link ActionStatus} to all {@link Action}s of given * Append {@link ActionStatus} to all {@link Action}s of given {@link Target}s.
* {@link Target}s.
* *
* @param targets * @param targets
* to add {@link ActionStatus} * to add {@link ActionStatus}
@@ -1007,7 +974,6 @@ public class TestdataFactory {
* to add * to add
* @param msgs * @param msgs
* to add * to add
*
* @return updated {@link Action}. * @return updated {@link Action}.
*/ */
public List<Action> sendUpdateActionStatusToTargets(final Collection<Target> targets, final Status status, public List<Action> sendUpdateActionStatusToTargets(final Collection<Target> targets, final Status status,
@@ -1045,7 +1011,8 @@ public class TestdataFactory {
public Rollout createRolloutByVariables(final String rolloutName, final String rolloutDescription, public Rollout createRolloutByVariables(final String rolloutName, final String rolloutDescription,
final int groupSize, final String filterQuery, final DistributionSet distributionSet, final int groupSize, final String filterQuery, final DistributionSet distributionSet,
final String successCondition, final String errorCondition) { final String successCondition, final String errorCondition) {
return createRolloutByVariables(rolloutName, rolloutDescription, groupSize, filterQuery, distributionSet, successCondition, errorCondition, Action.ActionType.FORCED); return createRolloutByVariables(rolloutName, rolloutDescription, groupSize, filterQuery, distributionSet,
successCondition, errorCondition, Action.ActionType.FORCED);
} }
/** /**
@@ -1077,9 +1044,10 @@ public class TestdataFactory {
.errorCondition(RolloutGroupErrorCondition.THRESHOLD, errorCondition) .errorCondition(RolloutGroupErrorCondition.THRESHOLD, errorCondition)
.errorAction(RolloutGroupErrorAction.PAUSE, null).build(); .errorAction(RolloutGroupErrorAction.PAUSE, null).build();
final Rollout rollout = rolloutManagement.create(entityFactory.rollout().create().name(rolloutName) final Rollout rollout = rolloutManagement.create(
.description(rolloutDescription).targetFilterQuery(filterQuery).set(distributionSet) entityFactory.rollout().create().name(rolloutName).description(rolloutDescription)
.actionType(actionType), groupSize, conditions); .targetFilterQuery(filterQuery).set(distributionSet).actionType(actionType),
groupSize, conditions);
// Run here, because Scheduler is disabled during tests // Run here, because Scheduler is disabled during tests
rolloutManagement.handleRollouts(); rolloutManagement.handleRollouts();
@@ -1092,8 +1060,8 @@ public class TestdataFactory {
* {@link Target}s. * {@link Target}s.
* *
* @param prefix * @param prefix
* for rollouts name, description, * for rollouts name, description, {@link Target#getControllerId()}
* {@link Target#getControllerId()} filter * filter
* @return created {@link Rollout} * @return created {@link Rollout}
*/ */
public Rollout createRollout(final String prefix) { public Rollout createRollout(final String prefix) {
@@ -1103,12 +1071,12 @@ public class TestdataFactory {
} }
/** /**
* Create the soft deleted {@link Rollout} with a new * Create the soft deleted {@link Rollout} with a new {@link DistributionSet}
* {@link DistributionSet} and {@link Target}s. * and {@link Target}s.
* *
* @param prefix * @param prefix
* for rollouts name, description, * for rollouts name, description, {@link Target#getControllerId()}
* {@link Target#getControllerId()} filter * filter
* @return created {@link Rollout} * @return created {@link Rollout}
*/ */
public Rollout createSoftDeletedRollout(final String prefix) { public Rollout createSoftDeletedRollout(final String prefix) {