REST doc / DDI - fix missed info (#1618)

When spring restdoc was replaces with swagger & open api some info was lost
This commit returns back this info for DDI API

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2024-02-07 15:36:32 +02:00
committed by GitHub
parent ec10950308
commit fc561c3eff
32 changed files with 1208 additions and 818 deletions

View File

@@ -16,6 +16,9 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
/** /**
* <p> * <p>
@@ -35,12 +38,14 @@ import io.swagger.v3.oas.annotations.media.Schema;
* "execution": "closed", "result": { "final": "success", "progress": {} } * "execution": "closed", "result": { "final": "success", "progress": {} }
* "details": [], } } * "details": [], } }
* </p> * </p>
*
*/ */
@Getter
@EqualsAndHashCode
@ToString
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class DdiActionFeedback { public class DdiActionFeedback {
@Schema(example = "2023-08-03T12:31:41.890992967Z") @Schema(description = "Timestamp of the action", example = "2023-08-03T12:31:41.890992967Z")
private final String time; private final String time;
@NotNull @NotNull
@@ -50,29 +55,14 @@ public class DdiActionFeedback {
/** /**
* Constructs an action-feedback * Constructs an action-feedback
* *
* @param time * @param time time of feedback
* time of feedback * @param status status to be appended to the action
* @param status
* status to be appended to the action
*/ */
@JsonCreator @JsonCreator
public DdiActionFeedback(@JsonProperty(value = "time") final String time, public DdiActionFeedback(
@JsonProperty(value = "time") final String time,
@JsonProperty(value = "status", required = true) final DdiStatus status) { @JsonProperty(value = "status", required = true) final DdiStatus status) {
this.time = time; this.time = time;
this.status = status; this.status = status;
} }
}
public String getTime() {
return time;
}
public DdiStatus getStatus() {
return status;
}
@Override
public String toString() {
return "ActionFeedback [time=" + time + ", status=" + status + "]";
}
}

View File

@@ -12,6 +12,8 @@ package org.eclipse.hawkbit.ddi.json.model;
import java.util.List; import java.util.List;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.eclipse.hawkbit.ddi.rest.api.DdiRootControllerRestApi; import org.eclipse.hawkbit.ddi.rest.api.DdiRootControllerRestApi;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
@@ -21,41 +23,39 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
/** /**
* Provide action history information to the controller as part of response to * Provide action history information to the controller as part of response to
* {@link DdiRootControllerRestApi#getControllerBasedeploymentAction} and * {@link DdiRootControllerRestApi#getControllerDeploymentBaseAction} and
* {@link DdiRootControllerRestApi#getControllerBaseconfirmationAction}: * {@link DdiRootControllerRestApi#getConfirmationBaseAction}:
* 1. Current action status at the server; 2. List of messages from action history * <ol>
* that were sent to server earlier by the controller using * <li>Current action status at the server</li>
* {@link DdiActionFeedback}. * <li>List of messages from action history</li>
* </ol>
* that were sent to server earlier by the controller using {@link DdiActionFeedback}.
*/ */
@EqualsAndHashCode
@ToString
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonPropertyOrder({ "status", "messages" }) @JsonPropertyOrder({ "status", "messages" })
public class DdiActionHistory { public class DdiActionHistory {
@JsonProperty("status") @JsonProperty("status")
@Schema(example = "RUNNING") @Schema(description = "Status of the deployment based on previous feedback by the device", example = "RUNNING")
private final String actionStatus; private final String actionStatus;
@JsonProperty("messages") @JsonProperty("messages")
@Schema(description = "Messages are previously sent to the feedback channel in LIFO order by the device. Note: The first status message is set by the system and describes the trigger of the deployment")
private final List<String> messages; private final List<String> messages;
/** /**
* Parameterized constructor for creating {@link DdiActionHistory}. * Parameterized constructor for creating {@link DdiActionHistory}.
* *
* @param actionStatus * @param actionStatus is the current action status at the server
* is the current action status at the server * @param messages is a list of messages retrieved from action history.
* @param messages
* is a list of messages retrieved from action history.
*/ */
@JsonCreator @JsonCreator
public DdiActionHistory(@JsonProperty("status") final String actionStatus, public DdiActionHistory(
@JsonProperty("status") final String actionStatus,
@JsonProperty("messages") List<String> messages) { @JsonProperty("messages") List<String> messages) {
this.actionStatus = actionStatus; this.actionStatus = actionStatus;
this.messages = messages; this.messages = messages;
} }
}
@Override
public String toString() {
return "Action history [" + "status=" + actionStatus + ", messages={" + messages.toString() + "}]";
}
}

View File

@@ -13,25 +13,31 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@Getter
@EqualsAndHashCode
@ToString
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class DdiActivateAutoConfirmation { public class DdiActivateAutoConfirmation {
@JsonProperty(required = false) @JsonProperty(required = false)
@Schema(example = "exampleUser") @Schema(description = "Individual value (e.g. username) stored as initiator and automatically used as confirmed" +
" user in future actions", example = "exampleUser")
private final String initiator; private final String initiator;
@JsonProperty(required = false) @JsonProperty(required = false)
@Schema(example = "exampleRemark") @Schema(description = "Individual value to attach a remark which will be persisted when automatically " +
"confirming future actions", example = "exampleRemark")
private final String remark; private final String remark;
/** /**
* Constructor. * Constructor.
* *
* @param initiator * @param initiator can be null
* can be null * @param remark can be null
* @param remark
* can be null
*/ */
@JsonCreator @JsonCreator
public DdiActivateAutoConfirmation(@JsonProperty(value = "initiator") final String initiator, public DdiActivateAutoConfirmation(@JsonProperty(value = "initiator") final String initiator,
@@ -39,18 +45,4 @@ public class DdiActivateAutoConfirmation {
this.initiator = initiator; this.initiator = initiator;
this.remark = remark; 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

@@ -12,6 +12,8 @@ package org.eclipse.hawkbit.ddi.json.model;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.hateoas.RepresentationModel; import org.springframework.hateoas.RepresentationModel;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@@ -19,45 +21,52 @@ import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* Download information for all artifacts related to a specific {@link DdiChunk} * Download information for all artifacts related to a specific {@link DdiChunk}
* .
*/ */
@Data
@EqualsAndHashCode(callSuper = true)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@Schema(description = """
**_links**:
* **download** - HTTPs Download resource for artifacts. The resource supports partial download as specified by RFC7233 (range requests). Keep in mind that the target needs to have the artifact assigned in order to be granted permission to download.
* **md5sum** - HTTPs Download resource for MD5SUM file is an optional auto generated artifact that is especially useful for Linux based devices on order to check artifact consistency after download by using the md5sum command line tool. The MD5 and SHA1 are in addition available as metadata in the deployment command itself.
* **download-http** - HTTP Download resource for artifacts. The resource supports partial download as specified by RFC7233 (range requests). Keep in mind that the target needs to have the artifact assigned in order to be granted permission to download. (note: anonymous download needs to be enabled on the service account for non-TLS access)
* **md5sum-http** - HTTP Download resource for MD5SUM file is an optional auto generated artifact that is especially useful for Linux based devices on order to check artifact consistency after download by using the md5sum command line tool. The MD5 and SHA1 are in addition available as metadata in the deployment command itself. (note: anonymous download needs to be enabled on the service account for non-TLS access)
""", example = """
{
"filename" : "binaryFile",
"hashes" : {
"sha1" : "e4e667b70ff652cb9d9c8a49f141bd68e06cec6f",
"md5" : "13793b0e3a7830ed685d3ede7ff93048",
"sha256" : "c51368bf045803b429a67bdf04539a373d9fb8caa310fe0431265e6871b4f07a"
},
"size" : 11,
"_links" : {
"download" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/40/filename/binaryFile"
},
"download-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/40/filename/binaryFile"
},
"md5sum-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/40/filename/binaryFile.MD5SUM"
},
"md5sum" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/40/filename/binaryFile.MD5SUM"
}
}
}""")
public class DdiArtifact extends RepresentationModel<DdiArtifact> { public class DdiArtifact extends RepresentationModel<DdiArtifact> {
@NotNull @NotNull
@JsonProperty @JsonProperty
@Schema(example = "binary.tgz") @Schema(description = "File name", example = "binary.tgz")
private String filename; private String filename;
@JsonProperty @JsonProperty
@Schema(description = "Artifact hashes")
private DdiArtifactHash hashes; private DdiArtifactHash hashes;
@JsonProperty @JsonProperty
@Schema(example = "3") @Schema(description = "Artifact size", example = "3")
private Long size; private Long size;
}
public DdiArtifactHash getHashes() {
return hashes;
}
public void setHashes(final DdiArtifactHash hashes) {
this.hashes = hashes;
}
public String getFilename() {
return filename;
}
public void setFilename(final String fileName) {
filename = fileName;
}
public Long getSize() {
return size;
}
public void setSize(final Long size) {
this.size = size;
}
}

View File

@@ -14,33 +14,34 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
/** /**
* Hashes for given Artifact. * Hashes for given Artifact
*/ */
@NoArgsConstructor // needed for json create
@Getter
@EqualsAndHashCode
@ToString
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class DdiArtifactHash { public class DdiArtifactHash {
@JsonProperty @JsonProperty
@Schema(example = "2d86c2a659e364e9abba49ea6ffcd53dd5559f05") @Schema(description = "SHA1 hash of the artifact in Base 16 format", example = "2d86c2a659e364e9abba49ea6ffcd53dd5559f05")
private String sha1; private String sha1;
@JsonProperty @JsonProperty
@Schema(example = "0d1b08c34858921bc7c662b228acb7ba") @Schema(description = "MD5 hash of the artifact", example = "0d1b08c34858921bc7c662b228acb7ba")
private String md5; private String md5;
@JsonProperty @JsonProperty
@JsonInclude(Include.NON_NULL) @JsonInclude(Include.NON_NULL)
@Schema(example = "a03b221c6c6eae7122ca51695d456d5222e524889136394944b2f9763b483615") @Schema(description = "SHA-256 hash of the artifact in Base 16 format", example = "a03b221c6c6eae7122ca51695d456d5222e524889136394944b2f9763b483615")
private String sha256; private String sha256;
/**
* Default constructor.
*/
public DdiArtifactHash() {
// needed for json create
}
/** /**
* Public constructor. * Public constructor.
* *
@@ -56,25 +57,4 @@ public class DdiArtifactHash {
this.md5 = md5; this.md5 = md5;
this.sha256 = sha256; this.sha256 = sha256;
} }
}
/**
* @return the sha1
*/
public String getSha1() {
return sha1;
}
/**
* @return the md5
*/
public String getMd5() {
return md5;
}
/**
* @return the sha256
*/
public String getSha256() {
return sha256;
}
}

View File

@@ -15,8 +15,14 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.springframework.hateoas.RepresentationModel; import org.springframework.hateoas.RepresentationModel;
@NoArgsConstructor // needed for json create
@Data
@EqualsAndHashCode(callSuper = true)
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonPropertyOrder({ "active", "initiator", "remark", "activatedAt" }) @JsonPropertyOrder({ "active", "initiator", "remark", "activatedAt" })
@@ -33,13 +39,6 @@ public class DdiAutoConfirmationState extends RepresentationModel<DdiAutoConfirm
@Schema(example = "1691065895439") @Schema(example = "1691065895439")
private Long activatedAt; private Long activatedAt;
/**
* Constructor.
*/
public DdiAutoConfirmationState() {
// needed for json create.
}
public static DdiAutoConfirmationState active(final long activatedAt) { public static DdiAutoConfirmationState active(final long activatedAt) {
final DdiAutoConfirmationState state = new DdiAutoConfirmationState(); final DdiAutoConfirmationState state = new DdiAutoConfirmationState();
state.setActive(true); state.setActive(true);
@@ -50,37 +49,4 @@ public class DdiAutoConfirmationState extends RepresentationModel<DdiAutoConfirm
public static DdiAutoConfirmationState disabled() { public static DdiAutoConfirmationState disabled() {
return new DdiAutoConfirmationState(); 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

@@ -15,16 +15,23 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
/** /**
* Cancel action to be provided to the target. * Cancel action to be provided to the target.
*/ */
@Getter
@EqualsAndHashCode
@ToString
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class DdiCancel { public class DdiCancel {
@Schema(example = "11") @Schema(description = "Id of the action", example = "11")
private final String id; private final String id;
@Schema(description = "Action that needs to be canceled")
@NotNull @NotNull
private final DdiCancelActionToStop cancelAction; private final DdiCancelActionToStop cancelAction;
@@ -42,18 +49,4 @@ public class DdiCancel {
this.id = id; this.id = id;
this.cancelAction = cancelAction; this.cancelAction = cancelAction;
} }
}
public String getId() {
return id;
}
public DdiCancelActionToStop getCancelAction() {
return cancelAction;
}
@Override
public String toString() {
return "Cancel [id=" + id + ", cancelAction=" + cancelAction + "]";
}
}

View File

@@ -15,35 +15,30 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
/** /**
* The action that has to be stopped by the target. * The action that has to be stopped by the target.
*/ */
@Getter
@EqualsAndHashCode
@ToString
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class DdiCancelActionToStop { public class DdiCancelActionToStop {
@NotNull @NotNull
@Schema(example = "11") @Schema(description = "Id of the action that needs to be canceled (typically identical to id field on the cancel action itself)", example = "11")
private final String stopId; private final String stopId;
/** /**
* Parameterized constructor. * Parameterized constructor.
* *
* @param stopId * @param stopId ID of the action to be stopped
* ID of the action to be stopped
*/ */
@JsonCreator @JsonCreator
public DdiCancelActionToStop(@JsonProperty("stopId") final String stopId) { public DdiCancelActionToStop(@JsonProperty("stopId") final String stopId) {
this.stopId = stopId; this.stopId = stopId;
} }
}
public String getStopId() {
return stopId;
}
@Override
public String toString() {
return "CancelAction [stopId=" + stopId + "]";
}
}

View File

@@ -18,59 +18,60 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
/** /**
* Deployment chunks. * Deployment chunks.
*/ */
@NoArgsConstructor // needed for json create
@Getter
@EqualsAndHashCode
@ToString
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class DdiChunk { public class DdiChunk {
@JsonProperty("part") @JsonProperty("part")
@NotNull @NotNull
@Schema(example = "bApp") @Schema(description = "Type of the chunk, e.g. firmware, bundle, app. In update server mapped to Software Module Type")
private String part; private String part;
@JsonProperty("version") @JsonProperty("version")
@NotNull @NotNull
@Schema(example = "1.2.0") @Schema(description = "Software version of the chunk", example = "1.2.0")
private String version; private String version;
@JsonProperty("name") @JsonProperty("name")
@NotNull @NotNull
@Schema(example = "oneApp") @Schema(description = "Name of the chunk")
private String name; private String name;
@JsonProperty("encrypted") @JsonProperty("encrypted")
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
@Schema(description = "If encrypted")
private Boolean encrypted; private Boolean encrypted;
@JsonProperty("artifacts") @JsonProperty("artifacts")
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
@Schema(description = "List of artifacts")
private List<DdiArtifact> artifacts; private List<DdiArtifact> artifacts;
@JsonProperty("metadata") @JsonProperty("metadata")
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
@Schema(description = "Meta data of the respective software module that has been marked with 'target visible'")
private List<DdiMetadata> metadata; private List<DdiMetadata> metadata;
public DdiChunk() {
// needed for json create
}
/** /**
* Constructor. * Constructor.
* *
* @param part * @param part of the deployment chunk
* of the deployment chunk * @param version of the artifact
* @param version * @param name of the artifact
* of the artifact * @param encrypted if artifacts are encrypted
* @param name * @param artifacts download information
* of the artifact * @param metadata optional as additional information for the target/device
* @param encrypted
* if artifacts are encrypted
* @param artifacts
* download information
* @param metadata
* optional as additional information for the target/device
*/ */
public DdiChunk(final String part, final String version, final String name, final Boolean encrypted, public DdiChunk(final String part, final String version, final String name, final Boolean encrypted,
final List<DdiArtifact> artifacts, final List<DdiMetadata> metadata) { final List<DdiArtifact> artifacts, final List<DdiMetadata> metadata) {
@@ -82,22 +83,6 @@ public class DdiChunk {
this.metadata = metadata; this.metadata = metadata;
} }
public String getPart() {
return part;
}
public String getVersion() {
return version;
}
public String getName() {
return name;
}
public Boolean isEncrypted() {
return encrypted;
}
public List<DdiArtifact> getArtifacts() { public List<DdiArtifact> getArtifacts() {
if (artifacts == null) { if (artifacts == null) {
return Collections.emptyList(); return Collections.emptyList();
@@ -105,9 +90,4 @@ public class DdiChunk {
return Collections.unmodifiableList(artifacts); return Collections.unmodifiableList(artifacts);
} }
}
public List<DdiMetadata> getMetadata() {
return metadata;
}
}

View File

@@ -13,12 +13,22 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
/** /**
* Standard configuration for the target. * Standard configuration for the target.
*/ */
@NoArgsConstructor // needed for json deserialization
@Getter
@EqualsAndHashCode
@ToString
@JsonInclude(Include.NON_NULL) @JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@Schema(description = "DDI controller configuration")
public class DdiConfig { public class DdiConfig {
@JsonProperty @JsonProperty
@@ -27,22 +37,9 @@ public class DdiConfig {
/** /**
* Constructor. * Constructor.
* *
* @param polling * @param polling configuration of the polling for the target
* configuration of the SP target
*/ */
public DdiConfig(final DdiPolling polling) { public DdiConfig(final DdiPolling polling) {
this.polling = polling; this.polling = polling;
} }
}
/**
* Constructor.
*/
public DdiConfig() {
// needed for json create.
}
public DdiPolling getPolling() {
return polling;
}
}

View File

@@ -7,9 +7,8 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.ddi.json.model; package org.eclipse.hawkbit.ddi.json.model;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
@@ -18,17 +17,32 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
/** /**
* Feedback channel for ConfigData action. * Feedback channel for ConfigData action.
*/ */
@Getter
@EqualsAndHashCode
@ToString
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@Schema(example = """
{
"mode" : "merge",
"data" : {
"VIN" : "JH4TB2H26CC000000",
"hwRevision" : "2"
}
}""")
public class DdiConfigData { public class DdiConfigData {
@NotEmpty @NotEmpty
@Schema(description = "Link which is provided whenever the provisioning target or device is supposed to push its configuration data (aka. \"controller attributes\") to the server. Only shown for the initial configuration, after a successful update action, or if requested explicitly (e.g. via the management UI).")
private final Map<String, String> data; private final Map<String, String> data;
@Schema(example = "merge") @Schema(description = "Optional parameter to specify the update mode that should be applied when updating target attributes. Valid values are 'merge', 'replace', and 'remove'. Defaults to 'merge'.")
private final DdiUpdateMode mode; private final DdiUpdateMode mode;
/** /**
@@ -40,23 +54,10 @@ public class DdiConfigData {
* defines the mode of the update (replace, merge, remove) * defines the mode of the update (replace, merge, remove)
*/ */
@JsonCreator @JsonCreator
public DdiConfigData(@JsonProperty(value = "data") final Map<String, String> data, public DdiConfigData(
@JsonProperty(value = "data") final Map<String, String> data,
@JsonProperty(value = "mode") final DdiUpdateMode mode) { @JsonProperty(value = "mode") final DdiUpdateMode mode) {
this.data = data; this.data = data;
this.mode = mode; this.mode = mode;
} }
}
public Map<String, String> getData() {
return data;
}
public DdiUpdateMode getMode() {
return mode;
}
@Override
public String toString() {
return "ConfigData [data=" + data + ", mode=" + mode + ", toString()=" + super.toString() + "]";
}
}

View File

@@ -9,9 +9,14 @@
*/ */
package org.eclipse.hawkbit.ddi.json.model; package org.eclipse.hawkbit.ddi.json.model;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.hateoas.RepresentationModel; import org.springframework.hateoas.RepresentationModel;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@@ -21,21 +26,39 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* Confirmation base response. * Confirmation base response.
* Set order to place links at last. * Set order to place links at last.
*/ */
@NoArgsConstructor // needed for json create
@Getter
@EqualsAndHashCode(callSuper = true)
@ToString
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonPropertyOrder({ "autoConfirm" }) @JsonPropertyOrder({ "autoConfirm" })
@Schema(
description = """
**_links**:
* **confirmationBase** - confirmation base
* **deactivateAutoConfirm** - where to deactivate auto confirm
* **activateAutoConfirm** - where to activate auto confirm
""",
example = """
{
"autoConfirm" : {
"active" : false
},
"_links" : {
"activateAutoConfirm" : {
"href" : "https://management-api.host.com/TENANT_ID/controller/v1/CONTROLLER_ID/confirmationBase/activateAutoConfirm"
},
"confirmationBase" : {
"href" : "https://management-api.host.com/TENANT_ID/controller/v1/CONTROLLER_ID/confirmationBase/10?c=-2122565939"
}
}
}""")
public class DdiConfirmationBase extends RepresentationModel<DdiConfirmationBase> { public class DdiConfirmationBase extends RepresentationModel<DdiConfirmationBase> {
@JsonProperty("autoConfirm") @JsonProperty("autoConfirm")
@NotNull @NotNull
private DdiAutoConfirmationState autoConfirm; private DdiAutoConfirmationState autoConfirm;
/**
* Constructor.
*/
public DdiConfirmationBase() {
// needed for json create.
}
/** /**
* Constructor. * Constructor.
* *
@@ -44,8 +67,4 @@ public class DdiConfirmationBase extends RepresentationModel<DdiConfirmationBase
public DdiConfirmationBase(final DdiAutoConfirmationState autoConfirmState) { public DdiConfirmationBase(final DdiAutoConfirmationState autoConfirmState) {
this.autoConfirm = autoConfirmState; this.autoConfirm = autoConfirmState;
} }
}
public DdiAutoConfirmationState getAutoConfirm() {
return autoConfirm;
}
}

View File

@@ -14,6 +14,10 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.hateoas.RepresentationModel; import org.springframework.hateoas.RepresentationModel;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
@@ -22,17 +26,190 @@ import java.util.Objects;
/** /**
* Update action resource. * Update action resource.
*/ */
@NoArgsConstructor // needed for json create
@Getter
@EqualsAndHashCode(callSuper = true)
@ToString
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonPropertyOrder({ "id", "confirmation", "actionHistory" }) @JsonPropertyOrder({ "id", "confirmation", "actionHistory" })
@Schema(description = """
The response body includes the detailed information about the action awaiting confirmation in the same format as
for the deploymentBase operation.""",
example = """
{
"id" : "6",
"confirmation" : {
"download" : "forced",
"update" : "forced",
"maintenanceWindow" : "available",
"chunks" : [ {
"part" : "jvm",
"version" : "1.0.62",
"name" : "oneapp runtime",
"artifacts" : [ {
"filename" : "binary.tgz",
"hashes" : {
"sha1" : "3dceccec02e7626184bdbba12b247b67ff04c363",
"md5" : "a9a7df0aa4c72b3b03b654c42d29744b",
"sha256" : "971d8db88fef8e7a3e6d5bbf501d69b07d0c300d9be948aff8b52960ef039358"
},
"size" : 11,
"_links" : {
"download" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/17/filename/binary.tgz"
},
"download-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/17/filename/binary.tgz"
},
"md5sum-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/17/filename/binary.tgz.MD5SUM"
},
"md5sum" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/17/filename/binary.tgz.MD5SUM"
}
}
}, {
"filename" : "file.signature",
"hashes" : {
"sha1" : "3dceccec02e7626184bdbba12b247b67ff04c363",
"md5" : "a9a7df0aa4c72b3b03b654c42d29744b",
"sha256" : "971d8db88fef8e7a3e6d5bbf501d69b07d0c300d9be948aff8b52960ef039358"
},
"size" : 11,
"_links" : {
"download" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/17/filename/file.signature"
},
"download-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/17/filename/file.signature"
},
"md5sum-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/17/filename/file.signature.MD5SUM"
},
"md5sum" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/17/filename/file.signature.MD5SUM"
}
}
} ]
}, {
"part" : "bApp",
"version" : "1.0.96",
"name" : "oneapplication",
"artifacts" : [ {
"filename" : "binary.tgz",
"hashes" : {
"sha1" : "701c0c0fcbee5e96fa5c5b819cb519686940ade3",
"md5" : "f0f6a34c4c9e79d07c2d92c3c3d88560",
"sha256" : "cff472a07c3143741fb03ac6c577acabef72a186a8bfaab00bbb47ca5ebbe554"
},
"size" : 11,
"_links" : {
"download" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/16/filename/binary.tgz"
},
"download-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/16/filename/binary.tgz"
},
"md5sum-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/16/filename/binary.tgz.MD5SUM"
},
"md5sum" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/16/filename/binary.tgz.MD5SUM"
}
}
}, {
"filename" : "file.signature",
"hashes" : {
"sha1" : "701c0c0fcbee5e96fa5c5b819cb519686940ade3",
"md5" : "f0f6a34c4c9e79d07c2d92c3c3d88560",
"sha256" : "cff472a07c3143741fb03ac6c577acabef72a186a8bfaab00bbb47ca5ebbe554"
},
"size" : 11,
"_links" : {
"download" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/16/filename/file.signature"
},
"download-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/16/filename/file.signature"
},
"md5sum-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/16/filename/file.signature.MD5SUM"
},
"md5sum" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/16/filename/file.signature.MD5SUM"
}
}
} ]
}, {
"part" : "os",
"version" : "1.0.44",
"name" : "one Firmware",
"artifacts" : [ {
"filename" : "binary.tgz",
"hashes" : {
"sha1" : "2b09765e953cd138b7da8f4725e48183dab62aec",
"md5" : "9b0aa2f51379cb4a5e0b7d026c2605c9",
"sha256" : "618faa741070b3f8148bad06f088e537a8f7913e734df4dde61fb163725cb4ee"
},
"size" : 15,
"_links" : {
"download" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/18/filename/binary.tgz"
},
"download-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/18/filename/binary.tgz"
},
"md5sum-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/18/filename/binary.tgz.MD5SUM"
},
"md5sum" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/18/filename/binary.tgz.MD5SUM"
}
}
}, {
"filename" : "file.signature",
"hashes" : {
"sha1" : "2b09765e953cd138b7da8f4725e48183dab62aec",
"md5" : "9b0aa2f51379cb4a5e0b7d026c2605c9",
"sha256" : "618faa741070b3f8148bad06f088e537a8f7913e734df4dde61fb163725cb4ee"
},
"size" : 15,
"_links" : {
"download" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/18/filename/file.signature"
},
"download-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/18/filename/file.signature"
},
"md5sum-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/18/filename/file.signature.MD5SUM"
},
"md5sum" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/18/filename/file.signature.MD5SUM"
}
}
} ],
"metadata" : [ {
"key" : "aMetadataKey",
"value" : "Metadata value as defined in software module"
} ]
} ]
},
"actionHistory" : {
"status" : "WAIT_FOR_CONFIRMATION",
"messages" : [ "Assignment initiated by user 'TestPrincipal'", "Waiting for the confirmation by the device before processing with the deployment" ]
}
}""")
public class DdiConfirmationBaseAction extends RepresentationModel<DdiConfirmationBaseAction> { public class DdiConfirmationBaseAction extends RepresentationModel<DdiConfirmationBaseAction> {
@JsonProperty("id") @JsonProperty("id")
@NotNull @NotNull
@Schema(example = "6") @Schema(description = "Id of the action", example = "6")
private String id; private String id;
@JsonProperty("confirmation") @JsonProperty("confirmation")
@NotNull @NotNull
@Schema(description = "Deployment confirmation operation")
private DdiDeployment confirmation; private DdiDeployment confirmation;
/** /**
@@ -41,24 +218,18 @@ public class DdiConfirmationBaseAction extends RepresentationModel<DdiConfirmati
*/ */
@JsonProperty("actionHistory") @JsonProperty("actionHistory")
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
@Schema(description = """
(Optional) GET parameter to retrieve a given number of messages which are previously
provided by the device. Useful if the devices sent state information to the feedback channel and never
stored them locally""")
private DdiActionHistory actionHistory; private DdiActionHistory actionHistory;
/**
* Constructor.
*/
public DdiConfirmationBaseAction() {
// needed for json create.
}
/** /**
* Constructor. * Constructor.
* *
* @param id * @param id of the update action
* of the update action * @param confirmation chunk details
* @param confirmation * @param actionHistory containing current action status and a list of feedback messages
* chunk details
* @param actionHistory
* containing current action status and a list of feedback messages
* received earlier from the controller. * received earlier from the controller.
*/ */
public DdiConfirmationBaseAction(final String id, final DdiDeployment confirmation, public DdiConfirmationBaseAction(final String id, final DdiDeployment confirmation,
@@ -67,46 +238,4 @@ public class DdiConfirmationBaseAction extends RepresentationModel<DdiConfirmati
this.confirmation = confirmation; this.confirmation = confirmation;
this.actionHistory = actionHistory; 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

@@ -17,6 +17,10 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -24,52 +28,13 @@ import java.util.List;
* New update actions require confirmation when confirmation flow is switched on. This is the feedback channel for * 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: * confirmation messages for DDI API. The confirmation message has a mandatory field confirmation with possible values:
* "confirmed" and "denied". * "confirmed" and "denied".
*
*/ */
@Getter
@EqualsAndHashCode
@ToString
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class DdiConfirmationFeedback { public class DdiConfirmationFeedback {
@NotNull
@Valid
private final Confirmation confirmation;
@Schema(example = "200")
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 { public enum Confirmation {
/** /**
* Confirm the action. * Confirm the action.
@@ -94,8 +59,37 @@ public class DdiConfirmationFeedback {
} }
} }
@Override @NotNull
public String toString() { @Valid
return "DdiConfirmationFeedback [confirmation=" + confirmation + ", code=" + code + ", details=" + details +"]"; @Schema(description = "Action confirmation state")
private final Confirmation confirmation;
@Schema(description = "(Optional) Individual status code", example = "200")
private final Integer code;
@Schema(description = "List of detailed message information", example = "[ \"Feedback message\" ]")
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 List<String> getDetails() {
if (details == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(details);
}
}

View File

@@ -9,6 +9,11 @@
*/ */
package org.eclipse.hawkbit.ddi.json.model; package org.eclipse.hawkbit.ddi.json.model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.hateoas.RepresentationModel; import org.springframework.hateoas.RepresentationModel;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@@ -19,8 +24,37 @@ import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* {@link DdiControllerBase} resource content. * {@link DdiControllerBase} resource content.
*/ */
@NoArgsConstructor // needed for json deserialization
@Getter
@EqualsAndHashCode(callSuper = true)
@ToString
@JsonInclude(Include.NON_NULL) @JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@Schema(description = """
**_links**: Actions that the server has for the target
* **deploymentBase** - Detailed deployment operation
* **installedBase** - Detailed operation of last successfully finished action
* **configData** - Link which is provided whenever the provisioning target or device is supposed to push its configuration data (aka. "controller attributes") to the server. Only shown for the initial configuration, after a successful update action, or if requested explicitly (e.g. via the management UI)
""",
example = """
{
"config" : {
"polling" : {
"sleep" : "12:00:00"
}
},
"_links" : {
"deploymentBase" : {
"href" : "https://management-api.host.com/TENANT_ID/controller/v1/CONTROLLER_ID/deploymentBase/5?c=-2127183556"
},
"installedBase" : {
"href" : "https://management-api.host.com/TENANT_ID/controller/v1/CONTROLLER_ID/installedBase/4"
},
"configData" : {
"href" : "https://management-api.host.com/TENANT_ID/controller/v1/CONTROLLER_ID/configData"
}
}
}""")
public class DdiControllerBase extends RepresentationModel<DdiControllerBase> { public class DdiControllerBase extends RepresentationModel<DdiControllerBase> {
@JsonProperty @JsonProperty
@@ -35,13 +69,4 @@ public class DdiControllerBase extends RepresentationModel<DdiControllerBase> {
public DdiControllerBase(final DdiConfig config) { public DdiControllerBase(final DdiConfig config) {
this.config = config; this.config = config;
} }
}
public DdiControllerBase() {
// needed for json create
}
public DdiConfig getConfig() {
return config;
}
}

View File

@@ -12,83 +12,29 @@ package org.eclipse.hawkbit.ddi.json.model;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.annotation.JsonValue;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
/** /**
* Detailed update action information. * Detailed update action information.
*/ */
@NoArgsConstructor // needed for json create
@Getter
@EqualsAndHashCode
@ToString
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class DdiDeployment { public class DdiDeployment {
private HandlingType download;
private HandlingType update;
@JsonProperty("chunks")
@NotNull
private List<DdiChunk> chunks;
private DdiMaintenanceWindowStatus maintenanceWindow;
/**
* Constructor.
*/
public DdiDeployment() {
// needed for json create.
}
/**
* Constructor.
*
* @param download
* handling type
* @param update
* handling type
* @param chunks
* to handle.
* @param maintenanceWindow
* specifying whether there is a maintenance schedule associated.
* If it is, the value is either 'available' (i.e. the
* maintenance window is now available as per defined schedule
* and the update can progress) or 'unavailable' (implying that
* maintenance window is not available now and update should not
* be attempted). If there is no maintenance schedule defined,
* the parameter is null.
*/
public DdiDeployment(final HandlingType download, final HandlingType update, final List<DdiChunk> chunks,
final DdiMaintenanceWindowStatus maintenanceWindow) {
this.download = download;
this.update = update;
this.chunks = chunks;
this.maintenanceWindow = maintenanceWindow;
}
public HandlingType getDownload() {
return download;
}
public HandlingType getUpdate() {
return update;
}
public List<DdiChunk> getChunks() {
if (chunks == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(chunks);
}
public DdiMaintenanceWindowStatus getMaintenanceWindow() {
return this.maintenanceWindow;
}
/** /**
* The handling type for the update action. * The handling type for the update action.
*/ */
@@ -152,10 +98,54 @@ public class DdiDeployment {
} }
} }
@Override @Schema(description = """
public String toString() { Handling for the download part of the provisioning process ('skip': do not download yet, 'attempt': server asks
return "Deployment [download=" + download + ", update=" + update + ", chunks=" + chunks to download, 'forced': server requests immediate download)""")
+ (maintenanceWindow == null ? "]" : (", maintenanceWindow=" + maintenanceWindow + "]")); private HandlingType download;
@Schema(description = """
Handling for the update part of the provisioning process ('skip': do not update yet,
'attempt': server asks to update, 'forced': server requests immediate update)""")
private HandlingType update;
@JsonProperty("chunks")
@NotNull
@Schema(description = "Software chunks of an update. In server mapped by Software Module")
private List<DdiChunk> chunks;
@Schema(description = """
Separation of download and installation by defining a maintenance window for the installation. Status shows if
currently in a window""")
private DdiMaintenanceWindowStatus maintenanceWindow;
/**
* Constructor.
*
* @param download handling type
* @param update handling type
* @param chunks to handle.
* @param maintenanceWindow
* specifying whether there is a maintenance schedule associated.
* If it is, the value is either 'available' (i.e. the
* maintenance window is now available as per defined schedule
* and the update can progress) or 'unavailable' (implying that
* maintenance window is not available now and update should not
* be attempted). If there is no maintenance schedule defined,
* the parameter is null.
*/
public DdiDeployment(final HandlingType download, final HandlingType update, final List<DdiChunk> chunks,
final DdiMaintenanceWindowStatus maintenanceWindow) {
this.download = download;
this.update = update;
this.chunks = chunks;
this.maintenanceWindow = maintenanceWindow;
} }
} public List<DdiChunk> getChunks() {
if (chunks == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(chunks);
}
}

View File

@@ -12,6 +12,10 @@ package org.eclipse.hawkbit.ddi.json.model;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.hateoas.RepresentationModel; import org.springframework.hateoas.RepresentationModel;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@@ -22,17 +26,187 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
/** /**
* Update action resource. * Update action resource.
*/ */
@NoArgsConstructor // needed for json create
@Getter
@EqualsAndHashCode(callSuper = true)
@ToString
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonPropertyOrder({ "id", "deployment", "actionHistory" }) @JsonPropertyOrder({ "id", "deployment", "actionHistory" })
@Schema(example = """
{
"id" : "8",
"deployment" : {
"download" : "forced",
"update" : "forced",
"maintenanceWindow" : "available",
"chunks" : [ {
"part" : "jvm",
"version" : "1.0.75",
"name" : "oneapp runtime",
"artifacts" : [ {
"filename" : "binary.tgz",
"hashes" : {
"sha1" : "986a1ade8b8a2f758ce951340cc5e21335cc2a00",
"md5" : "d04440e6533863247655ac5fd4345bcc",
"sha256" : "b3a04740a19e36057ccf258701922f3cd2f1a880536be53a3ca8d50f6b615975"
},
"size" : 13,
"_links" : {
"download" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/23/filename/binary.tgz"
},
"download-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/23/filename/binary.tgz"
},
"md5sum-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/23/filename/binary.tgz.MD5SUM"
},
"md5sum" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/23/filename/binary.tgz.MD5SUM"
}
}
}, {
"filename" : "file.signature",
"hashes" : {
"sha1" : "986a1ade8b8a2f758ce951340cc5e21335cc2a00",
"md5" : "d04440e6533863247655ac5fd4345bcc",
"sha256" : "b3a04740a19e36057ccf258701922f3cd2f1a880536be53a3ca8d50f6b615975"
},
"size" : 13,
"_links" : {
"download" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/23/filename/file.signature"
},
"download-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/23/filename/file.signature"
},
"md5sum-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/23/filename/file.signature.MD5SUM"
},
"md5sum" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/23/filename/file.signature.MD5SUM"
}
}
} ]
}, {
"part" : "os",
"version" : "1.0.79",
"name" : "one Firmware",
"artifacts" : [ {
"filename" : "binary.tgz",
"hashes" : {
"sha1" : "574cd34be20f75d101ed23518339cc38c5157bdb",
"md5" : "a0637c1ccb9fd53e2ba6f45712516989",
"sha256" : "498014801aab66be1d7fbea56b1aa5959651b6fd710308e196a8c414029e7291"
},
"size" : 13,
"_links" : {
"download" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/24/filename/binary.tgz"
},
"download-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/24/filename/binary.tgz"
},
"md5sum-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/24/filename/binary.tgz.MD5SUM"
},
"md5sum" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/24/filename/binary.tgz.MD5SUM"
}
}
}, {
"filename" : "file.signature",
"hashes" : {
"sha1" : "574cd34be20f75d101ed23518339cc38c5157bdb",
"md5" : "a0637c1ccb9fd53e2ba6f45712516989",
"sha256" : "498014801aab66be1d7fbea56b1aa5959651b6fd710308e196a8c414029e7291"
},
"size" : 13,
"_links" : {
"download" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/24/filename/file.signature"
},
"download-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/24/filename/file.signature"
},
"md5sum-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/24/filename/file.signature.MD5SUM"
},
"md5sum" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/24/filename/file.signature.MD5SUM"
}
}
} ]
}, {
"part" : "bApp",
"version" : "1.0.91",
"name" : "oneapplication",
"artifacts" : [ {
"filename" : "binary.tgz",
"hashes" : {
"sha1" : "e3ba7ff5839c210c98e254dde655147ffc49f5c9",
"md5" : "020017c498e6b0b8f76168fd55fa6fd1",
"sha256" : "80406288820379a82bbcbfbf7e8690146e46256f505de1c6d430c0168a74f6dd"
},
"size" : 11,
"_links" : {
"download" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/22/filename/binary.tgz"
},
"download-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/22/filename/binary.tgz"
},
"md5sum-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/22/filename/binary.tgz.MD5SUM"
},
"md5sum" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/22/filename/binary.tgz.MD5SUM"
}
}
}, {
"filename" : "file.signature",
"hashes" : {
"sha1" : "e3ba7ff5839c210c98e254dde655147ffc49f5c9",
"md5" : "020017c498e6b0b8f76168fd55fa6fd1",
"sha256" : "80406288820379a82bbcbfbf7e8690146e46256f505de1c6d430c0168a74f6dd"
},
"size" : 11,
"_links" : {
"download" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/22/filename/file.signature"
},
"download-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/22/filename/file.signature"
},
"md5sum-http" : {
"href" : "http://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/22/filename/file.signature.MD5SUM"
},
"md5sum" : {
"href" : "https://link-to-cdn.com/api/v1/TENANT_ID/download/controller/CONTROLLER_ID/softwaremodules/22/filename/file.signature.MD5SUM"
}
}
} ],
"metadata" : [ {
"key" : "aMetadataKey",
"value" : "Metadata value as defined in software module"
} ]
} ]
},
"actionHistory" : {
"status" : "RUNNING",
"messages" : [ "Reboot", "Write firmware", "Download done", "Download failed. ErrorCode #5876745. Retry", "Started download", "Assignment initiated by user 'TestPrincipal'" ]
}
}""")
public class DdiDeploymentBase extends RepresentationModel<DdiDeploymentBase> { public class DdiDeploymentBase extends RepresentationModel<DdiDeploymentBase> {
@JsonProperty("id") @JsonProperty("id")
@NotNull @NotNull
@Schema(example = "8") @Schema(description = "Id of the action", example = "8")
private String id; private String id;
@JsonProperty("deployment") @JsonProperty("deployment")
@NotNull @NotNull
@Schema(description = "Detailed deployment operation")
private DdiDeployment deployment; private DdiDeployment deployment;
/** /**
@@ -41,15 +215,9 @@ public class DdiDeploymentBase extends RepresentationModel<DdiDeploymentBase> {
*/ */
@JsonProperty("actionHistory") @JsonProperty("actionHistory")
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
@Schema(description = "Current deployment state")
private DdiActionHistory actionHistory; private DdiActionHistory actionHistory;
/**
* Constructor.
*/
public DdiDeploymentBase() {
// needed for json create.
}
/** /**
* Constructor. * Constructor.
* *
@@ -66,24 +234,4 @@ public class DdiDeploymentBase extends RepresentationModel<DdiDeploymentBase> {
this.deployment = deployment; this.deployment = deployment;
this.actionHistory = actionHistory; this.actionHistory = actionHistory;
} }
}
public DdiDeployment getDeployment() {
return deployment;
}
/**
* 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 "DeploymentBase [id=" + id + ", deployment=" + deployment + " actionHistory=" + actionHistory + "]";
}
}

View File

@@ -15,21 +15,27 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
/** /**
* Additional metadata to be provided for the target/device. * Additional metadata to be provided for the target/device.
*
*/ */
@Getter
@EqualsAndHashCode
@ToString
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class DdiMetadata { public class DdiMetadata {
@JsonProperty @JsonProperty
@NotNull @NotNull
@Schema(example = "aMetadataKey") @Schema(description = "Key of meta data entry")
private final String key; private final String key;
@JsonProperty @JsonProperty
@NotNull @NotNull
@Schema(example = "Metadata value as defined in software module") @Schema(description = "Value of meta data entry")
private final String value; private final String value;
@JsonCreator @JsonCreator
@@ -37,13 +43,4 @@ public class DdiMetadata {
this.key = key; this.key = key;
this.value = value; this.value = value;
} }
}
public String getKey() {
return key;
}
public String getValue() {
return value;
}
}

View File

@@ -13,9 +13,6 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import com.fasterxml.jackson.annotation.JsonValue;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
/** /**
@@ -23,10 +20,11 @@ import io.swagger.v3.oas.annotations.media.Schema;
*/ */
@JsonInclude(Include.NON_NULL) @JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@Schema(description = "Suggested sleep time between polls")
public class DdiPolling { public class DdiPolling {
@JsonProperty @JsonProperty
@Schema(example = "12:00:00") @Schema(description = "Sleep time in HH:MM:SS notation", pattern = "HH:MM:SS", example = "12:00:00")
private String sleep; private String sleep;
/** /**
@@ -50,5 +48,4 @@ public class DdiPolling {
public String getSleep() { public String getSleep() {
return sleep; return sleep;
} }
}
}

View File

@@ -15,46 +15,36 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
/** /**
* Action fulfillment progress by means of gives the achieved amount of maximal * Action fulfillment progress by means of gives the achieved amount of maximal
* of possible levels. * of possible levels.
*/ */
@Getter
@EqualsAndHashCode
@ToString
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class DdiProgress { public class DdiProgress {
@NotNull @NotNull
@Schema(example = "2") @Schema(description = "Achieved amount", example = "2")
private final Integer cnt; private final Integer cnt;
@Schema(example = "5") @Schema(description = "Maximum levels", example = "5")
private final Integer of; private final Integer of;
/** /**
* Constructor. * Constructor.
* *
* @param cnt * @param cnt achieved amount
* achieved amount * @param of maximum levels
* @param of
* maximum levels
*/ */
@JsonCreator @JsonCreator
public DdiProgress(@JsonProperty("cnt") final Integer cnt, @JsonProperty("of") final Integer of) { public DdiProgress(@JsonProperty("cnt") final Integer cnt, @JsonProperty("of") final Integer of) {
this.cnt = cnt; this.cnt = cnt;
this.of = of; this.of = of;
} }
}
public Integer getCnt() {
return cnt;
}
public Integer getOf() {
return of;
}
@Override
public String toString() {
return "Progress [cnt=" + cnt + ", of=" + of + "]";
}
}

View File

@@ -17,43 +17,20 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.annotation.JsonValue;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
/** /**
* Result information of the action progress which can by an intermediate or * Result information of the action progress which can by an intermediate or
* final update. * final update.
*/ */
@Getter
@EqualsAndHashCode
@ToString
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class DdiResult { public class DdiResult {
@NotNull
@Valid
private final FinalResult finished;
private final DdiProgress progress;
/**
* Constructor.
*
* @param finished
* as final result
* @param progress
* if not yet finished
*/
@JsonCreator
public DdiResult(@JsonProperty("finished") final FinalResult finished,
@JsonProperty("progress") final DdiProgress progress) {
this.finished = finished;
this.progress = progress;
}
public FinalResult getFinished() {
return finished;
}
public DdiProgress getProgress() {
return progress;
}
/** /**
* Defined status of the final result. * Defined status of the final result.
* *
@@ -86,9 +63,26 @@ public class DdiResult {
} }
} }
@Override @NotNull
public String toString() { @Valid
return "Result [finished=" + finished + ", progress=" + progress + "]"; @Schema(description = "Result of the action execution")
} private final FinalResult finished;
} @Schema(description = "Progress assumption of the device (currently not supported)")
private final DdiProgress progress;
/**
* Constructor.
*
* @param finished
* as final result
* @param progress
* if not yet finished
*/
@JsonCreator
public DdiResult(@JsonProperty("finished") final FinalResult finished,
@JsonProperty("progress") final DdiProgress progress) {
this.finished = finished;
this.progress = progress;
}
}

View File

@@ -20,68 +20,20 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.annotation.JsonValue;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
/** /**
* Details status information concerning the action processing. * Details status information concerning the action processing.
*/ */
@Getter
@EqualsAndHashCode
@ToString
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@Schema(description = "Target action status")
public class DdiStatus { public class DdiStatus {
@NotNull
@Valid
private final ExecutionStatus execution;
@NotNull
@Valid
private final DdiResult result;
@Schema(example = "200")
private final Integer code;
private final List<String> details;
/**
* Constructor.
*
* @param execution
* status
* @param result
* information
* @param code
* as optional code (can be null)
* @param details
* as optional addition
*/
@JsonCreator
public DdiStatus(@JsonProperty("execution") final ExecutionStatus execution,
@JsonProperty("result") final DdiResult result, @JsonProperty("code") final Integer code,
@JsonProperty("details") final List<String> details) {
this.execution = execution;
this.result = result;
this.code = code;
this.details = details;
}
public ExecutionStatus getExecution() {
return execution;
}
public DdiResult getResult() {
return result;
}
public List<String> getDetails() {
if (details == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(details);
}
public Integer getCode() {
return code;
}
/** /**
* The element status contains information about the execution of the * The element status contains information about the execution of the
* operation. * operation.
@@ -140,10 +92,49 @@ public class DdiStatus {
} }
} }
@Override @NotNull
public String toString() { @Valid
return "Status [execution=" + execution + ", result=" + result + ", code=" @Schema(description = "Status of the action execution")
+ code + ", details=" + details + "]"; private final ExecutionStatus execution;
@NotNull
@Valid
@Schema(description = "Result of the action execution")
private final DdiResult result;
@Schema(description = "(Optional) Individual status code", example = "200")
private final Integer code;
@Schema(description = "List of details message information", example = "[ \"Some feedback\" ]")
private final List<String> details;
/**
* Constructor.
*
* @param execution
* status
* @param result
* information
* @param code
* as optional code (can be null)
* @param details
* as optional addition
*/
@JsonCreator
public DdiStatus(@JsonProperty("execution") final ExecutionStatus execution,
@JsonProperty("result") final DdiResult result, @JsonProperty("code") final Integer code,
@JsonProperty("details") final List<String> details) {
this.execution = execution;
this.result = result;
this.code = code;
this.details = details;
} }
} public List<String> getDetails() {
if (details == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(details);
}
}

View File

@@ -45,5 +45,4 @@ public enum DdiUpdateMode {
public String getName() { public String getName() {
return name; return name;
} }
}
}

View File

@@ -67,7 +67,7 @@ public final class DdiRestConstants {
/** /**
* Default value specifying that no action history to be sent as part of * Default value specifying that no action history to be sent as part of
* response to deploymentBase * response to deploymentBase
* {@link DdiRootControllerRestApi#getControllerBasedeploymentAction}. * {@link DdiRootControllerRestApi#getControllerDeploymentBaseAction}.
* {@link DdiRootControllerRestApi#getConfirmationBaseAction}. * {@link DdiRootControllerRestApi#getConfirmationBaseAction}.
*/ */
public static final String NO_ACTION_HISTORY = "0"; public static final String NO_ACTION_HISTORY = "0";

View File

@@ -56,23 +56,29 @@ public interface DdiRootControllerRestApi {
/** /**
* Returns all artifacts of a given software module and target. * Returns all artifacts of a given software module and target.
* *
* @param tenant * @param tenant of the client
* of the client * @param controllerId of the target that matches to controller id
* @param controllerId * @param softwareModuleId of the software module
* of the target that matches to controller id
* @param softwareModuleId
* of the software module
* @return the response * @return the response
*/ */
@Operation(summary = "Return all artifacts of a given software module and target", description = "Returns all artifacts that are assigned to the software module") @Operation(summary = "Return all artifacts of a given software module and target",
description = "Returns all artifacts that are assigned to the software module")
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"), @ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))), @ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
@ApiResponse(responseCode = "401", description = "The request requires user authentication.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
@ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "401", description = "The request requires user authentication.",
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be " +
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts and the client has to wait another second.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))) "changed (i.e. read-only) or data volume restriction applies.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
"and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
}) })
@GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING @GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING
+ "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts", produces = { MediaTypes.HAL_JSON_VALUE, + "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts", produces = { MediaTypes.HAL_JSON_VALUE,
@@ -84,26 +90,35 @@ public interface DdiRootControllerRestApi {
/** /**
* Root resource for an individual {@link Target}. * Root resource for an individual {@link Target}.
* *
* @param tenant * @param tenant of the request
* of the request * @param controllerId of the target that matches to controller id
* @param controllerId
* of the target that matches to controller id
* *
* @return the response * @return the response
*/ */
@Operation(summary = "Root resource for an individual Target", description = """ @Operation(summary = "Root resource for an individual Target", description = """
This base resource can be regularly polled by the controller on the provisioning target or device in order to retrieve actions that need to be executed. Those are provided as a list of links to give more detailed information about the action. Links are only available for initial configuration, open actions, or the latest installed action, respectively. The resource supports Etag based modification checks in order to save traffic. This base resource can be regularly polled by the controller on the provisioning target or device in order to
retrieve actions that need to be executed. Those are provided as a list of links to give more detailed
information about the action. Links are only available for initial configuration, open actions, or the latest
installed action, respectively. The resource supports Etag based modification checks in order to save traffic.
Note: deployments have to be confirmed in order to move on to the next action. Cancellations have to be confirmed or rejected. Note: deployments have to be confirmed in order to move on to the next action. Cancellations have to be
""") confirmed or rejected.""")
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "In this case the (optional) query for the last 10 messages, previously provided by the device, are included. Useful if the devices provide state information previously on the feedback channel and wont store it locally."), @ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))), @ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
@ApiResponse(responseCode = "401", description = "The request requires user authentication.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
@ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "401", description = "The request requires user authentication.",
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to " +
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts and the client has to wait another second.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))) "be changed (i.e. read-only) or data volume restriction applies.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
"and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
}) })
@GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}", produces = { @GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE, DdiRestConstants.MEDIA_TYPE_CBOR }) MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE, DdiRestConstants.MEDIA_TYPE_CBOR })
@@ -114,29 +129,35 @@ public interface DdiRootControllerRestApi {
* Handles GET {@link DdiArtifact} download request. This could be full or * Handles GET {@link DdiArtifact} download request. This could be full or
* partial (as specified by RFC7233 (Range Requests)) download request. * partial (as specified by RFC7233 (Range Requests)) download request.
* *
* @param tenant * @param tenant of the request
* of the request * @param controllerId of the target
* @param controllerId * @param softwareModuleId of the parent software module
* of the target * @param fileName of the related local artifact
* @param softwareModuleId
* of the parent software module
* @param fileName
* of the related local artifact
* *
* @return response of the servlet which in case of success is status code * @return response of the servlet which in case of success is status code
* {@link HttpStatus#OK} or in case of partial download * {@link HttpStatus#OK} or in case of partial download
* {@link HttpStatus#PARTIAL_CONTENT}. * {@link HttpStatus#PARTIAL_CONTENT}.
*/ */
@Operation(summary = "Artifact download", description = "Handles GET DdiArtifact download request. This could be full or partial (as specified by RFC7233 (Range Requests)) download request.") @Operation(summary = "Artifact download", description = "Handles GET DdiArtifact download request. This could be " +
"full or partial (as specified by RFC7233 (Range Requests)) download request.")
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"), @ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))), @ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
@ApiResponse(responseCode = "401", description = "The request requires user authentication.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
@ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "401", description = "The request requires user authentication.",
@ApiResponse(responseCode = "404", description = "Target or Module not found", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be" +
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), " changed (i.e. read-only) or data volume restriction applies.",
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts and the client has to wait another second.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))) content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", description = "Target or Module not found",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts" +
" and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
}) })
@GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING @GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING
+ "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{fileName}") + "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{fileName}")
@@ -160,16 +181,26 @@ public interface DdiRootControllerRestApi {
* @return {@link ResponseEntity} with status {@link HttpStatus#OK} if * @return {@link ResponseEntity} with status {@link HttpStatus#OK} if
* successful * successful
*/ */
@Operation(summary = "MD5 checksum download", description = "Handles GET {@link DdiArtifact} MD5 checksum file download request.") @Operation(summary = "MD5 checksum download",
description = "Handles GET {@link DdiArtifact} MD5 checksum file download request.")
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"), @ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))), @ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
@ApiResponse(responseCode = "401", description = "The request requires user authentication.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
@ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "401", description = "The request requires user authentication.",
@ApiResponse(responseCode = "404", description = "Target or Module not found", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be " +
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), "changed (i.e. read-only) or data volume restriction applies.",
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts and the client has to wait another second.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))) content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", description = "Target or Module not found",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
"and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
}) })
@GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING @GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING
+ "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{fileName}" + "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{fileName}"
@@ -215,27 +246,60 @@ public interface DdiRootControllerRestApi {
@Operation(summary = "Resource for software module (Deployment Base)", description = """ @Operation(summary = "Resource for software module (Deployment Base)", description = """
Core resource for deployment operations. Contains all information necessary in order to execute the operation. Core resource for deployment operations. Contains all information necessary in order to execute the operation.
Keep in mind that the provided download links for the artifacts are generated dynamically by the update server. Host, port and path and not guaranteed to be similar to the provided examples below but will be defined at runtime. Keep in mind that the provided download links for the artifacts are generated dynamically by the update server.
Host, port and path and not guaranteed to be similar to the provided examples below but will be defined at
runtime.
""") """)
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"), @ApiResponse(responseCode = "200", description = """
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))), Successfully retrieved
@ApiResponse(responseCode = "401", description = "The request requires user authentication.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), In case a device provides state information on the feedback channel and wont store it locally,
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), a query for, e.q, the last 10 messages, could be used which will include the previously provided by the
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), device,
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts and the client has to wait another second.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))) feedback.
In addition to the straight forward approach to inform the device to download and install the software
in one transaction hawkBit supports the separation of download and installation into separate steps.
This feature is called Maintenance Window where the device is informed to download the software first
and then when it enters a defined (maintenance) window the installation triggers follows as usual.
"""),
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = ExceptionInfo.class))),
@ApiResponse(responseCode = "401", description = "The request requires user authentication.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403",
description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or " +
"data volume restriction applies.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
"and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
}) })
@GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/" @GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/"
+ DdiRestConstants.DEPLOYMENT_BASE_ACTION + "/{actionId}", produces = { MediaTypes.HAL_JSON_VALUE, + DdiRestConstants.DEPLOYMENT_BASE_ACTION + "/{actionId}", produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE, DdiRestConstants.MEDIA_TYPE_CBOR }) MediaType.APPLICATION_JSON_VALUE, DdiRestConstants.MEDIA_TYPE_CBOR })
ResponseEntity<DdiDeploymentBase> getControllerBasedeploymentAction(@PathVariable("tenant") final String tenant, ResponseEntity<DdiDeploymentBase> getControllerDeploymentBaseAction(@PathVariable("tenant") final String tenant,
@PathVariable("controllerId") @NotEmpty final String controllerId, @PathVariable("controllerId") @NotEmpty
@PathVariable("actionId") @NotNull final Long actionId, final String controllerId,
@RequestParam(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING @PathVariable("actionId") @NotNull
+ "c", required = false, defaultValue = "-1") final int resource, final Long actionId,
@RequestParam(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING @RequestParam(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "c", required = false, defaultValue = "-1")
+ "actionHistory", defaultValue = DdiRestConstants.NO_ACTION_HISTORY) final Integer actionHistoryMessageCount); final int resource,
@RequestParam(
value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "actionHistory",
defaultValue = DdiRestConstants.NO_ACTION_HISTORY)
@Schema(description = """
(Optional) GET parameter to retrieve a given number of messages which are previously provided by the
device. Useful if the devices sent state information to the feedback channel and never stored them
locally.""")
final Integer actionHistoryMessageCount);
/** /**
* This is the feedback channel for the {@link DdiDeploymentBase} action. * This is the feedback channel for the {@link DdiDeploymentBase} action.
@@ -253,25 +317,42 @@ public interface DdiRootControllerRestApi {
*/ */
@Operation(summary = "Feedback channel for the DeploymentBase action", description = """ @Operation(summary = "Feedback channel for the DeploymentBase action", description = """
Feedback channel. It is up to the device how much intermediate feedback is provided. Feedback channel. It is up to the device how much intermediate feedback is provided.
However, the action will be kept open until the controller on the device reports a finished (either successful or error). However, the action will be kept open until the controller on the device reports a finished (either successful
or error).
""") """)
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"), @ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))), @ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
@ApiResponse(responseCode = "401", description = "The request requires user authentication.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
@ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "401", description = "The request requires user authentication.",
@ApiResponse(responseCode = "404", description = "Target not found", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be " +
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), "changed (i.e. read-only) or data volume restriction applies.",
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another user in another request at the same time. You may retry your modification request.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "410", description = "Action is not active anymore.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "404", description = "Target not found",
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not supported by the server for this resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts and the client has to wait another second.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))) @ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another " +
"user in another request at the same time. You may retry your modification request.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "410", description = "Action is not active anymore.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not " +
"supported by the server for this resource.",
content = @Content(mediaType = "application/json",
schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
"and the client has to wait another second.",
content = @Content(mediaType = "application/json",
schema = @Schema(hidden = true)))
}) })
@PostMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/" @PostMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/"
+ DdiRestConstants.DEPLOYMENT_BASE_ACTION + "/{actionId}/" + DdiRestConstants.FEEDBACK, consumes = { + DdiRestConstants.DEPLOYMENT_BASE_ACTION + "/{actionId}/" + DdiRestConstants.FEEDBACK, consumes = {
MediaType.APPLICATION_JSON_VALUE, DdiRestConstants.MEDIA_TYPE_CBOR }) MediaType.APPLICATION_JSON_VALUE, DdiRestConstants.MEDIA_TYPE_CBOR })
ResponseEntity<Void> postBasedeploymentActionFeedback(@Valid final DdiActionFeedback feedback, ResponseEntity<Void> postDeploymentBaseActionFeedback(@Valid final DdiActionFeedback feedback,
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId, @PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
@PathVariable("actionId") @NotNull final Long actionId); @PathVariable("actionId") @NotNull final Long actionId);
@@ -288,18 +369,31 @@ public interface DdiRootControllerRestApi {
* @return status of the request * @return status of the request
*/ */
@Operation(summary = "Feedback channel for the config data action", description = """ @Operation(summary = "Feedback channel for the config data action", description = """
The usual behaviour is that when a new device registers at the server it is requested to provide the meta information that will allow the server to identify the device on a hardware level (e.g. hardware revision, mac address, serial number etc.). The usual behaviour is that when a new device registers at the server it is requested to provide the meta
""") information that will allow the server to identify the device on a hardware level (e.g. hardware revision,
mac address, serial number etc.).""")
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"), @ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))), @ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
@ApiResponse(responseCode = "401", description = "The request requires user authentication.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
@ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "401", description = "The request requires user authentication.",
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be " +
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another user in another request at the same time. You may retry your modification request.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), "changed (i.e. read-only) or data volume restriction applies.",
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not supported by the server for this resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts and the client has to wait another second.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))) @ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another " +
"user in another request at the same time. You may retry your modification request.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not " +
"supported by the server for this resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
"and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
}) })
@PutMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/" @PutMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/"
+ DdiRestConstants.CONFIG_DATA_ACTION, consumes = { MediaType.APPLICATION_JSON_VALUE, + DdiRestConstants.CONFIG_DATA_ACTION, consumes = { MediaType.APPLICATION_JSON_VALUE,
@@ -320,16 +414,24 @@ public interface DdiRootControllerRestApi {
* @return the {@link DdiCancel} response * @return the {@link DdiCancel} response
*/ */
@Operation(summary = "Cancel an action", description = """ @Operation(summary = "Cancel an action", description = """
The Hawkbit server might cancel an operation, e.g. an unfinished update has a successor. It is up to the provisioning target to decide to accept the cancelation or reject it. The Hawkbit server might cancel an operation, e.g. an unfinished update has a successor. It is up to the
""") provisioning target to decide to accept the cancelation or reject it.""")
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"), @ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))), @ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
@ApiResponse(responseCode = "401", description = "The request requires user authentication.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
@ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "401", description = "The request requires user authentication.",
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be " +
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts and the client has to wait another second.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))) "changed (i.e. read-only) or data volume restriction applies.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
"and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
}) })
@GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/" + DdiRestConstants.CANCEL_ACTION @GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/" + DdiRestConstants.CANCEL_ACTION
+ "/{actionId}", produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE, + "/{actionId}", produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE,
@@ -354,18 +456,31 @@ public interface DdiRootControllerRestApi {
* @return the {@link DdiActionFeedback} response * @return the {@link DdiActionFeedback} response
*/ */
@Operation(summary = "Feedback channel for cancel actions", description = """ @Operation(summary = "Feedback channel for cancel actions", description = """
It is up to the device how much intermediate feedback is provided. However, the action will be kept open until the controller on the device reports a finished (either successful or error) or rejects the action, e.g. the canceled actions have been started already. It is up to the device how much intermediate feedback is provided. However, the action will be kept open
""") until the controller on the device reports a finished (either successful or error) or rejects the action,
e.g. the canceled actions have been started already.""")
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"), @ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))), @ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
@ApiResponse(responseCode = "401", description = "The request requires user authentication.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
@ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "401", description = "The request requires user authentication.",
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be " +
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another user in another request at the same time. You may retry your modification request.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), "changed (i.e. read-only) or data volume restriction applies.",
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not supported by the server for this resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts and the client has to wait another second.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))) @ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another " +
"user in another request at the same time. You may retry your modification request.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not " +
"supported by the server for this resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
"and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
}) })
@PostMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/" + DdiRestConstants.CANCEL_ACTION @PostMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/" + DdiRestConstants.CANCEL_ACTION
+ "/{actionId}/" + DdiRestConstants.FEEDBACK, consumes = { MediaType.APPLICATION_JSON_VALUE, + "/{actionId}/" + DdiRestConstants.FEEDBACK, consumes = { MediaType.APPLICATION_JSON_VALUE,
@@ -407,21 +522,36 @@ public interface DdiRootControllerRestApi {
* for the /deploymentBase resource. * for the /deploymentBase resource.
*/ */
@Operation(summary = "Previously installed action", description = """ @Operation(summary = "Previously installed action", description = """
Resource to receive information of the previous installation. Can be used to re-retrieve artifacts of the already finished action, for example in case a re-installation is necessary. The response will be of the same format as the deploymentBase operation, providing the previous action that has been finished successfully. As the action is already finished, no further feedback is expected. Resource to receive information of the previous installation. Can be used to re-retrieve artifacts of
the already finished action, for example in case a re-installation is necessary. The response will be of
the same format as the deploymentBase operation, providing the previous action that has been finished
successfully. As the action is already finished, no further feedback is expected.
Keep in mind that the provided download links for the artifacts are generated dynamically by the update server. Host, port and path are not guaranteed to be similar to the provided examples below but will be defined at runtime. Keep in mind that the provided download links for the artifacts are generated dynamically by the update server.
Host, port and path are not guaranteed to be similar to the provided examples below but will be defined at
runtime.
""") """)
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = """ @ApiResponse(responseCode = "200", description = """
The response body includes the detailed operation for the already finished action in the same format as for the deploymentBase operation. The response body includes the detailed operation for the already finished action in the same format as
for the deploymentBase operation.
In this case the (optional) query for the last 10 messages, previously provided by the device, are included."""), In this case the (optional) query for the last 10 messages, previously provided by the device, are included.
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))), """),
@ApiResponse(responseCode = "401", description = "The request requires user authentication.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
@ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "401", description = "The request requires user authentication.",
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts and the client has to wait another second.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))) @ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be " +
"changed (i.e. read-only) or data volume restriction applies.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
"and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
}) })
@GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/" @GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/"
+ DdiRestConstants.INSTALLED_BASE_ACTION + "/{actionId}", produces = { MediaTypes.HAL_JSON_VALUE, + DdiRestConstants.INSTALLED_BASE_ACTION + "/{actionId}", produces = { MediaTypes.HAL_JSON_VALUE,
@@ -430,7 +560,8 @@ public interface DdiRootControllerRestApi {
@PathVariable("controllerId") @NotEmpty final String controllerId, @PathVariable("controllerId") @NotEmpty final String controllerId,
@PathVariable("actionId") @NotNull final Long actionId, @PathVariable("actionId") @NotNull final Long actionId,
@RequestParam(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING @RequestParam(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING
+ "actionHistory", defaultValue = DdiRestConstants.NO_ACTION_HISTORY) final Integer actionHistoryMessageCount); + "actionHistory", defaultValue = DdiRestConstants.NO_ACTION_HISTORY)
final Integer actionHistoryMessageCount);
/** /**
* Returns the confirmation base with the current auto-confirmation state * Returns the confirmation base with the current auto-confirmation state
@@ -438,14 +569,14 @@ public interface DdiRootControllerRestApi {
* present where the confirmation is required, a reference to it will be * present where the confirmation is required, a reference to it will be
* returned as well. * returned as well.
* *
* @param tenant * @param tenant the controllerId is corresponding too
* the controllerId is corresponding too * @param controllerId to check the state for
* @param controllerId
* to check the state for
* @return the state as {@link DdiAutoConfirmationState} * @return the state as {@link DdiAutoConfirmationState}
*/ */
@Operation(summary = "Resource to request confirmation specific information for the controller", description = """ @Operation(summary = "Resource to request confirmation specific information for the controller", description = """
Core resource for confirmation related operations. While active actions awaiting confirmation will be referenced, the current auto-confirmation status will be shown. In case auto-confirmation is active, details like the initiator, remark and date of activation (as unix timestamp) will be provided. Core resource for confirmation related operations. While active actions awaiting confirmation will be
referenced, the current auto-confirmation status will be shown. In case auto-confirmation is active, details
like the initiator, remark and date of activation (as unix timestamp) will be provided.
Reference links to switch the auto-confirmation state are exposed as well. Reference links to switch the auto-confirmation state are exposed as well.
""") """)
@ApiResponses(value = { @ApiResponses(value = {
@@ -453,12 +584,20 @@ public interface DdiRootControllerRestApi {
The response body in case auto-confirmation is active is richer - it contains additional information The response body in case auto-confirmation is active is richer - it contains additional information
such as initiator, remark and when the auto-confirmation had been activated. such as initiator, remark and when the auto-confirmation had been activated.
"""), """),
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))), @ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
@ApiResponse(responseCode = "401", description = "The request requires user authentication.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
@ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "401", description = "The request requires user authentication.",
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be " +
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts and the client has to wait another second.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))) "changed (i.e. read-only) or data volume restriction applies.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
"and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
}) })
@GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/" @GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/"
+ DdiRestConstants.CONFIRMATION_BASE, produces = { MediaTypes.HAL_JSON_VALUE, + DdiRestConstants.CONFIRMATION_BASE, produces = { MediaTypes.HAL_JSON_VALUE,
@@ -500,19 +639,33 @@ public interface DdiRootControllerRestApi {
* @return the response * @return the response
*/ */
@Operation(summary = "Confirmation status of an action", description = """ @Operation(summary = "Confirmation status of an action", description = """
Resource to receive information about a pending confirmation. The response will be of the same format as the deploymentBase operation. The controller should provide feedback about the confirmation first, before processing the deployment. Resource to receive information about a pending confirmation. The response will be of the same format as the
deploymentBase operation. The controller should provide feedback about the confirmation first, before
processing the deployment.
Keep in mind that the provided download links for the artifacts are generated dynamically by the update server. Host, port and path are not guaranteed to be similar to the provided examples below but will be defined at runtime. Keep in mind that the provided download links for the artifacts are generated dynamically by the update server.
Host, port and path are not guaranteed to be similar to the provided examples below but will be defined at
runtime.
""") """)
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "The response body includes the detailed information about the action awaiting confirmation in the same format as for the deploymentBase operation."), @ApiResponse(responseCode = "200", description = "The response body includes the detailed information about " +
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))), "the action awaiting confirmation in the same format as for the deploymentBase operation."),
@ApiResponse(responseCode = "401", description = "The request requires user authentication.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
@ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
@ApiResponse(responseCode = "404", description = "Target not found", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "401", description = "The request requires user authentication.",
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be " +
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts and the client has to wait another second.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))) "changed (i.e. read-only) or data volume restriction applies.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", description = "Target not found",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
"and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
}) })
@GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/" @GetMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/"
+ DdiRestConstants.CONFIRMATION_BASE + "/{actionId}", produces = { MediaTypes.HAL_JSON_VALUE, + DdiRestConstants.CONFIRMATION_BASE + "/{actionId}", produces = { MediaTypes.HAL_JSON_VALUE,
@@ -523,7 +676,8 @@ public interface DdiRootControllerRestApi {
@RequestParam(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING @RequestParam(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING
+ "c", required = false, defaultValue = "-1") final int resource, + "c", required = false, defaultValue = "-1") final int resource,
@RequestParam(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING @RequestParam(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING
+ "actionHistory", defaultValue = DdiRestConstants.NO_ACTION_HISTORY) final Integer actionHistoryMessageCount); + "actionHistory", defaultValue = DdiRestConstants.NO_ACTION_HISTORY)
final Integer actionHistoryMessageCount);
/** /**
* This is the feedback channel for the {@link DdiConfirmationBaseAction} * This is the feedback channel for the {@link DdiConfirmationBaseAction}
@@ -541,20 +695,36 @@ public interface DdiRootControllerRestApi {
* @return the response * @return the response
*/ */
@Operation(summary = "Feedback channel for actions waiting for confirmation", description = """ @Operation(summary = "Feedback channel for actions waiting for confirmation", description = """
The device will use this resource to either confirm or deny an action which is waiting for confirmation. The action will be transferred into the RUNNING state in case the device is confirming it. Afterwards it will be exposed by the deploymentBase. The device will use this resource to either confirm or deny an action which is waiting for confirmation. The
action will be transferred into the RUNNING state in case the device is confirming it. Afterwards it will be
exposed by the deploymentBase.
""") """)
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"), @ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))), @ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
@ApiResponse(responseCode = "401", description = "The request requires user authentication.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
@ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "401", description = "The request requires user authentication.",
@ApiResponse(responseCode = "404", description = "Target or Action not found", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be " +
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), "changed (i.e. read-only) or data volume restriction applies.",
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another user in another request at the same time. You may retry your modification request.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "410", description = "Action is not active anymore.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "404", description = "Target or Action not found",
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not supported by the server for this resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts and the client has to wait another second.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))) @ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another " +
"user in another request at the same time. You may retry your modification request.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "410", description = "Action is not active anymore.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not " +
"supported by the server for this resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
"and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
}) })
@PostMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/" @PostMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/"
+ DdiRestConstants.CONFIRMATION_BASE + "/{actionId}/" + DdiRestConstants.FEEDBACK, consumes = { + DdiRestConstants.CONFIRMATION_BASE + "/{actionId}/" + DdiRestConstants.FEEDBACK, consumes = {
@@ -580,19 +750,34 @@ public interface DdiRootControllerRestApi {
* auto-confirmation was active already. * auto-confirmation was active already.
*/ */
@Operation(summary = "Interface to activate auto-confirmation for a specific device", description = """ @Operation(summary = "Interface to activate auto-confirmation for a specific device", description = """
The device can use this resource to activate auto-confirmation. As a result all current active as well as future actions will automatically be confirmed by mentioning the initiator as triggered person. Actions will be automatically confirmed, as long as auto-confirmation is active. The device can use this resource to activate auto-confirmation. As a result all current active as well as
future actions will automatically be confirmed by mentioning the initiator as triggered person. Actions will
be automatically confirmed, as long as auto-confirmation is active.
""") """)
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"), @ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))), @ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
@ApiResponse(responseCode = "401", description = "The request requires user authentication.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
@ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "401", description = "The request requires user authentication.",
@ApiResponse(responseCode = "404", description = "Target not found", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be " +
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), "changed (i.e. read-only) or data volume restriction applies.",
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another user in another request at the same time. You may retry your modification request.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not supported by the server for this resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "404", description = "Target not found",
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts and the client has to wait another second.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))) content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another " +
"user in another request at the same time. You may retry your modification request.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not " +
"supported by the server for this resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
"and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
}) })
@PostMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/" @PostMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/"
+ DdiRestConstants.CONFIRMATION_BASE + "/" + DdiRestConstants.AUTO_CONFIRM_ACTIVATE, consumes = { + DdiRestConstants.CONFIRMATION_BASE + "/" + DdiRestConstants.AUTO_CONFIRM_ACTIVATE, consumes = {
@@ -604,30 +789,41 @@ public interface DdiRootControllerRestApi {
/** /**
* Deactivate auto confirmation for a given controller id. * Deactivate auto confirmation for a given controller id.
* *
* @param tenant * @param tenant the controllerId is corresponding too
* the controllerId is corresponding too * @param controllerId to disable auto-confirmation for
* @param controllerId * @return {@link org.springframework.http.HttpStatus#OK} if successfully executed
* to disable auto-confirmation for
* @return {@link org.springframework.http.HttpStatus#OK} if successfully
* executed
*/ */
@Operation(summary = "Interface to deactivate auto-confirmation for a specific controller", description = """ @Operation(summary = "Interface to deactivate auto-confirmation for a specific controller", description = """
The device can use this resource to deactivate auto-confirmation. All active actions will remain unchanged while all future actions need to be confirmed, before processing with the deployment. The device can use this resource to deactivate auto-confirmation. All active actions will remain unchanged
while all future actions need to be confirmed, before processing with the deployment.
""") """)
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"), @ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))), @ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
@ApiResponse(responseCode = "401", description = "The request requires user authentication.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
@ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or data volume restriction applies.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "401", description = "The request requires user authentication.",
@ApiResponse(responseCode = "404", description = "Target not found", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "403", description = "Insufficient permissions, entity is not allowed to be " +
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), "changed (i.e. read-only) or data volume restriction applies.",
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another user in another request at the same time. You may retry your modification request.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not supported by the server for this resource.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))), @ApiResponse(responseCode = "404", description = "Target not found",
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts and the client has to wait another second.", content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))) content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "409", description = "E.g. in case an entity is created or modified by another " +
"user in another request at the same time. You may retry your modification request.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "415", description = "The request was attempt with a media-type which is not " +
"supported by the server for this resource.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
"and the client has to wait another second.",
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
}) })
@PostMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/" @PostMapping(value = DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/"
+ DdiRestConstants.CONFIRMATION_BASE + "/" + DdiRestConstants.AUTO_CONFIRM_DEACTIVATE) + DdiRestConstants.CONFIRMATION_BASE + "/" + DdiRestConstants.AUTO_CONFIRM_DEACTIVATE)
ResponseEntity<Void> deactivateAutoConfirmation(@PathVariable("tenant") final String tenant, ResponseEntity<Void> deactivateAutoConfirmation(@PathVariable("tenant") final String tenant,
@PathVariable("controllerId") @NotEmpty final String controllerId); @PathVariable("controllerId") @NotEmpty final String controllerId);
} }

View File

@@ -33,19 +33,19 @@ import io.qameta.allure.Story;
@Story("Serializability of DDI api Models") @Story("Serializability of DDI api Models")
public class DdiActionHistoryTest { public class DdiActionHistoryTest {
private ObjectMapper mapper = new ObjectMapper(); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Test @Test
@Description("Verify the correct serialization and deserialization of the model") @Description("Verify the correct serialization and deserialization of the model")
public void shouldSerializeAndDeserializeObject() throws IOException { public void shouldSerializeAndDeserializeObject() throws IOException {
// Setup // Setup
String actionStatus = "TestAction"; final String actionStatus = "TestAction";
List<String> messages = Arrays.asList("Action status message 1", "Action status message 2"); final List<String> messages = Arrays.asList("Action status message 1", "Action status message 2");
DdiActionHistory ddiActionHistory = new DdiActionHistory(actionStatus, messages); final DdiActionHistory ddiActionHistory = new DdiActionHistory(actionStatus, messages);
// Test // Test
String serializedDdiActionHistory = mapper.writeValueAsString(ddiActionHistory); final String serializedDdiActionHistory = OBJECT_MAPPER.writeValueAsString(ddiActionHistory);
DdiActionHistory deserializedDdiActionHistory = mapper.readValue(serializedDdiActionHistory, final DdiActionHistory deserializedDdiActionHistory = OBJECT_MAPPER.readValue(serializedDdiActionHistory,
DdiActionHistory.class); DdiActionHistory.class);
assertThat(serializedDdiActionHistory).contains(actionStatus, messages.get(0), messages.get(1)); assertThat(serializedDdiActionHistory).contains(actionStatus, messages.get(0), messages.get(1));
@@ -56,10 +56,15 @@ public class DdiActionHistoryTest {
@Description("Verify the correct deserialization of a model with a additional unknown property") @Description("Verify the correct deserialization of a model with a additional unknown property")
public void shouldDeserializeObjectWithUnknownProperty() throws IOException { public void shouldDeserializeObjectWithUnknownProperty() throws IOException {
// Setup // Setup
String serializedDdiActionHistory = "{\"status\":\"SomeAction\", \"messages\":[\"Some message\"], \"unknownProperty\": \"test\"}"; final String serializedDdiActionHistory = """
{
"status": "SomeAction",
"messages": [ "Some message"],
"unknownProperty": "test"
}""";
// Test // Test
DdiActionHistory ddiActionHistory = mapper.readValue(serializedDdiActionHistory, DdiActionHistory.class); final DdiActionHistory ddiActionHistory = OBJECT_MAPPER.readValue(serializedDdiActionHistory, DdiActionHistory.class);
assertThat(ddiActionHistory.toString()).contains("SomeAction", "Some message"); assertThat(ddiActionHistory.toString()).contains("SomeAction", "Some message");
} }
@@ -68,9 +73,13 @@ public class DdiActionHistoryTest {
@Description("Verify that deserialization fails for known properties with a wrong datatype") @Description("Verify that deserialization fails for known properties with a wrong datatype")
public void shouldFailForObjectWithWrongDataTypes() throws IOException { public void shouldFailForObjectWithWrongDataTypes() throws IOException {
// Setup // Setup
String serializedDdiActionFeedback = "{\"status\": [SomeAction], \"messages\": [\"Some message\"]}"; final String serializedDdiActionFeedback = """
{
"status": [SomeAction],
"messages": ["Some message"]
}""";
assertThatExceptionOfType(MismatchedInputException.class).isThrownBy( assertThatExceptionOfType(MismatchedInputException.class).isThrownBy(
() -> mapper.readValue(serializedDdiActionFeedback, DdiActionHistory.class)); () -> OBJECT_MAPPER.readValue(serializedDdiActionFeedback, DdiActionHistory.class));
} }
} }

View File

@@ -164,7 +164,7 @@ public final class DataConversionHelper {
// response because of eTags. // response because of eTags.
result.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder result.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder
.methodOn(DdiRootController.class, tenantAware.getCurrentTenant()) .methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
.getControllerBasedeploymentAction(tenantAware.getCurrentTenant(), target.getControllerId(), .getControllerDeploymentBaseAction(tenantAware.getCurrentTenant(), target.getControllerId(),
activeAction.getId(), calculateEtag(activeAction), null)) activeAction.getId(), calculateEtag(activeAction), null))
.withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION).expand()); .withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION).expand());
} }

View File

@@ -277,7 +277,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
} }
@Override @Override
public ResponseEntity<DdiDeploymentBase> getControllerBasedeploymentAction( public ResponseEntity<DdiDeploymentBase> getControllerDeploymentBaseAction(
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId, @PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
@PathVariable("actionId") final Long actionId, @PathVariable("actionId") final Long actionId,
@RequestParam(value = "c", required = false, defaultValue = "-1") final int resource, @RequestParam(value = "c", required = false, defaultValue = "-1") final int resource,
@@ -329,7 +329,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
} }
@Override @Override
public ResponseEntity<Void> postBasedeploymentActionFeedback(@Valid @RequestBody final DdiActionFeedback feedback, public ResponseEntity<Void> postDeploymentBaseActionFeedback(@Valid @RequestBody final DdiActionFeedback feedback,
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId, @PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
@PathVariable("actionId") @NotNull final Long actionId) { @PathVariable("actionId") @NotNull final Long actionId) {
log.debug("provideBasedeploymentActionFeedback for target [{},{}]: {}", controllerId, actionId, feedback); log.debug("provideBasedeploymentActionFeedback for target [{},{}]: {}", controllerId, actionId, feedback);

View File

@@ -14,6 +14,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString;
/** /**
* Hashes for given Artifact. * Hashes for given Artifact.
@@ -21,6 +22,7 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor // used for jackson to instantiate @NoArgsConstructor // used for jackson to instantiate
@Getter @Getter
@EqualsAndHashCode @EqualsAndHashCode
@ToString
public class MgmtArtifactHash { public class MgmtArtifactHash {
@JsonProperty @JsonProperty

View File

@@ -18,6 +18,7 @@ import lombok.AccessLevel;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -25,6 +26,7 @@ import java.util.Map;
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
@Getter @Getter
@EqualsAndHashCode @EqualsAndHashCode
@ToString
@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtDistributionSetStatistics { public class MgmtDistributionSetStatistics {

View File

@@ -12,6 +12,7 @@ package org.eclipse.hawkbit.im.authentication;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.ToString; import lombok.ToString;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
@@ -24,6 +25,7 @@ import org.springframework.security.core.userdetails.User;
* *
*/ */
@Getter @Getter
@EqualsAndHashCode(callSuper = true)
@ToString @ToString
public class UserPrincipal extends User { public class UserPrincipal extends User {

View File

@@ -12,6 +12,7 @@ package org.eclipse.hawkbit.security;
import java.util.List; import java.util.List;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.ToString; import lombok.ToString;
@@ -21,6 +22,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* The common properties for DDI security. * The common properties for DDI security.
*/ */
@Getter @Getter
@EqualsAndHashCode
@ToString
@ConfigurationProperties("hawkbit.server.ddi.security") @ConfigurationProperties("hawkbit.server.ddi.security")
public class DdiSecurityProperties { public class DdiSecurityProperties {