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:
Vasil Ilchev
2024-12-04 09:53:10 +02:00
committed by GitHub
parent 58e427d1ea
commit b4215a9d76
17 changed files with 96 additions and 64 deletions

View File

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

View File

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

View File

@@ -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())) {

View File

@@ -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(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,

View File

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

View File

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