diff --git a/hawkbit-ddi/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiActionFeedback.java b/hawkbit-ddi/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiActionFeedback.java index 354fc41c50..5478ad1ed2 100644 --- a/hawkbit-ddi/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiActionFeedback.java +++ b/hawkbit-ddi/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiActionFeedback.java @@ -31,7 +31,7 @@ * *

* The answer header would look like: { - * "time": "20140511T121314", + * "timestamp": "1733218554123", * "status": { * "execution": "closed", * "result": { @@ -47,8 +47,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class DdiActionFeedback { - @Schema(description = "Timestamp of the action", example = "2023-08-03T12:31:41.890992967Z") - private final String time; + @Schema(description = "Timestamp of the action in milliseconds since epoch", example = "1627997501890") + private final Long timestamp; @NotNull @Valid @@ -57,14 +57,19 @@ public class DdiActionFeedback { /** * Constructs an action-feedback * - * @param time time of feedback + * @param timestamp time of feedback * @param status status to be appended to the action */ @JsonCreator public DdiActionFeedback( - @JsonProperty(value = "time") final String time, - @JsonProperty(value = "status", required = true) final DdiStatus status) { - this.time = time; + @JsonProperty(value = "status", required = true) final DdiStatus status, + @JsonProperty(value = "timestamp") final Long timestamp) { this.status = status; + this.timestamp = timestamp != null ? timestamp : System.currentTimeMillis(); } + + public DdiActionFeedback(final DdiStatus status) { + this(status, null); + } + } \ No newline at end of file diff --git a/hawkbit-ddi/hawkbit-ddi-api/src/test/java/org/eclipse/hawkbit/ddi/json/model/DdiActionFeedbackTest.java b/hawkbit-ddi/hawkbit-ddi-api/src/test/java/org/eclipse/hawkbit/ddi/json/model/DdiActionFeedbackTest.java index 395f42d418..a0520a5793 100644 --- a/hawkbit-ddi/hawkbit-ddi-api/src/test/java/org/eclipse/hawkbit/ddi/json/model/DdiActionFeedbackTest.java +++ b/hawkbit-ddi/hawkbit-ddi-api/src/test/java/org/eclipse/hawkbit/ddi/json/model/DdiActionFeedbackTest.java @@ -39,7 +39,7 @@ class DdiActionFeedbackTest { void shouldSerializeAndDeserializeObjectWithoutOptionalValues() throws IOException { // Setup 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 final String serializedDdiActionFeedback = mapper.writeValueAsString(ddiActionFeedback); @@ -53,17 +53,16 @@ void shouldSerializeAndDeserializeObjectWithoutOptionalValues() throws IOExcepti @Description("Verify the correct serialization and deserialization of the model with all values provided") void shouldSerializeAndDeserializeObjectWithOptionalValues() throws IOException { // 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 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 final String serializedDdiActionFeedback = mapper.writeValueAsString(ddiActionFeedback); final DdiActionFeedback deserializedDdiActionFeedback = mapper.readValue(serializedDdiActionFeedback, DdiActionFeedback.class); - assertThat(serializedDdiActionFeedback).contains(time); - assertThat(deserializedDdiActionFeedback.getTime()).isEqualTo(time); + assertThat(deserializedDdiActionFeedback.getTimestamp()).isEqualTo(timestamp); assertThat(deserializedDdiActionFeedback.getStatus()).hasToString(ddiStatus.toString()); } @@ -71,28 +70,38 @@ void shouldSerializeAndDeserializeObjectWithOptionalValues() throws IOException @Description("Verify that deserialization fails for known properties with a wrong datatype") void shouldFailForObjectWithWrongDataTypes() throws IOException { // 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( () -> mapper.readValue(serializedDdiActionFeedback, DdiActionFeedback.class)); } @Test @Description("Verify that deserialization works if optional fields are not parsed") - void shouldConvertItWithoutOptionalFieldTime() throws JsonProcessingException { + void shouldConvertItWithoutOptionalFieldTimestamp() throws JsonProcessingException { // Setup - final String serializedDdiActionFeedback = "{\n" + // - " \"status\" : {\n" + // - " \"result\" : {\n" + // - " \"finished\" : \"none\"\n" + // - " },\n" + // - " \"execution\" : \"download\",\n" + // - " \"details\" : [ \"Some message\" ]\n" + // - " }\n" + // - "}";// + final String serializedDdiActionFeedback = """ + { + "status" : { + "result" : { + "finished" : "none" + }, + "execution" : "download", + "details" : [ "Some message" ] + } + } + """; assertThat(mapper.readValue(serializedDdiActionFeedback, DdiActionFeedback.class)).satisfies(deserializedDdiActionFeedback -> { - assertThat(deserializedDdiActionFeedback.getTime()).isNull(); + assertThat(deserializedDdiActionFeedback.getTimestamp()).isNotNull(); assertThat(deserializedDdiActionFeedback.getStatus()).isNotNull(); assertThat(deserializedDdiActionFeedback.getStatus().getResult()).isNotNull(); assertThat(deserializedDdiActionFeedback.getStatus().getResult().getFinished()).isEqualTo(DdiResult.FinalResult.NONE); diff --git a/hawkbit-ddi/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java b/hawkbit-ddi/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java index 0149e5b046..9fd779ff6f 100644 --- a/hawkbit-ddi/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java +++ b/hawkbit-ddi/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java @@ -503,7 +503,7 @@ private static void addMessageIfEmpty(final String text, final List mess private static ActionStatusCreate generateActionCancelStatus( 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 messages = new ArrayList<>(); final Status status; switch (feedback.getStatus().getExecution()) { @@ -616,7 +616,7 @@ private ActionStatus checkAndLogDownload(final HttpServletRequest request, final } 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 messages = new ArrayList<>(); if (!CollectionUtils.isEmpty(feedback.getStatus().getDetails())) { diff --git a/hawkbit-ddi/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/AbstractDDiApiIntegrationTest.java b/hawkbit-ddi/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/AbstractDDiApiIntegrationTest.java index 0af6cc31ec..31042fe266 100644 --- a/hawkbit-ddi/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/AbstractDDiApiIntegrationTest.java +++ b/hawkbit-ddi/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/AbstractDDiApiIntegrationTest.java @@ -295,7 +295,7 @@ protected String getJsonActionFeedback( final DdiStatus.ExecutionStatus executionStatus, final DdiResult ddiResult, final List messages) throws JsonProcessingException { 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( @@ -308,7 +308,7 @@ protected String getJsonActionFeedback( final DdiStatus.ExecutionStatus executionStatus, final DdiResult.FinalResult finalResult, final Integer code, final List messages) throws JsonProcessingException { 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( diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java index 9aaa342342..2f5f050713 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java @@ -330,7 +330,7 @@ protected Message createPingMessage(final String correlationId, final String ten protected void createAndSendActionStatusUpdateMessage(final String target, final long actionId, final DmfActionStatus status) { - final DmfActionUpdateStatus dmfActionUpdateStatus = new DmfActionUpdateStatus(actionId, status); + final DmfActionUpdateStatus dmfActionUpdateStatus = new DmfActionUpdateStatus(actionId, status, System.currentTimeMillis()); final Message eventMessage = createUpdateActionEventMessage(dmfActionUpdateStatus); eventMessage.getMessageProperties().getHeaders().put(MessageHeaderKey.THING_ID, target); diff --git a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfActionUpdateStatus.java b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfActionUpdateStatus.java index 16df9ded85..f6edba82b3 100644 --- a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfActionUpdateStatus.java +++ b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfActionUpdateStatus.java @@ -34,6 +34,7 @@ public class DmfActionUpdateStatus { private final Long actionId; private final DmfActionStatus actionStatus; + private final long timestamp; @JsonProperty private Long softwareModuleId; @@ -46,9 +47,14 @@ public class DmfActionUpdateStatus { private Integer code; 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.actionStatus = actionStatus; + this.timestamp = timestamp != null ? timestamp : System.currentTimeMillis(); + } + + public DmfActionUpdateStatus(final Long actionId, final DmfActionStatus actionStatus) { + this(actionId, actionStatus, null); } @JsonIgnore diff --git a/hawkbit-mgmt/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/action/MgmtAction.java b/hawkbit-mgmt/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/action/MgmtAction.java index ab1091e56f..64b16e81f3 100644 --- a/hawkbit-mgmt/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/action/MgmtAction.java +++ b/hawkbit-mgmt/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/action/MgmtAction.java @@ -131,4 +131,5 @@ public class MgmtAction extends MgmtBaseEntity { @JsonProperty @Schema(description = "If created by external system this field contains the external reference for the action") private String externalRef; + } \ No newline at end of file diff --git a/hawkbit-mgmt/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/action/MgmtActionStatus.java b/hawkbit-mgmt/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/action/MgmtActionStatus.java index e043964de6..785df0e740 100644 --- a/hawkbit-mgmt/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/action/MgmtActionStatus.java +++ b/hawkbit-mgmt/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/action/MgmtActionStatus.java @@ -43,6 +43,10 @@ public class MgmtActionStatus { @Schema(example = "1691065929524") private Long reportedAt; + @JsonProperty + @Schema(example = "1691065929524") + private Long timestamp; + @JsonProperty @Schema(example = "200") private Integer code; diff --git a/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java b/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java index c50e42bf72..0a322a1a40 100644 --- a/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java +++ b/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java @@ -354,6 +354,7 @@ private static MgmtActionStatus toResponse(final ActionStatus actionStatus, fina result.setMessages(messages); result.setReportedAt(actionStatus.getCreatedAt()); + result.setTimestamp(actionStatus.getOccurredAt()); result.setStatusId(actionStatus.getId()); result.setType(actionStatus.getStatus().name().toLowerCase()); actionStatus.getCode().ifPresent(result::setCode); diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/TimestampCalculator.java b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/TimestampCalculator.java index e2bc7932ef..be205439f0 100644 --- a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/TimestampCalculator.java +++ b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/TimestampCalculator.java @@ -37,7 +37,7 @@ public final class TimestampCalculator { * @return overdue_ts in milliseconds since Unix epoch as long value */ 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(); } diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/rsql/VirtualPropertyResolver.java b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/rsql/VirtualPropertyResolver.java index 1842841849..1d99864ea0 100644 --- a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/rsql/VirtualPropertyResolver.java +++ b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/rsql/VirtualPropertyResolver.java @@ -10,7 +10,6 @@ package org.eclipse.hawkbit.repository.rsql; import java.io.Serial; -import java.time.Instant; import org.apache.commons.lang3.text.StrLookup; import org.apache.commons.lang3.text.StrSubstitutor; @@ -54,7 +53,7 @@ public String lookup(final String rhs) { String resolved = null; if ("now_ts".equalsIgnoreCase(rhs)) { - resolved = String.valueOf(Instant.now().toEpochMilli()); + resolved = String.valueOf(System.currentTimeMillis()); } else if ("overdue_ts".equalsIgnoreCase(rhs)) { resolved = String.valueOf(TimestampCalculator.calculateOverdueTimestamp()); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaActionManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaActionManagement.java index c2b274155d..e79132e54b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaActionManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaActionManagement.java @@ -88,7 +88,7 @@ protected JpaAction getActionAndThrowExceptionIfNotFound(final Long 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 } @@ -165,7 +165,7 @@ private Action handleAddUpdateActionStatus(final JpaActionStatus actionStatus, f assertActionStatusMessageQuota(actionStatus); actionStatus.setAction(action); - onActionStatusUpdate(actionStatus.getStatus(), action); + onActionStatusUpdate(actionStatus, action); actionStatusRepository.save(actionStatus); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaConfirmationManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaConfirmationManagement.java index 4c7a306af7..a7ae954877 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaConfirmationManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaConfirmationManagement.java @@ -163,8 +163,8 @@ public void deactivateAutoConfirmation(String controllerId) { } @Override - protected void onActionStatusUpdate(final Status updatedActionStatus, final JpaAction action) { - if (updatedActionStatus == Status.RUNNING && action.isActive()) { + protected void onActionStatusUpdate(final JpaActionStatus newActionStatus, final JpaAction action) { + if (newActionStatus.getStatus() == Status.RUNNING && action.isActive()) { action.setStatus(Status.RUNNING); } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java index a8283f3f9f..a6187b609b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java @@ -177,7 +177,9 @@ public int getWeightConsideringDefault(final Action action) { } @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) { case ERROR: { final JpaTarget target = (JpaTarget) action.getTarget(); @@ -186,7 +188,7 @@ protected void onActionStatusUpdate(final Action.Status updatedActionStatus, fin break; } case FINISHED: { - handleFinishedAndStoreInTargetStatus(action).ifPresent(this::requestControllerAttributes); + handleFinishedAndStoreInTargetStatus(occurredAt, action).ifPresent(this::requestControllerAttributes); break; } case DOWNLOADED: { @@ -865,33 +867,35 @@ private void handleErrorOnAction(final JpaAction mergedAction, final JpaTarget m * @return a present controllerId in case the attributes needs to be * requested. */ - private Optional handleFinishedAndStoreInTargetStatus(final JpaAction action) { + private Optional handleFinishedAndStoreInTargetStatus(final long occurredAt, final JpaAction action) { final JpaTarget target = (JpaTarget) action.getTarget(); action.setActive(false); action.setStatus(Status.FINISHED); - final JpaDistributionSet ds = (JpaDistributionSet) entityManager.merge(action.getDistributionSet()); - - target.setInstalledDistributionSet(ds); - target.setInstallationDate(System.currentTimeMillis()); + if (target.getInstallationDate() == null || target.getInstallationDate() < occurredAt) { + final JpaDistributionSet ds = (JpaDistributionSet) entityManager.merge(action.getDistributionSet()); + + target.setInstalledDistributionSet(ds); + target.setInstallationDate(occurredAt); + + // Target reported an installation of a DOWNLOAD_ONLY assignment, the + // assigned DS has to be adapted + // because the currently assigned DS can be unequal to the currently + // installed DS (the downloadOnly DS) + if (isDownloadOnly(action)) { + target.setAssignedDistributionSet((JpaDistributionSet) action.getDistributionSet()); + } - // Target reported an installation of a DOWNLOAD_ONLY assignment, the - // assigned DS has to be adapted - // because the currently assigned DS can be unequal to the currently - // installed DS (the downloadOnly DS) - if (isDownloadOnly(action)) { - 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); + } - // 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); } - targetRepository.save(target); - entityManager.detach(ds); - return Optional.of(target.getControllerId()); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java index 27f1a83d44..87f59e9ec5 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java @@ -18,6 +18,8 @@ import java.util.Map; import java.util.Optional; +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.ConstraintMode; @@ -51,6 +53,7 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder; +import org.springframework.data.annotation.CreatedDate; /** * JPA implementation of {@link Action}. @@ -283,6 +286,7 @@ public Optional getLastActionStatusCode() { return Optional.ofNullable(lastActionStatusCode); } + @Override public Optional getMaintenanceWindowStartTime() { return MaintenanceScheduleHelper.getNextMaintenanceWindow(maintenanceWindowSchedule, maintenanceWindowDuration, diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementSearchTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementSearchTest.java index 789d5e5ee8..fd0a93aaa3 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementSearchTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementSearchTest.java @@ -89,7 +89,7 @@ void targetSearchWithVariousFilterCombinations() { final DistributionSet installedSet = testdataFactory.createDistributionSet("another"); - final Long lastTargetQueryNotOverdue = Instant.now().toEpochMilli(); + final Long lastTargetQueryNotOverdue = System.currentTimeMillis(); final Long lastTargetQueryAlwaysOverdue = 0L; final String targetDsAIdPref = "targ-A"; @@ -276,7 +276,7 @@ void targetSearchWithOrderByDistributionSetAndSortParam() { void targetSearchWithOverdueFilterAndOrderByDistributionSet() { final Long lastTargetQueryAlwaysOverdue = 0L; - final long lastTargetQueryNotOverdue = Instant.now().toEpochMilli(); + final long lastTargetQueryNotOverdue = System.currentTimeMillis(); final Long[] overdueMix = { lastTargetQueryAlwaysOverdue, lastTargetQueryNotOverdue, lastTargetQueryAlwaysOverdue, null, lastTargetQueryAlwaysOverdue }; diff --git a/hawkbit-sdk/hawkbit-sdk-device/src/main/java/org/eclipse/hawkbit/sdk/device/UpdateStatus.java b/hawkbit-sdk/hawkbit-sdk-device/src/main/java/org/eclipse/hawkbit/sdk/device/UpdateStatus.java index 214427bc56..f5953a1a62 100644 --- a/hawkbit-sdk/hawkbit-sdk-device/src/main/java/org/eclipse/hawkbit/sdk/device/UpdateStatus.java +++ b/hawkbit-sdk/hawkbit-sdk-device/src/main/java/org/eclipse/hawkbit/sdk/device/UpdateStatus.java @@ -18,8 +18,7 @@ public record UpdateStatus(Status status, List messages) { DdiActionFeedback feedback() { - return new DdiActionFeedback(null, - new DdiStatus(status.executionStatus, new DdiResult(status.finalResult, null), status.code, messages)); + return new DdiActionFeedback(new DdiStatus(status.executionStatus, new DdiResult(status.finalResult, null), status.code, messages)); } /**