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:
committed by
Kai Zimmermann
parent
e700acc312
commit
607cf92a9e
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.dmf.json.model;
|
||||
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -18,16 +17,28 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* Map of {@link Target} attributes.
|
||||
*
|
||||
* JSON representation of the Attribute Update message.
|
||||
*/
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class DmfAttributeUpdate {
|
||||
|
||||
@JsonProperty
|
||||
private final Map<String, String> attributes = new HashMap<>();
|
||||
|
||||
@JsonProperty
|
||||
private DmfUpdateMode mode;
|
||||
|
||||
public DmfUpdateMode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public void setMode(final DmfUpdateMode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public Map<String, String> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2018 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;
|
||||
|
||||
/**
|
||||
* Enumerates the supported update modes. Each mode represents an attribute
|
||||
* update strategy.
|
||||
*
|
||||
* @see DmfAttributeUpdate
|
||||
*/
|
||||
public enum DmfUpdateMode {
|
||||
|
||||
/**
|
||||
* Merge update strategy
|
||||
*/
|
||||
MERGE,
|
||||
|
||||
/**
|
||||
* Replacement update strategy
|
||||
*/
|
||||
REPLACE,
|
||||
|
||||
/**
|
||||
* Removal update strategy
|
||||
*/
|
||||
REMOVE;
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user