Add resource collection /rest/v1/actions to Management REST API (#1299)
* Initial commit Signed-off-by: Stefan Behl <stefan.behl@bosch.io> * Added filtering by RSQL Signed-off-by: Stefan Behl <stefan.behl@bosch.io> * Support for filtering actions by distribution set, target, rollout * Added REST docs * Fixed REST docs * Introduce a config property which allows to disable the actions endpoint * Introduce representation mode parameter * Adapt REST docs * Incorporate review findings * Adapt REST docs * Improve unit tests * Minor improvements * Fix REST docs * Fix REST docs * Fix PR review findings Signed-off-by: Stefan Behl <stefan.behl@bosch.io>
This commit is contained in:
@@ -8,9 +8,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository;
|
package org.eclipse.hawkbit.repository;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sort fields for {@link ActionRest}.
|
* Sort fields for {@link ActionRest}.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public enum ActionFields implements FieldNameProvider, FieldValueConverter<ActionFields> {
|
public enum ActionFields implements FieldNameProvider, FieldValueConverter<ActionFields> {
|
||||||
|
|
||||||
@@ -18,22 +21,65 @@ public enum ActionFields implements FieldNameProvider, FieldValueConverter<Actio
|
|||||||
* The status field.
|
* The status field.
|
||||||
*/
|
*/
|
||||||
STATUS("active"),
|
STATUS("active"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The detailed action status
|
||||||
|
*/
|
||||||
|
DETAILSTATUS("status"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The id field.
|
* The id field.
|
||||||
*/
|
*/
|
||||||
ID("id"),
|
ID("id"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The weight field.
|
* The weight field.
|
||||||
*/
|
*/
|
||||||
WEIGHT("weight");
|
WEIGHT("weight"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The target field
|
||||||
|
*/
|
||||||
|
TARGET("target", TargetFields.ID.getFieldName(), TargetFields.NAME.getFieldName(),
|
||||||
|
TargetFields.UPDATESTATUS.getFieldName(), TargetFields.IPADDRESS.getFieldName()),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The distribution set field
|
||||||
|
*/
|
||||||
|
DISTRIBUTIONSET("distributionSet", DistributionSetFields.ID.getFieldName(),
|
||||||
|
DistributionSetFields.NAME.getFieldName(), DistributionSetFields.VERSION.getFieldName(),
|
||||||
|
DistributionSetFields.TYPE.getFieldName()),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The rollout field
|
||||||
|
*/
|
||||||
|
ROLLOUT("rollout", RolloutFields.ID.getFieldName(), RolloutFields.NAME.getFieldName()),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The rollout field
|
||||||
|
*/
|
||||||
|
ROLLOUTGROUP("rolloutGroup", RolloutGroupFields.ID.getFieldName(), RolloutGroupFields.NAME.getFieldName());
|
||||||
|
|
||||||
private static final String ACTIVE = "pending";
|
private static final String ACTIVE = "pending";
|
||||||
private static final String INACTIVE = "finished";
|
private static final String INACTIVE = "finished";
|
||||||
|
|
||||||
private final String fieldName;
|
private final String fieldName;
|
||||||
|
|
||||||
|
private List<String> subEntityAttributes;
|
||||||
|
|
||||||
private ActionFields(final String fieldName) {
|
private ActionFields(final String fieldName) {
|
||||||
this.fieldName = fieldName;
|
this.fieldName = fieldName;
|
||||||
|
this.subEntityAttributes = Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActionFields(final String fieldName, final String... subEntityAttributes) {
|
||||||
|
this.fieldName = fieldName;
|
||||||
|
this.subEntityAttributes = Arrays.asList(subEntityAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getSubEntityAttributes() {
|
||||||
|
return subEntityAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -49,6 +95,14 @@ public enum ActionFields implements FieldNameProvider, FieldValueConverter<Actio
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] possibleValues(final ActionFields e) {
|
||||||
|
if (STATUS == e) {
|
||||||
|
return new String[] { ACTIVE, INACTIVE };
|
||||||
|
}
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
|
||||||
private static Object convertStatusValue(final String value) {
|
private static Object convertStatusValue(final String value) {
|
||||||
final String trimmedValue = value.trim();
|
final String trimmedValue = value.trim();
|
||||||
if (trimmedValue.equalsIgnoreCase(ACTIVE)) {
|
if (trimmedValue.equalsIgnoreCase(ACTIVE)) {
|
||||||
@@ -60,11 +114,4 @@ public enum ActionFields implements FieldNameProvider, FieldValueConverter<Actio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] possibleValues(final ActionFields e) {
|
|
||||||
if (STATUS == e) {
|
|
||||||
return new String[] { ACTIVE, INACTIVE };
|
|
||||||
}
|
|
||||||
return new String[0];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,6 +217,16 @@ public interface DeploymentManagement {
|
|||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||||
long countActionsAll();
|
long countActionsAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counts the actions which match the given query.
|
||||||
|
*
|
||||||
|
* @param rsqlParam
|
||||||
|
* RSQL query.
|
||||||
|
* @return the total number of actions matching the given RSQL query.
|
||||||
|
*/
|
||||||
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||||
|
long countActions(@NotNull String rsqlParam);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counts all actions associated to a specific target.
|
* Counts all actions associated to a specific target.
|
||||||
*
|
*
|
||||||
@@ -274,6 +284,19 @@ public interface DeploymentManagement {
|
|||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||||
Slice<Action> findActionsAll(@NotNull Pageable pageable);
|
Slice<Action> findActionsAll(@NotNull Pageable pageable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves all {@link Action} entities which match the given RSQL query.
|
||||||
|
*
|
||||||
|
* @param rsqlParam
|
||||||
|
* RSQL query string
|
||||||
|
* @param pageable
|
||||||
|
* the page request parameter for paging and sorting the result
|
||||||
|
*
|
||||||
|
* @return a paged list of {@link Action}s.
|
||||||
|
*/
|
||||||
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||||
|
Slice<Action> findActions(@NotNull String rsqlParam, @NotNull Pageable pageable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves all {@link Action} which assigned to a specific
|
* Retrieves all {@link Action} which assigned to a specific
|
||||||
* {@link DistributionSet}.
|
* {@link DistributionSet}.
|
||||||
|
|||||||
@@ -72,8 +72,6 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
|
|||||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType;
|
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
|
||||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||||
@@ -582,7 +580,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
if (rolloutGroupActions.getContent().isEmpty()) {
|
if (rolloutGroupActions.getContent().isEmpty()) {
|
||||||
return 0L;
|
return 0L;
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Action> newTargetAssignments = handleTargetAssignments(rolloutGroupActions);
|
final List<Action> newTargetAssignments = handleTargetAssignments(rolloutGroupActions);
|
||||||
|
|
||||||
if (!newTargetAssignments.isEmpty()) {
|
if (!newTargetAssignments.isEmpty()) {
|
||||||
@@ -592,7 +590,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
return rolloutGroupActions.getTotalElements();
|
return rolloutGroupActions.getTotalElements();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Action> handleTargetAssignments(final Page<Action> rolloutGroupActions) {
|
private List<Action> handleTargetAssignments(final Page<Action> rolloutGroupActions) {
|
||||||
// Close actions already assigned and collect pending assignments
|
// Close actions already assigned and collect pending assignments
|
||||||
final List<JpaAction> pendingTargetAssignments = rolloutGroupActions.getContent().stream()
|
final List<JpaAction> pendingTargetAssignments = rolloutGroupActions.getContent().stream()
|
||||||
@@ -651,9 +649,9 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
return Collections.unmodifiableList(savedActions);
|
return Collections.unmodifiableList(savedActions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void closeOrCancelOpenDeviceActions(final List<JpaAction> actions){
|
private void closeOrCancelOpenDeviceActions(final List<JpaAction> actions) {
|
||||||
final List<Long> targetIds = actions.stream().map(JpaAction::getTarget).map(Target::getId)
|
final List<Long> targetIds = actions.stream().map(JpaAction::getTarget).map(Target::getId)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
if (isActionsAutocloseEnabled()) {
|
if (isActionsAutocloseEnabled()) {
|
||||||
onlineDsAssignmentStrategy.closeObsoleteUpdateActions(targetIds);
|
onlineDsAssignmentStrategy.closeObsoleteUpdateActions(targetIds);
|
||||||
} else {
|
} else {
|
||||||
@@ -661,7 +659,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<JpaAction> activateActions(final List<JpaAction> actions){
|
private List<JpaAction> activateActions(final List<JpaAction> actions) {
|
||||||
actions.forEach(action -> {
|
actions.forEach(action -> {
|
||||||
action.setActive(true);
|
action.setActive(true);
|
||||||
action.setStatus(Status.RUNNING);
|
action.setStatus(Status.RUNNING);
|
||||||
@@ -860,6 +858,13 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
return actionRepository.count();
|
return actionRepository.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long countActions(final String rsqlParam) {
|
||||||
|
final List<Specification<JpaAction>> specList = Arrays.asList(
|
||||||
|
RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database));
|
||||||
|
return JpaManagementHelper.countBySpec(actionRepository, specList);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long countActionsByDistributionSetIdAndActiveIsTrue(final Long distributionSet) {
|
public long countActionsByDistributionSetIdAndActiveIsTrue(final Long distributionSet) {
|
||||||
return actionRepository.countByDistributionSetIdAndActiveIsTrue(distributionSet);
|
return actionRepository.countByDistributionSetIdAndActiveIsTrue(distributionSet);
|
||||||
@@ -882,6 +887,13 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
return JpaManagementHelper.findAllWithoutCountBySpec(actionRepository, pageable, null);
|
return JpaManagementHelper.findAllWithoutCountBySpec(actionRepository, pageable, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Slice<Action> findActions(final String rsqlParam, final Pageable pageable) {
|
||||||
|
final List<Specification<JpaAction>> specList = Arrays.asList(
|
||||||
|
RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer, database));
|
||||||
|
return JpaManagementHelper.findAllWithoutCountBySpec(actionRepository, pageable, specList);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<DistributionSet> getAssignedDistributionSet(final String controllerId) {
|
public Optional<DistributionSet> getAssignedDistributionSet(final String controllerId) {
|
||||||
throwExceptionIfTargetDoesNotExist(controllerId);
|
throwExceptionIfTargetDoesNotExist(controllerId);
|
||||||
@@ -990,4 +1002,5 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ public class MgmtAction extends MgmtBaseEntity {
|
|||||||
@JsonProperty
|
@JsonProperty
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String detailStatus;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private Long forceTime;
|
private Long forceTime;
|
||||||
|
|
||||||
@@ -65,7 +68,7 @@ public class MgmtAction extends MgmtBaseEntity {
|
|||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private MgmtMaintenanceWindow maintenanceWindow;
|
private MgmtMaintenanceWindow maintenanceWindow;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private Long rollout;
|
private Long rollout;
|
||||||
|
|
||||||
@@ -132,7 +135,7 @@ public class MgmtAction extends MgmtBaseEntity {
|
|||||||
return rollout;
|
return rollout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRollout(Long rollout) {
|
public void setRollout(final Long rollout) {
|
||||||
this.rollout = rollout;
|
this.rollout = rollout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,8 +143,16 @@ public class MgmtAction extends MgmtBaseEntity {
|
|||||||
return rolloutName;
|
return rolloutName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRolloutName(String rolloutName) {
|
public void setRolloutName(final String rolloutName) {
|
||||||
this.rolloutName = rolloutName;
|
this.rolloutName = rolloutName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDetailStatus() {
|
||||||
|
return detailStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDetailStatus(final String detailStatus) {
|
||||||
|
this.detailStatus = detailStatus;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2022 Bosch.IO GmbH and others.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.mgmt.rest.api;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
|
||||||
|
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction;
|
||||||
|
import org.springframework.hateoas.MediaTypes;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API providing (read-only) access to actions.
|
||||||
|
*/
|
||||||
|
@RequestMapping(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING)
|
||||||
|
public interface MgmtActionRestApi {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the GET request of retrieving all actions.
|
||||||
|
*
|
||||||
|
* @param pagingOffsetParam
|
||||||
|
* the offset of list of actions for pagination, might not be
|
||||||
|
* present in the rest request then default value will be applied
|
||||||
|
* @param pagingLimitParam
|
||||||
|
* the limit of the paged request, might not be present in the
|
||||||
|
* rest request then default value will be applied
|
||||||
|
* @param sortParam
|
||||||
|
* the sorting parameter in the request URL, syntax
|
||||||
|
* {@code field:direction, field:direction}
|
||||||
|
* @param rsqlParam
|
||||||
|
* the search parameter in the request URL, syntax
|
||||||
|
* {@code q=distributionSet.id==1}
|
||||||
|
* @param representationModeParam
|
||||||
|
* the representation mode parameter specifying whether a compact
|
||||||
|
* or a full representation shall be returned
|
||||||
|
* @return a list of all actions for a defined or default page request with
|
||||||
|
* status OK. The response is always paged. In any failure the
|
||||||
|
* JsonResponseExceptionHandler is handling the response.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@GetMapping(produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||||
|
ResponseEntity<PagedList<MgmtAction>> getActions(
|
||||||
|
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam,
|
||||||
|
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam,
|
||||||
|
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam,
|
||||||
|
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam,
|
||||||
|
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_REPRESENTATION_MODE, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_REPRESENTATION_MODE_DEFAULT) String representationModeParam);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2022 Bosch.IO GmbH and others.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.mgmt.rest.api;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumeration of the supported representation modes.
|
||||||
|
*/
|
||||||
|
public enum MgmtRepresentationMode {
|
||||||
|
|
||||||
|
FULL("full"),
|
||||||
|
|
||||||
|
COMPACT("compact");
|
||||||
|
|
||||||
|
private final String mode;
|
||||||
|
|
||||||
|
private MgmtRepresentationMode(final String mode) {
|
||||||
|
this.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<MgmtRepresentationMode> fromValue(final String value) {
|
||||||
|
return Arrays.stream(MgmtRepresentationMode.values()).filter(v -> v.mode.equalsIgnoreCase(value)).findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -63,7 +63,7 @@ public final class MgmtRestConstants {
|
|||||||
/**
|
/**
|
||||||
* The target URL mapping, href link for assigned target type.
|
* The target URL mapping, href link for assigned target type.
|
||||||
*/
|
*/
|
||||||
public static final String TARGET_V1_ASSIGNED_TARGET_TYPE= "targetType";
|
public static final String TARGET_V1_ASSIGNED_TARGET_TYPE = "targetType";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The target URL mapping, href link for assigned distribution set.
|
* The target URL mapping, href link for assigned distribution set.
|
||||||
@@ -140,6 +140,11 @@ public final class MgmtRestConstants {
|
|||||||
*/
|
*/
|
||||||
public static final String DISTRIBUTIONSET_TAG_DISTRIBUTIONSETS_REQUEST_MAPPING = "/{distributionsetTagId}/assigned";
|
public static final String DISTRIBUTIONSET_TAG_DISTRIBUTIONSETS_REQUEST_MAPPING = "/{distributionsetTagId}/assigned";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The action URL mapping rest resource.
|
||||||
|
*/
|
||||||
|
public static final String ACTION_V1_REQUEST_MAPPING = BASE_V1_REQUEST_MAPPING + "/actions";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default offset parameter in case the offset parameter is not present
|
* The default offset parameter in case the offset parameter is not present
|
||||||
* in the request.
|
* in the request.
|
||||||
@@ -186,6 +191,17 @@ public final class MgmtRestConstants {
|
|||||||
*/
|
*/
|
||||||
public static final String REQUEST_PARAMETER_SEARCH = "q";
|
public static final String REQUEST_PARAMETER_SEARCH = "q";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The request parameter for specifying the representation mode. The value
|
||||||
|
* of this parameter can either be "full" or "compact".
|
||||||
|
*/
|
||||||
|
public static final String REQUEST_PARAMETER_REPRESENTATION_MODE = "representation";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default representation mode.
|
||||||
|
*/
|
||||||
|
public static final String REQUEST_PARAMETER_REPRESENTATION_MODE_DEFAULT = "compact";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The software module type URL mapping rest resource.
|
* The software module type URL mapping rest resource.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -45,16 +45,20 @@ public interface MgmtRolloutRestApi {
|
|||||||
* @param rsqlParam
|
* @param rsqlParam
|
||||||
* the search parameter in the request URL, syntax
|
* the search parameter in the request URL, syntax
|
||||||
* {@code q=name==abc}
|
* {@code q=name==abc}
|
||||||
|
* @param representationModeParam
|
||||||
|
* the representation mode parameter specifying whether a compact
|
||||||
|
* or a full representation shall be returned
|
||||||
* @return a list of all rollouts for a defined or default page request with
|
* @return a list of all rollouts for a defined or default page request with
|
||||||
* status OK. The response is always paged. In any failure the
|
* status OK. The response is always paged. In any failure the
|
||||||
* JsonResponseExceptionHandler is handling the response.
|
* JsonResponseExceptionHandler is handling the response.
|
||||||
*/
|
*/
|
||||||
@GetMapping(produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
@GetMapping(produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||||
ResponseEntity<PagedList<MgmtRolloutResponseBody>> getRollouts(
|
ResponseEntity<PagedList<MgmtRolloutResponseBody>> getRollouts(
|
||||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam,
|
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam,
|
||||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam,
|
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam,
|
||||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam,
|
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam,
|
||||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam);
|
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam,
|
||||||
|
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_REPRESENTATION_MODE, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_REPRESENTATION_MODE_DEFAULT) String representationModeParam);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the GET request of retrieving a single rollout.
|
* Handles the GET request of retrieving a single rollout.
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2022 Bosch.IO GmbH and others.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction;
|
||||||
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
|
import org.eclipse.hawkbit.rest.data.ResponseList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mapper which maps repository model to RESTful model representation and
|
||||||
|
* back.
|
||||||
|
*/
|
||||||
|
public final class MgmtActionMapper {
|
||||||
|
|
||||||
|
private MgmtActionMapper() {
|
||||||
|
// Utility class
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a response for actions.
|
||||||
|
*
|
||||||
|
* @param actions
|
||||||
|
* list of actions
|
||||||
|
* @param repMode
|
||||||
|
* the representation mode
|
||||||
|
*
|
||||||
|
* @return the response
|
||||||
|
*/
|
||||||
|
public static List<MgmtAction> toResponse(final Collection<Action> actions, final MgmtRepresentationMode repMode) {
|
||||||
|
if (actions == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return new ResponseList<>(actions.stream().map(action -> MgmtActionMapper.toResponse(action, repMode))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static MgmtAction toResponse(final Action action, final MgmtRepresentationMode repMode) {
|
||||||
|
final String controllerId = action.getTarget().getControllerId();
|
||||||
|
if (repMode == MgmtRepresentationMode.COMPACT) {
|
||||||
|
return MgmtTargetMapper.toResponse(controllerId, action);
|
||||||
|
}
|
||||||
|
return MgmtTargetMapper.toResponseWithLinks(controllerId, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2022 Bosch.IO GmbH and others.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
|
||||||
|
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction;
|
||||||
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtActionRestApi;
|
||||||
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode;
|
||||||
|
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Slice;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@ConditionalOnProperty(name = "hawkbit.rest.MgmtActionResource.enabled", matchIfMissing = true)
|
||||||
|
public class MgmtActionResource implements MgmtActionRestApi {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(MgmtActionResource.class);
|
||||||
|
|
||||||
|
private final DeploymentManagement deploymentManagement;
|
||||||
|
|
||||||
|
MgmtActionResource(final DeploymentManagement deploymentManagement) {
|
||||||
|
this.deploymentManagement = deploymentManagement;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<PagedList<MgmtAction>> getActions(final int pagingOffsetParam, final int pagingLimitParam,
|
||||||
|
final String sortParam, final String rsqlParam, final String representationModeParam) {
|
||||||
|
|
||||||
|
final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam);
|
||||||
|
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
|
||||||
|
final Sort sorting = PagingUtility.sanitizeActionSortParam(sortParam);
|
||||||
|
final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting);
|
||||||
|
|
||||||
|
final Slice<Action> actions;
|
||||||
|
final Long totalActionCount;
|
||||||
|
if (rsqlParam != null) {
|
||||||
|
actions = this.deploymentManagement.findActions(rsqlParam, pageable);
|
||||||
|
totalActionCount = this.deploymentManagement.countActions(rsqlParam);
|
||||||
|
} else {
|
||||||
|
actions = this.deploymentManagement.findActionsAll(pageable);
|
||||||
|
totalActionCount = this.deploymentManagement.countActionsAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
final MgmtRepresentationMode repMode = MgmtRepresentationMode.fromValue(representationModeParam)
|
||||||
|
.orElseGet(() -> {
|
||||||
|
// no need for a 400, just apply a safe fallback
|
||||||
|
LOG.warn("Received an invalid representation mode: {}", representationModeParam);
|
||||||
|
return MgmtRepresentationMode.COMPACT;
|
||||||
|
});
|
||||||
|
|
||||||
|
return ResponseEntity
|
||||||
|
.ok(new PagedList<>(MgmtActionMapper.toResponse(actions.getContent(), repMode), totalActionCount));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutSuccessAction;
|
|||||||
import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutSuccessAction.SuccessAction;
|
import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutSuccessAction.SuccessAction;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtRolloutGroup;
|
import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtRolloutGroup;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtRolloutGroupResponseBody;
|
import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtRolloutGroupResponseBody;
|
||||||
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetRestApi;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRolloutRestApi;
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRolloutRestApi;
|
||||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||||
@@ -57,11 +58,15 @@ final class MgmtRolloutMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static List<MgmtRolloutResponseBody> toResponseRollout(final List<Rollout> rollouts) {
|
static List<MgmtRolloutResponseBody> toResponseRollout(final List<Rollout> rollouts) {
|
||||||
|
return toResponseRollout(rollouts, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<MgmtRolloutResponseBody> toResponseRollout(final List<Rollout> rollouts, final boolean withDetails) {
|
||||||
if (rollouts == null) {
|
if (rollouts == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
return rollouts.stream().map(rollout -> toResponseRollout(rollout, false)).collect(Collectors.toList());
|
return rollouts.stream().map(rollout -> toResponseRollout(rollout, withDetails)).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
static MgmtRolloutResponseBody toResponseRollout(final Rollout rollout, final boolean withDetails) {
|
static MgmtRolloutResponseBody toResponseRollout(final Rollout rollout, final boolean withDetails) {
|
||||||
@@ -95,6 +100,10 @@ final class MgmtRolloutMapper {
|
|||||||
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).getRolloutGroups(rollout.getId(),
|
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).getRolloutGroups(rollout.getId(),
|
||||||
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE,
|
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE,
|
||||||
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, null, null)).withRel("groups"));
|
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, null, null)).withRel("groups"));
|
||||||
|
|
||||||
|
final DistributionSet distributionSet = rollout.getDistributionSet();
|
||||||
|
body.add(linkTo(methodOn(MgmtDistributionSetRestApi.class).getDistributionSet(distributionSet.getId()))
|
||||||
|
.withRel("distributionset").withName(distributionSet.getName() + ":" + distributionSet.getVersion()));
|
||||||
}
|
}
|
||||||
|
|
||||||
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).getRollout(rollout.getId())).withSelfRel());
|
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).getRollout(rollout.getId())).withSelfRel());
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutResponseBody;
|
|||||||
import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutRestRequestBody;
|
import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutRestRequestBody;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtRolloutGroupResponseBody;
|
import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtRolloutGroupResponseBody;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
|
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
|
||||||
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRolloutRestApi;
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRolloutRestApi;
|
||||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||||
@@ -35,6 +36,8 @@ import org.eclipse.hawkbit.repository.model.Rollout;
|
|||||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||||
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
|
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
@@ -51,6 +54,9 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
public class MgmtRolloutResource implements MgmtRolloutRestApi {
|
public class MgmtRolloutResource implements MgmtRolloutRestApi {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(MgmtRolloutResource.class);
|
||||||
|
|
||||||
private final RolloutManagement rolloutManagement;
|
private final RolloutManagement rolloutManagement;
|
||||||
|
|
||||||
private final RolloutGroupManagement rolloutGroupManagement;
|
private final RolloutGroupManagement rolloutGroupManagement;
|
||||||
@@ -76,14 +82,14 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
|
|||||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam,
|
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam,
|
||||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam,
|
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam,
|
||||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam,
|
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam,
|
||||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam) {
|
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam,
|
||||||
|
final String representationModeParam) {
|
||||||
|
|
||||||
final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam);
|
final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam);
|
||||||
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
|
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
|
||||||
final Sort sorting = PagingUtility.sanitizeRolloutSortParam(sortParam);
|
final Sort sorting = PagingUtility.sanitizeRolloutSortParam(sortParam);
|
||||||
|
|
||||||
final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting);
|
final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting);
|
||||||
|
|
||||||
final Page<Rollout> findRolloutsAll;
|
final Page<Rollout> findRolloutsAll;
|
||||||
if (rsqlParam != null) {
|
if (rsqlParam != null) {
|
||||||
findRolloutsAll = this.rolloutManagement.findByRsql(pageable, rsqlParam, false);
|
findRolloutsAll = this.rolloutManagement.findByRsql(pageable, rsqlParam, false);
|
||||||
@@ -91,7 +97,15 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
|
|||||||
findRolloutsAll = this.rolloutManagement.findAll(pageable, false);
|
findRolloutsAll = this.rolloutManagement.findAll(pageable, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<MgmtRolloutResponseBody> rest = MgmtRolloutMapper.toResponseRollout(findRolloutsAll.getContent());
|
final MgmtRepresentationMode repMode = MgmtRepresentationMode.fromValue(representationModeParam)
|
||||||
|
.orElseGet(() -> {
|
||||||
|
// no need for a 400, just apply a safe fallback
|
||||||
|
LOG.warn("Received an invalid representation mode: {}", representationModeParam);
|
||||||
|
return MgmtRepresentationMode.COMPACT;
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<MgmtRolloutResponseBody> rest = MgmtRolloutMapper.toResponseRollout(findRolloutsAll.getContent(),
|
||||||
|
repMode == MgmtRepresentationMode.FULL);
|
||||||
return ResponseEntity.ok(new PagedList<>(rest, findRolloutsAll.getTotalElements()));
|
return ResponseEntity.ok(new PagedList<>(rest, findRolloutsAll.getTotalElements()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import org.eclipse.hawkbit.repository.builder.TargetCreate;
|
|||||||
import org.eclipse.hawkbit.repository.model.Action;
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||||
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.MetaData;
|
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||||
import org.eclipse.hawkbit.repository.model.PollStatus;
|
import org.eclipse.hawkbit.repository.model.PollStatus;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
@@ -226,6 +227,8 @@ public final class MgmtTargetMapper {
|
|||||||
result.setStatus(MgmtAction.ACTION_FINISHED);
|
result.setStatus(MgmtAction.ACTION_FINISHED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.setDetailStatus(action.getStatus().toString().toLowerCase());
|
||||||
|
|
||||||
final Rollout rollout = action.getRollout();
|
final Rollout rollout = action.getRollout();
|
||||||
if (rollout != null) {
|
if (rollout != null) {
|
||||||
result.setRollout(rollout.getId());
|
result.setRollout(rollout.getId());
|
||||||
@@ -256,9 +259,13 @@ public final class MgmtTargetMapper {
|
|||||||
result.add(linkTo(methodOn(MgmtTargetRestApi.class).getAction(controllerId, action.getId()))
|
result.add(linkTo(methodOn(MgmtTargetRestApi.class).getAction(controllerId, action.getId()))
|
||||||
.withRel(MgmtRestConstants.TARGET_V1_CANCELED_ACTION));
|
.withRel(MgmtRestConstants.TARGET_V1_CANCELED_ACTION));
|
||||||
}
|
}
|
||||||
result.add(linkTo(
|
|
||||||
methodOn(MgmtDistributionSetRestApi.class).getDistributionSet(action.getDistributionSet().getId()))
|
result.add(linkTo(methodOn(MgmtTargetRestApi.class).getTarget(controllerId)).withRel("target")
|
||||||
.withRel("distributionset"));
|
.withName(action.getTarget().getName()));
|
||||||
|
|
||||||
|
final DistributionSet distributionSet = action.getDistributionSet();
|
||||||
|
result.add(linkTo(methodOn(MgmtDistributionSetRestApi.class).getDistributionSet(distributionSet.getId()))
|
||||||
|
.withRel("distributionset").withName(distributionSet.getName() + ":" + distributionSet.getVersion()));
|
||||||
|
|
||||||
result.add(linkTo(methodOn(MgmtTargetRestApi.class).getActionStatusList(controllerId, action.getId(), 0,
|
result.add(linkTo(methodOn(MgmtTargetRestApi.class).getActionStatusList(controllerId, action.getId(), 0,
|
||||||
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE,
|
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE,
|
||||||
@@ -268,7 +275,7 @@ public final class MgmtTargetMapper {
|
|||||||
final Rollout rollout = action.getRollout();
|
final Rollout rollout = action.getRollout();
|
||||||
if (rollout != null) {
|
if (rollout != null) {
|
||||||
result.add(linkTo(methodOn(MgmtRolloutRestApi.class).getRollout(rollout.getId()))
|
result.add(linkTo(methodOn(MgmtRolloutRestApi.class).getRollout(rollout.getId()))
|
||||||
.withRel(MgmtRestConstants.TARGET_V1_ROLLOUT));
|
.withRel(MgmtRestConstants.TARGET_V1_ROLLOUT).withName(rollout.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -0,0 +1,421 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2022 Bosch.IO GmbH and others.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||||
|
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.request.MockMvcRequestBuilders.put;
|
||||||
|
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.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.awaitility.Awaitility;
|
||||||
|
import org.awaitility.Duration;
|
||||||
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtActionRestApi;
|
||||||
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode;
|
||||||
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
|
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import io.qameta.allure.Description;
|
||||||
|
import io.qameta.allure.Feature;
|
||||||
|
import io.qameta.allure.Story;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration test for the {@link MgmtActionRestApi}.
|
||||||
|
*/
|
||||||
|
@Feature("Component Tests - Management API")
|
||||||
|
@Story("Action Resource")
|
||||||
|
class MgmtActionResourceTest extends AbstractManagementApiIntegrationTest {
|
||||||
|
|
||||||
|
private static final String JSON_PATH_ROOT = "$";
|
||||||
|
private static final String JSON_PATH_FIELD_CONTENT = ".content";
|
||||||
|
private static final String JSON_PATH_FIELD_SIZE = ".size";
|
||||||
|
private static final String JSON_PATH_FIELD_TOTAL = ".total";
|
||||||
|
|
||||||
|
private static final String JSON_PATH_PAGED_LIST_CONTENT = JSON_PATH_ROOT + JSON_PATH_FIELD_CONTENT;
|
||||||
|
private static final String JSON_PATH_PAGED_LIST_SIZE = JSON_PATH_ROOT + JSON_PATH_FIELD_SIZE;
|
||||||
|
private static final String JSON_PATH_PAGED_LIST_TOTAL = JSON_PATH_ROOT + JSON_PATH_FIELD_TOTAL;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that actions can be filtered based on action status.")
|
||||||
|
void filterActionsByStatus() throws Exception {
|
||||||
|
|
||||||
|
// prepare test
|
||||||
|
final DistributionSet dsA = testdataFactory.createDistributionSet("");
|
||||||
|
assignDistributionSet(dsA, Collections.singletonList(testdataFactory.createTarget("knownTargetId")));
|
||||||
|
|
||||||
|
final String rsqlPendingStatus = "status==pending";
|
||||||
|
final String rsqlFinishedStatus = "status==finished";
|
||||||
|
final String rsqlPendingOrFinishedStatus = rsqlFinishedStatus + "," + rsqlPendingStatus;
|
||||||
|
|
||||||
|
// pending status one result
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "?q=" + rsqlPendingStatus))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andExpect(jsonPath("total", equalTo(1)))
|
||||||
|
.andExpect(jsonPath("size", equalTo(1))).andExpect(jsonPath("content[0].status", equalTo("pending")));
|
||||||
|
|
||||||
|
// finished status none result
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "?q=" + rsqlFinishedStatus))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andExpect(jsonPath("total", equalTo(0)))
|
||||||
|
.andExpect(jsonPath("size", equalTo(0)));
|
||||||
|
|
||||||
|
// pending or finished status one result
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "?q=" + rsqlPendingOrFinishedStatus))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andExpect(jsonPath("total", equalTo(1)))
|
||||||
|
.andExpect(jsonPath("size", equalTo(1))).andExpect(jsonPath("content[0].status", equalTo("pending")));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that actions can be filtered based on the detailed action status.")
|
||||||
|
void filterActionsByDetailStatus() throws Exception {
|
||||||
|
|
||||||
|
// prepare test
|
||||||
|
final DistributionSet dsA = testdataFactory.createDistributionSet("");
|
||||||
|
assignDistributionSet(dsA, Collections.singletonList(testdataFactory.createTarget("knownTargetId")));
|
||||||
|
|
||||||
|
final String rsqlPendingStatus = "detailStatus==running";
|
||||||
|
final String rsqlFinishedStatus = "detailStatus==finished";
|
||||||
|
final String rsqlPendingOrFinishedStatus = rsqlFinishedStatus + "," + rsqlPendingStatus;
|
||||||
|
|
||||||
|
// running status one result
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "?q=" + rsqlPendingStatus))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andExpect(jsonPath("total", equalTo(1)))
|
||||||
|
.andExpect(jsonPath("size", equalTo(1)))
|
||||||
|
.andExpect(jsonPath("content[0].detailStatus", equalTo("running")))
|
||||||
|
.andExpect(jsonPath("content[0].status", equalTo("pending")));
|
||||||
|
|
||||||
|
// finished status none result
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "?q=" + rsqlFinishedStatus))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andExpect(jsonPath("total", equalTo(0)))
|
||||||
|
.andExpect(jsonPath("size", equalTo(0)));
|
||||||
|
|
||||||
|
// running or finished status one result
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "?q=" + rsqlPendingOrFinishedStatus))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andExpect(jsonPath("total", equalTo(1)))
|
||||||
|
.andExpect(jsonPath("size", equalTo(1)))
|
||||||
|
.andExpect(jsonPath("content[0].detailStatus", equalTo("running")))
|
||||||
|
.andExpect(jsonPath("content[0].status", equalTo("pending")));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that actions can be filtered based on distribution set fields.")
|
||||||
|
void filterActionsByDistributionSet() throws Exception {
|
||||||
|
|
||||||
|
// prepare test
|
||||||
|
final DistributionSet ds = testdataFactory.createDistributionSet("");
|
||||||
|
assignDistributionSet(ds, Collections.singletonList(testdataFactory.createTarget("knownTargetId")));
|
||||||
|
|
||||||
|
final String rsqlDsName = "distributionSet.name==" + ds.getName() + "*";
|
||||||
|
final String rsqlDsVersion = "distributionSet.version==" + ds.getVersion();
|
||||||
|
final String rsqlDsId = "distributionSet.id==" + ds.getId();
|
||||||
|
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "?q=" + rsqlDsName)
|
||||||
|
.param(MgmtRestConstants.REQUEST_PARAMETER_REPRESENTATION_MODE, MgmtRepresentationMode.FULL.toString()))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andExpect(jsonPath("total", equalTo(1)))
|
||||||
|
.andExpect(jsonPath("size", equalTo(1))).andExpect(jsonPath("content.[0]._links.distributionset.name",
|
||||||
|
equalTo(ds.getName() + ":" + ds.getVersion())));
|
||||||
|
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "?q=" + rsqlDsVersion))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andExpect(jsonPath("total", equalTo(1)))
|
||||||
|
.andExpect(jsonPath("size", equalTo(1)));
|
||||||
|
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "?q=" + rsqlDsName + "," + rsqlDsVersion))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andExpect(jsonPath("total", equalTo(1)))
|
||||||
|
.andExpect(jsonPath("size", equalTo(1)));
|
||||||
|
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "?q=distributionSet.name==FooBar"))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andExpect(jsonPath("total", equalTo(0)))
|
||||||
|
.andExpect(jsonPath("size", equalTo(0)));
|
||||||
|
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "?q=" + rsqlDsId))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andExpect(jsonPath("total", equalTo(1)))
|
||||||
|
.andExpect(jsonPath("size", equalTo(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that actions can be filtered based on rollout fields.")
|
||||||
|
void filterActionsByRollout() throws Exception {
|
||||||
|
|
||||||
|
// prepare test
|
||||||
|
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||||
|
final Target target0 = testdataFactory.createTarget("t0");
|
||||||
|
|
||||||
|
// manual assignment
|
||||||
|
assignDistributionSet(ds, Collections.singletonList(target0));
|
||||||
|
|
||||||
|
// rollout
|
||||||
|
final Target target1 = testdataFactory.createTarget("t1");
|
||||||
|
final Rollout rollout = testdataFactory.createRolloutByVariables("TestRollout", "TestDesc", 1,
|
||||||
|
"name==" + target1.getName(), ds, "50", "5");
|
||||||
|
rolloutManagement.start(rollout.getId());
|
||||||
|
rolloutManagement.handleRollouts();
|
||||||
|
|
||||||
|
final String rsqlRolloutName = "rollout.name==" + rollout.getName();
|
||||||
|
final String rsqlRolloutId = "rollout.id==" + rollout.getId();
|
||||||
|
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "?q=" + rsqlRolloutName)
|
||||||
|
.param(MgmtRestConstants.REQUEST_PARAMETER_REPRESENTATION_MODE, MgmtRepresentationMode.FULL.toString()))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andExpect(jsonPath("total", equalTo(1)))
|
||||||
|
.andExpect(jsonPath("size", equalTo(1)))
|
||||||
|
.andExpect(jsonPath("content.[0]._links.target.name", equalTo(target1.getName()))).andExpect(jsonPath(
|
||||||
|
"content.[0]._links.distributionset.name", equalTo(ds.getName() + ":" + ds.getVersion())));
|
||||||
|
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "?q=" + rsqlRolloutId)
|
||||||
|
.param(MgmtRestConstants.REQUEST_PARAMETER_REPRESENTATION_MODE, MgmtRepresentationMode.FULL.toString()))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andExpect(jsonPath("total", equalTo(1)))
|
||||||
|
.andExpect(jsonPath("size", equalTo(1)))
|
||||||
|
.andExpect(jsonPath("content.[0]._links.target.name", equalTo(target1.getName()))).andExpect(jsonPath(
|
||||||
|
"content.[0]._links.distributionset.name", equalTo(ds.getName() + ":" + ds.getVersion())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that actions can be filtered based on target fields.")
|
||||||
|
void filterActionsByTarget() throws Exception {
|
||||||
|
|
||||||
|
// prepare test
|
||||||
|
final Target target = testdataFactory.createTarget("knownTargetId");
|
||||||
|
final DistributionSet ds = testdataFactory.createDistributionSet("");
|
||||||
|
assignDistributionSet(ds, Collections.singletonList(target));
|
||||||
|
|
||||||
|
final String rsqlTargetName = "target.name==knownTargetId";
|
||||||
|
|
||||||
|
// pending status one result
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "?q=" + rsqlTargetName)
|
||||||
|
.param(MgmtRestConstants.REQUEST_PARAMETER_REPRESENTATION_MODE, MgmtRepresentationMode.FULL.toString()))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andExpect(jsonPath("total", equalTo(1)))
|
||||||
|
.andExpect(jsonPath("size", equalTo(1)))
|
||||||
|
.andExpect(jsonPath("content.[0]._links.target.name", equalTo(target.getName()))).andExpect(jsonPath(
|
||||||
|
"content.[0]._links.distributionset.name", equalTo(ds.getName() + ":" + ds.getVersion())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that all available actions are returned if the complete collection is requested.")
|
||||||
|
void getActions() throws Exception {
|
||||||
|
final String knownTargetId = "targetId";
|
||||||
|
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
|
||||||
|
|
||||||
|
final Action action0 = actions.get(0);
|
||||||
|
final Action action1 = actions.get(1);
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING).param(MgmtRestConstants.REQUEST_PARAMETER_SORTING,
|
||||||
|
"ID:ASC")).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||||
|
|
||||||
|
// verify action 1
|
||||||
|
.andExpect(jsonPath("content.[1].id", equalTo(action1.getId().intValue())))
|
||||||
|
.andExpect(jsonPath("content.[1].type", equalTo("update")))
|
||||||
|
.andExpect(jsonPath("content.[1].status", equalTo("pending")))
|
||||||
|
.andExpect(jsonPath("content.[1].detailStatus", equalTo("running")))
|
||||||
|
.andExpect(jsonPath("content.[1]._links.self.href",
|
||||||
|
equalTo(generateActionLink(knownTargetId, action1.getId()))))
|
||||||
|
|
||||||
|
// verify action 0
|
||||||
|
.andExpect(jsonPath("content.[0].id", equalTo(action0.getId().intValue())))
|
||||||
|
.andExpect(jsonPath("content.[0].type", equalTo("cancel")))
|
||||||
|
.andExpect(jsonPath("content.[0].status", equalTo("pending")))
|
||||||
|
.andExpect(jsonPath("content.[1].detailStatus", equalTo("running")))
|
||||||
|
.andExpect(jsonPath("content.[0]._links.self.href",
|
||||||
|
equalTo(generateActionLink(knownTargetId, action0.getId()))))
|
||||||
|
|
||||||
|
// verify collection properties
|
||||||
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(2)))
|
||||||
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(2)))
|
||||||
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that a full representation of all actions is returned if the collection is requested for representation mode 'full'.")
|
||||||
|
void getActionsFullRepresentation() throws Exception {
|
||||||
|
final String knownTargetId = "targetId";
|
||||||
|
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
|
||||||
|
|
||||||
|
final Action action0 = actions.get(0);
|
||||||
|
final Action action1 = actions.get(1);
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING)
|
||||||
|
.param(MgmtRestConstants.REQUEST_PARAMETER_SORTING, "ID:ASC")
|
||||||
|
.param(MgmtRestConstants.REQUEST_PARAMETER_REPRESENTATION_MODE, MgmtRepresentationMode.FULL.toString()))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||||
|
|
||||||
|
// verify action 1
|
||||||
|
.andExpect(jsonPath("content.[1].id", equalTo(action1.getId().intValue())))
|
||||||
|
.andExpect(jsonPath("content.[1].type", equalTo("update")))
|
||||||
|
.andExpect(jsonPath("content.[1].status", equalTo("pending")))
|
||||||
|
.andExpect(jsonPath("content.[1].detailStatus", equalTo("running")))
|
||||||
|
.andExpect(jsonPath("content.[1]._links.self.href",
|
||||||
|
equalTo(generateActionLink(knownTargetId, action1.getId()))))
|
||||||
|
.andExpect(jsonPath("content.[1]._links.target.href", equalTo(generateTargetLink(knownTargetId))))
|
||||||
|
.andExpect(jsonPath("content.[1]._links.distributionset.href",
|
||||||
|
equalTo(generateDistributionSetLink(action1))))
|
||||||
|
|
||||||
|
// verify action 0
|
||||||
|
.andExpect(jsonPath("content.[0].id", equalTo(action0.getId().intValue())))
|
||||||
|
.andExpect(jsonPath("content.[0].type", equalTo("cancel")))
|
||||||
|
.andExpect(jsonPath("content.[0].status", equalTo("pending")))
|
||||||
|
.andExpect(jsonPath("content.[0].detailStatus", equalTo("canceling")))
|
||||||
|
.andExpect(jsonPath("content.[0]._links.self.href",
|
||||||
|
equalTo(generateActionLink(knownTargetId, action0.getId()))))
|
||||||
|
.andExpect(jsonPath("content.[0]._links.target.href", equalTo(generateTargetLink(knownTargetId))))
|
||||||
|
.andExpect(jsonPath("content.[0]._links.distributionset.href",
|
||||||
|
equalTo(generateDistributionSetLink(action0))))
|
||||||
|
|
||||||
|
// verify collection properties
|
||||||
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(2)))
|
||||||
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(2)))
|
||||||
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that the get request for actions returns an empty collection if no assignments have been done yet.")
|
||||||
|
void getActionsWithEmptyResult() throws Exception {
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING)).andDo(MockMvcResultPrinter.print())
|
||||||
|
.andExpect(status().isOk()).andExpect(jsonPath("size", equalTo(0)))
|
||||||
|
.andExpect(jsonPath("content", hasSize(0))).andExpect(jsonPath("total", equalTo(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies paging is respected as expected.")
|
||||||
|
void getMultipleActionsWithPagingLimitRequestParameter() throws Exception {
|
||||||
|
final String knownTargetId = "targetId";
|
||||||
|
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
|
||||||
|
|
||||||
|
// page 1: one entry
|
||||||
|
final Action action0 = actions.get(0);
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING)
|
||||||
|
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(1))
|
||||||
|
.param(MgmtRestConstants.REQUEST_PARAMETER_SORTING, "ID:ASC")).andDo(MockMvcResultPrinter.print())
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
|
||||||
|
// verify action 0
|
||||||
|
.andExpect(jsonPath("content.[0].id", equalTo(action0.getId().intValue())))
|
||||||
|
.andExpect(jsonPath("content.[0].type", equalTo("cancel")))
|
||||||
|
.andExpect(jsonPath("content.[0].status", equalTo("pending")))
|
||||||
|
.andExpect(jsonPath("content.[0].detailStatus", equalTo(action0.getStatus().toString().toLowerCase())))
|
||||||
|
.andExpect(jsonPath("content.[0]._links.self.href",
|
||||||
|
equalTo(generateActionLink(knownTargetId, action0.getId()))))
|
||||||
|
|
||||||
|
// verify collection properties
|
||||||
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(2)))
|
||||||
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(1)))
|
||||||
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(1)));
|
||||||
|
|
||||||
|
// page 2: one entry
|
||||||
|
final Action action1 = actions.get(1);
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING)
|
||||||
|
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(1))
|
||||||
|
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, String.valueOf(1))
|
||||||
|
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, String.valueOf(1))
|
||||||
|
.param(MgmtRestConstants.REQUEST_PARAMETER_SORTING, "ID:ASC")).andDo(MockMvcResultPrinter.print())
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
|
||||||
|
// verify action 1
|
||||||
|
.andExpect(jsonPath("content.[0].id", equalTo(action1.getId().intValue())))
|
||||||
|
.andExpect(jsonPath("content.[0].type", equalTo("update")))
|
||||||
|
.andExpect(jsonPath("content.[0].status", equalTo("pending")))
|
||||||
|
.andExpect(jsonPath("content.[0].detailStatus", equalTo(action1.getStatus().toString().toLowerCase())))
|
||||||
|
.andExpect(jsonPath("content.[0]._links.self.href",
|
||||||
|
equalTo(generateActionLink(knownTargetId, action1.getId()))))
|
||||||
|
|
||||||
|
// verify collection properties
|
||||||
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(2)))
|
||||||
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(1)))
|
||||||
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that the actions resource is read-only.")
|
||||||
|
void invalidRequestsOnActionResource() throws Exception {
|
||||||
|
final String knownTargetId = "targetId";
|
||||||
|
|
||||||
|
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
|
||||||
|
final Long actionId = actions.get(0).getId();
|
||||||
|
|
||||||
|
// ensure specific action cannot be accessed via the actions resource
|
||||||
|
mvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "/" + actionId))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
|
||||||
|
|
||||||
|
// not allowed methods
|
||||||
|
mvc.perform(post(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING)).andDo(MockMvcResultPrinter.print())
|
||||||
|
.andExpect(status().isMethodNotAllowed());
|
||||||
|
mvc.perform(put(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING)).andDo(MockMvcResultPrinter.print())
|
||||||
|
.andExpect(status().isMethodNotAllowed());
|
||||||
|
mvc.perform(delete(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING)).andDo(MockMvcResultPrinter.print())
|
||||||
|
.andExpect(status().isMethodNotAllowed());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Action> generateTargetWithTwoUpdatesWithOneOverride(final String knownTargetId) {
|
||||||
|
return generateTargetWithTwoUpdatesWithOneOverrideWithMaintenanceWindow(knownTargetId, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Action> generateTargetWithTwoUpdatesWithOneOverrideWithMaintenanceWindow(final String knownTargetId,
|
||||||
|
final String schedule, final String duration, final String timezone) {
|
||||||
|
final Target target = testdataFactory.createTarget(knownTargetId);
|
||||||
|
|
||||||
|
final Iterator<DistributionSet> sets = testdataFactory.createDistributionSets(2).iterator();
|
||||||
|
final DistributionSet one = sets.next();
|
||||||
|
final DistributionSet two = sets.next();
|
||||||
|
|
||||||
|
// Update
|
||||||
|
if (schedule == null) {
|
||||||
|
final List<Target> updatedTargets = assignDistributionSet(one, Collections.singletonList(target))
|
||||||
|
.getAssignedEntity().stream().map(Action::getTarget).collect(Collectors.toList());
|
||||||
|
// 2nd update
|
||||||
|
// sleep 10ms to ensure that we can sort by reportedAt
|
||||||
|
Awaitility.await().atMost(Duration.ONE_HUNDRED_MILLISECONDS).atLeast(5, TimeUnit.MILLISECONDS)
|
||||||
|
.pollInterval(10, TimeUnit.MILLISECONDS)
|
||||||
|
.until(() -> updatedTargets.stream().allMatch(t -> t.getLastModifiedAt() > 0L));
|
||||||
|
assignDistributionSet(two, updatedTargets);
|
||||||
|
} else {
|
||||||
|
final List<Target> updatedTargets = assignDistributionSetWithMaintenanceWindow(one.getId(),
|
||||||
|
target.getControllerId(), schedule, duration, timezone).getAssignedEntity().stream()
|
||||||
|
.map(Action::getTarget).collect(Collectors.toList());
|
||||||
|
// 2nd update
|
||||||
|
// sleep 10ms to ensure that we can sort by reportedAt
|
||||||
|
Awaitility.await().atMost(Duration.ONE_HUNDRED_MILLISECONDS).atLeast(5, TimeUnit.MILLISECONDS)
|
||||||
|
.pollInterval(10, TimeUnit.MILLISECONDS)
|
||||||
|
.until(() -> updatedTargets.stream().allMatch(t -> t.getLastModifiedAt() > 0L));
|
||||||
|
assignDistributionSetWithMaintenanceWindow(two.getId(), updatedTargets.get(0).getControllerId(), schedule,
|
||||||
|
duration, timezone);
|
||||||
|
}
|
||||||
|
|
||||||
|
// two updates, one cancellation
|
||||||
|
final List<Action> actions = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE)
|
||||||
|
.getContent();
|
||||||
|
|
||||||
|
assertThat(actions).hasSize(2);
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String generateActionLink(final String targetId, final Long actionId) {
|
||||||
|
return "http://localhost" + MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + targetId + "/"
|
||||||
|
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/" + actionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String generateTargetLink(final String targetId) {
|
||||||
|
return "http://localhost" + MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + targetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String generateDistributionSetLink(final Action action) {
|
||||||
|
return "http://localhost" + MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/"
|
||||||
|
+ action.getDistributionSet().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
:doctype: book
|
||||||
|
:icons: font
|
||||||
|
:source-highlighter: highlightjs
|
||||||
|
:toc: macro
|
||||||
|
:toclevels: 1
|
||||||
|
:sectlinks:
|
||||||
|
:linkattrs:
|
||||||
|
|
||||||
|
[[actions]]
|
||||||
|
= Actions
|
||||||
|
|
||||||
|
toc::[]
|
||||||
|
|
||||||
|
== GET /rest/v1/actions
|
||||||
|
|
||||||
|
=== Implementation notes
|
||||||
|
|
||||||
|
Handles the GET request of retrieving all actions within Hawkbit. Required permission: READ_TARGET
|
||||||
|
|
||||||
|
=== Get paged list of actions
|
||||||
|
|
||||||
|
==== CURL
|
||||||
|
|
||||||
|
include::{snippets}/actions/get-actions/curl-request.adoc[]
|
||||||
|
|
||||||
|
==== Request URL
|
||||||
|
|
||||||
|
A `GET` request is used to access the actions
|
||||||
|
|
||||||
|
include::{snippets}/actions/get-actions/http-request.adoc[]
|
||||||
|
|
||||||
|
==== Request query parameter
|
||||||
|
|
||||||
|
include::{snippets}/actions/get-actions-with-parameters/request-parameters.adoc[]
|
||||||
|
|
||||||
|
==== Request parameter example
|
||||||
|
|
||||||
|
include::{snippets}/actions/get-actions-with-parameters/http-request.adoc[]
|
||||||
|
|
||||||
|
=== Response (Status 200)
|
||||||
|
|
||||||
|
==== Response fields
|
||||||
|
|
||||||
|
include::{snippets}/actions/get-actions/response-fields.adoc[]
|
||||||
|
|
||||||
|
==== Response example
|
||||||
|
|
||||||
|
include::{snippets}/actions/get-actions/http-response.adoc[]
|
||||||
|
|
||||||
|
==== Response example for representation mode 'full'
|
||||||
|
|
||||||
|
include::{snippets}/actions/get-actions-with-parameters/http-response.adoc[]
|
||||||
|
|
||||||
|
|
||||||
|
=== Error responses
|
||||||
|
|
||||||
|
|===
|
||||||
|
| HTTP Status Code | Reason | Response Model
|
||||||
|
|
||||||
|
include::../errors/400.adoc[]
|
||||||
|
include::../errors/401.adoc[]
|
||||||
|
include::../errors/403.adoc[]
|
||||||
|
include::../errors/405.adoc[]
|
||||||
|
include::../errors/406.adoc[]
|
||||||
|
include::../errors/429.adoc[]
|
||||||
|
|===
|
||||||
|
|
||||||
|
== Additional content
|
||||||
|
|
||||||
|
[[error-body]]
|
||||||
|
=== Error body
|
||||||
|
|
||||||
|
include::../errors/error-response-body.adoc[]
|
||||||
@@ -33,6 +33,7 @@ public final class MgmtApiModelProperties {
|
|||||||
public static final String LINK_TO_OPTIONAL_SMT = "Link to optional software modules types in this distribution set type.";
|
public static final String LINK_TO_OPTIONAL_SMT = "Link to optional software modules types in this distribution set type.";
|
||||||
public static final String LINK_TO_ROLLOUT = "The link to the rollout.";
|
public static final String LINK_TO_ROLLOUT = "The link to the rollout.";
|
||||||
public static final String LINK_TO_TARGET_TYPE = "The link to the target type.";
|
public static final String LINK_TO_TARGET_TYPE = "The link to the target type.";
|
||||||
|
public static final String LINK_TO_TARGET = "The link to the target.";
|
||||||
|
|
||||||
// software module types
|
// software module types
|
||||||
public static final String SMT_TYPE = "The type of the software module identified by its key.";
|
public static final String SMT_TYPE = "The type of the software module identified by its key.";
|
||||||
@@ -158,6 +159,8 @@ public final class MgmtApiModelProperties {
|
|||||||
|
|
||||||
public static final String ACTION_EXECUTION_STATUS = "Status of action.";
|
public static final String ACTION_EXECUTION_STATUS = "Status of action.";
|
||||||
|
|
||||||
|
public static final String ACTION_DETAIL_STATUS = "Detailed status of action.";
|
||||||
|
|
||||||
public static final String ACTION_LIST = "List of actions.";
|
public static final String ACTION_LIST = "List of actions.";
|
||||||
|
|
||||||
public static final String ACTION_WEIGHT = "Weight of the action showing the importance of the update.";
|
public static final String ACTION_WEIGHT = "Weight of the action showing the importance of the update.";
|
||||||
@@ -245,4 +248,7 @@ public final class MgmtApiModelProperties {
|
|||||||
public static final String CONFIG_PARAM = "The name of the configuration parameter.";
|
public static final String CONFIG_PARAM = "The name of the configuration parameter.";
|
||||||
|
|
||||||
public static final String DS_NEW_ASSIGNED_ACTIONS = "The newly created actions as a result of this assignment";
|
public static final String DS_NEW_ASSIGNED_ACTIONS = "The newly created actions as a result of this assignment";
|
||||||
|
|
||||||
|
public static final String REPRESENTATION_MODE = "The representation mode. Can be \"full\" or \"compact\". Defaults to \"compact\"";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2018 Bosch Software Innovations GmbH and others.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.rest.mgmt.documentation;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
|
||||||
|
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||||
|
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
|
||||||
|
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
|
||||||
|
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||||
|
import org.eclipse.hawkbit.repository.ActionStatusFields;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
|
import org.eclipse.hawkbit.rest.documentation.AbstractApiRestDocumentation;
|
||||||
|
import org.eclipse.hawkbit.rest.documentation.ApiModelPropertiesGeneric;
|
||||||
|
import org.eclipse.hawkbit.rest.documentation.MgmtApiModelProperties;
|
||||||
|
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Sort.Direction;
|
||||||
|
import org.springframework.restdocs.payload.JsonFieldType;
|
||||||
|
|
||||||
|
import io.qameta.allure.Description;
|
||||||
|
import io.qameta.allure.Feature;
|
||||||
|
import io.qameta.allure.Story;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Documentation generation for Management API for {@link Action}.
|
||||||
|
*/
|
||||||
|
@Feature("Spring Rest Docs Tests - Action")
|
||||||
|
@Story("Action Resource")
|
||||||
|
public class ActionResourceDocumentationTest extends AbstractApiRestDocumentation {
|
||||||
|
|
||||||
|
private final String targetId = "target137";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getResourceName() {
|
||||||
|
return "actions";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Handles the GET request of retrieving all actions. Required Permission: READ_TARGET.")
|
||||||
|
public void getActions() throws Exception {
|
||||||
|
enableMultiAssignments();
|
||||||
|
generateRolloutActionForTarget(targetId);
|
||||||
|
|
||||||
|
mockMvc.perform(get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING)).andExpect(status().isOk())
|
||||||
|
.andDo(MockMvcResultPrinter.print())
|
||||||
|
.andDo(this.document.document(responseFields(
|
||||||
|
fieldWithPath("size").type(JsonFieldType.NUMBER).description(ApiModelPropertiesGeneric.SIZE),
|
||||||
|
fieldWithPath("total").description(ApiModelPropertiesGeneric.TOTAL_ELEMENTS),
|
||||||
|
fieldWithPath("content[]").description(MgmtApiModelProperties.ACTION_LIST),
|
||||||
|
fieldWithPath("content[].createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
||||||
|
fieldWithPath("content[].createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
|
||||||
|
fieldWithPath("content[].lastModifiedBy")
|
||||||
|
.description(ApiModelPropertiesGeneric.LAST_MODIFIED_BY).type("String"),
|
||||||
|
fieldWithPath("content[].lastModifiedAt")
|
||||||
|
.description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT).type("String"),
|
||||||
|
fieldWithPath("content[].type").description(MgmtApiModelProperties.ACTION_TYPE)
|
||||||
|
.attributes(key("value").value("['update', 'cancel']")),
|
||||||
|
|
||||||
|
fieldWithPath("content[].status").description(MgmtApiModelProperties.ACTION_EXECUTION_STATUS)
|
||||||
|
.attributes(key("value").value("['finished', 'pending']")),
|
||||||
|
fieldWithPath("content[].detailStatus").description(MgmtApiModelProperties.ACTION_DETAIL_STATUS)
|
||||||
|
.attributes(key("value").value(
|
||||||
|
"['finished', 'error', 'running', 'warning', 'scheduled', 'canceling', 'canceled', 'download', 'downloaded', 'retrieved', 'cancel_rejected']")),
|
||||||
|
fieldWithPath("content[]._links").description(MgmtApiModelProperties.LINK_TO_ACTION),
|
||||||
|
fieldWithPath("content[].id").description(MgmtApiModelProperties.ACTION_ID),
|
||||||
|
fieldWithPath("content[].weight").description(MgmtApiModelProperties.ACTION_WEIGHT),
|
||||||
|
fieldWithPath("content[].rollout").description(MgmtApiModelProperties.ACTION_ROLLOUT),
|
||||||
|
fieldWithPath("content[].rolloutName")
|
||||||
|
.description(MgmtApiModelProperties.ACTION_ROLLOUT_NAME))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Handles the GET request of retrieving all actions based on parameters. Required Permission: READ_TARGET.")
|
||||||
|
public void getActionsWithParameters() throws Exception {
|
||||||
|
final Action action = generateRolloutActionForTarget(targetId);
|
||||||
|
|
||||||
|
mockMvc.perform(
|
||||||
|
get(MgmtRestConstants.ACTION_V1_REQUEST_MAPPING + "?limit=10&sort=id:ASC&offset=0&q=rollout.id=="
|
||||||
|
+ action.getRollout().getId() + "&representation=full"))
|
||||||
|
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
|
||||||
|
.andDo(this.document.document(requestParameters(
|
||||||
|
parameterWithName("limit").attributes(key("type").value("query"))
|
||||||
|
.description(ApiModelPropertiesGeneric.LIMIT),
|
||||||
|
parameterWithName("sort").description(ApiModelPropertiesGeneric.SORT),
|
||||||
|
parameterWithName("offset").description(ApiModelPropertiesGeneric.OFFSET),
|
||||||
|
parameterWithName("q").description(ApiModelPropertiesGeneric.FIQL),
|
||||||
|
parameterWithName("representation").description(MgmtApiModelProperties.REPRESENTATION_MODE))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Action generateRolloutActionForTarget(final String knownControllerId) throws Exception {
|
||||||
|
return generateActionForTarget(knownControllerId, true, false, null, null, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Action generateActionForTarget(final String knownControllerId, final boolean inSync,
|
||||||
|
final boolean timeforced, final String maintenanceWindowSchedule, final String maintenanceWindowDuration,
|
||||||
|
final String maintenanceWindowTimeZone, final boolean createRollout) throws Exception {
|
||||||
|
final PageRequest pageRequest = PageRequest.of(0, 1, Direction.ASC, ActionStatusFields.ID.getFieldName());
|
||||||
|
|
||||||
|
createTargetByGivenNameWithAttributes(knownControllerId, inSync, timeforced, createDistributionSet(),
|
||||||
|
maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone, createRollout);
|
||||||
|
|
||||||
|
final List<Action> actions = deploymentManagement.findActionsAll(pageRequest).getContent();
|
||||||
|
|
||||||
|
assertThat(actions).hasSize(1);
|
||||||
|
return actions.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -155,6 +155,8 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
|
|||||||
.description(MgmtApiModelProperties.ROLLOUT_LINKS_APPROVE));
|
.description(MgmtApiModelProperties.ROLLOUT_LINKS_APPROVE));
|
||||||
allFieldDescriptor.add(
|
allFieldDescriptor.add(
|
||||||
fieldWithPath(arrayPrefix + "_links.deny").description(MgmtApiModelProperties.ROLLOUT_LINKS_DENY));
|
fieldWithPath(arrayPrefix + "_links.deny").description(MgmtApiModelProperties.ROLLOUT_LINKS_DENY));
|
||||||
|
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "_links.distributionset")
|
||||||
|
.description(MgmtApiModelProperties.LINK_TO_DS));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DocumenationResponseFieldsSnippet(allFieldDescriptor);
|
return new DocumenationResponseFieldsSnippet(allFieldDescriptor);
|
||||||
|
|||||||
@@ -237,6 +237,10 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
|
|||||||
fieldWithPath("content[].status")
|
fieldWithPath("content[].status")
|
||||||
.description(MgmtApiModelProperties.ACTION_EXECUTION_STATUS)
|
.description(MgmtApiModelProperties.ACTION_EXECUTION_STATUS)
|
||||||
.attributes(key("value").value("['finished', 'pending']")),
|
.attributes(key("value").value("['finished', 'pending']")),
|
||||||
|
fieldWithPath("content[].detailStatus")
|
||||||
|
.description(MgmtApiModelProperties.ACTION_DETAIL_STATUS)
|
||||||
|
.attributes(key("value").value(
|
||||||
|
"['finished', 'error', 'running', 'warning', 'scheduled', 'canceling', 'canceled', 'download', 'downloaded', 'retrieved', 'cancel_rejected']")),
|
||||||
fieldWithPath("content[]._links").description(MgmtApiModelProperties.LINK_TO_ACTION),
|
fieldWithPath("content[]._links").description(MgmtApiModelProperties.LINK_TO_ACTION),
|
||||||
fieldWithPath("content[].id").description(MgmtApiModelProperties.ACTION_ID),
|
fieldWithPath("content[].id").description(MgmtApiModelProperties.ACTION_ID),
|
||||||
fieldWithPath("content[].weight").description(MgmtApiModelProperties.ACTION_WEIGHT),
|
fieldWithPath("content[].weight").description(MgmtApiModelProperties.ACTION_WEIGHT),
|
||||||
@@ -270,11 +274,15 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
|
|||||||
.description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT).type("String"),
|
.description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT).type("String"),
|
||||||
fieldWithPath("content[].type").description(MgmtApiModelProperties.ACTION_TYPE)
|
fieldWithPath("content[].type").description(MgmtApiModelProperties.ACTION_TYPE)
|
||||||
.attributes(key("value").value("['update', 'cancel']")),
|
.attributes(key("value").value("['update', 'cancel']")),
|
||||||
|
|
||||||
fieldWithPath("content[].status")
|
fieldWithPath("content[].status")
|
||||||
.description(MgmtApiModelProperties.ACTION_EXECUTION_STATUS)
|
.description(MgmtApiModelProperties.ACTION_EXECUTION_STATUS)
|
||||||
.attributes(key("value").value("['finished', 'pending']")),
|
.attributes(key("value").value("['finished', 'pending']")),
|
||||||
fieldWithPath("content[]._links").description(MgmtApiModelProperties.LINK_TO_ACTION),
|
fieldWithPath("content[].detailStatus")
|
||||||
|
.description(MgmtApiModelProperties.ACTION_DETAIL_STATUS)
|
||||||
|
.attributes(key("value").value(
|
||||||
|
"['finished', 'error', 'running', 'warning', 'scheduled', 'canceling', 'canceled', 'download', 'downloaded', 'retrieved', 'cancel_rejected']")),
|
||||||
|
fieldWithPath("content[]._links.self")
|
||||||
|
.description(MgmtApiModelProperties.LINK_TO_ACTION),
|
||||||
fieldWithPath("content[].id").description(MgmtApiModelProperties.ACTION_ID),
|
fieldWithPath("content[].id").description(MgmtApiModelProperties.ACTION_ID),
|
||||||
fieldWithPath("content[].weight").description(MgmtApiModelProperties.ACTION_WEIGHT),
|
fieldWithPath("content[].weight").description(MgmtApiModelProperties.ACTION_WEIGHT),
|
||||||
fieldWithPath("content[].maintenanceWindow")
|
fieldWithPath("content[].maintenanceWindow")
|
||||||
@@ -363,13 +371,17 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
|
|||||||
.type("String"),
|
.type("String"),
|
||||||
fieldWithPath("status").description(MgmtApiModelProperties.ACTION_EXECUTION_STATUS)
|
fieldWithPath("status").description(MgmtApiModelProperties.ACTION_EXECUTION_STATUS)
|
||||||
.attributes(key("value").value("['finished', 'pending']")),
|
.attributes(key("value").value("['finished', 'pending']")),
|
||||||
|
fieldWithPath("detailStatus").description(MgmtApiModelProperties.ACTION_DETAIL_STATUS)
|
||||||
|
.attributes(key("value").value(
|
||||||
|
"['finished', 'error', 'running', 'warning', 'scheduled', 'canceling', 'canceled', 'download', 'downloaded', 'retrieved', 'cancel_rejected']")),
|
||||||
fieldWithPath("rollout").description(MgmtApiModelProperties.ACTION_ROLLOUT),
|
fieldWithPath("rollout").description(MgmtApiModelProperties.ACTION_ROLLOUT),
|
||||||
fieldWithPath("rolloutName").description(MgmtApiModelProperties.ACTION_ROLLOUT_NAME),
|
fieldWithPath("rolloutName").description(MgmtApiModelProperties.ACTION_ROLLOUT_NAME),
|
||||||
fieldWithPath("_links.self").ignored(),
|
fieldWithPath("_links.self").ignored(),
|
||||||
fieldWithPath("_links.distributionset").description(MgmtApiModelProperties.LINK_TO_DS),
|
fieldWithPath("_links.distributionset").description(MgmtApiModelProperties.LINK_TO_DS),
|
||||||
fieldWithPath("_links.status")
|
fieldWithPath("_links.status")
|
||||||
.description(MgmtApiModelProperties.LINKS_ACTION_STATUSES),
|
.description(MgmtApiModelProperties.LINKS_ACTION_STATUSES),
|
||||||
fieldWithPath("_links.rollout").description(MgmtApiModelProperties.LINK_TO_ROLLOUT))));
|
fieldWithPath("_links.rollout").description(MgmtApiModelProperties.LINK_TO_ROLLOUT),
|
||||||
|
fieldWithPath("_links.target").description(MgmtApiModelProperties.LINK_TO_TARGET))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -401,6 +413,9 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
|
|||||||
.type("String"),
|
.type("String"),
|
||||||
fieldWithPath("status").description(MgmtApiModelProperties.ACTION_EXECUTION_STATUS)
|
fieldWithPath("status").description(MgmtApiModelProperties.ACTION_EXECUTION_STATUS)
|
||||||
.attributes(key("value").value("['finished', 'pending']")),
|
.attributes(key("value").value("['finished', 'pending']")),
|
||||||
|
fieldWithPath("detailStatus").description(MgmtApiModelProperties.ACTION_DETAIL_STATUS)
|
||||||
|
.attributes(key("value").value(
|
||||||
|
"['finished', 'error', 'running', 'warning', 'scheduled', 'canceling', 'canceled', 'download', 'downloaded', 'retrieved', 'cancel_rejected']")),
|
||||||
fieldWithPath("maintenanceWindow")
|
fieldWithPath("maintenanceWindow")
|
||||||
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW),
|
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW),
|
||||||
fieldWithPath("maintenanceWindow.schedule")
|
fieldWithPath("maintenanceWindow.schedule")
|
||||||
@@ -414,7 +429,8 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
|
|||||||
fieldWithPath("_links.self").ignored(),
|
fieldWithPath("_links.self").ignored(),
|
||||||
fieldWithPath("_links.distributionset").description(MgmtApiModelProperties.LINK_TO_DS),
|
fieldWithPath("_links.distributionset").description(MgmtApiModelProperties.LINK_TO_DS),
|
||||||
fieldWithPath("_links.status")
|
fieldWithPath("_links.status")
|
||||||
.description(MgmtApiModelProperties.LINKS_ACTION_STATUSES))));
|
.description(MgmtApiModelProperties.LINKS_ACTION_STATUSES),
|
||||||
|
fieldWithPath("_links.target").description(MgmtApiModelProperties.LINK_TO_TARGET))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -451,10 +467,14 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
|
|||||||
.attributes(key("value").value("['forced', 'soft', 'timeforced']")),
|
.attributes(key("value").value("['forced', 'soft', 'timeforced']")),
|
||||||
fieldWithPath("status").description(MgmtApiModelProperties.ACTION_EXECUTION_STATUS)
|
fieldWithPath("status").description(MgmtApiModelProperties.ACTION_EXECUTION_STATUS)
|
||||||
.attributes(key("value").value("['finished', 'pending']")),
|
.attributes(key("value").value("['finished', 'pending']")),
|
||||||
|
fieldWithPath("detailStatus").description(MgmtApiModelProperties.ACTION_DETAIL_STATUS)
|
||||||
|
.attributes(key("value").value(
|
||||||
|
"['finished', 'error', 'running', 'warning', 'scheduled', 'canceling', 'canceled', 'download', 'downloaded', 'retrieved', 'cancel_rejected']")),
|
||||||
fieldWithPath("_links.self").ignored(),
|
fieldWithPath("_links.self").ignored(),
|
||||||
fieldWithPath("_links.distributionset").description(MgmtApiModelProperties.LINK_TO_DS),
|
fieldWithPath("_links.distributionset").description(MgmtApiModelProperties.LINK_TO_DS),
|
||||||
fieldWithPath("_links.status")
|
fieldWithPath("_links.status")
|
||||||
.description(MgmtApiModelProperties.LINKS_ACTION_STATUSES))));
|
.description(MgmtApiModelProperties.LINKS_ACTION_STATUSES),
|
||||||
|
fieldWithPath("_links.target").description(MgmtApiModelProperties.LINK_TO_TARGET))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user