Integrated Maintenance Window fields in Management API and UI (#677)

* Added Maintenance Window properties to API and UI

* extended Management API with Maintenance Window schedule, duration, timezone and nextAt properties
* extended integration tests for the above properties
* extended Management UI with Maintenance Window column in Action History grid, added tooltip for next execution
* general refactoring

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com>

* fixed Sonar issues

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com>

* changed the documentation help link for maintenance window

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com>

* added licence header, first refactoring after partial PR review

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com>

* changes related to PR review findings

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com>

* last PR review findings

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com>
This commit is contained in:
Bondar Bogdan
2018-05-15 11:12:33 +02:00
committed by Dominic Schabel
parent 1deb47a4db
commit b5114081be
22 changed files with 474 additions and 210 deletions

View File

@@ -8,88 +8,32 @@
*/
package org.eclipse.hawkbit.mgmt.json.model;
import java.util.concurrent.TimeUnit;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* JSON model for Management API to define the maintenance window based on a
* schedule defined as cron expression, duration in HH:mm:ss format and time
* zone as offset from UTC.
* JSON model for Management API to define the maintenance window.
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtMaintenanceWindow {
private String maintenanceSchedule;
private String maintenanceWindowDuration;
private String maintenanceWindowTimeZone;
public class MgmtMaintenanceWindow extends MgmtMaintenanceWindowRequestBody {
/**
* Sets the maintenance schedule.
*
* @param maintenanceSchedule
* is the cron expression to be used for scheduling maintenance
* window(s). Expression has 6 mandatory fields and a last
* optional field: "second minute hour dayofmonth month weekday
* year".
* Time in {@link TimeUnit#MILLISECONDS} of the next maintenance window
* start
*/
@JsonSetter("schedule")
public void setMaintenanceSchedule(final String maintenanceSchedule) {
this.maintenanceSchedule = maintenanceSchedule;
@JsonProperty
private long nextStartAt;
public long getNextStartAt() {
return nextStartAt;
}
/**
* Sets the maintenance window duration.
*
* @param maintenanceWindowDuration
* in HH:mm:ss format specifying the duration of a maintenance
* window, for example 00:30:00 for 30 minutes.
*/
@JsonSetter("duration")
public void setMaintenanceWindowDuration(final String maintenanceWindowDuration) {
this.maintenanceWindowDuration = maintenanceWindowDuration;
}
/**
* Sets the maintenance window timezone.
*
* @param maintenanceWindowTimeZone
* is the time zone specified as +/-hh:mm offset from UTC. For
* example +02:00 for CET summer time and +00:00 for UTC. The
* start time of a maintenance window calculated based on the
* cron expression is relative to this time zone.
*/
@JsonSetter("timezone")
public void setMaintenanceWindowTimeZone(final String maintenanceWindowTimeZone) {
this.maintenanceWindowTimeZone = maintenanceWindowTimeZone;
}
/**
* Returns the maintenance schedule for the {@link Action}.
*
* @return cron expression as {@link String}.
*/
public String getMaintenanceSchedule() {
return maintenanceSchedule;
}
/**
* Returns the duration of maintenance window for the {@link Action}.
*
* @return duration in HH:mm:ss format as {@link String}.
*/
public String getMaintenanceWindowDuration() {
return maintenanceWindowDuration;
}
/**
* Returns the timezone of maintenance window for the {@link Action}.
*
* @return the timezone offset from UTC in +/-hh:mm as {@link String}.
*/
public String getMaintenanceWindowTimeZone() {
return maintenanceWindowTimeZone;
public void setNextStartAt(final long nextStartAt) {
this.nextStartAt = nextStartAt;
}
}

View File

@@ -0,0 +1,76 @@
/**
* Copyright (c) 2018 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Request body for maintenance window PUT/POST commands, based on a schedule
* defined as cron expression, duration in HH:mm:ss format and time zone as
* offset from UTC.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtMaintenanceWindowRequestBody {
@JsonProperty
private String schedule;
@JsonProperty
private String duration;
@JsonProperty
private String timezone;
public String getSchedule() {
return schedule;
}
/**
* @param schedule
* is the cron expression to be used for scheduling maintenance
* window(s). Expression has 6 mandatory fields and a last
* optional field: "second minute hour dayofmonth month weekday
* year".
*/
public void setSchedule(final String schedule) {
this.schedule = schedule;
}
public String getDuration() {
return duration;
}
/**
* @param duration
* in HH:mm:ss format specifying the duration of a maintenance
* window, for example 00:30:00 for 30 minutes.
*/
public void setDuration(final String duration) {
this.duration = duration;
}
public String getTimezone() {
return timezone;
}
/**
* @param timezone
* is the time zone specified as +/-hh:mm offset from UTC. For
* example +02:00 for CET summer time and +00:00 for UTC. The
* start time of a maintenance window calculated based on the
* cron expression is relative to this time zone.
*/
public void setTimezone(final String timezone) {
this.timezone = timezone;
}
}

View File

@@ -9,6 +9,7 @@
package org.eclipse.hawkbit.mgmt.json.model.action;
import org.eclipse.hawkbit.mgmt.json.model.MgmtBaseEntity;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindow;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@@ -59,6 +60,17 @@ public class MgmtAction extends MgmtBaseEntity {
@JsonProperty
private MgmtActionType forceType;
@JsonProperty
private MgmtMaintenanceWindow maintenanceWindow;
public MgmtMaintenanceWindow getMaintenanceWindow() {
return maintenanceWindow;
}
public void setMaintenanceWindow(final MgmtMaintenanceWindow maintenanceWindow) {
this.maintenanceWindow = maintenanceWindow;
}
public Long getForceTime() {
return forceTime;
}
@@ -75,47 +87,26 @@ public class MgmtAction extends MgmtBaseEntity {
this.forceType = forceType;
}
/**
* @return the status
*/
public String getStatus() {
return status;
}
/**
* @param status
* the status to set
*/
public void setStatus(final String status) {
this.status = status;
}
/**
* @return the actionId
*/
public Long getActionId() {
return actionId;
}
/**
* @param actionId
* the actionId to set
*/
public void setActionId(final Long actionId) {
this.actionId = actionId;
}
/**
* @return the type
*/
public String getType() {
return type;
}
/**
* @param type
* the type to set
*/
public void setType(final String type) {
this.type = type;
}

View File

@@ -8,7 +8,7 @@
*/
package org.eclipse.hawkbit.mgmt.json.model.distributionset;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindow;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindowRequestBody;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -28,10 +28,10 @@ public class MgmtTargetAssignmentRequestBody {
private MgmtActionType type;
/**
* {@link MgmtMaintenanceWindow} object containing schedule, duration and
* timezone.
* {@link MgmtMaintenanceWindowRequestBody} object containing schedule,
* duration and timezone.
*/
private MgmtMaintenanceWindow maintenanceWindow;
private MgmtMaintenanceWindowRequestBody maintenanceWindow;
public String getId() {
return id;
@@ -57,11 +57,11 @@ public class MgmtTargetAssignmentRequestBody {
this.forcetime = forcetime;
}
public MgmtMaintenanceWindow getMaintenanceWindow() {
public MgmtMaintenanceWindowRequestBody getMaintenanceWindow() {
return maintenanceWindow;
}
public void setMaintenanceWindow(final MgmtMaintenanceWindow maintenanceWindow) {
public void setMaintenanceWindow(final MgmtMaintenanceWindowRequestBody maintenanceWindow) {
this.maintenanceWindow = maintenanceWindow;
}

View File

@@ -4,7 +4,7 @@
package org.eclipse.hawkbit.mgmt.json.model.target;
import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindow;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindowRequestBody;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
/**
@@ -16,10 +16,10 @@ public class MgmtDistributionSetAssignment extends MgmtId {
private MgmtActionType type;
/**
* {@link MgmtMaintenanceWindow} object defining a schedule, duration and
* timezone.
* {@link MgmtMaintenanceWindowRequestBody} object defining a schedule,
* duration and timezone.
*/
private MgmtMaintenanceWindow maintenanceWindow;
private MgmtMaintenanceWindowRequestBody maintenanceWindow;
public MgmtActionType getType() {
return type;
@@ -38,21 +38,23 @@ public class MgmtDistributionSetAssignment extends MgmtId {
}
/**
* Returns {@link MgmtMaintenanceWindow} for distribution set assignment.
* Returns {@link MgmtMaintenanceWindowRequestBody} for distribution set
* assignment.
*
* @return {@link MgmtMaintenanceWindow}.
* @return {@link MgmtMaintenanceWindowRequestBody}.
*/
public MgmtMaintenanceWindow getMaintenanceWindow() {
public MgmtMaintenanceWindowRequestBody getMaintenanceWindow() {
return maintenanceWindow;
}
/**
* Sets {@link MgmtMaintenanceWindow} for distribution set assignment.
* Sets {@link MgmtMaintenanceWindowRequestBody} for distribution set
* assignment.
*
* @param maintenanceWindow
* as {@link MgmtMaintenanceWindow}.
* as {@link MgmtMaintenanceWindowRequestBody}.
*/
public void setMaintenanceWindow(final MgmtMaintenanceWindow maintenanceWindow) {
public void setMaintenanceWindow(final MgmtMaintenanceWindowRequestBody maintenanceWindow) {
this.maintenanceWindow = maintenanceWindow;
}
}

View File

@@ -12,6 +12,7 @@ import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindowRequestBody;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadataBodyPut;
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
@@ -252,14 +253,16 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
final DistributionSetAssignmentResult assignDistributionSet = this.deployManagament
.assignDistributionSet(distributionSetId, assignments.stream().map(t -> {
if (t.getMaintenanceWindow() == null) {
final MgmtMaintenanceWindowRequestBody maintenanceWindow = t.getMaintenanceWindow();
if (maintenanceWindow == null) {
return new TargetWithActionType(t.getId(), MgmtRestModelMapper.convertActionType(t.getType()),
t.getForcetime());
}
final String cronSchedule = t.getMaintenanceWindow().getMaintenanceSchedule();
final String duration = t.getMaintenanceWindow().getMaintenanceWindowDuration();
final String timezone = t.getMaintenanceWindow().getMaintenanceWindowTimeZone();
final String cronSchedule = maintenanceWindow.getSchedule();
final String duration = maintenanceWindow.getDuration();
final String timezone = maintenanceWindow.getTimezone();
MaintenanceScheduleHelper.validateMaintenanceSchedule(cronSchedule, duration, timezone);

View File

@@ -19,6 +19,7 @@ import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindow;
import org.eclipse.hawkbit.mgmt.json.model.MgmtPollStatus;
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction;
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionStatus;
@@ -194,6 +195,16 @@ public final class MgmtTargetMapper {
result.setStatus(MgmtAction.ACTION_FINISHED);
}
if (action.hasMaintenanceSchedule()) {
final MgmtMaintenanceWindow maintenanceWindow = new MgmtMaintenanceWindow();
maintenanceWindow.setSchedule(action.getMaintenanceWindowSchedule());
maintenanceWindow.setDuration(action.getMaintenanceWindowDuration());
maintenanceWindow.setTimezone(action.getMaintenanceWindowTimeZone());
action.getMaintenanceWindowStartTime()
.ifPresent(nextStart -> maintenanceWindow.setNextStartAt(nextStart.toInstant().toEpochMilli()));
result.setMaintenanceWindow(maintenanceWindow);
}
MgmtRestModelMapper.mapBaseToBase(result, action);
result.add(linkTo(methodOn(MgmtTargetRestApi.class).getAction(targetId, action.getId())).withSelfRel());

View File

@@ -15,7 +15,7 @@ import java.util.Map;
import javax.validation.ValidationException;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindow;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindowRequestBody;
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction;
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionRequestBodyPut;
@@ -276,7 +276,7 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
}
findTargetWithExceptionIfNotFound(controllerId);
final MgmtMaintenanceWindow maintenanceWindow = dsId.getMaintenanceWindow();
final MgmtMaintenanceWindowRequestBody maintenanceWindow = dsId.getMaintenanceWindow();
if (maintenanceWindow == null) {
return ResponseEntity.ok(MgmtDistributionSetMapper.toResponse(this.deploymentManagement
@@ -284,9 +284,9 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
MgmtRestModelMapper.convertActionType(dsId.getType()), dsId.getForcetime())))));
}
final String cronSchedule = maintenanceWindow.getMaintenanceSchedule();
final String duration = maintenanceWindow.getMaintenanceWindowDuration();
final String timezone = maintenanceWindow.getMaintenanceWindowTimeZone();
final String cronSchedule = maintenanceWindow.getSchedule();
final String duration = maintenanceWindow.getDuration();
final String timezone = maintenanceWindow.getTimezone();
MaintenanceScheduleHelper.validateMaintenanceSchedule(cronSchedule, duration, timezone);

View File

@@ -853,17 +853,97 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
}
@Test
@Description("Ensures that the expected response is return when update was cancelled.")
@Description("Ensures that the expected response is returned for update action.")
public void getUpdateAction() throws Exception {
final String knownTargetId = "targetId";
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/" + actions.get(1).getId()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("id", equalTo(actions.get(1).getId().intValue())))
.andExpect(jsonPath("type", equalTo("update"))).andExpect(jsonPath("status", equalTo("pending")))
.andExpect(jsonPath("forceType", equalTo("forced")))
.andExpect(jsonPath("maintenanceWindow").doesNotExist())
.andExpect(jsonPath("_links.self.href",
equalTo(generateActionSelfLink(knownTargetId, actions.get(1).getId()))))
.andExpect(jsonPath("_links.distributionset.href",
equalTo(generateActionDsLink(actions.get(1).getDistributionSet().getId()))))
.andExpect(jsonPath("_links.status.href",
equalTo(generateStatusreferenceLink(knownTargetId, actions.get(1).getId()))));
}
@Test
@Description("Ensures that the expected response is returned for update action with maintenance window.")
public void getUpdateActionWithMaintenanceWindow() throws Exception {
final String knownTargetId = "targetId";
final String schedule = getTestSchedule(10);
final String duration = getTestDuration(10);
final String timezone = getTestTimeZone();
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverrideWithMaintenanceWindow(knownTargetId,
schedule, duration, timezone);
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/" + actions.get(1).getId()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("id", equalTo(actions.get(1).getId().intValue())))
.andExpect(jsonPath("type", equalTo("update"))).andExpect(jsonPath("status", equalTo("pending")))
.andExpect(jsonPath("forceType", equalTo("forced")))
.andExpect(jsonPath("maintenanceWindow.schedule", equalTo(schedule)))
.andExpect(jsonPath("maintenanceWindow.duration", equalTo(duration)))
.andExpect(jsonPath("maintenanceWindow.timezone", equalTo(timezone)))
.andExpect(jsonPath("maintenanceWindow.nextStartAt",
equalTo(actions.get(1).getMaintenanceWindowStartTime().get().toInstant().toEpochMilli())))
.andExpect(jsonPath("_links.self.href",
equalTo(generateActionSelfLink(knownTargetId, actions.get(1).getId()))))
.andExpect(jsonPath("_links.distributionset.href",
equalTo(generateActionDsLink(actions.get(1).getDistributionSet().getId()))))
.andExpect(jsonPath("_links.status.href",
equalTo(generateStatusreferenceLink(knownTargetId, actions.get(1).getId()))));
}
@Test
@Description("Ensures that the expected response is returned when update action was cancelled.")
public void getCancelAction() throws Exception {
final String knownTargetId = "targetId";
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/" + actions.get(0).getId()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("id", equalTo(actions.get(0).getId().intValue())))
.andExpect(jsonPath("type", equalTo("cancel"))).andExpect(jsonPath("status", equalTo("pending")))
.andExpect(jsonPath("forceType", equalTo("forced")))
.andExpect(jsonPath("maintenanceWindow").doesNotExist())
.andExpect(jsonPath("_links.self.href",
equalTo(generateActionSelfLink(knownTargetId, actions.get(0).getId()))))
.andExpect(jsonPath("_links.canceledaction.href",
equalTo(generateCanceledactionreferenceLink(knownTargetId, actions.get(0)))))
.andExpect(jsonPath("_links.status.href",
equalTo(generateStatusreferenceLink(knownTargetId, actions.get(0).getId()))));
}
@Test
@Description("Ensures that the expected response is returned when update action with maintenance window was cancelled.")
public void getCancelActionWithMaintenanceWindow() throws Exception {
final String knownTargetId = "targetId";
final String schedule = getTestSchedule(10);
final String duration = getTestDuration(10);
final String timezone = getTestTimeZone();
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverrideWithMaintenanceWindow(knownTargetId,
schedule, duration, timezone);
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/" + actions.get(0).getId()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("id", equalTo(actions.get(0).getId().intValue())))
.andExpect(jsonPath("forceType", equalTo("forced"))).andExpect(jsonPath("type", equalTo("cancel")))
.andExpect(jsonPath("status", equalTo("pending")))
.andExpect(jsonPath("maintenanceWindow.schedule", equalTo(schedule)))
.andExpect(jsonPath("maintenanceWindow.duration", equalTo(duration)))
.andExpect(jsonPath("maintenanceWindow.timezone", equalTo(timezone)))
.andExpect(jsonPath("maintenanceWindow.nextStartAt",
equalTo(actions.get(0).getMaintenanceWindowStartTime().get().toInstant().toEpochMilli())))
.andExpect(jsonPath("_links.self.href",
equalTo(generateActionSelfLink(knownTargetId, actions.get(0).getId()))))
.andExpect(jsonPath("_links.canceledaction.href",
@@ -896,6 +976,44 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(2)));
}
@Test
@Description("Ensures that the expected response of getting actions with maintenance window of a target is returned.")
public void getMultipleActionsWithMaintenanceWindow() throws Exception {
final String knownTargetId = "targetId";
final String schedule = getTestSchedule(10);
final String duration = getTestDuration(10);
final String timezone = getTestTimeZone();
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverrideWithMaintenanceWindow(knownTargetId,
schedule, duration, timezone);
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS).param(MgmtRestConstants.REQUEST_PARAMETER_SORTING, "ID:ASC"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("content.[1].id", equalTo(actions.get(1).getId().intValue())))
.andExpect(jsonPath("content.[1].type", equalTo("update")))
.andExpect(jsonPath("content.[1].status", equalTo("pending")))
.andExpect(jsonPath("content.[1].maintenanceWindow.schedule", equalTo(schedule)))
.andExpect(jsonPath("content.[1].maintenanceWindow.duration", equalTo(duration)))
.andExpect(jsonPath("content.[1].maintenanceWindow.timezone", equalTo(timezone)))
.andExpect(jsonPath("content.[1].maintenanceWindow.nextStartAt",
equalTo(actions.get(1).getMaintenanceWindowStartTime().get().toInstant().toEpochMilli())))
.andExpect(jsonPath("content.[1]._links.self.href",
equalTo(generateActionSelfLink(knownTargetId, actions.get(1).getId()))))
.andExpect(jsonPath("content.[0].id", equalTo(actions.get(0).getId().intValue())))
.andExpect(jsonPath("content.[0].type", equalTo("cancel")))
.andExpect(jsonPath("content.[0].status", equalTo("pending")))
.andExpect(jsonPath("content.[0].maintenanceWindow.schedule", equalTo(schedule)))
.andExpect(jsonPath("content.[0].maintenanceWindow.duration", equalTo(duration)))
.andExpect(jsonPath("content.[0].maintenanceWindow.timezone", equalTo(timezone)))
.andExpect(jsonPath("content.[0].maintenanceWindow.nextStartAt",
equalTo(actions.get(0).getMaintenanceWindowStartTime().get().toInstant().toEpochMilli())))
.andExpect(jsonPath("content.[0]._links.self.href",
equalTo(generateActionSelfLink(knownTargetId, actions.get(0).getId()))))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(2)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(2)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(2)));
}
@Test
@Description("Verifies that the API returns the status list with expected content.")
public void getMultipleActionStatus() throws Exception {
@@ -1054,6 +1172,10 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/" + actionId;
}
private String generateActionDsLink(final Long dsId) {
return "http://localhost" + MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + dsId;
}
private String generateCanceledactionreferenceLink(final String knownTargetId, final Action action) {
return "http://localhost" + MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/" + action.getId();
@@ -1067,20 +1189,33 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
private List<Action> generateTargetWithTwoUpdatesWithOneOverride(final String knownTargetId)
throws InterruptedException {
return generateTargetWithTwoUpdatesWithOneOverrideWithMaintenanceWindow(knownTargetId, null, null, null);
}
private List<Action> generateTargetWithTwoUpdatesWithOneOverrideWithMaintenanceWindow(final String knownTargetId,
final String schedule, final String duration, final String timezone) throws InterruptedException {
final Target target = testdataFactory.createTarget(knownTargetId);
final List<Target> targets = Arrays.asList(target);
final Iterator<DistributionSet> sets = testdataFactory.createDistributionSets(2).iterator();
final DistributionSet one = sets.next();
final DistributionSet two = sets.next();
// Update
final List<Target> updatedTargets = assignDistributionSet(one, targets).getAssignedEntity();
// 2nd update
// sleep 10ms to ensure that we can sort by reportedAt
Thread.sleep(10);
assignDistributionSet(two, updatedTargets);
if (schedule == null) {
final List<Target> updatedTargets = assignDistributionSet(one, Arrays.asList(target)).getAssignedEntity();
// 2nd update
// sleep 10ms to ensure that we can sort by reportedAt
Thread.sleep(10);
assignDistributionSet(two, updatedTargets);
} else {
final List<Target> updatedTargets = assignDistributionSetWithMaintenanceWindow(one.getId(),
target.getControllerId(), schedule, duration, timezone).getAssignedEntity();
// 2nd update
// sleep 10ms to ensure that we can sort by reportedAt
Thread.sleep(10);
assignDistributionSetWithMaintenanceWindow(two.getId(), updatedTargets.get(0).getControllerId(), schedule,
duration, timezone);
}
// two updates, one cancellation
final List<Action> actions = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE)
@@ -1090,26 +1225,6 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
return actions;
}
@Test
public void getUpdateAction() throws Exception {
final String knownTargetId = "targetId";
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/" + actions.get(1).getId()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("id", equalTo(actions.get(1).getId().intValue())))
.andExpect(jsonPath("type", equalTo("update"))).andExpect(jsonPath("status", equalTo("pending")))
.andExpect(jsonPath("forceType", equalTo("forced")))
.andExpect(jsonPath("_links.self.href",
equalTo(generateActionSelfLink(knownTargetId, actions.get(1).getId()))))
.andExpect(jsonPath("_links.distributionset.href",
equalTo("http://localhost/rest/v1/distributionsets/"
+ actions.get(1).getDistributionSet().getId())))
.andExpect(jsonPath("_links.status.href",
equalTo(generateStatusreferenceLink(knownTargetId, actions.get(1).getId()))));
}
@Test
@Description("Verfies that an action is switched from soft to forced if requested by management API")
public void updateAction() throws Exception {
@@ -1266,6 +1381,28 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
.andExpect(status().isOk());
}
@Test
@Description("Assigns distribution set to target with maintenance window next execution start (should be ignored, calculated automaticaly based on schedule, duration and timezone)")
public void assignDistributionSetToTargetWithMaintenanceWindowNextExecutionStart() throws Exception {
final Target target = testdataFactory.createTarget("fsdfsd");
final DistributionSet set = testdataFactory.createDistributionSet("one");
final long nextExecutionStart = System.currentTimeMillis();
final String body = new JSONObject().put("id", set.getId())
.put("maintenanceWindow", getMaintenanceWindowWithNextStart(getTestSchedule(10), getTestDuration(10),
getTestTimeZone(), nextExecutionStart))
.toString();
mvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + target.getControllerId() + "/assignedDS")
.content(body).contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + target.getControllerId() + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS)).andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
.andExpect(jsonPath("content.[0].maintenanceWindow.nextStartAt", not(nextExecutionStart)));
}
@Test
@Description("Assigns distribution set to target with last maintenance window scheduled before current time.")
public void assignDistributionSetToTargetWithMaintenanceWindowEndTimeBeforeStartTime() throws Exception {

View File

@@ -55,7 +55,7 @@ public final class MaintenanceScheduleHelper {
* start time of a maintenance window calculated based on the
* cron expression is relative to this time zone.
*
* @return {@link Optional<ZonedDateTime>} of the next available window. In
* @return { @link Optional<ZonedDateTime>} of the next available window. In
* case there is none, or there are maintenance window validation
* errors, returns empty value.
*

View File

@@ -8,6 +8,8 @@
*/
package org.eclipse.hawkbit.repository.model;
import java.time.ZonedDateTime;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
/**
@@ -18,7 +20,7 @@ public interface Action extends TenantAwareBaseEntity {
/**
* Maximum length of controllerId.
*/
int MAINTENANCE_SCHEDULE_CRON_LENGTH = 128;
int MAINTENANCE_WINDOW_SCHEDULE_LENGTH = 128;
/**
* Maximum length of controllerId.
@@ -81,6 +83,21 @@ public interface Action extends TenantAwareBaseEntity {
*/
Rollout getRollout();
/**
* @return maintenance window schedule related to this {@link Action}.
*/
String getMaintenanceWindowSchedule();
/**
* @return maintenance window duration related to this {@link Action}.
*/
String getMaintenanceWindowDuration();
/**
* @return maintenance window time zone related to this {@link Action}.
*/
String getMaintenanceWindowTimeZone();
/**
* checks if the {@link #getForcedTime()} is hit by the given
* {@code hitTimeMillis}, by means if the given milliseconds are greater
@@ -215,6 +232,15 @@ public interface Action extends TenantAwareBaseEntity {
TIMEFORCED;
}
/**
* Returns the start time of next available maintenance window for the
* {@link Action} as {@link ZonedDateTime}. If a maintenance window is
* already active, the start time of currently active window is returned.
*
* @return the start time as { @link Optional<ZonedDateTime>}.
*/
Optional<ZonedDateTime> getMaintenanceWindowStartTime();
/**
* The method checks whether the action has a maintenance schedule defined
* for it. A maintenance schedule defines a set of maintenance windows

View File

@@ -229,7 +229,7 @@ public abstract class AbstractDsAssignmentStrategy {
actionForTarget.setActive(true);
actionForTarget.setTarget(target);
actionForTarget.setDistributionSet(set);
actionForTarget.setMaintenanceSchedule(targetWithActionType.getMaintenanceSchedule());
actionForTarget.setMaintenanceWindowSchedule(targetWithActionType.getMaintenanceSchedule());
actionForTarget.setMaintenanceWindowDuration(targetWithActionType.getMaintenanceWindowDuration());
actionForTarget.setMaintenanceWindowTimeZone(targetWithActionType.getMaintenanceWindowTimeZone());
return actionForTarget;

View File

@@ -117,8 +117,8 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@JoinColumn(name = "rollout", updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rollout"))
private JpaRollout rollout;
@Column(name = "maintenance_cron_schedule", updatable = false, length = Action.MAINTENANCE_SCHEDULE_CRON_LENGTH)
private String maintenanceSchedule;
@Column(name = "maintenance_cron_schedule", updatable = false, length = Action.MAINTENANCE_WINDOW_SCHEDULE_LENGTH)
private String maintenanceWindowSchedule;
@Column(name = "maintenance_duration", updatable = false, length = Action.MAINTENANCE_WINDOW_DURATION_LENGTH)
private String maintenanceWindowDuration;
@@ -231,14 +231,24 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
// there is no action deletion
}
@Override
public String getMaintenanceWindowSchedule() {
return maintenanceWindowSchedule;
}
/**
* Sets the maintenance schedule.
*
* @param maintenanceSchedule
* @param maintenanceWindowSchedule
* is a cron expression to be used for scheduling.
*/
public void setMaintenanceSchedule(final String maintenanceSchedule) {
this.maintenanceSchedule = maintenanceSchedule;
public void setMaintenanceWindowSchedule(final String maintenanceWindowSchedule) {
this.maintenanceWindowSchedule = maintenanceWindowSchedule;
}
@Override
public String getMaintenanceWindowDuration() {
return maintenanceWindowDuration;
}
/**
@@ -252,6 +262,11 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
this.maintenanceWindowDuration = maintenanceWindowDuration;
}
@Override
public String getMaintenanceWindowTimeZone() {
return maintenanceWindowTimeZone;
}
/**
* Sets the time zone to be used for maintenance window.
*
@@ -265,15 +280,9 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
this.maintenanceWindowTimeZone = maintenanceWindowTimeZone;
}
/**
* Returns the start time of next available maintenance window for the
* {@link Action} as {@link ZonedDateTime}. If a maintenance window is
* already active, the start time of currently active window is returned.
*
* @return the start time as {@link Optional<ZonedDateTime>}.
*/
@Override
public Optional<ZonedDateTime> getMaintenanceWindowStartTime() {
return MaintenanceScheduleHelper.getNextMaintenanceWindow(maintenanceSchedule, maintenanceWindowDuration,
return MaintenanceScheduleHelper.getNextMaintenanceWindow(maintenanceWindowSchedule, maintenanceWindowDuration,
maintenanceWindowTimeZone);
}
@@ -282,7 +291,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
* the {@link Action} as {@link ZonedDateTime}. If a maintenance window is
* already active, the end time of currently active window is returned.
*
* @return the end time of window as {@link Optional<ZonedDateTime>}.
* @return the end time of window as { @link Optional<ZonedDateTime>}.
*/
private Optional<ZonedDateTime> getMaintenanceWindowEndTime() {
return getMaintenanceWindowStartTime()
@@ -291,7 +300,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@Override
public boolean hasMaintenanceSchedule() {
return this.maintenanceSchedule != null;
return this.maintenanceWindowSchedule != null;
}
@Override

View File

@@ -225,7 +225,7 @@ public abstract class AbstractIntegrationTest {
}
};
protected DistributionSetAssignmentResult assignDistributionSet(final Long dsID, final String controllerId) {
protected DistributionSetAssignmentResult assignDistributionSet(final long dsID, final String controllerId) {
return deploymentManagement.assignDistributionSet(dsID, Arrays.asList(
new TargetWithActionType(controllerId, ActionType.FORCED, RepositoryModelConstants.NO_FORCE_TIME)));
}
@@ -253,18 +253,26 @@ public abstract class AbstractIntegrationTest {
* start time of a maintenance window calculated based on the
* cron expression is relative to this time zone
*
* @return result of the assignment as
* {@link DistributionSetAssignmentResult}.
* @return result of the assignment as { @link
* DistributionSetAssignmentResult}.
*/
protected DistributionSetAssignmentResult assignDistributionSetWithMaintenanceWindow(final Long dsID,
final String controllerId, final String maintenanceSchedule, final String maintenanceWindowDuration,
protected DistributionSetAssignmentResult assignDistributionSetWithMaintenanceWindow(final long dsID,
final String controllerId, final String maintenanceWindowSchedule, final String maintenanceWindowDuration,
final String maintenanceWindowTimeZone) {
return deploymentManagement.assignDistributionSet(dsID,
Arrays.asList(new TargetWithActionType(controllerId, ActionType.FORCED,
RepositoryModelConstants.NO_FORCE_TIME, maintenanceSchedule, maintenanceWindowDuration,
RepositoryModelConstants.NO_FORCE_TIME, maintenanceWindowSchedule, maintenanceWindowDuration,
maintenanceWindowTimeZone)));
}
protected DistributionSetAssignmentResult assignDistributionSetWithMaintenanceWindowTimeForced(final long dsID,
final String controllerId, final String maintenanceWindowSchedule, final String maintenanceWindowDuration,
final String maintenanceWindowTimeZone) {
return deploymentManagement.assignDistributionSet(dsID,
Arrays.asList(new TargetWithActionType(controllerId, ActionType.TIMEFORCED, System.currentTimeMillis(),
maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone)));
}
protected DistributionSetAssignmentResult assignDistributionSet(final DistributionSet pset,
final List<Target> targets) {
return deploymentManagement.assignDistributionSet(pset.getId(),
@@ -275,11 +283,17 @@ public abstract class AbstractIntegrationTest {
return assignDistributionSet(pset, Arrays.asList(target));
}
protected DistributionSetMetadata createDistributionSetMetadata(final Long dsId, final MetaData md) {
protected DistributionSetAssignmentResult assignDistributionSetTimeForced(final DistributionSet pset,
final Target target) {
return deploymentManagement.assignDistributionSet(pset.getId(), Arrays.asList(
new TargetWithActionType(target.getControllerId(), ActionType.TIMEFORCED, System.currentTimeMillis())));
}
protected DistributionSetMetadata createDistributionSetMetadata(final long dsId, final MetaData md) {
return createDistributionSetMetadata(dsId, Collections.singletonList(md)).get(0);
}
protected List<DistributionSetMetadata> createDistributionSetMetadata(final Long dsId, final List<MetaData> md) {
protected List<DistributionSetMetadata> createDistributionSetMetadata(final long dsId, final List<MetaData> md) {
return distributionSetManagement.createMetaData(dsId, md);
}
@@ -378,23 +392,23 @@ public abstract class AbstractIntegrationTest {
*
* @return {@link String} containing a valid cron expression.
*/
public static String getTestSchedule(final int minutesToAdd) {
protected static String getTestSchedule(final int minutesToAdd) {
ZonedDateTime currentTime = ZonedDateTime.now();
currentTime = currentTime.plusMinutes(minutesToAdd);
return String.format("0 %d %d %d %d ? %d", currentTime.getMinute(), currentTime.getHour(),
currentTime.getDayOfMonth(), currentTime.getMonthValue(), currentTime.getYear());
}
public static String getTestDuration(final int duration) {
protected static String getTestDuration(final int duration) {
return String.format("%02d:%02d:00", duration / 60, duration % 60);
}
public static String getTestTimeZone() {
protected static String getTestTimeZone() {
final ZonedDateTime currentTime = ZonedDateTime.now();
return currentTime.getOffset().getId().replace("Z", "+00:00");
}
public static Map<String, String> getMaintenanceWindow(final String schedule, final String duration,
protected static Map<String, String> getMaintenanceWindow(final String schedule, final String duration,
final String timezone) {
final Map<String, String> maintenanceWindowMap = new HashMap<>();
maintenanceWindowMap.put("schedule", schedule);
@@ -402,4 +416,11 @@ public abstract class AbstractIntegrationTest {
maintenanceWindowMap.put("timezone", timezone);
return maintenanceWindowMap;
}
protected static Map<String, String> getMaintenanceWindowWithNextStart(final String schedule, final String duration,
final String timezone, final long nextStartAt) {
final Map<String, String> maintenanceWindowMap = getMaintenanceWindow(schedule, duration, timezone);
maintenanceWindowMap.put("nextStartAt", String.valueOf(nextStartAt));
return maintenanceWindowMap;
}
}

View File

@@ -8,12 +8,16 @@
*/
package org.eclipse.hawkbit.ui.common.grid;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.ui.SpPermissionChecker;
import org.eclipse.hawkbit.ui.components.RefreshableContainer;
import org.eclipse.hawkbit.ui.management.actionhistory.ProxyAction;
import org.eclipse.hawkbit.ui.management.actionhistory.ProxyActionStatus;
import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
@@ -284,8 +288,8 @@ public abstract class AbstractGrid<T extends Indexed> extends Grid implements Re
/**
* Template method invoked by {@link this#addNewContainerDS()} for adding
* properties to the container (usually by invoing
* {@link Container#addContainerProperty(Object, Class, Object))})
* properties to the container (usually by invoking { @link
* Container#addContainerProperty(Object, Class, Object))})
*/
protected abstract void addContainerProperties();
@@ -581,7 +585,7 @@ public abstract class AbstractGrid<T extends Indexed> extends Grid implements Re
* CellStyleGenerator that concerns about alignment in the grid cells.
*/
protected static class AlignCellStyleGenerator implements CellStyleGenerator {
private static final long serialVersionUID = 5573570647129792429L;
private static final long serialVersionUID = 1L;
private final String[] left;
private final String[] center;
@@ -621,29 +625,40 @@ public abstract class AbstractGrid<T extends Indexed> extends Grid implements Re
}
/**
* Adds a tooltip to the 'Date and time' column in detailed format.
* Adds a tooltip to the 'Date and time' and 'Maintenance Window' columns in
* detailed format.
*/
public static class ModifiedTimeTooltipGenerator implements CellDescriptionGenerator {
private static final long serialVersionUID = -6617911967167729195L;
protected static class TooltipGenerator implements CellDescriptionGenerator {
private static final long serialVersionUID = 1L;
private final String datePropertyId;
private final VaadinMessageSource i18n;
/**
* Constructor.
*
* @param datePropertyId
*/
public ModifiedTimeTooltipGenerator(final String datePropertyId) {
this.datePropertyId = datePropertyId;
public TooltipGenerator(final VaadinMessageSource i18n) {
this.i18n = i18n;
}
@Override
public String getDescription(final CellReference cell) {
if (!datePropertyId.equals(cell.getPropertyId())) {
final String propertyId = (String) cell.getPropertyId();
switch (propertyId) {
case ProxyAction.PXY_ACTION_LAST_MODIFIED_AT:
case ProxyActionStatus.PXY_AS_CREATED_AT:
final Long timestamp = (Long) cell.getItem().getItemProperty(propertyId).getValue();
return SPDateTimeUtil.getFormattedDate(timestamp);
case ProxyAction.PXY_ACTION_MAINTENANCE_WINDOW:
final Action action = (Action) cell.getItem().getItemProperty(ProxyAction.PXY_ACTION).getValue();
return action.getMaintenanceWindowStartTime().map(this::getFormattedNextMaintenanceWindow).orElse(null);
default:
return null;
}
final Long timestamp = (Long) cell.getItem().getItemProperty(datePropertyId).getValue();
return SPDateTimeUtil.getFormattedDate(timestamp);
}
private String getFormattedNextMaintenanceWindow(final ZonedDateTime nextAt) {
final long nextAtMilli = nextAt.toInstant().toEpochMilli();
return i18n.getMessage("tooltip.next.maintenancewindow",
SPDateTimeUtil.getFormattedDate(nextAtMilli, SPUIDefinitions.LAST_QUERY_DATE_FORMAT_SHORT));
}
}

View File

@@ -115,12 +115,19 @@ public class ActionBeanQuery extends AbstractBeanQuery<ProxyAction> {
proxyAction.setLastModifiedAt(action.getLastModifiedAt());
proxyAction.setRolloutName(action.getRollout() != null ? action.getRollout().getName() : "");
proxyAction.setStatus(action.getStatus());
proxyAction.setMaintenanceWindow(
action.hasMaintenanceSchedule() ? buildMaintenanceWindowDisplayText(action) : "");
proxyActions.add(proxyAction);
}
return proxyActions;
}
private static String buildMaintenanceWindowDisplayText(final Action action) {
return action.getMaintenanceWindowSchedule() + "/" + action.getMaintenanceWindowDuration() + "/"
+ action.getMaintenanceWindowTimeZone();
}
/**
* Generates a virtual IsActiveDecoration for the presentation layer that is
* calculated from {@link Action#isActive()} and

View File

@@ -78,13 +78,14 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
private static final Object[] maxColumnOrder = new Object[] { ProxyAction.PXY_ACTION_IS_ACTIVE_DECO,
ProxyAction.PXY_ACTION_ID, ProxyAction.PXY_ACTION_DS_NAME_VERSION, ProxyAction.PXY_ACTION_LAST_MODIFIED_AT,
ProxyAction.PXY_ACTION_STATUS, ProxyAction.PXY_ACTION_ROLLOUT_NAME, VIRT_PROP_FORCED, VIRT_PROP_TIMEFORCED,
VIRT_PROP_ACTION_CANCEL, VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT };
ProxyAction.PXY_ACTION_STATUS, ProxyAction.PXY_ACTION_MAINTENANCE_WINDOW,
ProxyAction.PXY_ACTION_ROLLOUT_NAME, VIRT_PROP_FORCED, VIRT_PROP_TIMEFORCED, VIRT_PROP_ACTION_CANCEL,
VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT };
private static final Object[] minColumnOrder = new Object[] { ProxyAction.PXY_ACTION_IS_ACTIVE_DECO,
ProxyAction.PXY_ACTION_DS_NAME_VERSION, ProxyAction.PXY_ACTION_LAST_MODIFIED_AT,
ProxyAction.PXY_ACTION_STATUS, VIRT_PROP_FORCED, VIRT_PROP_TIMEFORCED, VIRT_PROP_ACTION_CANCEL,
VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT };
ProxyAction.PXY_ACTION_STATUS, ProxyAction.PXY_ACTION_MAINTENANCE_WINDOW, VIRT_PROP_FORCED,
VIRT_PROP_TIMEFORCED, VIRT_PROP_ACTION_CANCEL, VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT };
private static final String[] leftAlignedColumns = new String[] { VIRT_PROP_TIMEFORCED };
@@ -99,7 +100,7 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
private Target selectedTarget;
private final AlignCellStyleGenerator alignGenerator;
private final ModifiedTimeTooltipGenerator modTimetooltipGenerator;
private final TooltipGenerator tooltipGenerator;
private final Map<Action.Status, StatusFontIcon> states;
private final Map<IsActiveDecoration, StatusFontIcon> activeStates;
@@ -139,7 +140,7 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
activeStates = conf
.createActiveStatusLabelConfig(UIComponentIdProvider.ACTION_HISTORY_TABLE_ACTIVESTATE_LABEL_ID);
alignGenerator = new AlignCellStyleGenerator(leftAlignedColumns, centerAlignedColumns, rightAlignedColumns);
modTimetooltipGenerator = new ModifiedTimeTooltipGenerator(ProxyAction.PXY_ACTION_LAST_MODIFIED_AT);
tooltipGenerator = new TooltipGenerator(i18n);
init();
}
@@ -208,6 +209,7 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
rawCont.addContainerProperty(ProxyAction.PXY_ACTION_ID, String.class, null, true, true);
rawCont.addContainerProperty(ProxyAction.PXY_ACTION_ROLLOUT_NAME, String.class, null, true, true);
rawCont.addContainerProperty(ProxyAction.PXY_ACTION_MAINTENANCE_WINDOW, String.class, null, true, false);
}
@Override
@@ -436,11 +438,14 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
getColumn(VIRT_PROP_ACTION_CANCEL).setHidable(false);
getColumn(VIRT_PROP_ACTION_FORCE).setHidable(false);
getColumn(VIRT_PROP_ACTION_FORCE_QUIT).setHidable(false);
getColumn(ProxyAction.PXY_ACTION_MAINTENANCE_WINDOW).setHidden(true);
getColumn(ProxyAction.PXY_ACTION_MAINTENANCE_WINDOW).setHidable(true);
}
@Override
protected CellDescriptionGenerator getDescriptionGenerator() {
return modTimetooltipGenerator;
return tooltipGenerator;
}
@Override
@@ -451,6 +456,8 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
getColumn(ProxyAction.PXY_ACTION_DS_NAME_VERSION).setHeaderCaption(SPUIDefinitions.ACTION_HIS_TBL_DIST);
getColumn(ProxyAction.PXY_ACTION_LAST_MODIFIED_AT).setHeaderCaption(SPUIDefinitions.ACTION_HIS_TBL_DATETIME);
getColumn(ProxyAction.PXY_ACTION_STATUS).setHeaderCaption(SPUIDefinitions.ACTION_HIS_TBL_STATUS);
getColumn(ProxyAction.PXY_ACTION_MAINTENANCE_WINDOW)
.setHeaderCaption(SPUIDefinitions.ACTION_HIS_TBL_MAINTENANCE_WINDOW);
getColumn(VIRT_PROP_FORCED).setHeaderCaption(String.valueOf(forceClientRefreshToggle));
forceClientRefreshToggle = !forceClientRefreshToggle;
@@ -463,8 +470,9 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
protected void setColumnExpandRatio() {
setColumnsSize(50.0, 50.0, ProxyAction.PXY_ACTION_IS_ACTIVE_DECO);
setColumnsSize(107.0, 500.0, ProxyAction.PXY_ACTION_DS_NAME_VERSION);
setColumnsSize(100.0, 120.0, ProxyAction.PXY_ACTION_LAST_MODIFIED_AT);
setColumnsSize(100.0, 130.0, ProxyAction.PXY_ACTION_LAST_MODIFIED_AT);
setColumnsSize(53.0, 55.0, ProxyAction.PXY_ACTION_STATUS);
setColumnsSize(150.0, 200.0, ProxyAction.PXY_ACTION_MAINTENANCE_WINDOW);
setColumnsSize(FIXED_PIX_MIN, FIXED_PIX_MIN, VIRT_PROP_FORCED, VIRT_PROP_TIMEFORCED, VIRT_PROP_ACTION_CANCEL,
VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT);
}

View File

@@ -36,7 +36,7 @@ public class ActionStatusGrid extends AbstractGrid<LazyQueryContainer> {
private static final String[] centerAlignedColumns = new String[] { ProxyActionStatus.PXY_AS_STATUS };
private final AlignCellStyleGenerator alignGenerator;
private final ModifiedTimeTooltipGenerator modTimetooltipGenerator;
private final TooltipGenerator tooltipGenerator;
private final Map<Action.Status, StatusFontIcon> states;
@@ -58,7 +58,7 @@ public class ActionStatusGrid extends AbstractGrid<LazyQueryContainer> {
final LabelConfig conf = new ActionHistoryGrid.LabelConfig();
states = conf.createStatusLabelConfig(i18n, UIComponentIdProvider.ACTION_STATUS_GRID_STATUS_LABEL_ID);
alignGenerator = new AlignCellStyleGenerator(leftAlignedColumns, centerAlignedColumns, null);
modTimetooltipGenerator = new ModifiedTimeTooltipGenerator(ProxyActionStatus.PXY_AS_CREATED_AT);
tooltipGenerator = new TooltipGenerator(i18n);
init();
}
@@ -153,7 +153,7 @@ public class ActionStatusGrid extends AbstractGrid<LazyQueryContainer> {
@Override
protected CellDescriptionGenerator getDescriptionGenerator() {
return modTimetooltipGenerator;
return tooltipGenerator;
}
}

View File

@@ -26,6 +26,7 @@ public class ProxyAction implements Serializable {
public static final String PXY_ACTION = "action";
public static final String PXY_ACTION_LAST_MODIFIED_AT = "lastModifiedAt";
public static final String PXY_ACTION_ROLLOUT_NAME = "rolloutName";
public static final String PXY_ACTION_MAINTENANCE_WINDOW = "maintenanceWindow";
private Action.Status status;
private boolean isActive;
@@ -35,6 +36,15 @@ public class ProxyAction implements Serializable {
private Action action;
private Long lastModifiedAt;
private String rolloutName;
private String maintenanceWindow;
public String getMaintenanceWindow() {
return maintenanceWindow;
}
public void setMaintenanceWindow(final String maintenanceWindow) {
this.maintenanceWindow = maintenanceWindow;
}
/**
* Get id for the entry.
@@ -51,7 +61,7 @@ public class ProxyAction implements Serializable {
* @param id
* of the action entry.
*/
public void setId(Long id) {
public void setId(final Long id) {
this.id = id;
}
@@ -70,7 +80,7 @@ public class ProxyAction implements Serializable {
* @param status
* literal
*/
public void setStatus(Action.Status status) {
public void setStatus(final Action.Status status) {
this.status = status;
}
@@ -91,7 +101,7 @@ public class ProxyAction implements Serializable {
* <code>true</code> if the action is active, otherwise
* <code>false</code>
*/
public void setActive(boolean isActive) {
public void setActive(final boolean isActive) {
this.isActive = isActive;
}
@@ -113,7 +123,7 @@ public class ProxyAction implements Serializable {
* @param isActiveDecoration
* pre-calculated literal
*/
public void setIsActiveDecoration(IsActiveDecoration isActiveDecoration) {
public void setIsActiveDecoration(final IsActiveDecoration isActiveDecoration) {
this.isActiveDecoration = isActiveDecoration;
}
@@ -133,7 +143,7 @@ public class ProxyAction implements Serializable {
* @param dsNameVersion
* combined value
*/
public void setDsNameVersion(String dsNameVersion) {
public void setDsNameVersion(final String dsNameVersion) {
this.dsNameVersion = dsNameVersion;
}
@@ -151,7 +161,7 @@ public class ProxyAction implements Serializable {
*
* @param action
*/
public void setAction(Action action) {
public void setAction(final Action action) {
this.action = action;
}
@@ -170,7 +180,7 @@ public class ProxyAction implements Serializable {
* @param lastModifiedAt
* raw long-value for lastModifiedAt-date
*/
public void setLastModifiedAt(Long lastModifiedAt) {
public void setLastModifiedAt(final Long lastModifiedAt) {
this.lastModifiedAt = lastModifiedAt;
}
@@ -188,7 +198,7 @@ public class ProxyAction implements Serializable {
*
* @param rolloutName
*/
public void setRolloutName(String rolloutName) {
public void setRolloutName(final String rolloutName) {
this.rolloutName = rolloutName;
}

View File

@@ -84,7 +84,7 @@ public class MaintenanceWindowLayout extends VerticalLayout {
* Text field to specify the schedule.
*/
private void createMaintenanceScheduleControl() {
schedule = new TextFieldBuilder(Action.MAINTENANCE_SCHEDULE_CRON_LENGTH)
schedule = new TextFieldBuilder(Action.MAINTENANCE_WINDOW_SCHEDULE_LENGTH)
.id(UIComponentIdProvider.MAINTENANCE_WINDOW_SCHEDULE_ID)
.caption(i18n.getMessage("caption.maintenancewindow.schedule")).validator(new CronValidator())
.prompt("0 0 3 ? * 6").required(true, i18n).buildTextComponent();
@@ -259,6 +259,7 @@ public class MaintenanceWindowLayout extends VerticalLayout {
schedule.setValue("");
duration.setValue("");
timeZone.setValue(getClientTimeZone());
scheduleTranslator.setValue(i18n.getMessage(CRON_VALIDATION_ERROR));
}
/**

View File

@@ -561,6 +561,8 @@ public final class SPUIDefinitions {
*/
public static final String SP_BUTTON_STATUS_STYLE = "targetStatusBtn";
public static final String ACTION_HIS_TBL_MAINTENANCE_WINDOW = "Maintenance Window";
/**
* /** Constructor.
*/

View File

@@ -251,6 +251,7 @@ tooltip.check.for.mandatory=Check to make Mandatory
tooltip.artifact.icon=Show Artifact Details
tooltip.click.to.edit = Click to edit
tooltip.metadata.icon = Manage Metadata
tooltip.next.maintenancewindow = next on {0}
#rollout action
tooltip.rollout.run = Run
tooltip.rollout.pause = Pause