Enable specifying target type when created using DMF API (#1472)
Extension of DMF API with possibility of setting target type name when creating target. If a target type with the provided name is found (was created beforehand) then it is associated with the new target. Signed-off-by: Ondrej Charvat <ondrej.charvat@proton.me>
This commit is contained in:
@@ -231,14 +231,18 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
|||||||
final URI amqpUri = IpUtil.createAmqpUri(virtualHost, replyTo);
|
final URI amqpUri = IpUtil.createAmqpUri(virtualHost, replyTo);
|
||||||
final Target target;
|
final Target target;
|
||||||
if (isOptionalMessageBodyEmpty(message)) {
|
if (isOptionalMessageBodyEmpty(message)) {
|
||||||
|
LOG.debug("Received \"THING_CREATED\" AMQP message for thing \"{}\" without body.", thingId);
|
||||||
target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(thingId, amqpUri);
|
target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(thingId, amqpUri);
|
||||||
} else {
|
} else {
|
||||||
checkContentTypeJson(message);
|
checkContentTypeJson(message);
|
||||||
final DmfCreateThing thingCreateBody = convertMessage(message, DmfCreateThing.class);
|
final DmfCreateThing thingCreateBody = convertMessage(message, DmfCreateThing.class);
|
||||||
final DmfAttributeUpdate thingAttributeUpdateBody = thingCreateBody.getAttributeUpdate();
|
final DmfAttributeUpdate thingAttributeUpdateBody = thingCreateBody.getAttributeUpdate();
|
||||||
|
|
||||||
|
LOG.debug("Received \"THING_CREATED\" AMQP message for thing \"{}\" with target name \"{}\" and type " +
|
||||||
|
"\"{}\".", thingId, thingCreateBody.getName(), thingCreateBody.getType());
|
||||||
|
|
||||||
target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(thingId, amqpUri,
|
target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(thingId, amqpUri,
|
||||||
thingCreateBody.getName());
|
thingCreateBody.getName(), thingCreateBody.getType());
|
||||||
|
|
||||||
if (thingAttributeUpdateBody != null) {
|
if (thingAttributeUpdateBody != null) {
|
||||||
controllerManagement.updateControllerAttributes(thingId, thingAttributeUpdateBody.getAttributes(),
|
controllerManagement.updateControllerAttributes(thingId, thingAttributeUpdateBody.getAttributes(),
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ import org.eclipse.hawkbit.repository.UpdateMode;
|
|||||||
import org.eclipse.hawkbit.repository.builder.ActionStatusBuilder;
|
import org.eclipse.hawkbit.repository.builder.ActionStatusBuilder;
|
||||||
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
|
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
|
||||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
|
||||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusBuilder;
|
import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusBuilder;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder;
|
import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder;
|
||||||
@@ -167,6 +166,9 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
@Captor
|
@Captor
|
||||||
private ArgumentCaptor<String> targetNameCaptor;
|
private ArgumentCaptor<String> targetNameCaptor;
|
||||||
|
|
||||||
|
@Captor
|
||||||
|
private ArgumentCaptor<String> targetTypeNameCaptor;
|
||||||
|
|
||||||
@Captor
|
@Captor
|
||||||
private ArgumentCaptor<URI> uriCaptor;
|
private ArgumentCaptor<URI> uriCaptor;
|
||||||
|
|
||||||
@@ -228,7 +230,8 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
uriCaptor.capture())).thenReturn(targetMock);
|
uriCaptor.capture())).thenReturn(targetMock);
|
||||||
} else {
|
} else {
|
||||||
when(controllerManagementMock.findOrRegisterTargetIfItDoesNotExist(targetIdCaptor.capture(),
|
when(controllerManagementMock.findOrRegisterTargetIfItDoesNotExist(targetIdCaptor.capture(),
|
||||||
uriCaptor.capture(), targetNameCaptor.capture())).thenReturn(targetMock);
|
uriCaptor.capture(), targetNameCaptor.capture(), targetTypeNameCaptor.capture()))
|
||||||
|
.thenReturn(targetMock);
|
||||||
if (payload.getAttributeUpdate() != null) {
|
if (payload.getAttributeUpdate() != null) {
|
||||||
when(controllerManagementMock.updateControllerAttributes(targetIdCaptor.capture(),
|
when(controllerManagementMock.updateControllerAttributes(targetIdCaptor.capture(),
|
||||||
attributesCaptor.capture(), modeCaptor.capture())).thenReturn(null);
|
attributesCaptor.capture(), modeCaptor.capture())).thenReturn(null);
|
||||||
@@ -280,6 +283,27 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
assertThat(targetNameCaptor.getValue()).as("Thing name is wrong").isEqualTo(thingName);
|
assertThat(targetNameCaptor.getValue()).as("Thing name is wrong").isEqualTo(thingName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Tests the creation of a target/thing with specified type name by calling the same method that incoming RabbitMQ messages would access.")
|
||||||
|
public void createThingWithType() {
|
||||||
|
final String knownThingId = "2";
|
||||||
|
final String knownThingTypeName = "TargetTypeName";
|
||||||
|
|
||||||
|
final DmfCreateThing payload = new DmfCreateThing();
|
||||||
|
payload.setType(knownThingTypeName);
|
||||||
|
|
||||||
|
processThingCreatedMessage(knownThingId, payload);
|
||||||
|
|
||||||
|
assertThingIdCapturedField(knownThingId);
|
||||||
|
assertReplyToCapturedField("MyTest");
|
||||||
|
assertThingTypeCapturedField(knownThingTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private void assertThingTypeCapturedField(final String thingType) {
|
||||||
|
assertThat(targetTypeNameCaptor.getValue()).as("Thing type is wrong").isEqualTo(thingType);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Tests not allowed body in message")
|
@Description("Tests not allowed body in message")
|
||||||
public void createThingWithWrongBody() {
|
public void createThingWithWrongBody() {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public final class SoftwareModuleJsonMatcher {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a matcher that matches when the list of repository software
|
* Creates a matcher that matches when the list of repository software
|
||||||
* modules arelogically equal to the specified JSON software modules.
|
* modules are logically equal to the specified JSON software modules.
|
||||||
* <p>
|
* <p>
|
||||||
* If the specified repository software modules are <code>null</code> then
|
* If the specified repository software modules are <code>null</code> then
|
||||||
* the created matcher will only match if the JSON software modules are
|
* the created matcher will only match if the JSON software modules are
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ public class DmfCreateThing {
|
|||||||
@JsonProperty
|
@JsonProperty
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String type;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private DmfAttributeUpdate attributeUpdate;
|
private DmfAttributeUpdate attributeUpdate;
|
||||||
|
|
||||||
@@ -35,6 +38,14 @@ public class DmfCreateThing {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(final String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
public DmfAttributeUpdate getAttributeUpdate() {
|
public DmfAttributeUpdate getAttributeUpdate() {
|
||||||
return attributeUpdate;
|
return attributeUpdate;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -223,7 +223,8 @@ public interface ControllerManagement {
|
|||||||
/**
|
/**
|
||||||
* Register new target in the repository (plug-and-play) and in case it
|
* Register new target in the repository (plug-and-play) and in case it
|
||||||
* already exists updates {@link Target#getAddress()} and
|
* already exists updates {@link Target#getAddress()} and
|
||||||
* {@link Target#getLastTargetQuery()} and {@link Target#getName()} and
|
* {@link Target#getLastTargetQuery()} and {@link Target#getName()}
|
||||||
|
* and {@link Target#getTargetType()} and
|
||||||
* switches if {@link TargetUpdateStatus#UNKNOWN} to
|
* switches if {@link TargetUpdateStatus#UNKNOWN} to
|
||||||
* {@link TargetUpdateStatus#REGISTERED}.
|
* {@link TargetUpdateStatus#REGISTERED}.
|
||||||
*
|
*
|
||||||
@@ -233,10 +234,13 @@ public interface ControllerManagement {
|
|||||||
* the client IP address of the target, might be {@code null}
|
* the client IP address of the target, might be {@code null}
|
||||||
* @param name
|
* @param name
|
||||||
* the name of the target
|
* the name of the target
|
||||||
|
* @param type
|
||||||
|
* the target type name of the target
|
||||||
* @return target reference
|
* @return target reference
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||||
Target findOrRegisterTargetIfItDoesNotExist(@NotEmpty String controllerId, @NotNull URI address, String name);
|
Target findOrRegisterTargetIfItDoesNotExist(@NotEmpty String controllerId, @NotNull URI address, String name,
|
||||||
|
String type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves last {@link Action} for a download of an artifact of given
|
* Retrieves last {@link Action} for a download of an artifact of given
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import org.eclipse.hawkbit.repository.QuotaManagement;
|
|||||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.TargetTypeManagement;
|
||||||
import org.eclipse.hawkbit.repository.UpdateMode;
|
import org.eclipse.hawkbit.repository.UpdateMode;
|
||||||
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
|
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent;
|
import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent;
|
||||||
@@ -84,6 +85,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
|
|||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
|
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||||
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
||||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||||
@@ -160,6 +162,9 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ConfirmationManagement confirmationManagement;
|
private ConfirmationManagement confirmationManagement;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TargetTypeManagement targetTypeManagement;
|
||||||
|
|
||||||
public JpaControllerManagement(final ScheduledExecutorService executorService,
|
public JpaControllerManagement(final ScheduledExecutorService executorService,
|
||||||
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
||||||
final QuotaManagement quotaManagement, final RepositoryProperties repositoryProperties) {
|
final QuotaManagement quotaManagement, final RepositoryProperties repositoryProperties) {
|
||||||
@@ -384,28 +389,42 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
|||||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||||
@Retryable(include = ConcurrencyFailureException.class, exclude = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
@Retryable(include = ConcurrencyFailureException.class, exclude = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||||
public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address) {
|
public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address) {
|
||||||
return findOrRegisterTargetIfItDoesNotExist(controllerId, address, null);
|
return findOrRegisterTargetIfItDoesNotExist(controllerId, address, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||||
@Retryable(include = ConcurrencyFailureException.class, exclude = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
@Retryable(include = ConcurrencyFailureException.class, exclude = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||||
public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address,
|
public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address,
|
||||||
final String name) {
|
final String name, final String type) {
|
||||||
final Specification<JpaTarget> spec = (targetRoot, query, cb) -> cb
|
final Specification<JpaTarget> spec =
|
||||||
.equal(targetRoot.get(JpaTarget_.controllerId), controllerId);
|
(targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaTarget_.controllerId), controllerId);
|
||||||
|
|
||||||
return targetRepository.findOne(spec).map(target -> updateTarget(target, address, name))
|
return targetRepository.findOne(spec).map(target -> updateTarget(target, address, name, type))
|
||||||
.orElseGet(() -> createTarget(controllerId, address, name));
|
.orElseGet(() -> createTarget(controllerId, address, name, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Target createTarget(final String controllerId, final URI address, final String name) {
|
private Target createTarget(final String controllerId, final URI address, final String name, final String type) {
|
||||||
|
|
||||||
final Target result = targetRepository.save((JpaTarget) entityFactory.target().create()
|
LOG.debug("Creating target for thing ID \"{}\".", controllerId);
|
||||||
|
JpaTarget jpaTarget = (JpaTarget) entityFactory.target().create()
|
||||||
.controllerId(controllerId).description("Plug and Play target: " + controllerId)
|
.controllerId(controllerId).description("Plug and Play target: " + controllerId)
|
||||||
.name((StringUtils.hasText(name) ? name : controllerId)).status(TargetUpdateStatus.REGISTERED)
|
.name((StringUtils.hasText(name) ? name : controllerId)).status(TargetUpdateStatus.REGISTERED)
|
||||||
.lastTargetQuery(System.currentTimeMillis())
|
.lastTargetQuery(System.currentTimeMillis())
|
||||||
.address(Optional.ofNullable(address).map(URI::toString).orElse(null)).build());
|
.address(Optional.ofNullable(address).map(URI::toString).orElse(null)).build();
|
||||||
|
|
||||||
|
if (StringUtils.hasText(type)) {
|
||||||
|
var targetTypeOptional = getTargetType(type);
|
||||||
|
if (targetTypeOptional.isPresent()) {
|
||||||
|
LOG.debug("Setting target type for thing ID \"{}\" to \"{}\".", controllerId, type);
|
||||||
|
jpaTarget.setTargetType(targetTypeOptional.get());
|
||||||
|
} else {
|
||||||
|
LOG.error("Target type with the provided name \"{}\" was not found. Creating target for thing ID" +
|
||||||
|
" \"{}\" without target type assignment", type, controllerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Target result = targetRepository.save(jpaTarget);
|
||||||
|
|
||||||
afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher()
|
afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher()
|
||||||
.publishEvent(new TargetPollEvent(result, eventPublisherHolder.getApplicationId())));
|
.publishEvent(new TargetPollEvent(result, eventPublisherHolder.getApplicationId())));
|
||||||
@@ -413,6 +432,10 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<TargetType> getTargetType(String targetTypeName) {
|
||||||
|
return systemSecurityContext.runAsSystem(() -> targetTypeManagement.getByName(targetTypeName));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flush the update queue by means to persisting
|
* Flush the update queue by means to persisting
|
||||||
* {@link Target#getLastTargetQuery()}.
|
* {@link Target#getLastTargetQuery()}.
|
||||||
@@ -501,14 +524,30 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
|||||||
* or the buffer queue is full.
|
* or the buffer queue is full.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private Target updateTarget(final JpaTarget toUpdate, final URI address, final String name) {
|
private Target updateTarget(final JpaTarget toUpdate, final URI address, final String name, final String type) {
|
||||||
if (isStoreEager(toUpdate, address, name) || !queue.offer(new TargetPoll(toUpdate))) {
|
if (isStoreEager(toUpdate, address, name, type) || !queue.offer(new TargetPoll(toUpdate))) {
|
||||||
if (isAddressChanged(toUpdate.getAddress(), address)) {
|
if (isAddressChanged(toUpdate.getAddress(), address)) {
|
||||||
toUpdate.setAddress(address.toString());
|
toUpdate.setAddress(address.toString());
|
||||||
}
|
}
|
||||||
if (isNameChanged(toUpdate.getName(), name)) {
|
if (isNameChanged(toUpdate.getName(), name)) {
|
||||||
toUpdate.setName(name);
|
toUpdate.setName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isTypeChanged(toUpdate.getTargetType(), type)) {
|
||||||
|
if (StringUtils.hasText(type)) {
|
||||||
|
var targetTypeOptional = getTargetType(type);
|
||||||
|
if (targetTypeOptional.isPresent()) {
|
||||||
|
LOG.debug("Updating target type for thing ID \"{}\" to \"{}\".", toUpdate.getControllerId(), type);
|
||||||
|
toUpdate.setTargetType(targetTypeOptional.get());
|
||||||
|
} else {
|
||||||
|
LOG.error("Target type with the provided name \"{}\" was not found. Target type for thing ID" +
|
||||||
|
" \"{}\" will not be updated", type, toUpdate.getControllerId());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.debug("Removing target type assignment for thing ID \"{}\".", toUpdate.getControllerId());
|
||||||
|
toUpdate.setTargetType(null); //unassign target type if "" target type name was provided
|
||||||
|
}
|
||||||
|
}
|
||||||
if (isStatusUnknown(toUpdate.getUpdateStatus())) {
|
if (isStatusUnknown(toUpdate.getUpdateStatus())) {
|
||||||
toUpdate.setUpdateStatus(TargetUpdateStatus.REGISTERED);
|
toUpdate.setUpdateStatus(TargetUpdateStatus.REGISTERED);
|
||||||
}
|
}
|
||||||
@@ -520,9 +559,10 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
|||||||
return toUpdate;
|
return toUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isStoreEager(final JpaTarget toUpdate, final URI address, final String name) {
|
private boolean isStoreEager(final JpaTarget toUpdate, final URI address, final String name, final String type) {
|
||||||
return repositoryProperties.isEagerPollPersistence() || isAddressChanged(toUpdate.getAddress(), address)
|
return repositoryProperties.isEagerPollPersistence() || isAddressChanged(toUpdate.getAddress(), address)
|
||||||
|| isNameChanged(toUpdate.getName(), name) || isStatusUnknown(toUpdate.getUpdateStatus());
|
|| isNameChanged(toUpdate.getName(), name) || isTypeChanged(toUpdate.getTargetType(), type)
|
||||||
|
|| isStatusUnknown(toUpdate.getUpdateStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isAddressChanged(final URI addressToUpdate, final URI address) {
|
private static boolean isAddressChanged(final URI addressToUpdate, final URI address) {
|
||||||
@@ -533,6 +573,10 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
|||||||
return StringUtils.hasText(name) && !nameToUpdate.equals(name);
|
return StringUtils.hasText(name) && !nameToUpdate.equals(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isTypeChanged(final TargetType targetTypeToUpdate, final String type) {
|
||||||
|
return (type != null) && (targetTypeToUpdate == null || !targetTypeToUpdate.getName().equals(type));
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isStatusUnknown(final TargetUpdateStatus statusToUpdate) {
|
private static boolean isStatusUnknown(final TargetUpdateStatus statusToUpdate) {
|
||||||
return TargetUpdateStatus.UNKNOWN == statusToUpdate;
|
return TargetUpdateStatus.UNKNOWN == statusToUpdate;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.management;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.CONTROLLER_ROLE;
|
||||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS;
|
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS;
|
||||||
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
|
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
|
||||||
import static org.eclipse.hawkbit.repository.model.Action.ActionType.DOWNLOAD_ONLY;
|
import static org.eclipse.hawkbit.repository.model.Action.ActionType.DOWNLOAD_ONLY;
|
||||||
@@ -52,6 +53,7 @@ import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedE
|
|||||||
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||||
|
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeCreatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||||
@@ -77,6 +79,8 @@ import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
|||||||
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
||||||
import org.eclipse.hawkbit.repository.test.util.TargetTestData;
|
import org.eclipse.hawkbit.repository.test.util.TargetTestData;
|
||||||
import org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch;
|
import org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch;
|
||||||
|
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -510,6 +514,7 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Register a controller which does not exist")
|
@Description("Register a controller which does not exist")
|
||||||
|
@WithUser(principal = "controller", authorities = { CONTROLLER_ROLE })
|
||||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
@Expect(type = TargetPollEvent.class, count = 2) })
|
@Expect(type = TargetPollEvent.class, count = 2) })
|
||||||
void findOrRegisterTargetIfItDoesNotExist() {
|
void findOrRegisterTargetIfItDoesNotExist() {
|
||||||
@@ -523,15 +528,122 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Register a controller with name which does not exist and update its name")
|
@Description("Register a controller with name which does not exist and update its name")
|
||||||
|
@WithUser(principal = "controller", authorities = { CONTROLLER_ROLE })
|
||||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
@Expect(type = TargetPollEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 1) })
|
@Expect(type = TargetPollEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 1) })
|
||||||
void findOrRegisterTargetIfItDoesNotExistWithName() {
|
void findOrRegisterTargetIfItDoesNotExistWithName() {
|
||||||
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST, "TestName");
|
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST, "TestName", null);
|
||||||
final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
|
final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
|
||||||
"ChangedTestName");
|
"ChangedTestName", null);
|
||||||
assertThat(target.getId()).as("Target should be the equals").isEqualTo(sameTarget.getId());
|
assertThat(target.getId()).as("Target should be the equals").isEqualTo(sameTarget.getId());
|
||||||
assertThat(target.getName()).as("Taget names should be different").isNotEqualTo(sameTarget.getName());
|
assertThat(target.getName()).as("Target names should be different").isNotEqualTo(sameTarget.getName());
|
||||||
assertThat(sameTarget.getName()).as("Taget name should be changed").isEqualTo("ChangedTestName");
|
assertThat(sameTarget.getName()).as("Target name should be changed").isEqualTo("ChangedTestName");
|
||||||
|
assertThat(targetRepository.count()).as("Only 1 target should be registred").isEqualTo(1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Register a controller which does not exist with existing target type and update its target type to another existing one")
|
||||||
|
@WithUser(principal = "controller", authorities = { CONTROLLER_ROLE })
|
||||||
|
@ExpectEvents({ @Expect(type = TargetTypeCreatedEvent.class, count = 2),
|
||||||
|
@Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetPollEvent.class, count = 2),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 1) })
|
||||||
|
void findOrRegisterTargetIfItDoesNotExistWithExistingTypeAndUpdateToExistingType() {
|
||||||
|
createTargetType("knownTargetTypeName1");
|
||||||
|
createTargetType("knownTargetTypeName2");
|
||||||
|
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
|
||||||
|
null, "knownTargetTypeName1");
|
||||||
|
final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
|
||||||
|
null, "knownTargetTypeName2");
|
||||||
|
assertThat(target.getId()).as("Target should be the same").isEqualTo(sameTarget.getId());
|
||||||
|
assertThat(target.getTargetType().getName()).as("Target type should be set")
|
||||||
|
.isEqualTo("knownTargetTypeName1");
|
||||||
|
assertThat(sameTarget.getTargetType().getName()).as("Target type should be changed")
|
||||||
|
.isEqualTo("knownTargetTypeName2");
|
||||||
|
assertThat(targetRepository.count()).as("Only 1 target should be registred").isEqualTo(1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createTargetType(String targetTypeName) {
|
||||||
|
systemSecurityContext.runAsSystem(() ->
|
||||||
|
targetTypeManagement.create(entityFactory.targetType().create().name(targetTypeName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Register a controller which does not exist with existing target type and update its target type to non existing one")
|
||||||
|
@WithUser(principal = "controller", authorities = { CONTROLLER_ROLE })
|
||||||
|
@ExpectEvents({ @Expect(type = TargetTypeCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetPollEvent.class, count = 2) })
|
||||||
|
void findOrRegisterTargetIfItDoesNotExistWithExistingTypeAndUpdateToNonExistingType() {
|
||||||
|
createTargetType("knownTargetTypeName");
|
||||||
|
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
|
||||||
|
null,"knownTargetTypeName");
|
||||||
|
final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
|
||||||
|
null,"unknownTargetTypeName");
|
||||||
|
assertThat(target.getId()).as("Target should be the same").isEqualTo(sameTarget.getId());
|
||||||
|
assertThat(sameTarget.getTargetType().getName()).as("Target type should be unchanged")
|
||||||
|
.isEqualTo("knownTargetTypeName");
|
||||||
|
assertThat(targetRepository.count()).as("Only 1 target should be registred").isEqualTo(1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Register a controller which does not exist with existing target type and unassign its target type")
|
||||||
|
@WithUser(principal = "controller", authorities = { CONTROLLER_ROLE })
|
||||||
|
@ExpectEvents({ @Expect(type = TargetTypeCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetPollEvent.class, count = 2),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 1) })
|
||||||
|
void findOrRegisterTargetIfItDoesNotExistWithExistingTypeAndUnassignType() {
|
||||||
|
createTargetType("knownTargetTypeName");
|
||||||
|
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
|
||||||
|
null, "knownTargetTypeName");
|
||||||
|
final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
|
||||||
|
null, "");
|
||||||
|
assertThat(target.getId()).as("Target should be the same").isEqualTo(sameTarget.getId());
|
||||||
|
assertThat(sameTarget.getTargetType()).as("Target type should be unassigned")
|
||||||
|
.isNull();
|
||||||
|
assertThat(targetRepository.count()).as("Only 1 target should be registred").isEqualTo(1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Register a controller which does not exist without target type and update its target type to existing one")
|
||||||
|
@WithUser(principal = "controller", authorities = { CONTROLLER_ROLE })
|
||||||
|
@ExpectEvents({ @Expect(type = TargetTypeCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetPollEvent.class, count = 2),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 1) })
|
||||||
|
void findOrRegisterTargetIfItDoesNotExistWithoutTypeAndUpdateToExistingType() {
|
||||||
|
createTargetType("knownTargetTypeName");
|
||||||
|
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
|
||||||
|
null, null);
|
||||||
|
final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
|
||||||
|
null, "knownTargetTypeName");
|
||||||
|
assertThat(target.getId()).as("Target should be the equals").isEqualTo(sameTarget.getId());
|
||||||
|
assertThat(target.getTargetType()).as("Target type should not be assigned")
|
||||||
|
.isNull();
|
||||||
|
assertThat(sameTarget.getTargetType().getName()).as("Target type should be assigned")
|
||||||
|
.isEqualTo("knownTargetTypeName");
|
||||||
|
assertThat(targetRepository.count()).as("Only 1 target should be registred").isEqualTo(1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Register a controller which does not exist with non existing target type and update its target type to existing one")
|
||||||
|
@WithUser(principal = "controller", authorities = { CONTROLLER_ROLE })
|
||||||
|
@ExpectEvents({ @Expect(type = TargetTypeCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetPollEvent.class, count = 2),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 1) })
|
||||||
|
void findOrRegisterTargetIfItDoesNotExistWithNonExistingTypeAndUpdateToExistingType() {
|
||||||
|
createTargetType("knownTargetTypeName");
|
||||||
|
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
|
||||||
|
null, "unknownTargetTypeName");
|
||||||
|
final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
|
||||||
|
null, "knownTargetTypeName");
|
||||||
|
assertThat(target.getId()).as("Target should be the equals").isEqualTo(sameTarget.getId());
|
||||||
|
assertThat(target.getTargetType()).as("Target type should not be assigned")
|
||||||
|
.isNull();
|
||||||
|
assertThat(sameTarget.getTargetType().getName()).as("Target type should be assigned")
|
||||||
|
.isEqualTo("knownTargetTypeName");
|
||||||
assertThat(targetRepository.count()).as("Only 1 target should be registred").isEqualTo(1L);
|
assertThat(targetRepository.count()).as("Only 1 target should be registred").isEqualTo(1L);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,7 +728,7 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
assertThat(newTarget.getName()).isEqualTo(controllerId);
|
assertThat(newTarget.getName()).isEqualTo(controllerId);
|
||||||
|
|
||||||
final Target firstTimeUpdatedTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId,
|
final Target firstTimeUpdatedTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId,
|
||||||
LOCALHOST, targetName);
|
LOCALHOST, targetName, null);
|
||||||
assertThat(firstTimeUpdatedTarget.getName()).isEqualTo(targetName);
|
assertThat(firstTimeUpdatedTarget.getName()).isEqualTo(targetName);
|
||||||
|
|
||||||
// Name should not change to default (name=targetId) if target is
|
// Name should not change to default (name=targetId) if target is
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ Payload Template (optional):
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"name": "String",
|
"name": "String",
|
||||||
|
"type": "String",
|
||||||
"attributeUpdate": {
|
"attributeUpdate": {
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"exampleKey1" : "exampleValue1",
|
"exampleKey1" : "exampleValue1",
|
||||||
@@ -74,6 +75,18 @@ Payload Template (optional):
|
|||||||
```
|
```
|
||||||
|
|
||||||
The "name" property specifies the name of the thing, which by default is the thing ID. This property is optional.<br />
|
The "name" property specifies the name of the thing, which by default is the thing ID. This property is optional.<br />
|
||||||
|
<br />
|
||||||
|
The "type" property specifies name of a target type which should be assigned to the created/updated target. The
|
||||||
|
target type with the specified name should be created in advance, otherwise it can't be assigned to the target,
|
||||||
|
resulting in:
|
||||||
|
* error is logged
|
||||||
|
* if the target does not exist then it is created without any target type assigned
|
||||||
|
* if it exists already then no changes to its target type assignment are made.
|
||||||
|
|
||||||
|
If the "type" property is set to a blank string while updating an existing target then any eventual target type
|
||||||
|
assignment is removed from the target. This property is optional and if omitted then no changes to the target type
|
||||||
|
assignment are made.<br />
|
||||||
|
<br />
|
||||||
The "attributeUpdate" property provides the attributes of the thing, for details see UPDATE_ATTRIBUTES message. This property is optional.
|
The "attributeUpdate" property provides the attributes of the thing, for details see UPDATE_ATTRIBUTES message. This property is optional.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user