Add timestamp to Actions (#2113)
* Add timestamp to Actions Signed-off-by: Vasil Ilchev <Vasil.Ilchev@bosch.com> * Add Timestamp to All Actions Feedback DDI/DMF * After review * Removed Action timestamp as we have timestamp in each ActionStatus so use that instead * Unify to use everywhere System.currentTimeMillis() * Add constructor w/o timestamp to DmfActionUpdateStatus --------- Signed-off-by: Vasil Ilchev <Vasil.Ilchev@bosch.com> Co-authored-by: vasilchev <vasil.ilchev@bosch.com>
This commit is contained in:
@@ -31,7 +31,7 @@ import lombok.Data;
|
|||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* The answer header would look like: {
|
* The answer header would look like: {
|
||||||
* "time": "20140511T121314",
|
* "timestamp": "1733218554123",
|
||||||
* "status": {
|
* "status": {
|
||||||
* "execution": "closed",
|
* "execution": "closed",
|
||||||
* "result": {
|
* "result": {
|
||||||
@@ -47,8 +47,8 @@ import lombok.Data;
|
|||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class DdiActionFeedback {
|
public class DdiActionFeedback {
|
||||||
|
|
||||||
@Schema(description = "Timestamp of the action", example = "2023-08-03T12:31:41.890992967Z")
|
@Schema(description = "Timestamp of the action in milliseconds since epoch", example = "1627997501890")
|
||||||
private final String time;
|
private final Long timestamp;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
@@ -57,14 +57,19 @@ public class DdiActionFeedback {
|
|||||||
/**
|
/**
|
||||||
* Constructs an action-feedback
|
* Constructs an action-feedback
|
||||||
*
|
*
|
||||||
* @param time time of feedback
|
* @param timestamp 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(
|
public DdiActionFeedback(
|
||||||
@JsonProperty(value = "time") final String time,
|
@JsonProperty(value = "status", required = true) final DdiStatus status,
|
||||||
@JsonProperty(value = "status", required = true) final DdiStatus status) {
|
@JsonProperty(value = "timestamp") final Long timestamp) {
|
||||||
this.time = time;
|
|
||||||
this.status = status;
|
this.status = status;
|
||||||
|
this.timestamp = timestamp != null ? timestamp : System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DdiActionFeedback(final DdiStatus status) {
|
||||||
|
this(status, null);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,7 @@ class DdiActionFeedbackTest {
|
|||||||
void shouldSerializeAndDeserializeObjectWithoutOptionalValues() throws IOException {
|
void shouldSerializeAndDeserializeObjectWithoutOptionalValues() throws IOException {
|
||||||
// Setup
|
// Setup
|
||||||
final DdiStatus ddiStatus = new DdiStatus(DdiStatus.ExecutionStatus.CLOSED, null, null, Collections.emptyList());
|
final DdiStatus ddiStatus = new DdiStatus(DdiStatus.ExecutionStatus.CLOSED, null, null, Collections.emptyList());
|
||||||
final DdiActionFeedback ddiActionFeedback = new DdiActionFeedback(null, ddiStatus);
|
final DdiActionFeedback ddiActionFeedback = new DdiActionFeedback(ddiStatus);
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
final String serializedDdiActionFeedback = mapper.writeValueAsString(ddiActionFeedback);
|
final String serializedDdiActionFeedback = mapper.writeValueAsString(ddiActionFeedback);
|
||||||
@@ -53,17 +53,16 @@ class DdiActionFeedbackTest {
|
|||||||
@Description("Verify the correct serialization and deserialization of the model with all values provided")
|
@Description("Verify the correct serialization and deserialization of the model with all values provided")
|
||||||
void shouldSerializeAndDeserializeObjectWithOptionalValues() throws IOException {
|
void shouldSerializeAndDeserializeObjectWithOptionalValues() throws IOException {
|
||||||
// Setup
|
// Setup
|
||||||
final String time = Instant.now().toString();
|
final Long timestamp = System.currentTimeMillis();
|
||||||
final DdiResult ddiResult = new DdiResult(DdiResult.FinalResult.SUCCESS, new DdiProgress(10, 10));
|
final DdiResult ddiResult = new DdiResult(DdiResult.FinalResult.SUCCESS, new DdiProgress(10, 10));
|
||||||
final DdiStatus ddiStatus = new DdiStatus(DdiStatus.ExecutionStatus.CLOSED, ddiResult, 200, Collections.singletonList("myMessage"));
|
final DdiStatus ddiStatus = new DdiStatus(DdiStatus.ExecutionStatus.CLOSED, ddiResult, 200, Collections.singletonList("myMessage"));
|
||||||
final DdiActionFeedback ddiActionFeedback = new DdiActionFeedback(time, ddiStatus);
|
final DdiActionFeedback ddiActionFeedback = new DdiActionFeedback(ddiStatus, timestamp);
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
final String serializedDdiActionFeedback = mapper.writeValueAsString(ddiActionFeedback);
|
final String serializedDdiActionFeedback = mapper.writeValueAsString(ddiActionFeedback);
|
||||||
final DdiActionFeedback deserializedDdiActionFeedback = mapper.readValue(serializedDdiActionFeedback, DdiActionFeedback.class);
|
final DdiActionFeedback deserializedDdiActionFeedback = mapper.readValue(serializedDdiActionFeedback, DdiActionFeedback.class);
|
||||||
|
|
||||||
assertThat(serializedDdiActionFeedback).contains(time);
|
assertThat(deserializedDdiActionFeedback.getTimestamp()).isEqualTo(timestamp);
|
||||||
assertThat(deserializedDdiActionFeedback.getTime()).isEqualTo(time);
|
|
||||||
assertThat(deserializedDdiActionFeedback.getStatus()).hasToString(ddiStatus.toString());
|
assertThat(deserializedDdiActionFeedback.getStatus()).hasToString(ddiStatus.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,28 +70,38 @@ class DdiActionFeedbackTest {
|
|||||||
@Description("Verify that deserialization fails for known properties with a wrong datatype")
|
@Description("Verify that deserialization fails for known properties with a wrong datatype")
|
||||||
void shouldFailForObjectWithWrongDataTypes() throws IOException {
|
void shouldFailForObjectWithWrongDataTypes() throws IOException {
|
||||||
// Setup
|
// Setup
|
||||||
final String serializedDdiActionFeedback = "{\"time\":\"20190809T121314\",\"status\":{\"execution\": [closed],\"result\":null,\"details\":[]}}";
|
final String serializedDdiActionFeedback = """
|
||||||
|
{
|
||||||
|
"timestamp" : "1627997501890",
|
||||||
|
"status" : {
|
||||||
|
"execution" : "[closed]",
|
||||||
|
"result" : null,
|
||||||
|
"details" : []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
assertThatExceptionOfType(MismatchedInputException.class).isThrownBy(
|
assertThatExceptionOfType(MismatchedInputException.class).isThrownBy(
|
||||||
() -> mapper.readValue(serializedDdiActionFeedback, DdiActionFeedback.class));
|
() -> mapper.readValue(serializedDdiActionFeedback, DdiActionFeedback.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Verify that deserialization works if optional fields are not parsed")
|
@Description("Verify that deserialization works if optional fields are not parsed")
|
||||||
void shouldConvertItWithoutOptionalFieldTime() throws JsonProcessingException {
|
void shouldConvertItWithoutOptionalFieldTimestamp() throws JsonProcessingException {
|
||||||
// Setup
|
// Setup
|
||||||
final String serializedDdiActionFeedback = "{\n" + //
|
final String serializedDdiActionFeedback = """
|
||||||
" \"status\" : {\n" + //
|
{
|
||||||
" \"result\" : {\n" + //
|
"status" : {
|
||||||
" \"finished\" : \"none\"\n" + //
|
"result" : {
|
||||||
" },\n" + //
|
"finished" : "none"
|
||||||
" \"execution\" : \"download\",\n" + //
|
},
|
||||||
" \"details\" : [ \"Some message\" ]\n" + //
|
"execution" : "download",
|
||||||
" }\n" + //
|
"details" : [ "Some message" ]
|
||||||
"}";//
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
assertThat(mapper.readValue(serializedDdiActionFeedback, DdiActionFeedback.class)).satisfies(deserializedDdiActionFeedback -> {
|
assertThat(mapper.readValue(serializedDdiActionFeedback, DdiActionFeedback.class)).satisfies(deserializedDdiActionFeedback -> {
|
||||||
assertThat(deserializedDdiActionFeedback.getTime()).isNull();
|
assertThat(deserializedDdiActionFeedback.getTimestamp()).isNotNull();
|
||||||
assertThat(deserializedDdiActionFeedback.getStatus()).isNotNull();
|
assertThat(deserializedDdiActionFeedback.getStatus()).isNotNull();
|
||||||
assertThat(deserializedDdiActionFeedback.getStatus().getResult()).isNotNull();
|
assertThat(deserializedDdiActionFeedback.getStatus().getResult()).isNotNull();
|
||||||
assertThat(deserializedDdiActionFeedback.getStatus().getResult().getFinished()).isEqualTo(DdiResult.FinalResult.NONE);
|
assertThat(deserializedDdiActionFeedback.getStatus().getResult().getFinished()).isEqualTo(DdiResult.FinalResult.NONE);
|
||||||
|
|||||||
@@ -503,7 +503,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
|||||||
|
|
||||||
private static ActionStatusCreate generateActionCancelStatus(
|
private static ActionStatusCreate generateActionCancelStatus(
|
||||||
final DdiActionFeedback feedback, final Target target, final Long actionId, final EntityFactory entityFactory) {
|
final DdiActionFeedback feedback, final Target target, final Long actionId, final EntityFactory entityFactory) {
|
||||||
final ActionStatusCreate actionStatusCreate = entityFactory.actionStatus().create(actionId);
|
final ActionStatusCreate actionStatusCreate = entityFactory.actionStatus().create(actionId).occurredAt(feedback.getTimestamp());
|
||||||
final List<String> messages = new ArrayList<>();
|
final List<String> messages = new ArrayList<>();
|
||||||
final Status status;
|
final Status status;
|
||||||
switch (feedback.getStatus().getExecution()) {
|
switch (feedback.getStatus().getExecution()) {
|
||||||
@@ -616,7 +616,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ActionStatusCreate generateUpdateStatus(final DdiActionFeedback feedback, final String controllerId, final Long actionId) {
|
private ActionStatusCreate generateUpdateStatus(final DdiActionFeedback feedback, final String controllerId, final Long actionId) {
|
||||||
final ActionStatusCreate actionStatusCreate = entityFactory.actionStatus().create(actionId);
|
final ActionStatusCreate actionStatusCreate = entityFactory.actionStatus().create(actionId).occurredAt(feedback.getTimestamp());
|
||||||
final List<String> messages = new ArrayList<>();
|
final List<String> messages = new ArrayList<>();
|
||||||
|
|
||||||
if (!CollectionUtils.isEmpty(feedback.getStatus().getDetails())) {
|
if (!CollectionUtils.isEmpty(feedback.getStatus().getDetails())) {
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
|
|||||||
final DdiStatus.ExecutionStatus executionStatus, final DdiResult ddiResult,
|
final DdiStatus.ExecutionStatus executionStatus, final DdiResult ddiResult,
|
||||||
final List<String> messages) throws JsonProcessingException {
|
final List<String> messages) throws JsonProcessingException {
|
||||||
final DdiStatus ddiStatus = new DdiStatus(executionStatus, ddiResult, null, messages);
|
final DdiStatus ddiStatus = new DdiStatus(executionStatus, ddiResult, null, messages);
|
||||||
return OBJECT_MAPPER.writeValueAsString(new DdiActionFeedback(Instant.now().toString(), ddiStatus));
|
return OBJECT_MAPPER.writeValueAsString(new DdiActionFeedback(ddiStatus));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getJsonActionFeedback(
|
protected String getJsonActionFeedback(
|
||||||
@@ -308,7 +308,7 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
|
|||||||
final DdiStatus.ExecutionStatus executionStatus,
|
final DdiStatus.ExecutionStatus executionStatus,
|
||||||
final DdiResult.FinalResult finalResult, final Integer code, final List<String> messages) throws JsonProcessingException {
|
final DdiResult.FinalResult finalResult, final Integer code, final List<String> messages) throws JsonProcessingException {
|
||||||
final DdiStatus ddiStatus = new DdiStatus(executionStatus, new DdiResult(finalResult, new DdiProgress(2, 5)), code, messages);
|
final DdiStatus ddiStatus = new DdiStatus(executionStatus, new DdiResult(finalResult, new DdiProgress(2, 5)), code, messages);
|
||||||
return OBJECT_MAPPER.writeValueAsString(new DdiActionFeedback(Instant.now().toString(), ddiStatus));
|
return OBJECT_MAPPER.writeValueAsString(new DdiActionFeedback(ddiStatus));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getJsonConfirmationFeedback(
|
protected String getJsonConfirmationFeedback(
|
||||||
|
|||||||
@@ -330,7 +330,7 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
|
|||||||
|
|
||||||
protected void createAndSendActionStatusUpdateMessage(final String target, final long actionId,
|
protected void createAndSendActionStatusUpdateMessage(final String target, final long actionId,
|
||||||
final DmfActionStatus status) {
|
final DmfActionStatus status) {
|
||||||
final DmfActionUpdateStatus dmfActionUpdateStatus = new DmfActionUpdateStatus(actionId, status);
|
final DmfActionUpdateStatus dmfActionUpdateStatus = new DmfActionUpdateStatus(actionId, status, System.currentTimeMillis());
|
||||||
|
|
||||||
final Message eventMessage = createUpdateActionEventMessage(dmfActionUpdateStatus);
|
final Message eventMessage = createUpdateActionEventMessage(dmfActionUpdateStatus);
|
||||||
eventMessage.getMessageProperties().getHeaders().put(MessageHeaderKey.THING_ID, target);
|
eventMessage.getMessageProperties().getHeaders().put(MessageHeaderKey.THING_ID, target);
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public class DmfActionUpdateStatus {
|
|||||||
|
|
||||||
private final Long actionId;
|
private final Long actionId;
|
||||||
private final DmfActionStatus actionStatus;
|
private final DmfActionStatus actionStatus;
|
||||||
|
private final long timestamp;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private Long softwareModuleId;
|
private Long softwareModuleId;
|
||||||
@@ -46,9 +47,14 @@ public class DmfActionUpdateStatus {
|
|||||||
private Integer code;
|
private Integer code;
|
||||||
|
|
||||||
public DmfActionUpdateStatus(@JsonProperty(value = "actionId", required = true) final Long actionId,
|
public DmfActionUpdateStatus(@JsonProperty(value = "actionId", required = true) final Long actionId,
|
||||||
@JsonProperty(value = "actionStatus", required = true) final DmfActionStatus actionStatus) {
|
@JsonProperty(value = "actionStatus", required = true) final DmfActionStatus actionStatus, @JsonProperty(value = "timestamp") final Long timestamp) {
|
||||||
this.actionId = actionId;
|
this.actionId = actionId;
|
||||||
this.actionStatus = actionStatus;
|
this.actionStatus = actionStatus;
|
||||||
|
this.timestamp = timestamp != null ? timestamp : System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DmfActionUpdateStatus(final Long actionId, final DmfActionStatus actionStatus) {
|
||||||
|
this(actionId, actionStatus, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
|
|||||||
@@ -131,4 +131,5 @@ public class MgmtAction extends MgmtBaseEntity {
|
|||||||
@JsonProperty
|
@JsonProperty
|
||||||
@Schema(description = "If created by external system this field contains the external reference for the action")
|
@Schema(description = "If created by external system this field contains the external reference for the action")
|
||||||
private String externalRef;
|
private String externalRef;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -43,6 +43,10 @@ public class MgmtActionStatus {
|
|||||||
@Schema(example = "1691065929524")
|
@Schema(example = "1691065929524")
|
||||||
private Long reportedAt;
|
private Long reportedAt;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
@Schema(example = "1691065929524")
|
||||||
|
private Long timestamp;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@Schema(example = "200")
|
@Schema(example = "200")
|
||||||
private Integer code;
|
private Integer code;
|
||||||
|
|||||||
@@ -354,6 +354,7 @@ public final class MgmtTargetMapper {
|
|||||||
|
|
||||||
result.setMessages(messages);
|
result.setMessages(messages);
|
||||||
result.setReportedAt(actionStatus.getCreatedAt());
|
result.setReportedAt(actionStatus.getCreatedAt());
|
||||||
|
result.setTimestamp(actionStatus.getOccurredAt());
|
||||||
result.setStatusId(actionStatus.getId());
|
result.setStatusId(actionStatus.getId());
|
||||||
result.setType(actionStatus.getStatus().name().toLowerCase());
|
result.setType(actionStatus.getStatus().name().toLowerCase());
|
||||||
actionStatus.getCode().ifPresent(result::setCode);
|
actionStatus.getCode().ifPresent(result::setCode);
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public final class TimestampCalculator {
|
|||||||
* @return <em>overdue_ts</em> in milliseconds since Unix epoch as long value
|
* @return <em>overdue_ts</em> in milliseconds since Unix epoch as long value
|
||||||
*/
|
*/
|
||||||
public static long calculateOverdueTimestamp() {
|
public static long calculateOverdueTimestamp() {
|
||||||
return Instant.now().toEpochMilli() - getDurationForKey(TenantConfigurationKey.POLLING_TIME_INTERVAL).toMillis()
|
return System.currentTimeMillis() - getDurationForKey(TenantConfigurationKey.POLLING_TIME_INTERVAL).toMillis()
|
||||||
- getDurationForKey(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL).toMillis();
|
- getDurationForKey(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL).toMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
package org.eclipse.hawkbit.repository.rsql;
|
package org.eclipse.hawkbit.repository.rsql;
|
||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.time.Instant;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.text.StrLookup;
|
import org.apache.commons.lang3.text.StrLookup;
|
||||||
import org.apache.commons.lang3.text.StrSubstitutor;
|
import org.apache.commons.lang3.text.StrSubstitutor;
|
||||||
@@ -54,7 +53,7 @@ public class VirtualPropertyResolver extends StrLookup<String> implements Virtua
|
|||||||
String resolved = null;
|
String resolved = null;
|
||||||
|
|
||||||
if ("now_ts".equalsIgnoreCase(rhs)) {
|
if ("now_ts".equalsIgnoreCase(rhs)) {
|
||||||
resolved = String.valueOf(Instant.now().toEpochMilli());
|
resolved = String.valueOf(System.currentTimeMillis());
|
||||||
} else if ("overdue_ts".equalsIgnoreCase(rhs)) {
|
} else if ("overdue_ts".equalsIgnoreCase(rhs)) {
|
||||||
resolved = String.valueOf(TimestampCalculator.calculateOverdueTimestamp());
|
resolved = String.valueOf(TimestampCalculator.calculateOverdueTimestamp());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ public class JpaActionManagement {
|
|||||||
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onActionStatusUpdate(final Action.Status updatedActionStatus, final JpaAction action) {
|
protected void onActionStatusUpdate(final JpaActionStatus newActionStatus, final JpaAction action) {
|
||||||
// can be overwritten to intercept the persistence of the action status
|
// can be overwritten to intercept the persistence of the action status
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ public class JpaActionManagement {
|
|||||||
assertActionStatusMessageQuota(actionStatus);
|
assertActionStatusMessageQuota(actionStatus);
|
||||||
actionStatus.setAction(action);
|
actionStatus.setAction(action);
|
||||||
|
|
||||||
onActionStatusUpdate(actionStatus.getStatus(), action);
|
onActionStatusUpdate(actionStatus, action);
|
||||||
|
|
||||||
actionStatusRepository.save(actionStatus);
|
actionStatusRepository.save(actionStatus);
|
||||||
|
|
||||||
|
|||||||
@@ -163,8 +163,8 @@ public class JpaConfirmationManagement extends JpaActionManagement implements Co
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActionStatusUpdate(final Status updatedActionStatus, final JpaAction action) {
|
protected void onActionStatusUpdate(final JpaActionStatus newActionStatus, final JpaAction action) {
|
||||||
if (updatedActionStatus == Status.RUNNING && action.isActive()) {
|
if (newActionStatus.getStatus() == Status.RUNNING && action.isActive()) {
|
||||||
action.setStatus(Status.RUNNING);
|
action.setStatus(Status.RUNNING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,7 +177,9 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActionStatusUpdate(final Action.Status updatedActionStatus, final JpaAction action) {
|
protected void onActionStatusUpdate(final JpaActionStatus newActionStatus, final JpaAction action) {
|
||||||
|
final Action.Status updatedActionStatus = newActionStatus.getStatus();
|
||||||
|
final long occurredAt = newActionStatus.getOccurredAt();
|
||||||
switch (updatedActionStatus) {
|
switch (updatedActionStatus) {
|
||||||
case ERROR: {
|
case ERROR: {
|
||||||
final JpaTarget target = (JpaTarget) action.getTarget();
|
final JpaTarget target = (JpaTarget) action.getTarget();
|
||||||
@@ -186,7 +188,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FINISHED: {
|
case FINISHED: {
|
||||||
handleFinishedAndStoreInTargetStatus(action).ifPresent(this::requestControllerAttributes);
|
handleFinishedAndStoreInTargetStatus(occurredAt, action).ifPresent(this::requestControllerAttributes);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DOWNLOADED: {
|
case DOWNLOADED: {
|
||||||
@@ -865,33 +867,35 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
|||||||
* @return a present controllerId in case the attributes needs to be
|
* @return a present controllerId in case the attributes needs to be
|
||||||
* requested.
|
* requested.
|
||||||
*/
|
*/
|
||||||
private Optional<String> handleFinishedAndStoreInTargetStatus(final JpaAction action) {
|
private Optional<String> handleFinishedAndStoreInTargetStatus(final long occurredAt, final JpaAction action) {
|
||||||
final JpaTarget target = (JpaTarget) action.getTarget();
|
final JpaTarget target = (JpaTarget) action.getTarget();
|
||||||
action.setActive(false);
|
action.setActive(false);
|
||||||
action.setStatus(Status.FINISHED);
|
action.setStatus(Status.FINISHED);
|
||||||
final JpaDistributionSet ds = (JpaDistributionSet) entityManager.merge(action.getDistributionSet());
|
if (target.getInstallationDate() == null || target.getInstallationDate() < occurredAt) {
|
||||||
|
final JpaDistributionSet ds = (JpaDistributionSet) entityManager.merge(action.getDistributionSet());
|
||||||
|
|
||||||
target.setInstalledDistributionSet(ds);
|
target.setInstalledDistributionSet(ds);
|
||||||
target.setInstallationDate(System.currentTimeMillis());
|
target.setInstallationDate(occurredAt);
|
||||||
|
|
||||||
// Target reported an installation of a DOWNLOAD_ONLY assignment, the
|
// Target reported an installation of a DOWNLOAD_ONLY assignment, the
|
||||||
// assigned DS has to be adapted
|
// assigned DS has to be adapted
|
||||||
// because the currently assigned DS can be unequal to the currently
|
// because the currently assigned DS can be unequal to the currently
|
||||||
// installed DS (the downloadOnly DS)
|
// installed DS (the downloadOnly DS)
|
||||||
if (isDownloadOnly(action)) {
|
if (isDownloadOnly(action)) {
|
||||||
target.setAssignedDistributionSet((JpaDistributionSet) action.getDistributionSet());
|
target.setAssignedDistributionSet((JpaDistributionSet) action.getDistributionSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the assigned set is equal to the installed set (not
|
||||||
|
// necessarily the case as another update might be pending already).
|
||||||
|
if (target.getAssignedDistributionSet() != null
|
||||||
|
&& target.getAssignedDistributionSet().getId().equals(target.getInstalledDistributionSet().getId())) {
|
||||||
|
target.setUpdateStatus(TargetUpdateStatus.IN_SYNC);
|
||||||
|
}
|
||||||
|
|
||||||
|
targetRepository.save(target);
|
||||||
|
entityManager.detach(ds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the assigned set is equal to the installed set (not
|
|
||||||
// necessarily the case as another update might be pending already).
|
|
||||||
if (target.getAssignedDistributionSet() != null
|
|
||||||
&& target.getAssignedDistributionSet().getId().equals(target.getInstalledDistributionSet().getId())) {
|
|
||||||
target.setUpdateStatus(TargetUpdateStatus.IN_SYNC);
|
|
||||||
}
|
|
||||||
|
|
||||||
targetRepository.save(target);
|
|
||||||
entityManager.detach(ds);
|
|
||||||
|
|
||||||
return Optional.of(target.getControllerId());
|
return Optional.of(target.getControllerId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import jakarta.persistence.Access;
|
||||||
|
import jakarta.persistence.AccessType;
|
||||||
import jakarta.persistence.CascadeType;
|
import jakarta.persistence.CascadeType;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.ConstraintMode;
|
import jakarta.persistence.ConstraintMode;
|
||||||
@@ -51,6 +53,7 @@ import org.eclipse.hawkbit.repository.model.Rollout;
|
|||||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
||||||
|
import org.springframework.data.annotation.CreatedDate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JPA implementation of {@link Action}.
|
* JPA implementation of {@link Action}.
|
||||||
@@ -283,6 +286,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
|||||||
return Optional.ofNullable(lastActionStatusCode);
|
return Optional.ofNullable(lastActionStatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<ZonedDateTime> getMaintenanceWindowStartTime() {
|
public Optional<ZonedDateTime> getMaintenanceWindowStartTime() {
|
||||||
return MaintenanceScheduleHelper.getNextMaintenanceWindow(maintenanceWindowSchedule, maintenanceWindowDuration,
|
return MaintenanceScheduleHelper.getNextMaintenanceWindow(maintenanceWindowSchedule, maintenanceWindowDuration,
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
final DistributionSet installedSet = testdataFactory.createDistributionSet("another");
|
final DistributionSet installedSet = testdataFactory.createDistributionSet("another");
|
||||||
|
|
||||||
final Long lastTargetQueryNotOverdue = Instant.now().toEpochMilli();
|
final Long lastTargetQueryNotOverdue = System.currentTimeMillis();
|
||||||
final Long lastTargetQueryAlwaysOverdue = 0L;
|
final Long lastTargetQueryAlwaysOverdue = 0L;
|
||||||
|
|
||||||
final String targetDsAIdPref = "targ-A";
|
final String targetDsAIdPref = "targ-A";
|
||||||
@@ -276,7 +276,7 @@ class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
|
|||||||
void targetSearchWithOverdueFilterAndOrderByDistributionSet() {
|
void targetSearchWithOverdueFilterAndOrderByDistributionSet() {
|
||||||
|
|
||||||
final Long lastTargetQueryAlwaysOverdue = 0L;
|
final Long lastTargetQueryAlwaysOverdue = 0L;
|
||||||
final long lastTargetQueryNotOverdue = Instant.now().toEpochMilli();
|
final long lastTargetQueryNotOverdue = System.currentTimeMillis();
|
||||||
|
|
||||||
final Long[] overdueMix = { lastTargetQueryAlwaysOverdue, lastTargetQueryNotOverdue,
|
final Long[] overdueMix = { lastTargetQueryAlwaysOverdue, lastTargetQueryNotOverdue,
|
||||||
lastTargetQueryAlwaysOverdue, null, lastTargetQueryAlwaysOverdue };
|
lastTargetQueryAlwaysOverdue, null, lastTargetQueryAlwaysOverdue };
|
||||||
|
|||||||
@@ -18,8 +18,7 @@ import org.eclipse.hawkbit.ddi.json.model.DdiStatus;
|
|||||||
public record UpdateStatus(Status status, List<String> messages) {
|
public record UpdateStatus(Status status, List<String> messages) {
|
||||||
|
|
||||||
DdiActionFeedback feedback() {
|
DdiActionFeedback feedback() {
|
||||||
return new DdiActionFeedback(null,
|
return new DdiActionFeedback(new DdiStatus(status.executionStatus, new DdiResult(status.finalResult, null), status.code, messages));
|
||||||
new DdiStatus(status.executionStatus, new DdiResult(status.finalResult, null), status.code, messages));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user