Support user consent flow (#1293)
* Introduce user consent flow * Add permissions to confirmation management * rename from consent to confirmation * Reformat code. Remove unused imports. Change and add permission checks when configuring auto-confirmation. * Do not include null values for DDI confirmation base endpoint * fix confirmation required checkbox id * Remove unused import. Fix consume/produce type of new API's. * Change term processing to proceeding when activating user consent flow * Align formatting and extend integration test cases for DMF and DDI. * Extend DMF test cases to consider auto-confirmation * Refactor action management to fix problem of handling action status updates on closed actions. * remove unsupported validation * use new confirmation api for DMF. Extend test cases., * Remove unnecessary fields. * Extend API documentation for DDI and MGMT API. * adapt ddi api docs adoc file * Fixed the duplicate migration version for db files * fix method to support confirmation * Fixed PR comments * Addressed PR comments * Fixed after merge compilation issue * Fixed after merge compilation issue * Fix failing tests in MgmtRolloutResourceTest * Fixed the permissions issue reflected by integration tests * Added back the missing line of code lost during merge * Fix the failing test on Jenkins Signed-off-by: Stanislav Trailov <stanislav.trailov@bosch.io> Signed-off-by: Dimitar Shterev <dimitar.shterev@bosch.io> Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io> Signed-off-by: Shruthi Manavalli Ramanna <shruthimanavalli.ramanna@bosch-si.com> Co-authored-by: Shruthi Manavalli Ramanna <shruthimanavalli.ramanna@bosch-si.com>
This commit is contained in:
@@ -19,8 +19,9 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
|
||||
/**
|
||||
* Provide action history information to the controller as part of response to
|
||||
* {@link DdiRootControllerRestApi#getControllerBasedeploymentAction}: 1.
|
||||
* Current action status at the server; 2. List of messages from action history
|
||||
* {@link DdiRootControllerRestApi#getControllerBasedeploymentAction} and
|
||||
* {@link DdiRootControllerRestApi#getControllerBaseconfirmationAction}:
|
||||
* 1. Current action status at the server; 2. List of messages from action history
|
||||
* that were sent to server earlier by the controller using
|
||||
* {@link DdiActionFeedback}.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* 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.ddi.json.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class DdiActivateAutoConfirmation {
|
||||
|
||||
@JsonProperty(required = false)
|
||||
private final String initiator;
|
||||
|
||||
@JsonProperty(required = false)
|
||||
private final String remark;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param initiator
|
||||
* can be null
|
||||
* @param remark
|
||||
* can be null
|
||||
*/
|
||||
@JsonCreator
|
||||
public DdiActivateAutoConfirmation(@JsonProperty(value = "initiator") final String initiator,
|
||||
@JsonProperty(value = "remark") final String remark) {
|
||||
this.initiator = initiator;
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DdiActivateAutoConfirmation [initiator=" + initiator + ", remark=" + remark + ", toString()="
|
||||
+ super.toString() + "]";
|
||||
}
|
||||
|
||||
public String getInitiator() {
|
||||
return initiator;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 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.ddi.json.model;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import org.springframework.hateoas.RepresentationModel;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonPropertyOrder({ "active", "initiator", "remark", "activatedAt" })
|
||||
public class DdiAutoConfirmationState extends RepresentationModel<DdiAutoConfirmationState> {
|
||||
|
||||
@NotNull
|
||||
private boolean active;
|
||||
private String initiator;
|
||||
private String remark;
|
||||
|
||||
private Long activatedAt;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public DdiAutoConfirmationState() {
|
||||
// needed for json create.
|
||||
}
|
||||
|
||||
public static DdiAutoConfirmationState active(final long activatedAt) {
|
||||
final DdiAutoConfirmationState state = new DdiAutoConfirmationState();
|
||||
state.setActive(true);
|
||||
state.setActivatedAt(activatedAt);
|
||||
return state;
|
||||
}
|
||||
|
||||
public static DdiAutoConfirmationState disabled() {
|
||||
return new DdiAutoConfirmationState();
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setActive(final boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
public Long getActivatedAt() {
|
||||
return activatedAt;
|
||||
}
|
||||
|
||||
public void setActivatedAt(final long activatedAt) {
|
||||
this.activatedAt = activatedAt;
|
||||
}
|
||||
|
||||
public String getInitiator() {
|
||||
return initiator;
|
||||
}
|
||||
|
||||
public void setInitiator(final String initiator) {
|
||||
this.initiator = initiator;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(final String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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.ddi.json.model;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import org.springframework.hateoas.RepresentationModel;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* Confirmation base response.
|
||||
* Set order to place links at last.
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonPropertyOrder({ "autoConfirm" })
|
||||
public class DdiConfirmationBase extends RepresentationModel<DdiConfirmationBase> {
|
||||
|
||||
@JsonProperty("autoConfirm")
|
||||
@NotNull
|
||||
private DdiAutoConfirmationState autoConfirm;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public DdiConfirmationBase() {
|
||||
// needed for json create.
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param autoConfirmState
|
||||
*/
|
||||
public DdiConfirmationBase(final DdiAutoConfirmationState autoConfirmState) {
|
||||
this.autoConfirm = autoConfirmState;
|
||||
}
|
||||
|
||||
public DdiAutoConfirmationState getAutoConfirm() {
|
||||
return autoConfirm;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* 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.ddi.json.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import org.springframework.hateoas.RepresentationModel;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Update action resource.
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonPropertyOrder({ "id", "confirmation", "actionHistory" })
|
||||
public class DdiConfirmationBaseAction extends RepresentationModel<DdiConfirmationBaseAction> {
|
||||
|
||||
@JsonProperty("id")
|
||||
@NotNull
|
||||
private String id;
|
||||
|
||||
@JsonProperty("confirmation")
|
||||
@NotNull
|
||||
private DdiDeployment confirmation;
|
||||
|
||||
/**
|
||||
* Action history containing current action status and a list of feedback
|
||||
* messages received earlier from the controller.
|
||||
*/
|
||||
@JsonProperty("actionHistory")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
private DdiActionHistory actionHistory;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public DdiConfirmationBaseAction() {
|
||||
// needed for json create.
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param id
|
||||
* of the update action
|
||||
* @param confirmation
|
||||
* chunk details
|
||||
* @param actionHistory
|
||||
* containing current action status and a list of feedback messages
|
||||
* received earlier from the controller.
|
||||
*/
|
||||
public DdiConfirmationBaseAction(final String id, final DdiDeployment confirmation,
|
||||
final DdiActionHistory actionHistory) {
|
||||
this.id = id;
|
||||
this.confirmation = confirmation;
|
||||
this.actionHistory = actionHistory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the confirmation data containing chunks needed to confirm a action.
|
||||
*
|
||||
* @return {@link DdiDeployment}
|
||||
*/
|
||||
public DdiDeployment getConfirmation() {
|
||||
return confirmation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action history containing current action status and a list of
|
||||
* feedback messages received earlier from the controller.
|
||||
*
|
||||
* @return {@link DdiActionHistory}
|
||||
*/
|
||||
public DdiActionHistory getActionHistory() {
|
||||
return actionHistory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ConfirmationBase [id=" + id + ", confirmation=" + confirmation + " actionHistory=" + actionHistory
|
||||
+ "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (this.getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final DdiConfirmationBaseAction oBase = (DdiConfirmationBaseAction) obj;
|
||||
return Objects.equals(this.id, oBase.id) && Objects.equals(this.confirmation, oBase.confirmation)
|
||||
&& Objects.equals(this.actionHistory, oBase.actionHistory);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* 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.ddi.json.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* New update actions require confirmation when confirmation flow is switched on. This is the feedback channel for
|
||||
* confirmation messages for DDI API. The confirmation message has a mandatory field confirmation with possible values:
|
||||
* "confirmed" and "denied".
|
||||
*
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class DdiConfirmationFeedback {
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
private final Confirmation confirmation;
|
||||
|
||||
private final Integer code;
|
||||
|
||||
private final List<String> details;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs an confirmation-feedback
|
||||
*
|
||||
* @param confirmation confirmation value for the action. Valid values are "Confirmed" and "Denied
|
||||
* @param code code for confirmation
|
||||
* @param details messages
|
||||
*/
|
||||
@JsonCreator public DdiConfirmationFeedback(
|
||||
@JsonProperty(value = "confirmation", required = true) final Confirmation confirmation,
|
||||
@JsonProperty(value = "code") final Integer code,
|
||||
@JsonProperty(value = "details") final List<String> details) {
|
||||
this.confirmation = confirmation;
|
||||
this.code = code;
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
public Confirmation getConfirmation() {
|
||||
return confirmation;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public List<String> getDetails() {
|
||||
if (details == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.unmodifiableList(details);
|
||||
}
|
||||
|
||||
public enum Confirmation {
|
||||
/**
|
||||
* Confirm the action.
|
||||
*/
|
||||
CONFIRMED("confirmed"),
|
||||
|
||||
/**
|
||||
* Deny the action.
|
||||
*/
|
||||
DENIED("denied");
|
||||
|
||||
|
||||
private final String name;
|
||||
|
||||
Confirmation(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@JsonValue
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DdiConfirmationFeedback [confirmation=" + confirmation + ", code=" + code + ", details=" + details +"]";
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,21 @@ public final class DdiRestConstants {
|
||||
*/
|
||||
public static final String DEPLOYMENT_BASE_ACTION = "deploymentBase";
|
||||
|
||||
/**
|
||||
* Confirmation base resource.
|
||||
*/
|
||||
public static final String CONFIRMATION_BASE = "confirmationBase";
|
||||
|
||||
/**
|
||||
* Activate auto-confirm
|
||||
*/
|
||||
public static final String AUTO_CONFIRM_ACTIVATE = "activateAutoConfirm";
|
||||
|
||||
/**
|
||||
* Deactivate auto-confirm
|
||||
*/
|
||||
public static final String AUTO_CONFIRM_DEACTIVATE = "deactivateAutoConfirm";
|
||||
|
||||
/**
|
||||
* Installed action resources.
|
||||
*/
|
||||
@@ -52,6 +67,7 @@ public final class DdiRestConstants {
|
||||
* Default value specifying that no action history to be sent as part of
|
||||
* response to deploymentBase
|
||||
* {@link DdiRootControllerRestApi#getControllerBasedeploymentAction}.
|
||||
* {@link DdiRootControllerRestApi#getConfirmationBaseAction}.
|
||||
*/
|
||||
public static final String NO_ACTION_HISTORY = "0";
|
||||
|
||||
|
||||
@@ -16,9 +16,14 @@ import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiActionFeedback;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiActivateAutoConfirmation;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiArtifact;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiAutoConfirmationState;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiCancel;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiConfigData;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiConfirmationBase;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiConfirmationBaseAction;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiConfirmationFeedback;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiControllerBase;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiDeploymentBase;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
@@ -29,6 +34,7 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@@ -123,17 +129,16 @@ public interface DdiRootControllerRestApi {
|
||||
* @param controllerId
|
||||
* of the target
|
||||
* @param actionId
|
||||
* of the {@link DdiDeploymentBase} that matches to active
|
||||
* actions.
|
||||
* of the {@link DdiDeploymentBase} that matches to active actions.
|
||||
* @param resource
|
||||
* an hashcode of the resource which indicates if the action has
|
||||
* been changed, e.g. from 'soft' to 'force' and the eTag needs
|
||||
* to be re-generated
|
||||
* an hashcode of the resource which indicates if the action has been
|
||||
* changed, e.g. from 'soft' to 'force' and the eTag needs to be
|
||||
* re-generated
|
||||
* @param actionHistoryMessageCount
|
||||
* specifies the number of messages to be returned from action
|
||||
* history. Regardless of the passed value, in order to restrict
|
||||
* resource utilization by controllers, maximum number of
|
||||
* messages that are retrieved from database is limited by
|
||||
* resource utilization by controllers, maximum number of messages
|
||||
* that are retrieved from database is limited by
|
||||
* {@link RepositoryConstants#MAX_ACTION_HISTORY_MSG_COUNT}.
|
||||
*
|
||||
* actionHistoryMessageCount less than zero: retrieves the maximum
|
||||
@@ -212,8 +217,8 @@ public interface DdiRootControllerRestApi {
|
||||
@PathVariable("actionId") @NotEmpty final Long actionId);
|
||||
|
||||
/**
|
||||
* RequestMethod.POST method receiving the {@link DdiActionFeedback} from
|
||||
* the target.
|
||||
* RequestMethod.POST method receiving the {@link DdiActionFeedback} from the
|
||||
* target.
|
||||
*
|
||||
* @param feedback
|
||||
* the {@link DdiActionFeedback} from the target.
|
||||
@@ -270,4 +275,119 @@ public interface DdiRootControllerRestApi {
|
||||
@PathVariable("actionId") @NotEmpty final Long actionId,
|
||||
@RequestParam(value = "actionHistory", defaultValue = DdiRestConstants.NO_ACTION_HISTORY) final Integer actionHistoryMessageCount);
|
||||
|
||||
/**
|
||||
* Returns the confirmation base with the current auto-confirmation state for a
|
||||
* given controllerId and toggle links. In case there are actions present where
|
||||
* the confirmation is required, a reference to it will be returned as well.
|
||||
*
|
||||
* @param tenant
|
||||
* the controllerId is corresponding too
|
||||
* @param controllerId
|
||||
* to check the state for
|
||||
* @return the state as {@link DdiAutoConfirmationState}
|
||||
*/
|
||||
@GetMapping(value = "/{controllerId}/" + DdiRestConstants.CONFIRMATION_BASE, produces = {
|
||||
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE, DdiRestConstants.MEDIA_TYPE_CBOR })
|
||||
ResponseEntity<DdiConfirmationBase> getConfirmationBase(@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("controllerId") @NotEmpty final String controllerId);
|
||||
|
||||
/**
|
||||
* Resource for confirmation of an action.
|
||||
*
|
||||
* @param tenant
|
||||
* of the request
|
||||
* @param controllerId
|
||||
* of the target
|
||||
* @param actionId
|
||||
* of the {@link DdiConfirmationBaseAction} that matches to active
|
||||
* actions in WAITING_FOR_CONFIRMATION status.
|
||||
* @param resource
|
||||
* an hashcode of the resource which indicates if the action has been
|
||||
* changed, e.g. from 'soft' to 'force' and the eTag needs to be
|
||||
* re-generated
|
||||
* @param actionHistoryMessageCount
|
||||
* specifies the number of messages to be returned from action
|
||||
* history. Regardless of the passed value, in order to restrict
|
||||
* resource utilization by controllers, maximum number of messages
|
||||
* that are retrieved from database is limited by
|
||||
* {@link RepositoryConstants#MAX_ACTION_HISTORY_MSG_COUNT}.
|
||||
*
|
||||
* actionHistoryMessageCount less than zero: retrieves the maximum
|
||||
* allowed number of action status messages from history;
|
||||
*
|
||||
* actionHistoryMessageCount equal to zero: does not retrieve any
|
||||
* message;
|
||||
*
|
||||
* actionHistoryMessageCount greater than zero: retrieves the
|
||||
* specified number of messages, limited by maximum allowed number.
|
||||
*
|
||||
* @return the response
|
||||
*/
|
||||
@GetMapping(value = "/{controllerId}/" + DdiRestConstants.CONFIRMATION_BASE + "/{actionId}", produces = {
|
||||
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE, DdiRestConstants.MEDIA_TYPE_CBOR })
|
||||
ResponseEntity<DdiConfirmationBaseAction> getConfirmationBaseAction(@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("controllerId") @NotEmpty final String controllerId,
|
||||
@PathVariable("actionId") @NotEmpty final Long actionId,
|
||||
@RequestParam(value = "c", required = false, defaultValue = "-1") final int resource,
|
||||
@RequestParam(value = "actionHistory", defaultValue = DdiRestConstants.NO_ACTION_HISTORY) final Integer actionHistoryMessageCount);
|
||||
|
||||
/**
|
||||
* This is the feedback channel for the {@link DdiConfirmationBaseAction}
|
||||
* action.
|
||||
*
|
||||
* @param tenant
|
||||
* of the client
|
||||
* @param feedback
|
||||
* to provide
|
||||
* @param controllerId
|
||||
* of the target that matches to controller id
|
||||
* @param actionId
|
||||
* of the action we have feedback for
|
||||
*
|
||||
* @return the response
|
||||
*/
|
||||
@PostMapping(value = "/{controllerId}/" + DdiRestConstants.CONFIRMATION_BASE + "/{actionId}/"
|
||||
+ DdiRestConstants.FEEDBACK, consumes = { MediaType.APPLICATION_JSON_VALUE,
|
||||
DdiRestConstants.MEDIA_TYPE_CBOR })
|
||||
ResponseEntity<Void> postConfirmationActionFeedback(@Valid final DdiConfirmationFeedback feedback,
|
||||
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("actionId") @NotEmpty final Long actionId);
|
||||
|
||||
/**
|
||||
* Activate auto confirmation for a given controllerId. Will use the provided
|
||||
* initiator and remark field from the provided
|
||||
* {@link DdiActivateAutoConfirmation}. If not present, the values will be
|
||||
* prefilled with a default remark and the CONTROLLER as initiator.
|
||||
*
|
||||
* @param tenant
|
||||
* the controllerId is corresponding too
|
||||
* @param controllerId
|
||||
* to activate auto-confirmation for
|
||||
* @param body
|
||||
* as {@link DdiActivateAutoConfirmation}
|
||||
* @return {@link org.springframework.http.HttpStatus#OK} if successful or
|
||||
* {@link org.springframework.http.HttpStatus#CONFLICT} in case
|
||||
* auto-confirmation was active already.
|
||||
*/
|
||||
@PostMapping(value = "/{controllerId}/" + DdiRestConstants.CONFIRMATION_BASE + "/"
|
||||
+ DdiRestConstants.AUTO_CONFIRM_ACTIVATE, consumes = { MediaType.APPLICATION_JSON_VALUE,
|
||||
DdiRestConstants.MEDIA_TYPE_CBOR })
|
||||
ResponseEntity<Void> activateAutoConfirmation(@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("controllerId") @NotEmpty final String controllerId,
|
||||
@Valid @RequestBody(required = false) final DdiActivateAutoConfirmation body);
|
||||
|
||||
/**
|
||||
* Deactivate auto confirmation for a given controller id.
|
||||
*
|
||||
* @param tenant
|
||||
* the controllerId is corresponding too
|
||||
* @param controllerId
|
||||
* to disable auto-confirmation for
|
||||
* @return {@link org.springframework.http.HttpStatus#OK} if successfully
|
||||
* executed
|
||||
*/
|
||||
@PostMapping(value = "/{controllerId}/" + DdiRestConstants.CONFIRMATION_BASE + "/"
|
||||
+ DdiRestConstants.AUTO_CONFIRM_DEACTIVATE)
|
||||
ResponseEntity<Void> deactivateAutoConfirmation(@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("controllerId") @NotEmpty final String controllerId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* 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.ddi.json.model;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.eclipse.hawkbit.ddi.json.model.DdiDeployment.DdiMaintenanceWindowStatus.AVAILABLE;
|
||||
import static org.eclipse.hawkbit.ddi.json.model.DdiDeployment.HandlingType.ATTEMPT;
|
||||
import static org.eclipse.hawkbit.ddi.json.model.DdiDeployment.HandlingType.FORCED;
|
||||
|
||||
/**
|
||||
* Test serializability of DDI api model 'DdiConfirmationBase'
|
||||
*/
|
||||
@Feature("Unit Tests - Direct Device Integration API")
|
||||
@Story("CHeck JSON serialization of DDI api confirmation models")
|
||||
class DdiConfirmationBaseTest {
|
||||
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
@Test
|
||||
@Description("Verify the correct serialization and deserialization of the model")
|
||||
void shouldSerializeAndDeserializeObject() throws IOException {
|
||||
// Setup
|
||||
final String id = "1234";
|
||||
final DdiDeployment ddiDeployment = new DdiDeployment(FORCED, ATTEMPT, Collections.emptyList(), AVAILABLE);
|
||||
final String actionStatus = "TestAction";
|
||||
final DdiActionHistory ddiActionHistory = new DdiActionHistory(actionStatus,
|
||||
Arrays.asList("Action status message 1", "Action status message 2"));
|
||||
final DdiConfirmationBaseAction ddiConfirmationBaseAction = new DdiConfirmationBaseAction(id, ddiDeployment,
|
||||
ddiActionHistory);
|
||||
|
||||
// Test
|
||||
String serializedDdiConfirmationBase = mapper.writeValueAsString(ddiConfirmationBaseAction);
|
||||
final DdiConfirmationBaseAction deserializedDdiConfigurationBase = mapper
|
||||
.readValue(serializedDdiConfirmationBase, DdiConfirmationBaseAction.class);
|
||||
|
||||
assertThat(serializedDdiConfirmationBase).contains(id, FORCED.getName(), ATTEMPT.getName(),
|
||||
AVAILABLE.getStatus(), actionStatus);
|
||||
assertThat(deserializedDdiConfigurationBase.getConfirmation().getDownload())
|
||||
.isEqualTo(ddiDeployment.getDownload());
|
||||
assertThat(deserializedDdiConfigurationBase.getConfirmation().getUpdate()).isEqualTo(ddiDeployment.getUpdate());
|
||||
assertThat(deserializedDdiConfigurationBase.getConfirmation().getMaintenanceWindow())
|
||||
.isEqualTo(ddiDeployment.getMaintenanceWindow());
|
||||
assertThat(deserializedDdiConfigurationBase.getActionHistory().toString())
|
||||
.isEqualTo(ddiActionHistory.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify the correct deserialization of a model with a additional unknown property")
|
||||
void shouldDeserializeObjectWithUnknownProperty() throws IOException {
|
||||
// Setup
|
||||
String serializedDdiConfirmationBase = "{\"id\":\"1234\",\"confirmation\":{\"download\":\"forced\","
|
||||
+ "\"update\":\"attempt\",\"maintenanceWindow\":\"available\",\"chunks\":[]},"
|
||||
+ "\"actionHistory\":{\"status\":\"TestAction\",\"messages\":[\"Action status message 1\","
|
||||
+ "\"Action status message 2\"]},\"links\":[],\"unknownProperty\":\"test\"}";
|
||||
|
||||
// Test
|
||||
DdiConfirmationBaseAction ddiConfirmationBaseAction = mapper.readValue(serializedDdiConfirmationBase,
|
||||
DdiConfirmationBaseAction.class);
|
||||
|
||||
assertThat(ddiConfirmationBaseAction.getConfirmation().getDownload().getName()).isEqualTo(FORCED.getName());
|
||||
assertThat(ddiConfirmationBaseAction.getConfirmation().getUpdate().getName()).isEqualTo(ATTEMPT.getName());
|
||||
assertThat(ddiConfirmationBaseAction.getConfirmation().getMaintenanceWindow().getStatus())
|
||||
.isEqualTo(AVAILABLE.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that deserialization fails for known properties with a wrong datatype")
|
||||
void shouldFailForObjectWithWrongDataTypes() throws IOException {
|
||||
// Setup
|
||||
String serializedDdiConfirmationBase = "{\"id\":[\"1234\"],\"confirmation\":{\"download\":\"forced\","
|
||||
+ "\"update\":\"attempt\",\"maintenanceWindow\":\"available\",\"chunks\":[]},"
|
||||
+ "\"actionHistory\":{\"status\":\"TestAction\",\"messages\":[\"Action status message 1\","
|
||||
+ "\"Action status message 2\"]},\"links\":[]}";
|
||||
|
||||
// Test
|
||||
assertThatExceptionOfType(MismatchedInputException.class)
|
||||
.isThrownBy(() -> mapper.readValue(serializedDdiConfirmationBase, DdiConfirmationBaseAction.class));
|
||||
}
|
||||
}
|
||||
@@ -31,20 +31,23 @@ import io.qameta.allure.Story;
|
||||
public class JsonIgnorePropertiesAnnotationTest {
|
||||
|
||||
@Test
|
||||
@Description(
|
||||
"This test verifies that all model classes within the 'org.eclipse.hawkbit.ddi.json.model' package are annotated with '@JsonIgnoreProperties(ignoreUnknown = true)'")
|
||||
@Description("This test verifies that all model classes within the 'org.eclipse.hawkbit.ddi.json.model' package are annotated with '@JsonIgnoreProperties(ignoreUnknown = true)'")
|
||||
public void shouldCheckAnnotationsForAllModelClasses() throws IOException {
|
||||
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
final String packageName = this.getClass().getPackage().getName();
|
||||
|
||||
final ImmutableSet<ClassPath.ClassInfo> topLevelClasses = ClassPath.from(loader).getTopLevelClasses(packageName);
|
||||
final ImmutableSet<ClassPath.ClassInfo> topLevelClasses = ClassPath.from(loader)
|
||||
.getTopLevelClasses(packageName);
|
||||
for (final ClassPath.ClassInfo classInfo : topLevelClasses) {
|
||||
final Class<?> modelClass = classInfo.load();
|
||||
if (modelClass.getSimpleName().contains("Test") || modelClass.isEnum()) {
|
||||
continue;
|
||||
}
|
||||
final JsonIgnoreProperties annotation = modelClass.getAnnotation(JsonIgnoreProperties.class);
|
||||
assertThat(annotation).isNotNull();
|
||||
assertThat(annotation)
|
||||
.describedAs(
|
||||
"Annotation 'JsonIgnoreProperties' is missing for class: " + modelClass.getSimpleName())
|
||||
.isNotNull();
|
||||
assertThat(annotation.ignoreUnknown()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user