Code format hawkbit-ddi-resource (#1941)
Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
# Eclipse.IoT hawkBit - DDI Resource
|
||||
# Eclipse.IoT hawkBit - DDI Resource
|
||||
|
||||
This is the server-side implementation of the hawkBit DDI API and the hawkBit DDI Download API that is used by devices for communicating with the HawkBit Update Server through HTTP.
|
||||
This is the server-side implementation of the hawkBit DDI API and the hawkBit DDI Download API that is used by devices
|
||||
for communicating with the HawkBit Update Server through HTTP.
|
||||
|
||||
# Compile
|
||||
|
||||
|
||||
@@ -10,104 +10,105 @@
|
||||
|
||||
-->
|
||||
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-rest-parent</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<artifactId>hawkbit-ddi-resource</artifactId>
|
||||
<name>hawkBit :: REST :: DDI Resources</name>
|
||||
<project 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"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-rest-parent</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<artifactId>hawkbit-ddi-resource</artifactId>
|
||||
<name>hawkBit :: REST :: DDI Resources</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-ddi-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-rest-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.plugin</groupId>
|
||||
<artifactId>spring-plugin-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-ddi-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-rest-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.plugin</groupId>
|
||||
<artifactId>spring-plugin-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-test</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-jpa</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-rest-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>tests</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.el</groupId>
|
||||
<artifactId>javax.el-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-http-security</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-json</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-aspects</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.qameta.allure</groupId>
|
||||
<artifactId>allure-junit5</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-test</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-jpa</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-rest-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>tests</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.el</groupId>
|
||||
<artifactId>javax.el-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-http-security</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-json</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-aspects</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.qameta.allure</groupId>
|
||||
<artifactId>allure-junit5</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -45,72 +45,12 @@ import org.springframework.util.CollectionUtils;
|
||||
* Utility class for the DDI API.
|
||||
*/
|
||||
public final class DataConversionHelper {
|
||||
|
||||
// utility class, private constructor.
|
||||
private DataConversionHelper() {
|
||||
|
||||
}
|
||||
|
||||
static List<DdiChunk> createChunks(final Target target, final Action uAction,
|
||||
final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement,
|
||||
final HttpRequest request, final ControllerManagement controllerManagement) {
|
||||
|
||||
final Map<Long, List<SoftwareModuleMetadata>> metadata = controllerManagement
|
||||
.findTargetVisibleMetaDataBySoftwareModuleId(uAction.getDistributionSet().getModules().stream()
|
||||
.map(SoftwareModule::getId).collect(Collectors.toList()));
|
||||
|
||||
return new ResponseList<>(uAction.getDistributionSet().getModules().stream()
|
||||
.map(module -> new DdiChunk(mapChunkLegacyKeys(module.getType().getKey()), module.getVersion(),
|
||||
module.getName(), module.isEncrypted() ? Boolean.TRUE : null,
|
||||
createArtifacts(target, module, artifactUrlHandler, systemManagement, request),
|
||||
mapMetadata(metadata.get(module.getId()))))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
}
|
||||
|
||||
private static List<DdiMetadata> mapMetadata(final List<SoftwareModuleMetadata> metadata) {
|
||||
return CollectionUtils.isEmpty(metadata) ? null
|
||||
: metadata.stream().map(md -> new DdiMetadata(md.getKey(), md.getValue())).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static String mapChunkLegacyKeys(final String key) {
|
||||
if ("application".equals(key)) {
|
||||
return "bApp";
|
||||
}
|
||||
if ("runtime".equals(key)) {
|
||||
return "jvm";
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
static List<DdiArtifact> createArtifacts(final Target target, final SoftwareModule module,
|
||||
final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement,
|
||||
final HttpRequest request) {
|
||||
|
||||
return new ResponseList<>(module.getArtifacts().stream()
|
||||
.map(artifact -> createArtifact(target, artifactUrlHandler, artifact, systemManagement, request))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private static DdiArtifact createArtifact(final Target target, final ArtifactUrlHandler artifactUrlHandler,
|
||||
final Artifact artifact, final SystemManagement systemManagement, final HttpRequest request) {
|
||||
final DdiArtifact file = new DdiArtifact();
|
||||
file.setHashes(new DdiArtifactHash(artifact.getSha1Hash(), artifact.getMd5Hash(), artifact.getSha256Hash()));
|
||||
file.setFilename(artifact.getFilename());
|
||||
file.setSize(artifact.getSize());
|
||||
|
||||
artifactUrlHandler
|
||||
.getUrls(new URLPlaceholder(systemManagement.getTenantMetadata().getTenant(),
|
||||
systemManagement.getTenantMetadata().getId(), target.getControllerId(), target.getId(),
|
||||
new SoftwareData(artifact.getSoftwareModule().getId(), artifact.getFilename(), artifact.getId(),
|
||||
artifact.getSha1Hash())),
|
||||
ApiType.DDI, request.getURI())
|
||||
.forEach(entry -> file.add(Link.of(entry.getRef()).withRel(entry.getRel()).expand()));
|
||||
|
||||
return file;
|
||||
|
||||
}
|
||||
|
||||
public static DdiConfirmationBase createConfirmationBase(final Target target, final Action activeAction,
|
||||
final DdiAutoConfirmationState autoConfirmationState, final TenantAware tenantAware) {
|
||||
final String controllerId = target.getControllerId();
|
||||
@@ -145,9 +85,9 @@ public final class DataConversionHelper {
|
||||
if (activeAction != null) {
|
||||
if (activeAction.isWaitingConfirmation()) {
|
||||
result.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder
|
||||
.methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
|
||||
.getConfirmationBaseAction(tenantAware.getCurrentTenant(), target.getControllerId(),
|
||||
activeAction.getId(), calculateEtag(activeAction), null))
|
||||
.methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
|
||||
.getConfirmationBaseAction(tenantAware.getCurrentTenant(), target.getControllerId(),
|
||||
activeAction.getId(), calculateEtag(activeAction), null))
|
||||
.withRel(DdiRestConstants.CONFIRMATION_BASE).expand());
|
||||
|
||||
} else if (activeAction.isCancelingOrCanceled()) {
|
||||
@@ -163,9 +103,9 @@ public final class DataConversionHelper {
|
||||
// change the payload of the
|
||||
// response because of eTags.
|
||||
result.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder
|
||||
.methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
|
||||
.getControllerDeploymentBaseAction(tenantAware.getCurrentTenant(), target.getControllerId(),
|
||||
activeAction.getId(), calculateEtag(activeAction), null))
|
||||
.methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
|
||||
.getControllerDeploymentBaseAction(tenantAware.getCurrentTenant(), target.getControllerId(),
|
||||
activeAction.getId(), calculateEtag(activeAction), null))
|
||||
.withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION).expand());
|
||||
}
|
||||
}
|
||||
@@ -189,13 +129,73 @@ public final class DataConversionHelper {
|
||||
return result;
|
||||
}
|
||||
|
||||
static List<DdiChunk> createChunks(final Target target, final Action uAction,
|
||||
final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement,
|
||||
final HttpRequest request, final ControllerManagement controllerManagement) {
|
||||
|
||||
final Map<Long, List<SoftwareModuleMetadata>> metadata = controllerManagement
|
||||
.findTargetVisibleMetaDataBySoftwareModuleId(uAction.getDistributionSet().getModules().stream()
|
||||
.map(SoftwareModule::getId).collect(Collectors.toList()));
|
||||
|
||||
return new ResponseList<>(uAction.getDistributionSet().getModules().stream()
|
||||
.map(module -> new DdiChunk(mapChunkLegacyKeys(module.getType().getKey()), module.getVersion(),
|
||||
module.getName(), module.isEncrypted() ? Boolean.TRUE : null,
|
||||
createArtifacts(target, module, artifactUrlHandler, systemManagement, request),
|
||||
mapMetadata(metadata.get(module.getId()))))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
}
|
||||
|
||||
static List<DdiArtifact> createArtifacts(final Target target, final SoftwareModule module,
|
||||
final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement,
|
||||
final HttpRequest request) {
|
||||
|
||||
return new ResponseList<>(module.getArtifacts().stream()
|
||||
.map(artifact -> createArtifact(target, artifactUrlHandler, artifact, systemManagement, request))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private static List<DdiMetadata> mapMetadata(final List<SoftwareModuleMetadata> metadata) {
|
||||
return CollectionUtils.isEmpty(metadata) ? null
|
||||
: metadata.stream().map(md -> new DdiMetadata(md.getKey(), md.getValue())).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static String mapChunkLegacyKeys(final String key) {
|
||||
if ("application".equals(key)) {
|
||||
return "bApp";
|
||||
}
|
||||
if ("runtime".equals(key)) {
|
||||
return "jvm";
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
private static DdiArtifact createArtifact(final Target target, final ArtifactUrlHandler artifactUrlHandler,
|
||||
final Artifact artifact, final SystemManagement systemManagement, final HttpRequest request) {
|
||||
final DdiArtifact file = new DdiArtifact();
|
||||
file.setHashes(new DdiArtifactHash(artifact.getSha1Hash(), artifact.getMd5Hash(), artifact.getSha256Hash()));
|
||||
file.setFilename(artifact.getFilename());
|
||||
file.setSize(artifact.getSize());
|
||||
|
||||
artifactUrlHandler
|
||||
.getUrls(new URLPlaceholder(systemManagement.getTenantMetadata().getTenant(),
|
||||
systemManagement.getTenantMetadata().getId(), target.getControllerId(), target.getId(),
|
||||
new SoftwareData(artifact.getSoftwareModule().getId(), artifact.getFilename(), artifact.getId(),
|
||||
artifact.getSha1Hash())),
|
||||
ApiType.DDI, request.getURI())
|
||||
.forEach(entry -> file.add(Link.of(entry.getRef()).withRel(entry.getRel()).expand()));
|
||||
|
||||
return file;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates an etag for the given {@link Action} based on the entities
|
||||
* hashcode and the {@link Action#isHitAutoForceTime(long)} to reflect a
|
||||
* force switch.
|
||||
*
|
||||
* @param action
|
||||
* to calculate the etag for
|
||||
*
|
||||
* @param action to calculate the etag for
|
||||
* @return the etag
|
||||
*/
|
||||
private static int calculateEtag(final Action action) {
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.springframework.stereotype.Controller;
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@Import({RestConfiguration.class, OpenApiConfiguration.class})
|
||||
@Import({ RestConfiguration.class, OpenApiConfiguration.class })
|
||||
public class DdiApiConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ import java.util.Optional;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
@@ -28,6 +28,7 @@ import org.eclipse.hawkbit.ddi.json.model.DdiActionFeedback;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiActionHistory;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiActivateAutoConfirmation;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiArtifact;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiAssignedVersion;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiAutoConfirmationState;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiCancel;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiCancelActionToStop;
|
||||
@@ -41,7 +42,6 @@ import org.eclipse.hawkbit.ddi.json.model.DdiDeployment;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiDeployment.DdiMaintenanceWindowStatus;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiDeployment.HandlingType;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiDeploymentBase;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiAssignedVersion;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiResult.FinalResult;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiUpdateMode;
|
||||
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
|
||||
@@ -99,7 +99,7 @@ import org.springframework.web.context.WebApplicationContext;
|
||||
@RestController
|
||||
@Scope(value = WebApplicationContext.SCOPE_REQUEST)
|
||||
public class DdiRootController implements DdiRootControllerRestApi {
|
||||
|
||||
|
||||
private static final String GIVEN_ACTION_IS_NOT_ASSIGNED_TO_GIVEN_TARGET = "given action ({}) is not assigned to given target ({}).";
|
||||
private static final String FALLBACK_REMARK = "Initiated using the Device Direct Integration API without providing a remark.";
|
||||
|
||||
@@ -208,36 +208,14 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
RequestResponseContextHolder.getHttpServletRequest(),
|
||||
(length, shippedSinceLastEvent,
|
||||
total) -> eventPublisher.publishEvent(new DownloadProgressEvent(
|
||||
tenantAware.getCurrentTenant(), statusId, shippedSinceLastEvent,
|
||||
serviceMatcher != null ? serviceMatcher.getBusId() : bus.getId())));
|
||||
tenantAware.getCurrentTenant(), statusId, shippedSinceLastEvent,
|
||||
serviceMatcher != null ? serviceMatcher.getBusId() : bus.getId())));
|
||||
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private ActionStatus checkAndLogDownload(final HttpServletRequest request, final Target target, final Long module) {
|
||||
final Action action = controllerManagement
|
||||
.getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(), module)
|
||||
.orElseThrow(() -> new SoftwareModuleNotAssignedToTargetException(module, target.getControllerId()));
|
||||
final String range = request.getHeader("Range");
|
||||
|
||||
final String message;
|
||||
if (range != null) {
|
||||
message = RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target downloads range " + range + " of: "
|
||||
+ request.getRequestURI();
|
||||
} else {
|
||||
message = RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target downloads " + request.getRequestURI();
|
||||
}
|
||||
|
||||
return controllerManagement.addInformationalActionStatus(
|
||||
entityFactory.actionStatus().create(action.getId()).status(Status.DOWNLOAD).message(message));
|
||||
}
|
||||
|
||||
private static boolean checkModule(final String fileName, final SoftwareModule module) {
|
||||
return module == null || module.getArtifactByFilename(fileName).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
// Exception squid:S3655 - Optional access is checked in checkModule
|
||||
// subroutine
|
||||
@@ -301,30 +279,6 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
private static HandlingType calculateDownloadType(final Action action) {
|
||||
if (action.isDownloadOnly() || action.isForcedOrTimeForced()) {
|
||||
return HandlingType.FORCED;
|
||||
}
|
||||
return HandlingType.ATTEMPT;
|
||||
}
|
||||
|
||||
private static DdiMaintenanceWindowStatus calculateMaintenanceWindow(final Action action) {
|
||||
if (action.hasMaintenanceSchedule()) {
|
||||
return action.isMaintenanceWindowAvailable() ? DdiMaintenanceWindowStatus.AVAILABLE
|
||||
: DdiMaintenanceWindowStatus.UNAVAILABLE;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static HandlingType calculateUpdateType(final Action action, final HandlingType downloadType) {
|
||||
if (action.isDownloadOnly()) {
|
||||
return HandlingType.SKIP;
|
||||
} else if (action.hasMaintenanceSchedule()) {
|
||||
return action.isMaintenanceWindowAvailable() ? downloadType : HandlingType.SKIP;
|
||||
}
|
||||
return downloadType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> postDeploymentBaseActionFeedback(@Valid @RequestBody final DdiActionFeedback feedback,
|
||||
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
|
||||
@@ -349,91 +303,6 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
private ActionStatusCreate generateUpdateStatus(final DdiActionFeedback feedback, final String controllerId,
|
||||
final Long actionId) {
|
||||
|
||||
final ActionStatusCreate actionStatusCreate = entityFactory.actionStatus().create(actionId);
|
||||
|
||||
final List<String> messages = new ArrayList<>();
|
||||
|
||||
if (!CollectionUtils.isEmpty(feedback.getStatus().getDetails())) {
|
||||
messages.addAll(feedback.getStatus().getDetails());
|
||||
}
|
||||
|
||||
final Integer code = feedback.getStatus().getCode();
|
||||
if (code != null) {
|
||||
actionStatusCreate.code(code);
|
||||
messages.add("Device reported status code: " + code);
|
||||
}
|
||||
|
||||
final Status status;
|
||||
switch (feedback.getStatus().getExecution()) {
|
||||
case CANCELED:
|
||||
log.debug("Controller confirmed cancel (actionId: {}, controllerId: {}) as we got {} report.", actionId,
|
||||
controllerId, feedback.getStatus().getExecution());
|
||||
status = Status.CANCELED;
|
||||
addMessageIfEmpty("Target confirmed cancellation.", messages);
|
||||
break;
|
||||
case REJECTED:
|
||||
log.info("Controller reported internal error (actionId: {}, controllerId: {}) as we got {} report.",
|
||||
actionId, controllerId, feedback.getStatus().getExecution());
|
||||
status = Status.WARNING;
|
||||
addMessageIfEmpty("Target REJECTED update", messages);
|
||||
break;
|
||||
case CLOSED:
|
||||
status = handleClosedCase(feedback, controllerId, actionId, messages);
|
||||
break;
|
||||
case DOWNLOAD:
|
||||
log.debug("Controller confirmed status of download (actionId: {}, controllerId: {}) as we got {} report.",
|
||||
actionId, controllerId, feedback.getStatus().getExecution());
|
||||
status = Status.DOWNLOAD;
|
||||
addMessageIfEmpty("Target confirmed download start", messages);
|
||||
break;
|
||||
case DOWNLOADED:
|
||||
log.debug("Controller confirmed download (actionId: {}, controllerId: {}) as we got {} report.", actionId,
|
||||
controllerId, feedback.getStatus().getExecution());
|
||||
status = Status.DOWNLOADED;
|
||||
addMessageIfEmpty("Target confirmed download finished", messages);
|
||||
break;
|
||||
default:
|
||||
status = handleDefaultCase(feedback, controllerId, actionId, messages);
|
||||
break;
|
||||
}
|
||||
|
||||
return actionStatusCreate.status(status).messages(messages);
|
||||
}
|
||||
|
||||
private static void addMessageIfEmpty(final String text, final List<String> messages) {
|
||||
if (messages != null && messages.isEmpty()) {
|
||||
messages.add(RepositoryConstants.SERVER_MESSAGE_PREFIX + text + ".");
|
||||
}
|
||||
}
|
||||
|
||||
private Status handleDefaultCase(final DdiActionFeedback feedback, final String controllerId, final Long actionId,
|
||||
final List<String> messages) {
|
||||
final Status status;
|
||||
log.debug("Controller reported intermediate status (actionId: {}, controllerId: {}) as we got {} report.",
|
||||
actionId, controllerId, feedback.getStatus().getExecution());
|
||||
status = Status.RUNNING;
|
||||
addMessageIfEmpty("Target reported " + feedback.getStatus().getExecution(), messages);
|
||||
return status;
|
||||
}
|
||||
|
||||
private Status handleClosedCase(final DdiActionFeedback feedback, final String controllerId, final Long actionId,
|
||||
final List<String> messages) {
|
||||
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) {
|
||||
status = Status.ERROR;
|
||||
addMessageIfEmpty("Target reported CLOSED with ERROR!", messages);
|
||||
} else {
|
||||
status = Status.FINISHED;
|
||||
addMessageIfEmpty("Target reported CLOSED with OK!", messages);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> putConfigData(@Valid @RequestBody final DdiConfigData configData,
|
||||
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId) {
|
||||
@@ -501,6 +370,149 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<DdiConfirmationBase> getConfirmationBase(final String tenant, final String controllerId) {
|
||||
log.debug("getConfirmationBase is called [controllerId={}].", controllerId);
|
||||
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, IpUtil
|
||||
.getClientIpFromRequest(RequestResponseContextHolder.getHttpServletRequest(), securityProperties));
|
||||
final Action activeAction = controllerManagement.findActiveActionWithHighestWeight(controllerId).orElse(null);
|
||||
|
||||
final DdiAutoConfirmationState autoConfirmationState = getAutoConfirmationState(controllerId);
|
||||
|
||||
final DdiConfirmationBase confirmationBase = DataConversionHelper.createConfirmationBase(target, activeAction,
|
||||
autoConfirmationState, tenantAware);
|
||||
return new ResponseEntity<>(confirmationBase, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<DdiConfirmationBaseAction> getConfirmationBaseAction(
|
||||
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("actionId") final Long actionId,
|
||||
@RequestParam(value = "c", required = false, defaultValue = "-1") final int resource,
|
||||
@RequestParam(value = "actionHistory", defaultValue = DdiRestConstants.NO_ACTION_HISTORY) final Integer actionHistoryMessageCount) {
|
||||
log.debug("getConfirmationBaseAction({},{})", controllerId, resource);
|
||||
|
||||
final Target target = findTarget(controllerId);
|
||||
final Action action = findActionForTarget(actionId, target);
|
||||
|
||||
checkAndCancelExpiredAction(action);
|
||||
|
||||
if (!action.isCancelingOrCanceled() && action.isWaitingConfirmation()) {
|
||||
|
||||
final DdiConfirmationBaseAction base = generateDdiConfirmationBase(target, action,
|
||||
actionHistoryMessageCount);
|
||||
|
||||
log.debug("Found an active UpdateAction for target {}. Returning confirmation: {}", controllerId, base);
|
||||
|
||||
return new ResponseEntity<>(base, HttpStatus.OK);
|
||||
}
|
||||
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> postConfirmationActionFeedback(
|
||||
@Valid @RequestBody final DdiConfirmationFeedback feedback, @PathVariable("tenant") final String tenant,
|
||||
@PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("actionId") @NotNull final Long actionId) {
|
||||
log.debug("provideConfirmationActionFeedback with feedback [controllerId={}, actionId={}]: {}", controllerId,
|
||||
actionId, feedback);
|
||||
|
||||
final Target target = findTarget(controllerId);
|
||||
final Action action = findActionForTarget(actionId, target);
|
||||
|
||||
try {
|
||||
|
||||
switch (feedback.getConfirmation()) {
|
||||
case CONFIRMED:
|
||||
log.info("Controller confirmed the action (actionId: {}, controllerId: {}) as we got {} report.",
|
||||
actionId, controllerId, feedback.getConfirmation());
|
||||
confirmationManagement.confirmAction(actionId, feedback.getCode(), feedback.getDetails());
|
||||
break;
|
||||
case DENIED:
|
||||
default:
|
||||
log.debug("Controller denied the action (actionId: {}, controllerId: {}) as we got {} report.",
|
||||
actionId, controllerId, feedback.getConfirmation());
|
||||
confirmationManagement.denyAction(actionId, feedback.getCode(), feedback.getDetails());
|
||||
break;
|
||||
}
|
||||
} catch (final InvalidConfirmationFeedbackException e) {
|
||||
if (e.getReason() == InvalidConfirmationFeedbackException.Reason.ACTION_CLOSED) {
|
||||
log.warn("Updating action {} with confirmation {} not possible since action not active anymore.",
|
||||
action.getId(), feedback.getConfirmation());
|
||||
return new ResponseEntity<>(HttpStatus.GONE);
|
||||
} else if (e.getReason() == InvalidConfirmationFeedbackException.Reason.NOT_AWAITING_CONFIRMATION) {
|
||||
log.debug("Action is not waiting for confirmation, deny request.");
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
}
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> activateAutoConfirmation(final String tenant, final String controllerId,
|
||||
final DdiActivateAutoConfirmation body) {
|
||||
final String initiator = body == null ? null : body.getInitiator();
|
||||
final String remark = body == null ? FALLBACK_REMARK : body.getRemark();
|
||||
log.debug("Activate auto-confirmation request for device '{}' with payload: [initiator='{}' | remark='{}'",
|
||||
controllerId, initiator, remark);
|
||||
confirmationManagement.activateAutoConfirmation(controllerId, initiator, remark);
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> deactivateAutoConfirmation(final String tenant, final String controllerId) {
|
||||
log.debug("Deactivate auto-confirmation request for device ‘{}‘", controllerId);
|
||||
confirmationManagement.deactivateAutoConfirmation(controllerId);
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> setAsssignedOfflineVersion(@Valid @RequestBody DdiAssignedVersion ddiAssignedVersion,
|
||||
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId) {
|
||||
boolean updated = controllerManagement.updateOfflineAssignedVersion(controllerId,
|
||||
ddiAssignedVersion.getName(), ddiAssignedVersion.getVersion());
|
||||
if (updated) {
|
||||
return new ResponseEntity<>(HttpStatus.CREATED);
|
||||
}
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
|
||||
private static boolean checkModule(final String fileName, final SoftwareModule module) {
|
||||
return module == null || module.getArtifactByFilename(fileName).isEmpty();
|
||||
}
|
||||
|
||||
private static HandlingType calculateDownloadType(final Action action) {
|
||||
if (action.isDownloadOnly() || action.isForcedOrTimeForced()) {
|
||||
return HandlingType.FORCED;
|
||||
}
|
||||
return HandlingType.ATTEMPT;
|
||||
}
|
||||
|
||||
private static DdiMaintenanceWindowStatus calculateMaintenanceWindow(final Action action) {
|
||||
if (action.hasMaintenanceSchedule()) {
|
||||
return action.isMaintenanceWindowAvailable() ? DdiMaintenanceWindowStatus.AVAILABLE
|
||||
: DdiMaintenanceWindowStatus.UNAVAILABLE;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static HandlingType calculateUpdateType(final Action action, final HandlingType downloadType) {
|
||||
if (action.isDownloadOnly()) {
|
||||
return HandlingType.SKIP;
|
||||
} else if (action.hasMaintenanceSchedule()) {
|
||||
return action.isMaintenanceWindowAvailable() ? downloadType : HandlingType.SKIP;
|
||||
}
|
||||
return downloadType;
|
||||
}
|
||||
|
||||
private static void addMessageIfEmpty(final String text, final List<String> messages) {
|
||||
if (messages != null && messages.isEmpty()) {
|
||||
messages.add(RepositoryConstants.SERVER_MESSAGE_PREFIX + text + ".");
|
||||
}
|
||||
}
|
||||
|
||||
private static ActionStatusCreate generateActionCancelStatus(final DdiActionFeedback feedback, final Target target,
|
||||
final Long actionId, final EntityFactory entityFactory) {
|
||||
|
||||
@@ -509,21 +521,21 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
final List<String> messages = new ArrayList<>();
|
||||
final Status status;
|
||||
switch (feedback.getStatus().getExecution()) {
|
||||
case CANCELED:
|
||||
status = handleCaseCancelCanceled(feedback, target, actionId, messages);
|
||||
break;
|
||||
case REJECTED:
|
||||
log.info("Target rejected the cancellation request (actionId: {}, controllerId: {}).", actionId,
|
||||
target.getControllerId());
|
||||
status = Status.CANCEL_REJECTED;
|
||||
messages.add(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target rejected the cancellation request.");
|
||||
break;
|
||||
case CLOSED:
|
||||
status = handleCancelClosedCase(feedback, messages);
|
||||
break;
|
||||
default:
|
||||
status = Status.RUNNING;
|
||||
break;
|
||||
case CANCELED:
|
||||
status = handleCaseCancelCanceled(feedback, target, actionId, messages);
|
||||
break;
|
||||
case REJECTED:
|
||||
log.info("Target rejected the cancellation request (actionId: {}, controllerId: {}).", actionId,
|
||||
target.getControllerId());
|
||||
status = Status.CANCEL_REJECTED;
|
||||
messages.add(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target rejected the cancellation request.");
|
||||
break;
|
||||
case CLOSED:
|
||||
status = handleCancelClosedCase(feedback, messages);
|
||||
break;
|
||||
default:
|
||||
status = Status.RUNNING;
|
||||
break;
|
||||
}
|
||||
|
||||
if (feedback.getStatus().getDetails() != null) {
|
||||
@@ -564,6 +576,114 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the update mode from the given update message.
|
||||
*/
|
||||
private static UpdateMode getUpdateMode(final DdiConfigData configData) {
|
||||
final DdiUpdateMode mode = configData.getMode();
|
||||
if (mode != null) {
|
||||
return UpdateMode.valueOf(mode.name());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ActionStatus checkAndLogDownload(final HttpServletRequest request, final Target target, final Long module) {
|
||||
final Action action = controllerManagement
|
||||
.getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(), module)
|
||||
.orElseThrow(() -> new SoftwareModuleNotAssignedToTargetException(module, target.getControllerId()));
|
||||
final String range = request.getHeader("Range");
|
||||
|
||||
final String message;
|
||||
if (range != null) {
|
||||
message = RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target downloads range " + range + " of: "
|
||||
+ request.getRequestURI();
|
||||
} else {
|
||||
message = RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target downloads " + request.getRequestURI();
|
||||
}
|
||||
|
||||
return controllerManagement.addInformationalActionStatus(
|
||||
entityFactory.actionStatus().create(action.getId()).status(Status.DOWNLOAD).message(message));
|
||||
}
|
||||
|
||||
private ActionStatusCreate generateUpdateStatus(final DdiActionFeedback feedback, final String controllerId,
|
||||
final Long actionId) {
|
||||
|
||||
final ActionStatusCreate actionStatusCreate = entityFactory.actionStatus().create(actionId);
|
||||
|
||||
final List<String> messages = new ArrayList<>();
|
||||
|
||||
if (!CollectionUtils.isEmpty(feedback.getStatus().getDetails())) {
|
||||
messages.addAll(feedback.getStatus().getDetails());
|
||||
}
|
||||
|
||||
final Integer code = feedback.getStatus().getCode();
|
||||
if (code != null) {
|
||||
actionStatusCreate.code(code);
|
||||
messages.add("Device reported status code: " + code);
|
||||
}
|
||||
|
||||
final Status status;
|
||||
switch (feedback.getStatus().getExecution()) {
|
||||
case CANCELED:
|
||||
log.debug("Controller confirmed cancel (actionId: {}, controllerId: {}) as we got {} report.", actionId,
|
||||
controllerId, feedback.getStatus().getExecution());
|
||||
status = Status.CANCELED;
|
||||
addMessageIfEmpty("Target confirmed cancellation.", messages);
|
||||
break;
|
||||
case REJECTED:
|
||||
log.info("Controller reported internal error (actionId: {}, controllerId: {}) as we got {} report.",
|
||||
actionId, controllerId, feedback.getStatus().getExecution());
|
||||
status = Status.WARNING;
|
||||
addMessageIfEmpty("Target REJECTED update", messages);
|
||||
break;
|
||||
case CLOSED:
|
||||
status = handleClosedCase(feedback, controllerId, actionId, messages);
|
||||
break;
|
||||
case DOWNLOAD:
|
||||
log.debug("Controller confirmed status of download (actionId: {}, controllerId: {}) as we got {} report.",
|
||||
actionId, controllerId, feedback.getStatus().getExecution());
|
||||
status = Status.DOWNLOAD;
|
||||
addMessageIfEmpty("Target confirmed download start", messages);
|
||||
break;
|
||||
case DOWNLOADED:
|
||||
log.debug("Controller confirmed download (actionId: {}, controllerId: {}) as we got {} report.", actionId,
|
||||
controllerId, feedback.getStatus().getExecution());
|
||||
status = Status.DOWNLOADED;
|
||||
addMessageIfEmpty("Target confirmed download finished", messages);
|
||||
break;
|
||||
default:
|
||||
status = handleDefaultCase(feedback, controllerId, actionId, messages);
|
||||
break;
|
||||
}
|
||||
|
||||
return actionStatusCreate.status(status).messages(messages);
|
||||
}
|
||||
|
||||
private Status handleDefaultCase(final DdiActionFeedback feedback, final String controllerId, final Long actionId,
|
||||
final List<String> messages) {
|
||||
final Status status;
|
||||
log.debug("Controller reported intermediate status (actionId: {}, controllerId: {}) as we got {} report.",
|
||||
actionId, controllerId, feedback.getStatus().getExecution());
|
||||
status = Status.RUNNING;
|
||||
addMessageIfEmpty("Target reported " + feedback.getStatus().getExecution(), messages);
|
||||
return status;
|
||||
}
|
||||
|
||||
private Status handleClosedCase(final DdiActionFeedback feedback, final String controllerId, final Long actionId,
|
||||
final List<String> messages) {
|
||||
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) {
|
||||
status = Status.ERROR;
|
||||
addMessageIfEmpty("Target reported CLOSED with ERROR!", messages);
|
||||
} else {
|
||||
status = Status.FINISHED;
|
||||
addMessageIfEmpty("Target reported CLOSED with OK!", messages);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
private Target findTarget(final String controllerId) {
|
||||
return controllerManagement.getByControllerId(controllerId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
|
||||
@@ -588,8 +708,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
* If the action has a maintenance schedule defined but is no longer valid,
|
||||
* cancel the action.
|
||||
*
|
||||
* @param action
|
||||
* is the {@link Action} to check.
|
||||
* @param action is the {@link Action} to check.
|
||||
*/
|
||||
private void checkAndCancelExpiredAction(final Action action) {
|
||||
if (action != null && action.hasMaintenanceSchedule() && action.isMaintenanceScheduleLapsed()) {
|
||||
@@ -601,44 +720,6 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the update mode from the given update message.
|
||||
*/
|
||||
private static UpdateMode getUpdateMode(final DdiConfigData configData) {
|
||||
final DdiUpdateMode mode = configData.getMode();
|
||||
if (mode != null) {
|
||||
return UpdateMode.valueOf(mode.name());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ResponseEntity<DdiConfirmationBaseAction> getConfirmationBaseAction(
|
||||
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("actionId") final Long actionId,
|
||||
@RequestParam(value = "c", required = false, defaultValue = "-1") final int resource,
|
||||
@RequestParam(value = "actionHistory", defaultValue = DdiRestConstants.NO_ACTION_HISTORY) final Integer actionHistoryMessageCount) {
|
||||
log.debug("getConfirmationBaseAction({},{})", controllerId, resource);
|
||||
|
||||
final Target target = findTarget(controllerId);
|
||||
final Action action = findActionForTarget(actionId, target);
|
||||
|
||||
checkAndCancelExpiredAction(action);
|
||||
|
||||
if (!action.isCancelingOrCanceled() && action.isWaitingConfirmation()) {
|
||||
|
||||
final DdiConfirmationBaseAction base = generateDdiConfirmationBase(target, action,
|
||||
actionHistoryMessageCount);
|
||||
|
||||
log.debug("Found an active UpdateAction for target {}. Returning confirmation: {}", controllerId, base);
|
||||
|
||||
return new ResponseEntity<>(base, HttpStatus.OK);
|
||||
}
|
||||
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
private DdiDeploymentBase generateDdiDeploymentBase(final Target target, final Action action,
|
||||
final Integer actionHistoryMessageCount) {
|
||||
final DdiActionHistory actionHistory = generateDdiActionHistory(action, actionHistoryMessageCount).orElse(null);
|
||||
@@ -672,60 +753,6 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
: Optional.of(new DdiActionHistory(action.getStatus().name(), actionHistoryMsgs));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> postConfirmationActionFeedback(
|
||||
@Valid @RequestBody final DdiConfirmationFeedback feedback, @PathVariable("tenant") final String tenant,
|
||||
@PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("actionId") @NotNull final Long actionId) {
|
||||
log.debug("provideConfirmationActionFeedback with feedback [controllerId={}, actionId={}]: {}", controllerId,
|
||||
actionId, feedback);
|
||||
|
||||
final Target target = findTarget(controllerId);
|
||||
final Action action = findActionForTarget(actionId, target);
|
||||
|
||||
try {
|
||||
|
||||
switch (feedback.getConfirmation()) {
|
||||
case CONFIRMED:
|
||||
log.info("Controller confirmed the action (actionId: {}, controllerId: {}) as we got {} report.",
|
||||
actionId, controllerId, feedback.getConfirmation());
|
||||
confirmationManagement.confirmAction(actionId, feedback.getCode(), feedback.getDetails());
|
||||
break;
|
||||
case DENIED:
|
||||
default:
|
||||
log.debug("Controller denied the action (actionId: {}, controllerId: {}) as we got {} report.",
|
||||
actionId, controllerId, feedback.getConfirmation());
|
||||
confirmationManagement.denyAction(actionId, feedback.getCode(), feedback.getDetails());
|
||||
break;
|
||||
}
|
||||
} catch (final InvalidConfirmationFeedbackException e) {
|
||||
if (e.getReason() == InvalidConfirmationFeedbackException.Reason.ACTION_CLOSED) {
|
||||
log.warn("Updating action {} with confirmation {} not possible since action not active anymore.",
|
||||
action.getId(), feedback.getConfirmation());
|
||||
return new ResponseEntity<>(HttpStatus.GONE);
|
||||
} else if (e.getReason() == InvalidConfirmationFeedbackException.Reason.NOT_AWAITING_CONFIRMATION) {
|
||||
log.debug("Action is not waiting for confirmation, deny request.");
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
}
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<DdiConfirmationBase> getConfirmationBase(final String tenant, final String controllerId) {
|
||||
log.debug("getConfirmationBase is called [controllerId={}].", controllerId);
|
||||
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, IpUtil
|
||||
.getClientIpFromRequest(RequestResponseContextHolder.getHttpServletRequest(), securityProperties));
|
||||
final Action activeAction = controllerManagement.findActiveActionWithHighestWeight(controllerId).orElse(null);
|
||||
|
||||
final DdiAutoConfirmationState autoConfirmationState = getAutoConfirmationState(controllerId);
|
||||
|
||||
final DdiConfirmationBase confirmationBase = DataConversionHelper.createConfirmationBase(target, activeAction,
|
||||
autoConfirmationState, tenantAware);
|
||||
return new ResponseEntity<>(confirmationBase, HttpStatus.OK);
|
||||
}
|
||||
|
||||
private DdiAutoConfirmationState getAutoConfirmationState(final String controllerId) {
|
||||
return confirmationManagement.getStatus(controllerId).map(status -> {
|
||||
final DdiAutoConfirmationState state = DdiAutoConfirmationState.active(status.getActivatedAt());
|
||||
@@ -739,33 +766,4 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
return DdiAutoConfirmationState.disabled();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> activateAutoConfirmation(final String tenant, final String controllerId,
|
||||
final DdiActivateAutoConfirmation body) {
|
||||
final String initiator = body == null ? null : body.getInitiator();
|
||||
final String remark = body == null ? FALLBACK_REMARK : body.getRemark();
|
||||
log.debug("Activate auto-confirmation request for device '{}' with payload: [initiator='{}' | remark='{}'",
|
||||
controllerId, initiator, remark);
|
||||
confirmationManagement.activateAutoConfirmation(controllerId, initiator, remark);
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> deactivateAutoConfirmation(final String tenant, final String controllerId) {
|
||||
log.debug("Deactivate auto-confirmation request for device ‘{}‘", controllerId);
|
||||
confirmationManagement.deactivateAutoConfirmation(controllerId);
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> setAsssignedOfflineVersion(@Valid @RequestBody DdiAssignedVersion ddiAssignedVersion,
|
||||
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId){
|
||||
boolean updated = controllerManagement.updateOfflineAssignedVersion(controllerId,
|
||||
ddiAssignedVersion.getName(), ddiAssignedVersion.getVersion());
|
||||
if (updated) {
|
||||
return new ResponseEntity<>(HttpStatus.CREATED);
|
||||
}
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,10 +24,18 @@ import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiActionFeedback;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiConfirmationFeedback;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiAssignedVersion;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiConfirmationFeedback;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiProgress;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiResult;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiStatus;
|
||||
@@ -49,15 +57,6 @@ import org.springframework.test.web.servlet.ResultActions;
|
||||
import org.springframework.test.web.servlet.ResultMatcher;
|
||||
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;
|
||||
|
||||
@ContextConfiguration(classes = { DdiApiConfiguration.class, RestConfiguration.class,
|
||||
RepositoryApplicationConfiguration.class, TestConfiguration.class })
|
||||
@Import(TestChannelBinderConfiguration.class)
|
||||
@@ -65,19 +64,17 @@ import com.fasterxml.jackson.dataformat.cbor.CBORParser;
|
||||
public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrationTest {
|
||||
|
||||
public static final int HTTP_PORT = 8080;
|
||||
protected static final String HTTP_LOCALHOST = String.format("http://localhost:%s/",HTTP_PORT);
|
||||
protected static final String HTTP_LOCALHOST = String.format("http://localhost:%s/", HTTP_PORT);
|
||||
protected static final String CONTROLLER_BASE = "/{tenant}/controller/v1/{controllerId}";
|
||||
|
||||
protected static final String SOFTWARE_MODULE_ARTIFACTS = CONTROLLER_BASE
|
||||
+ "/softwaremodules/{softwareModuleId}/artifacts";
|
||||
protected static final String DEPLOYMENT_BASE = CONTROLLER_BASE + "/deploymentBase/{actionId}";
|
||||
protected static final String DEPLOYMENT_FEEDBACK = DEPLOYMENT_BASE + "/feedback";
|
||||
protected static final String CANCEL_ACTION = CONTROLLER_BASE + "/cancelAction/{actionId}";
|
||||
protected static final String CANCEL_FEEDBACK = CANCEL_ACTION + "/feedback";
|
||||
protected static final String INSTALLED_BASE = CONTROLLER_BASE + "/installedBase/{actionId}";
|
||||
protected static final String INSTALLED_BASE_ROOT = CONTROLLER_BASE + "/installedBase";
|
||||
|
||||
protected static final String DEPLOYMENT_FEEDBACK = DEPLOYMENT_BASE + "/feedback";
|
||||
protected static final String CANCEL_FEEDBACK = CANCEL_ACTION + "/feedback";
|
||||
|
||||
protected static final String CONFIRMATION_BASE = CONTROLLER_BASE + "/confirmationBase";
|
||||
protected static final String ACTIVATE_AUTO_CONFIRM = CONFIRMATION_BASE + "/activateAutoConfirm";
|
||||
protected static final String DEACTIVATE_AUTO_CONFIRM = CONFIRMATION_BASE + "/deactivateAutoConfirm";
|
||||
@@ -91,11 +88,9 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
|
||||
/**
|
||||
* Convert JSON to a CBOR equivalent.
|
||||
*
|
||||
* @param json
|
||||
* JSON object to convert
|
||||
* @param json JSON object to convert
|
||||
* @return Equivalent CBOR data
|
||||
* @throws IOException
|
||||
* Invalid JSON input
|
||||
* @throws IOException Invalid JSON input
|
||||
*/
|
||||
protected static byte[] jsonToCbor(final String json) throws IOException {
|
||||
final JsonFactory jsonFactory = new JsonFactory();
|
||||
@@ -113,11 +108,9 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
|
||||
/**
|
||||
* Convert CBOR to JSON equivalent.
|
||||
*
|
||||
* @param input
|
||||
* CBOR data to convert
|
||||
* @param input CBOR data to convert
|
||||
* @return Equivalent JSON string
|
||||
* @throws IOException
|
||||
* Invalid CBOR input
|
||||
* @throws IOException Invalid CBOR input
|
||||
*/
|
||||
protected static String cborToJson(final byte[] input) throws IOException {
|
||||
final CBORFactory cborFactory = new CBORFactory();
|
||||
@@ -132,6 +125,10 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
|
||||
return stringWriter.toString();
|
||||
}
|
||||
|
||||
protected static ObjectMapper getMapper() {
|
||||
return objectMapper;
|
||||
}
|
||||
|
||||
protected ResultActions postDeploymentFeedback(final String controllerId, final Long actionId, final String content,
|
||||
final ResultMatcher statusMatcher) throws Exception {
|
||||
return postDeploymentFeedback(MediaType.APPLICATION_JSON_UTF8, controllerId, actionId, content.getBytes(),
|
||||
@@ -139,7 +136,7 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
|
||||
}
|
||||
|
||||
protected ResultActions putInstalledBase(final String controllerId, final String content,
|
||||
final ResultMatcher statusMatcher) throws Exception {
|
||||
final ResultMatcher statusMatcher) throws Exception {
|
||||
return mvc.perform(put(INSTALLED_BASE_ROOT, tenantAware.getCurrentTenant(), controllerId)
|
||||
.content(content.getBytes()).contentType(MediaType.APPLICATION_JSON_UTF8))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(statusMatcher);
|
||||
@@ -170,7 +167,7 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
|
||||
protected ResultActions performGet(final String url, final MediaType mediaType, final ResultMatcher statusMatcher,
|
||||
final String... values) throws Exception {
|
||||
return mvc.perform(MockMvcRequestBuilders.get(url, values).accept(mediaType)
|
||||
.with(new RequestOnHawkbitDefaultPortPostProcessor()))
|
||||
.with(new RequestOnHawkbitDefaultPortPostProcessor()))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(statusMatcher)
|
||||
.andExpect(content().contentTypeCompatibleWith(mediaType));
|
||||
}
|
||||
@@ -200,61 +197,6 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
|
||||
getDownloadAndUploadType(actionType), getDownloadAndUploadType(actionType));
|
||||
}
|
||||
|
||||
private ResultActions verifyBasePayload(final String prefix, 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))))
|
||||
.andExpect(jsonPath(prefix + ".download", equalTo(downloadType)))
|
||||
.andExpect(jsonPath(prefix + ".update", equalTo(updateType)))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='jvm')].name",
|
||||
contains(ds.findFirstModuleByType(runtimeType).get().getName())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='jvm')].version",
|
||||
contains(ds.findFirstModuleByType(runtimeType).get().getVersion())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].name",
|
||||
contains(ds.findFirstModuleByType(osType).get().getName())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].version",
|
||||
contains(ds.findFirstModuleByType(osType).get().getVersion())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].size", contains(ARTIFACT_SIZE)))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].filename",
|
||||
contains(artifact.getFilename())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].hashes.md5",
|
||||
contains(artifact.getMd5Hash())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].hashes.sha1",
|
||||
contains(artifact.getSha1Hash())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].hashes.sha256",
|
||||
contains(artifact.getSha256Hash())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0]._links.download-http.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
|
||||
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifact.getFilename()
|
||||
+ "/download")))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0]._links.md5sum-http.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
|
||||
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifact.getFilename()
|
||||
+ "/download.MD5SUM")))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].size", contains(ARTIFACT_SIZE)))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].filename",
|
||||
contains(artifactSignature.getFilename())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].hashes.md5",
|
||||
contains(artifactSignature.getMd5Hash())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].hashes.sha1",
|
||||
contains(artifactSignature.getSha1Hash())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].hashes.sha256",
|
||||
contains(artifactSignature.getSha256Hash())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1]._links.download-http.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
|
||||
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifactSignature.getFilename()
|
||||
+ "/download")))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1]._links.md5sum-http.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
|
||||
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifactSignature.getFilename()
|
||||
+ "/download.MD5SUM")))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='bApp')].version",
|
||||
contains(ds.findFirstModuleByType(appType).get().getVersion())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='bApp')].metadata").doesNotExist())
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='bApp')].name")
|
||||
.value(ds.findFirstModuleByType(appType).get().getName()));
|
||||
}
|
||||
|
||||
protected String installedBaseLink(final String controllerId, final String actionId) {
|
||||
return HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
|
||||
+ "/installedBase/" + actionId;
|
||||
@@ -265,13 +207,6 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
|
||||
+ "/deploymentBase/" + actionId;
|
||||
}
|
||||
|
||||
private static String getDownloadAndUploadType(final Action.ActionType actionType) {
|
||||
if (Action.ActionType.FORCED.equals(actionType)) {
|
||||
return "forced";
|
||||
}
|
||||
return "attempt";
|
||||
}
|
||||
|
||||
protected String getJsonRejectedCancelActionFeedback() throws JsonProcessingException {
|
||||
return getJsonActionFeedback(DdiStatus.ExecutionStatus.REJECTED, DdiResult.FinalResult.SUCCESS,
|
||||
Collections.singletonList("rejected"));
|
||||
@@ -367,7 +302,7 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
|
||||
}
|
||||
|
||||
protected String getJsonConfirmationFeedback(final DdiConfirmationFeedback.Confirmation confirmation,
|
||||
final Integer code, final List<String> messages) throws JsonProcessingException {
|
||||
final Integer code, final List<String> messages) throws JsonProcessingException {
|
||||
return objectMapper.writeValueAsString(new DdiConfirmationFeedback(confirmation, code, messages));
|
||||
}
|
||||
|
||||
@@ -375,10 +310,6 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
|
||||
return objectMapper.writeValueAsString(new DdiAssignedVersion(name, version));
|
||||
}
|
||||
|
||||
protected static ObjectMapper getMapper(){
|
||||
return objectMapper;
|
||||
}
|
||||
|
||||
protected ResultActions getAndVerifyConfirmationBasePayload(final String controllerId, final MediaType mediaType,
|
||||
final DistributionSet ds, final Artifact artifact, final Artifact artifactSignature, final Long actionId,
|
||||
final Long osModuleId, final String downloadType, final String updateType) throws Exception {
|
||||
@@ -391,4 +322,66 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
|
||||
static void implicitLock(final DistributionSet set) {
|
||||
((JpaDistributionSet) set).setOptLockRevision(set.getOptLockRevision() + 1);
|
||||
}
|
||||
|
||||
private static String getDownloadAndUploadType(final Action.ActionType actionType) {
|
||||
if (Action.ActionType.FORCED.equals(actionType)) {
|
||||
return "forced";
|
||||
}
|
||||
return "attempt";
|
||||
}
|
||||
|
||||
private ResultActions verifyBasePayload(final String prefix, 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))))
|
||||
.andExpect(jsonPath(prefix + ".download", equalTo(downloadType)))
|
||||
.andExpect(jsonPath(prefix + ".update", equalTo(updateType)))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='jvm')].name",
|
||||
contains(ds.findFirstModuleByType(runtimeType).get().getName())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='jvm')].version",
|
||||
contains(ds.findFirstModuleByType(runtimeType).get().getVersion())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].name",
|
||||
contains(ds.findFirstModuleByType(osType).get().getName())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].version",
|
||||
contains(ds.findFirstModuleByType(osType).get().getVersion())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].size", contains(ARTIFACT_SIZE)))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].filename",
|
||||
contains(artifact.getFilename())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].hashes.md5",
|
||||
contains(artifact.getMd5Hash())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].hashes.sha1",
|
||||
contains(artifact.getSha1Hash())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].hashes.sha256",
|
||||
contains(artifact.getSha256Hash())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0]._links.download-http.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
|
||||
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifact.getFilename()
|
||||
+ "/download")))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0]._links.md5sum-http.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
|
||||
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifact.getFilename()
|
||||
+ "/download.MD5SUM")))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].size", contains(ARTIFACT_SIZE)))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].filename",
|
||||
contains(artifactSignature.getFilename())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].hashes.md5",
|
||||
contains(artifactSignature.getMd5Hash())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].hashes.sha1",
|
||||
contains(artifactSignature.getSha1Hash())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].hashes.sha256",
|
||||
contains(artifactSignature.getSha256Hash())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1]._links.download-http.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
|
||||
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifactSignature.getFilename()
|
||||
+ "/download")))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1]._links.md5sum-http.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
|
||||
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifactSignature.getFilename()
|
||||
+ "/download.MD5SUM")))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='bApp')].version",
|
||||
contains(ds.findFirstModuleByType(appType).get().getVersion())))
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='bApp')].metadata").doesNotExist())
|
||||
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='bApp')].name")
|
||||
.value(ds.findFirstModuleByType(appType).get().getName()));
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,9 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.eclipse.hawkbit.ddi.rest.resource.DdiArtifactDownloadTest.DownloadTestConfiguration;
|
||||
import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
|
||||
@@ -48,10 +51,6 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
|
||||
/**
|
||||
* Test artifact downloads from the controller.
|
||||
*/
|
||||
@@ -102,55 +101,55 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// test now consistent data to test allowed methods
|
||||
mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
|
||||
.header(HttpHeaders.IF_MATCH, artifact.getSha1Hash()))
|
||||
.andExpect(status().isOk());
|
||||
mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
// test failed If-match
|
||||
mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
|
||||
.header(HttpHeaders.IF_MATCH, "fsjkhgjfdhg"))
|
||||
.andExpect(status().isPreconditionFailed());
|
||||
|
||||
// test invalid range
|
||||
mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
|
||||
.header("Range", "bytes=1-10,hdsfjksdh"))
|
||||
.andExpect(header().string("Content-Range", "bytes */" + 5 * 1024))
|
||||
.andExpect(status().isRequestedRangeNotSatisfiable());
|
||||
|
||||
mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
|
||||
.header("Range", "bytes=100-10"))
|
||||
.andExpect(header().string("Content-Range", "bytes */" + 5 * 1024))
|
||||
.andExpect(status().isRequestedRangeNotSatisfiable());
|
||||
|
||||
// not allowed methods
|
||||
mvc.perform(put("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
|
||||
mvc.perform(delete("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
|
||||
mvc.perform(post("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
|
||||
mvc.perform(put("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
|
||||
mvc.perform(delete("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
|
||||
mvc.perform(post("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
}
|
||||
|
||||
@@ -182,8 +181,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
||||
// now assign and download successful
|
||||
assignDistributionSet(ds, targets);
|
||||
final MvcResult result = mvc.perform(get(
|
||||
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
.andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
|
||||
.andExpect(header().string("Accept-Ranges", "bytes"))
|
||||
.andExpect(header().string("Last-Modified", dateFormat.format(new Date(artifact.getCreatedAt()))))
|
||||
@@ -191,7 +190,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
||||
.andReturn();
|
||||
|
||||
assertTrue(
|
||||
Arrays.equals(result.getResponse().getContentAsByteArray(), random), "The same file that was uploaded is expected when downloaded");
|
||||
Arrays.equals(result.getResponse().getContentAsByteArray(), random),
|
||||
"The same file that was uploaded is expected when downloaded");
|
||||
|
||||
// download complete
|
||||
assertThat(downLoadProgress).isEqualTo(10);
|
||||
@@ -217,8 +217,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// download
|
||||
final MvcResult result = mvc.perform(get(
|
||||
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}.MD5SUM",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}.MD5SUM",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||
.andExpect(status().isOk()).andExpect(header().string("Content-Disposition",
|
||||
"attachment;filename=" + artifact.getFilename() + ".MD5SUM"))
|
||||
.andReturn();
|
||||
@@ -258,8 +258,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
||||
final String rangeString = "" + i * range + "-" + ((i + 1) * range - 1);
|
||||
|
||||
final MvcResult result = mvc.perform(get(
|
||||
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1").header("Range",
|
||||
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1").header("Range",
|
||||
"bytes=" + rangeString))
|
||||
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
|
||||
@@ -276,8 +276,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// return last 1000 Bytes
|
||||
MvcResult result = mvc.perform(
|
||||
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
|
||||
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
|
||||
.header("Range", "bytes=-1000"))
|
||||
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
|
||||
@@ -293,8 +293,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// skip first 1000 Bytes and return the rest
|
||||
result = mvc.perform(
|
||||
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
|
||||
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
|
||||
.header("Range", "bytes=1000-"))
|
||||
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
|
||||
@@ -310,8 +310,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// Start download from file end fails
|
||||
mvc.perform(
|
||||
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
|
||||
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
|
||||
.header("Range", "bytes=" + random.length + "-"))
|
||||
.andExpect(status().isRequestedRangeNotSatisfiable())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
|
||||
@@ -322,8 +322,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// multipart download - first 20 bytes in 2 parts
|
||||
result = mvc.perform(
|
||||
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
|
||||
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
|
||||
.header("Range", "bytes=0-9,10-19"))
|
||||
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
|
||||
.andExpect(content().contentType("multipart/byteranges; boundary=THIS_STRING_SEPARATES_MULTIPART"))
|
||||
|
||||
@@ -25,6 +25,9 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiResult;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiStatus;
|
||||
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
|
||||
@@ -41,10 +44,6 @@ import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.integration.json.JsonPathUtils;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
|
||||
/**
|
||||
* Test cancel action from the controller.
|
||||
*/
|
||||
@@ -52,6 +51,9 @@ import io.qameta.allure.Story;
|
||||
@Story("Cancel Action Resource")
|
||||
class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
ActionStatusRepository actionStatusRepository;
|
||||
|
||||
@Test
|
||||
@Description("Tests that the cancel action resource can be used with CBOR.")
|
||||
void cancelActionCbor() throws Exception {
|
||||
@@ -65,7 +67,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
final byte[] result = mvc
|
||||
.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
|
||||
+ cancelAction.getId(), tenantAware.getCurrentTenant())
|
||||
.accept(DdiRestConstants.MEDIA_TYPE_CBOR))
|
||||
.accept(DdiRestConstants.MEDIA_TYPE_CBOR))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(DdiRestConstants.MEDIA_TYPE_CBOR)).andReturn().getResponse()
|
||||
.getContentAsByteArray();
|
||||
@@ -76,7 +78,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// and submit feedback as CBOR
|
||||
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
.content(jsonToCbor(getJsonProceedingCancelActionFeedback()))
|
||||
.contentType(DdiRestConstants.MEDIA_TYPE_CBOR).accept(DdiRestConstants.MEDIA_TYPE_CBOR))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
@@ -97,7 +99,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// controller rejects cancellation
|
||||
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
.content(getJsonRejectedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
@@ -106,8 +108,8 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// get update action anyway
|
||||
mvc.perform(
|
||||
get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/" + actionId,
|
||||
tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
|
||||
get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/" + actionId,
|
||||
tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(jsonPath("$.id", equalTo(String.valueOf(actionId))))
|
||||
@@ -122,9 +124,9 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// and finish it
|
||||
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/"
|
||||
+ actionId + "/feedback", tenantAware.getCurrentTenant()).content(
|
||||
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.NONE,
|
||||
Collections.singletonList("message")))
|
||||
+ 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());
|
||||
|
||||
@@ -135,7 +137,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
.isEqualTo(ds);
|
||||
assertThat(
|
||||
targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getInstallationDate())
|
||||
.isGreaterThanOrEqualTo(current);
|
||||
.isGreaterThanOrEqualTo(current);
|
||||
|
||||
}
|
||||
|
||||
@@ -150,7 +152,7 @@ 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())
|
||||
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",
|
||||
@@ -179,7 +181,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
final long timeBefore2ndPoll = System.currentTimeMillis();
|
||||
mvc.perform(get("/{tenant}/controller/v1/{controller}", tenantAware.getCurrentTenant(),
|
||||
TestdataFactory.DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
TestdataFactory.DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaTypes.HAL_JSON))
|
||||
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
|
||||
.andExpect(jsonPath("$._links.cancelAction.href",
|
||||
@@ -190,7 +192,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
.isBetween(timeBefore2ndPoll, timeAfter2ndPoll);
|
||||
|
||||
mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
|
||||
+ cancelAction.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
|
||||
+ cancelAction.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(jsonPath("$.id", equalTo(String.valueOf(cancelAction.getId()))))
|
||||
@@ -200,7 +202,7 @@ 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)
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON)
|
||||
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
@@ -220,41 +222,31 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// not allowed methods
|
||||
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/1",
|
||||
tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
|
||||
tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
|
||||
mvc.perform(put("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/1",
|
||||
tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
|
||||
tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
|
||||
mvc.perform(delete("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/1",
|
||||
tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
|
||||
tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
|
||||
// non existing target
|
||||
mvc.perform(get("/{tenant}/controller/v1/34534543/cancelAction/1", tenantAware.getCurrentTenant())
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
createCancelAction("34534543");
|
||||
|
||||
// wrong media type
|
||||
mvc.perform(get("/{tenant}/controller/v1/34534543/cancelAction/1", tenantAware.getCurrentTenant())
|
||||
.accept(MediaType.APPLICATION_ATOM_XML)).andDo(MockMvcResultPrinter.print())
|
||||
.accept(MediaType.APPLICATION_ATOM_XML)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotAcceptable());
|
||||
|
||||
}
|
||||
|
||||
private Action createCancelAction(final String targetid) {
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet(targetid);
|
||||
final Target savedTarget = testdataFactory.createTarget(targetid);
|
||||
final List<Target> toAssign = new ArrayList<>();
|
||||
toAssign.add(savedTarget);
|
||||
final Long actionId = getFirstAssignedActionId(assignDistributionSet(ds, toAssign));
|
||||
|
||||
return deploymentManagement.cancelAction(actionId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests the feedback channel of the cancel operation.")
|
||||
void rootRsCancelActionFeedback() throws Exception {
|
||||
@@ -272,7 +264,7 @@ 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())
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
.content(getJsonProceedingCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
@@ -281,7 +273,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
assertThat(countActionStatusAll()).isEqualTo(3);
|
||||
|
||||
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
.content(getJsonResumedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
@@ -289,7 +281,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
assertThat(countActionStatusAll()).isEqualTo(4);
|
||||
|
||||
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
.content(getJsonScheduledCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
@@ -299,7 +291,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
// cancellation canceled -> should remove the action from active
|
||||
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
|
||||
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
.content(getJsonCanceledCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
@@ -312,7 +304,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
// error
|
||||
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
|
||||
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
.content(getJsonRejectedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
@@ -321,7 +313,7 @@ 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())
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
@@ -359,7 +351,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(3);
|
||||
assertThat(deploymentManagement.countActionsByTarget(savedTarget.getControllerId())).isEqualTo(3);
|
||||
mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
|
||||
+ cancelAction.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
|
||||
+ cancelAction.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||
.andExpect(jsonPath("$.id", equalTo(String.valueOf(cancelAction.getId()))))
|
||||
@@ -367,7 +359,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
assertThat(countActionStatusAll()).isEqualTo(6);
|
||||
|
||||
mvc.perform(get("/{tenant}/controller/v1/{controllerId}", tenantAware.getCurrentTenant(),
|
||||
TestdataFactory.DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
TestdataFactory.DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaTypes.HAL_JSON))
|
||||
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
|
||||
.andExpect(jsonPath("$._links.cancelAction.href",
|
||||
@@ -376,7 +368,7 @@ 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())
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
@@ -386,7 +378,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(2);
|
||||
assertThat(deploymentManagement.countActionsByTarget(savedTarget.getControllerId())).isEqualTo(3);
|
||||
mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
|
||||
+ cancelAction2.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
|
||||
+ cancelAction2.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(jsonPath("$.id", equalTo(String.valueOf(cancelAction2.getId()))))
|
||||
@@ -394,7 +386,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
assertThat(countActionStatusAll()).isEqualTo(8);
|
||||
|
||||
mvc.perform(get("/{tenant}/controller/v1/{controller}", tenantAware.getCurrentTenant(),
|
||||
TestdataFactory.DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
TestdataFactory.DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaTypes.HAL_JSON))
|
||||
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
|
||||
.andExpect(jsonPath("$._links.cancelAction.href",
|
||||
@@ -403,7 +395,7 @@ 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())
|
||||
+ cancelAction2.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
@@ -412,8 +404,8 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
assertThat(deploymentManagement.getAssignedDistributionSet(TestdataFactory.DEFAULT_CONTROLLER_ID).get())
|
||||
.isEqualTo(ds3);
|
||||
mvc.perform(
|
||||
get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/" + actionId3,
|
||||
tenantAware.getCurrentTenant()))
|
||||
get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/" + actionId3,
|
||||
tenantAware.getCurrentTenant()))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
assertThat(countActionStatusAll()).isEqualTo(10);
|
||||
|
||||
@@ -429,7 +421,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
.isEqualTo(ds3);
|
||||
|
||||
mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
|
||||
+ cancelAction3.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
|
||||
+ cancelAction3.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(jsonPath("$.id", equalTo(String.valueOf(cancelAction3.getId()))))
|
||||
@@ -438,7 +430,7 @@ 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())
|
||||
+ cancelAction3.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
@@ -466,13 +458,13 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
// only 97 action status left
|
||||
for (int i = 0; i < 98; i++) {
|
||||
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).content(feedback)
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).content(feedback)
|
||||
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).content(feedback)
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).content(feedback)
|
||||
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
@@ -485,22 +477,22 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// not allowed methods
|
||||
mvc.perform(put("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
.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/"
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()))
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
|
||||
|
||||
mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()))
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
|
||||
|
||||
// bad content type
|
||||
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_ATOM_XML)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isUnsupportedMediaType());
|
||||
@@ -508,33 +500,41 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
// 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(invalidFeedback)
|
||||
+ 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(getJsonClosedCancelActionFeedback())
|
||||
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(invalidFeedback)
|
||||
+ 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())
|
||||
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
|
||||
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Autowired
|
||||
ActionStatusRepository actionStatusRepository;
|
||||
private Action createCancelAction(final String targetid) {
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet(targetid);
|
||||
final Target savedTarget = testdataFactory.createTarget(targetid);
|
||||
final List<Target> toAssign = new ArrayList<>();
|
||||
toAssign.add(savedTarget);
|
||||
final Long actionId = getFirstAssignedActionId(assignDistributionSet(ds, toAssign));
|
||||
|
||||
return deploymentManagement.cancelAction(actionId);
|
||||
}
|
||||
|
||||
private long countActionStatusAll() {
|
||||
return actionStatusRepository.count();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,10 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Step;
|
||||
import io.qameta.allure.Story;
|
||||
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
|
||||
import org.eclipse.hawkbit.exception.SpServerError;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
@@ -40,10 +44,6 @@ import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Step;
|
||||
import io.qameta.allure.Story;
|
||||
/**
|
||||
* Test config data from the controller.
|
||||
*/
|
||||
@@ -66,8 +66,8 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
||||
attributes.put(ATTRIBUTE_KEY_VALID, ATTRIBUTE_VALUE_VALID);
|
||||
|
||||
mvc.perform(put(TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
|
||||
.content(jsonToCbor(JsonBuilder.configData(attributes).toString()))
|
||||
.contentType(DdiRestConstants.MEDIA_TYPE_CBOR)).andDo(MockMvcResultPrinter.print())
|
||||
.content(jsonToCbor(JsonBuilder.configData(attributes).toString()))
|
||||
.contentType(DdiRestConstants.MEDIA_TYPE_CBOR)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk());
|
||||
assertThat(targetManagement.getControllerAttributes(TARGET1_ID)).isEqualTo(attributes);
|
||||
}
|
||||
@@ -81,15 +81,15 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
final long current = System.currentTimeMillis();
|
||||
mvc.perform(
|
||||
get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()).accept(MediaTypes.HAL_JSON))
|
||||
get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()).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.configData.href", equalTo(
|
||||
"http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/4712/configData")));
|
||||
Thread.sleep(1); // is required: otherwise processing the next line is
|
||||
// often too fast and
|
||||
// the following assert will fail
|
||||
// often too fast and
|
||||
// the following assert will fail
|
||||
assertThat(targetManagement.getByControllerID("4712").orElseThrow(NoSuchElementException::new)
|
||||
.getLastTargetQuery())
|
||||
.isLessThanOrEqualTo(System.currentTimeMillis());
|
||||
@@ -107,7 +107,7 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
||||
assertThat(updateControllerAttributes.isRequestControllerAttributes()).isFalse();
|
||||
|
||||
mvc.perform(
|
||||
get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()).accept(MediaTypes.HAL_JSON))
|
||||
get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()).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")))
|
||||
@@ -125,14 +125,14 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
||||
attributes.put(ATTRIBUTE_KEY_VALID, ATTRIBUTE_VALUE_VALID);
|
||||
|
||||
mvc.perform(put(TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
assertThat(targetManagement.getControllerAttributes(TARGET1_ID)).isEqualTo(attributes);
|
||||
|
||||
// update
|
||||
attributes.put("sdsds", "123412");
|
||||
mvc.perform(put(TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
assertThat(targetManagement.getControllerAttributes(TARGET1_ID)).isEqualTo(attributes);
|
||||
}
|
||||
@@ -149,13 +149,13 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
||||
attributes.put("dsafsdf" + i, "sdsds" + i);
|
||||
}
|
||||
mvc.perform(put(TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
attributes = new HashMap<>();
|
||||
attributes.put("on too many", "sdsds");
|
||||
mvc.perform(put(TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isForbidden())
|
||||
.andExpect(jsonPath("$.exceptionClass", equalTo(AssignmentQuotaExceededException.class.getName())))
|
||||
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
||||
@@ -183,17 +183,17 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
||||
final Map<String, String> attributes = new HashMap<>();
|
||||
attributes.put("dsafsdf", "sdsds");
|
||||
mvc.perform(put("/{tenant}/controller/v1/4712/configData", tenantAware.getCurrentTenant())
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaTypes.HAL_JSON))
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaTypes.HAL_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isUnsupportedMediaType());
|
||||
|
||||
// non existing target
|
||||
mvc.perform(put("/{tenant}/controller/v1/456456/configData", tenantAware.getCurrentTenant())
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
|
||||
|
||||
// bad body
|
||||
mvc.perform(put("/{tenant}/controller/v1/4712/configData", tenantAware.getCurrentTenant())
|
||||
.content("{\"id\": \"51659181\"}").contentType(MediaType.APPLICATION_JSON))
|
||||
.content("{\"id\": \"51659181\"}").contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@@ -206,26 +206,6 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
||||
putAndVerifyConfigDataWithValueTooLong();
|
||||
}
|
||||
|
||||
@Step
|
||||
private void putAndVerifyConfigDataWithKeyTooLong() throws Exception {
|
||||
final Map<String, String> attributes = Collections.singletonMap(ATTRIBUTE_KEY_TOO_LONG, ATTRIBUTE_VALUE_VALID);
|
||||
mvc.perform(put(DdiConfigDataTest.TARGET2_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.exceptionClass", equalTo(InvalidTargetAttributeException.class.getName())))
|
||||
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_TARGET_ATTRIBUTES_INVALID.getKey())));
|
||||
}
|
||||
|
||||
@Step
|
||||
private void putAndVerifyConfigDataWithValueTooLong() throws Exception {
|
||||
final Map<String, String> attributes = Collections.singletonMap(ATTRIBUTE_KEY_VALID, ATTRIBUTE_VALUE_TOO_LONG);
|
||||
mvc.perform(put(DdiConfigDataTest.TARGET2_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.exceptionClass", equalTo(InvalidTargetAttributeException.class.getName())))
|
||||
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_TARGET_ATTRIBUTES_INVALID.getKey())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that config data (device attributes) can be updated by the controller using different update modes (merge, replace, remove).")
|
||||
void putConfigDataWithDifferentUpdateModes() throws Exception {
|
||||
@@ -249,6 +229,26 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
||||
putConfigDataWithInvalidUpdateMode();
|
||||
}
|
||||
|
||||
@Step
|
||||
private void putAndVerifyConfigDataWithKeyTooLong() throws Exception {
|
||||
final Map<String, String> attributes = Collections.singletonMap(ATTRIBUTE_KEY_TOO_LONG, ATTRIBUTE_VALUE_VALID);
|
||||
mvc.perform(put(DdiConfigDataTest.TARGET2_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.exceptionClass", equalTo(InvalidTargetAttributeException.class.getName())))
|
||||
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_TARGET_ATTRIBUTES_INVALID.getKey())));
|
||||
}
|
||||
|
||||
@Step
|
||||
private void putAndVerifyConfigDataWithValueTooLong() throws Exception {
|
||||
final Map<String, String> attributes = Collections.singletonMap(ATTRIBUTE_KEY_VALID, ATTRIBUTE_VALUE_TOO_LONG);
|
||||
mvc.perform(put(DdiConfigDataTest.TARGET2_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.exceptionClass", equalTo(InvalidTargetAttributeException.class.getName())))
|
||||
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_TARGET_ATTRIBUTES_INVALID.getKey())));
|
||||
}
|
||||
|
||||
@Step
|
||||
private void putConfigDataWithInvalidUpdateMode() throws Exception {
|
||||
// create some attriutes
|
||||
@@ -258,8 +258,8 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// use an invalid update mode
|
||||
mvc.perform(put(DdiConfigDataTest.TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
|
||||
.content(JsonBuilder.configData(attributes, "KJHGKJHGKJHG").toString())
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.content(JsonBuilder.configData(attributes, "KJHGKJHGKJHG").toString())
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@@ -274,8 +274,8 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
||||
removeAttributes.put("k3", "bar");
|
||||
|
||||
mvc.perform(put(DdiConfigDataTest.TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
|
||||
.content(JsonBuilder.configData(removeAttributes, "remove").toString())
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.content(JsonBuilder.configData(removeAttributes, "remove").toString())
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk());
|
||||
|
||||
// verify attribute removal
|
||||
@@ -298,8 +298,8 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
||||
mergeAttributes.put("k1", "v1_modified_again");
|
||||
mergeAttributes.put("k4", "v4");
|
||||
mvc.perform(put(DdiConfigDataTest.TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
|
||||
.content(JsonBuilder.configData(mergeAttributes, "merge").toString())
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.content(JsonBuilder.configData(mergeAttributes, "merge").toString())
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk());
|
||||
|
||||
// verify attribute merge
|
||||
@@ -325,8 +325,8 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
||||
replacementAttributes.put("k2", "v2");
|
||||
replacementAttributes.put("k3", "v3");
|
||||
mvc.perform(put(DdiConfigDataTest.TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
|
||||
.content(JsonBuilder.configData(replacementAttributes, "replace").toString())
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.content(JsonBuilder.configData(replacementAttributes, "replace").toString())
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk());
|
||||
|
||||
// verify attribute replacement
|
||||
@@ -349,7 +349,7 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// set the initial attributes
|
||||
mvc.perform(put(DdiConfigDataTest.TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
|
||||
// verify the initial parameters
|
||||
|
||||
@@ -9,6 +9,21 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.ddi.rest.resource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.eclipse.hawkbit.repository.jpa.management.JpaConfirmationManagement.CONFIRMATION_CODE_MSG_PREFIX;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Step;
|
||||
@@ -49,21 +64,6 @@ import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.ResultActions;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.eclipse.hawkbit.repository.jpa.management.JpaConfirmationManagement.CONFIRMATION_CODE_MSG_PREFIX;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Test confirmation base from the controller.
|
||||
*/
|
||||
@@ -91,7 +91,7 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
final List<Target> targetsAssignedToDs = assignDistributionSet(ds.getId(), savedTarget.getControllerId(),
|
||||
Action.ActionType.FORCED).getAssignedEntity().stream().map(Action::getTarget)
|
||||
.collect(Collectors.toList());
|
||||
.collect(Collectors.toList());
|
||||
implicitLock(ds);
|
||||
|
||||
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
|
||||
@@ -116,10 +116,10 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
performGet(CONTROLLER_BASE, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
|
||||
DEFAULT_CONTROLLER_ID)
|
||||
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
|
||||
.andExpect(jsonPath("$._links.confirmationBase.href",
|
||||
containsString(expectedConfirmationBaseLink)))
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist());
|
||||
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
|
||||
.andExpect(jsonPath("$._links.confirmationBase.href",
|
||||
containsString(expectedConfirmationBaseLink)))
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist());
|
||||
|
||||
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
|
||||
.isGreaterThanOrEqualTo(current);
|
||||
@@ -179,12 +179,12 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
.get(0);
|
||||
|
||||
mvc.perform(
|
||||
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
|
||||
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.confirmationBase.href").doesNotExist());
|
||||
|
||||
mvc.perform(get(CONFIRMATION_BASE_ACTION, tenantAware.getCurrentTenant(), controllerId, savedAction.getId())
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
.get(0);
|
||||
|
||||
mvc.perform(
|
||||
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
|
||||
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.confirmationBase.href").exists())
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist());
|
||||
@@ -212,7 +212,7 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
|
||||
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), controllerId, savedAction.getId())
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@@ -238,14 +238,14 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// verify confirmation endpoint is still accessible
|
||||
sendConfirmationFeedback(savedTarget, savedAction, DdiConfirmationFeedback.Confirmation.DENIED, 20,
|
||||
"Action denied message.").andExpect(status().isOk());
|
||||
"Action denied message.").andExpect(status().isOk());
|
||||
|
||||
// confirmation base should still be exposed
|
||||
verifyActionInConfirmationBaseState(savedTarget.getControllerId(), savedAction.getId());
|
||||
|
||||
// verify confirmation endpoint is still accessible
|
||||
sendConfirmationFeedback(savedTarget, savedAction, DdiConfirmationFeedback.Confirmation.CONFIRMED, 10,
|
||||
"Action confirmed message.").andExpect(status().isOk());
|
||||
"Action confirmed message.").andExpect(status().isOk());
|
||||
|
||||
// assert deployment link is exposed to the target
|
||||
verifyActionInDeploymentBaseState(controllerId, savedAction.getId());
|
||||
@@ -282,38 +282,6 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
verifyActionInDeploymentBaseState(controllerId, savedAction.getId());
|
||||
}
|
||||
|
||||
@Step
|
||||
private void verifyActionInDeploymentBaseState(final String controllerId, final long actionId) throws Exception {
|
||||
final String expectedDeploymentBaseLink = String.format("/%s/controller/v1/%s/deploymentBase/%d",
|
||||
tenantAware.getCurrentTenant(), controllerId, actionId);
|
||||
|
||||
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)))
|
||||
.andExpect(jsonPath("$._links.confirmationBase.href").doesNotExist());
|
||||
|
||||
// assert that deployment endpoint is working
|
||||
mvc.perform(get(expectedDeploymentBaseLink).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Step
|
||||
private void verifyActionInConfirmationBaseState(final String controllerId, final long actionId) throws Exception {
|
||||
mvc.perform(
|
||||
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.confirmationBase.href").exists())
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist());
|
||||
|
||||
mvc.perform(get(CONFIRMATION_BASE_ACTION, tenantAware.getCurrentTenant(), controllerId, actionId)
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
|
||||
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), controllerId, actionId)
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Confirmation base provides right values if auto-confirm not active.")
|
||||
void getConfirmationBaseProvidesAutoConfirmStatusNotActive() throws Exception {
|
||||
@@ -331,7 +299,7 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
tenantAware.getCurrentTenant(), controllerId);
|
||||
|
||||
mvc.perform(
|
||||
get(CONFIRMATION_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
|
||||
get(CONFIRMATION_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("autoConfirm.active", equalTo(Boolean.FALSE)))
|
||||
.andExpect(jsonPath("$._links.confirmationBase.href", containsString(confirmationBaseActionLink)))
|
||||
@@ -353,7 +321,7 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
controllerId);
|
||||
|
||||
mvc.perform(
|
||||
get(CONFIRMATION_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
|
||||
get(CONFIRMATION_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("autoConfirm.active", equalTo(Boolean.TRUE)))
|
||||
.andExpect(initiator == null ? jsonPath("autoConfirm.initiator").doesNotExist()
|
||||
@@ -364,11 +332,6 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
.andExpect(jsonPath("$._links.activateAutoConfirm").doesNotExist());
|
||||
}
|
||||
|
||||
private static Stream<Arguments> possibleActiveStates() {
|
||||
return Stream.of(Arguments.of("someInitiator", "someRemark"), Arguments.of(null, "someRemark"),
|
||||
Arguments.of("someInitiator", null), Arguments.of(null, null));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("possibleActiveStates")
|
||||
@Description("Verify auto-confirm activation is handled correctly.")
|
||||
@@ -378,7 +341,7 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
final DdiActivateAutoConfirmation body = new DdiActivateAutoConfirmation(initiator, remark);
|
||||
|
||||
mvc.perform(post(ACTIVATE_AUTO_CONFIRM, tenantAware.getCurrentTenant(), controllerId)
|
||||
.content(getMapper().writeValueAsString(body)).contentType(MediaType.APPLICATION_JSON_UTF8))
|
||||
.content(getMapper().writeValueAsString(body)).contentType(MediaType.APPLICATION_JSON_UTF8))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
|
||||
assertThat(confirmationManagement.getStatus(controllerId)).hasValueSatisfying(status -> {
|
||||
@@ -433,29 +396,16 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
tenantAware.getCurrentTenant(), controllerId, savedAction.getId());
|
||||
|
||||
mvc.perform(
|
||||
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
|
||||
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist())
|
||||
.andExpect(jsonPath("$._links.confirmationBase.href", containsString(expectedConfirmationBaseLink)));
|
||||
|
||||
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), controllerId, savedAction.getId())
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
private ResultActions sendConfirmationFeedback(final Target target, final Action action,
|
||||
final DdiConfirmationFeedback.Confirmation confirmation, Integer code, String message) throws Exception {
|
||||
|
||||
if (message == null) {
|
||||
message = RandomStringUtils.randomAlphanumeric(1000);
|
||||
}
|
||||
|
||||
final String feedback = getJsonConfirmationFeedback(confirmation, code, Collections.singletonList(message));
|
||||
return mvc.perform(
|
||||
post(CONFIRMATION_FEEDBACK, tenantAware.getCurrentTenant(), target.getControllerId(), action.getId())
|
||||
.content(feedback).contentType(MediaType.APPLICATION_JSON));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test to verify that only a specific count of messages are returned based on the input actionHistory for getControllerDeploymentActionFeedback endpoint.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@@ -485,18 +435,68 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// confirmationBase not available in RUNNING state anymore
|
||||
mvc.perform(get(CONFIRMATION_BASE_ACTION, tenantAware.getCurrentTenant(), savedTarget.getControllerId(),
|
||||
savedAction.getId()).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
|
||||
savedAction.getId()).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
|
||||
|
||||
// assert confirmed message against deploymentBase endpoint
|
||||
// this call will update the action due to retrieved action status update
|
||||
mvc.perform(
|
||||
get(DEPLOYMENT_BASE + "?actionHistory=2", tenantAware.getCurrentTenant(), savedTarget.getControllerId(),
|
||||
savedAction.getId()).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
|
||||
get(DEPLOYMENT_BASE + "?actionHistory=2", tenantAware.getCurrentTenant(), savedTarget.getControllerId(),
|
||||
savedAction.getId()).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.actionHistory.messages", hasItem(containsString(CONFIRMED_MESSAGE))))
|
||||
.andExpect(jsonPath("$.actionHistory.messages",
|
||||
hasItem(containsString(String.format(CONFIRMATION_CODE_MSG_PREFIX, CONFIRMED_CODE)))));
|
||||
}
|
||||
|
||||
private static Stream<Arguments> possibleActiveStates() {
|
||||
return Stream.of(Arguments.of("someInitiator", "someRemark"), Arguments.of(null, "someRemark"),
|
||||
Arguments.of("someInitiator", null), Arguments.of(null, null));
|
||||
}
|
||||
|
||||
@Step
|
||||
private void verifyActionInDeploymentBaseState(final String controllerId, final long actionId) throws Exception {
|
||||
final String expectedDeploymentBaseLink = String.format("/%s/controller/v1/%s/deploymentBase/%d",
|
||||
tenantAware.getCurrentTenant(), controllerId, actionId);
|
||||
|
||||
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)))
|
||||
.andExpect(jsonPath("$._links.confirmationBase.href").doesNotExist());
|
||||
|
||||
// assert that deployment endpoint is working
|
||||
mvc.perform(get(expectedDeploymentBaseLink).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Step
|
||||
private void verifyActionInConfirmationBaseState(final String controllerId, final long actionId) throws Exception {
|
||||
mvc.perform(
|
||||
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.confirmationBase.href").exists())
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist());
|
||||
|
||||
mvc.perform(get(CONFIRMATION_BASE_ACTION, tenantAware.getCurrentTenant(), controllerId, actionId)
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
|
||||
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), controllerId, actionId)
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
private ResultActions sendConfirmationFeedback(final Target target, final Action action,
|
||||
final DdiConfirmationFeedback.Confirmation confirmation, Integer code, String message) throws Exception {
|
||||
|
||||
if (message == null) {
|
||||
message = RandomStringUtils.randomAlphanumeric(1000);
|
||||
}
|
||||
|
||||
final String feedback = getJsonConfirmationFeedback(confirmation, code, Collections.singletonList(message));
|
||||
return mvc.perform(
|
||||
post(CONFIRMATION_FEEDBACK, tenantAware.getCurrentTenant(), target.getControllerId(), action.getId())
|
||||
.content(feedback).contentType(MediaType.APPLICATION_JSON));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,10 @@ import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.assertj.core.api.Condition;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiResult;
|
||||
@@ -66,12 +70,6 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
|
||||
/**
|
||||
* Test deployment base from the controller.
|
||||
*/
|
||||
@@ -79,6 +77,10 @@ import io.qameta.allure.Story;
|
||||
@Story("Deployment Action Resource")
|
||||
public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
ActionRepository actionRepository;
|
||||
@Autowired
|
||||
ActionStatusRepository actionStatusRepository;
|
||||
private static final String DEFAULT_CONTROLLER_ID = "4712";
|
||||
|
||||
@Test
|
||||
@@ -130,10 +132,10 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
performGet(SOFTWARE_MODULE_ARTIFACTS, MediaType.APPLICATION_JSON, status().isOk(),
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), softwareModuleId.toString())
|
||||
.andExpect(jsonPath("$", hasSize(3)))
|
||||
.andExpect(jsonPath("$.[?(@.filename=='filename0')]", hasSize(1)))
|
||||
.andExpect(jsonPath("$.[?(@.filename=='filename1')]", hasSize(1)))
|
||||
.andExpect(jsonPath("$.[?(@.filename=='filename2')]", hasSize(1)));
|
||||
.andExpect(jsonPath("$", hasSize(3)))
|
||||
.andExpect(jsonPath("$.[?(@.filename=='filename0')]", hasSize(1)))
|
||||
.andExpect(jsonPath("$.[?(@.filename=='filename1')]", hasSize(1)))
|
||||
.andExpect(jsonPath("$.[?(@.filename=='filename2')]", hasSize(1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -171,8 +173,8 @@ 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")))
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href",
|
||||
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href",
|
||||
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
|
||||
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
|
||||
.isGreaterThanOrEqualTo(current);
|
||||
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
|
||||
@@ -214,7 +216,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
DEFAULT_CONTROLLER_ID).andReturn();
|
||||
assertThat(JsonPath.compile("_links.deploymentBase.href").read(mvcResult.getResponse().getContentAsString())
|
||||
.toString()).isEqualTo(urlBeforeSwitch)
|
||||
.startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, actionId.toString()));
|
||||
.startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, actionId.toString()));
|
||||
|
||||
// After the time is over we should see a new etag
|
||||
TimeUnit.MILLISECONDS.sleep(2_000);
|
||||
@@ -269,8 +271,8 @@ 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")))
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href",
|
||||
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href",
|
||||
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
|
||||
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
|
||||
.isGreaterThanOrEqualTo(current);
|
||||
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
|
||||
@@ -327,8 +329,8 @@ 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")))
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href",
|
||||
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href",
|
||||
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
|
||||
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
|
||||
.isGreaterThanOrEqualTo(current);
|
||||
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
|
||||
@@ -373,7 +375,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
final List<Target> saved = assignDistributionSet(ds.getId(), savedTarget.getControllerId(),
|
||||
ActionType.DOWNLOAD_ONLY).getAssignedEntity().stream().map(Action::getTarget)
|
||||
.collect(Collectors.toList());
|
||||
.collect(Collectors.toList());
|
||||
implicitLock(ds);
|
||||
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
|
||||
|
||||
@@ -394,8 +396,8 @@ 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")))
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href",
|
||||
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href",
|
||||
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
|
||||
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
|
||||
.isGreaterThanOrEqualTo(current);
|
||||
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
|
||||
@@ -417,17 +419,6 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
}
|
||||
|
||||
private void getAndVerifyDeploymentBasePayload(final String controllerId, final MediaType mediaType,
|
||||
final DistributionSet ds, final String visibleMetadataOsKey, final String visibleMetadataOsValue,
|
||||
final Artifact artifact, final Artifact artifactSignature, final Long actionId, final String downloadType,
|
||||
final String updateType, final Long osModuleId) throws Exception {
|
||||
getAndVerifyDeploymentBasePayload(controllerId, mediaType, ds, artifact, artifactSignature, actionId,
|
||||
osModuleId, downloadType, updateType).andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part=='os')].metadata[0].key").value(visibleMetadataOsKey))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].metadata[0].value")
|
||||
.value(visibleMetadataOsValue));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test various invalid access attempts to the deployment resource und the expected behaviour of the server.")
|
||||
public void badDeploymentAction() throws Exception {
|
||||
@@ -449,7 +440,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// no deployment
|
||||
mvc.perform(
|
||||
MockMvcRequestBuilders.get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID, "1"))
|
||||
MockMvcRequestBuilders.get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID, "1"))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
|
||||
|
||||
// wrong media type
|
||||
@@ -460,8 +451,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
mvc.perform(MockMvcRequestBuilders.get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID,
|
||||
actionId)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||
mvc.perform(MockMvcRequestBuilders
|
||||
.get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID, actionId)
|
||||
.accept(MediaType.APPLICATION_ATOM_XML)).andDo(MockMvcResultPrinter.print())
|
||||
.get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID, actionId)
|
||||
.accept(MediaType.APPLICATION_ATOM_XML)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotAcceptable());
|
||||
}
|
||||
|
||||
@@ -552,8 +543,6 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
assertStatusMessagesCount(6);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
ActionRepository actionRepository;
|
||||
@Test
|
||||
@Description("Verifies that an update action is correctly set to error if the controller provides error feedback.")
|
||||
public void rootRsSingleDeploymentActionWithErrorFeedback() throws Exception {
|
||||
@@ -669,7 +658,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// not allowed methods
|
||||
mvc.perform(MockMvcRequestBuilders.get(DEPLOYMENT_FEEDBACK, tenantAware.getCurrentTenant(),
|
||||
DEFAULT_CONTROLLER_ID, "2")).andDo(MockMvcResultPrinter.print())
|
||||
DEFAULT_CONTROLLER_ID, "2")).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
|
||||
mvc.perform(put(DEPLOYMENT_FEEDBACK, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID, "2"))
|
||||
@@ -750,6 +739,21 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
.andExpect(jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getCanonicalName())));
|
||||
}
|
||||
|
||||
public long countActionStatusAll() {
|
||||
return actionStatusRepository.count();
|
||||
}
|
||||
|
||||
private void getAndVerifyDeploymentBasePayload(final String controllerId, final MediaType mediaType,
|
||||
final DistributionSet ds, final String visibleMetadataOsKey, final String visibleMetadataOsValue,
|
||||
final Artifact artifact, final Artifact artifactSignature, final Long actionId, final String downloadType,
|
||||
final String updateType, final Long osModuleId) throws Exception {
|
||||
getAndVerifyDeploymentBasePayload(controllerId, mediaType, ds, artifact, artifactSignature, actionId,
|
||||
osModuleId, downloadType, updateType).andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part=='os')].metadata[0].key").value(visibleMetadataOsKey))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].metadata[0].value")
|
||||
.value(visibleMetadataOsValue));
|
||||
}
|
||||
|
||||
private void assertActionStatusCount(final int actionStatusCount, final int minActionStatusCountInPage) {
|
||||
final Target target = targetManagement.getByControllerID(DdiDeploymentBaseTest.DEFAULT_CONTROLLER_ID).get();
|
||||
assertThat(target.getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING);
|
||||
@@ -762,19 +766,6 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
new ActionStatusCondition(Status.RUNNING));
|
||||
}
|
||||
|
||||
private static class ActionStatusCondition extends Condition<ActionStatus> {
|
||||
private final Action.Status status;
|
||||
|
||||
public ActionStatusCondition(final Action.Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(final ActionStatus value) {
|
||||
return value.getStatus() == status;
|
||||
}
|
||||
}
|
||||
|
||||
private Target createTargetAndAssertNoActiveActions() {
|
||||
final Target savedTarget = testdataFactory.createTarget(DdiDeploymentBaseTest.DEFAULT_CONTROLLER_ID);
|
||||
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).isEmpty();
|
||||
@@ -829,12 +820,21 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
.hasSize(activeActions);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
ActionStatusRepository actionStatusRepository;
|
||||
private Page<ActionStatus> findActionStatusAll(final Pageable pageable) {
|
||||
return JpaManagementHelper.findAllWithCountBySpec(actionStatusRepository, pageable, null);
|
||||
}
|
||||
public long countActionStatusAll() {
|
||||
return actionStatusRepository.count();
|
||||
|
||||
private static class ActionStatusCondition extends Condition<ActionStatus> {
|
||||
|
||||
private final Action.Status status;
|
||||
|
||||
public ActionStatusCondition(final Action.Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(final ActionStatus value) {
|
||||
return value.getStatus() == status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiResult;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiStatus;
|
||||
@@ -62,10 +65,6 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.ResultActions;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
|
||||
/**
|
||||
* Test installed base from the controller.
|
||||
*/
|
||||
@@ -73,6 +72,8 @@ import io.qameta.allure.Story;
|
||||
@Story("Installed Base Resource")
|
||||
public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
ActionStatusRepository actionStatusRepository;
|
||||
private static final int ARTIFACT_SIZE = 5 * 1024;
|
||||
private static final String CONTROLLER_ID = "4715";
|
||||
|
||||
@@ -105,17 +106,17 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
final Target target = createTargetAndAssertNoActiveActions();
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet("");
|
||||
|
||||
|
||||
// update assigned version
|
||||
putInstalledBase(target.getControllerId(),getJsonInstalledBase(ds.getName(),ds.getVersion()),status()
|
||||
putInstalledBase(target.getControllerId(), getJsonInstalledBase(ds.getName(), ds.getVersion()), status()
|
||||
.isCreated());
|
||||
|
||||
assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId()).get().getId())
|
||||
.isEqualTo(ds.getId());
|
||||
|
||||
// update assigned version while version already assigned
|
||||
putInstalledBase(target.getControllerId(),getJsonInstalledBase(ds.getName(),ds.getVersion()),status().isOk());
|
||||
putInstalledBase(target.getControllerId(), getJsonInstalledBase(ds.getName(), ds.getVersion()), status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Ensure that installedVersion is version self assigned")
|
||||
public void installedVersionNotExist() throws Exception {
|
||||
@@ -123,9 +124,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
final String dsName = "unknown";
|
||||
final String dsVersion = "1.0.0";
|
||||
|
||||
|
||||
// get installed base
|
||||
putInstalledBase(target.getControllerId(),getJsonInstalledBase(dsName,dsVersion),status().isNotFound());
|
||||
putInstalledBase(target.getControllerId(), getJsonInstalledBase(dsName, dsVersion), status().isNotFound());
|
||||
|
||||
assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId()).isEmpty()).isTrue();
|
||||
}
|
||||
@@ -401,14 +401,10 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
performGet(SOFTWARE_MODULE_ARTIFACTS, MediaType.APPLICATION_JSON, status().isOk(),
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), softwareModuleId.toString())
|
||||
.andExpect(jsonPath("$", hasSize(3)))
|
||||
.andExpect(jsonPath("$.[?(@.filename=='filename0')]", hasSize(1)))
|
||||
.andExpect(jsonPath("$.[?(@.filename=='filename1')]", hasSize(1)))
|
||||
.andExpect(jsonPath("$.[?(@.filename=='filename2')]", hasSize(1)));
|
||||
}
|
||||
|
||||
private static Stream<Action.ActionType> actionTypeForDeployment() {
|
||||
return Stream.of(Action.ActionType.SOFT, Action.ActionType.FORCED);
|
||||
.andExpect(jsonPath("$", hasSize(3)))
|
||||
.andExpect(jsonPath("$.[?(@.filename=='filename0')]", hasSize(1)))
|
||||
.andExpect(jsonPath("$.[?(@.filename=='filename1')]", hasSize(1)))
|
||||
.andExpect(jsonPath("$.[?(@.filename=='filename2')]", hasSize(1)));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@@ -484,8 +480,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
performGet(CONTROLLER_BASE, MediaType.APPLICATION_JSON, status().isOk(), tenantAware.getCurrentTenant(),
|
||||
target.getControllerId()).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href").exists())
|
||||
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href").exists())
|
||||
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
|
||||
|
||||
postDeploymentFeedback(target.getControllerId(), actionId, getJsonDownloadDeploymentActionFeedback(),
|
||||
status().isOk());
|
||||
@@ -495,8 +491,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
// Test
|
||||
performGet(CONTROLLER_BASE, MediaType.APPLICATION_JSON, status().isOk(), tenantAware.getCurrentTenant(),
|
||||
target.getControllerId()).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist())
|
||||
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist())
|
||||
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@@ -511,8 +507,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
performGet(CONTROLLER_BASE, MediaType.APPLICATION_JSON, status().isOk(), tenantAware.getCurrentTenant(),
|
||||
target.getControllerId()).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href").exists())
|
||||
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href").exists())
|
||||
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
|
||||
|
||||
postDeploymentFeedback(target.getControllerId(), actionId,
|
||||
getJsonActionFeedback(DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.NONE),
|
||||
@@ -525,8 +521,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
// Test
|
||||
performGet(CONTROLLER_BASE, MediaType.APPLICATION_JSON, status().isOk(), tenantAware.getCurrentTenant(),
|
||||
target.getControllerId()).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist())
|
||||
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist())
|
||||
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -556,14 +552,14 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
// Test
|
||||
// for zero input no action history is returned
|
||||
mvc.perform(get(INSTALLED_BASE + "?actionHistory", 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());
|
||||
|
||||
// depending on given query parameter value, only the latest messages are
|
||||
// returned
|
||||
mvc.perform(get(INSTALLED_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("Installation completed"))))
|
||||
.andExpect(jsonPath("$.actionHistory.messages", hasItem(containsString("Installation proceeding"))))
|
||||
@@ -572,7 +568,7 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
// for negative input the entire action history is returned
|
||||
mvc.perform(get(INSTALLED_BASE + "?actionHistory=-3", 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("Installation completed"))))
|
||||
.andExpect(jsonPath("$.actionHistory.messages", hasItem(containsString("Installation proceeding"))))
|
||||
@@ -611,12 +607,14 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
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)
|
||||
.accept(MediaType.APPLICATION_ATOM_XML)).andDo(MockMvcResultPrinter.print())
|
||||
.accept(MediaType.APPLICATION_ATOM_XML)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotAcceptable());
|
||||
}
|
||||
|
||||
@Autowired
|
||||
ActionStatusRepository actionStatusRepository;
|
||||
private static Stream<Action.ActionType> actionTypeForDeployment() {
|
||||
return Stream.of(Action.ActionType.SOFT, Action.ActionType.FORCED);
|
||||
}
|
||||
|
||||
private Target createTargetAndAssertNoActiveActions() {
|
||||
final Target savedTarget = testdataFactory.createTarget(CONTROLLER_ID);
|
||||
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).isEmpty();
|
||||
|
||||
@@ -33,6 +33,10 @@ import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Step;
|
||||
import io.qameta.allure.Story;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiResult;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiStatus;
|
||||
@@ -72,11 +76,6 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.ResultActions;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Step;
|
||||
import io.qameta.allure.Story;
|
||||
|
||||
/**
|
||||
* Test the root controller resources.
|
||||
*/
|
||||
@@ -270,7 +269,7 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
|
||||
assertThat(etagWithFirstUpdate).isNotNull();
|
||||
|
||||
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).header("If-None-Match",
|
||||
etagWithFirstUpdate).with(new RequestOnHawkbitDefaultPortPostProcessor()))
|
||||
etagWithFirstUpdate).with(new RequestOnHawkbitDefaultPortPostProcessor()))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotModified());
|
||||
|
||||
// now lets finish the update
|
||||
@@ -300,8 +299,8 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
|
||||
.getContent().get(0);
|
||||
|
||||
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId)
|
||||
.header("If-None-Match", etagAfterInstallation).accept(MediaType.APPLICATION_JSON)
|
||||
.with(new RequestOnHawkbitDefaultPortPostProcessor()))
|
||||
.header("If-None-Match", etagAfterInstallation).accept(MediaType.APPLICATION_JSON)
|
||||
.with(new RequestOnHawkbitDefaultPortPostProcessor()))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
|
||||
@@ -430,7 +429,7 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
|
||||
assertThatAttributesUpdateIsRequested(savedTarget.getControllerId());
|
||||
|
||||
mvc.perform(put(CONTROLLER_BASE + "/configData", tenantAware.getCurrentTenant(), savedTarget.getControllerId())
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk());
|
||||
assertThatAttributesUpdateIsNotRequested(savedTarget.getControllerId());
|
||||
|
||||
@@ -439,59 +438,6 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
|
||||
assertAttributesUpdateRequestedAfterSuccessfulDeployment(savedTarget, ds);
|
||||
}
|
||||
|
||||
@Step
|
||||
private void assertAttributesUpdateNotRequestedAfterFailedDeployment(Target target, final DistributionSet ds)
|
||||
throws Exception {
|
||||
target = getFirstAssignedTarget(assignDistributionSet(ds.getId(), target.getControllerId()));
|
||||
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId())
|
||||
.getContent().get(0);
|
||||
sendDeploymentActionFeedback(target, action, "closed", "failure").andExpect(status().isOk());
|
||||
assertThatAttributesUpdateIsNotRequested(target.getControllerId());
|
||||
}
|
||||
|
||||
@Step
|
||||
private void assertAttributesUpdateRequestedAfterSuccessfulDeployment(Target target, final DistributionSet ds)
|
||||
throws Exception {
|
||||
target = getFirstAssignedTarget(assignDistributionSet(ds.getId(), target.getControllerId()));
|
||||
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId())
|
||||
.getContent().get(0);
|
||||
sendDeploymentActionFeedback(target, action, "closed", null).andExpect(status().isOk());
|
||||
assertThatAttributesUpdateIsRequested(target.getControllerId());
|
||||
}
|
||||
|
||||
private void assertThatAttributesUpdateIsRequested(final String targetControllerId) throws Exception {
|
||||
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), targetControllerId)
|
||||
.accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.configData.href").isNotEmpty());
|
||||
}
|
||||
|
||||
private void assertThatAttributesUpdateIsNotRequested(final String targetControllerId) throws Exception {
|
||||
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), targetControllerId)
|
||||
.accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.configData").doesNotExist());
|
||||
}
|
||||
|
||||
private ResultActions sendDeploymentActionFeedback(final Target target, final Action action, final String execution,
|
||||
String finished, String message) throws Exception {
|
||||
if (finished == null) {
|
||||
finished = "none";
|
||||
}
|
||||
if (message == null) {
|
||||
message = RandomStringUtils.randomAlphanumeric(1000);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
private ResultActions sendDeploymentActionFeedback(final Target target, final Action action, final String execution,
|
||||
final String finished) throws Exception {
|
||||
return sendDeploymentActionFeedback(target, action, execution, finished, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test to verify that only a specific count of messages are returned based on the input actionHistory for getControllerDeploymentActionFeedback endpoint.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@@ -520,7 +466,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))))
|
||||
@@ -558,12 +504,12 @@ 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());
|
||||
|
||||
mvc.perform(get(DEPLOYMENT_BASE + "?actionHistory", 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());
|
||||
}
|
||||
@@ -596,7 +542,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))))
|
||||
@@ -668,7 +614,7 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
|
||||
.getContent().get(0);
|
||||
|
||||
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), "1911", action.getId())
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.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")));
|
||||
@@ -688,7 +634,7 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
|
||||
.getContent().get(0);
|
||||
|
||||
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), "1911", action.getId())
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.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")));
|
||||
@@ -719,12 +665,65 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@Step
|
||||
private void assertAttributesUpdateNotRequestedAfterFailedDeployment(Target target, final DistributionSet ds)
|
||||
throws Exception {
|
||||
target = getFirstAssignedTarget(assignDistributionSet(ds.getId(), target.getControllerId()));
|
||||
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId())
|
||||
.getContent().get(0);
|
||||
sendDeploymentActionFeedback(target, action, "closed", "failure").andExpect(status().isOk());
|
||||
assertThatAttributesUpdateIsNotRequested(target.getControllerId());
|
||||
}
|
||||
|
||||
@Step
|
||||
private void assertAttributesUpdateRequestedAfterSuccessfulDeployment(Target target, final DistributionSet ds)
|
||||
throws Exception {
|
||||
target = getFirstAssignedTarget(assignDistributionSet(ds.getId(), target.getControllerId()));
|
||||
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId())
|
||||
.getContent().get(0);
|
||||
sendDeploymentActionFeedback(target, action, "closed", null).andExpect(status().isOk());
|
||||
assertThatAttributesUpdateIsRequested(target.getControllerId());
|
||||
}
|
||||
|
||||
private void assertThatAttributesUpdateIsRequested(final String targetControllerId) throws Exception {
|
||||
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), targetControllerId)
|
||||
.accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.configData.href").isNotEmpty());
|
||||
}
|
||||
|
||||
private void assertThatAttributesUpdateIsNotRequested(final String targetControllerId) throws Exception {
|
||||
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), targetControllerId)
|
||||
.accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.configData").doesNotExist());
|
||||
}
|
||||
|
||||
private ResultActions sendDeploymentActionFeedback(final Target target, final Action action, final String execution,
|
||||
String finished, String message) throws Exception {
|
||||
if (finished == null) {
|
||||
finished = "none";
|
||||
}
|
||||
if (message == null) {
|
||||
message = RandomStringUtils.randomAlphanumeric(1000);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
private ResultActions sendDeploymentActionFeedback(final Target target, final Action action, final String execution,
|
||||
final String finished) throws Exception {
|
||||
return sendDeploymentActionFeedback(target, action, execution, finished, null);
|
||||
}
|
||||
|
||||
private void assertDeploymentActionIsExposedToTarget(final String controllerId, final long expectedActionId)
|
||||
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))
|
||||
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._links.deploymentBase.href", containsString(expectedDeploymentBaseLink)));
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
@@ -30,13 +33,8 @@ import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
|
||||
/**
|
||||
* Test potential DOS attack scenarios and check if the filter prevents them.
|
||||
*
|
||||
*/
|
||||
@ActiveProfiles({ "test" })
|
||||
@Feature("Component Tests - REST Security")
|
||||
@@ -100,7 +98,8 @@ class DosFilterTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Ensures that a relatively high number of READ requests is allowed if it is below the DoS detection threshold")
|
||||
@SuppressWarnings("squid:S2925") // No idea how to get rid of the Thread.sleep here
|
||||
@SuppressWarnings("squid:S2925")
|
||||
// No idea how to get rid of the Thread.sleep here
|
||||
void acceptableGetLoad() throws Exception {
|
||||
|
||||
for (int x = 0; x < 3; x++) {
|
||||
@@ -123,7 +122,7 @@ class DosFilterTest extends AbstractDDiApiIntegrationTest {
|
||||
int requests = 0;
|
||||
do {
|
||||
result = mvc.perform(post("/{tenant}/controller/v1/4711/deploymentBase/" + actionId + "/feedback",
|
||||
tenantAware.getCurrentTenant()).header(X_FORWARDED_FOR, "10.0.0.1").content(feedback)
|
||||
tenantAware.getCurrentTenant()).header(X_FORWARDED_FOR, "10.0.0.1").content(feedback)
|
||||
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
|
||||
.andReturn();
|
||||
requests++;
|
||||
@@ -139,7 +138,8 @@ class DosFilterTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Ensures that a relatively high number of WRITE requests is allowed if it is below the DoS detection threshold")
|
||||
@SuppressWarnings("squid:S2925") // No idea how to get rid of the Thread.sleep here
|
||||
@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 = getJsonProceedingDeploymentActionFeedback();
|
||||
@@ -150,7 +150,7 @@ class DosFilterTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
for (int i = 0; i < 9; i++) {
|
||||
mvc.perform(post("/{tenant}/controller/v1/4711/deploymentBase/" + actionId + "/feedback",
|
||||
tenantAware.getCurrentTenant()).header(X_FORWARDED_FOR, "10.0.0.1")
|
||||
tenantAware.getCurrentTenant()).header(X_FORWARDED_FOR, "10.0.0.1")
|
||||
.content(feedback).contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
Reference in New Issue
Block a user