Feature/java11 build (#1280)

* hawkBit on Java 11

Signed-off-by: Dominic Schabel <dominic.schabel@bosch.io>

* Preparing java 11 build

- Update eclipse-link maven plugin dependencies
- Fixing warnings, adopt to java-11 style

Signed-off-by: Peter Vigier <Peter.Vigier@bosch.io>

* Preparing java 11 build

- Fixing warnings, adapt to java-11 style
- Added since to deprecated

Signed-off-by: Peter Vigier <Peter.Vigier@bosch.io>

* Fixing sonar warnings

- removed deprecated API

Signed-off-by: Peter Vigier <Peter.Vigier@bosch.io>

* Fixing sonar warnings & failing test

- Added suppressWarning
- added WithSpringAuthorityRule to clean-up listener

Signed-off-by: Peter Vigier <Peter.Vigier@bosch.io>

* Compile warnings

- Test if final causes issues in tests

Signed-off-by: Peter Vigier <Peter.Vigier@bosch.io>

* Removed deprecated code

Signed-off-by: Peter Vigier <Peter.Vigier@bosch.io>

* Reverted changes

Signed-off-by: Peter Vigier <Peter.Vigier@bosch.io>

* Removed final as this causes invalid reflective access exceptions

- The eclipselink generated classes seem to modify the field directly
- update plugin version

Signed-off-by: Peter Vigier <Peter.Vigier@bosch.io>

* Upgrade eclipselink from 2.7.9 to 2.7.10

* Remove @deprecated endpoints from MgmtTargetTagResource

* Remove dependencies already defined in eclipselink-maven-plugin

* Try eclipselink 2.7.11-RC1

* Set project encoding to UTF-8

* Upgrade surefire and failsafe plugins to 3.0.0-M7

* Try fixed string instead of a random generated one

* Replace JsonBuilder by Jackson ObjectMapper usage

* Use JsonBuilder again

* Use APPLICATION_JSON_UTF8 instead of APPLICATION_JSON

* Try to replace com.vaadin.external.google:android-json by org.json:json

* Add debugging outputs

* Improve debugging outputs

* Improve debugging outputs

* Use Jackson instead of JsonBuilder

* Use Jackson instead of JsonBuilder 2nd part

* Use Spring json dependency

* Use eclipselink 2.7.11

* Fix RootControllerDocumentationTest

* Improve helper methods of AbstractDDiApiIntegrationTest

* Upgrade SpringBoot and SpringCloud versions

* Improve deprecation notice for 0.3.0M8

* Fix BaseAmqpServiceTest

* Fix SpecificationsBuilderTest

* Removed deprecated code

* Define maven-enforcer-plugin version

* Remove com.google.code.findbugs.jsr305

Signed-off-by: Florian Ruschbaschan <florian.ruschbaschan@bosch.io>

* Update circleci image to openjdk:openjdk:11.0.13-jdk-buster

Signed-off-by: Florian Ruschbaschan <florian.ruschbaschan@bosch.io>

* Fix javadoc generation and license check

Signed-off-by: Florian Ruschbaschan <florian.ruschbaschan@bosch.io>

* Fix review findings

Signed-off-by: Florian Ruschbaschan <florian.ruschbaschan@bosch.io>

Signed-off-by: Dominic Schabel <dominic.schabel@bosch.io>
Signed-off-by: Peter Vigier <Peter.Vigier@bosch.io>
Signed-off-by: Florian Ruschbaschan <florian.ruschbaschan@bosch.io>
Co-authored-by: Dominic Schabel <dominic.schabel@bosch.io>
Co-authored-by: Peter Vigier <Peter.Vigier@bosch.io>
Co-authored-by: Markus Block <markus.block@bosch-si.com>
This commit is contained in:
Florian Ruschbaschan
2022-09-19 10:33:31 +02:00
committed by GitHub
parent 537548defb
commit 32718676a4
36 changed files with 562 additions and 825 deletions

View File

@@ -14,7 +14,7 @@ jobs:
working_directory: ~/hawkBit
docker:
- image: circleci/openjdk:8u181-jdk-stretch
- image: circleci/openjdk:11.0.13-jdk-buster
auth:
username: $DOCKERHUB_USER
password: $DOCKERHUB_ACCESSTOKEN

View File

@@ -65,7 +65,7 @@ public class BaseAmqpServiceTest {
@Description("Tests invalid null message content")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void convertMessageWithNullContent() {
final Message message = createMessage(null);
final Message message = createMessage("".getBytes());
assertThatExceptionOfType(MessageConversionException.class)
.as("Expected MessageConversionException for invalid JSON")
.isThrownBy(() -> baseAmqpService.convertMessage(message, DmfActionUpdateStatus.class));

View File

@@ -32,6 +32,7 @@ public interface ArtifactEncryption {
*
* @return secrets key/value pairs
* @throws ArtifactEncryptionFailedException
* thrown in case of an error while generating secrets
*/
Map<String, String> generateSecrets();
@@ -44,6 +45,7 @@ public interface ArtifactEncryption {
* artifact stream to encrypt
* @return encrypted input stream
* @throws ArtifactEncryptionFailedException
* thrown in case of an error while encrypting the provided stream
*/
InputStream encryptStream(final Map<String, String> secrets, final InputStream stream);
@@ -56,6 +58,7 @@ public interface ArtifactEncryption {
* artifact stream to decrypt
* @return decrypted input stream
* @throws ArtifactEncryptionFailedException
* thrown in case of an error while decrypting the provided stream
*/
InputStream decryptStream(final Map<String, String> secrets, final InputStream stream);

View File

@@ -206,6 +206,7 @@ public interface TargetFilterQueryManagement {
*
* @return the page with the found {@link TargetFilterQuery}s
* @param pageable
* pagination information
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Slice<TargetFilterQuery> findWithAutoAssignDS(@NotNull Pageable pageable);

View File

@@ -66,59 +66,16 @@ public interface TargetManagement {
List<Target> assignTag(@NotEmpty Collection<String> controllerIds, long tagId);
/**
* Counts number of targets with given
* {@link Target#getAssignedDistributionSet()}.
*
* @param distId
* to search for
* Counts number of targets with the given distribution set assigned.
*
* @param distId to search for
* @return number of found {@link Target}s.
*
* @throws EntityNotFoundException
* if distribution set with given ID does not exist
* @throws EntityNotFoundException if distribution set with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
long countByAssignedDistributionSet(long distId);
/**
* Count {@link Target}s for all the given filter parameters.
*
* @param status
* find targets having one of these {@link TargetUpdateStatus}s.
* Set to <code>null</code> in case this is not required.
* @param overdueState
* find targets that are overdue (targets that did not respond
* during the configured intervals: poll_itvl + overdue_itvl).
* Set to <code>null</code> in case this is not required.
* @param searchText
* to find targets having the text anywhere in name or
* description. Set <code>null</code> in case this is not
* required.
* @param installedOrAssignedDistributionSetId
* to find targets having the {@link DistributionSet} as
* installed or assigned. Set to <code>null</code> in case this
* is not required.
* @param tagNames
* to find targets which are having any one in this tag names.
* Set <code>null</code> in case this is not required.
* @param selectTargetWithNoTag
* flag to select targets with no tag assigned
*
* @return the found number of {@link Target}s
*
* @throws EntityNotFoundException
* if distribution set with given ID does not exist
*
* @deprecated this method
* {@link TargetManagement#countByFilters(FilterParams)} should
* be used instead.
*/
@Deprecated
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
long countByFilters(Collection<TargetUpdateStatus> status, Boolean overdueState, String searchText,
Long installedOrAssignedDistributionSetId, Boolean selectTargetWithNoTag, String... tagNames);
/**
* Count {@link Target}s for all the given filter parameters.
*
@@ -135,14 +92,11 @@ public interface TargetManagement {
long countByFilters(@NotNull final FilterParams filterParams);
/**
* Counts number of targets with given with given distribution set Id
* Get the count of targets with the given distribution set id.
*
* @param distId
* to search for
* @param distId to search for
* @return number of found {@link Target}s.
*
* @throws EntityNotFoundException
* if distribution set with given ID does not exist
* @throws EntityNotFoundException if distribution set with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
@@ -152,12 +106,9 @@ public interface TargetManagement {
* Checks if there is already a {@link Target} that has the given
* distribution set Id assigned or installed.
*
* @param distId
* to search for
* @param distId to search for
* @return <code>true</code> if a {@link Target} exists.
*
* @throws EntityNotFoundException
* if distribution set with given ID does not exist
* @throws EntityNotFoundException if distribution set with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
@@ -226,8 +177,8 @@ public interface TargetManagement {
Target create(@NotNull @Valid TargetCreate create);
/**
* creates multiple {@link Target}s. If some of the given {@link Target}s
* already exists in the DB a {@link EntityAlreadyExistsException} is
* creates multiple {@link Target}s. If the given {@link Target}s
* already exists in the DB an {@link EntityAlreadyExistsException} is
* thrown. {@link Target}s contain all objects of the parameter targets,
* including duplicates.
*
@@ -546,28 +497,27 @@ public interface TargetManagement {
Slice<Target> findByTargetFilterQuery(@NotNull Pageable pageable, long targetFilterQueryId);
/**
* method retrieves all {@link Target}s from the repo in the following
* order:
* method retrieves all {@link Target}s from the repo in the following order:
* <p>
* 1) {@link Target}s which have the given {@link DistributionSet} as
* {@link Target#getTarget()} {@link Target#getInstalledDistributionSet()}
* <p>
* 2) {@link Target}s which have the given {@link DistributionSet} as
* {@link Target#getAssignedDistributionSet()}
* <p>
* 3) {@link Target}s which have no connection to the given
* {@link DistributionSet}.
* <ol>
* <li>{@link Target}s which have the given {@link DistributionSet} as installed
* distribution set</li>
* <li>{@link Target}s which have the given {@link DistributionSet} as assigned
* distribution set</li>
* <li>{@link Target}s which have no connection to the given
* {@link DistributionSet}</li>
* </ol>
*
* @param pageable
* the page request to page the result set
* @param orderByDistributionId
* {@link DistributionSet#getId()} to be ordered by
* @param filterParams
* the filters to apply; only filters are enabled that have
* non-null value; filters are AND-gated
* the filters to apply; only filters are enabled that have non-null
* value; filters are AND-gated
* @return a paged result {@link Page} of the {@link Target}s in a defined
* order.
*
*
* @throws EntityNotFoundException
* if distribution set with given ID does not exist
*/
@@ -614,17 +564,16 @@ public interface TargetManagement {
Page<Target> findByRsqlAndTag(@NotNull Pageable pageable, @NotNull String rsqlParam, long tagId);
/**
* Toggles {@link TargetTag} assignment to given {@link Target}s by means
* that if some (or all) of the targets in the list have the {@link Tag} not
* yet assigned, they will be. Only if all of theme have the tag already
* assigned they will be removed instead.
* Toggles {@link TargetTag} assignment to given {@link Target}s by means that
* if some (or all) of the targets in the list have the {@link Tag} not yet
* assigned, they will be. Only if all of them have the tag already assigned
* they will be removed instead.
*
* @param controllerIds
* to toggle for
* @param tagName
* to toggle
* @return TagAssigmentResult with all meta data of the assignment outcome.
*
* @return TagAssigmentResult with all metadata of the assignment outcome.
* @throws EntityNotFoundException
* if tag with given name does not exist
*/
@@ -641,7 +590,7 @@ public interface TargetManagement {
* to set the type to
* @param typeId
* to assign targets to
* @return {@link TargetTypeAssignmentResult} with all meta data of the
* @return {@link TargetTypeAssignmentResult} with all metadata of the
* assignment outcome.
*
* @throws EntityNotFoundException
@@ -656,7 +605,7 @@ public interface TargetManagement {
*
* @param controllerIds
* to remove the type from
* @return {@link TargetTypeAssignmentResult} with all meta data of the
* @return {@link TargetTypeAssignmentResult} with all metadata of the
* assignment outcome.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
@@ -827,15 +776,15 @@ public interface TargetManagement {
* for
* @param metadata
* the meta data entries to create or update
* @return the updated or created target meta data entries
*
* @return the updated or created target metadata entries
*
* @throws EntityNotFoundException
* if given target does not exist
*
*
* @throws EntityAlreadyExistsException
* in case one of the meta data entry already exists for the
* in case one of the metadata entry already exists for the
* specific key
*
*
* @throws AssignmentQuotaExceededException
* if the maximum number of {@link MetaData} entries is exceeded
* for the addressed {@link Target}
@@ -847,10 +796,10 @@ public interface TargetManagement {
* Deletes a target meta data entry.
*
* @param controllerId
* where meta data has to be deleted
* where metadata has to be deleted
* @param key
* of the meta data element
*
*
* @throws EntityNotFoundException
* if given target does not exist
*/
@@ -863,10 +812,10 @@ public interface TargetManagement {
* @param pageable
* the page request to page the result
* @param controllerId
* the controller id to retrieve the meta data from
* the controller id to retrieve the metadata from
*
* @return a paged result of all meta data entries for a given target id
*
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*/
@@ -875,12 +824,12 @@ public interface TargetManagement {
/**
* Counts all meta data by the given target id.
*
*
* @param controllerId
* the controller id to retrieve the meta data from
*
* @return count of all meta data entries for a given target id
*
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*/
@@ -888,17 +837,17 @@ public interface TargetManagement {
long countMetaDataByControllerId(@NotEmpty String controllerId);
/**
* Finds all meta data by the given target id and query.
*
* Finds all metadata by the given target id and query.
*
* @param pageable
* the page request to page the result
* @param controllerId
* the controller id to retrieve the meta data from
* the controller id to retrieve the metadata from
* @param rsqlParam
* rsql query string
*
* @return a paged result of all meta data entries for a given target id
*
*
* @throws RSQLParameterUnsupportedFieldException
* if a field in the RSQL string is used but not provided by the
* given {@code fieldNameProvider}
@@ -932,14 +881,14 @@ public interface TargetManagement {
* Updates a target meta data value if corresponding entry exists.
*
* @param controllerId
* {@link Target} controller id of the meta data entry to be
* {@link Target} controller id of the metadata entry to be
* updated
* @param metadata
* meta data entry to be updated
* @return the updated meta data entry
*
*
* @throws EntityNotFoundException
* in case the meta data entry does not exists and cannot be
* in case the metadata entry does not exist and cannot be
* updated
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)

View File

@@ -56,8 +56,10 @@ public interface TargetType extends NamedEntity {
}
/**
* Unassigns a distribution set type from target types
* Unassigns a {@link DistributionSetType} from {@link TargetType}
*
* @param dsTypeId
* that will be removed from {@link TargetType}
* @return the resulting target type
*/
TargetType removeDistributionSetType(final Long dsTypeId);

View File

@@ -29,6 +29,7 @@ public class TenantUsage {
* Constructor.
*
* @param tenantName
* name of the tenant
*/
public TenantUsage(final String tenantName) {
this.tenantName = tenantName;

View File

@@ -9,7 +9,7 @@
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
@@ -18,13 +18,11 @@
</parent>
<artifactId>hawkbit-repository-jpa</artifactId>
<name>hawkBit :: Repository :: JPA Implementation</name>
<properties>
<apt.source.dir>${project.build.directory}/generated-sources/apt/</apt.source.dir>
<classgraph.version>4.8.90</classgraph.version>
<eclipselink.maven.plugin.version>2.7.5.1</eclipselink.maven.plugin.version>
</properties>
<dependencies>
<!-- Hawkbit -->
<dependency>
@@ -70,7 +68,11 @@
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
@@ -89,14 +91,13 @@
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Static weaver for EclipseLink -->
<plugin>
<groupId>com.ethlo.persistence.tools</groupId>
<artifactId>eclipselink-maven-plugin</artifactId>
<version>${eclipselink.maven.plugin.version}</version>
<executions>
<execution>
<id>modelgen</id>
@@ -117,24 +118,10 @@
</configuration>
<dependencies>
<dependency>
<!--
Required for Java 11 since plugin version is build on
EclipseLink 2.7.5 (which doesn't have latest fixes for Java 11)
-->
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<version>${eclipselink.version}</version>
</dependency>
<dependency>
<!--
Required for Java 11 since plugin uses older version (4.8.21)
which suffer from NPE while weaving. We require at least 4.8.48
see: https://github.com/ethlo/eclipselink-maven-plugin/issues/31
-->
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
<version>${classgraph.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>

View File

@@ -743,7 +743,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
throw new InvalidTargetAttributeException();
}
final JpaTarget target = (JpaTarget) targetRepository.findOne(TargetSpecifications.hasControllerId(controllerId))
final JpaTarget target = targetRepository.findOne(TargetSpecifications.hasControllerId(controllerId))
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
// get the modifiable attribute map

View File

@@ -188,10 +188,10 @@ public class JpaTargetManagement implements TargetManagement {
final JpaTarget updatedTarget = JpaManagementHelper.touch(entityManager, targetRepository, target);
final List<TargetMetadata> createdMetadata = Collections.unmodifiableList(md.stream()
final List<TargetMetadata> createdMetadata = md.stream()
.map(meta -> targetMetadataRepository
.save(new JpaTargetMetadata(meta.getKey(), meta.getValue(), updatedTarget)))
.collect(Collectors.toList()));
.collect(Collectors.toUnmodifiableList());
// TargetUpdatedEvent is not sent within the touch() method due to the
// "lastModifiedAt" field being ignored in JpaTarget
@@ -228,12 +228,12 @@ public class JpaTargetManagement implements TargetManagement {
// target indirectly
final JpaTarget target = JpaManagementHelper.touch(entityManager, targetRepository,
getByControllerIdAndThrowIfNotFound(controllerId));
final JpaTargetMetadata matadata = targetMetadataRepository.save(updatedMetadata);
final JpaTargetMetadata metadata = targetMetadataRepository.save(updatedMetadata);
// target update event is set to ignore "lastModifiedAt" field so it is
// not send automatically within the touch() method
eventPublisherHolder.getEventPublisher()
.publishEvent(new TargetUpdatedEvent(target, eventPublisherHolder.getApplicationId()));
return matadata;
return metadata;
}
@Override
@@ -247,7 +247,7 @@ public class JpaTargetManagement implements TargetManagement {
final JpaTarget target = JpaManagementHelper.touch(entityManager, targetRepository,
getByControllerIdAndThrowIfNotFound(controllerId));
targetMetadataRepository.deleteById(metadata.getId());
// target update event is set to ignore "lastModifiedAt" field so it is
// target update event is set to ignore "lastModifiedAt" field, so it is
// not send automatically within the touch() method
eventPublisherHolder.getEventPublisher()
.publishEvent(new TargetUpdatedEvent(target, eventPublisherHolder.getApplicationId()));
@@ -419,14 +419,6 @@ public class JpaTargetManagement implements TargetManagement {
return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, pageable, specList);
}
@Override
public long countByFilters(final Collection<TargetUpdateStatus> status, final Boolean overdueState,
final String searchText, final Long installedOrAssignedDistributionSetId,
final Boolean selectTargetWithNoTag, final String... tagNames) {
return countByFilters(new FilterParams(status, overdueState, searchText, installedOrAssignedDistributionSetId,
selectTargetWithNoTag, tagNames));
}
@Override
public long countByFilters(final FilterParams filterParams) {
final List<Specification<JpaTarget>> specList = buildSpecificationList(filterParams);
@@ -590,8 +582,8 @@ public class JpaTargetManagement implements TargetManagement {
allTargets.forEach(target -> target.addTag(tag));
final List<Target> result = Collections
.unmodifiableList(allTargets.stream().map(targetRepository::save).collect(Collectors.toList()));
final List<Target> result = allTargets.stream().map(targetRepository::save)
.collect(Collectors.toUnmodifiableList());
// No reason to save the tag
entityManager.detach(tag);

View File

@@ -45,7 +45,7 @@ public class ParseExceptionWrapper {
/**
* Get the current token
*
*
* @return the current token or {@code null} if there is non.
*/
public TokenWrapper getCurrentToken() {

View File

@@ -46,7 +46,7 @@ public class LazyControllerManagementTest extends AbstractJpaIntegrationTest {
TimeUnit.MILLISECONDS.sleep(10);
controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST);
TimeUnit.MILLISECONDS.sleep(repositoryProperties.getPollPersistenceFlushTime() + 1);
TimeUnit.MILLISECONDS.sleep(repositoryProperties.getPollPersistenceFlushTime() + 10);
final Target updated = targetManagement.get(target.getId()).get();

View File

@@ -93,7 +93,7 @@ public class SpecificationsBuilderTest {
when(criteriaBuilder.equal(any(Path.class), eq("testValue1"))).thenReturn(equalPredicate1);
when(criteriaBuilder.equal(any(Path.class), eq("testValue2"))).thenReturn(equalPredicate2);
when(criteriaBuilder.and(eq(equalPredicate2), eq(equalPredicate1))).thenReturn(combinedPredicate);
when(criteriaBuilder.and(eq(equalPredicate1), eq(equalPredicate2))).thenReturn(combinedPredicate);
when(root.get("field1")).thenReturn(field1);
when(root.get("field2")).thenReturn(field2);

View File

@@ -34,6 +34,11 @@
<artifactId>hawkbit-repository-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-security-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-artifact-repository-filesystem</artifactId>
@@ -140,4 +145,4 @@
<artifactId>awaitility</artifactId>
</dependency>
</dependencies>
</project>
</project>

View File

@@ -24,10 +24,12 @@ public class CleanupTestExecutionListener extends AbstractTestExecutionListener
@Override
public void afterTestMethod(final TestContext testContext) throws Exception {
final ApplicationContext applicationContext = testContext.getApplicationContext();
new JpaTestRepositoryManagement(applicationContext.getBean(TenantAwareCacheManager.class),
applicationContext.getBean(SystemSecurityContext.class),
applicationContext.getBean(SystemManagement.class)).clearTestRepository();
WithSpringAuthorityRule.runAsPrivileged(() -> {
final ApplicationContext applicationContext = testContext.getApplicationContext();
new JpaTestRepositoryManagement(applicationContext.getBean(TenantAwareCacheManager.class),
applicationContext.getBean(SystemSecurityContext.class),
applicationContext.getBean(SystemManagement.class)).clearTestRepository();
return null;
});
}
}

View File

@@ -29,21 +29,15 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* </p>
*
* <p>
* The answer header would look like: { "id": "51659181", "time":
* "20140511T121314", "status": { "execution": "closed", "result": { "final":
* "success", "progress": {} } "details": [], } }
* The answer header would look like: { "time": "20140511T121314", "status": {
* "execution": "closed", "result": { "final": "success", "progress": {} }
* "details": [], } }
* </p>
*
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class DdiActionFeedback {
/**
* @deprecated
* This ID is always given by the actionId path variable.
* Will be removed in future versions.
*/
@Deprecated
private final Long id;
private final String time;
@NotNull
@@ -51,27 +45,20 @@ public class DdiActionFeedback {
private final DdiStatus status;
/**
* Constructor.
*
* @param id
* of the actions the feedback is for
* Constructs an action-feedback
*
* @param time
* of the feedback
* time of feedback
* @param status
* is the feedback itself
* status to be appended to the action
*/
@JsonCreator
public DdiActionFeedback(@JsonProperty("id") final Long id, @JsonProperty("time") final String time,
@JsonProperty("status") final DdiStatus status) {
this.id = id;
public DdiActionFeedback(@JsonProperty(value = "time", required = true) final String time,
@JsonProperty(value = "status", required = true) final DdiStatus status) {
this.time = time;
this.status = status;
}
public Long getId() {
return id;
}
public String getTime() {
return time;
}
@@ -82,7 +69,7 @@ public class DdiActionFeedback {
@Override
public String toString() {
return "ActionFeedback [id=" + id + ", time=" + time + ", status=" + status + "]";
return "ActionFeedback [time=" + time + ", status=" + status + "]";
}
}

View File

@@ -61,16 +61,6 @@ public final class DdiRestConstants {
*/
public static final String MEDIA_TYPE_CBOR = "application/cbor";
/**
* Media type for CBOR content with strings encoded as UTF-8. Technically
* redundant since CBOR always uses UTF-8, but Spring will append it
* regardless.
* @deprecated Since the Spring Framework (v5.2.4.RELEASE) dropped the charset attribute
* from content type headers, please use {@link DdiRestConstants#MEDIA_TYPE_CBOR} instead.
*/
@Deprecated
public static final String MEDIA_TYPE_CBOR_UTF8 = "application/cbor;charset=UTF-8";
private DdiRestConstants() {
// constant class, private constructor.
}

View File

@@ -13,8 +13,10 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import java.io.IOException;
import java.time.Instant;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
@@ -22,57 +24,41 @@ import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import org.junit.jupiter.api.Test;
/**
* Test serializability of DDI api model 'DdiActionFeedback'
* Test serialization of DDI api model 'DdiActionFeedback'
*/
@Feature("Unit Tests - Direct Device Integration API")
@Story("Serializability of DDI api Models")
@Story("Serialization of DDI api Models")
public class DdiActionFeedbackTest {
private ObjectMapper mapper = new ObjectMapper();
private final ObjectMapper mapper = new ObjectMapper();
@Test
@Description("Verify the correct serialization and deserialization of the model")
public void shouldSerializeAndDeserializeObject() throws IOException {
// Setup
Long id = 123L;
String time = "20190809T121314";
DdiStatus ddiStatus = new DdiStatus(DdiStatus.ExecutionStatus.CLOSED, null, Lists.emptyList());
DdiActionFeedback ddiActionFeedback = new DdiActionFeedback(id, time, ddiStatus);
final String time = Instant.now().toString();
final DdiStatus ddiStatus = new DdiStatus(DdiStatus.ExecutionStatus.CLOSED, null, Lists.emptyList());
final DdiActionFeedback ddiActionFeedback = new DdiActionFeedback(time, ddiStatus);
// Test
String serializedDdiActionFeedback = mapper.writeValueAsString(ddiActionFeedback);
DdiActionFeedback deserializedDdiActionFeedback = mapper.readValue(serializedDdiActionFeedback,
final String serializedDdiActionFeedback = mapper.writeValueAsString(ddiActionFeedback);
final DdiActionFeedback deserializedDdiActionFeedback = mapper.readValue(serializedDdiActionFeedback,
DdiActionFeedback.class);
assertThat(serializedDdiActionFeedback).contains(id.toString(), time);
assertThat(deserializedDdiActionFeedback.getId()).isEqualTo(id);
assertThat(serializedDdiActionFeedback).contains(time);
assertThat(deserializedDdiActionFeedback.getTime()).isEqualTo(time);
assertThat(deserializedDdiActionFeedback.getStatus().toString()).isEqualTo(ddiStatus.toString());
}
@Test
@Description("Verify the correct deserialization of a model with a additional unknown property")
public void shouldDeserializeObjectWithUnknownProperty() throws IOException {
// Setup
String serializedDdiActionFeedback = "{\"id\":1, \"time\":\"20190809T121314\", \"status\":{\"execution\":\"closed\", \"result\":null, \"details\":[]}, \"unknownProperty\": \"test\"}";
// Test
DdiActionFeedback ddiActionFeedback = mapper.readValue(serializedDdiActionFeedback, DdiActionFeedback.class);
assertThat(ddiActionFeedback.getId()).isEqualTo(1L);
assertThat(ddiActionFeedback.getTime()).matches("20190809T121314");
assertThat(deserializedDdiActionFeedback.getStatus()).hasToString(ddiStatus.toString());
}
@Test
@Description("Verify that deserialization fails for known properties with a wrong datatype")
public void shouldFailForObjectWithWrongDataTypes() throws IOException {
// Setup
String serializedDdiActionFeedback = "{\"id\": [1],\"time\":\"20190809T121314\",\"status\":{\"execution\":\"closed\",\"result\":null,\"details\":[]}}";
final String serializedDdiActionFeedback = "{\"time\":\"20190809T121314\",\"status\":{\"execution\": [closed],\"result\":null,\"details\":[]}}";
assertThatExceptionOfType(MismatchedInputException.class).isThrownBy(
() -> mapper.readValue(serializedDdiActionFeedback, DdiActionFeedback.class));
}
}
}

View File

@@ -27,7 +27,7 @@ import io.qameta.allure.Story;
* Check DDI api model classes for '@JsonIgnoreProperties' annotation
*/
@Feature("Unit Tests - Direct Device Integration API")
@Story("Serializability of DDI api Models")
@Story("Serialization of DDI api Models")
public class JsonIgnorePropertiesAnnotationTest {
@Test
@@ -35,15 +35,15 @@ public class JsonIgnorePropertiesAnnotationTest {
"This test verifies that all model classes within the 'org.eclipse.hawkbit.ddi.json.model' package are annotated with '@JsonIgnoreProperties(ignoreUnknown = true)'")
public void shouldCheckAnnotationsForAllModelClasses() throws IOException {
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
String packageName = this.getClass().getPackage().getName();
final String packageName = this.getClass().getPackage().getName();
ImmutableSet<ClassPath.ClassInfo> topLevelClasses = ClassPath.from(loader).getTopLevelClasses(packageName);
for (ClassPath.ClassInfo classInfo : topLevelClasses) {
Class<?> modelClass = classInfo.load();
final ImmutableSet<ClassPath.ClassInfo> topLevelClasses = ClassPath.from(loader).getTopLevelClasses(packageName);
for (final ClassPath.ClassInfo classInfo : topLevelClasses) {
final Class<?> modelClass = classInfo.load();
if (modelClass.getSimpleName().contains("Test") || modelClass.isEnum()) {
continue;
}
JsonIgnoreProperties annotation = modelClass.getAnnotation(JsonIgnoreProperties.class);
final JsonIgnoreProperties annotation = modelClass.getAnnotation(JsonIgnoreProperties.class);
assertThat(annotation).isNotNull();
assertThat(annotation.ignoreUnknown()).isTrue();
}

View File

@@ -212,7 +212,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
.orElseThrow(() -> new SoftwareModuleNotAssignedToTargetException(module, target.getControllerId()));
final String range = request.getHeader("Range");
String message;
final String message;
if (range != null) {
message = RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target downloads range " + range + " of: "
+ request.getRequestURI();
@@ -327,7 +327,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
if (!action.isActive()) {
LOG.warn("Updating action {} with feedback {} not possible since action not active anymore.",
action.getId(), feedback.getId());
action.getId(), feedback.getStatus());
return new ResponseEntity<>(HttpStatus.GONE);
}
@@ -346,7 +346,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
messages.addAll(feedback.getStatus().getDetails());
}
Status status;
final Status status;
switch (feedback.getStatus().getExecution()) {
case CANCELED:
LOG.debug("Controller confirmed cancel (actionId: {}, controllerId: {}) as we got {} report.", actionId,
@@ -391,7 +391,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
private Status handleDefaultCase(final DdiActionFeedback feedback, final String controllerId, final Long actionId,
final List<String> messages) {
Status status;
final Status status;
LOG.debug("Controller reported intermediate status (actionId: {}, controllerId: {}) as we got {} report.",
actionId, controllerId, feedback.getStatus().getExecution());
status = Status.RUNNING;
@@ -401,7 +401,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
private Status handleClosedCase(final DdiActionFeedback feedback, final String controllerId, final Long actionId,
final List<String> messages) {
Status status;
final Status status;
LOG.debug("Controller reported closed (actionId: {}, controllerId: {}) as we got {} report.", actionId,
controllerId, feedback.getStatus().getExecution());
if (feedback.getStatus().getResult().getFinished() == FinalResult.FAILURE) {
@@ -482,8 +482,8 @@ public class DdiRootController implements DdiRootControllerRestApi {
}
private DdiDeploymentBase generateDdiDeploymentBase(Target target, Action action,
Integer actionHistoryMessageCount) {
private DdiDeploymentBase generateDdiDeploymentBase(final Target target, final Action action,
final Integer actionHistoryMessageCount) {
final List<DdiChunk> chunks = DataConversionHelper.createChunks(target, action, artifactUrlHandler,
systemManagement, new ServletServerHttpRequest(requestResponseContextHolder.getHttpServletRequest()),
controllerManagement);
@@ -508,7 +508,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
final Long actionId, final EntityFactory entityFactory) {
final List<String> messages = new ArrayList<>();
Status status;
final Status status;
switch (feedback.getStatus().getExecution()) {
case CANCELED:
status = handleCaseCancelCanceled(feedback, target, actionId, messages);
@@ -536,7 +536,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
}
private static Status handleCancelClosedCase(final DdiActionFeedback feedback, final List<String> messages) {
Status status;
final Status status;
if (feedback.getStatus().getResult().getFinished() == FinalResult.FAILURE) {
status = Status.ERROR;
addMessageIfEmpty("Target was not able to complete cancellation", messages);
@@ -549,7 +549,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
private static Status handleCaseCancelCanceled(final DdiActionFeedback feedback, final Target target,
final Long actionId, final List<String> messages) {
Status status;
final Status status;
LOG.error(
"Target reported cancel for a cancel which is not supported by the server (actionId: {}, controllerId: {}) as we got {} report.",
actionId, target.getControllerId(), feedback.getStatus().getExecution());
@@ -570,7 +570,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
return verifyActionBelongsToTarget(action, target);
}
private Action verifyActionBelongsToTarget(Action action, Target target) {
private Action verifyActionBelongsToTarget(final Action action, final Target target) {
if (!action.getTarget().getId().equals(target.getId())) {
LOG.debug(GIVEN_ACTION_IS_NOT_ASSIGNED_TO_GIVEN_TARGET, action.getId(), target.getId());
throw new EntityNotFoundException(

View File

@@ -18,7 +18,15 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.time.Instant;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.ddi.json.model.DdiActionFeedback;
import org.eclipse.hawkbit.ddi.json.model.DdiProgress;
import org.eclipse.hawkbit.ddi.json.model.DdiResult;
import org.eclipse.hawkbit.ddi.json.model.DdiStatus;
import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Artifact;
@@ -38,6 +46,8 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
import com.fasterxml.jackson.dataformat.cbor.CBORGenerator;
import com.fasterxml.jackson.dataformat.cbor.CBORParser;
@@ -59,22 +69,23 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
protected static final String CANCEL_FEEDBACK = CANCEL_ACTION + "/feedback";
protected static final int ARTIFACT_SIZE = 5 * 1024;
private static final ObjectMapper objectMapper = new ObjectMapper();
/**
* Convert JSON to a CBOR equivalent.
*
*
* @param json
* JSON object to convert
* @return Equivalent CBOR data
* @throws IOException
* Invalid JSON input
*/
protected static byte[] jsonToCbor(String json) throws IOException {
JsonFactory jsonFactory = new JsonFactory();
JsonParser jsonParser = jsonFactory.createParser(json);
ByteArrayOutputStream out = new ByteArrayOutputStream();
CBORFactory cborFactory = new CBORFactory();
CBORGenerator cborGenerator = cborFactory.createGenerator(out);
protected static byte[] jsonToCbor(final String json) throws IOException {
final JsonFactory jsonFactory = new JsonFactory();
final JsonParser jsonParser = jsonFactory.createParser(json);
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final CBORFactory cborFactory = new CBORFactory();
final CBORGenerator cborGenerator = cborFactory.createGenerator(out);
while (jsonParser.nextToken() != null) {
cborGenerator.copyCurrentEvent(jsonParser);
}
@@ -84,19 +95,19 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
/**
* Convert CBOR to JSON equivalent.
*
*
* @param input
* CBOR data to convert
* @return Equivalent JSON string
* @throws IOException
* Invalid CBOR input
*/
protected static String cborToJson(byte[] input) throws IOException {
CBORFactory cborFactory = new CBORFactory();
CBORParser cborParser = cborFactory.createParser(input);
JsonFactory jsonFactory = new JsonFactory();
StringWriter stringWriter = new StringWriter();
JsonGenerator jsonGenerator = jsonFactory.createGenerator(stringWriter);
protected static String cborToJson(final byte[] input) throws IOException {
final CBORFactory cborFactory = new CBORFactory();
final CBORParser cborParser = cborFactory.createParser(input);
final JsonFactory jsonFactory = new JsonFactory();
final StringWriter stringWriter = new StringWriter();
final JsonGenerator jsonGenerator = jsonFactory.createGenerator(stringWriter);
while (cborParser.nextToken() != null) {
jsonGenerator.copyCurrentEvent(cborParser);
}
@@ -106,7 +117,7 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
protected ResultActions postDeploymentFeedback(final String controllerId, final Long actionId, final String content,
final ResultMatcher statusMatcher) throws Exception {
return postDeploymentFeedback(MediaType.APPLICATION_JSON, controllerId, actionId, content.getBytes(),
return postDeploymentFeedback(MediaType.APPLICATION_JSON_UTF8, controllerId, actionId, content.getBytes(),
statusMatcher);
}
@@ -120,7 +131,7 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
protected ResultActions postCancelFeedback(final String controllerId, final Long actionId, final String content,
final ResultMatcher statusMatcher) throws Exception {
return postCancelFeedback(MediaType.APPLICATION_JSON, controllerId, actionId, content.getBytes(),
return postCancelFeedback(MediaType.APPLICATION_JSON_UTF8, controllerId, actionId, content.getBytes(),
statusMatcher);
}
@@ -164,7 +175,7 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
getDownloadAndUploadType(actionType), getDownloadAndUploadType(actionType));
}
private ResultActions verifyBasePayload(ResultActions resultActions, final String controllerId,
private ResultActions verifyBasePayload(final ResultActions resultActions, final String controllerId,
final DistributionSet ds, final Artifact artifact, final Artifact artifactSignature, final Long actionId,
final Long osModuleId, final String downloadType, final String updateType) throws Exception {
return resultActions.andExpect(jsonPath("$.id", equalTo(String.valueOf(actionId))))
@@ -234,4 +245,92 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
return "attempt";
}
protected String getJsonRejectedCancelActionFeedback() throws JsonProcessingException {
return getJsonActionFeedback(DdiStatus.ExecutionStatus.REJECTED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("rejected"));
}
protected String getJsonRejectedDeploymentActionFeedback() throws JsonProcessingException {
return getJsonActionFeedback(DdiStatus.ExecutionStatus.REJECTED, DdiResult.FinalResult.NONE,
Collections.singletonList("rejected"));
}
protected String getJsonDownloadDeploymentActionFeedback() throws JsonProcessingException {
return getJsonActionFeedback(DdiStatus.ExecutionStatus.DOWNLOAD, DdiResult.FinalResult.NONE,
Collections.singletonList("download"));
}
protected String getJsonDownloadedDeploymentActionFeedback() throws JsonProcessingException {
return getJsonActionFeedback(DdiStatus.ExecutionStatus.DOWNLOADED, DdiResult.FinalResult.NONE,
Collections.singletonList("download"));
}
protected String getJsonCanceledCancelActionFeedback() throws JsonProcessingException {
return getJsonActionFeedback(DdiStatus.ExecutionStatus.CANCELED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("canceled"));
}
protected String getJsonCanceledDeploymentActionFeedback() throws JsonProcessingException {
return getJsonActionFeedback(DdiStatus.ExecutionStatus.CANCELED, DdiResult.FinalResult.NONE,
Collections.singletonList("canceled"));
}
protected String getJsonScheduledCancelActionFeedback() throws JsonProcessingException {
return getJsonActionFeedback(DdiStatus.ExecutionStatus.SCHEDULED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("scheduled"));
}
protected String getJsonScheduledDeploymentActionFeedback() throws JsonProcessingException {
return getJsonActionFeedback(DdiStatus.ExecutionStatus.SCHEDULED, DdiResult.FinalResult.NONE,
Collections.singletonList("scheduled"));
}
protected String getJsonResumedCancelActionFeedback() throws JsonProcessingException {
return getJsonActionFeedback(DdiStatus.ExecutionStatus.RESUMED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("resumed"));
}
protected String getJsonResumedDeploymentActionFeedback() throws JsonProcessingException {
return getJsonActionFeedback(DdiStatus.ExecutionStatus.RESUMED, DdiResult.FinalResult.NONE,
Collections.singletonList("resumed"));
}
protected String getJsonProceedingCancelActionFeedback() throws JsonProcessingException {
return getJsonActionFeedback(DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("proceeding"));
}
protected String getJsonProceedingDeploymentActionFeedback() throws JsonProcessingException {
return getJsonActionFeedback(DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.NONE,
Collections.singletonList("proceeding"));
}
protected String getJsonClosedCancelActionFeedback() throws JsonProcessingException {
return getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("closed"));
}
protected String getJsonClosedDeploymentActionFeedback() throws JsonProcessingException {
return getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.NONE,
Collections.singletonList("closed"));
}
protected String getJsonActionFeedback(final DdiStatus.ExecutionStatus executionStatus,
final DdiResult.FinalResult finalResult) throws JsonProcessingException {
return getJsonActionFeedback(executionStatus, finalResult,
Collections.singletonList(RandomStringUtils.randomAlphanumeric(1000)));
}
protected String getJsonActionFeedback(final DdiStatus.ExecutionStatus executionStatus, final DdiResult ddiResult,
final List<String> messages) throws JsonProcessingException {
final DdiStatus ddiStatus = new DdiStatus(executionStatus, ddiResult, messages);
return objectMapper.writeValueAsString(new DdiActionFeedback(Instant.now().toString(), ddiStatus));
}
protected String getJsonActionFeedback(final DdiStatus.ExecutionStatus executionStatus,
final DdiResult.FinalResult finalResult, final List<String> messages) throws JsonProcessingException {
final DdiStatus ddiStatus = new DdiStatus(executionStatus, new DdiResult(finalResult, new DdiProgress(2, 5)),
messages);
return objectMapper.writeValueAsString(new DdiActionFeedback(Instant.now().toString(), ddiStatus));
}
}

View File

@@ -21,15 +21,17 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.hawkbit.ddi.json.model.DdiResult;
import org.eclipse.hawkbit.ddi.json.model.DdiStatus;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.junit.jupiter.api.Test;
import org.springframework.hateoas.MediaTypes;
@@ -57,18 +59,22 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
final Action cancelAction = deploymentManagement.cancelAction(actionId);
// check that we can get the cancel action as CBOR
final byte[] result = mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId(), tenantAware.getCurrentTenant()).accept(DdiRestConstants.MEDIA_TYPE_CBOR))
final byte[] result = mvc
.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId(), tenantAware.getCurrentTenant())
.accept(DdiRestConstants.MEDIA_TYPE_CBOR))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(DdiRestConstants.MEDIA_TYPE_CBOR))
.andReturn().getResponse().getContentAsByteArray();
assertThat(JsonPathUtils.<String>evaluate(cborToJson(result), "$.id")).isEqualTo(String.valueOf(cancelAction.getId()));
assertThat(JsonPathUtils.<String>evaluate(cborToJson(result), "$.cancelAction.stopId")).isEqualTo(String.valueOf(actionId));
.andExpect(content().contentType(DdiRestConstants.MEDIA_TYPE_CBOR)).andReturn().getResponse()
.getContentAsByteArray();
assertThat(JsonPathUtils.<String> evaluate(cborToJson(result), "$.id"))
.isEqualTo(String.valueOf(cancelAction.getId()));
assertThat(JsonPathUtils.<String> evaluate(cborToJson(result), "$.cancelAction.stopId"))
.isEqualTo(String.valueOf(actionId));
// and submit feedback as CBOR
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).content(
jsonToCbor(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "proceeding")))
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(jsonToCbor(getJsonProceedingCancelActionFeedback()))
.contentType(DdiRestConstants.MEDIA_TYPE_CBOR).accept(DdiRestConstants.MEDIA_TYPE_CBOR))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
}
@@ -85,11 +91,11 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
final Action cancelAction = deploymentManagement.cancelAction(actionId);
// controller rejects cancelation
// controller rejects cancellation
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "rejected"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.content(getJsonRejectedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
final long current = System.currentTimeMillis();
@@ -112,8 +118,9 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// and finish it
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/"
+ actionId + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(actionId.toString(), "closed", "success"))
+ actionId + "/feedback", tenantAware.getCurrentTenant()).content(
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.NONE,
Collections.singletonList("message")))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
@@ -139,9 +146,8 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
final long timeBeforeFirstPoll = System.currentTimeMillis();
mvc.perform(get("/{tenant}/controller/v1/{controller}", tenantAware.getCurrentTenant(),
TestdataFactory.DEFAULT_CONTROLLER_ID).accept(MediaTypes.HAL_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaTypes.HAL_JSON))
TestdataFactory.DEFAULT_CONTROLLER_ID).accept(MediaTypes.HAL_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(content().contentType(MediaTypes.HAL_JSON))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith("http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/"
@@ -191,8 +197,8 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// controller confirmed cancelled action, should not be active anymore
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON)
.content(JsonBuilder.cancelActionFeedback(actionId.toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
activeActionsByTarget = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
@@ -263,8 +269,8 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "proceeding"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.content(getJsonProceedingCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
@@ -272,16 +278,16 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "resumed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.content(getJsonResumedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(4);
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "scheduled"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.content(getJsonScheduledCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(5);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
@@ -290,8 +296,8 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "canceled"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.content(getJsonCanceledCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(6);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
@@ -303,8 +309,8 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "rejected"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.content(getJsonRejectedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(7);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
@@ -312,8 +318,8 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// update closed -> should remove the action from active
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(8);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).isEmpty();
@@ -364,8 +370,8 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// now lets return feedback for the first cancelation
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(7);
@@ -391,8 +397,8 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// now lets return feedback for the second cancelation
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction2.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction2.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(9);
@@ -426,8 +432,8 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// now lets return feedback for the third cancelation
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction3.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction3.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(13);
@@ -447,7 +453,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
final Action cancelAction = deploymentManagement.cancelAction(actionId);
final String feedback = JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "proceeding");
final String feedback = getJsonProceedingCancelActionFeedback();
// assignDistributionSet creates an ActionStatus and cancel action
// stores an action status, so
// only 97 action status left
@@ -473,8 +479,8 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// not allowed methods
mvc.perform(put("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
mvc.perform(delete("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
@@ -488,36 +494,35 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// bad content type
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_ATOM_XML).accept(MediaType.APPLICATION_JSON))
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_ATOM_XML)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isUnsupportedMediaType());
// bad body
String invalidFeedback = "{\"status\":{\"execution\":\"546456456\",\"result\":{\"finished\":\"none\",\"progress\":{\"cnt\":2,\"of\":5}},\"details\":\"none\"]}}";
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "546456456"))
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).content(invalidFeedback)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
// non existing target
mvc.perform(post("/{tenant}/controller/v1/12345/cancelAction/" + cancelAction.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "closed"))
tenantAware.getCurrentTenant()).content(getJsonClosedCancelActionFeedback())
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// invalid action
invalidFeedback = "{\"id\":\"sdfsdfsdfs\",\"status\":{\"execution\":\"closed\",\"result\":{\"finished\":\"none\",\"progress\":{\"cnt\":2,\"of\":5}},\"details\":\"details\"]}}";
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback("sdfsdfsdfs", "closed"))
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).content(invalidFeedback)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
// finaly, get it right :)
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
}
}

View File

@@ -27,6 +27,8 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.RandomUtils;
import org.assertj.core.api.Condition;
import org.eclipse.hawkbit.ddi.json.model.DdiResult;
import org.eclipse.hawkbit.ddi.json.model.DdiStatus;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
@@ -45,7 +47,6 @@ import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.rest.exception.MessageNotReadableException;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.PageRequest;
@@ -81,8 +82,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
.getContent().get(0);
// get deployment base
performGet(DEPLOYMENT_BASE,
MediaType.parseMediaType(DdiRestConstants.MEDIA_TYPE_CBOR), status().isOk(),
performGet(DEPLOYMENT_BASE, MediaType.parseMediaType(DdiRestConstants.MEDIA_TYPE_CBOR), status().isOk(),
tenantAware.getCurrentTenant(), target.getControllerId(), action.getId().toString());
final Long softwareModuleId = distributionSet.getModules().stream().findAny().get().getId();
@@ -92,8 +92,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
status().isOk(), tenantAware.getCurrentTenant(), target.getControllerId(),
String.valueOf(softwareModuleId));
final byte[] feedback = jsonToCbor(
JsonBuilder.deploymentActionFeedback(action.getId().toString(), "proceeding"));
final byte[] feedback = jsonToCbor(getJsonProceedingDeploymentActionFeedback());
postDeploymentFeedback(MediaType.parseMediaType(DdiRestConstants.MEDIA_TYPE_CBOR), target.getControllerId(),
action.getId(), feedback, status().isOk());
@@ -164,8 +163,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
// Run test
final long current = System.currentTimeMillis();
performGet(CONTROLLER_BASE, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
DEFAULT_CONTROLLER_ID)
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
DEFAULT_CONTROLLER_ID).andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
@@ -262,8 +260,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final long current = System.currentTimeMillis();
performGet(CONTROLLER_BASE, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
DEFAULT_CONTROLLER_ID)
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
DEFAULT_CONTROLLER_ID).andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
@@ -274,9 +271,9 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final DistributionSet findDistributionSetByAction = distributionSetManagement.getByAction(action.getId()).get();
getAndVerifyDeploymentBasePayload(DEFAULT_CONTROLLER_ID, MediaType.APPLICATION_JSON, ds,
visibleMetadataOsKey, visibleMetadataOsValue, artifact, artifactSignature, action.getId(), "attempt",
"attempt", getOsModule(findDistributionSetByAction));
getAndVerifyDeploymentBasePayload(DEFAULT_CONTROLLER_ID, MediaType.APPLICATION_JSON, ds, visibleMetadataOsKey,
visibleMetadataOsValue, artifact, artifactSignature, action.getId(), "attempt", "attempt",
getOsModule(findDistributionSetByAction));
// Retrieved is reported
final List<ActionStatus> actionStatusMessages = deploymentManagement
@@ -301,8 +298,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final Target savedTarget = createTargetAndAssertNoActiveActions();
final List<Target> saved = assignDistributionSet(ds.getId(), savedTarget.getControllerId(),
ActionType.TIMEFORCED)
.getAssignedEntity().stream().map(Action::getTarget).collect(Collectors.toList());
ActionType.TIMEFORCED).getAssignedEntity().stream().map(Action::getTarget).collect(Collectors.toList());
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
@@ -321,8 +317,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final long current = System.currentTimeMillis();
performGet(CONTROLLER_BASE, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
DEFAULT_CONTROLLER_ID)
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
DEFAULT_CONTROLLER_ID).andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
@@ -337,9 +332,9 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
artifactSignature, action.getId(),
findDistributionSetByAction.findFirstModuleByType(osType).get().getId(), "forced", "forced");
getAndVerifyDeploymentBasePayload(DEFAULT_CONTROLLER_ID, MediaTypes.HAL_JSON, ds, artifact,
artifactSignature, action.getId(),
findDistributionSetByAction.findFirstModuleByType(osType).get().getId(), "forced", "forced");
getAndVerifyDeploymentBasePayload(DEFAULT_CONTROLLER_ID, MediaTypes.HAL_JSON, ds, artifact, artifactSignature,
action.getId(), findDistributionSetByAction.findFirstModuleByType(osType).get().getId(), "forced",
"forced");
// Retrieved is reported
final Iterable<ActionStatus> actionStatusMessages = deploymentManagement
@@ -368,8 +363,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final Target savedTarget = createTargetAndAssertNoActiveActions();
final List<Target> saved = assignDistributionSet(ds.getId(), savedTarget.getControllerId(),
ActionType.DOWNLOAD_ONLY)
.getAssignedEntity().stream().map(Action::getTarget).collect(Collectors.toList());
ActionType.DOWNLOAD_ONLY).getAssignedEntity().stream().map(Action::getTarget)
.collect(Collectors.toList());
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
@@ -388,8 +383,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final long current = System.currentTimeMillis();
performGet(CONTROLLER_BASE, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
DEFAULT_CONTROLLER_ID)
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
DEFAULT_CONTROLLER_ID).andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
@@ -472,7 +466,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final Action action = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent()
.get(0);
final String feedback = JsonBuilder.deploymentActionFeedback(action.getId().toString(), "proceeding");
final String feedback = getJsonProceedingDeploymentActionFeedback();
// assign distribution set creates an action status, so only 99 left
for (int i = 0; i < 99; i++) {
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, action.getId(), feedback, status().isOk());
@@ -497,10 +491,9 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
messages.add(String.valueOf(i));
}
final String feedback = JsonBuilder.deploymentActionFeedback(action.getId().toString(), "proceeding", "none",
final String feedback = getJsonActionFeedback(DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.NONE,
messages);
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, action.getId(), feedback,
status().isForbidden());
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, action.getId(), feedback, status().isForbidden());
}
@Test
@@ -522,22 +515,22 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
assertThat(targetManagement.findByUpdateStatus(PageRequest.of(0, 10), TargetUpdateStatus.UNKNOWN)).hasSize(2);
// action1 done
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId1,
JsonBuilder.deploymentActionFeedback(actionId1.toString(), "closed"), status().isOk());
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId1, getJsonClosedDeploymentActionFeedback(),
status().isOk());
findTargetAndAssertUpdateStatus(Optional.of(ds3), TargetUpdateStatus.PENDING, 2, Optional.of(ds1));
assertStatusMessagesCount(4);
// action2 done
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId2,
JsonBuilder.deploymentActionFeedback(actionId2.toString(), "closed"), status().isOk());
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId2, getJsonClosedDeploymentActionFeedback(),
status().isOk());
findTargetAndAssertUpdateStatus(Optional.of(ds3), TargetUpdateStatus.PENDING, 1, Optional.of(ds2));
assertStatusMessagesCount(5);
// action3 done
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId3,
JsonBuilder.deploymentActionFeedback(actionId3.toString(), "closed"), status().isOk());
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId3, getJsonClosedDeploymentActionFeedback(),
status().isOk());
findTargetAndAssertUpdateStatus(Optional.of(ds3), TargetUpdateStatus.IN_SYNC, 0, Optional.of(ds3));
assertStatusMessagesCount(6);
@@ -556,7 +549,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final Action action = deploymentManagement.findActionsByDistributionSet(PAGE, ds.getId()).getContent().get(0);
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, action.getId(),
JsonBuilder.deploymentActionFeedback(action.getId().toString(), "closed", "failure", "error message"),
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.FAILURE,
Collections.singletonList("Error message")),
status().isOk());
findTargetAndAssertUpdateStatus(Optional.empty(), TargetUpdateStatus.ERROR, 0, Optional.empty());
@@ -569,8 +563,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
Collections.singletonList(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get()));
final Action action2 = deploymentManagement.findActiveActionsByTarget(PAGE, DEFAULT_CONTROLLER_ID).getContent()
.get(0);
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, action2.getId(),
JsonBuilder.deploymentActionFeedback(action2.getId().toString(), "closed", "SUCCESS"), status().isOk());
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, action2.getId(), getJsonClosedCancelActionFeedback(),
status().isOk());
findTargetAndAssertUpdateStatus(Optional.of(ds), TargetUpdateStatus.IN_SYNC, 0, Optional.of(ds));
assertTargetCountByStatus(0, 0, 1);
assertThat(deploymentManagement.findInActiveActionsByTarget(PAGE, DEFAULT_CONTROLLER_ID)).hasSize(2);
@@ -592,33 +586,33 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
// Now valid Feedback
for (int i = 0; i < 4; i++) {
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "proceeding"), status().isOk());
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId, getJsonProceedingDeploymentActionFeedback(),
status().isOk());
assertActionStatusCount(i + 2, i);
}
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "scheduled"), status().isOk());
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId, getJsonScheduledDeploymentActionFeedback(),
status().isOk());
assertActionStatusCount(6, 5);
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "resumed"), status().isOk());
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId, getJsonResumedDeploymentActionFeedback(),
status().isOk());
assertActionStatusCount(7, 6);
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "canceled"), status().isOk());
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId, getJsonCanceledDeploymentActionFeedback(),
status().isOk());
assertStatusAndActiveActionsCount(TargetUpdateStatus.PENDING, 1);
assertActionStatusCount(8, 7, 0, 0, 1);
assertTargetCountByStatus(1, 0, 0);
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "rejected"), status().isOk());
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId, getJsonRejectedDeploymentActionFeedback(),
status().isOk());
assertStatusAndActiveActionsCount(TargetUpdateStatus.PENDING, 1);
assertActionStatusCount(9, 6, 1, 0, 1);
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "closed"), status().isOk());
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId, getJsonClosedDeploymentActionFeedback(),
status().isOk());
assertStatusAndActiveActionsCount(TargetUpdateStatus.IN_SYNC, 0);
assertActionStatusCount(10, 7, 1, 1, 1);
assertTargetCountByStatus(0, 0, 1);
@@ -635,14 +629,13 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
// target does not exist
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, 1234L,
JsonBuilder.deploymentActionInProgressFeedback("1234"), status().isNotFound());
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, 1234L, getJsonProceedingDeploymentActionFeedback(),
status().isNotFound());
final Target savedTarget = testdataFactory.createTarget(DEFAULT_CONTROLLER_ID);
// Action does not exist
postDeploymentFeedback("4713", 1234L, JsonBuilder.deploymentActionInProgressFeedback("1234"),
status().isNotFound());
postDeploymentFeedback("4713", 1234L, getJsonProceedingDeploymentActionFeedback(), status().isNotFound());
assignDistributionSet(savedSet, Collections.singletonList(savedTarget)).getAssignedEntity().iterator().next();
assignDistributionSet(savedSet2, Collections.singletonList(testdataFactory.createTarget("4713")));
@@ -651,8 +644,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
.getContent().get(0);
// action exists but is not assigned to this target
postDeploymentFeedback("4713", updateAction.getId(),
JsonBuilder.deploymentActionInProgressFeedback(updateAction.getId().toString()), status().isNotFound());
postDeploymentFeedback("4713", updateAction.getId(), getJsonActionFeedback(DdiStatus.ExecutionStatus.PROCEEDING,
DdiResult.FinalResult.NONE, Collections.singletonList("")), status().isNotFound());
// not allowed methods
mvc.perform(MockMvcRequestBuilders.get(DEPLOYMENT_FEEDBACK, tenantAware.getCurrentTenant(),
@@ -669,11 +662,11 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that an invalid id in feedback body returns a bad request.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1)})
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1) })
public void invalidIdInFeedbackReturnsBadRequest() throws Exception {
final Target target = testdataFactory.createTarget("1080");
final DistributionSet ds = testdataFactory.createDistributionSet("");
@@ -681,17 +674,17 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
assignDistributionSet(ds.getId(), "1080");
final Action action = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent()
.get(0);
postDeploymentFeedback("1080", action.getId(),
JsonBuilder.deploymentActionInProgressFeedback("AAAA"), status().isBadRequest());
final String invalidFeedback = "{\"id\":\"AAAA\",\"status\":{\"execution\":\"proceeding\",\"result\":{\"finished\":\"none\",\"progress\":{\"cnt\":2,\"of\":5}},\"details\":\"details\"]}}";
postDeploymentFeedback("1080", action.getId(), invalidFeedback, status().isBadRequest());
}
@Test
@Description("Ensures that a missing feedback result in feedback body returns a bad request.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1)})
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1) })
public void missingResultAttributeInFeedbackReturnsBadRequest() throws Exception {
final Target target = testdataFactory.createTarget("1080");
@@ -700,20 +693,21 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
assignDistributionSet(ds.getId(), "1080");
final Action action = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent()
.get(0);
final String missingResultInFeedback = JsonBuilder.missingResultInFeedback(action.getId().toString(), "closed",
"test");
postDeploymentFeedback("1080", action.getId(), missingResultInFeedback,
status().isBadRequest()).andExpect(jsonPath("$.*", hasSize(3))).andExpect(
jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getCanonicalName())));
final String missingResultInFeedback = getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, (DdiResult) null,
Collections.singletonList("test"));
postDeploymentFeedback("1080", action.getId(), missingResultInFeedback, status().isBadRequest())
.andExpect(jsonPath("$.*", hasSize(3)))
.andExpect(jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getCanonicalName())));
}
@Test
@Description("Ensures that a missing finished result in feedback body returns a bad request.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1)})
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1) })
public void missingFinishedAttributeInFeedbackReturnsBadRequest() throws Exception {
final Target target = testdataFactory.createTarget("1080");
@@ -722,11 +716,13 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final Action action = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent()
.get(0);
final String missingFinishedResultInFeedback = JsonBuilder
.missingFinishedResultInFeedback(action.getId().toString(), "closed", "test");
postDeploymentFeedback("1080", action.getId(), missingFinishedResultInFeedback,
status().isBadRequest()).andExpect(jsonPath("$.*", hasSize(3))).andExpect(
jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getCanonicalName())));
final String missingFinishedResultInFeedback = getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED,
new DdiResult(null, null),
Collections.singletonList("test"));
postDeploymentFeedback("1080", action.getId(), missingFinishedResultInFeedback, status().isBadRequest())
.andExpect(jsonPath("$.*", hasSize(3)))
.andExpect(jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getCanonicalName())));
}
private void assertActionStatusCount(final int actionStatusCount, final int minActionStatusCountInPage) {

View File

@@ -27,6 +27,8 @@ import java.util.List;
import java.util.stream.Stream;
import org.apache.commons.lang3.RandomUtils;
import org.eclipse.hawkbit.ddi.json.model.DdiResult;
import org.eclipse.hawkbit.ddi.json.model.DdiStatus;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
@@ -45,7 +47,6 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
@@ -77,8 +78,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
final Target target = testdataFactory.createTarget();
final DistributionSet ds = testdataFactory.createDistributionSet("");
final Long actionId = getFirstAssignedActionId(assignDistributionSet(ds, target));
postDeploymentFeedback(target.getControllerId(), actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "closed"), status().isOk());
postDeploymentFeedback(target.getControllerId(), actionId, getJsonClosedDeploymentActionFeedback(),
status().isOk());
// get installed base
performGet(INSTALLED_BASE, MediaType.parseMediaType(DdiRestConstants.MEDIA_TYPE_CBOR), status().isOk(),
@@ -123,7 +124,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
actionId1, ds1.findFirstModuleByType(osType).get().getId(), Action.ActionType.SOFT);
postDeploymentFeedback(target.getControllerId(), actionId1,
JsonBuilder.deploymentActionFeedback(actionId1.toString(), "closed", "success", "Closed"),
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("Closed")),
status().isOk());
getAndVerifyInstalledBasePayload(CONTROLLER_ID, MediaType.APPLICATION_JSON, ds1, artifact1, artifactSignature1,
@@ -143,7 +145,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
actionId2, ds2.findFirstModuleByType(osType).get().getId(), Action.ActionType.FORCED);
postDeploymentFeedback(target.getControllerId(), actionId2,
JsonBuilder.deploymentActionFeedback(actionId2.toString(), "closed", "success", "Closed"),
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("Closed")),
status().isOk());
getAndVerifyInstalledBasePayload(CONTROLLER_ID, MediaType.APPLICATION_JSON, ds2, artifact2, artifactSignature2,
@@ -155,7 +158,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
startsWith(installedBaseLink(CONTROLLER_ID, actionId2.toString()))))
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist());
// older installed action is still accessible, although not part of controller base
// older installed action is still accessible, although not part of controller
// base
getAndVerifyInstalledBasePayload(CONTROLLER_ID, MediaType.APPLICATION_JSON, ds1, artifact1, artifactSignature1,
actionId1, ds1.findFirstModuleByType(osType).get().getId(), Action.ActionType.SOFT);
}
@@ -177,20 +181,25 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
assignDistributionSet(ds1.getId(), target.getControllerId(), Action.ActionType.SOFT));
deploymentManagement.cancelAction(actionId1);
postCancelFeedback(target.getControllerId(), actionId1,
JsonBuilder.cancelActionFeedback(actionId1.toString(), "closed", "Canceled"), status().isOk());
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("Canceled")),
status().isOk());
// assign ds1, action2 - and provide cancel feedback
final Long actionId2 = getFirstAssignedActionId(
assignDistributionSet(ds1.getId(), target.getControllerId(), Action.ActionType.FORCED));
deploymentManagement.cancelAction(actionId2);
postCancelFeedback(target.getControllerId(), actionId2,
JsonBuilder.cancelActionFeedback(actionId2.toString(), "closed", "Canceled"), status().isOk());
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("Canceled")),
status().isOk());
// assign ds1, action 3 - and provide success feedback
final Long actionId3 = getFirstAssignedActionId(
assignDistributionSet(ds1.getId(), target.getControllerId(), Action.ActionType.SOFT));
postDeploymentFeedback(target.getControllerId(), actionId3,
JsonBuilder.deploymentActionFeedback(actionId3.toString(), "closed", "success", "Closed"),
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("Canceled")),
status().isOk());
// Test: latest succeeded action is returned in installedBase
@@ -228,7 +237,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
final Long actionId1 = getFirstAssignedActionId(
assignDistributionSet(ds1.getId(), target.getControllerId(), Action.ActionType.SOFT));
postDeploymentFeedback(target.getControllerId(), actionId1,
JsonBuilder.deploymentActionFeedback(actionId1.toString(), "closed", "success", "Success"),
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("Success")),
status().isOk());
// assign ds2, action2 - assign ds1, action 3 - and cancel both
@@ -238,10 +248,14 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
assignDistributionSet(ds1.getId(), target.getControllerId(), Action.ActionType.SOFT));
deploymentManagement.cancelAction(actionId2);
postCancelFeedback(target.getControllerId(), actionId2,
JsonBuilder.cancelActionFeedback(actionId2.toString(), "closed", "Canceled"), status().isOk());
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("Canceled")),
status().isOk());
deploymentManagement.cancelAction(actionId3);
postCancelFeedback(target.getControllerId(), actionId3,
JsonBuilder.cancelActionFeedback(actionId3.toString(), "closed", "Canceled"), status().isOk());
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("Canceled")),
status().isOk());
// Test: the succeeded action is returned in installedBase instead of the latest
// cancelled action
@@ -279,7 +293,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
final Long actionId1 = getFirstAssignedActionId(
assignDistributionSet(ds1.getId(), target.getControllerId(), Action.ActionType.SOFT));
postDeploymentFeedback(target.getControllerId(), actionId1,
JsonBuilder.deploymentActionFeedback(actionId1.toString(), "closed", "success", "Success"),
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("Success")),
status().isOk());
// assign ds2, action2 - assign ds1, action 3 - and cancel action 2
@@ -289,7 +304,9 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
assignDistributionSet(ds1.getId(), target.getControllerId(), Action.ActionType.SOFT));
deploymentManagement.cancelAction(actionId2);
postCancelFeedback(target.getControllerId(), actionId2,
JsonBuilder.cancelActionFeedback(actionId2.toString(), "closed", "Canceled"), status().isOk());
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("Canceled")),
status().isOk());
// Test: the succeeded action is returned in installedBase instead of the latest
// cancelled action
@@ -338,8 +355,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
final DistributionSet ds = testdataFactory.createDistributionSet("");
final Long actionId = getFirstAssignedActionId(assignDistributionSet(ds, target));
postDeploymentFeedback(target.getControllerId(), actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "closed"), status().isOk());
postDeploymentFeedback(target.getControllerId(), actionId, getJsonClosedDeploymentActionFeedback(),
status().isOk());
final Long softwareModuleId = ds.getModules().stream().findAny().get().getId();
performGet(SOFTWARE_MODULE_ARTIFACTS, MediaType.APPLICATION_JSON, status().isOk(),
@@ -385,7 +402,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
assignDistributionSet(ds.getId(), target.getControllerId(), actionType));
postDeploymentFeedback(target.getControllerId(), actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "closed", "success", "Closed"),
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("Closed")),
status().isOk());
// Run test
@@ -433,10 +451,10 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
.andExpect(jsonPath("$._links.deploymentBase.href").exists())
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
postDeploymentFeedback(target.getControllerId(), actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "download"), status().isOk());
postDeploymentFeedback(target.getControllerId(), actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "downloaded"), status().isOk());
postDeploymentFeedback(target.getControllerId(), actionId, getJsonDownloadDeploymentActionFeedback(),
status().isOk());
postDeploymentFeedback(target.getControllerId(), actionId, getJsonDownloadedDeploymentActionFeedback(),
status().isOk());
// Test
performGet(CONTROLLER_BASE, MediaType.APPLICATION_JSON, status().isOk(), tenantAware.getCurrentTenant(),
@@ -448,7 +466,7 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
@ParameterizedTest
@MethodSource("org.eclipse.hawkbit.ddi.rest.resource.DdiInstalledBaseTest#actionTypeForDeployment")
@Description("Test a failed deployment to a controller. Checks that closed action is not represented as installedBase.")
public void deploymentActionFailedNotInInstalledBase(Action.ActionType actionType) throws Exception {
public void deploymentActionFailedNotInInstalledBase(final Action.ActionType actionType) throws Exception {
// Prepare test data
final Target target = testdataFactory.createTarget();
final DistributionSet ds = testdataFactory.createDistributionSet("");
@@ -461,9 +479,11 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
postDeploymentFeedback(target.getControllerId(), actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "proceeding"), status().isOk());
getJsonActionFeedback(DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.NONE),
status().isOk());
postDeploymentFeedback(target.getControllerId(), actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "closed", "failure", "Installation failed"),
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.FAILURE,
Collections.singletonList("Installation failed")),
status().isOk());
// Test
@@ -482,14 +502,20 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
final Action savedAction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
postDeploymentFeedback(savedTarget.getControllerId(), savedAction.getId(), JsonBuilder.deploymentActionFeedback(
savedAction.getId().toString(), "scheduled", "Installation scheduled"), status().isOk());
postDeploymentFeedback(savedTarget.getControllerId(), savedAction.getId(),
getJsonActionFeedback(DdiStatus.ExecutionStatus.SCHEDULED, DdiResult.FinalResult.NONE,
Collections.singletonList("Installation scheduled")),
status().isOk());
postDeploymentFeedback(savedTarget.getControllerId(), savedAction.getId(), JsonBuilder.deploymentActionFeedback(
savedAction.getId().toString(), "proceeding", "Installation proceeding"), status().isOk());
postDeploymentFeedback(savedTarget.getControllerId(), savedAction.getId(),
getJsonActionFeedback(DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.NONE,
Collections.singletonList("Installation proceeding")),
status().isOk());
// only this feedback triggers the ActionUpdateEvent
postDeploymentFeedback(savedTarget.getControllerId(), savedAction.getId(), JsonBuilder.deploymentActionFeedback(
savedAction.getId().toString(), "closed", "success", "Installation completed"), status().isOk());
postDeploymentFeedback(savedTarget.getControllerId(), savedAction.getId(),
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("Installation completed")),
status().isOk());
// Test
// for zero input no action history is returned
@@ -545,8 +571,7 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
final DistributionSet savedSet = testdataFactory.createDistributionSet("");
final Long actionId = getFirstAssignedActionId(assignDistributionSet(savedSet, toAssign));
postDeploymentFeedback(CONTROLLER_ID, actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "closed", "success"), status().isOk());
postDeploymentFeedback(CONTROLLER_ID, actionId, getJsonClosedCancelActionFeedback(), status().isOk());
mvc.perform(MockMvcRequestBuilders.get(INSTALLED_BASE, tenantAware.getCurrentTenant(), CONTROLLER_ID, actionId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(MockMvcRequestBuilders.get(INSTALLED_BASE, tenantAware.getCurrentTenant(), CONTROLLER_ID, actionId)

View File

@@ -33,6 +33,8 @@ import java.util.Map;
import java.util.UUID;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.ddi.json.model.DdiResult;
import org.eclipse.hawkbit.ddi.json.model.DdiStatus;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
@@ -88,9 +90,9 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensure that the root poll resource is available as CBOR")
void rootPollResourceCbor() throws Exception {
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), 4711)
.accept(DdiRestConstants.MEDIA_TYPE_CBOR)).andDo(MockMvcResultPrinter.print())
.andExpect(content().contentType(DdiRestConstants.MEDIA_TYPE_CBOR)).andExpect(status().isOk());
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), 4711).accept(DdiRestConstants.MEDIA_TYPE_CBOR))
.andDo(MockMvcResultPrinter.print()).andExpect(content().contentType(DdiRestConstants.MEDIA_TYPE_CBOR))
.andExpect(status().isOk());
}
@Test
@@ -147,11 +149,12 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
// make a poll, audit information should not be changed, run as
// controller principal!
WithSpringAuthorityRule.runAs(WithSpringAuthorityRule.withController("controller", CONTROLLER_ROLE_ANONYMOUS), () -> {
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), knownTargetControllerId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
return null;
});
WithSpringAuthorityRule.runAs(WithSpringAuthorityRule.withController("controller", CONTROLLER_ROLE_ANONYMOUS),
() -> {
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), knownTargetControllerId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
return null;
});
// verify that audit information has not changed
final Target targetVerify = targetManagement.getByControllerID(knownTargetControllerId).get();
@@ -175,7 +178,7 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
void rootRsPlugAndPlay() throws Exception {
final long current = System.currentTimeMillis();
String controllerId = "4711";
final String controllerId = "4711";
mvc.perform(get(CONTROLLER_BASE, "default-tenant", controllerId)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(content().contentType(MediaTypes.HAL_JSON))
@@ -204,16 +207,16 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Expect(type = TargetPollEvent.class, count = 1),
@Expect(type = TenantConfigurationCreatedEvent.class, count = 1) })
void pollWithModifiedGlobalPollingTime() throws Exception {
WithSpringAuthorityRule.runAs(WithSpringAuthorityRule.withUser("tenantadmin", HAS_AUTH_TENANT_CONFIGURATION), () -> {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.POLLING_TIME_INTERVAL,
"00:02:00");
return null;
});
WithSpringAuthorityRule.runAs(WithSpringAuthorityRule.withUser("tenantadmin", HAS_AUTH_TENANT_CONFIGURATION),
() -> {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.POLLING_TIME_INTERVAL,
"00:02:00");
return null;
});
WithSpringAuthorityRule.runAs(WithSpringAuthorityRule.withUser("controller", CONTROLLER_ROLE_ANONYMOUS), () -> {
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), 4711))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaTypes.HAL_JSON))
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), 4711)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(content().contentType(MediaTypes.HAL_JSON))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:02:00")));
return null;
});
@@ -230,7 +233,7 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6) })
void rootRsNotModified() throws Exception {
String controllerId = "4711";
final String controllerId = "4711";
final String etag = mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaTypes.HAL_JSON))
@@ -308,7 +311,7 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
void rootRsPrecommissioned() throws Exception {
String controllerId = "4711";
final String controllerId = "4711";
testdataFactory.createTarget(controllerId);
assertThat(targetManagement.getByControllerID(controllerId).get().getUpdateStatus())
@@ -339,12 +342,12 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
final long create = System.currentTimeMillis();
// make a poll, audit information should be set on plug and play
WithSpringAuthorityRule.runAs(WithSpringAuthorityRule.withController("controller", CONTROLLER_ROLE_ANONYMOUS), () -> {
mvc.perform(
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), knownControllerId1))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
return null;
});
WithSpringAuthorityRule.runAs(WithSpringAuthorityRule.withController("controller", CONTROLLER_ROLE_ANONYMOUS),
() -> {
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), knownControllerId1))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
return null;
});
// verify
final Target target = targetManagement.getByControllerID(knownControllerId1).get();
@@ -414,9 +417,8 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
final Map<String, String> attributes = Collections.singletonMap("AttributeKey", "AttributeValue");
assertThatAttributesUpdateIsRequested(savedTarget.getControllerId());
mvc.perform(put(CONTROLLER_BASE + "/configData", tenantAware.getCurrentTenant(),
savedTarget.getControllerId()).content(JsonBuilder.configData(attributes).toString())
.contentType(MediaType.APPLICATION_JSON))
mvc.perform(put(CONTROLLER_BASE + "/configData", tenantAware.getCurrentTenant(), savedTarget.getControllerId())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
assertThatAttributesUpdateIsNotRequested(savedTarget.getControllerId());
@@ -465,8 +467,9 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
if (message == null) {
message = RandomStringUtils.randomAlphanumeric(1000);
}
final String feedback = JsonBuilder.deploymentActionFeedback(action.getId().toString(), execution, finished,
message);
final String feedback = getJsonActionFeedback(DdiStatus.ExecutionStatus.valueOf(execution.toUpperCase()),
DdiResult.FinalResult.valueOf(finished.toUpperCase()), Collections.singletonList(message));
return mvc.perform(
post(DEPLOYMENT_FEEDBACK, tenantAware.getCurrentTenant(), target.getControllerId(), action.getId())
.content(feedback).contentType(MediaType.APPLICATION_JSON));
@@ -503,8 +506,7 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(get(DEPLOYMENT_BASE + "?actionHistory=2", tenantAware.getCurrentTenant(), 911, savedAction.getId())
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages",
hasItem(containsString(TARGET_COMPLETED_INSTALLATION_MSG))))
@@ -540,8 +542,7 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(get(DEPLOYMENT_BASE + "?actionHistory=0", tenantAware.getCurrentTenant(), 911, savedAction.getId())
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages").doesNotExist());
@@ -577,8 +578,7 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(get(DEPLOYMENT_BASE + "?actionHistory=-1", tenantAware.getCurrentTenant(), 911, savedAction.getId())
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages",
hasItem(containsString(TARGET_SCHEDULED_INSTALLATION_MSG))))
@@ -593,13 +593,14 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
void sleepTimeResponseForDifferentMaintenanceWindowParameters() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
WithSpringAuthorityRule.runAs(WithSpringAuthorityRule.withUser("tenantadmin", HAS_AUTH_TENANT_CONFIGURATION), () -> {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.POLLING_TIME_INTERVAL,
"00:05:00");
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MIN_POLLING_TIME_INTERVAL,
"00:01:00");
return null;
});
WithSpringAuthorityRule.runAs(WithSpringAuthorityRule.withUser("tenantadmin", HAS_AUTH_TENANT_CONFIGURATION),
() -> {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.POLLING_TIME_INTERVAL,
"00:05:00");
tenantConfigurationManagement
.addOrUpdateConfiguration(TenantConfigurationKey.MIN_POLLING_TIME_INTERVAL, "00:01:00");
return null;
});
final Target savedTarget = testdataFactory.createTarget("1911");
assignDistributionSetWithMaintenanceWindow(ds.getId(), savedTarget.getControllerId(), getTestSchedule(16),
@@ -648,9 +649,9 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), "1911",
action.getId()).accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(jsonPath("$.deployment.download", equalTo("forced")))
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), "1911", action.getId())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.deployment.download", equalTo("forced")))
.andExpect(jsonPath("$.deployment.update", equalTo("skip")))
.andExpect(jsonPath("$.deployment.maintenanceWindow", equalTo("unavailable")));
}
@@ -668,9 +669,9 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), "1911",
action.getId()).accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(jsonPath("$.deployment.download", equalTo("forced")))
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), "1911", action.getId())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.deployment.download", equalTo("forced")))
.andExpect(jsonPath("$.deployment.update", equalTo("forced")))
.andExpect(jsonPath("$.deployment.maintenanceWindow", equalTo("available")));
}
@@ -704,8 +705,9 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
throws Exception {
final String expectedDeploymentBaseLink = String.format("/%s/controller/v1/%s/deploymentBase/%d",
tenantAware.getCurrentTenant(), controllerId, expectedActionId);
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId)
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
mvc.perform(
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$._links.deploymentBase.href", containsString(expectedDeploymentBaseLink)));
}

