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:
Michael Herdt
2023-01-25 12:11:05 +01:00
committed by GitHub
parent b919ceda5c
commit 21f1569881
208 changed files with 7830 additions and 831 deletions

View File

@@ -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}.
*/

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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 +"]";
}
}

View File

@@ -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";

View File

@@ -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);
}

View File

@@ -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));
}
}

View File

@@ -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();
}
}