Optional controller attributes for THING_CREATED (#1154)
* added optional attributes payload to THING_CREATED DMF message Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * added optional attributes unit test for THING_CREATED message handler Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * extended Target interface with getControllerAttributes method, extended DMF message handler integration tests Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * updated hawkbit docs Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * fixed review findings Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io>
This commit is contained in:
@@ -63,11 +63,19 @@ Payload Template (optional):
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"name": "String"
|
"name": "String",
|
||||||
|
"attributeUpdate": {
|
||||||
|
"attributes": {
|
||||||
|
"exampleKey1" : "exampleValue1",
|
||||||
|
"exampleKey2" : "exampleValue2"
|
||||||
|
},
|
||||||
|
"mode": "String"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The "name" property specifies the name of the thing, which by default is the thing ID. This property is optional.
|
The "name" property specifies the name of the thing, which by default is the thing ID. This property is optional.<br />
|
||||||
|
The "attributeUpdate" property provides the attributes of the thing, for details see UPDATE_ATTRIBUTES message. This property is optional.
|
||||||
|
|
||||||
|
|
||||||
### THING_REMOVED
|
### THING_REMOVED
|
||||||
@@ -93,7 +101,7 @@ type=THING\_REMOVED <br /> tenant=default <br /> thingId=abc | content\_type=app
|
|||||||
|
|
||||||
### UPDATE_ATTRIBUTES
|
### UPDATE_ATTRIBUTES
|
||||||
|
|
||||||
Message to update target attributes. This message can be send in response to a _REQUEST_ATTRIBUTES_UPDATE_ event, sent by hawkBit.
|
Message to update target attributes. This message can be send in response to a REQUEST_ATTRIBUTES_UPDATE event, sent by hawkBit.
|
||||||
|
|
||||||
| Header | Description | Type | Mandatory
|
| Header | Description | Type | Mandatory
|
||||||
|-----------------------------|----------------------------------|-------------------------------------|----------------
|
|-----------------------------|----------------------------------|-------------------------------------|----------------
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ 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;
|
||||||
@@ -53,7 +52,6 @@ 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.messaging.handler.annotation.Header;
|
import org.springframework.messaging.handler.annotation.Header;
|
||||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
@@ -201,12 +199,12 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to create a new target or to find the target if it already exists and
|
* Method to create a new target or to find the target if it already exists
|
||||||
* update its poll time, status and optionally its name.
|
* and update its poll time, status and optionally its name and attributes.
|
||||||
*
|
*
|
||||||
* @param message
|
* @param message
|
||||||
* the message that contains replyTo property and optionally the name
|
* the message that contains replyTo property and optionally the
|
||||||
* in body
|
* name and attributes in body
|
||||||
* @param virtualHost
|
* @param virtualHost
|
||||||
* the virtual host
|
* the virtual host
|
||||||
*/
|
*/
|
||||||
@@ -225,14 +223,22 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
|||||||
target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(thingId, amqpUri);
|
target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(thingId, amqpUri);
|
||||||
} else {
|
} else {
|
||||||
checkContentTypeJson(message);
|
checkContentTypeJson(message);
|
||||||
|
final DmfCreateThing thingCreateBody = convertMessage(message, DmfCreateThing.class);
|
||||||
|
final DmfAttributeUpdate thingAttributeUpdateBody = thingCreateBody.getAttributeUpdate();
|
||||||
|
|
||||||
target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(thingId, amqpUri, convertMessage(message, DmfCreateThing.class).getName());
|
target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(thingId, amqpUri,
|
||||||
|
thingCreateBody.getName());
|
||||||
|
|
||||||
|
if (thingAttributeUpdateBody != null) {
|
||||||
|
controllerManagement.updateControllerAttributes(thingId, thingAttributeUpdateBody.getAttributes(),
|
||||||
|
getUpdateMode(thingAttributeUpdateBody));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
LOG.debug("Target {} reported online state.", thingId);
|
LOG.debug("Target {} reported online state.", thingId);
|
||||||
sendUpdateCommandToTarget(target);
|
sendUpdateCommandToTarget(target);
|
||||||
} catch (final EntityAlreadyExistsException e) {
|
} catch (final EntityAlreadyExistsException e) {
|
||||||
throw new AmqpRejectAndDontRequeueException("Tried to register previously registered target, message will be ignored!", e);
|
throw new AmqpRejectAndDontRequeueException(
|
||||||
|
"Tried to register previously registered target, message will be ignored!", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,8 +257,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sendCurrentActionsAsMultiActionToTarget(final Target target) {
|
private void sendCurrentActionsAsMultiActionToTarget(final Target target) {
|
||||||
final List<Action> actions = controllerManagement
|
final List<Action> actions = controllerManagement.findActiveActionsWithHighestWeight(target.getControllerId(),
|
||||||
.findActiveActionsWithHighestWeight(target.getControllerId(), MAX_ACTION_COUNT);
|
MAX_ACTION_COUNT);
|
||||||
|
|
||||||
final Set<DistributionSet> distributionSets = actions.stream().map(Action::getDistributionSet)
|
final Set<DistributionSet> distributionSets = actions.stream().map(Action::getDistributionSet)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationPrope
|
|||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.lenient;
|
import static org.mockito.Mockito.lenient;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
@@ -79,6 +79,7 @@ import org.springframework.http.HttpStatus;
|
|||||||
|
|
||||||
import io.qameta.allure.Description;
|
import io.qameta.allure.Description;
|
||||||
import io.qameta.allure.Feature;
|
import io.qameta.allure.Feature;
|
||||||
|
import io.qameta.allure.Step;
|
||||||
import io.qameta.allure.Story;
|
import io.qameta.allure.Story;
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
@@ -143,6 +144,9 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
@Captor
|
@Captor
|
||||||
private ArgumentCaptor<String> targetIdCaptor;
|
private ArgumentCaptor<String> targetIdCaptor;
|
||||||
|
|
||||||
|
@Captor
|
||||||
|
private ArgumentCaptor<String> targetNameCaptor;
|
||||||
|
|
||||||
@Captor
|
@Captor
|
||||||
private ArgumentCaptor<URI> uriCaptor;
|
private ArgumentCaptor<URI> uriCaptor;
|
||||||
|
|
||||||
@@ -185,50 +189,64 @@ 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");
|
processThingCreatedMessage(knownThingId, null);
|
||||||
final Message message = messageConverter.toMessage(new byte[0], messageProperties);
|
|
||||||
|
assertThingIdCapturedField(knownThingId);
|
||||||
|
assertReplyToCapturedField("MyTest");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private void processThingCreatedMessage(final String thingId, final DmfCreateThing payload) {
|
||||||
|
final MessageProperties messageProperties = getThingCreatedMessageProperties(thingId);
|
||||||
|
final Message message = createMessage(payload != null ? payload : new byte[0], messageProperties);
|
||||||
|
|
||||||
final Target targetMock = mock(Target.class);
|
final Target targetMock = mock(Target.class);
|
||||||
|
if (payload == null) {
|
||||||
final ArgumentCaptor<String> targetIdCaptor = ArgumentCaptor.forClass(String.class);
|
when(controllerManagementMock.findOrRegisterTargetIfItDoesNotExist(targetIdCaptor.capture(),
|
||||||
final ArgumentCaptor<URI> uriCaptor = ArgumentCaptor.forClass(URI.class);
|
uriCaptor.capture())).thenReturn(targetMock);
|
||||||
when(controllerManagementMock.findOrRegisterTargetIfItDoesNotExist(targetIdCaptor.capture(),
|
} else {
|
||||||
uriCaptor.capture())).thenReturn(targetMock);
|
when(controllerManagementMock.findOrRegisterTargetIfItDoesNotExist(targetIdCaptor.capture(),
|
||||||
|
uriCaptor.capture(), targetNameCaptor.capture())).thenReturn(targetMock);
|
||||||
|
if (payload.getAttributeUpdate() != null) {
|
||||||
|
when(controllerManagementMock.updateControllerAttributes(targetIdCaptor.capture(),
|
||||||
|
attributesCaptor.capture(), modeCaptor.capture())).thenReturn(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
when(controllerManagementMock.findActiveActionWithHighestWeight(any())).thenReturn(Optional.empty());
|
when(controllerManagementMock.findActiveActionWithHighestWeight(any())).thenReturn(Optional.empty());
|
||||||
|
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, VIRTUAL_HOST);
|
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, VIRTUAL_HOST);
|
||||||
|
}
|
||||||
|
|
||||||
// verify
|
@Step
|
||||||
assertThat(targetIdCaptor.getValue()).as("Thing id is wrong").isEqualTo(knownThingId);
|
private void assertThingIdCapturedField(final String thingId) {
|
||||||
assertThat(uriCaptor.getValue().toString()).as("Uri is not right")
|
assertThat(targetIdCaptor.getValue()).as("Thing id is wrong").isEqualTo(thingId);
|
||||||
.isEqualTo("amqp://" + VIRTUAL_HOST + "/MyTest");
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private void assertReplyToCapturedField(final String replyTo) {
|
||||||
|
assertThat(uriCaptor.getValue()).as("Uri is not right").hasToString("amqp://" + VIRTUAL_HOST + "/" + replyTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Tests the creation of a target/thing with specified name by calling the same method that incoming RabbitMQ messages would access.")
|
@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() {
|
public void createThingWithName() {
|
||||||
final String knownThingId = "2";
|
final String knownThingId = "2";
|
||||||
final DmfCreateThing targetProperties = new DmfCreateThing();
|
final String knownThingName = "NonDefaultTargetName";
|
||||||
targetProperties.setName("NonDefaultTargetName");
|
|
||||||
|
|
||||||
final Target targetMock = mock(Target.class);
|
final DmfCreateThing payload = new DmfCreateThing();
|
||||||
|
payload.setName(knownThingName);
|
||||||
|
|
||||||
targetIdCaptor = ArgumentCaptor.forClass(String.class);
|
processThingCreatedMessage(knownThingId, payload);
|
||||||
uriCaptor = ArgumentCaptor.forClass(URI.class);
|
|
||||||
final ArgumentCaptor<String> targetNameCaptor = ArgumentCaptor.forClass(String.class);
|
|
||||||
|
|
||||||
when(controllerManagementMock.findOrRegisterTargetIfItDoesNotExist(targetIdCaptor.capture(),
|
assertThingIdCapturedField(knownThingId);
|
||||||
uriCaptor.capture(), targetNameCaptor.capture())).thenReturn(targetMock);
|
assertReplyToCapturedField("MyTest");
|
||||||
when(controllerManagementMock.findActiveActionWithHighestWeight(any())).thenReturn(Optional.empty());
|
assertThingNameCapturedField(knownThingName);
|
||||||
|
}
|
||||||
|
|
||||||
amqpMessageHandlerService.onMessage(
|
@Step
|
||||||
createMessage(targetProperties, getThingCreatedMessageProperties(knownThingId)),
|
private void assertThingNameCapturedField(final String thingName) {
|
||||||
MessageType.THING_CREATED.name(), TENANT, "vHost");
|
assertThat(targetNameCaptor.getValue()).as("Thing name is wrong").isEqualTo(thingName);
|
||||||
|
|
||||||
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
|
@Test
|
||||||
@@ -243,6 +261,58 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
MessageType.THING_CREATED.name(), TENANT, VIRTUAL_HOST));
|
MessageType.THING_CREATED.name(), TENANT, VIRTUAL_HOST));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Tests the creation of a target/thing with specified attributes by calling the same method that incoming RabbitMQ messages would access.")
|
||||||
|
public void createThingWithAttributes() {
|
||||||
|
final String knownThingId = "4";
|
||||||
|
|
||||||
|
final DmfAttributeUpdate attributeUpdate = new DmfAttributeUpdate();
|
||||||
|
attributeUpdate.getAttributes().put("testKey1", "testValue1");
|
||||||
|
attributeUpdate.getAttributes().put("testKey2", "testValue2");
|
||||||
|
|
||||||
|
final DmfCreateThing payload = new DmfCreateThing();
|
||||||
|
payload.setAttributeUpdate(attributeUpdate);
|
||||||
|
|
||||||
|
processThingCreatedMessage(knownThingId, payload);
|
||||||
|
|
||||||
|
assertThingIdCapturedField(knownThingId);
|
||||||
|
assertReplyToCapturedField("MyTest");
|
||||||
|
assertThingAttributesCapturedField(attributeUpdate.getAttributes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private void assertThingAttributesCapturedField(final Map<String, String> attributes) {
|
||||||
|
assertThat(attributesCaptor.getValue()).as("Attributes is not right").isEqualTo(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Tests the creation of a target/thing with specified name and attributes by calling the same method that incoming RabbitMQ messages would access.")
|
||||||
|
public void createThingWithNameAndAttributes() {
|
||||||
|
final String knownThingId = "5";
|
||||||
|
final String knownThingName = "NonDefaultTargetName";
|
||||||
|
|
||||||
|
final DmfAttributeUpdate attributeUpdate = new DmfAttributeUpdate();
|
||||||
|
attributeUpdate.getAttributes().put("testKey1", "testValue1");
|
||||||
|
attributeUpdate.getAttributes().put("testKey2", "testValue2");
|
||||||
|
attributeUpdate.setMode(DmfUpdateMode.REPLACE);
|
||||||
|
|
||||||
|
final DmfCreateThing payload = new DmfCreateThing();
|
||||||
|
payload.setName(knownThingName);
|
||||||
|
payload.setAttributeUpdate(attributeUpdate);
|
||||||
|
|
||||||
|
processThingCreatedMessage(knownThingId, payload);
|
||||||
|
|
||||||
|
assertThingIdCapturedField(knownThingId);
|
||||||
|
assertReplyToCapturedField("MyTest");
|
||||||
|
assertThingAttributesCapturedField(attributeUpdate.getAttributes());
|
||||||
|
assertThingAttributesModeCapturedField(UpdateMode.REPLACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private void assertThingAttributesModeCapturedField(final UpdateMode attributesUpdateMode) {
|
||||||
|
assertThat(modeCaptor.getValue()).as("Attributes update mode is not right").isEqualTo(attributesUpdateMode);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Tests the target attribute update by calling the same method that incoming RabbitMQ messages would access.")
|
@Description("Tests the target attribute update by calling the same method that incoming RabbitMQ messages would access.")
|
||||||
public void updateAttributes() {
|
public void updateAttributes() {
|
||||||
@@ -254,18 +324,15 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
attributeUpdate.getAttributes().put("testKey1", "testValue1");
|
attributeUpdate.getAttributes().put("testKey1", "testValue1");
|
||||||
attributeUpdate.getAttributes().put("testKey2", "testValue2");
|
attributeUpdate.getAttributes().put("testKey2", "testValue2");
|
||||||
|
|
||||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(attributeUpdate,
|
final Message message = createMessage(attributeUpdate, messageProperties);
|
||||||
messageProperties);
|
|
||||||
|
|
||||||
when(controllerManagementMock.updateControllerAttributes(targetIdCaptor.capture(), attributesCaptor.capture(),
|
when(controllerManagementMock.updateControllerAttributes(targetIdCaptor.capture(), attributesCaptor.capture(),
|
||||||
modeCaptor.capture())).thenReturn(null);
|
modeCaptor.capture())).thenReturn(null);
|
||||||
|
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, VIRTUAL_HOST);
|
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, VIRTUAL_HOST);
|
||||||
|
|
||||||
// verify
|
assertThingIdCapturedField(knownThingId);
|
||||||
assertThat(targetIdCaptor.getValue()).as("Thing id is wrong").isEqualTo(knownThingId);
|
assertThingAttributesCapturedField(attributeUpdate.getAttributes());
|
||||||
assertThat(attributesCaptor.getValue()).as("Attributes is not right")
|
|
||||||
.isEqualTo(attributeUpdate.getAttributes());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -283,32 +350,32 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
modeCaptor.capture())).thenReturn(null);
|
modeCaptor.capture())).thenReturn(null);
|
||||||
|
|
||||||
// send a message which does not specify a update mode
|
// send a message which does not specify a update mode
|
||||||
Message message = amqpMessageHandlerService.getMessageConverter().toMessage(attributeUpdate, messageProperties);
|
Message message = createMessage(attributeUpdate, messageProperties);
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, VIRTUAL_HOST);
|
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, VIRTUAL_HOST);
|
||||||
// verify that NO fallback is made on the way to the controller
|
// verify that NO fallback is made on the way to the controller
|
||||||
// management layer
|
// management layer
|
||||||
assertThat(modeCaptor.getValue()).isNull();
|
assertThingAttributesModeCapturedField(null);
|
||||||
|
|
||||||
// send a message which specifies update mode MERGE
|
// send a message which specifies update mode MERGE
|
||||||
attributeUpdate.setMode(DmfUpdateMode.MERGE);
|
attributeUpdate.setMode(DmfUpdateMode.MERGE);
|
||||||
message = amqpMessageHandlerService.getMessageConverter().toMessage(attributeUpdate, messageProperties);
|
message = createMessage(attributeUpdate, messageProperties);
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, VIRTUAL_HOST);
|
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, VIRTUAL_HOST);
|
||||||
// verify that the update mode is converted and forwarded as expected
|
// verify that the update mode is converted and forwarded as expected
|
||||||
assertThat(modeCaptor.getValue()).isEqualTo(UpdateMode.MERGE);
|
assertThingAttributesModeCapturedField(UpdateMode.MERGE);
|
||||||
|
|
||||||
// send a message which specifies update mode REPLACE
|
// send a message which specifies update mode REPLACE
|
||||||
attributeUpdate.setMode(DmfUpdateMode.REPLACE);
|
attributeUpdate.setMode(DmfUpdateMode.REPLACE);
|
||||||
message = amqpMessageHandlerService.getMessageConverter().toMessage(attributeUpdate, messageProperties);
|
message = createMessage(attributeUpdate, messageProperties);
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, VIRTUAL_HOST);
|
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, VIRTUAL_HOST);
|
||||||
// verify that the update mode is converted and forwarded as expected
|
// verify that the update mode is converted and forwarded as expected
|
||||||
assertThat(modeCaptor.getValue()).isEqualTo(UpdateMode.REPLACE);
|
assertThingAttributesModeCapturedField(UpdateMode.REPLACE);
|
||||||
|
|
||||||
// send a message which specifies update mode REMOVE
|
// send a message which specifies update mode REMOVE
|
||||||
attributeUpdate.setMode(DmfUpdateMode.REMOVE);
|
attributeUpdate.setMode(DmfUpdateMode.REMOVE);
|
||||||
message = amqpMessageHandlerService.getMessageConverter().toMessage(attributeUpdate, messageProperties);
|
message = createMessage(attributeUpdate, messageProperties);
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, VIRTUAL_HOST);
|
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, VIRTUAL_HOST);
|
||||||
// verify that the update mode is converted and forwarded as expected
|
// verify that the update mode is converted and forwarded as expected
|
||||||
assertThat(modeCaptor.getValue()).isEqualTo(UpdateMode.REMOVE);
|
assertThingAttributesModeCapturedField(UpdateMode.REMOVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -316,7 +383,7 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
public void createThingWithoutReplyTo() {
|
public void createThingWithoutReplyTo() {
|
||||||
final MessageProperties messageProperties = createMessageProperties(MessageType.THING_CREATED, null);
|
final MessageProperties messageProperties = createMessageProperties(MessageType.THING_CREATED, null);
|
||||||
messageProperties.setHeader(MessageHeaderKey.THING_ID, "1");
|
messageProperties.setHeader(MessageHeaderKey.THING_ID, "1");
|
||||||
final Message message = messageConverter.toMessage("", messageProperties);
|
final Message message = createMessage("", messageProperties);
|
||||||
|
|
||||||
assertThatExceptionOfType(AmqpRejectAndDontRequeueException.class)
|
assertThatExceptionOfType(AmqpRejectAndDontRequeueException.class)
|
||||||
.as(FAIL_MESSAGE_AMQP_REJECT_REASON + "since no replyTo header was set")
|
.as(FAIL_MESSAGE_AMQP_REJECT_REASON + "since no replyTo header was set")
|
||||||
@@ -328,7 +395,7 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
@Description("Tests the creation of a target/thing without a thingID by calling the same method that incoming RabbitMQ messages would access.")
|
@Description("Tests the creation of a target/thing without a thingID by calling the same method that incoming RabbitMQ messages would access.")
|
||||||
public void createThingWithoutID() {
|
public void createThingWithoutID() {
|
||||||
final MessageProperties messageProperties = createMessageProperties(MessageType.THING_CREATED);
|
final MessageProperties messageProperties = createMessageProperties(MessageType.THING_CREATED);
|
||||||
final Message message = messageConverter.toMessage(new byte[0], messageProperties);
|
final Message message = createMessage(new byte[0], messageProperties);
|
||||||
|
|
||||||
assertThatExceptionOfType(AmqpRejectAndDontRequeueException.class)
|
assertThatExceptionOfType(AmqpRejectAndDontRequeueException.class)
|
||||||
.as(FAIL_MESSAGE_AMQP_REJECT_REASON + "since no thingId was set")
|
.as(FAIL_MESSAGE_AMQP_REJECT_REASON + "since no thingId was set")
|
||||||
@@ -342,7 +409,7 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
final String type = "bumlux";
|
final String type = "bumlux";
|
||||||
final MessageProperties messageProperties = createMessageProperties(MessageType.THING_CREATED);
|
final MessageProperties messageProperties = createMessageProperties(MessageType.THING_CREATED);
|
||||||
messageProperties.setHeader(MessageHeaderKey.THING_ID, "");
|
messageProperties.setHeader(MessageHeaderKey.THING_ID, "");
|
||||||
final Message message = messageConverter.toMessage(new byte[0], messageProperties);
|
final Message message = createMessage(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")
|
||||||
@@ -378,8 +445,7 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
|
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
|
||||||
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
|
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
|
||||||
final DmfActionUpdateStatus actionUpdateStatus = new DmfActionUpdateStatus(1L, DmfActionStatus.DOWNLOAD);
|
final DmfActionUpdateStatus actionUpdateStatus = new DmfActionUpdateStatus(1L, DmfActionStatus.DOWNLOAD);
|
||||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(actionUpdateStatus,
|
final Message message = createMessage(actionUpdateStatus, messageProperties);
|
||||||
messageProperties);
|
|
||||||
|
|
||||||
assertThatExceptionOfType(AmqpRejectAndDontRequeueException.class)
|
assertThatExceptionOfType(AmqpRejectAndDontRequeueException.class)
|
||||||
.as(FAIL_MESSAGE_AMQP_REJECT_REASON + "since no action id was set")
|
.as(FAIL_MESSAGE_AMQP_REJECT_REASON + "since no action id was set")
|
||||||
@@ -395,8 +461,7 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
when(controllerManagementMock.findActionWithDetails(anyLong())).thenReturn(Optional.empty());
|
when(controllerManagementMock.findActionWithDetails(anyLong())).thenReturn(Optional.empty());
|
||||||
|
|
||||||
final DmfActionUpdateStatus actionUpdateStatus = createActionUpdateStatus(DmfActionStatus.DOWNLOAD);
|
final DmfActionUpdateStatus actionUpdateStatus = createActionUpdateStatus(DmfActionStatus.DOWNLOAD);
|
||||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(actionUpdateStatus,
|
final Message message = createMessage(actionUpdateStatus, messageProperties);
|
||||||
messageProperties);
|
|
||||||
|
|
||||||
assertThatExceptionOfType(AmqpRejectAndDontRequeueException.class)
|
assertThatExceptionOfType(AmqpRejectAndDontRequeueException.class)
|
||||||
.as(FAIL_MESSAGE_AMQP_REJECT_REASON + "since no action id was set")
|
.as(FAIL_MESSAGE_AMQP_REJECT_REASON + "since no action id was set")
|
||||||
@@ -410,8 +475,7 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
final MessageProperties messageProperties = createMessageProperties(null);
|
final MessageProperties messageProperties = createMessageProperties(null);
|
||||||
final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID,
|
final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID,
|
||||||
TARGET_ID, FileResource.createFileResourceBySha1("12345"));
|
TARGET_ID, FileResource.createFileResourceBySha1("12345"));
|
||||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
|
final Message message = createMessage(securityToken, messageProperties);
|
||||||
messageProperties);
|
|
||||||
|
|
||||||
// test
|
// test
|
||||||
final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message);
|
final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message);
|
||||||
@@ -429,8 +493,7 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
final MessageProperties messageProperties = createMessageProperties(null);
|
final MessageProperties messageProperties = createMessageProperties(null);
|
||||||
final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID,
|
final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID,
|
||||||
TARGET_ID, FileResource.createFileResourceBySha1("12345"));
|
TARGET_ID, FileResource.createFileResourceBySha1("12345"));
|
||||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
|
final Message message = createMessage(securityToken, messageProperties);
|
||||||
messageProperties);
|
|
||||||
|
|
||||||
final Artifact localArtifactMock = mock(Artifact.class);
|
final Artifact localArtifactMock = mock(Artifact.class);
|
||||||
when(artifactManagementMock.findFirstBySHA1(anyString())).thenReturn(Optional.of(localArtifactMock));
|
when(artifactManagementMock.findFirstBySHA1(anyString())).thenReturn(Optional.of(localArtifactMock));
|
||||||
@@ -451,8 +514,7 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
final MessageProperties messageProperties = createMessageProperties(null);
|
final MessageProperties messageProperties = createMessageProperties(null);
|
||||||
final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID,
|
final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID,
|
||||||
TARGET_ID, FileResource.createFileResourceBySha1("12345"));
|
TARGET_ID, FileResource.createFileResourceBySha1("12345"));
|
||||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
|
final Message message = createMessage(securityToken, messageProperties);
|
||||||
messageProperties);
|
|
||||||
|
|
||||||
// mock
|
// mock
|
||||||
final Artifact localArtifactMock = mock(Artifact.class);
|
final Artifact localArtifactMock = mock(Artifact.class);
|
||||||
@@ -494,14 +556,12 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
when(create.status(any())).thenReturn(create);
|
when(create.status(any())).thenReturn(create);
|
||||||
when(entityFactoryMock.actionStatus()).thenReturn(builder);
|
when(entityFactoryMock.actionStatus()).thenReturn(builder);
|
||||||
// for the test the same action can be used
|
// for the test the same action can be used
|
||||||
when(controllerManagementMock.findActiveActionWithHighestWeight(any()))
|
when(controllerManagementMock.findActiveActionWithHighestWeight(any())).thenReturn(Optional.of(action));
|
||||||
.thenReturn(Optional.of(action));
|
|
||||||
|
|
||||||
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
|
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
|
||||||
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
|
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
|
||||||
final DmfActionUpdateStatus actionUpdateStatus = createActionUpdateStatus(DmfActionStatus.FINISHED, 23L);
|
final DmfActionUpdateStatus actionUpdateStatus = createActionUpdateStatus(DmfActionStatus.FINISHED, 23L);
|
||||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(actionUpdateStatus,
|
final Message message = createMessage(actionUpdateStatus, messageProperties);
|
||||||
messageProperties);
|
|
||||||
|
|
||||||
// test
|
// test
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, VIRTUAL_HOST);
|
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, VIRTUAL_HOST);
|
||||||
@@ -579,7 +639,7 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
final String knownThingId = "1";
|
final String knownThingId = "1";
|
||||||
final MessageProperties messageProperties = createMessageProperties(MessageType.THING_REMOVED);
|
final MessageProperties messageProperties = createMessageProperties(MessageType.THING_REMOVED);
|
||||||
messageProperties.setHeader(MessageHeaderKey.THING_ID, knownThingId);
|
messageProperties.setHeader(MessageHeaderKey.THING_ID, knownThingId);
|
||||||
final Message message = messageConverter.toMessage(new byte[0], messageProperties);
|
final Message message = createMessage(new byte[0], messageProperties);
|
||||||
|
|
||||||
// test
|
// test
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.THING_REMOVED.name(), TENANT, VIRTUAL_HOST);
|
amqpMessageHandlerService.onMessage(message, MessageType.THING_REMOVED.name(), TENANT, VIRTUAL_HOST);
|
||||||
@@ -593,7 +653,7 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
public void deleteThingWithoutThingId() {
|
public void deleteThingWithoutThingId() {
|
||||||
// prepare invalid message
|
// prepare invalid message
|
||||||
final MessageProperties messageProperties = createMessageProperties(MessageType.THING_REMOVED);
|
final MessageProperties messageProperties = createMessageProperties(MessageType.THING_REMOVED);
|
||||||
final Message message = messageConverter.toMessage(new byte[0], messageProperties);
|
final Message message = createMessage(new byte[0], messageProperties);
|
||||||
|
|
||||||
assertThatExceptionOfType(AmqpRejectAndDontRequeueException.class)
|
assertThatExceptionOfType(AmqpRejectAndDontRequeueException.class)
|
||||||
.as(FAIL_MESSAGE_AMQP_REJECT_REASON + "since no thingId was set")
|
.as(FAIL_MESSAGE_AMQP_REJECT_REASON + "since no thingId was set")
|
||||||
@@ -601,13 +661,13 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
VIRTUAL_HOST));
|
VIRTUAL_HOST));
|
||||||
}
|
}
|
||||||
|
|
||||||
private MessageProperties getThingCreatedMessageProperties(String thingId) {
|
private MessageProperties getThingCreatedMessageProperties(final String thingId) {
|
||||||
final MessageProperties messageProperties = createMessageProperties(MessageType.THING_CREATED);
|
final MessageProperties messageProperties = createMessageProperties(MessageType.THING_CREATED);
|
||||||
messageProperties.setHeader(MessageHeaderKey.THING_ID, thingId);
|
messageProperties.setHeader(MessageHeaderKey.THING_ID, thingId);
|
||||||
return messageProperties;
|
return messageProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Message createMessage(Object object, MessageProperties messageProperties) {
|
private Message createMessage(final Object object, final MessageProperties messageProperties) {
|
||||||
return messageConverter.toMessage(object, messageProperties);
|
return messageConverter.toMessage(object, messageProperties);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -28,6 +29,7 @@ import org.eclipse.hawkbit.dmf.json.model.DmfActionRequest;
|
|||||||
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.DmfDownloadAndUpdateRequest;
|
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadAndUpdateRequest;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DmfMetadata;
|
import org.eclipse.hawkbit.dmf.json.model.DmfMetadata;
|
||||||
import org.eclipse.hawkbit.integration.listener.DeadletterListener;
|
import org.eclipse.hawkbit.integration.listener.DeadletterListener;
|
||||||
@@ -54,6 +56,9 @@ import org.springframework.amqp.rabbit.test.RabbitListenerTestHarness;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration;
|
import org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import com.cronutils.utils.StringUtils;
|
||||||
|
|
||||||
import io.qameta.allure.Step;
|
import io.qameta.allure.Step;
|
||||||
|
|
||||||
@@ -92,7 +97,8 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
|
|||||||
|
|
||||||
private <T> T waitUntilIsPresent(final Callable<Optional<T>> callable) {
|
private <T> T waitUntilIsPresent(final Callable<Optional<T>> callable) {
|
||||||
|
|
||||||
createConditionFactory().until(() -> WithSpringAuthorityRule.runAsPrivileged(() -> callable.call().isPresent()));
|
createConditionFactory()
|
||||||
|
.until(() -> WithSpringAuthorityRule.runAsPrivileged(() -> callable.call().isPresent()));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return WithSpringAuthorityRule.runAsPrivileged(() -> callable.call().get());
|
return WithSpringAuthorityRule.runAsPrivileged(() -> callable.call().get());
|
||||||
@@ -188,7 +194,8 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
|
|||||||
|
|
||||||
protected void assertDmfDownloadAndUpdateRequest(final DmfDownloadAndUpdateRequest request,
|
protected void assertDmfDownloadAndUpdateRequest(final DmfDownloadAndUpdateRequest request,
|
||||||
final Set<SoftwareModule> softwareModules, final String controllerId) {
|
final Set<SoftwareModule> softwareModules, final String controllerId) {
|
||||||
assertThat(softwareModules).is(new HamcrestCondition<>(SoftwareModuleJsonMatcher.containsExactly(request.getSoftwareModules())));
|
assertThat(softwareModules)
|
||||||
|
.is(new HamcrestCondition<>(SoftwareModuleJsonMatcher.containsExactly(request.getSoftwareModules())));
|
||||||
request.getSoftwareModules().forEach(dmfModule -> assertThat(dmfModule.getMetadata()).containsExactly(
|
request.getSoftwareModules().forEach(dmfModule -> assertThat(dmfModule.getMetadata()).containsExactly(
|
||||||
new DmfMetadata(TestdataFactory.VISIBLE_SM_MD_KEY, TestdataFactory.VISIBLE_SM_MD_VALUE)));
|
new DmfMetadata(TestdataFactory.VISIBLE_SM_MD_KEY, TestdataFactory.VISIBLE_SM_MD_VALUE)));
|
||||||
final Target updatedTarget = waitUntilIsPresent(() -> targetManagement.getByControllerID(controllerId));
|
final Target updatedTarget = waitUntilIsPresent(() -> targetManagement.getByControllerID(controllerId));
|
||||||
@@ -205,8 +212,13 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
|
|||||||
assertAssignmentMessage(dsModules, controllerId, EventTopic.DOWNLOAD);
|
assertAssignmentMessage(dsModules, controllerId, EventTopic.DOWNLOAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void createAndSendThingCreated(final String target, final String tenant) {
|
protected void createAndSendThingCreated(final String controllerId, final String tenant) {
|
||||||
final Message message = createTargetMessage(target, tenant);
|
createAndSendThingCreated(controllerId, null, null, tenant);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void createAndSendThingCreated(final String controllerId, final String name,
|
||||||
|
final Map<String, String> attributes, final String tenant) {
|
||||||
|
final Message message = createTargetMessage(controllerId, name, attributes, tenant);
|
||||||
getDmfClient().send(message);
|
getDmfClient().send(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,32 +266,50 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
|
|||||||
registerAndAssertTargetWithExistingTenant(controllerId, 1);
|
registerAndAssertTargetWithExistingTenant(controllerId, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void registerAndAssertTargetWithExistingTenant(final String target,
|
protected void registerAndAssertTargetWithExistingTenant(final String controllerId,
|
||||||
final int existingTargetsAfterCreation) {
|
final int existingTargetsAfterCreation) {
|
||||||
|
registerAndAssertTargetWithExistingTenant(controllerId, existingTargetsAfterCreation,
|
||||||
registerAndAssertTargetWithExistingTenant(target, existingTargetsAfterCreation, TargetUpdateStatus.REGISTERED,
|
TargetUpdateStatus.REGISTERED, CREATED_BY);
|
||||||
CREATED_BY);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void registerAndAssertTargetWithExistingTenant(final String target,
|
protected void registerAndAssertTargetWithExistingTenant(final String controllerId,
|
||||||
final int existingTargetsAfterCreation, final TargetUpdateStatus expectedTargetStatus,
|
final int existingTargetsAfterCreation, final TargetUpdateStatus expectedTargetStatus,
|
||||||
final String createdBy) {
|
final String createdBy) {
|
||||||
createAndSendThingCreated(target, TENANT_EXIST);
|
registerAndAssertTargetWithExistingTenant(controllerId, null, existingTargetsAfterCreation,
|
||||||
final Target registeredTarget = waitUntilIsPresent(() -> targetManagement.getByControllerID(target));
|
expectedTargetStatus, createdBy, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void registerAndAssertTargetWithExistingTenant(final String controllerId, final String name,
|
||||||
|
final int existingTargetsAfterCreation, final TargetUpdateStatus expectedTargetStatus,
|
||||||
|
final String createdBy, final Map<String, String> attributes) {
|
||||||
|
registerAndAssertTargetWithExistingTenant(controllerId, name, existingTargetsAfterCreation,
|
||||||
|
expectedTargetStatus, createdBy, attributes, () -> targetManagement.getByControllerID(controllerId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerAndAssertTargetWithExistingTenant(final String controllerId, final String name,
|
||||||
|
final int existingTargetsAfterCreation, final TargetUpdateStatus expectedTargetStatus,
|
||||||
|
final String createdBy, final Map<String, String> attributes,
|
||||||
|
final Callable<Optional<Target>> fetchTarget) {
|
||||||
|
createAndSendThingCreated(controllerId, name, attributes, TENANT_EXIST);
|
||||||
|
final Target registeredTarget = waitUntilIsPresent(fetchTarget::call);
|
||||||
assertAllTargetsCount(existingTargetsAfterCreation);
|
assertAllTargetsCount(existingTargetsAfterCreation);
|
||||||
assertThat(registeredTarget).isNotNull();
|
assertThat(registeredTarget).isNotNull();
|
||||||
assertTarget(registeredTarget, expectedTargetStatus, createdBy);
|
assertTarget(registeredTarget, name != null ? name : controllerId, expectedTargetStatus, createdBy,
|
||||||
|
attributes != null ? attributes : Collections.emptyMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void registerSameTargetAndAssertBasedOnVersion(final String controllerId,
|
protected void registerSameTargetAndAssertBasedOnVersion(final String controllerId,
|
||||||
final int existingTargetsAfterCreation, final TargetUpdateStatus expectedTargetStatus) {
|
final int existingTargetsAfterCreation, final TargetUpdateStatus expectedTargetStatus) {
|
||||||
|
registerSameTargetAndAssertBasedOnVersion(controllerId, null, existingTargetsAfterCreation,
|
||||||
|
expectedTargetStatus, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void registerSameTargetAndAssertBasedOnVersion(final String controllerId, final String name,
|
||||||
|
final int existingTargetsAfterCreation, final TargetUpdateStatus expectedTargetStatus,
|
||||||
|
final Map<String, String> attributes) {
|
||||||
final int version = controllerManagement.getByControllerId(controllerId).get().getOptLockRevision();
|
final int version = controllerManagement.getByControllerId(controllerId).get().getOptLockRevision();
|
||||||
createAndSendThingCreated(controllerId, TENANT_EXIST);
|
registerAndAssertTargetWithExistingTenant(controllerId, name, existingTargetsAfterCreation,
|
||||||
final Target registeredTarget = waitUntilIsPresent(() -> findTargetBasedOnNewVersion(controllerId, version));
|
expectedTargetStatus, CREATED_BY, attributes, () -> findTargetBasedOnNewVersion(controllerId, version));
|
||||||
assertAllTargetsCount(existingTargetsAfterCreation);
|
|
||||||
assertThat(registeredTarget).isNotNull();
|
|
||||||
assertThat(registeredTarget.getUpdateStatus()).isEqualTo(expectedTargetStatus);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Target> findTargetBasedOnNewVersion(final String controllerId, final int version) {
|
private Optional<Target> findTargetBasedOnNewVersion(final String controllerId, final int version) {
|
||||||
@@ -290,23 +320,43 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
|
|||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertTarget(final Target target, final TargetUpdateStatus updateStatus, final String createdBy) {
|
private void assertTarget(final Target target, final String name, final TargetUpdateStatus updateStatus,
|
||||||
|
final String createdBy, final Map<String, String> attributes) {
|
||||||
assertThat(target.getTenant()).isEqualTo(TENANT_EXIST);
|
assertThat(target.getTenant()).isEqualTo(TENANT_EXIST);
|
||||||
|
assertThat(target.getName()).isEqualTo(name);
|
||||||
assertThat(target.getDescription()).contains("Plug and Play");
|
assertThat(target.getDescription()).contains("Plug and Play");
|
||||||
assertThat(target.getDescription()).contains(target.getControllerId());
|
assertThat(target.getDescription()).contains(target.getControllerId());
|
||||||
assertThat(target.getCreatedBy()).isEqualTo(createdBy);
|
assertThat(target.getCreatedBy()).isEqualTo(createdBy);
|
||||||
assertThat(target.getUpdateStatus()).isEqualTo(updateStatus);
|
assertThat(target.getUpdateStatus()).isEqualTo(updateStatus);
|
||||||
assertThat(target.getAddress()).isEqualTo(
|
assertThat(target.getAddress())
|
||||||
IpUtil.createAmqpUri(getVirtualHost(), DmfTestConfiguration.REPLY_TO_EXCHANGE));
|
.isEqualTo(IpUtil.createAmqpUri(getVirtualHost(), DmfTestConfiguration.REPLY_TO_EXCHANGE));
|
||||||
|
assertThat(targetManagement.getControllerAttributes(target.getControllerId())).isEqualTo(attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Message createTargetMessage(final String target, final String tenant) {
|
protected Message createTargetMessage(final String controllerId, final String tenant) {
|
||||||
|
return createTargetMessage(controllerId, null, null, tenant);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Message createTargetMessage(final String controllerId, final String name,
|
||||||
|
final Map<String, String> attributes, final String tenant) {
|
||||||
final MessageProperties messageProperties = createMessagePropertiesWithTenant(tenant);
|
final MessageProperties messageProperties = createMessagePropertiesWithTenant(tenant);
|
||||||
messageProperties.getHeaders().put(MessageHeaderKey.THING_ID, target);
|
messageProperties.getHeaders().put(MessageHeaderKey.THING_ID, controllerId);
|
||||||
messageProperties.getHeaders().put(MessageHeaderKey.TYPE, MessageType.THING_CREATED.toString());
|
messageProperties.getHeaders().put(MessageHeaderKey.TYPE, MessageType.THING_CREATED.toString());
|
||||||
messageProperties.setReplyTo(DmfTestConfiguration.REPLY_TO_EXCHANGE);
|
messageProperties.setReplyTo(DmfTestConfiguration.REPLY_TO_EXCHANGE);
|
||||||
|
|
||||||
return createMessage(null, messageProperties);
|
DmfCreateThing payload = null;
|
||||||
|
if (!StringUtils.isEmpty(name) || !CollectionUtils.isEmpty(attributes)) {
|
||||||
|
payload = new DmfCreateThing();
|
||||||
|
payload.setName(name);
|
||||||
|
|
||||||
|
if (!CollectionUtils.isEmpty(attributes)) {
|
||||||
|
final DmfAttributeUpdate attributeUpdate = new DmfAttributeUpdate();
|
||||||
|
attributeUpdate.getAttributes().putAll(attributes);
|
||||||
|
payload.setAttributeUpdate(attributeUpdate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return createMessage(payload, messageProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Message createPingMessage(final String correlationId, final String tenant) {
|
protected Message createPingMessage(final String correlationId, final String tenant) {
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
|
|||||||
|
|
||||||
assertPingReplyMessage(CORRELATION_ID);
|
assertPingReplyMessage(CORRELATION_ID);
|
||||||
|
|
||||||
Mockito.verifyZeroInteractions(getDeadletterListener());
|
Mockito.verifyNoInteractions(getDeadletterListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -107,7 +107,63 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
|
|||||||
registerAndAssertTargetWithExistingTenant(target2, 2);
|
registerAndAssertTargetWithExistingTenant(target2, 2);
|
||||||
|
|
||||||
registerSameTargetAndAssertBasedOnVersion(target2, 2, TargetUpdateStatus.REGISTERED);
|
registerSameTargetAndAssertBasedOnVersion(target2, 2, TargetUpdateStatus.REGISTERED);
|
||||||
Mockito.verifyZeroInteractions(getDeadletterListener());
|
Mockito.verifyNoInteractions(getDeadletterListener());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Tests register target with name")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 2) })
|
||||||
|
public void registerTargetWithName() {
|
||||||
|
final String controllerId = TARGET_PREFIX + "registerTargetWithName";
|
||||||
|
final String name = "NonDefaultTargetName";
|
||||||
|
registerAndAssertTargetWithExistingTenant(controllerId, name, 1, TargetUpdateStatus.REGISTERED, CREATED_BY,
|
||||||
|
null);
|
||||||
|
|
||||||
|
registerSameTargetAndAssertBasedOnVersion(controllerId, name + "_updated", 1, TargetUpdateStatus.REGISTERED,
|
||||||
|
null);
|
||||||
|
|
||||||
|
Mockito.verifyNoInteractions(getDeadletterListener());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Tests register target with attributes")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 2) })
|
||||||
|
public void registerTargetWithAttributes() {
|
||||||
|
final String controllerId = TARGET_PREFIX + "registerTargetWithAttributes";
|
||||||
|
final Map<String, String> attributes = new HashMap<>();
|
||||||
|
attributes.put("testKey1", "testValue1");
|
||||||
|
attributes.put("testKey2", "testValue2");
|
||||||
|
|
||||||
|
registerAndAssertTargetWithExistingTenant(controllerId, null, 1, TargetUpdateStatus.REGISTERED, CREATED_BY,
|
||||||
|
attributes);
|
||||||
|
|
||||||
|
attributes.put("testKey3", "testValue3");
|
||||||
|
registerSameTargetAndAssertBasedOnVersion(controllerId, null, 1, TargetUpdateStatus.REGISTERED, attributes);
|
||||||
|
|
||||||
|
Mockito.verifyNoInteractions(getDeadletterListener());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Tests register target with name and attributes")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 3), @Expect(type = TargetPollEvent.class, count = 2) })
|
||||||
|
public void registerTargetWithNameAndAttributes() {
|
||||||
|
final String controllerId = TARGET_PREFIX + "registerTargetWithAttributes";
|
||||||
|
final String name = "NonDefaultTargetName";
|
||||||
|
final Map<String, String> attributes = new HashMap<>();
|
||||||
|
attributes.put("testKey1", "testValue1");
|
||||||
|
attributes.put("testKey2", "testValue2");
|
||||||
|
|
||||||
|
registerAndAssertTargetWithExistingTenant(controllerId, name, 1, TargetUpdateStatus.REGISTERED, CREATED_BY,
|
||||||
|
attributes);
|
||||||
|
|
||||||
|
attributes.put("testKey3", "testValue3");
|
||||||
|
registerSameTargetAndAssertBasedOnVersion(controllerId, name + "_updated", 1, TargetUpdateStatus.REGISTERED,
|
||||||
|
attributes);
|
||||||
|
|
||||||
|
Mockito.verifyNoInteractions(getDeadletterListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -510,7 +566,7 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
|
|||||||
|
|
||||||
// verify
|
// verify
|
||||||
assertDownloadAndInstallMessage(distributionSet.getModules(), controllerId);
|
assertDownloadAndInstallMessage(distributionSet.getModules(), controllerId);
|
||||||
Mockito.verifyZeroInteractions(getDeadletterListener());
|
Mockito.verifyNoInteractions(getDeadletterListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -537,7 +593,7 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
|
|||||||
|
|
||||||
// verify
|
// verify
|
||||||
assertDownloadMessage(distributionSet.getModules(), controllerId);
|
assertDownloadMessage(distributionSet.getModules(), controllerId);
|
||||||
Mockito.verifyZeroInteractions(getDeadletterListener());
|
Mockito.verifyNoInteractions(getDeadletterListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -564,7 +620,7 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
|
|||||||
|
|
||||||
// verify
|
// verify
|
||||||
assertDownloadAndInstallMessage(distributionSet.getModules(), controllerId);
|
assertDownloadAndInstallMessage(distributionSet.getModules(), controllerId);
|
||||||
Mockito.verifyZeroInteractions(getDeadletterListener());
|
Mockito.verifyNoInteractions(getDeadletterListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -592,7 +648,7 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
|
|||||||
|
|
||||||
// verify
|
// verify
|
||||||
assertCancelActionMessage(getFirstAssignedActionId(distributionSetAssignmentResult), controllerId);
|
assertCancelActionMessage(getFirstAssignedActionId(distributionSetAssignmentResult), controllerId);
|
||||||
Mockito.verifyZeroInteractions(getDeadletterListener());
|
Mockito.verifyNoInteractions(getDeadletterListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -836,15 +892,15 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
|
|||||||
|
|
||||||
// verify
|
// verify
|
||||||
final Message message = assertReplyMessageHeader(EventTopic.DOWNLOAD, controllerId);
|
final Message message = assertReplyMessageHeader(EventTopic.DOWNLOAD, controllerId);
|
||||||
Mockito.verifyZeroInteractions(getDeadletterListener());
|
Mockito.verifyNoInteractions(getDeadletterListener());
|
||||||
|
|
||||||
// get actionId from Message
|
// get actionId from Message
|
||||||
Long actionId = Long.parseLong(getJsonFieldFromBody(message.getBody(), "actionId"));
|
final Long actionId = Long.parseLong(getJsonFieldFromBody(message.getBody(), "actionId"));
|
||||||
|
|
||||||
// Send DOWNLOADED message
|
// Send DOWNLOADED message
|
||||||
createAndSendActionStatusUpdateMessage(controllerId, actionId, DmfActionStatus.DOWNLOADED);
|
createAndSendActionStatusUpdateMessage(controllerId, actionId, DmfActionStatus.DOWNLOADED);
|
||||||
assertAction(actionId, 1, Status.RUNNING, Status.DOWNLOADED);
|
assertAction(actionId, 1, Status.RUNNING, Status.DOWNLOADED);
|
||||||
Mockito.verifyZeroInteractions(getDeadletterListener());
|
Mockito.verifyNoInteractions(getDeadletterListener());
|
||||||
|
|
||||||
verifyAssignedDsAndInstalledDs(controllerId, distributionSet.getId(), null);
|
verifyAssignedDsAndInstalledDs(controllerId, distributionSet.getId(), null);
|
||||||
}
|
}
|
||||||
@@ -868,22 +924,22 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
|
|||||||
|
|
||||||
// verify
|
// verify
|
||||||
final Message message = assertReplyMessageHeader(EventTopic.DOWNLOAD, controllerId);
|
final Message message = assertReplyMessageHeader(EventTopic.DOWNLOAD, controllerId);
|
||||||
Mockito.verifyZeroInteractions(getDeadletterListener());
|
Mockito.verifyNoInteractions(getDeadletterListener());
|
||||||
|
|
||||||
// get actionId from Message
|
// get actionId from Message
|
||||||
Long actionId = Long.parseLong(getJsonFieldFromBody(message.getBody(), "actionId"));
|
final Long actionId = Long.parseLong(getJsonFieldFromBody(message.getBody(), "actionId"));
|
||||||
|
|
||||||
// Send DOWNLOADED message, should result in the action being closed
|
// Send DOWNLOADED message, should result in the action being closed
|
||||||
createAndSendActionStatusUpdateMessage(controllerId, actionId, DmfActionStatus.DOWNLOADED);
|
createAndSendActionStatusUpdateMessage(controllerId, actionId, DmfActionStatus.DOWNLOADED);
|
||||||
assertAction(actionId, 1, Status.RUNNING, Status.DOWNLOADED);
|
assertAction(actionId, 1, Status.RUNNING, Status.DOWNLOADED);
|
||||||
Mockito.verifyZeroInteractions(getDeadletterListener());
|
Mockito.verifyNoInteractions(getDeadletterListener());
|
||||||
|
|
||||||
verifyAssignedDsAndInstalledDs(controllerId, distributionSet.getId(), null);
|
verifyAssignedDsAndInstalledDs(controllerId, distributionSet.getId(), null);
|
||||||
|
|
||||||
// Send FINISHED message
|
// Send FINISHED message
|
||||||
createAndSendActionStatusUpdateMessage(controllerId, actionId, DmfActionStatus.FINISHED);
|
createAndSendActionStatusUpdateMessage(controllerId, actionId, DmfActionStatus.FINISHED);
|
||||||
assertAction(actionId, 2, Status.RUNNING, Status.DOWNLOADED, Status.FINISHED);
|
assertAction(actionId, 2, Status.RUNNING, Status.DOWNLOADED, Status.FINISHED);
|
||||||
Mockito.verifyZeroInteractions(getDeadletterListener());
|
Mockito.verifyNoInteractions(getDeadletterListener());
|
||||||
|
|
||||||
verifyAssignedDsAndInstalledDs(controllerId, distributionSet.getId(), distributionSet.getId());
|
verifyAssignedDsAndInstalledDs(controllerId, distributionSet.getId(), distributionSet.getId());
|
||||||
}
|
}
|
||||||
@@ -899,7 +955,7 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
|
|||||||
final String controllerId = "dummy_target";
|
final String controllerId = "dummy_target";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (Class<? extends RuntimeException> exceptionClass : exceptionsThatShouldNotBeRequeued) {
|
for (final Class<? extends RuntimeException> exceptionClass : exceptionsThatShouldNotBeRequeued) {
|
||||||
doThrow(exceptionClass).when(mockedControllerManagement)
|
doThrow(exceptionClass).when(mockedControllerManagement)
|
||||||
.findOrRegisterTargetIfItDoesNotExist(eq(controllerId), any());
|
.findOrRegisterTargetIfItDoesNotExist(eq(controllerId), any());
|
||||||
|
|
||||||
@@ -998,7 +1054,7 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void assertEmptyReceiverQueueCount() {
|
private void assertEmptyReceiverQueueCount() {
|
||||||
assertThat(getAuthenticationMessageCount()).isEqualTo(0);
|
assertThat(getAuthenticationMessageCount()).isZero();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyOneDeadLetterMessage() {
|
private void verifyOneDeadLetterMessage() {
|
||||||
@@ -1013,7 +1069,7 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String getJsonFieldFromBody(final byte[] body, final String fieldName) throws IOException {
|
private static String getJsonFieldFromBody(final byte[] body, final String fieldName) throws IOException {
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
final ObjectNode node = objectMapper.readValue(new String(body, Charset.defaultCharset()), ObjectNode.class);
|
final ObjectNode node = objectMapper.readValue(new String(body, Charset.defaultCharset()), ObjectNode.class);
|
||||||
assertThat(node.has(fieldName)).isTrue();
|
assertThat(node.has(fieldName)).isTrue();
|
||||||
return node.get(fieldName).asText();
|
return node.get(fieldName).asText();
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ public class DmfCreateThing {
|
|||||||
@JsonProperty
|
@JsonProperty
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private DmfAttributeUpdate attributeUpdate;
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@@ -31,4 +34,11 @@ public class DmfCreateThing {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DmfAttributeUpdate getAttributeUpdate() {
|
||||||
|
return attributeUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttributeUpdate(final DmfAttributeUpdate attributeUpdate) {
|
||||||
|
this.attributeUpdate = attributeUpdate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,5 +98,4 @@ public interface Target extends NamedEntity {
|
|||||||
* {@link #getControllerAttributes()}.
|
* {@link #getControllerAttributes()}.
|
||||||
*/
|
*/
|
||||||
boolean isRequestControllerAttributes();
|
boolean isRequestControllerAttributes();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user