Feature Reset Target Attributes (#664)

* Enhance DDI putConfigData REST entry point with update mode

Signed-off-by: stefbehl <stefan.behl@bosch-si.com>

* ControllerManagement unit tests for new target attribute update modes

Signed-off-by: stefbehl <stefan.behl@bosch-si.com>

* Added DdiRootControllerRestApi test for new update mode

Signed-off-by: stefbehl <stefan.behl@bosch-si.com>

* Enhance DMF UPDATE_ATTRIBUTES message with update mode

Signed-off-by: stefbehl <stefan.behl@bosch-si.com>

* Enhance DMF integration tests

Signed-off-by: stefbehl <stefan.behl@bosch-si.com>

* Enhance DMF integration tests

Signed-off-by: stefbehl <stefan.behl@bosch-si.com>

* Fix DMF integration tests

* Fix failing tests

Signed-off-by: stefbehl <stefan.behl@bosch-si.com>

* Fix failing tests

Signed-off-by: stefbehl <stefan.behl@bosch-si.com>

* Fix Sonar findings

Signed-off-by: stefbehl <stefan.behl@bosch-si.com>

* Javadoc improvements

Signed-off-by: stefbehl <stefan.behl@bosch-si.com>

* Fix codacy findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix PR review findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix PR review findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix PR review findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix PR review findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix PR review findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix PR review findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix PR review findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>
This commit is contained in:
Stefan Behl
2018-03-26 17:50:20 +02:00
committed by Kai Zimmermann
parent e700acc312
commit 607cf92a9e
19 changed files with 746 additions and 125 deletions

View File

@@ -22,11 +22,13 @@ import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
import org.eclipse.hawkbit.dmf.json.model.DmfActionUpdateStatus;
import org.eclipse.hawkbit.dmf.json.model.DmfAttributeUpdate;
import org.eclipse.hawkbit.dmf.json.model.DmfUpdateMode;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.UpdateMode;
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
@@ -248,8 +250,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
private void updateAttributes(final Message message) {
final DmfAttributeUpdate attributeUpdate = convertMessage(message, DmfAttributeUpdate.class);
final String thingId = getStringHeaderKey(message, MessageHeaderKey.THING_ID, "ThingId is null");
controllerManagement.updateControllerAttributes(thingId, attributeUpdate.getAttributes());
controllerManagement.updateControllerAttributes(thingId, attributeUpdate.getAttributes(),
getUpdateMode(attributeUpdate));
}
/**
@@ -364,4 +366,16 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
return findActionWithDetails.get();
}
/**
* Retrieve the update mode from the given update message.
*/
private static UpdateMode getUpdateMode(final DmfAttributeUpdate update) {
final DmfUpdateMode mode = update.getMode();
if (mode != null) {
return UpdateMode.valueOf(mode.name());
}
return null;
}
}

View File

