Feature Approval Workflow for rollouts (#678)

* implement feature approval workflow

Signed-off-by: Ioannis Spyropoulos <ioannis.spyropoulos@bosch-si.com>
Signed-off-by: Michael Müller <Michael.Mueller17@bosch-si.com>

* add documentation for REST endpoints

Signed-off-by: Ioannis Spyropoulos <ioannis.spyropoulos@bosch-si.com>
Signed-off-by: Michael Müller <Michael.Mueller17@bosch-si.com>

* fix broken documentation test

Signed-off-by: Ioannis Spyropoulos <ioannis.spyropoulos@bosch-si.com>

* fix broken documentation test II

Signed-off-by: Ioannis Spyropoulos <ioannis.spyropoulos@bosch-si.com>
This commit is contained in:
Michael Müller
2018-06-04 16:36:56 +02:00
committed by Dominic Schabel
parent 8a14c931c8
commit cef7c2bbf2
43 changed files with 1056 additions and 43 deletions

View File

@@ -151,4 +151,12 @@ public class SpPermissionChecker implements Serializable {
public boolean hasRolloutTargetsReadPermission() {
return hasTargetReadPermission() && permissionService.hasPermission(SpPermission.READ_ROLLOUT);
}
/**
*
* @return <code>true</code> if rollout can be approved by the user.
*/
public boolean hasRolloutApprovalPermission() {
return hasRolloutReadPermission() && permissionService.hasPermission(SpPermission.APPROVE_ROLLOUT);
}
}

View File

@@ -137,6 +137,11 @@ public class UiProperties implements Serializable {
*/
private String security = "";
/**
* Link to rollout related documentation.
*/
private String rollout = "";
/**
* Link to target filter view.
*/
@@ -167,6 +172,10 @@ public class UiProperties implements Serializable {
return security;
}
public String getRollout() {
return rollout;
}
public String getSystemConfigurationView() {
return systemConfigurationView;
}
@@ -203,6 +212,10 @@ public class UiProperties implements Serializable {
this.security = security;
}
public void setRollout(final String rollout) {
this.rollout = rollout;
}
public void setSystemConfigurationView(final String systemConfigurationView) {
this.systemConfigurationView = systemConfigurationView;
}

View File

@@ -19,6 +19,7 @@ import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.ui.AbstractHawkbitUI;
import org.eclipse.hawkbit.ui.SpPermissionChecker;
@@ -69,16 +70,17 @@ public class RolloutView extends VerticalLayout implements View {
@Autowired
RolloutView(final SpPermissionChecker permissionChecker, final RolloutUIState rolloutUIState,
final UIEventBus eventBus, final RolloutManagement rolloutManagement,
final RolloutGroupManagement rolloutGroupManagement, final TargetManagement targetManagement,
final UINotification uiNotification, final UiProperties uiProperties, final EntityFactory entityFactory,
final VaadinMessageSource i18n, final TargetFilterQueryManagement targetFilterQueryManagement,
final QuotaManagement quotaManagement) {
final UIEventBus eventBus, final RolloutManagement rolloutManagement,
final RolloutGroupManagement rolloutGroupManagement, final TargetManagement targetManagement,
final UINotification uiNotification, final UiProperties uiProperties, final EntityFactory entityFactory,
final VaadinMessageSource i18n, final TargetFilterQueryManagement targetFilterQueryManagement,
final QuotaManagement quotaManagement,
final TenantConfigurationManagement tenantConfigManagement) {
this.permChecker = permissionChecker;
this.rolloutManagement = rolloutManagement;
this.rolloutListView = new RolloutListView(permissionChecker, rolloutUIState, eventBus, rolloutManagement,
targetManagement, uiNotification, uiProperties, entityFactory, i18n, targetFilterQueryManagement,
rolloutGroupManagement, quotaManagement);
rolloutGroupManagement, quotaManagement, tenantConfigManagement);
this.rolloutGroupsListView = new RolloutGroupsListView(i18n, eventBus, rolloutGroupManagement, rolloutUIState,
permissionChecker);
this.rolloutGroupTargetsListView = new RolloutGroupTargetsListView(eventBus, i18n, rolloutUIState);

View File

@@ -78,9 +78,11 @@ import com.vaadin.data.util.converter.StringToIntegerConverter;
import com.vaadin.data.validator.IntegerRangeValidator;
import com.vaadin.data.validator.LongRangeValidator;
import com.vaadin.data.validator.NullValidator;
import com.vaadin.server.FontAwesome;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.OptionGroup;
import com.vaadin.ui.TabSheet;
@@ -106,6 +108,10 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
private static final String MESSAGE_ENTER_NUMBER = "message.enter.number";
private static final String APPROVAL_BUTTON_LABEL = "button.approve";
private static final String DENY_BUTTON_LABEL = "button.deny";
private final ActionTypeOptionGroupLayout actionTypeOptionGroupLayout;
private final AutoStartOptionGroupLayout autoStartOptionGroupLayout;
@@ -150,6 +156,10 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
private OptionGroup errorThresholdOptionGroup;
private Label approvalLabel;
private HorizontalLayout approvalButtonsLayout;
private CommonDialogWindow window;
private boolean editRolloutEnabled;
@@ -166,6 +176,10 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
private GroupsLegendLayout groupsLegendLayout;
private OptionGroup approveButtonsGroup;
private TextField approvalRemarkField;
private final transient RolloutGroupConditions defaultRolloutGroupConditions;
private final NullValidator nullValidator = new NullValidator(null, false);
@@ -213,6 +227,11 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
if (editRolloutEnabled) {
editRollout();
if (rollout.getStatus().equals(Rollout.RolloutStatus.WAITING_FOR_APPROVAL)) {
rolloutManagement.approveOrDeny(rollout.getId(),
(Rollout.ApprovalDecision) approveButtonsGroup.getValue(), approvalRemarkField.getValue());
eventBus.publish(this, RolloutEvent.UPDATE_ROLLOUT);
}
return;
}
createRollout();
@@ -413,6 +432,12 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
rollout = null;
groupsDefinitionTabs.setVisible(true);
groupsDefinitionTabs.setSelectedTab(0);
approvalLabel.setVisible(false);
approvalButtonsLayout.setVisible(false);
approveButtonsGroup.clear();
approvalRemarkField.clear();
approveButtonsGroup.removeAllValidators();
}
private void addGroupsDefinitionTabs() {
@@ -443,7 +468,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
setSpacing(true);
setSizeUndefined();
setRows(7);
setRows(8);
setColumns(4);
setStyleName("marginTop");
setColumnExpandRatio(3, 1);
@@ -477,6 +502,9 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
addComponent(groupsDefinitionTabs, 0, 6, 3, 6);
addComponent(approvalLabel, 0, 7);
addComponent(approvalButtonsLayout, 1, 7, 3, 7);
rolloutName.focus();
}
@@ -541,6 +569,8 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
groupsLegendLayout = new GroupsLegendLayout(i18n);
approvalLabel = getLabel("label.approval.decision");
approvalButtonsLayout = createApprovalLayout();
}
private void displayValidationStatus(final DefineGroupsLayout.ValidationStatus status) {
@@ -610,6 +640,29 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
return layout;
}
private HorizontalLayout createApprovalLayout() {
approveButtonsGroup = new OptionGroup();
approveButtonsGroup.setId(UIComponentIdProvider.ROLLOUT_APPROVAL_OPTIONGROUP_ID);
approveButtonsGroup.addStyleName(ValoTheme.OPTIONGROUP_SMALL);
approveButtonsGroup.addStyleName(ValoTheme.OPTIONGROUP_HORIZONTAL);
approveButtonsGroup.addStyleName("custom-option-group");
approveButtonsGroup.addItems(Rollout.ApprovalDecision.APPROVED, Rollout.ApprovalDecision.DENIED);
approveButtonsGroup.setItemCaption(Rollout.ApprovalDecision.APPROVED, i18n.getMessage(APPROVAL_BUTTON_LABEL));
approveButtonsGroup.setItemIcon(Rollout.ApprovalDecision.APPROVED, FontAwesome.CHECK);
approveButtonsGroup.setItemCaption(Rollout.ApprovalDecision.DENIED, i18n.getMessage(DENY_BUTTON_LABEL));
approveButtonsGroup.setItemIcon(Rollout.ApprovalDecision.DENIED, FontAwesome.TIMES);
approvalRemarkField = createTextField("label.approval.remark",
UIComponentIdProvider.ROLLOUT_APPROVAL_REMARK_FIELD_ID, Rollout.APPROVAL_REMARK_MAX_SIZE);
approvalRemarkField.setWidth(100.0F, Unit.PERCENTAGE);
HorizontalLayout layout = new HorizontalLayout(approveButtonsGroup, approvalRemarkField);
layout.setWidth(100.0F, Unit.PERCENTAGE);
layout.setExpandRatio(approvalRemarkField, 1.0F);
return layout;
}
private static Label createCountLabel() {
final Label groupSize = new LabelBuilder().visible(false).name("").buildLabel();
groupSize.addStyleName(ValoTheme.LABEL_TINY + " " + "rollout-target-count-message");
@@ -966,6 +1019,13 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
if (rollout.getStatus() != Rollout.RolloutStatus.READY) {
disableRequiredFieldsOnEdit();
}
if (rollout.getStatus() == Rollout.RolloutStatus.WAITING_FOR_APPROVAL) {
approvalButtonsLayout.setVisible(true);
approveButtonsGroup.addValidator(nullValidator);
approvalLabel.setVisible(true);
}
rolloutName.setValue(rollout.getName());
groupsDefinitionTabs.setVisible(false);

View File

@@ -43,6 +43,8 @@ public class ProxyRollout {
private long forcedTime;
private RolloutStatus status;
private TotalTargetCountStatus totalTargetCountStatus;
private String approvalDecidedBy;
private String approvalRemark;
public RolloutRendererData getRolloutRendererData() {
return rolloutRendererData;
@@ -217,4 +219,20 @@ public class ProxyRollout {
public void setTotalTargetCountStatus(final TotalTargetCountStatus totalTargetCountStatus) {
this.totalTargetCountStatus = totalTargetCountStatus;
}
public String getApprovalDecidedBy() {
return approvalDecidedBy;
}
public void setApprovalDecidedBy(final String approvalDecidedBy) {
this.approvalDecidedBy = approvalDecidedBy;
}
public String getApprovalRemark() {
return approvalRemark;
}
public void setApprovalRemark(final String approvalRemark) {
this.approvalRemark = approvalRemark;
}
}

View File

@@ -124,6 +124,8 @@ public class RolloutBeanQuery extends AbstractBeanQuery<ProxyRollout> {
final TotalTargetCountStatus totalTargetCountActionStatus = rollout.getTotalTargetCountStatus();
proxyRollout.setTotalTargetCountStatus(totalTargetCountActionStatus);
proxyRollout.setTotalTargetsCount(String.valueOf(rollout.getTotalTargets()));
proxyRollout.setApprovalDecidedBy(rollout.getApprovalDecidedBy());
proxyRollout.setApprovalRemark(rollout.getApprovalRemark());
return proxyRollout;
}

View File

@@ -8,9 +8,11 @@
*/
package org.eclipse.hawkbit.ui.rollout.rollout;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.*;
import static org.eclipse.hawkbit.ui.rollout.DistributionBarHelper.getTooltip;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Locale;
@@ -24,6 +26,7 @@ import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
@@ -77,6 +80,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
private static final String ROLLOUT_RENDERER_DATA = "rolloutRendererData";
private static final String VIRT_PROP_APPROVE = "approve";
private static final String VIRT_PROP_RUN = "run";
private static final String VIRT_PROP_PAUSE = "pause";
private static final String VIRT_PROP_UPDATE = "update";
@@ -87,6 +91,8 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
private final transient RolloutGroupManagement rolloutGroupManagement;
private final transient TenantConfigurationManagement tenantConfigManagement;
private final AddUpdateRolloutWindowLayout addUpdateRolloutWindow;
private final UINotification uiNotification;
@@ -95,7 +101,8 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
private static final List<RolloutStatus> DELETE_COPY_BUTTON_ENABLED = Arrays.asList(RolloutStatus.CREATING,
RolloutStatus.ERROR_CREATING, RolloutStatus.ERROR_STARTING, RolloutStatus.PAUSED, RolloutStatus.READY,
RolloutStatus.RUNNING, RolloutStatus.STARTING, RolloutStatus.STOPPED, RolloutStatus.FINISHED);
RolloutStatus.RUNNING, RolloutStatus.STARTING, RolloutStatus.STOPPED, RolloutStatus.FINISHED,
RolloutStatus.WAITING_FOR_APPROVAL, RolloutStatus.APPROVAL_DENIED);
private static final List<RolloutStatus> UPDATE_BUTTON_ENABLED = Arrays.asList(RolloutStatus.CREATING,
RolloutStatus.ERROR_CREATING, RolloutStatus.ERROR_STARTING, RolloutStatus.PAUSED, RolloutStatus.READY,
@@ -106,11 +113,14 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
private static final List<RolloutStatus> RUN_BUTTON_ENABLED = Arrays.asList(RolloutStatus.READY,
RolloutStatus.PAUSED);
private static final List<RolloutStatus> APPROVE_BUTTON_ENABLED = Collections.singletonList(RolloutStatus.WAITING_FOR_APPROVAL);
private static final Map<RolloutStatus, StatusFontIcon> statusIconMap = new EnumMap<>(RolloutStatus.class);
private static final List<Object> HIDDEN_COLUMNS = Arrays.asList(SPUILabelDefinitions.VAR_CREATED_DATE,
SPUILabelDefinitions.VAR_CREATED_USER, SPUILabelDefinitions.VAR_MODIFIED_DATE,
SPUILabelDefinitions.VAR_MODIFIED_BY, SPUILabelDefinitions.VAR_DESC);
SPUILabelDefinitions.VAR_MODIFIED_BY, SPUILabelDefinitions.VAR_APPROVAL_DECIDED_BY,
SPUILabelDefinitions.VAR_APPROVAL_REMARK, SPUILabelDefinitions.VAR_DESC);
static {
statusIconMap.put(RolloutStatus.FINISHED,
@@ -118,6 +128,10 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
statusIconMap.put(RolloutStatus.PAUSED,
new StatusFontIcon(FontAwesome.PAUSE, SPUIStyleDefinitions.STATUS_ICON_BLUE));
statusIconMap.put(RolloutStatus.RUNNING, new StatusFontIcon(null, SPUIStyleDefinitions.STATUS_SPINNER_YELLOW));
statusIconMap.put(RolloutStatus.WAITING_FOR_APPROVAL,
new StatusFontIcon(FontAwesome.HOURGLASS_HALF, SPUIStyleDefinitions.STATUS_ICON_ORANGE));
statusIconMap.put(RolloutStatus.APPROVAL_DENIED,
new StatusFontIcon(FontAwesome.TIMES_CIRCLE, SPUIStyleDefinitions.STATUS_ICON_RED));
statusIconMap.put(RolloutStatus.READY,
new StatusFontIcon(FontAwesome.DOT_CIRCLE_O, SPUIStyleDefinitions.STATUS_ICON_LIGHT_BLUE));
statusIconMap.put(RolloutStatus.STOPPED,
@@ -132,14 +146,17 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
}
RolloutListGrid(final VaadinMessageSource i18n, final UIEventBus eventBus,
final RolloutManagement rolloutManagement, final UINotification uiNotification,
final RolloutUIState rolloutUIState, final SpPermissionChecker permissionChecker,
final TargetManagement targetManagement, final EntityFactory entityFactory, final UiProperties uiProperties,
final TargetFilterQueryManagement targetFilterQueryManagement,
final RolloutGroupManagement rolloutGroupManagement, final QuotaManagement quotaManagement) {
final RolloutManagement rolloutManagement, final UINotification uiNotification,
final RolloutUIState rolloutUIState, final SpPermissionChecker permissionChecker,
final TargetManagement targetManagement, final EntityFactory entityFactory,
final UiProperties uiProperties,
final TargetFilterQueryManagement targetFilterQueryManagement,
final RolloutGroupManagement rolloutGroupManagement, final QuotaManagement quotaManagement,
final TenantConfigurationManagement tenantConfigManagement) {
super(i18n, eventBus, permissionChecker);
this.rolloutManagement = rolloutManagement;
this.rolloutGroupManagement = rolloutGroupManagement;
this.tenantConfigManagement = tenantConfigManagement;
this.addUpdateRolloutWindow = new AddUpdateRolloutWindowLayout(rolloutManagement, targetManagement,
uiNotification, uiProperties, entityFactory, i18n, eventBus, targetFilterQueryManagement,
rolloutGroupManagement, quotaManagement);
@@ -250,7 +267,10 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
false);
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_CREATED_DATE, String.class, null, false,
false);
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_APPROVAL_DECIDED_BY, String.class, null, false,
false);
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_APPROVAL_REMARK, String.class, null, false,
false);
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_MODIFIED_DATE, String.class, null, false,
false);
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_CREATED_USER, String.class, null, false,
@@ -289,6 +309,9 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
getColumn(VIRT_PROP_RUN).setMinimumWidth(25);
getColumn(VIRT_PROP_RUN).setMaximumWidth(25);
getColumn(VIRT_PROP_APPROVE).setMinimumWidth(25);
getColumn(VIRT_PROP_APPROVE).setMaximumWidth(25);
getColumn(VIRT_PROP_PAUSE).setMinimumWidth(25);
getColumn(VIRT_PROP_PAUSE).setMaximumWidth(25);
@@ -315,12 +338,15 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
getColumn(SPUILabelDefinitions.VAR_CREATED_USER).setHeaderCaption(i18n.getMessage("header.createdBy"));
getColumn(SPUILabelDefinitions.VAR_MODIFIED_DATE).setHeaderCaption(i18n.getMessage("header.modifiedDate"));
getColumn(SPUILabelDefinitions.VAR_MODIFIED_BY).setHeaderCaption(i18n.getMessage("header.modifiedBy"));
getColumn(SPUILabelDefinitions.VAR_APPROVAL_REMARK).setHeaderCaption(i18n.getMessage("header.approvalRemark"));
getColumn(SPUILabelDefinitions.VAR_APPROVAL_DECIDED_BY).setHeaderCaption(i18n.getMessage("header.approvalDecidedBy"));
getColumn(SPUILabelDefinitions.VAR_DESC).setHeaderCaption(i18n.getMessage("header.description"));
getColumn(SPUILabelDefinitions.VAR_TOTAL_TARGETS_COUNT_STATUS)
.setHeaderCaption(i18n.getMessage("header.detail.status"));
getColumn(SPUILabelDefinitions.VAR_STATUS).setHeaderCaption(i18n.getMessage("header.status"));
getColumn(VIRT_PROP_RUN).setHeaderCaption(i18n.getMessage("header.action.run"));
getColumn(VIRT_PROP_APPROVE).setHeaderCaption(i18n.getMessage("header.action.approve"));
getColumn(VIRT_PROP_PAUSE).setHeaderCaption(i18n.getMessage("header.action.pause"));
getColumn(VIRT_PROP_UPDATE).setHeaderCaption(i18n.getMessage("header.action.update"));
getColumn(VIRT_PROP_COPY).setHeaderCaption(i18n.getMessage("header.action.copy"));
@@ -331,7 +357,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
private HeaderCell joinColumns() {
return getDefaultHeaderRow().join(VIRT_PROP_RUN, VIRT_PROP_PAUSE, VIRT_PROP_UPDATE, VIRT_PROP_COPY,
return getDefaultHeaderRow().join(VIRT_PROP_RUN, VIRT_PROP_APPROVE, VIRT_PROP_PAUSE, VIRT_PROP_UPDATE, VIRT_PROP_COPY,
VIRT_PROP_DELETE);
}
@@ -346,10 +372,11 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
final List<String> columnsToShowInOrder = Arrays.asList(ROLLOUT_RENDERER_DATA,
SPUILabelDefinitions.VAR_DIST_NAME_VERSION, SPUILabelDefinitions.VAR_STATUS,
SPUILabelDefinitions.VAR_TOTAL_TARGETS_COUNT_STATUS, SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS,
SPUILabelDefinitions.VAR_TOTAL_TARGETS, VIRT_PROP_RUN, VIRT_PROP_PAUSE, VIRT_PROP_UPDATE,
SPUILabelDefinitions.VAR_TOTAL_TARGETS, VIRT_PROP_APPROVE, VIRT_PROP_RUN, VIRT_PROP_PAUSE, VIRT_PROP_UPDATE,
VIRT_PROP_COPY, VIRT_PROP_DELETE, SPUILabelDefinitions.VAR_CREATED_DATE,
SPUILabelDefinitions.VAR_CREATED_USER, SPUILabelDefinitions.VAR_MODIFIED_DATE,
SPUILabelDefinitions.VAR_MODIFIED_BY, SPUILabelDefinitions.VAR_DESC);
SPUILabelDefinitions.VAR_MODIFIED_BY, SPUILabelDefinitions.VAR_APPROVAL_DECIDED_BY,
SPUILabelDefinitions.VAR_APPROVAL_REMARK, SPUILabelDefinitions.VAR_DESC);
setColumns(columnsToShowInOrder.toArray());
}
@@ -361,6 +388,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
}
getColumn(VIRT_PROP_RUN).setHidable(false);
getColumn(VIRT_PROP_APPROVE).setHidable(false);
getColumn(VIRT_PROP_PAUSE).setHidable(false);
getColumn(VIRT_PROP_DELETE).setHidable(false);
getColumn(VIRT_PROP_UPDATE).setHidable(false);
@@ -388,6 +416,9 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
getColumn(VIRT_PROP_RUN).setRenderer(
new GridButtonRenderer(clickEvent -> startOrResumeRollout((Long) clickEvent.getItemId())),
new RolloutGridButtonConverter(this::createRunButtonMetadata));
getColumn(VIRT_PROP_APPROVE).setRenderer(
new GridButtonRenderer(clickEvent -> approveRollout((Long) clickEvent.getItemId())),
new RolloutGridButtonConverter(this::createApprovalButtonMetadata));
getColumn(VIRT_PROP_PAUSE).setRenderer(
new GridButtonRenderer(clickEvent -> pauseRollout((Long) clickEvent.getItemId())),
new RolloutGridButtonConverter(this::createPauseButtonMetadata));
@@ -440,6 +471,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
final GeneratedPropertyContainer decoratedContainer = getDecoratedContainer();
decoratedContainer.addGeneratedProperty(VIRT_PROP_RUN, new GenericPropertyValueGenerator());
decoratedContainer.addGeneratedProperty(VIRT_PROP_APPROVE, new GenericPropertyValueGenerator());
decoratedContainer.addGeneratedProperty(VIRT_PROP_PAUSE, new GenericPropertyValueGenerator());
decoratedContainer.addGeneratedProperty(VIRT_PROP_UPDATE, new GenericPropertyValueGenerator());
decoratedContainer.addGeneratedProperty(VIRT_PROP_COPY, new GenericPropertyValueGenerator());
@@ -520,6 +552,13 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
}
}
private void approveRollout(final Long rolloutId) {
final CommonDialogWindow addTargetWindow = addUpdateRolloutWindow.getWindow(rolloutId, false);
addTargetWindow.setCaption(i18n.getMessage("caption.approve.rollout"));
UI.getCurrent().addWindow(addTargetWindow);
addTargetWindow.setVisible(Boolean.TRUE);
}
private void updateRollout(final Long rolloutId) {
final CommonDialogWindow addTargetWindow = addUpdateRolloutWindow.getWindow(rolloutId, false);
addTargetWindow.setCaption(i18n.getMessage("caption.update.rollout"));
@@ -578,7 +617,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
String description = null;
if (SPUILabelDefinitions.VAR_STATUS.equals(cell.getPropertyId())) {
description = cell.getProperty().getValue().toString().toLowerCase();
description = cell.getProperty().getValue().toString().toLowerCase().replace("_", " ");
} else if (SPUILabelDefinitions.ACTION.equals(cell.getPropertyId())) {
description = SPUILabelDefinitions.ACTION.toLowerCase();
} else if (ROLLOUT_RENDERER_DATA.equals(cell.getPropertyId())) {
@@ -595,6 +634,12 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
return !expectedRolloutStatus.contains(currentRolloutStatus);
}
private StatusFontIcon createApprovalButtonMetadata(final RolloutStatus rolloutStatus) {
final boolean isDisabled = hasToBeDisabled(rolloutStatus, APPROVE_BUTTON_ENABLED);
return new StatusFontIcon(FontAwesome.GAVEL, null, i18n.getMessage("tooltip.rollout.approve"),
UIComponentIdProvider.ROLLOUT_APPROVAL_BUTTON_ID, isDisabled);
}
private StatusFontIcon createRunButtonMetadata(final RolloutStatus rolloutStatus) {
final boolean isDisabled = hasToBeDisabled(rolloutStatus, RUN_BUTTON_ENABLED);
return new StatusFontIcon(FontAwesome.PLAY, null, i18n.getMessage("tooltip.rollout.run"),
@@ -744,6 +789,11 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
if (!permissionChecker.hasRolloutCreatePermission()) {
modifiableColumnsList.remove(VIRT_PROP_COPY);
}
if (!permissionChecker.hasRolloutApprovalPermission() ||
!tenantConfigManagement.getConfigurationValue(
TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class).getValue()) {
modifiableColumnsList.remove(VIRT_PROP_APPROVE);
}
if (!permissionChecker.hasRolloutDeletePermission()) {
modifiableColumnsList.remove(VIRT_PROP_DELETE);
}

View File

@@ -14,6 +14,7 @@ import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.ui.SpPermissionChecker;
import org.eclipse.hawkbit.ui.UiProperties;
import org.eclipse.hawkbit.ui.common.grid.AbstractGrid;
@@ -38,6 +39,7 @@ public class RolloutListView extends AbstractGridComponentLayout {
private final transient EntityFactory entityFactory;
private final transient TargetFilterQueryManagement targetFilterQueryManagement;
private final transient QuotaManagement quotaManagement;
private final transient TenantConfigurationManagement tenantConfigManagement;
private final SpPermissionChecker permissionChecker;
private final RolloutUIState rolloutUIState;
@@ -45,11 +47,13 @@ public class RolloutListView extends AbstractGridComponentLayout {
private final UiProperties uiProperties;
public RolloutListView(final SpPermissionChecker permissionChecker, final RolloutUIState rolloutUIState,
final UIEventBus eventBus, final RolloutManagement rolloutManagement,
final TargetManagement targetManagement, final UINotification uiNotification,
final UiProperties uiProperties, final EntityFactory entityFactory, final VaadinMessageSource i18n,
final TargetFilterQueryManagement targetFilterQueryManagement,
final RolloutGroupManagement rolloutGroupManagement, final QuotaManagement quotaManagement) {
final UIEventBus eventBus, final RolloutManagement rolloutManagement,
final TargetManagement targetManagement, final UINotification uiNotification,
final UiProperties uiProperties, final EntityFactory entityFactory,
final VaadinMessageSource i18n,
final TargetFilterQueryManagement targetFilterQueryManagement,
final RolloutGroupManagement rolloutGroupManagement, final QuotaManagement quotaManagement,
final TenantConfigurationManagement tenantConfigManagement) {
super(i18n, eventBus);
this.permissionChecker = permissionChecker;
this.rolloutUIState = rolloutUIState;
@@ -61,6 +65,7 @@ public class RolloutListView extends AbstractGridComponentLayout {
this.uiProperties = uiProperties;
this.entityFactory = entityFactory;
this.targetFilterQueryManagement = targetFilterQueryManagement;
this.tenantConfigManagement = tenantConfigManagement;
init();
}
@@ -76,7 +81,7 @@ public class RolloutListView extends AbstractGridComponentLayout {
public AbstractGrid<LazyQueryContainer> createGrid() {
return new RolloutListGrid(i18n, eventBus, rolloutManagement, uiNotification, rolloutUIState, permissionChecker,
targetManagement, entityFactory, uiProperties, targetFilterQueryManagement, rolloutGroupManagement,
quotaManagement);
quotaManagement, tenantConfigManagement);
}
}

View File

@@ -0,0 +1,110 @@
/**
* 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.ui.tenantconfiguration;
import com.vaadin.data.Property;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Link;
import com.vaadin.ui.Panel;
import com.vaadin.ui.VerticalLayout;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.ui.UiProperties;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.tenantconfiguration.rollout.ApprovalConfigurationItem;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
/**
* Provides configuration of the RolloutManagement including enabling/disabling
* of the approval workflow.
*/
public class RolloutConfigurationView extends BaseConfigurationView
implements Property.ValueChangeListener, ConfigurationItem.ConfigurationItemChangeListener {
private static final long serialVersionUID = 1L;
private final ApprovalConfigurationItem approvalConfigurationItem;
private final VaadinMessageSource i18n;
private final UiProperties uiProperties;
private CheckBox approvalCheckbox;
RolloutConfigurationView(final VaadinMessageSource i18n,
final TenantConfigurationManagement tenantConfigurationManagement, final UiProperties uiProperties) {
this.i18n = i18n;
this.uiProperties = uiProperties;
this.approvalConfigurationItem = new ApprovalConfigurationItem(tenantConfigurationManagement, i18n);
this.init();
}
private void init() {
final Panel rootPanel = new Panel();
rootPanel.setSizeFull();
rootPanel.addStyleName("config-panel");
final VerticalLayout vLayout = new VerticalLayout();
vLayout.setMargin(true);
vLayout.setSizeFull();
final Label headerDisSetType = new Label(i18n.getMessage("configuration.rollout.title"));
headerDisSetType.addStyleName("config-panel-header");
vLayout.addComponent(headerDisSetType);
final Link linkToApprovalHelp = SPUIComponentProvider
.getHelpLink(uiProperties.getLinks().getDocumentation().getRollout());
vLayout.addComponent(linkToApprovalHelp);
final GridLayout gridLayout = new GridLayout(2, 1);
gridLayout.setSpacing(true);
gridLayout.setImmediate(true);
gridLayout.setColumnExpandRatio(1, 1.0F);
approvalCheckbox = SPUIComponentProvider.getCheckBox("", "", null, false, "");
approvalCheckbox.setId(UIComponentIdProvider.ROLLOUT_APPROVAL_ENABLED_CHECKBOX);
approvalCheckbox.setValue(approvalConfigurationItem.isConfigEnabled());
approvalCheckbox.addValueChangeListener(this);
approvalConfigurationItem.addChangeListener(this);
gridLayout.addComponent(approvalCheckbox, 0, 0);
gridLayout.addComponent(approvalConfigurationItem, 1, 0);
vLayout.addComponent(gridLayout);
rootPanel.setContent(vLayout);
setCompositionRoot(rootPanel);
}
@Override
public void save() {
this.approvalConfigurationItem.save();
}
@Override
public void undo() {
this.approvalConfigurationItem.undo();
}
@Override
public void valueChange(Property.ValueChangeEvent event) {
if (approvalCheckbox.equals(event.getProperty())) {
if (approvalCheckbox.getValue()) {
approvalConfigurationItem.configEnable();
} else {
approvalConfigurationItem.configDisable();
}
notifyConfigurationChanged();
}
}
@Override
public void configurationHasChanged() {
notifyConfigurationChanged();
}
}

View File

@@ -62,6 +62,8 @@ public class TenantConfigurationDashboardView extends CustomComponent implements
private final PollingConfigurationView pollingConfigurationView;
private final RolloutConfigurationView rolloutConfigurationView;
private final VaadinMessageSource i18n;
private final UiProperties uiProperties;
@@ -90,6 +92,7 @@ public class TenantConfigurationDashboardView extends CustomComponent implements
this.pollingConfigurationView = new PollingConfigurationView(i18n, controllerPollProperties,
tenantConfigurationManagement);
this.repositoryConfigurationView = new RepositoryConfigurationView(i18n, tenantConfigurationManagement);
this.rolloutConfigurationView = new RolloutConfigurationView(i18n, tenantConfigurationManagement, uiProperties);
this.i18n = i18n;
this.uiProperties = uiProperties;
@@ -105,7 +108,7 @@ public class TenantConfigurationDashboardView extends CustomComponent implements
configurationViews.add(defaultDistributionSetTypeLayout);
}
configurationViews.add(repositoryConfigurationView);
configurationViews.add(rolloutConfigurationView);
configurationViews.add(authenticationConfigurationView);
configurationViews.add(pollingConfigurationView);
if (customConfigurationViews != null) {

View File

@@ -0,0 +1,71 @@
/**
* 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.ui.tenantconfiguration.rollout;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.hawkbit.ui.tenantconfiguration.generic.AbstractBooleanTenantConfigurationItem;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
/**
* This class represents the UI item for the target security token section in
* the authentication configuration view.
*/
public class ApprovalConfigurationItem extends AbstractBooleanTenantConfigurationItem {
private static final long serialVersionUID = 1L;
private boolean configurationEnabled;
private boolean configurationEnabledChange;
/**
* Constructor for tenant specific approval mode setting.
*
* @param tenantConfigurationManagement used to enable/disable the approval mode per tenant
* @param i18n used to translate labels
*/
public ApprovalConfigurationItem(final TenantConfigurationManagement tenantConfigurationManagement,
final VaadinMessageSource i18n) {
super(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, tenantConfigurationManagement, i18n);
super.init("configuration.rollout.approval.label");
configurationEnabled = isConfigEnabled();
}
@Override
public void configEnable() {
if (!configurationEnabled) {
configurationEnabledChange = true;
}
configurationEnabled = true;
}
@Override
public void configDisable() {
if (configurationEnabled) {
configurationEnabledChange = true;
}
configurationEnabled = false;
}
@Override
public void save() {
if (!configurationEnabledChange) {
return;
}
getTenantConfigurationManagement().addOrUpdateConfiguration(getConfigurationKey(), configurationEnabled);
}
@Override
public void undo() {
configurationEnabledChange = false;
configurationEnabled = getTenantConfigurationManagement()
.getConfigurationValue(getConfigurationKey(), Boolean.class).getValue();
}
}

View File

@@ -133,6 +133,16 @@ public final class SPUILabelDefinitions {
* Last modified date.
*/
public static final String VAR_MODIFIED_DATE = "modifiedDate";
/**
* Approve/Deny remark.
*/
public static final String VAR_APPROVAL_REMARK = "approvalRemark";
/**
* Approval decider.
*/
public static final String VAR_APPROVAL_DECIDED_BY = "approvalDecidedBy";
/**
* Poll Status.
*/

View File

@@ -866,6 +866,16 @@ public final class UIComponentIdProvider {
*/
public static final String ROLLOUT_RUN_BUTTON_ID = ROLLOUT_ACTION_ID + ".6";
/**
* Rollout approval button id.
*/
public static final String ROLLOUT_APPROVAL_BUTTON_ID = ROLLOUT_ACTION_ID + ".11";
/**
* Rollout approve/deny option button group id.
*/
public static final String ROLLOUT_APPROVAL_OPTIONGROUP_ID = ROLLOUT_ACTION_ID + ".12";
/**
* Rollout pause button id.
*/
@@ -1082,6 +1092,17 @@ public final class UIComponentIdProvider {
*/
public static final String REPOSITORY_ACTIONS_AUTOCLOSE_CHECKBOX = "repositoryactionsautoclosecheckbox";
/**
* Configuration checkbox for
* {@link TenantConfigurationKey#ROLLOUT_APPROVAL_ENABLED}
*/
public static final String ROLLOUT_APPROVAL_ENABLED_CHECKBOX = "rollout.approve.enabled.checkbox";
/**
* Id of the rollout approval remark field
*/
public static final String ROLLOUT_APPROVAL_REMARK_FIELD_ID = "rollout.approve.remark";
/**
* /* Private Constructor.
*/

View File

@@ -38,6 +38,9 @@ button.cancel = Cancel
button.upload.file = Upload File
button.no.auto.assignment = none
button.auto.assignment.desc = Select auto assign distribution set
button.approve = Approve
button.deny = Deny
button.apply = Apply
bulk.targets.upload = Please upload csv file.
bulkupload.ds.name = DS Name
button.discard=Discard
@@ -72,6 +75,7 @@ header.swmodules=SwModules
header.migrations.step=IsRequiredMigrationStep
header.action=Actions
header.action.run=Run
header.action.approve=Approve
header.action.pause=Pause
header.action.update=Edit
header.action.copy=Copy
@@ -219,6 +223,8 @@ label.unsupported.browser.ie=Sorry! current browser is not supported. Please use
label.auto.assign.description=When an auto assign distribution set is selected, it will be automatically assigned to all targets that match the target filter.
label.auto.assign.enable=Enable auto assignment
label.scheduled=Scheduled
label.approval.decision = Approval decision
label.approval.remark = Remark (optional)
# Checkbox label prefix with - checkbox
checkbox.dist.migration.required = Required Migration Step :
@@ -254,6 +260,7 @@ tooltip.metadata.icon = Manage Metadata
tooltip.next.maintenancewindow = next on {0}
#rollout action
tooltip.rollout.run = Run
tooltip.rollout.approve = Approve
tooltip.rollout.pause = Pause
tooltip.rollout.update = Update..
tooltip.rollout.copy = Copy..
@@ -483,6 +490,8 @@ configuration.polling.title=Polling Configuration
configuration.polling.time=Polling Time
configuration.polling.overduetime=Polling Overdue Time
configuration.polling.custom.value=use a custom value
configuration.rollout.title=Rollout Configuration
configuration.rollout.approval.label=Approve rollout before it can be started
#Calendar
calendar.year=year
@@ -511,13 +520,15 @@ header.assigned.ds = Assigned DS
header.installed.ds = Installed DS
header.target.status = Status
header.target.tags = Tags
header.total.targets = Targets
header.total.targets = Targets
header.key = Key
header.value = Value
metadata.targetvisible = Visible for targets
header.auto.assignment.ds = Auto assignment
header.target.filter.name = Target filter name
header.target.filter.query = Target filter query
header.approvalDecidedBy = Decided By
header.approvalRemark = Approval Remark
distribution.details.header = Distribution set
target.details.header = Target
@@ -556,6 +567,7 @@ caption.configure.rollout = Configure Rollout
caption.configure.rollout.groups = Configure Deployment Groups
caption.update.rollout = Update Rollout
caption.create.rollout = Create new Rollout
caption.approve.rollout = Approve Rollout
prompt.target.filter = Custom Target Filter
message.rollout.nonzero.group.number = Number of groups must be greater than zero
message.rollout.max.group.number = Number of groups must not be greater than 500