View File

@@ -19,7 +19,6 @@ import java.util.List;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.security.DosFilter;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
@@ -114,7 +113,7 @@ class DosFilterTest extends AbstractDDiApiIntegrationTest {
@Description("Ensures that a WRITE DoS attempt is blocked ")
void putPostFloddingAttackThatisPrevented() throws Exception {
final Long actionId = prepareDeploymentBase();
final String feedback = JsonBuilder.deploymentActionFeedback(actionId.toString(), "proceeding");
final String feedback = getJsonProceedingDeploymentActionFeedback();
MvcResult result = null;
int requests = 0;
@@ -139,7 +138,7 @@ class DosFilterTest extends AbstractDDiApiIntegrationTest {
@SuppressWarnings("squid:S2925") // No idea how to get rid of the Thread.sleep here
void acceptablePutPostLoad() throws Exception {
final Long actionId = prepareDeploymentBase();
final String feedback = JsonBuilder.deploymentActionFeedback(actionId.toString(), "proceeding");
final String feedback = getJsonProceedingDeploymentActionFeedback();
for (int x = 0; x < 5; x++) {
// sleep for one second

View File

@@ -11,19 +11,15 @@
hawkbit.controller.pollingTime=00:01:00
hawkbit.controller.pollingOverdueTime=00:01:00
hawkbit.controller.minPollingTime=00:00:30
hawkbit.controller.maintenanceWindowPollCount=3
# DDI configuration - END
# Upload configuration - START
spring.servlet.multipart.max-file-size=5MB
# Upload configuration - END
# Quota - START
hawkbit.server.security.dos.maxStatusEntriesPerAction=100
hawkbit.server.security.dos.maxAttributeEntriesPerTarget=10
# Quota - END
# Logging START - activate to see request/response details
#logging.level.org.eclipse.hawkbit.rest.util.MockMvcResultPrinter=DEBUG
# Logging END

View File

@@ -73,14 +73,14 @@ public interface MgmtDistributionSetTagRestApi {
ResponseEntity<MgmtTag> getDistributionSetTag(@PathVariable("distributionsetTagId") Long distributionsetTagId);
/**
* Handles the POST request of creating new distribution set tag. The
* request body must always be a list of tags.
* Handles the POST request of creating new distribution set tag. The request
* body must always be a list of tags.
*
* @param tags
* the distribution set tags to be created.
* @return In case all modules could successful created the ResponseEntity
* with status code 201 - Created. The Response Body are the created
* distribution set tags but without ResponseBody.
* @return In case all modules could successful created the ResponseEntity with
* status code 201 - Created. The Response Body contains the created
* distribution set tags but without details.
*/
@PostMapping(consumes = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
@@ -93,9 +93,9 @@ public interface MgmtDistributionSetTagRestApi {
* @param distributionsetTagId
* the ID of the distribution set tag
* @param restDSTagRest
* the the request body to be updated
* @return status OK if update is successful and the updated distribution
* set tag.
* the request body to be updated
* @return status OK if update is successful and the updated distribution set
* tag.
*/
@PutMapping(value = "/{distributionsetTagId}", consumes = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
@@ -144,26 +144,6 @@ public interface MgmtDistributionSetTagRestApi {
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam);
/**
* Handles the GET request of retrieving all assigned distribution sets by
* the given tag id.
*
* @param distributionsetTagId
* the ID of the distribution set tag
*
* @return the list of assigned distribution sets.
*
* @deprecated please use
* {@link #getAssignedDistributionSets(Long, int, int, String, String)}
* instead as this variant does not include paging and as result
* returns only a limited list of distributionsets
*/
@Deprecated
@GetMapping(value = MgmtRestConstants.DEPRECATED_DISTRIBUTIONSET_TAG_DISTRIBUTIONSETS_REQUEST_MAPPING, produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<List<MgmtDistributionSet>> getAssignedDistributionSets(
@PathVariable("distributionsetTagId") Long distributionsetTagId);
/**
* Handles the POST request to toggle the assignment of distribution sets by
* the given tag id.
@@ -182,28 +162,6 @@ public interface MgmtDistributionSetTagRestApi {
@PathVariable("distributionsetTagId") Long distributionsetTagId,
List<MgmtAssignedDistributionSetRequestBody> assignedDSRequestBodies);
/**
* Handles the POST request to toggle the assignment of distribution sets by
* the given tag id.
*
* @param distributionsetTagId
* the ID of the distribution set tag to retrieve
* @param assignedDSRequestBodies
* list of distribution set ids to be toggled
*
* @return the list of assigned distribution sets and unassigned
* distribution sets.
*
* @deprecated please use
* {@link MgmtDistributionSetTagRestApi#toggleTagAssignment}
*/
@Deprecated
@PostMapping(value = MgmtRestConstants.DEPRECATED_DISTRIBUTIONSET_TAG_DISTRIBUTIONSETS_REQUEST_MAPPING
+ "/toggleTagAssignment")
ResponseEntity<MgmtDistributionSetTagAssigmentResult> toggleTagAssignmentUnpaged(
@PathVariable("distributionsetTagId") Long distributionsetTagId,
List<MgmtAssignedDistributionSetRequestBody> assignedDSRequestBodies);
/**
* Handles the POST request to assign distribution sets to the given tag id.
*
@@ -221,27 +179,6 @@ public interface MgmtDistributionSetTagRestApi {
@PathVariable("distributionsetTagId") Long distributionsetTagId,
List<MgmtAssignedDistributionSetRequestBody> assignedDSRequestBodies);
/**
* Handles the POST request to assign distribution sets to the given tag id.
*
* @param distributionsetTagId
* the ID of the distribution set tag to retrieve
* @param assignedDSRequestBodies
* list of distribution sets ids to be assigned
*
* @return the list of assigned distribution set.
*
* @deprecated please use
* {@link MgmtDistributionSetTagRestApi#assignDistributionSets}
*/
@Deprecated
@PostMapping(value = MgmtRestConstants.DEPRECATED_DISTRIBUTIONSET_TAG_DISTRIBUTIONSETS_REQUEST_MAPPING, consumes = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<List<MgmtDistributionSet>> assignDistributionSetsUnpaged(
@PathVariable("distributionsetTagId") Long distributionsetTagId,
List<MgmtAssignedDistributionSetRequestBody> assignedDSRequestBodies);
/**
* Handles the DELETE request to unassign one distribution set from the
* given tag id.
@@ -257,22 +194,4 @@ public interface MgmtDistributionSetTagRestApi {
ResponseEntity<Void> unassignDistributionSet(@PathVariable("distributionsetTagId") Long distributionsetTagId,
@PathVariable("distributionsetId") Long distributionsetId);
/**
* Handles the DELETE request to unassign one distribution set from the
* given tag id.
*
* @param distributionsetTagId
* the ID of the distribution set tag
* @param distributionsetId
* the ID of the distribution set to unassign
* @return http status code
*
* @deprecated please use
* {@link MgmtDistributionSetTagRestApi#unassignDistributionSet}
*/
@Deprecated
@DeleteMapping(value = MgmtRestConstants.DEPRECATED_DISTRIBUTIONSET_TAG_DISTRIBUTIONSETS_REQUEST_MAPPING
+ "/{distributionsetId}")
ResponseEntity<Void> unassignDistributionSetUnpaged(@PathVariable("distributionsetTagId") Long distributionsetTagId,
@PathVariable("distributionsetId") Long distributionsetId);
}

View File

@@ -119,15 +119,6 @@ public final class MgmtRestConstants {
*/
public static final String TARGETTYPE_V1_DS_TYPES = "compatibledistributionsettypes";
/**
* The tag URL mapping rest resource.
*
* @deprecated {@link #TARGET_TAG_TARGETS_REQUEST_MAPPING} is preferred as
* this resource on GET supports paging
*/
@Deprecated
public static final String DEPRECATAED_TARGET_TAG_TARGETS_REQUEST_MAPPING = "/{targetTagId}/targets";
/**
* The tag URL mapping rest resource.
*/
@@ -144,15 +135,6 @@ public final class MgmtRestConstants {
*/
public static final String TARGET_FILTER_V1_REQUEST_MAPPING = BASE_V1_REQUEST_MAPPING + "/targetfilters";
/**
* The deprecated tag URL mapping rest resource.
*
* @deprecated {@link #DISTRIBUTIONSET_TAG_DISTRIBUTIONSETS_REQUEST_MAPPING}
* is preferred as this resource on GET supports paging
*/
@Deprecated
public static final String DEPRECATED_DISTRIBUTIONSET_TAG_DISTRIBUTIONSETS_REQUEST_MAPPING = "/{distributionsetTagId}/distributionsets";
/**
* The tag URL mapping rest resource.
*/

View File

@@ -112,25 +112,6 @@ public interface MgmtTargetTagRestApi {
@DeleteMapping(value = "/{targetTagId}")
ResponseEntity<Void> deleteTargetTag(@PathVariable("targetTagId") Long targetTagId);
/**
* Handles the GET request of retrieving all assigned targets by the given
* tag id.
*
* @param targetTagId
* the ID of the target tag to retrieve
*
* @return the list of assigned targets.
*
* @deprecated please use
* {@link #getAssignedTargets(Long, int, int, String, String)}
* instead as this variant does not include paging and as result
* returns only a limited list of targets
*/
@Deprecated
@GetMapping(value = MgmtRestConstants.DEPRECATAED_TARGET_TAG_TARGETS_REQUEST_MAPPING, produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<List<MgmtTarget>> getAssignedTargets(@PathVariable("targetTagId") Long targetTagId);
/**
* Handles the GET request of retrieving all assigned targets by the given
* tag id.
@@ -177,27 +158,6 @@ public interface MgmtTargetTagRestApi {
ResponseEntity<MgmtTargetTagAssigmentResult> toggleTagAssignment(@PathVariable("targetTagId") Long targetTagId,
List<MgmtAssignedTargetRequestBody> assignedTargetRequestBodies);
/**
* Handles the POST request to toggle the assignment of targets by the given
* tag id.
*
* @param targetTagId
* the ID of the target tag to retrieve
* @param assignedTargetRequestBodies
* list of controller ids to be toggled
*
* @return the list of assigned targets and unassigned targets.
* @deprecated please use {@link MgmtTargetTagRestApi#toggleTagAssignment}
*/
@Deprecated
@PostMapping(value = MgmtRestConstants.DEPRECATAED_TARGET_TAG_TARGETS_REQUEST_MAPPING
+ "/toggleTagAssignment", consumes = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtTargetTagAssigmentResult> toggleTagAssignmentUnpaged(
@PathVariable("targetTagId") Long targetTagId,
List<MgmtAssignedTargetRequestBody> assignedTargetRequestBodies);
/**
* Handles the POST request to assign targets to the given tag id.
*
@@ -214,25 +174,6 @@ public interface MgmtTargetTagRestApi {
ResponseEntity<List<MgmtTarget>> assignTargets(@PathVariable("targetTagId") Long targetTagId,
List<MgmtAssignedTargetRequestBody> assignedTargetRequestBodies);
/**
* Handles the POST request to assign targets to the given tag id.
*
* @param targetTagId
* the ID of the target tag to retrieve
* @param assignedTargetRequestBodies
* list of controller ids to be assigned
*
* @return the list of assigned targets.
*
* @deprecated please use {@link MgmtTargetTagRestApi#assignTargets}
*/
@Deprecated
@PostMapping(value = MgmtRestConstants.DEPRECATAED_TARGET_TAG_TARGETS_REQUEST_MAPPING, consumes = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<List<MgmtTarget>> assignTargetsUnpaged(@PathVariable("targetTagId") Long targetTagId,
List<MgmtAssignedTargetRequestBody> assignedTargetRequestBodies);
/**
* Handles the DELETE request to unassign one target from the given tag id.
*
@@ -245,20 +186,4 @@ public interface MgmtTargetTagRestApi {
@DeleteMapping(value = MgmtRestConstants.TARGET_TAG_TARGETS_REQUEST_MAPPING + "/{controllerId}")
ResponseEntity<Void> unassignTarget(@PathVariable("targetTagId") Long targetTagId,
@PathVariable("controllerId") String controllerId);
/**
* Handles the DELETE request to unassign one target from the given tag id.
*
* @param targetTagId
* the ID of the target tag
* @param controllerId
* the ID of the target to unassign
* @return http status code
*
* @deprecated please use {@link MgmtTargetTagRestApi#unassignTarget}
*/
@Deprecated
@DeleteMapping(value = MgmtRestConstants.DEPRECATAED_TARGET_TAG_TARGETS_REQUEST_MAPPING + "/{controllerId}")
ResponseEntity<Void> unassignTargetUnpaged(@PathVariable("targetTagId") Long targetTagId,
@PathVariable("controllerId") String controllerId);
}

View File

@@ -30,7 +30,6 @@ import org.eclipse.hawkbit.repository.model.DistributionSetTagAssignmentResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
@@ -139,15 +138,6 @@ public class MgmtDistributionSetTagResource implements MgmtDistributionSetTagRes
return ResponseEntity.ok().build();
}
@Override
public ResponseEntity<List<MgmtDistributionSet>> getAssignedDistributionSets(
@PathVariable("distributionsetTagId") final Long distributionsetTagId) {
return ResponseEntity.ok(MgmtDistributionSetMapper.toResponseDistributionSets(distributionSetManagement
.findByTag(PageRequest.of(0, MgmtRestConstants.REQUEST_PARAMETER_PAGING_MAX_LIMIT),
distributionsetTagId)
.getContent()));
}
@Override
public ResponseEntity<PagedList<MgmtDistributionSet>> getAssignedDistributionSets(
@PathVariable("distributionsetTagId") final Long distributionsetTagId,
@@ -204,7 +194,7 @@ public class MgmtDistributionSetTagResource implements MgmtDistributionSetTagRes
LOG.debug("Assign DistributionSet {} for ds tag {}", assignedDSRequestBodies.size(), distributionsetTagId);
final List<DistributionSet> assignedDs = this.distributionSetManagement
.assignTag(findDistributionSetIds(assignedDSRequestBodies), distributionsetTagId);
LOG.debug("Assignd DistributionSet {}", assignedDs.size());
LOG.debug("Assigned DistributionSet {}", assignedDs.size());
return ResponseEntity.ok(MgmtDistributionSetMapper.toResponseDistributionSets(assignedDs));
}
@@ -227,26 +217,4 @@ public class MgmtDistributionSetTagResource implements MgmtDistributionSetTagRes
return assignedDistributionSetRequestBodies.stream()
.map(MgmtAssignedDistributionSetRequestBody::getDistributionSetId).collect(Collectors.toList());
}
@Override
public ResponseEntity<MgmtDistributionSetTagAssigmentResult> toggleTagAssignmentUnpaged(
@PathVariable("distributionsetTagId") final Long distributionsetTagId,
@RequestBody final List<MgmtAssignedDistributionSetRequestBody> assignedDSRequestBodies) {
return toggleTagAssignment(distributionsetTagId, assignedDSRequestBodies);
}
@Override
public ResponseEntity<List<MgmtDistributionSet>> assignDistributionSetsUnpaged(
@PathVariable("distributionsetTagId") final Long distributionsetTagId,
@RequestBody final List<MgmtAssignedDistributionSetRequestBody> assignedDSRequestBodies) {
return assignDistributionSets(distributionsetTagId, assignedDSRequestBodies);
}
@Override
public ResponseEntity<Void> unassignDistributionSetUnpaged(
@PathVariable("distributionsetTagId") final Long distributionsetTagId,
@PathVariable("distributionsetId") final Long distributionsetId) {
return unassignDistributionSet(distributionsetTagId, distributionsetId);
}
}

View File

@@ -130,14 +130,6 @@ public class MgmtTargetTagResource implements MgmtTargetTagRestApi {
return ResponseEntity.ok().build();
}
@Override
public ResponseEntity<List<MgmtTarget>> getAssignedTargets(@PathVariable("targetTagId") final Long targetTagId) {
return ResponseEntity.ok(MgmtTargetMapper.toResponse(targetManagement
.findByTag(PageRequest.of(0, MgmtRestConstants.REQUEST_PARAMETER_PAGING_MAX_LIMIT), targetTagId)
.getContent()));
}
@Override
public ResponseEntity<PagedList<MgmtTarget>> getAssignedTargets(@PathVariable("targetTagId") final Long targetTagId,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam,
@@ -205,23 +197,4 @@ public class MgmtTargetTagResource implements MgmtTargetTagRestApi {
.collect(Collectors.toList());
}
@Override
public ResponseEntity<MgmtTargetTagAssigmentResult> toggleTagAssignmentUnpaged(
@PathVariable("targetTagId") final Long targetTagId,
@RequestBody final List<MgmtAssignedTargetRequestBody> assignedTargetRequestBodies) {
return toggleTagAssignment(targetTagId, assignedTargetRequestBodies);
}
@Override
public ResponseEntity<List<MgmtTarget>> assignTargetsUnpaged(@PathVariable("targetTagId") final Long targetTagId,
@RequestBody final List<MgmtAssignedTargetRequestBody> assignedTargetRequestBodies) {
return assignTargets(targetTagId, assignedTargetRequestBodies);
}
@Override
public ResponseEntity<Void> unassignTargetUnpaged(@PathVariable("targetTagId") final Long targetTagId,
@PathVariable("controllerId") final String controllerId) {
return unassignTarget(targetTagId, controllerId);
}
}

View File

@@ -8,13 +8,11 @@
*/
package org.eclipse.hawkbit.rest.util;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
@@ -186,96 +184,6 @@ public abstract class JsonBuilder {
}
/**
* builds a json string for the feedback for the execution "proceeding".
*
* @param id
* of the Action feedback refers to
* @return the built string
* @throws JSONException
*/
public static String deploymentActionInProgressFeedback(final String id) throws JSONException {
return deploymentActionFeedback(id, "proceeding");
}
/**
* builds a certain json string for a action feedback.
*
* @param id
* of the action the feedback refers to
* @param execution
* see ExecutionStatus
* @return the build json string
* @throws JSONException
*/
public static String deploymentActionFeedback(final String id, final String execution) throws JSONException {
return deploymentActionFeedback(id, execution, "none",
Arrays.asList(RandomStringUtils.randomAlphanumeric(1000)));
}
public static String deploymentActionFeedback(final String id, final String execution, final String message)
throws JSONException {
return deploymentActionFeedback(id, execution, "none", Arrays.asList(message));
}
public static String deploymentActionFeedback(final String id, final String execution, final String finished,
final String message) throws JSONException {
return deploymentActionFeedback(id, execution, finished, Arrays.asList(message));
}
public static String deploymentActionFeedback(final String id, final String execution, final String finished,
final Collection<String> messages) throws JSONException {
return new JSONObject().put("id", id).put("status", new JSONObject().put("execution", execution)
.put("result",
new JSONObject().put("finished", finished).put("progress",
new JSONObject().put("cnt", 2).put("of", 5)))
.put("details", new JSONArray(messages))).toString();
}
/**
* Build an invalid request body with missing result for feedback message.
*
* @param id
* id of the action
* @param execution
* the execution
* @param message
* the message
* @return a invalid request body
* @throws JSONException
*/
public static String missingResultInFeedback(final String id, final String execution, final String message)
throws JSONException {
return new JSONObject().put("id", id).put("time", "20140511T121314")
.put("status",
new JSONObject().put("execution", execution).put("details", new JSONArray().put(message)))
.toString();
}
/**
* Build an invalid request body with missing finished result for feedback
* message.
*
* @param id
* id of the action
* @param execution
* the execution
* @param message
* the message
* @return a invalid request body
* @throws JSONException
*/
public static String missingFinishedResultInFeedback(final String id, final String execution, final String message)
throws JSONException {
return new JSONObject().put("id", id).put("time", "20140511T121314")
.put("status", new JSONObject().put("execution", execution).put("result", new JSONObject())
.put("details", new JSONArray().put(message)))
.toString();
}
public static String distributionSetTypes(final List<DistributionSetType> types) throws JSONException {
final JSONArray result = new JSONArray();
@@ -512,8 +420,8 @@ public abstract class JsonBuilder {
}
});
final JSONObject json = new JSONObject().put("name", type.getName()).put("description", type.getDescription())
.put("colour", type.getColour());
final JSONObject json = new JSONObject().put("name", type.getName())
.put("description", type.getDescription()).put("colour", type.getColour());
if (dsTypes.length() != 0) {
json.put("compatibledistributionsettypes", dsTypes);
@@ -637,11 +545,6 @@ public abstract class JsonBuilder {
return json.toString();
}
public static String cancelActionFeedback(final String id, final String execution) throws JSONException {
return cancelActionFeedback(id, execution, RandomStringUtils.randomAlphanumeric(1000));
}
public static String cancelActionFeedback(final String id, final String execution, final String message)
throws JSONException {
return new JSONObject().put("id", id)

View File

@@ -36,6 +36,8 @@ final class DdiApiModelProperties {
static final String FEEDBACK_ACTION_ID = "(@deprecated) id of the action";
static final String FEEDBACK_ACTION_TIME = "timestamp of the action";
static final String CANCEL_ACTION = "action that needs to be canceled";
static final String ACTION_ID_CANCELED = "id of the action that needs to be canceled (typically identical to id field on the cancel action itself)";

View File

@@ -21,10 +21,16 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.io.ByteArrayInputStream;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.ddi.json.model.DdiActionFeedback;
import org.eclipse.hawkbit.ddi.json.model.DdiProgress;
import org.eclipse.hawkbit.ddi.json.model.DdiResult;
import org.eclipse.hawkbit.ddi.json.model.DdiStatus;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
@@ -41,6 +47,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType;
import org.springframework.restdocs.payload.JsonFieldType;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -177,11 +184,15 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio
final Long actionId = getFirstAssignedActionId(assignDistributionSet(set.getId(), target.getControllerId()));
final Action cancelAction = deploymentManagement.cancelAction(actionId);
final DdiStatus ddiStatus = new DdiStatus(DdiStatus.ExecutionStatus.CLOSED,
new DdiResult(DdiResult.FinalResult.SUCCESS, new DdiProgress(2, 5)), List.of("Some feedback"));
final DdiActionFeedback feedback = new DdiActionFeedback(Instant.now().toString(), ddiStatus);
mockMvc.perform(post(
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/" + DdiRestConstants.CANCEL_ACTION
+ "/{actionId}/feedback",
tenantAware.getCurrentTenant(), target.getControllerId(), cancelAction.getId()).content(
JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "closed", "Some feedback"))
objectMapper.writeValueAsString(feedback))
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andDo(this.document.document(
@@ -190,7 +201,10 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio
parameterWithName("actionId").description(DdiApiModelProperties.ACTION_ID_CANCELED)),
requestFields(
optionalRequestFieldWithPath("id")
.description(DdiApiModelProperties.FEEDBACK_ACTION_ID),
.description(DdiApiModelProperties.FEEDBACK_ACTION_ID)
.type(JsonFieldType.NUMBER),
optionalRequestFieldWithPath("time")
.description(DdiApiModelProperties.FEEDBACK_ACTION_TIME),
requestFieldWithPath("status").description(DdiApiModelProperties.TARGET_STATUS),
requestFieldWithPath("status.execution")
.description(DdiApiModelProperties.TARGET_EXEC_STATUS).type("enum")
@@ -381,11 +395,15 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio
final Target target = targetManagement.create(entityFactory.target().create().controllerId(CONTROLLER_ID));
final Long actionId = getFirstAssignedActionId(assignDistributionSet(set.getId(), target.getControllerId()));
mockMvc.perform(post(DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/"
+ DdiRestConstants.DEPLOYMENT_BASE_ACTION + "/{actionId}/feedback", tenantAware.getCurrentTenant(),
target.getControllerId(), actionId)
.content(
JsonBuilder.deploymentActionFeedback(actionId.toString(), "closed", "Feedback message"))
final DdiStatus ddiStatus = new DdiStatus(DdiStatus.ExecutionStatus.CLOSED,
new DdiResult(DdiResult.FinalResult.SUCCESS, new DdiProgress(2, 5)), List.of("Feedback message"));
final DdiActionFeedback feedback = new DdiActionFeedback(Instant.now().toString(), ddiStatus);
mockMvc.perform(post(
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/" + DdiRestConstants.DEPLOYMENT_BASE_ACTION
+ "/{actionId}/feedback",
tenantAware.getCurrentTenant(), target.getControllerId(), actionId)
.content(objectMapper.writeValueAsString(feedback))
.contentType(MediaType.APPLICATION_JSON_UTF8).accept(MediaTypes.HAL_JSON_VALUE))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andDo(this.document.document(
@@ -395,7 +413,10 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio
requestFields(
optionalRequestFieldWithPath("id")
.description(DdiApiModelProperties.FEEDBACK_ACTION_ID),
.description(DdiApiModelProperties.FEEDBACK_ACTION_ID)
.type(JsonFieldType.NUMBER),
optionalRequestFieldWithPath("time")
.description(DdiApiModelProperties.FEEDBACK_ACTION_TIME),
requestFieldWithPath("status").description(DdiApiModelProperties.TARGET_STATUS),
requestFieldWithPath("status.execution")
.description(DdiApiModelProperties.TARGET_EXEC_STATUS).type("enum")

57
pom.xml
View File

@@ -16,7 +16,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.7.RELEASE</version>
<version>2.3.12.RELEASE</version>
</parent>
<groupId>org.eclipse.hawkbit</groupId>
@@ -128,12 +128,11 @@
<properties>
<java.version>1.8</java.version>
<!-- Developer preview: java.version = 11 -->
<java.version>11</java.version>
<spring.boot.version>2.3.7.RELEASE</spring.boot.version>
<spring-framework.version>5.2.20.RELEASE</spring-framework.version>
<spring.cloud.version>Hoxton.SR7</spring.cloud.version>
<spring.boot.version>2.3.12.RELEASE</spring.boot.version>
<spring-framework.version>5.2.22.RELEASE</spring-framework.version>
<spring.cloud.version>Hoxton.SR12</spring.cloud.version>
<spring.plugin.core.version>2.0.0.RELEASE</spring.plugin.core.version>
<snapshotDependencyAllowed>true</snapshotDependencyAllowed>
@@ -155,15 +154,27 @@
<vaadin.gwt-user.version>2.8.2</vaadin.gwt-user.version>
<!-- Vaadin versions - END -->
<!-- ************************ -->
<!-- Maven Plugins -->
<!-- ************************ -->
<maven.processor.plugin.version>3.3.3</maven.processor.plugin.version>
<maven.scm.plugin.version>1.11.1</maven.scm.plugin.version>
<maven.site.plugin.version>3.9.0</maven.site.plugin.version>
<maven.site.plugin.version>3.9.1</maven.site.plugin.version>
<maven.surefire.plugin.version>3.0.0-M7</maven.surefire.plugin.version>
<maven.failsafe.plugin.version>3.0.0-M7</maven.failsafe.plugin.version>
<maven.enforcer.plugin.version>3.1.0</maven.enforcer.plugin.version>
<!-- ************************ -->
<!-- Maven Surefire settings -->
<!-- ************************ -->
<surefire.forkcount>1</surefire.forkcount>
<!-- Misc libraries versions - START -->
<!-- Misc libraries versions - START -->
<cron-utils.version>9.1.6</cron-utils.version>
<jsoup.version>1.15.3</jsoup.version>
<allure.version>2.13.6</allure.version>
<eclipselink.version>2.7.9</eclipselink.version>
<eclipselink.version>2.7.11</eclipselink.version>
<eclipselink.maven.plugin.version>2.7.9.1</eclipselink.maven.plugin.version>
<jaxb-api.version>2.3.0</jaxb-api.version>
<gwtmockito.version>1.1.8</gwtmockito.version>
<guava.version>30.1.1-jre</guava.version>
<javax.el-api.version>2.2.4</javax.el-api.version>
@@ -198,13 +209,7 @@
${project.basedir}/../hawkbit-test-report/target/jacoco-aggregate/jacoco.xml,
${project.basedir}/../../hawkbit-test-report/target/jacoco-aggregate/jacoco.xml
</sonar.coverage.jacoco.xmlReportPaths>
<!-- Sonar - END -->
<!-- ************************ -->
<!-- Maven Surefire settings -->
<!-- ************************ -->
<surefire.forkcount>1</surefire.forkcount>
</properties>
<build>
@@ -225,6 +230,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>${maven.enforcer.plugin.version}</version>
<executions>
<execution>
<!-- Make sure that only non-snapshot versions are used
@@ -404,6 +410,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven.surefire.plugin.version}</version>
<configuration>
<systemPropertyVariables>
<!-- Set output directory according to allure-framework recommendation -->
@@ -415,9 +422,7 @@
<useSystemClassLoader>false</useSystemClassLoader>
<reuseForks>true</reuseForks>
<forkCount>${surefire.forkcount}</forkCount>
<argLine>
${jacoco.agent.ut.arg}
</argLine>
<argLine>${jacoco.agent.ut.arg}</argLine>
<properties>
<property>
<name>listener</name>
@@ -437,6 +442,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${maven.failsafe.plugin.version}</version>
<configuration>
<reuseForks>false</reuseForks>
<forkCount>${surefire.forkcount}</forkCount>
@@ -477,6 +483,11 @@
<artifactId>maven-processor-plugin</artifactId>
<version>${maven.processor.plugin.version}</version>
</plugin>
<plugin>
<groupId>com.ethlo.persistence.tools</groupId>
<artifactId>eclipselink-maven-plugin</artifactId>
<version>${eclipselink.maven.plugin.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
@@ -653,9 +664,15 @@
<exclusion>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</exclusion>
</exclusion>
</exclusions>
</dependency>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>${jaxb-api.version}</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework.cloud</groupId>