@@ -36,9 +36,11 @@ import org.eclipse.hawkbit.dmf.json.model.DmfActionStatus;
import org.eclipse.hawkbit.dmf.json.model.DmfActionUpdateStatus;
import org.eclipse.hawkbit.dmf.json.model.DmfAttributeUpdate;
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadResponse;
import org.eclipse.hawkbit.dmf.json.model.DmfUpdateMode;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.UpdateMode;
import org.eclipse.hawkbit.repository.builder.ActionStatusBuilder;
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
@@ -123,6 +125,9 @@ public class AmqpMessageHandlerServiceTest {
@Captor
private ArgumentCaptor<String> targetIdCaptor;
@Captor
private ArgumentCaptor<UpdateMode> modeCaptor;
@Before
public void before() throws Exception {
messageConverter = new Jackson2JsonMessageConverter();
@@ -181,7 +186,7 @@ public class AmqpMessageHandlerServiceTest {
public void updateAttributes() {
final String knownThingId = "1";
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
messageProperties.setHeader(MessageHeaderKey.THING_ID, "1");
messageProperties.setHeader(MessageHeaderKey.THING_ID, knownThingId);
messageProperties.setHeader(MessageHeaderKey.TOPIC, "UPDATE_ATTRIBUTES");
final DmfAttributeUpdate attributeUpdate = new DmfAttributeUpdate();
attributeUpdate.getAttributes().put("testKey1", "testValue1");
@@ -190,8 +195,8 @@ public class AmqpMessageHandlerServiceTest {
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(attributeUpdate,
messageProperties);
when(controllerManagementMock.updateControllerAttributes(targetIdCaptor.capture(), attributesCaptor.capture()))
.thenReturn(null);
when(controllerManagementMock.updateControllerAttributes(targetIdCaptor.capture(), attributesCaptor.capture(),
modeCaptor.capture())).thenReturn(null);
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
@@ -202,6 +207,50 @@ public class AmqpMessageHandlerServiceTest {
}
@Test
@Description("Verifies that the update mode is retrieved from the UPDATE_ATTRIBUTES message and passed to the controller management.")
public void attributeUpdateModes() {
final String knownThingId = "1";
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
messageProperties.setHeader(MessageHeaderKey.THING_ID, knownThingId);
messageProperties.setHeader(MessageHeaderKey.TOPIC, "UPDATE_ATTRIBUTES");
final DmfAttributeUpdate attributeUpdate = new DmfAttributeUpdate();
attributeUpdate.getAttributes().put("testKey1", "testValue1");
attributeUpdate.getAttributes().put("testKey2", "testValue2");
when(controllerManagementMock.updateControllerAttributes(targetIdCaptor.capture(), attributesCaptor.capture(),
modeCaptor.capture())).thenReturn(null);
// send a message which does not specify a update mode
Message message = amqpMessageHandlerService.getMessageConverter().toMessage(attributeUpdate, messageProperties);
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
// verify that NO fallback is made on the way to the controller
// management layer
assertThat(modeCaptor.getValue()).isNull();
// send a message which specifies update mode MERGE
attributeUpdate.setMode(DmfUpdateMode.MERGE);
message = amqpMessageHandlerService.getMessageConverter().toMessage(attributeUpdate, messageProperties);
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
// verify that the update mode is converted and forwarded as expected
assertThat(modeCaptor.getValue()).isEqualTo(UpdateMode.MERGE);
// send a message which specifies update mode REPLACE
attributeUpdate.setMode(DmfUpdateMode.REPLACE);
message = amqpMessageHandlerService.getMessageConverter().toMessage(attributeUpdate, messageProperties);
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
// verify that the update mode is converted and forwarded as expected
assertThat(modeCaptor.getValue()).isEqualTo(UpdateMode.REPLACE);
// send a message which specifies update mode REMOVE
attributeUpdate.setMode(DmfUpdateMode.REMOVE);
message = amqpMessageHandlerService.getMessageConverter().toMessage(attributeUpdate, messageProperties);
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
// verify that the update mode is converted and forwarded as expected
assertThat(modeCaptor.getValue()).isEqualTo(UpdateMode.REMOVE);
}
@Test
@Description("Tests the creation of a thing without a 'reply to' header in message.")
public void createThingWitoutReplyTo() {

View File

@@ -10,7 +10,9 @@ package org.eclipse.hawkbit.integration;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -22,6 +24,7 @@ import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
import org.eclipse.hawkbit.dmf.json.model.DmfActionStatus;
import org.eclipse.hawkbit.dmf.json.model.DmfActionUpdateStatus;
import org.eclipse.hawkbit.dmf.json.model.DmfAttributeUpdate;
import org.eclipse.hawkbit.dmf.json.model.DmfUpdateMode;
import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
@@ -49,6 +52,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Step;
import ru.yandex.qatools.allure.annotations.Stories;
@Features("Component Tests - Device Management Federation API")
@@ -634,23 +638,104 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AmqpServiceIntegra
}
@Test
@Description("Verify that sending an update controller attribute message to an existing target works.")
@Description("Verify that sending an update controller attribute message to an existing target works. Verify that different update modes (merge, replace, remove) can be used.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void updateAttributes() {
@Expect(type = TargetUpdatedEvent.class, count = 4), @Expect(type = TargetPollEvent.class, count = 1) })
public void updateAttributesWithDifferentUpdateModes() {
final String controllerId = TARGET_PREFIX + "updateAttributes";
// setup
registerAndAssertTargetWithExistingTenant(controllerId);
final DmfAttributeUpdate controllerAttribute = new DmfAttributeUpdate();
controllerAttribute.getAttributes().put("test1", "testA");
controllerAttribute.getAttributes().put("test2", "testB");
// test
sendUpdateAttributeMessage(controllerId, TENANT_EXIST, controllerAttribute);
// no update mode specified
updateAttributesWithoutUpdateMode(controllerId);
// update mode REPLACE
updateAttributesWithUpdateModeReplace(controllerId);
// update mode REPLACE
updateAttributesWithUpdateModeMerge(controllerId);
// update mode REMOVE
updateAttributesWithUpdateModeRemove(controllerId);
}
@Step
private void updateAttributesWithUpdateModeRemove(final String controllerId) {
// assemble the expected attributes
final Map<String, String> expectedAttributes = targetManagement.getControllerAttributes(controllerId);
expectedAttributes.remove("k1");
expectedAttributes.remove("k3");
// send a update message with update mode
final Map<String, String> removeAttributes = new HashMap<>();
removeAttributes.put("k1", "foo");
removeAttributes.put("k3", "bar");
final DmfAttributeUpdate remove = new DmfAttributeUpdate();
remove.setMode(DmfUpdateMode.REMOVE);
remove.getAttributes().putAll(removeAttributes);
sendUpdateAttributeMessage(controllerId, TENANT_EXIST, remove);
// validate
assertUpdateAttributes(controllerId, controllerAttribute.getAttributes());
assertUpdateAttributes(controllerId, expectedAttributes);
}
@Step
private void updateAttributesWithUpdateModeMerge(final String controllerId) {
// get the current attributes
final Map<String, String> attributes = new HashMap<>(targetManagement.getControllerAttributes(controllerId));
// send a update message with update mode MERGE
final Map<String, String> mergeAttributes = new HashMap<>();
mergeAttributes.put("k1", "v1_modified_again");
mergeAttributes.put("k4", "v4");
final DmfAttributeUpdate merge = new DmfAttributeUpdate();
merge.setMode(DmfUpdateMode.MERGE);
merge.getAttributes().putAll(mergeAttributes);
sendUpdateAttributeMessage(controllerId, TENANT_EXIST, merge);
// validate
final Map<String, String> expectedAttributes = new HashMap<>();
expectedAttributes.putAll(attributes);
expectedAttributes.putAll(mergeAttributes);
assertUpdateAttributes(controllerId, expectedAttributes);
}
@Step
private void updateAttributesWithUpdateModeReplace(final String controllerId) {
// send a update message with update mode REPLACE
final Map<String, String> replacementAttributes = new HashMap<>();
replacementAttributes.put("k1", "v1_modified");
replacementAttributes.put("k2", "v2");
replacementAttributes.put("k3", "v3");
final DmfAttributeUpdate replace = new DmfAttributeUpdate();
replace.setMode(DmfUpdateMode.REPLACE);
replace.getAttributes().putAll(replacementAttributes);
sendUpdateAttributeMessage(controllerId, TENANT_EXIST, replace);
// validate
final Map<String, String> expectedAttributes = replacementAttributes;
assertUpdateAttributes(controllerId, expectedAttributes);
}
@Step
private void updateAttributesWithoutUpdateMode(final String controllerId) {
// send a update message which does not specify a update mode
final Map<String, String> initialAttributes = new HashMap<>();
initialAttributes.put("k0", "v0");
initialAttributes.put("k1", "v1");
final DmfAttributeUpdate defaultUpdate = new DmfAttributeUpdate();
defaultUpdate.getAttributes().putAll(initialAttributes);
sendUpdateAttributeMessage(controllerId, TENANT_EXIST, defaultUpdate);
// validate
final Map<String, String> expectedAttributes = initialAttributes;
assertUpdateAttributes(controllerId, expectedAttributes);
}
@Test

View File

@@ -49,6 +49,8 @@ import org.springframework.amqp.rabbit.test.RabbitListenerTestHarness;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import ru.yandex.qatools.allure.annotations.Step;
/**
*
* Common class for {@link AmqpMessageHandlerServiceIntegrationTest} and
@@ -233,6 +235,7 @@ public abstract class AmqpServiceIntegrationTest extends AbstractAmqpIntegration
return replyMessage;
}
@Step
protected void registerAndAssertTargetWithExistingTenant(final String controllerId) {
registerAndAssertTargetWithExistingTenant(controllerId, 1);
}
@@ -326,6 +329,7 @@ public abstract class AmqpServiceIntegrationTest extends AbstractAmqpIntegration
}
@Step
protected void assertUpdateAttributes(final String controllerId, final Map<String, String> attributes) {
final Target findByControllerId = waitUntilIsPresent(
() -> controllerManagement.getByControllerId(controllerId));