Create Rollout UI with Groups definition (#369)
* Rollout creation UI with groups definition Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com> * Improved code quality Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com> * Added a legend to the Rollouts Creation UI's PieChart Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com> * Updated color palette for pie chart. Using RolloutGroupCreate for validation. Displaying pie chart for group editing. Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com> * Optimized ComboBoxBuilder Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com> * Removed not necessary RPC interfaces. Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com> * Fix Rollout verification count query. Improved UI style. Added dependency management for d3. Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com> * Auto start and scheduled start of Rollouts Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com> * Optimized RSQL Utility to avoid multiple joins on the same table. Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com> * Fixed RSQL Utility to avoid multiple joins on the same table to only work with OR nodes. Optimized validation queries for rollout group creation. Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com> * Asynchronous handling of groups validation in the create rollouts UI. Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com> * Added loading indicator to create rollout UI. Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com> * Avoid parallel validation for a user session. Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com> * Fix issues after merge of master into branch. Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com> * Improve code quality Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com> * Refreshing the total target count after validation on the rollouts creation UI. Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com> * Remove unused RolloutUpdate method. Optimization of the RolloutHelper. Comments for RolloutGroupsValidation and RSQLUtility with explanation. Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com>
This commit is contained in:
committed by
Kai Zimmermann
parent
430bf632cf
commit
833b0795e3
@@ -33,6 +33,7 @@
|
||||
|
||||
<inherits name="org.eclipse.hawkbit.ui.customrenderers.CustomRendererWidgetSet" />
|
||||
<inherits name="org.eclipse.hawkbit.ui.filtermanagement.TextFieldSuggestionBox" />
|
||||
<inherits name="org.eclipse.hawkbit.ui.rollout.groupschart.GroupsPieChart" />
|
||||
<inherits name="org.eclipse.hawkbit.ui.dd.CustomAcceptCriteria" />
|
||||
|
||||
|
||||
|
||||
@@ -247,6 +247,13 @@ public class CommonDialogWindow extends Window {
|
||||
saveButton.setEnabled(isSaveButtonEnabledAfterValueChange(null, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the original values in case no value changed check is wished
|
||||
*/
|
||||
public final void clearOriginalValues() {
|
||||
orginalValues.clear();
|
||||
}
|
||||
|
||||
protected void addCloseListenerForSaveButton() {
|
||||
saveButton.addClickListener(closeClickListener);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2015 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.common.builder;
|
||||
|
||||
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
|
||||
import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions;
|
||||
|
||||
import com.vaadin.data.Property;
|
||||
import com.vaadin.ui.ComboBox;
|
||||
import com.vaadin.ui.themes.ValoTheme;
|
||||
|
||||
/**
|
||||
* Builds ComboBox Elements with a commonly used properties.
|
||||
*/
|
||||
public class ComboBoxBuilder {
|
||||
|
||||
private Property.ValueChangeListener valueChangeListener;
|
||||
|
||||
private String id;
|
||||
|
||||
private String prompt;
|
||||
|
||||
public ComboBoxBuilder setValueChangeListener(final Property.ValueChangeListener valueChangeListener) {
|
||||
this.valueChangeListener = valueChangeListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ComboBoxBuilder setId(final String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ComboBoxBuilder setPrompt(String prompt) {
|
||||
this.prompt = prompt;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a new ComboBox
|
||||
*/
|
||||
public ComboBox buildCombBox() {
|
||||
final ComboBox targetFilter = SPUIComponentProvider.getComboBox(null, "", null, ValoTheme.COMBOBOX_SMALL, false,
|
||||
"", prompt);
|
||||
targetFilter.setImmediate(true);
|
||||
targetFilter.setPageLength(7);
|
||||
targetFilter.setItemCaptionPropertyId(SPUILabelDefinitions.VAR_NAME);
|
||||
targetFilter.setSizeUndefined();
|
||||
if (id != null) {
|
||||
targetFilter.setId(id);
|
||||
}
|
||||
if (valueChangeListener != null) {
|
||||
targetFilter.addValueChangeListener(valueChangeListener);
|
||||
}
|
||||
return targetFilter;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import javax.annotation.PreDestroy;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
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.ui.HawkbitUI;
|
||||
import org.eclipse.hawkbit.ui.SpPermissionChecker;
|
||||
@@ -65,10 +66,10 @@ public class RolloutView extends VerticalLayout implements View {
|
||||
final UIEventBus eventBus, final RolloutManagement rolloutManagement,
|
||||
final RolloutGroupManagement rolloutGroupManagement, final TargetManagement targetManagement,
|
||||
final UINotification uiNotification, final UiProperties uiProperties, final EntityFactory entityFactory,
|
||||
final I18N i18n) {
|
||||
final I18N i18n, final TargetFilterQueryManagement targetFilterQueryManagement) {
|
||||
this.permChecker = permissionChecker;
|
||||
this.rolloutListView = new RolloutListView(permissionChecker, rolloutUIState, eventBus, rolloutManagement,
|
||||
targetManagement, uiNotification, uiProperties, entityFactory, i18n);
|
||||
targetManagement, uiNotification, uiProperties, entityFactory, i18n, targetFilterQueryManagement);
|
||||
this.rolloutGroupsListView = new RolloutGroupsListView(i18n, eventBus, rolloutGroupManagement, rolloutUIState,
|
||||
permissionChecker);
|
||||
this.rolloutGroupTargetsListView = new RolloutGroupTargetsListView(eventBus, i18n, rolloutUIState);
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Copyright (c) 2015 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
|
||||
|
||||
-->
|
||||
<module>
|
||||
<inherits name="com.vaadin.DefaultWidgetSet"/>
|
||||
|
||||
<inherits name="org.eclipse.hawkbit.ui.AppWidgetSet"/>
|
||||
|
||||
<inherits name="org.vaadin.peter.contextmenu.ContextmenuWidgetset"/>
|
||||
|
||||
<inherits name="org.vaadin.tokenfield.TokenfieldWidgetset"/>
|
||||
|
||||
<inherits name="org.eclipse.hawkbit.ui.customrenderers.CustomRendererWidgetSet"/>
|
||||
|
||||
<inherits name="org.vaadin.hene.flexibleoptiongroup.widgetset.FlexibleOptionGroupWidgetset"/>
|
||||
|
||||
<inherits name="org.vaadin.alump.distributionbar.gwt.DistributionBarWidgetset"/>
|
||||
|
||||
<inherits name="com.github.gwtd3.D3"/>
|
||||
|
||||
</module>
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2015 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.rollout.groupschart;
|
||||
|
||||
import org.eclipse.hawkbit.ui.rollout.groupschart.client.GroupsPieChartState;
|
||||
import com.vaadin.ui.AbstractComponent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Draws a pie charts for the provided groups.
|
||||
*/
|
||||
public class GroupsPieChart extends AbstractComponent {
|
||||
|
||||
private static final long serialVersionUID = 1311542227339430098L;
|
||||
|
||||
/**
|
||||
* Updates the state of the chart
|
||||
*
|
||||
* @param groupTargetCounts
|
||||
* list of target counts
|
||||
* @param totalTargetsCount
|
||||
* total count of targets that are represented by the pie
|
||||
*/
|
||||
public void setChartState(final List<Long> groupTargetCounts, final Long totalTargetsCount) {
|
||||
getState().setGroupTargetCounts(groupTargetCounts);
|
||||
getState().setTotalTargetCount(totalTargetsCount);
|
||||
markAsDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GroupsPieChartState getState() {
|
||||
return (GroupsPieChartState) super.getState();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2015 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.rollout.groupschart.client;
|
||||
|
||||
import com.vaadin.client.communication.StateChangeEvent;
|
||||
import org.eclipse.hawkbit.ui.rollout.groupschart.GroupsPieChart;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.vaadin.client.ui.AbstractComponentConnector;
|
||||
import com.vaadin.shared.ui.Connect;
|
||||
|
||||
/**
|
||||
* Connector between client side GroupsPieChartWidget and service side.
|
||||
*/
|
||||
@Connect(GroupsPieChart.class)
|
||||
public class GroupsPieChartConnector extends AbstractComponentConnector {
|
||||
|
||||
private static final long serialVersionUID = -2907528194018611155L;
|
||||
|
||||
@Override
|
||||
protected Widget createWidget() {
|
||||
return GWT.create(GroupsPieChartWidget.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupsPieChartWidget getWidget() {
|
||||
return (GroupsPieChartWidget) super.getWidget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupsPieChartState getState() {
|
||||
return (GroupsPieChartState) super.getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChanged(StateChangeEvent stateChangeEvent) {
|
||||
super.onStateChanged(stateChangeEvent);
|
||||
getWidget().update(getState().getGroupTargetCounts(), getState().getTotalTargetCount());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2015 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.rollout.groupschart.client;
|
||||
|
||||
import com.vaadin.shared.AbstractComponentState;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* State to transfer for the groups pie chart between server and client.
|
||||
*/
|
||||
public class GroupsPieChartState extends AbstractComponentState {
|
||||
|
||||
private static final long serialVersionUID = 7344220498082627571L;
|
||||
|
||||
private transient List<Long> groupTargetCounts;
|
||||
|
||||
private Long totalTargetCount;
|
||||
|
||||
public List<Long> getGroupTargetCounts() {
|
||||
return groupTargetCounts;
|
||||
}
|
||||
|
||||
public void setGroupTargetCounts(List<Long> groupTargetCounts) {
|
||||
this.groupTargetCounts = groupTargetCounts;
|
||||
}
|
||||
|
||||
public Long getTotalTargetCount() {
|
||||
return totalTargetCount;
|
||||
}
|
||||
|
||||
public void setTotalTargetCount(Long totalTargetCount) {
|
||||
this.totalTargetCount = totalTargetCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
* Copyright (c) 2015 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.rollout.groupschart.client;
|
||||
|
||||
import com.github.gwtd3.api.D3;
|
||||
import com.github.gwtd3.api.arrays.Array;
|
||||
import com.github.gwtd3.api.core.Selection;
|
||||
import com.github.gwtd3.api.core.UpdateSelection;
|
||||
import com.github.gwtd3.api.core.Value;
|
||||
import com.github.gwtd3.api.functions.DatumFunction;
|
||||
import com.github.gwtd3.api.svg.Arc;
|
||||
import com.google.gwt.dom.client.BrowserEvents;
|
||||
import com.google.gwt.dom.client.Element;
|
||||
import com.google.gwt.dom.client.Style;
|
||||
import com.google.gwt.user.client.ui.DockLayoutPanel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Draws a pie chart using D3. The slices are based on the list of Longs and on
|
||||
* the total target count. The total target count represents 100% of the pie. If
|
||||
* the sum in the list of Longs is less than the total target count a slice for
|
||||
* unassigned targets will be displayed.
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("squid:TrailingCommentCheck")
|
||||
public class GroupsPieChartWidget extends DockLayoutPanel {
|
||||
|
||||
private static final String ATTR_VISIBILITY = "visibility";
|
||||
private static final String ATTR_TRANSFORM = "transform";
|
||||
|
||||
private List<Long> groupTargetCounts;
|
||||
private Long totalTargetCount;
|
||||
private long unassignedTargets;
|
||||
|
||||
private Selection svg;
|
||||
private Selection pieGroup;
|
||||
private Selection infoText;
|
||||
|
||||
private Arc arc;
|
||||
|
||||
/**
|
||||
* Initializes the pie chart
|
||||
*/
|
||||
public GroupsPieChartWidget() {
|
||||
super(Style.Unit.PX);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
|
||||
initChart();
|
||||
|
||||
draw();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the pie chart with new data
|
||||
*
|
||||
* @param groupTargetCounts
|
||||
* list of target counts
|
||||
* @param totalTargetCount
|
||||
* total count of targets that are represented by the pie
|
||||
*/
|
||||
public void update(final List<Long> groupTargetCounts, final Long totalTargetCount) {
|
||||
this.groupTargetCounts = groupTargetCounts;
|
||||
this.totalTargetCount = totalTargetCount;
|
||||
|
||||
if (groupTargetCounts != null) {
|
||||
long sum = 0;
|
||||
for (Long targetCount : groupTargetCounts) {
|
||||
sum += targetCount;
|
||||
}
|
||||
unassignedTargets = totalTargetCount - sum;
|
||||
}
|
||||
|
||||
draw();
|
||||
|
||||
}
|
||||
|
||||
private static PieArc getPie(Long count, Long total, double startAngle) {
|
||||
final Double percentage = count.doubleValue() / total.doubleValue();
|
||||
return new PieArc(startAngle, startAngle + percentage * 2 * Math.PI);
|
||||
}
|
||||
|
||||
private void draw() {
|
||||
if (svg == null || groupTargetCounts == null || totalTargetCount == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Array<Arc> dataArray = Array.create();
|
||||
|
||||
PieArc pie = getPie(unassignedTargets, totalTargetCount, 0);
|
||||
dataArray.push(pie.getArc());
|
||||
|
||||
double lastAngle = pie.getEndAngle();
|
||||
for (int i = 0; i < groupTargetCounts.size(); i++) {
|
||||
final PieArc arcEntry = getPie(groupTargetCounts.get(i), totalTargetCount, lastAngle);
|
||||
dataArray.push(arcEntry.getArc());
|
||||
lastAngle = arcEntry.getEndAngle();
|
||||
}
|
||||
|
||||
UpdateSelection pies = pieGroup.selectAll(".pie").data(dataArray);
|
||||
pies.enter().append("path").classed("pie", true).on(BrowserEvents.MOUSEOVER, new DatumFunction<Void>() {
|
||||
@Override
|
||||
public Void apply(Element context, Value d, int index) {
|
||||
Array<Double> point = arc.centroid(d.as(Arc.class), index);
|
||||
double x = point.getNumber(0);
|
||||
double y = point.getNumber(1);
|
||||
if (index == 0) {
|
||||
updateHoverText("Unassigned: " + unassignedTargets, x, y);
|
||||
} else {
|
||||
updateHoverText(index + ": " + groupTargetCounts.get(index - 1), x, y);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}).on(BrowserEvents.MOUSEOUT, new DatumFunction<Void>() {
|
||||
@Override
|
||||
public Void apply(Element context, Value d, int index) {
|
||||
infoText.attr(ATTR_VISIBILITY, "hidden");
|
||||
return null;
|
||||
}
|
||||
});
|
||||
pies.exit().remove();
|
||||
pies.attr("d", arc);
|
||||
|
||||
}
|
||||
|
||||
private void updateHoverText(final String displayText, final double x, final double y) {
|
||||
final Selection text = infoText.select("text");
|
||||
final Selection background = infoText.select("rect");
|
||||
|
||||
text.html(displayText);
|
||||
|
||||
final double textWidth = getTextWidth(text.node());
|
||||
final double textHeight = getTextHeight(text.node());
|
||||
|
||||
background.attr("width", textWidth * 1.1);
|
||||
background.attr("height", textHeight);
|
||||
|
||||
moveSelection(background, -textWidth * 1.1 / 2.0, -textHeight*0.8);
|
||||
moveSelection(infoText, x, y);
|
||||
infoText.attr(ATTR_VISIBILITY, "visible");
|
||||
}
|
||||
|
||||
private static void moveSelection(Selection sel, double x, double y) {
|
||||
sel.attr(ATTR_TRANSFORM, "translate(" + x + ", " + y + ")");
|
||||
}
|
||||
|
||||
|
||||
private static final native double getTextWidth(Element e)/*-{
|
||||
return e.getBBox().width;
|
||||
}-*/;
|
||||
|
||||
private static final native double getTextHeight(Element e)/*-{
|
||||
return e.getBBox().height;
|
||||
}-*/;
|
||||
|
||||
private void initChart() {
|
||||
arc = D3.svg().arc().innerRadius(0).outerRadius(90);
|
||||
int height = 200;
|
||||
int width = 260;
|
||||
|
||||
svg = D3.select(this).append("svg").attr("width", width).attr("height", height).append("g");
|
||||
moveSelection(svg, (float) width / 2, (float) height / 2);
|
||||
|
||||
pieGroup = svg.append("g");
|
||||
|
||||
infoText = svg.append("g").attr(ATTR_VISIBILITY, "hidden").classed("pie-info", true);
|
||||
infoText.append("rect");
|
||||
infoText.append("text").attr("text-anchor", "middle");
|
||||
|
||||
}
|
||||
|
||||
private static class PieArc {
|
||||
private double startAngle;
|
||||
|
||||
private double endAngle;
|
||||
|
||||
public PieArc(double startAngle, double endAngle) {
|
||||
this.startAngle = startAngle;
|
||||
this.endAngle = endAngle;
|
||||
}
|
||||
|
||||
public double getEndAngle() {
|
||||
return endAngle;
|
||||
}
|
||||
|
||||
public Arc getArc() {
|
||||
return Arc.constantArc().startAngle(startAngle).endAngle(endAngle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,20 @@
|
||||
package org.eclipse.hawkbit.ui.rollout.rollout;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutUpdate;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
@@ -26,19 +33,22 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessActi
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroupsValidation;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.ui.UiProperties;
|
||||
import org.eclipse.hawkbit.ui.common.CommonDialogWindow;
|
||||
import org.eclipse.hawkbit.ui.common.CommonDialogWindow.SaveDialogCloseListener;
|
||||
import org.eclipse.hawkbit.ui.common.DistributionSetIdName;
|
||||
import org.eclipse.hawkbit.ui.common.builder.ComboBoxBuilder;
|
||||
import org.eclipse.hawkbit.ui.common.builder.LabelBuilder;
|
||||
import org.eclipse.hawkbit.ui.common.builder.TextAreaBuilder;
|
||||
import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder;
|
||||
import org.eclipse.hawkbit.ui.common.builder.WindowBuilder;
|
||||
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
|
||||
import org.eclipse.hawkbit.ui.filtermanagement.TargetFilterBeanQuery;
|
||||
import org.eclipse.hawkbit.ui.management.footer.ActionTypeOptionGroupLayout;
|
||||
import org.eclipse.hawkbit.ui.management.footer.ActionTypeOptionGroupLayout.ActionTypeOption;
|
||||
import org.eclipse.hawkbit.ui.rollout.event.RolloutEvent;
|
||||
import org.eclipse.hawkbit.ui.rollout.groupschart.GroupsPieChart;
|
||||
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
|
||||
import org.eclipse.hawkbit.ui.utils.I18N;
|
||||
import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil;
|
||||
@@ -47,6 +57,8 @@ import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions;
|
||||
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
|
||||
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
|
||||
import org.eclipse.hawkbit.ui.utils.UINotification;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory;
|
||||
import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer;
|
||||
import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition;
|
||||
@@ -66,6 +78,7 @@ import com.vaadin.ui.ComboBox;
|
||||
import com.vaadin.ui.GridLayout;
|
||||
import com.vaadin.ui.Label;
|
||||
import com.vaadin.ui.OptionGroup;
|
||||
import com.vaadin.ui.TabSheet;
|
||||
import com.vaadin.ui.TextArea;
|
||||
import com.vaadin.ui.TextField;
|
||||
import com.vaadin.ui.themes.ValoTheme;
|
||||
@@ -83,16 +96,22 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
|
||||
private final ActionTypeOptionGroupLayout actionTypeOptionGroupLayout;
|
||||
|
||||
private final AutoStartOptionGroupLayout autoStartOptionGroupLayout;
|
||||
|
||||
private final transient RolloutManagement rolloutManagement;
|
||||
|
||||
private final transient TargetManagement targetManagement;
|
||||
|
||||
private final transient TargetFilterQueryManagement targetFilterQueryManagement;
|
||||
|
||||
private final UINotification uiNotification;
|
||||
|
||||
private final UiProperties uiProperties;
|
||||
private final transient UiProperties uiProperties;
|
||||
|
||||
private final transient EntityFactory entityFactory;
|
||||
|
||||
private final DefineGroupsLayout defineGroupsLayout;
|
||||
|
||||
private final I18N i18n;
|
||||
|
||||
private final transient EventBus.UIEventBus eventBus;
|
||||
@@ -117,22 +136,29 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
|
||||
private CommonDialogWindow window;
|
||||
|
||||
private Boolean editRolloutEnabled;
|
||||
private boolean editRolloutEnabled;
|
||||
|
||||
private Rollout rolloutForEdit;
|
||||
private Rollout rollout;
|
||||
|
||||
private Long totalTargetsCount;
|
||||
|
||||
private Label totalTargetsLabel;
|
||||
|
||||
private TextArea targetFilterQuery;
|
||||
|
||||
private TabSheet groupsDefinitionTabs;
|
||||
|
||||
private GroupsPieChart groupsPieChart;
|
||||
|
||||
private GroupsLegendLayout groupsLegendLayout;
|
||||
|
||||
private final transient RolloutGroupConditions defaultRolloutGroupConditions;
|
||||
|
||||
private final NullValidator nullValidator = new NullValidator(null, false);
|
||||
|
||||
AddUpdateRolloutWindowLayout(final RolloutManagement rolloutManagement, final TargetManagement targetManagement,
|
||||
final UINotification uiNotification, final UiProperties uiProperties, final EntityFactory entityFactory,
|
||||
final I18N i18n, final UIEventBus eventBus) {
|
||||
final I18N i18n, final UIEventBus eventBus, final TargetFilterQueryManagement targetFilterQueryManagement) {
|
||||
this.actionTypeOptionGroupLayout = new ActionTypeOptionGroupLayout(i18n);
|
||||
this.autoStartOptionGroupLayout = new AutoStartOptionGroupLayout(i18n);
|
||||
this.rolloutManagement = rolloutManagement;
|
||||
this.targetManagement = targetManagement;
|
||||
this.uiNotification = uiNotification;
|
||||
@@ -140,10 +166,20 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
this.entityFactory = entityFactory;
|
||||
this.i18n = i18n;
|
||||
this.eventBus = eventBus;
|
||||
this.targetFilterQueryManagement = targetFilterQueryManagement;
|
||||
|
||||
this.defineGroupsLayout = new DefineGroupsLayout(i18n, entityFactory, rolloutManagement,
|
||||
targetFilterQueryManagement);
|
||||
|
||||
defaultRolloutGroupConditions = new RolloutGroupConditionBuilder().withDefaults().build();
|
||||
|
||||
setSizeUndefined();
|
||||
createRequiredComponents();
|
||||
buildLayout();
|
||||
|
||||
defineGroupsLayout.setValidationListener(this::displayValidationStatus);
|
||||
defineGroupsLayout.setDefaultErrorThreshold(defaultRolloutGroupConditions.getErrorConditionExp());
|
||||
defineGroupsLayout.setDefaultTriggerThreshold(defaultRolloutGroupConditions.getSuccessConditionExp());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,27 +209,39 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
*
|
||||
* @param rolloutId
|
||||
* the rollout id
|
||||
* @param copy
|
||||
* whether the rollout should be copied
|
||||
* @return the window
|
||||
*/
|
||||
public CommonDialogWindow getWindow(final Long rolloutId) {
|
||||
window = getWindow();
|
||||
populateData(rolloutId);
|
||||
public CommonDialogWindow getWindow(final Long rolloutId, final boolean copy) {
|
||||
resetComponents();
|
||||
window = createWindow();
|
||||
populateData(rolloutId, copy);
|
||||
return window;
|
||||
}
|
||||
|
||||
public CommonDialogWindow getWindow() {
|
||||
resetComponents();
|
||||
private CommonDialogWindow createWindow() {
|
||||
return new WindowBuilder(SPUIDefinitions.CREATE_UPDATE_WINDOW).caption(i18n.get("caption.configure.rollout"))
|
||||
.content(this).layout(this).i18n(i18n)
|
||||
.helpLink(uiProperties.getLinks().getDocumentation().getRolloutView())
|
||||
.saveDialogCloseListener(new SaveOnDialogCloseListener()).buildCommonDialogWindow();
|
||||
}
|
||||
|
||||
public CommonDialogWindow getWindow() {
|
||||
resetComponents();
|
||||
window = createWindow();
|
||||
window.updateAllComponents(noOfGroups);
|
||||
window.updateAllComponents(triggerThreshold);
|
||||
window.updateAllComponents(errorThreshold);
|
||||
return window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the field values.
|
||||
*/
|
||||
public void resetComponents() {
|
||||
editRolloutEnabled = Boolean.FALSE;
|
||||
defineGroupsLayout.resetComponents();
|
||||
editRolloutEnabled = false;
|
||||
rolloutName.clear();
|
||||
targetFilterQuery.clear();
|
||||
resetFields();
|
||||
@@ -201,22 +249,26 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
populateDistributionSet();
|
||||
populateTargetFilterQuery();
|
||||
setDefaultSaveStartGroupOption();
|
||||
totalTargetsLabel.setVisible(false);
|
||||
groupsLegendLayout.reset();
|
||||
groupSizeLabel.setVisible(false);
|
||||
noOfGroups.setVisible(true);
|
||||
removeComponent(1, 2);
|
||||
addComponent(targetFilterQueryCombo, 1, 2);
|
||||
actionTypeOptionGroupLayout.selectDefaultOption();
|
||||
autoStartOptionGroupLayout.selectDefaultOption();
|
||||
totalTargetsCount = 0L;
|
||||
rolloutForEdit = null;
|
||||
rollout = null;
|
||||
groupsDefinitionTabs.setVisible(true);
|
||||
groupsDefinitionTabs.setSelectedTab(0);
|
||||
}
|
||||
|
||||
private void resetFields() {
|
||||
rolloutName.removeStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR);
|
||||
noOfGroups.clear();
|
||||
noOfGroups.removeStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR);
|
||||
triggerThreshold.clear();
|
||||
triggerThreshold.setValue(defaultRolloutGroupConditions.getSuccessConditionExp());
|
||||
triggerThreshold.removeStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR);
|
||||
errorThreshold.clear();
|
||||
errorThreshold.setValue(defaultRolloutGroupConditions.getErrorConditionExp());
|
||||
errorThreshold.removeStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR);
|
||||
description.clear();
|
||||
description.removeStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR);
|
||||
@@ -224,11 +276,13 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
|
||||
private void buildLayout() {
|
||||
|
||||
setSpacing(Boolean.TRUE);
|
||||
setSpacing(true);
|
||||
setSizeUndefined();
|
||||
setRows(9);
|
||||
setColumns(3);
|
||||
setRows(7);
|
||||
setColumns(4);
|
||||
setStyleName("marginTop");
|
||||
setColumnExpandRatio(3, 1);
|
||||
setWidth(850, Unit.PIXELS);
|
||||
|
||||
addComponent(getMandatoryLabel("textfield.name"), 0, 0);
|
||||
addComponent(rolloutName, 1, 0);
|
||||
@@ -243,28 +297,19 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
targetFilterQueryCombo.addValidator(nullValidator);
|
||||
targetFilterQuery.removeValidator(nullValidator);
|
||||
|
||||
addComponent(totalTargetsLabel, 2, 2);
|
||||
addComponent(getLabel("textfield.description"), 0, 3);
|
||||
addComponent(description, 1, 3, 1, 3);
|
||||
|
||||
addComponent(getMandatoryLabel("prompt.number.of.groups"), 0, 3);
|
||||
addComponent(noOfGroups, 1, 3);
|
||||
noOfGroups.addValidator(nullValidator);
|
||||
addComponent(groupsLegendLayout, 3, 0, 3, 3);
|
||||
addComponent(groupsPieChart, 2, 0, 2, 3);
|
||||
|
||||
addComponent(groupSizeLabel, 2, 3);
|
||||
addComponent(getMandatoryLabel("caption.rollout.action.type"), 0, 4);
|
||||
addComponent(actionTypeOptionGroupLayout, 1, 4, 3, 4);
|
||||
|
||||
addComponent(getMandatoryLabel("prompt.tigger.threshold"), 0, 4);
|
||||
addComponent(triggerThreshold, 1, 4);
|
||||
triggerThreshold.addValidator(nullValidator);
|
||||
addComponent(getMandatoryLabel("caption.rollout.start.type"), 0, 5);
|
||||
addComponent(autoStartOptionGroupLayout, 1, 5, 3, 5);
|
||||
|
||||
addComponent(getPercentHintLabel(), 2, 4);
|
||||
|
||||
addComponent(getMandatoryLabel("prompt.error.threshold"), 0, 5);
|
||||
addComponent(errorThreshold, 1, 5);
|
||||
errorThreshold.addValidator(nullValidator);
|
||||
addComponent(errorThresholdOptionGroup, 2, 5);
|
||||
|
||||
addComponent(getLabel("textfield.description"), 0, 6);
|
||||
addComponent(description, 1, 6, 2, 6);
|
||||
addComponent(actionTypeOptionGroupLayout, 0, 7, 2, 7);
|
||||
addComponent(groupsDefinitionTabs, 0, 6, 3, 6);
|
||||
|
||||
rolloutName.focus();
|
||||
}
|
||||
@@ -310,15 +355,93 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
|
||||
noOfGroups = createNoOfGroupsField();
|
||||
groupSizeLabel = createCountLabel();
|
||||
|
||||
triggerThreshold = createTriggerThreshold();
|
||||
errorThreshold = createErrorThreshold();
|
||||
description = createDescription();
|
||||
errorThresholdOptionGroup = createErrorThresholdOptionGroup();
|
||||
setDefaultSaveStartGroupOption();
|
||||
actionTypeOptionGroupLayout.selectDefaultOption();
|
||||
totalTargetsLabel = createCountLabel();
|
||||
autoStartOptionGroupLayout.selectDefaultOption();
|
||||
targetFilterQuery = createTargetFilterQuery();
|
||||
actionTypeOptionGroupLayout.addStyleName(SPUIStyleDefinitions.ROLLOUT_ACTION_TYPE_LAYOUT);
|
||||
autoStartOptionGroupLayout.addStyleName(SPUIStyleDefinitions.ROLLOUT_ACTION_TYPE_LAYOUT);
|
||||
|
||||
groupsDefinitionTabs = createGroupDefinitionTabs();
|
||||
|
||||
groupsPieChart = new GroupsPieChart();
|
||||
groupsPieChart.setWidth(260, Unit.PIXELS);
|
||||
groupsPieChart.setHeight(220, Unit.PIXELS);
|
||||
groupsPieChart.setStyleName(SPUIStyleDefinitions.ROLLOUT_GROUPS_CHART);
|
||||
|
||||
groupsLegendLayout = new GroupsLegendLayout(i18n);
|
||||
|
||||
}
|
||||
|
||||
private void displayValidationStatus(DefineGroupsLayout.ValidationStatus status) {
|
||||
if(status == DefineGroupsLayout.ValidationStatus.LOADING) {
|
||||
groupsLegendLayout.displayLoading();
|
||||
} else {
|
||||
validateGroups();
|
||||
}
|
||||
}
|
||||
|
||||
private TabSheet createGroupDefinitionTabs() {
|
||||
TabSheet tabSheet = new TabSheet();
|
||||
tabSheet.setId(UIComponentIdProvider.ROLLOUT_GROUPS);
|
||||
tabSheet.setWidth(850, Unit.PIXELS);
|
||||
tabSheet.setHeight(300, Unit.PIXELS);
|
||||
tabSheet.setStyleName(SPUIStyleDefinitions.ROLLOUT_GROUPS);
|
||||
|
||||
TabSheet.Tab simpleTab = tabSheet.addTab(createSimpleGroupDefinitionTab(),
|
||||
i18n.get("caption.rollout.tabs.simple"));
|
||||
simpleTab.setId(UIComponentIdProvider.ROLLOUT_SIMPLE_TAB);
|
||||
|
||||
TabSheet.Tab advancedTab = tabSheet.addTab(defineGroupsLayout, i18n.get("caption.rollout.tabs.advanced"));
|
||||
advancedTab.setId(UIComponentIdProvider.ROLLOUT_ADVANCED_TAB);
|
||||
|
||||
tabSheet.addSelectedTabChangeListener(event -> validateGroups());
|
||||
|
||||
return tabSheet;
|
||||
}
|
||||
|
||||
private static int getPositionOfSelectedTab(final TabSheet tabSheet) {
|
||||
return tabSheet.getTabPosition(tabSheet.getTab(tabSheet.getSelectedTab()));
|
||||
}
|
||||
|
||||
private boolean isNumberOfGroups() {
|
||||
return getPositionOfSelectedTab(groupsDefinitionTabs) == 0;
|
||||
}
|
||||
|
||||
private boolean isGroupsDefinition() {
|
||||
return getPositionOfSelectedTab(groupsDefinitionTabs) == 1;
|
||||
}
|
||||
|
||||
private GridLayout createSimpleGroupDefinitionTab() {
|
||||
GridLayout layout = new GridLayout();
|
||||
layout.setSpacing(true);
|
||||
layout.setColumns(3);
|
||||
layout.setRows(4);
|
||||
layout.setStyleName("marginTop");
|
||||
|
||||
layout.addComponent(getLabel("caption.rollout.generate.groups"), 0, 0, 2, 0);
|
||||
|
||||
layout.addComponent(getMandatoryLabel("prompt.number.of.groups"), 0, 1);
|
||||
layout.addComponent(noOfGroups, 1, 1);
|
||||
noOfGroups.addValidator(nullValidator);
|
||||
layout.addComponent(groupSizeLabel, 2, 1);
|
||||
|
||||
layout.addComponent(getMandatoryLabel("prompt.tigger.threshold"), 0, 2);
|
||||
layout.addComponent(triggerThreshold, 1, 2);
|
||||
triggerThreshold.addValidator(nullValidator);
|
||||
layout.addComponent(getPercentHintLabel(), 2, 2);
|
||||
|
||||
layout.addComponent(getMandatoryLabel("prompt.error.threshold"), 0, 3);
|
||||
layout.addComponent(errorThreshold, 1, 3);
|
||||
errorThreshold.addValidator(nullValidator);
|
||||
layout.addComponent(errorThresholdOptionGroup, 2, 3);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private static Label createCountLabel() {
|
||||
@@ -365,35 +488,95 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
errorThreshold.getValidators();
|
||||
}
|
||||
|
||||
private void validateGroups() {
|
||||
if(editRolloutEnabled) {
|
||||
return;
|
||||
}
|
||||
if(isGroupsDefinition()) {
|
||||
List<RolloutGroupCreate> savedRolloutGroups = defineGroupsLayout.getSavedRolloutGroups();
|
||||
if(!defineGroupsLayout.isValid() || savedRolloutGroups == null || savedRolloutGroups.isEmpty()) {
|
||||
noOfGroups.clear();
|
||||
} else {
|
||||
noOfGroups.setValue(String.valueOf(savedRolloutGroups.size()));
|
||||
}
|
||||
updateGroupsChart(defineGroupsLayout.getGroupsValidation());
|
||||
}
|
||||
if(isNumberOfGroups()) {
|
||||
if(noOfGroups.isValid() && !noOfGroups.getValue().isEmpty()) {
|
||||
updateGroupsChart(Integer.parseInt(noOfGroups.getValue()));
|
||||
} else {
|
||||
updateGroupsChart(0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void updateGroupsChart(final RolloutGroupsValidation validation) {
|
||||
if(validation == null) {
|
||||
groupsPieChart.setChartState(null, null);
|
||||
return;
|
||||
}
|
||||
List<Long> targetsPerGroup = validation.getTargetsPerGroup();
|
||||
if(validation.getTotalTargets() == 0L || targetsPerGroup.isEmpty()) {
|
||||
groupsPieChart.setChartState(null, null);
|
||||
} else {
|
||||
groupsPieChart.setChartState(targetsPerGroup, validation.getTotalTargets());
|
||||
}
|
||||
|
||||
totalTargetsCount = validation.getTotalTargets();
|
||||
groupsLegendLayout.populateTotalTargets(validation.getTotalTargets());
|
||||
groupsLegendLayout.populateGroupsLegendByValidation(validation, defineGroupsLayout.getSavedRolloutGroups());
|
||||
|
||||
}
|
||||
|
||||
private void updateGroupsChart(final List<RolloutGroup> savedGroups, long totalTargetsCount) {
|
||||
List<Long> targetsPerGroup = savedGroups.stream().map(group -> (long) group.getTotalTargets())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
groupsPieChart.setChartState(targetsPerGroup, totalTargetsCount);
|
||||
groupsLegendLayout.populateGroupsLegendByGroups(savedGroups);
|
||||
}
|
||||
|
||||
private void updateGroupsChart(final int amountOfGroups) {
|
||||
if (totalTargetsCount == null || totalTargetsCount == 0L || amountOfGroups == 0) {
|
||||
groupsPieChart.setChartState(null, null);
|
||||
groupsLegendLayout.populateGroupsLegendByTargetCounts(Collections.emptyList());
|
||||
} else {
|
||||
final List<Long> groups = new ArrayList<>(amountOfGroups);
|
||||
long leftTargets = totalTargetsCount;
|
||||
for (int i = 0; i < amountOfGroups; i++) {
|
||||
float percentage = 1.0F / (amountOfGroups - i);
|
||||
long targetsInGroup = Math.round(percentage * (double) leftTargets);
|
||||
leftTargets -= targetsInGroup;
|
||||
groups.add(targetsInGroup);
|
||||
}
|
||||
|
||||
groupsPieChart.setChartState(groups, totalTargetsCount);
|
||||
groupsLegendLayout.populateGroupsLegendByTargetCounts(groups);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private ComboBox createTargetFilterQueryCombo() {
|
||||
final ComboBox targetFilter = SPUIComponentProvider.getComboBox(null, "", null, ValoTheme.COMBOBOX_SMALL, false,
|
||||
"", i18n.get("prompt.target.filter"));
|
||||
targetFilter.setImmediate(true);
|
||||
targetFilter.setPageLength(7);
|
||||
targetFilter.setItemCaptionPropertyId(SPUILabelDefinitions.VAR_NAME);
|
||||
targetFilter.setId(UIComponentIdProvider.ROLLOUT_TARGET_FILTER_COMBO_ID);
|
||||
targetFilter.setSizeUndefined();
|
||||
targetFilter.addValueChangeListener(this::onTargetFilterChange);
|
||||
return targetFilter;
|
||||
return new ComboBoxBuilder().setValueChangeListener(this::onTargetFilterChange)
|
||||
.setPrompt(i18n.get("prompt.target.filter")).setId(UIComponentIdProvider.ROLLOUT_TARGET_FILTER_COMBO_ID)
|
||||
.buildCombBox();
|
||||
}
|
||||
|
||||
private void onTargetFilterChange(final ValueChangeEvent event) {
|
||||
final String filterQueryString = getTargetFilterQuery();
|
||||
if (!Strings.isNullOrEmpty(filterQueryString)) {
|
||||
totalTargetsCount = targetManagement.countTargetByTargetFilterQuery(filterQueryString);
|
||||
totalTargetsLabel.setValue(getTotalTargetMessage());
|
||||
totalTargetsLabel.setVisible(true);
|
||||
} else {
|
||||
if (Strings.isNullOrEmpty(filterQueryString)) {
|
||||
totalTargetsCount = 0L;
|
||||
totalTargetsLabel.setVisible(false);
|
||||
groupsLegendLayout.populateTotalTargets(null);
|
||||
defineGroupsLayout.setTargetFilter(null);
|
||||
} else {
|
||||
totalTargetsCount = targetManagement.countTargetByTargetFilterQuery(filterQueryString);
|
||||
groupsLegendLayout.populateTotalTargets(totalTargetsCount);
|
||||
defineGroupsLayout.setTargetFilter(filterQueryString);
|
||||
}
|
||||
onGroupNumberChange(event);
|
||||
}
|
||||
|
||||
private String getTotalTargetMessage() {
|
||||
return new StringBuilder(i18n.get("label.target.filter.count")).append(totalTargetsCount).toString();
|
||||
}
|
||||
|
||||
private String getTargetPerGroupMessage(final String value) {
|
||||
return new StringBuilder(i18n.get("label.target.per.group")).append(value).toString();
|
||||
}
|
||||
@@ -403,6 +586,16 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
targetFilterQueryCombo.setContainerDataSource(container);
|
||||
}
|
||||
|
||||
private void populateTargetFilterQuery(final Rollout rollout) {
|
||||
final Page<TargetFilterQuery> filterQueries = targetFilterQueryManagement
|
||||
.findTargetFilterQueryByQuery(new PageRequest(0, 1), rollout.getTargetFilterQuery());
|
||||
if(filterQueries.getTotalElements() > 0) {
|
||||
final TargetFilterQuery filterQuery = filterQueries.getContent().get(0);
|
||||
targetFilterQueryCombo.setValue(filterQuery.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static Container createTargetFilterComboContainer() {
|
||||
final BeanQueryFactory<TargetFilterBeanQuery> targetFilterQF = new BeanQueryFactory<>(
|
||||
TargetFilterBeanQuery.class);
|
||||
@@ -412,19 +605,32 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
}
|
||||
|
||||
private void editRollout() {
|
||||
if (rolloutForEdit == null) {
|
||||
if (rollout == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Rollout updatedRollout = rolloutManagement.updateRollout(entityFactory.rollout()
|
||||
.update(rolloutForEdit.getId()).name(rolloutName.getValue()).description(description.getValue()));
|
||||
uiNotification.displaySuccess(i18n.get("message.update.success", new Object[] { updatedRollout.getName() }));
|
||||
final DistributionSetIdName distributionSetIdName = (DistributionSetIdName) distributionSet.getValue();
|
||||
|
||||
RolloutUpdate rolloutUpdate = entityFactory.rollout().update(rollout.getId()).name(rolloutName.getValue())
|
||||
.description(description.getValue()).set(distributionSetIdName.getId()).actionType(getActionType())
|
||||
.forcedTime(getForcedTimeStamp());
|
||||
|
||||
if (AutoStartOptionGroupLayout.AutoStartOption.AUTO_START.equals(getAutoStartOption())) {
|
||||
rolloutUpdate.startAt(System.currentTimeMillis());
|
||||
}
|
||||
if (AutoStartOptionGroupLayout.AutoStartOption.SCHEDULED.equals(getAutoStartOption())) {
|
||||
rolloutUpdate.startAt(getScheduledStartTime());
|
||||
}
|
||||
|
||||
final Rollout updatedRollout = rolloutManagement.updateRollout(rolloutUpdate);
|
||||
|
||||
uiNotification.displaySuccess(i18n.get("message.update.success", updatedRollout.getName()));
|
||||
eventBus.publish(this, RolloutEvent.UPDATE_ROLLOUT);
|
||||
}
|
||||
|
||||
private boolean duplicateCheckForEdit() {
|
||||
final String rolloutNameVal = getRolloutName();
|
||||
if (!rolloutForEdit.getName().equals(rolloutNameVal)
|
||||
if (!rollout.getName().equals(rolloutNameVal)
|
||||
&& rolloutManagement.findRolloutByName(rolloutNameVal) != null) {
|
||||
uiNotification.displayValidationError(i18n.get("message.rollout.duplicate.check", rolloutNameVal));
|
||||
return false;
|
||||
@@ -433,10 +639,19 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
}
|
||||
|
||||
private long getForcedTimeStamp() {
|
||||
return (((ActionTypeOptionGroupLayout.ActionTypeOption) actionTypeOptionGroupLayout.getActionTypeOptionGroup()
|
||||
.getValue()) == ActionTypeOption.AUTO_FORCED)
|
||||
? actionTypeOptionGroupLayout.getForcedTimeDateField().getValue().getTime()
|
||||
: RepositoryModelConstants.NO_FORCE_TIME;
|
||||
return ActionTypeOption.AUTO_FORCED.equals(actionTypeOptionGroupLayout.getActionTypeOptionGroup().getValue())
|
||||
? actionTypeOptionGroupLayout.getForcedTimeDateField().getValue().getTime()
|
||||
: RepositoryModelConstants.NO_FORCE_TIME;
|
||||
}
|
||||
|
||||
private Long getScheduledStartTime() {
|
||||
return AutoStartOptionGroupLayout.AutoStartOption.SCHEDULED.equals(getAutoStartOption())
|
||||
? autoStartOptionGroupLayout.getStartAtDateField().getValue().getTime() : null;
|
||||
}
|
||||
|
||||
private AutoStartOptionGroupLayout.AutoStartOption getAutoStartOption() {
|
||||
return (AutoStartOptionGroupLayout.AutoStartOption) autoStartOptionGroupLayout.getAutoStartOptionGroup()
|
||||
.getValue();
|
||||
}
|
||||
|
||||
private ActionType getActionType() {
|
||||
@@ -446,26 +661,40 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
|
||||
private void createRollout() {
|
||||
final Rollout rolloutToCreate = saveRollout();
|
||||
uiNotification.displaySuccess(i18n.get("message.save.success", new Object[] { rolloutToCreate.getName() }));
|
||||
uiNotification.displaySuccess(i18n.get("message.save.success", rolloutToCreate.getName()));
|
||||
}
|
||||
|
||||
private Rollout saveRollout() {
|
||||
|
||||
final int amountGroup = Integer.parseInt(noOfGroups.getValue());
|
||||
final int errorThresoldPercent = getErrorThresoldPercentage(amountGroup);
|
||||
|
||||
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder().withDefaults()
|
||||
.successAction(RolloutGroupSuccessAction.NEXTGROUP, null)
|
||||
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, triggerThreshold.getValue())
|
||||
.errorCondition(RolloutGroupErrorCondition.THRESHOLD, String.valueOf(errorThresoldPercent))
|
||||
.errorAction(RolloutGroupErrorAction.PAUSE, null).build();
|
||||
|
||||
final DistributionSetIdName distributionSetIdName = (DistributionSetIdName) distributionSet.getValue();
|
||||
|
||||
return rolloutManagement.createRollout(entityFactory.rollout().create().name(rolloutName.getValue())
|
||||
final int amountGroup = Integer.parseInt(noOfGroups.getValue());
|
||||
final int errorThresholdPercent = getErrorThresholdPercentage(amountGroup);
|
||||
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder()
|
||||
.successAction(RolloutGroupSuccessAction.NEXTGROUP, null)
|
||||
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, triggerThreshold.getValue())
|
||||
.errorCondition(RolloutGroupErrorCondition.THRESHOLD, String.valueOf(errorThresholdPercent))
|
||||
.errorAction(RolloutGroupErrorAction.PAUSE, null).build();
|
||||
|
||||
final RolloutCreate rolloutCreate = entityFactory.rollout().create().name(rolloutName.getValue())
|
||||
.description(description.getValue()).set(distributionSetIdName.getId())
|
||||
.targetFilterQuery(getTargetFilterQuery()).actionType(getActionType()).forcedTime(getForcedTimeStamp()),
|
||||
amountGroup, conditions);
|
||||
.targetFilterQuery(getTargetFilterQuery()).actionType(getActionType()).forcedTime(getForcedTimeStamp());
|
||||
|
||||
if (AutoStartOptionGroupLayout.AutoStartOption.AUTO_START.equals(getAutoStartOption())) {
|
||||
rolloutCreate.startAt(System.currentTimeMillis());
|
||||
}
|
||||
if (AutoStartOptionGroupLayout.AutoStartOption.SCHEDULED.equals(getAutoStartOption())) {
|
||||
rolloutCreate.startAt(getScheduledStartTime());
|
||||
}
|
||||
|
||||
if (isNumberOfGroups()) {
|
||||
return rolloutManagement.createRollout(rolloutCreate, amountGroup, conditions);
|
||||
} else if (isGroupsDefinition()) {
|
||||
List<RolloutGroupCreate> groups = defineGroupsLayout.getSavedRolloutGroups();
|
||||
return rolloutManagement.createRollout(rolloutCreate, groups, conditions);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Either of the Tabs must be selected");
|
||||
}
|
||||
|
||||
private String getTargetFilterQuery() {
|
||||
@@ -478,7 +707,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
return null;
|
||||
}
|
||||
|
||||
private int getErrorThresoldPercentage(final int amountGroup) {
|
||||
private int getErrorThresholdPercentage(final int amountGroup) {
|
||||
int errorThresoldPercent = Integer.parseInt(errorThreshold.getValue());
|
||||
if (errorThresholdOptionGroup.getValue().equals(ERRORTHRESOLDOPTIONS.COUNT.getValue())) {
|
||||
final int groupSize = (int) Math.ceil((double) totalTargetsCount / (double) amountGroup);
|
||||
@@ -491,7 +720,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
private boolean duplicateCheck() {
|
||||
if (rolloutManagement.findRolloutByName(getRolloutName()) != null) {
|
||||
uiNotification.displayValidationError(
|
||||
i18n.get("message.rollout.duplicate.check", new Object[] { getRolloutName() }));
|
||||
i18n.get("message.rollout.duplicate.check", getRolloutName()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -515,6 +744,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
UIComponentIdProvider.ROLLOUT_ERROR_THRESOLD_ID);
|
||||
errorField.addValidator(new ThresholdFieldValidator());
|
||||
errorField.setMaxLength(7);
|
||||
errorField.setValue(defaultRolloutGroupConditions.getErrorConditionExp());
|
||||
return errorField;
|
||||
}
|
||||
|
||||
@@ -522,6 +752,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
final TextField thresholdField = createIntegerTextField("prompt.tigger.threshold",
|
||||
UIComponentIdProvider.ROLLOUT_TRIGGER_THRESOLD_ID);
|
||||
thresholdField.addValidator(new ThresholdFieldValidator());
|
||||
thresholdField.setValue(defaultRolloutGroupConditions.getSuccessConditionExp());
|
||||
return thresholdField;
|
||||
}
|
||||
|
||||
@@ -535,23 +766,25 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
}
|
||||
|
||||
private void onGroupNumberChange(final ValueChangeEvent event) {
|
||||
if (event.getProperty().getValue() != null && noOfGroups.isValid()) {
|
||||
if(editRolloutEnabled) {
|
||||
return;
|
||||
}
|
||||
if (event.getProperty().getValue() != null && noOfGroups.isValid() && totalTargetsCount != null
|
||||
&& isNumberOfGroups()) {
|
||||
groupSizeLabel.setValue(getTargetPerGroupMessage(String.valueOf(getGroupSize())));
|
||||
groupSizeLabel.setVisible(true);
|
||||
updateGroupsChart(Integer.parseInt(noOfGroups.getValue()));
|
||||
} else {
|
||||
groupSizeLabel.setVisible(false);
|
||||
if(isNumberOfGroups()) {
|
||||
updateGroupsChart(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ComboBox createDistributionSetCombo() {
|
||||
final ComboBox dsSet = SPUIComponentProvider.getComboBox(null, "", null, ValoTheme.COMBOBOX_SMALL, false, "",
|
||||
i18n.get("prompt.distribution.set"));
|
||||
dsSet.setImmediate(true);
|
||||
dsSet.setPageLength(7);
|
||||
dsSet.setItemCaptionPropertyId(SPUILabelDefinitions.VAR_NAME);
|
||||
dsSet.setId(UIComponentIdProvider.ROLLOUT_DS_ID);
|
||||
dsSet.setSizeUndefined();
|
||||
return dsSet;
|
||||
return new ComboBoxBuilder().setPrompt(i18n.get("prompt.distribution.set"))
|
||||
.setId(UIComponentIdProvider.ROLLOUT_DS_ID).buildCombBox();
|
||||
}
|
||||
|
||||
private void populateDistributionSet() {
|
||||
@@ -618,12 +851,12 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
}
|
||||
|
||||
class GroupNumberValidator implements Validator {
|
||||
private static final long serialVersionUID = 9049939751976326550L;
|
||||
private static final long serialVersionUID = 9043919751971326521L;
|
||||
|
||||
@Override
|
||||
public void validate(final Object value) {
|
||||
if (value != null) {
|
||||
new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, 500), 0, 500)
|
||||
new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 1, 500), 1, 500)
|
||||
.validate(Integer.valueOf(value.toString()));
|
||||
}
|
||||
}
|
||||
@@ -636,31 +869,47 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
* @param rolloutId
|
||||
* rollout id
|
||||
*/
|
||||
private void populateData(final Long rolloutId) {
|
||||
private void populateData(final Long rolloutId, final boolean copy) {
|
||||
if (rolloutId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
editRolloutEnabled = Boolean.TRUE;
|
||||
rolloutForEdit = rolloutManagement.findRolloutById(rolloutId);
|
||||
rolloutName.setValue(rolloutForEdit.getName());
|
||||
description.setValue(rolloutForEdit.getDescription());
|
||||
distributionSet.setValue(DistributionSetIdName.generate(rolloutForEdit.getDistributionSet()));
|
||||
final List<RolloutGroup> rolloutGroups = rolloutForEdit.getRolloutGroups();
|
||||
setThresholdValues(rolloutGroups);
|
||||
setActionType(rolloutForEdit);
|
||||
disableRequiredFieldsOnEdit();
|
||||
targetFilterQuery.setValue(rolloutForEdit.getTargetFilterQuery());
|
||||
removeComponent(1, 2);
|
||||
targetFilterQueryCombo.removeValidator(nullValidator);
|
||||
addComponent(targetFilterQuery, 1, 2);
|
||||
targetFilterQuery.addValidator(nullValidator);
|
||||
rollout = rolloutManagement.findRolloutById(rolloutId);
|
||||
description.setValue(rollout.getDescription());
|
||||
distributionSet.setValue(DistributionSetIdName.generate(rollout.getDistributionSet()));
|
||||
setActionType(rollout);
|
||||
setAutoStartType(rollout);
|
||||
|
||||
totalTargetsCount = targetManagement.countTargetByTargetFilterQuery(rolloutForEdit.getTargetFilterQuery());
|
||||
totalTargetsLabel.setValue(getTotalTargetMessage());
|
||||
totalTargetsLabel.setVisible(true);
|
||||
if (copy) {
|
||||
rolloutName.setValue(i18n.get("textfield.rollout.copied.name", rollout.getName()));
|
||||
populateTargetFilterQuery(rollout);
|
||||
|
||||
window.setOrginaleValues();
|
||||
defineGroupsLayout.populateByRollout(rollout);
|
||||
groupsDefinitionTabs.setSelectedTab(1);
|
||||
|
||||
window.clearOriginalValues();
|
||||
|
||||
} else {
|
||||
editRolloutEnabled = true;
|
||||
if (rollout.getStatus() != Rollout.RolloutStatus.READY) {
|
||||
disableRequiredFieldsOnEdit();
|
||||
}
|
||||
rolloutName.setValue(rollout.getName());
|
||||
groupsDefinitionTabs.setVisible(false);
|
||||
|
||||
targetFilterQuery.setValue(rollout.getTargetFilterQuery());
|
||||
removeComponent(1, 2);
|
||||
targetFilterQueryCombo.removeValidator(nullValidator);
|
||||
addComponent(targetFilterQuery, 1, 2);
|
||||
targetFilterQuery.addValidator(nullValidator);
|
||||
|
||||
window.setOrginaleValues();
|
||||
|
||||
updateGroupsChart(rollout.getRolloutGroups(), rollout.getTotalTargets());
|
||||
}
|
||||
|
||||
totalTargetsCount = targetManagement.countTargetByTargetFilterQuery(rollout.getTargetFilterQuery());
|
||||
groupsLegendLayout.populateTotalTargets(totalTargetsCount);
|
||||
}
|
||||
|
||||
private void disableRequiredFieldsOnEdit() {
|
||||
@@ -668,9 +917,11 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
distributionSet.setEnabled(false);
|
||||
errorThreshold.setEnabled(false);
|
||||
triggerThreshold.setEnabled(false);
|
||||
actionTypeOptionGroupLayout.getActionTypeOptionGroup().setEnabled(false);
|
||||
errorThresholdOptionGroup.setEnabled(false);
|
||||
actionTypeOptionGroupLayout.getActionTypeOptionGroup().setEnabled(false);
|
||||
actionTypeOptionGroupLayout.addStyleName(SPUIStyleDefinitions.DISABLE_ACTION_TYPE_LAYOUT);
|
||||
autoStartOptionGroupLayout.getAutoStartOptionGroup().setEnabled(false);
|
||||
autoStartOptionGroupLayout.addStyleName(SPUIStyleDefinitions.DISABLE_ACTION_TYPE_LAYOUT);
|
||||
}
|
||||
|
||||
private void enableFields() {
|
||||
@@ -679,6 +930,8 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
triggerThreshold.setEnabled(true);
|
||||
actionTypeOptionGroupLayout.getActionTypeOptionGroup().setEnabled(true);
|
||||
actionTypeOptionGroupLayout.removeStyleName(SPUIStyleDefinitions.DISABLE_ACTION_TYPE_LAYOUT);
|
||||
autoStartOptionGroupLayout.getAutoStartOptionGroup().setEnabled(true);
|
||||
autoStartOptionGroupLayout.removeStyleName(SPUIStyleDefinitions.DISABLE_ACTION_TYPE_LAYOUT);
|
||||
noOfGroups.setEnabled(true);
|
||||
targetFilterQueryCombo.setEnabled(true);
|
||||
errorThresholdOptionGroup.setEnabled(true);
|
||||
@@ -697,39 +950,22 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rolloutGroups
|
||||
*/
|
||||
private void setThresholdValues(final List<RolloutGroup> rolloutGroups) {
|
||||
if (rolloutGroups != null && !rolloutGroups.isEmpty()) {
|
||||
errorThreshold.setValue(rolloutGroups.get(0).getErrorConditionExp());
|
||||
triggerThreshold.setValue(rolloutGroups.get(0).getSuccessConditionExp());
|
||||
noOfGroups.setValue(String.valueOf(rolloutGroups.size()));
|
||||
private void setAutoStartType(final Rollout rollout) {
|
||||
if (rollout.getStartAt() == null) {
|
||||
autoStartOptionGroupLayout.getAutoStartOptionGroup()
|
||||
.setValue(AutoStartOptionGroupLayout.AutoStartOption.MANUAL);
|
||||
} else if (rollout.getStartAt() < System.currentTimeMillis()) {
|
||||
autoStartOptionGroupLayout.getAutoStartOptionGroup()
|
||||
.setValue(AutoStartOptionGroupLayout.AutoStartOption.AUTO_START);
|
||||
autoStartOptionGroupLayout.getStartAtDateField().setValue(new Date(rollout.getStartAt()));
|
||||
} else {
|
||||
errorThreshold.setValue("0");
|
||||
triggerThreshold.setValue("0");
|
||||
noOfGroups.setValue("0");
|
||||
autoStartOptionGroupLayout.getAutoStartOptionGroup()
|
||||
.setValue(AutoStartOptionGroupLayout.AutoStartOption.SCHEDULED);
|
||||
autoStartOptionGroupLayout.getStartAtDateField().setValue(new Date(rollout.getStartAt()));
|
||||
}
|
||||
}
|
||||
|
||||
enum SAVESTARTOPTIONS {
|
||||
SAVE("Save"), START("Start");
|
||||
|
||||
String value;
|
||||
|
||||
SAVESTARTOPTIONS(final String val) {
|
||||
this.value = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the value
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
enum ERRORTHRESOLDOPTIONS {
|
||||
private enum ERRORTHRESOLDOPTIONS {
|
||||
PERCENT("%"), COUNT("Count");
|
||||
|
||||
String value;
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* Copyright (c) 2015 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.rollout.rollout;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.eclipse.hawkbit.ui.utils.I18N;
|
||||
import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil;
|
||||
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
|
||||
import org.vaadin.hene.flexibleoptiongroup.FlexibleOptionGroup;
|
||||
import org.vaadin.hene.flexibleoptiongroup.FlexibleOptionGroupItemComponent;
|
||||
|
||||
import com.vaadin.data.Property.ValueChangeEvent;
|
||||
import com.vaadin.data.Property.ValueChangeListener;
|
||||
import com.vaadin.server.FontAwesome;
|
||||
import com.vaadin.shared.ui.datefield.Resolution;
|
||||
import com.vaadin.ui.DateField;
|
||||
import com.vaadin.ui.HorizontalLayout;
|
||||
import com.vaadin.ui.Label;
|
||||
import com.vaadin.ui.themes.ValoTheme;
|
||||
|
||||
/**
|
||||
* Rollout start types options layout
|
||||
*/
|
||||
public class AutoStartOptionGroupLayout extends HorizontalLayout {
|
||||
|
||||
private static final long serialVersionUID = -8460459258964093525L;
|
||||
private static final String STYLE_DIST_WINDOW_AUTO_START = "dist-window-actiontype";
|
||||
|
||||
private final I18N i18n;
|
||||
|
||||
private FlexibleOptionGroup autoStartOptionGroup;
|
||||
|
||||
private DateField startAtDateField;
|
||||
|
||||
/**
|
||||
* Instantiates the auto start options layout
|
||||
*
|
||||
* @param i18n
|
||||
* the internationalization helper
|
||||
*/
|
||||
public AutoStartOptionGroupLayout(final I18N i18n) {
|
||||
this.i18n = i18n;
|
||||
|
||||
createOptionGroup();
|
||||
addValueChangeListener();
|
||||
setStyleName("dist-window-actiontype-horz-layout");
|
||||
setSizeUndefined();
|
||||
}
|
||||
|
||||
private void addValueChangeListener() {
|
||||
autoStartOptionGroup.addValueChangeListener(new ValueChangeListener() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void valueChange(final ValueChangeEvent event) {
|
||||
if (event.getProperty().getValue().equals(AutoStartOption.SCHEDULED)) {
|
||||
startAtDateField.setEnabled(true);
|
||||
startAtDateField.setRequired(true);
|
||||
} else {
|
||||
startAtDateField.setEnabled(false);
|
||||
startAtDateField.setRequired(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createOptionGroup() {
|
||||
autoStartOptionGroup = new FlexibleOptionGroup();
|
||||
autoStartOptionGroup.addItem(AutoStartOption.MANUAL);
|
||||
autoStartOptionGroup.addItem(AutoStartOption.AUTO_START);
|
||||
autoStartOptionGroup.addItem(AutoStartOption.SCHEDULED);
|
||||
selectDefaultOption();
|
||||
|
||||
final FlexibleOptionGroupItemComponent manualItem = autoStartOptionGroup
|
||||
.getItemComponent(AutoStartOption.MANUAL);
|
||||
manualItem.setStyleName(STYLE_DIST_WINDOW_AUTO_START);
|
||||
// set Id for Forced radio button.
|
||||
manualItem.setId(UIComponentIdProvider.ROLLOUT_START_MANUAL_ID);
|
||||
addComponent(manualItem);
|
||||
final Label manualLabel = new Label();
|
||||
manualLabel.setStyleName("statusIconPending");
|
||||
manualLabel.setIcon(FontAwesome.HAND_PAPER_O);
|
||||
manualLabel.setCaption(i18n.get("caption.rollout.start.manual"));
|
||||
manualLabel.setDescription(i18n.get("caption.rollout.start.manual.desc"));
|
||||
manualLabel.setStyleName("padding-right-style");
|
||||
addComponent(manualLabel);
|
||||
|
||||
final FlexibleOptionGroupItemComponent autoStartItem = autoStartOptionGroup
|
||||
.getItemComponent(AutoStartOption.AUTO_START);
|
||||
autoStartItem.setId(UIComponentIdProvider.ROLLOUT_START_AUTO_ID);
|
||||
autoStartItem.setStyleName(STYLE_DIST_WINDOW_AUTO_START);
|
||||
addComponent(autoStartItem);
|
||||
final Label autoStartLabel = new Label();
|
||||
autoStartLabel.setSizeFull();
|
||||
autoStartLabel.setIcon(FontAwesome.PLAY);
|
||||
autoStartLabel.setCaption(i18n.get("caption.rollout.start.auto"));
|
||||
autoStartLabel.setDescription(i18n.get("caption.rollout.start.auto.desc"));
|
||||
autoStartLabel.setStyleName("padding-right-style");
|
||||
addComponent(autoStartLabel);
|
||||
|
||||
final FlexibleOptionGroupItemComponent scheduledItem = autoStartOptionGroup
|
||||
.getItemComponent(AutoStartOption.SCHEDULED);
|
||||
scheduledItem.setStyleName(STYLE_DIST_WINDOW_AUTO_START);
|
||||
// setted Id for Time Forced radio button.
|
||||
scheduledItem.setId(UIComponentIdProvider.ROLLOUT_START_SCHEDULED_ID);
|
||||
addComponent(scheduledItem);
|
||||
final Label scheduledLabel = new Label();
|
||||
scheduledLabel.setStyleName("statusIconPending");
|
||||
scheduledLabel.setIcon(FontAwesome.CLOCK_O);
|
||||
scheduledLabel.setCaption(i18n.get("caption.rollout.start.scheduled"));
|
||||
scheduledLabel.setDescription(i18n.get("caption.rollout.start.scheduled.desc"));
|
||||
scheduledLabel.setStyleName(STYLE_DIST_WINDOW_AUTO_START);
|
||||
addComponent(scheduledLabel);
|
||||
|
||||
startAtDateField = new DateField();
|
||||
startAtDateField.setInvalidAllowed(false);
|
||||
startAtDateField.setInvalidCommitted(false);
|
||||
startAtDateField.setEnabled(false);
|
||||
startAtDateField.setStyleName("dist-window-forcedtime");
|
||||
|
||||
final TimeZone tz = SPDateTimeUtil.getBrowserTimeZone();
|
||||
startAtDateField.setValue(
|
||||
Date.from(LocalDateTime.now().plusMinutes(30).atZone(SPDateTimeUtil.getTimeZoneId(tz)).toInstant()));
|
||||
startAtDateField.setImmediate(true);
|
||||
startAtDateField.setTimeZone(tz);
|
||||
startAtDateField.setLocale(i18n.getLocale());
|
||||
startAtDateField.setResolution(Resolution.MINUTE);
|
||||
startAtDateField.addStyleName(ValoTheme.DATEFIELD_SMALL);
|
||||
addComponent(startAtDateField);
|
||||
}
|
||||
|
||||
/**
|
||||
* To Set Default option for save.
|
||||
*/
|
||||
|
||||
public void selectDefaultOption() {
|
||||
autoStartOptionGroup.select(AutoStartOption.MANUAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollout start options
|
||||
*/
|
||||
public enum AutoStartOption {
|
||||
MANUAL, AUTO_START, SCHEDULED;
|
||||
|
||||
}
|
||||
|
||||
public FlexibleOptionGroup getAutoStartOptionGroup() {
|
||||
return autoStartOptionGroup;
|
||||
}
|
||||
|
||||
public DateField getStartAtDateField() {
|
||||
return startAtDateField;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,617 @@
|
||||
/**
|
||||
* Copyright (c) 2015 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.rollout.rollout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.vaadin.ui.UI;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroupsValidation;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.ui.common.builder.ComboBoxBuilder;
|
||||
import org.eclipse.hawkbit.ui.common.builder.LabelBuilder;
|
||||
import org.eclipse.hawkbit.ui.common.builder.TextAreaBuilder;
|
||||
import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder;
|
||||
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
|
||||
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorderWithIcon;
|
||||
import org.eclipse.hawkbit.ui.filtermanagement.TargetFilterBeanQuery;
|
||||
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
|
||||
import org.eclipse.hawkbit.ui.utils.I18N;
|
||||
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
|
||||
import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions;
|
||||
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory;
|
||||
import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer;
|
||||
import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition;
|
||||
|
||||
import com.vaadin.data.Container;
|
||||
import com.vaadin.data.Item;
|
||||
import com.vaadin.data.Validator;
|
||||
import com.vaadin.data.util.converter.StringToFloatConverter;
|
||||
import com.vaadin.data.util.converter.StringToIntegerConverter;
|
||||
import com.vaadin.data.validator.FloatRangeValidator;
|
||||
import com.vaadin.data.validator.IntegerRangeValidator;
|
||||
import com.vaadin.data.validator.StringLengthValidator;
|
||||
import com.vaadin.server.FontAwesome;
|
||||
import com.vaadin.server.UserError;
|
||||
import com.vaadin.ui.Button;
|
||||
import com.vaadin.ui.ComboBox;
|
||||
import com.vaadin.ui.Component;
|
||||
import com.vaadin.ui.GridLayout;
|
||||
import com.vaadin.ui.HorizontalLayout;
|
||||
import com.vaadin.ui.Label;
|
||||
import com.vaadin.ui.TextArea;
|
||||
import com.vaadin.ui.TextField;
|
||||
|
||||
/**
|
||||
* Define groups for a Rollout
|
||||
*/
|
||||
public class DefineGroupsLayout extends GridLayout {
|
||||
|
||||
private static final long serialVersionUID = 2939193468001472916L;
|
||||
|
||||
private I18N i18n;
|
||||
|
||||
private transient EntityFactory entityFactory;
|
||||
|
||||
private transient RolloutManagement rolloutManagement;
|
||||
|
||||
private transient TargetFilterQueryManagement targetFilterQueryManagement;
|
||||
|
||||
private String defaultTriggerThreshold;
|
||||
|
||||
private String defaultErrorThreshold;
|
||||
|
||||
private String targetFilter;
|
||||
|
||||
private transient List<GroupRow> groupRows;
|
||||
|
||||
private int groupsCount;
|
||||
|
||||
private transient List<RolloutGroupCreate> savedRolloutGroups;
|
||||
|
||||
private transient ValidationListener validationListener;
|
||||
|
||||
private ValidationStatus validationStatus = ValidationStatus.VALID;
|
||||
|
||||
private transient RolloutGroupsValidation groupsValidation;
|
||||
|
||||
private transient ListenableFuture<RolloutGroupsValidation> runningValidation;
|
||||
|
||||
private boolean validationRequested;
|
||||
|
||||
DefineGroupsLayout(I18N i18n, EntityFactory entityFactory, RolloutManagement rolloutManagement,
|
||||
TargetFilterQueryManagement targetFilterQueryManagement) {
|
||||
this.i18n = i18n;
|
||||
this.entityFactory = entityFactory;
|
||||
this.rolloutManagement = rolloutManagement;
|
||||
this.targetFilterQueryManagement = targetFilterQueryManagement;
|
||||
|
||||
groupRows = new ArrayList<>(10);
|
||||
setSizeUndefined();
|
||||
buildLayout();
|
||||
|
||||
}
|
||||
|
||||
private void buildLayout() {
|
||||
|
||||
setSpacing(Boolean.TRUE);
|
||||
setSizeUndefined();
|
||||
setRows(3);
|
||||
setColumns(6);
|
||||
setStyleName("marginTop");
|
||||
|
||||
addComponent(getLabel("caption.rollout.group.definition.desc"), 0, 0, 5, 0);
|
||||
|
||||
final int headerRow = 1;
|
||||
addComponent(getLabel("header.name"), 0, headerRow);
|
||||
addComponent(getLabel("header.target.filter.query"), 1, headerRow);
|
||||
addComponent(getLabel("header.target.percentage"), 2, headerRow);
|
||||
addComponent(getLabel("header.rolloutgroup.threshold"), 3, headerRow);
|
||||
addComponent(getLabel("header.rolloutgroup.threshold.error"), 4, headerRow);
|
||||
|
||||
addComponent(createAddButton(), 0, 2, 5, 2);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param targetFilter
|
||||
* the target filter which is required for verification
|
||||
*/
|
||||
public void setTargetFilter(final String targetFilter) {
|
||||
this.targetFilter = targetFilter;
|
||||
updateValidation();
|
||||
}
|
||||
|
||||
private int addRow() {
|
||||
final int insertIndex = getRows() - 1;
|
||||
insertRow(insertIndex);
|
||||
return insertIndex;
|
||||
}
|
||||
|
||||
private Label getLabel(final String key) {
|
||||
return new LabelBuilder().name(i18n.get(key)).buildLabel();
|
||||
}
|
||||
|
||||
private Button createAddButton() {
|
||||
Button button = SPUIComponentProvider.getButton(UIComponentIdProvider.ROLLOUT_GROUP_ADD_ID,
|
||||
i18n.get("button.rollout.add.group"), "", "", true, FontAwesome.PLUS,
|
||||
SPUIButtonStyleNoBorderWithIcon.class);
|
||||
button.setSizeUndefined();
|
||||
button.addStyleName("default-color");
|
||||
button.setEnabled(true);
|
||||
button.setVisible(true);
|
||||
button.addClickListener(event -> addGroupRow());
|
||||
return button;
|
||||
}
|
||||
|
||||
private GroupRow addGroupRow() {
|
||||
int rowIndex = addRow();
|
||||
GroupRow groupRow = new GroupRow();
|
||||
groupRow.addToGridRow(this, rowIndex);
|
||||
groupRows.add(groupRow);
|
||||
updateValidation();
|
||||
return groupRow;
|
||||
}
|
||||
|
||||
public List<RolloutGroupCreate> getSavedRolloutGroups() {
|
||||
return savedRolloutGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the validation instance if was already validated
|
||||
*/
|
||||
public RolloutGroupsValidation getGroupsValidation() {
|
||||
return groupsValidation;
|
||||
}
|
||||
|
||||
private void removeAllRows() {
|
||||
for (int i = getRows() - 2; i > 1; i--) {
|
||||
removeRow(i);
|
||||
}
|
||||
|
||||
groupRows.clear();
|
||||
}
|
||||
|
||||
public void setDefaultTriggerThreshold(final String defaultTriggerThreshold) {
|
||||
this.defaultTriggerThreshold = defaultTriggerThreshold;
|
||||
}
|
||||
|
||||
public void setDefaultErrorThreshold(final String defaultErrorThreshold) {
|
||||
this.defaultErrorThreshold = defaultErrorThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the field values.
|
||||
*/
|
||||
public void resetComponents() {
|
||||
|
||||
validationStatus = ValidationStatus.VALID;
|
||||
groupsCount = 0;
|
||||
|
||||
removeAllRows();
|
||||
addGroupRow();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate groups by rollout
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout
|
||||
*/
|
||||
public void populateByRollout(final Rollout rollout) {
|
||||
if (rollout == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeAllRows();
|
||||
|
||||
final List<RolloutGroup> groups = rollout.getRolloutGroups();
|
||||
for (final RolloutGroup group : groups) {
|
||||
final GroupRow groupRow = addGroupRow();
|
||||
groupRow.populateByGroup(group);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the groups definition form is valid
|
||||
*/
|
||||
public boolean isValid() {
|
||||
if (groupRows.isEmpty() || validationStatus != ValidationStatus.VALID) {
|
||||
return false;
|
||||
}
|
||||
return groupRows.stream().allMatch(GroupRow::isValid);
|
||||
}
|
||||
|
||||
private void updateValidation() {
|
||||
validationStatus = ValidationStatus.VALID;
|
||||
if (isValid()) {
|
||||
setValidationStatus(ValidationStatus.LOADING);
|
||||
savedRolloutGroups = getGroupsFromRows();
|
||||
validateRemainingTargets();
|
||||
|
||||
} else {
|
||||
resetRemainingTargetsError();
|
||||
setValidationStatus(ValidationStatus.INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
private void setValidationStatus(final ValidationStatus status) {
|
||||
validationStatus = status;
|
||||
if (validationListener != null) {
|
||||
validationListener.validation(status);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetRemainingTargetsError() {
|
||||
groupRows.forEach(GroupRow::hideLastGroupError);
|
||||
}
|
||||
|
||||
private void validateRemainingTargets() {
|
||||
resetRemainingTargetsError();
|
||||
if (targetFilter == null || (runningValidation != null && !runningValidation.isDone())) {
|
||||
validationRequested = true;
|
||||
return;
|
||||
}
|
||||
|
||||
validationRequested = false;
|
||||
|
||||
final UI ui = UI.getCurrent();
|
||||
|
||||
runningValidation = rolloutManagement
|
||||
.validateTargetsInGroups(savedRolloutGroups, targetFilter, System.currentTimeMillis());
|
||||
runningValidation.addCallback(validation -> ui.access(() -> this.setGroupsValidation(validation)),
|
||||
throwable -> ui.access(() -> this.setGroupsValidation(null)));
|
||||
|
||||
}
|
||||
|
||||
private void setGroupsValidation(RolloutGroupsValidation validation) {
|
||||
groupsValidation = validation;
|
||||
|
||||
if(validationRequested) {
|
||||
validateRemainingTargets();
|
||||
return;
|
||||
}
|
||||
|
||||
final GroupRow lastRow = groupRows.get(groupRows.size() - 1);
|
||||
if (groupsValidation != null && groupsValidation.isValid() && validationStatus != ValidationStatus.INVALID) {
|
||||
lastRow.hideLastGroupError();
|
||||
setValidationStatus(ValidationStatus.VALID);
|
||||
|
||||
} else {
|
||||
lastRow.markWithLastGroupError();
|
||||
setValidationStatus(ValidationStatus.INVALID);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private List<RolloutGroupCreate> getGroupsFromRows() {
|
||||
return groupRows.stream().map(GroupRow::getGroupEntity).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public void setValidationListener(final ValidationListener validationListener) {
|
||||
this.validationListener = validationListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Status of the groups validation
|
||||
*/
|
||||
public enum ValidationStatus {
|
||||
VALID,
|
||||
INVALID,
|
||||
LOADING
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement the interface and set the instance with setValidationListener
|
||||
* to receive updates for any changes within the group rows.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ValidationListener {
|
||||
/**
|
||||
* Is called after user input
|
||||
*
|
||||
* @param isValid
|
||||
* whether the input of the group rows is valid
|
||||
*/
|
||||
void validation(ValidationStatus isValid);
|
||||
}
|
||||
|
||||
|
||||
private class GroupRow {
|
||||
|
||||
private TextField groupName;
|
||||
|
||||
private ComboBox targetFilterQueryCombo;
|
||||
|
||||
private TextArea targetFilterQuery;
|
||||
|
||||
private TextField targetPercentage;
|
||||
|
||||
private TextField triggerThreshold;
|
||||
|
||||
private TextField errorThreshold;
|
||||
|
||||
private HorizontalLayout optionsLayout;
|
||||
|
||||
private boolean populated;
|
||||
|
||||
private boolean initialized;
|
||||
|
||||
public GroupRow() {
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
groupsCount += 1;
|
||||
groupName = createTextField("textfield.name", UIComponentIdProvider.ROLLOUT_GROUP_LIST_GRID_ID);
|
||||
groupName.setValue(i18n.get("textfield.rollout.group.default.name", groupsCount));
|
||||
groupName.setStyleName("rollout-group-name");
|
||||
|
||||
targetFilterQueryCombo = createTargetFilterQueryCombo();
|
||||
populateTargetFilterQuery();
|
||||
|
||||
targetFilterQuery = createTargetFilterQuery();
|
||||
|
||||
targetPercentage = createPercentageWithDecimalsField("textfield.target.percentage",
|
||||
UIComponentIdProvider.ROLLOUT_GROUP_TARGET_PERC_ID);
|
||||
targetPercentage.setValue("100");
|
||||
|
||||
triggerThreshold = createPercentageField("prompt.tigger.threshold",
|
||||
UIComponentIdProvider.ROLLOUT_TRIGGER_THRESOLD_ID);
|
||||
triggerThreshold.setValue(defaultTriggerThreshold);
|
||||
|
||||
errorThreshold = createPercentageField("prompt.error.threshold",
|
||||
UIComponentIdProvider.ROLLOUT_ERROR_THRESOLD_ID);
|
||||
errorThreshold.setValue(defaultErrorThreshold);
|
||||
|
||||
optionsLayout = new HorizontalLayout();
|
||||
optionsLayout.addComponent(createRemoveButton());
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private TextField createTextField(final String in18Key, final String id) {
|
||||
final TextField textField = new TextFieldBuilder().prompt(i18n.get(in18Key)).immediate(true).id(id)
|
||||
.buildTextComponent();
|
||||
textField.setSizeUndefined();
|
||||
textField.addValidator(
|
||||
new StringLengthValidator(i18n.get("message.rollout.group.name.invalid"), 1, 64, false));
|
||||
textField.addValueChangeListener(event -> valueChanged());
|
||||
return textField;
|
||||
}
|
||||
|
||||
private TextField createPercentageField(final String in18Key, final String id) {
|
||||
final TextField textField = new TextFieldBuilder().prompt(i18n.get(in18Key)).immediate(true).id(id)
|
||||
.buildTextComponent();
|
||||
textField.setWidth(80, Unit.PIXELS);
|
||||
textField.setNullRepresentation("");
|
||||
textField.setConverter(new StringToIntegerConverter());
|
||||
textField.addValidator(this::validateMandatoryPercentage);
|
||||
textField.addValueChangeListener(event -> valueChanged());
|
||||
return textField;
|
||||
}
|
||||
|
||||
private TextField createPercentageWithDecimalsField(final String in18Key, final String id) {
|
||||
final TextField textField = createPercentageField(in18Key, id);
|
||||
textField.setConverter(new StringToFloatConverter());
|
||||
return textField;
|
||||
}
|
||||
|
||||
private void removeGroupRow(final GroupRow groupRow) {
|
||||
groupRows.remove(groupRow);
|
||||
updateValidation();
|
||||
}
|
||||
|
||||
private void validateMandatoryPercentage(final Object value) {
|
||||
if (value != null) {
|
||||
final String message = i18n.get("message.rollout.field.value.range", 0, 100);
|
||||
if (value instanceof Float) {
|
||||
new FloatRangeValidator(message, 0F, 100F).validate(value);
|
||||
}
|
||||
if (value instanceof Integer) {
|
||||
new IntegerRangeValidator(message, 0, 100).validate(value);
|
||||
}
|
||||
} else {
|
||||
throw new Validator.EmptyValueException(i18n.get("message.enter.number"));
|
||||
}
|
||||
}
|
||||
|
||||
private void valueChanged() {
|
||||
if (initialized) {
|
||||
updateValidation();
|
||||
}
|
||||
}
|
||||
|
||||
private ComboBox createTargetFilterQueryCombo() {
|
||||
return new ComboBoxBuilder().setId(UIComponentIdProvider.ROLLOUT_TARGET_FILTER_COMBO_ID)
|
||||
.setPrompt(i18n.get("prompt.target.filter")).setValueChangeListener(event -> valueChanged())
|
||||
.buildCombBox();
|
||||
}
|
||||
|
||||
private TextArea createTargetFilterQuery() {
|
||||
final TextArea filterField = new TextAreaBuilder().style("text-area-style")
|
||||
.id(UIComponentIdProvider.ROLLOUT_TARGET_FILTER_QUERY_FIELD)
|
||||
.maxLengthAllowed(SPUILabelDefinitions.TARGET_FILTER_QUERY_TEXT_FIELD_LENGTH).buildTextComponent();
|
||||
|
||||
filterField.setNullRepresentation(StringUtils.EMPTY);
|
||||
filterField.setEnabled(false);
|
||||
filterField.setSizeUndefined();
|
||||
return filterField;
|
||||
}
|
||||
|
||||
private void populateTargetFilterQuery() {
|
||||
final Container container = createTargetFilterComboContainer();
|
||||
targetFilterQueryCombo.setContainerDataSource(container);
|
||||
}
|
||||
|
||||
private void populateTargetFilterQuery(final RolloutGroup group) {
|
||||
if (StringUtils.isEmpty(group.getTargetFilterQuery())) {
|
||||
targetFilterQueryCombo.setValue(null);
|
||||
} else {
|
||||
final Page<TargetFilterQuery> filterQueries = targetFilterQueryManagement
|
||||
.findTargetFilterQueryByQuery(new PageRequest(0, 1), group.getTargetFilterQuery());
|
||||
if (filterQueries.getTotalElements() > 0) {
|
||||
final TargetFilterQuery filterQuery = filterQueries.getContent().get(0);
|
||||
targetFilterQueryCombo.setValue(filterQuery.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Container createTargetFilterComboContainer() {
|
||||
final BeanQueryFactory<TargetFilterBeanQuery> targetFilterQF = new BeanQueryFactory<>(
|
||||
TargetFilterBeanQuery.class);
|
||||
return new LazyQueryContainer(
|
||||
new LazyQueryDefinition(true, SPUIDefinitions.PAGE_SIZE, SPUILabelDefinitions.VAR_NAME),
|
||||
targetFilterQF);
|
||||
}
|
||||
|
||||
private Button createRemoveButton() {
|
||||
Button button = SPUIComponentProvider.getButton(UIComponentIdProvider.ROLLOUT_GROUP_REMOVE_ID, "", "", "",
|
||||
true, FontAwesome.MINUS, SPUIButtonStyleNoBorderWithIcon.class);
|
||||
button.setSizeUndefined();
|
||||
button.addStyleName("default-color");
|
||||
button.setEnabled(true);
|
||||
button.setVisible(true);
|
||||
button.addClickListener(event -> onRemove());
|
||||
return button;
|
||||
}
|
||||
|
||||
private void onRemove() {
|
||||
int index = findRowIndexFor(groupName, 0);
|
||||
if (index != -1) {
|
||||
removeRow(index);
|
||||
}
|
||||
removeGroupRow(this);
|
||||
}
|
||||
|
||||
private int findRowIndexFor(final Component component, final int col) {
|
||||
final int rows = getRows();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
Component rowComponent = getComponent(col, i);
|
||||
if (component.equals(rowComponent)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private String getTargetFilterQuery() {
|
||||
if (null != targetFilterQueryCombo.getValue()
|
||||
&& HawkbitCommonUtil.trimAndNullIfEmpty((String) targetFilterQueryCombo.getValue()) != null) {
|
||||
final Item filterItem = targetFilterQueryCombo.getContainerDataSource()
|
||||
.getItem(targetFilterQueryCombo.getValue());
|
||||
return (String) filterItem.getItemProperty("query").getValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this group row to a grid layout
|
||||
*
|
||||
* @param layout
|
||||
* the grid layout
|
||||
* @param rowIndex
|
||||
* the row of the grid layout
|
||||
*/
|
||||
public void addToGridRow(GridLayout layout, int rowIndex) {
|
||||
layout.addComponent(groupName, 0, rowIndex);
|
||||
if (populated) {
|
||||
layout.addComponent(targetFilterQuery, 1, rowIndex);
|
||||
} else {
|
||||
layout.addComponent(targetFilterQueryCombo, 1, rowIndex);
|
||||
}
|
||||
layout.addComponent(targetPercentage, 2, rowIndex);
|
||||
layout.addComponent(triggerThreshold, 3, rowIndex);
|
||||
layout.addComponent(errorThreshold, 4, rowIndex);
|
||||
layout.addComponent(optionsLayout, 5, rowIndex);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a group definition from this group row
|
||||
*
|
||||
* @return the RolloutGroupCreate definition
|
||||
*/
|
||||
public RolloutGroupCreate getGroupEntity() {
|
||||
final RolloutGroupConditionBuilder conditionBuilder = new RolloutGroupConditionBuilder()
|
||||
.successAction(RolloutGroup.RolloutGroupSuccessAction.NEXTGROUP, null)
|
||||
.successCondition(RolloutGroup.RolloutGroupSuccessCondition.THRESHOLD, triggerThreshold.getValue());
|
||||
if (StringUtils.isNotEmpty(errorThreshold.getValue())) {
|
||||
conditionBuilder
|
||||
.errorCondition(RolloutGroup.RolloutGroupErrorCondition.THRESHOLD, errorThreshold.getValue())
|
||||
.errorAction(RolloutGroup.RolloutGroupErrorAction.PAUSE, null);
|
||||
}
|
||||
String percentageString = targetPercentage.getValue().replace(",", ".");
|
||||
Float percentage = Float.parseFloat(percentageString);
|
||||
|
||||
return entityFactory.rolloutGroup().create().name(groupName.getValue()).description(groupName.getValue())
|
||||
.targetFilterQuery(getTargetFilterQuery()).targetPercentage(percentage)
|
||||
.conditions(conditionBuilder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the row with the data from the provided groups.
|
||||
*
|
||||
* @param group
|
||||
* the data source
|
||||
*/
|
||||
public void populateByGroup(final RolloutGroup group) {
|
||||
|
||||
groupName.setValue(group.getName());
|
||||
targetFilterQuery.setValue(group.getTargetFilterQuery());
|
||||
populateTargetFilterQuery(group);
|
||||
|
||||
targetPercentage.setValue(String.format("%.2f", group.getTargetPercentage()));
|
||||
triggerThreshold.setValue(group.getSuccessConditionExp());
|
||||
errorThreshold.setValue(group.getErrorConditionExp());
|
||||
|
||||
populated = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the data entered in this row is valid
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return StringUtils.isNotEmpty(groupName.getValue()) && targetPercentage.isValid()
|
||||
&& triggerThreshold.isValid() && errorThreshold.isValid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an error for the row
|
||||
*/
|
||||
public void markWithLastGroupError() {
|
||||
targetPercentage.setComponentError(new UserError(i18n.get("message.rollout.remaining.targets.error")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides an error of the row
|
||||
*/
|
||||
public void hideLastGroupError() {
|
||||
targetPercentage.setComponentError(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
* Copyright (c) 2015 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.rollout.rollout;
|
||||
|
||||
import com.vaadin.ui.Component;
|
||||
import com.vaadin.ui.Label;
|
||||
import com.vaadin.ui.VerticalLayout;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroupsValidation;
|
||||
import org.eclipse.hawkbit.ui.common.builder.LabelBuilder;
|
||||
import org.eclipse.hawkbit.ui.utils.I18N;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Displays a legend for the Groups of a Rollout with the count of targets in
|
||||
* each group. On top of the group list, the total targets are displayed. If
|
||||
* there are unassigned targets, they get display on top of the groups list.
|
||||
*/
|
||||
public class GroupsLegendLayout extends VerticalLayout {
|
||||
|
||||
private static final long serialVersionUID = 5483206203739308677L;
|
||||
|
||||
private final I18N i18n;
|
||||
|
||||
private Label totalTargetsLabel;
|
||||
|
||||
private Label loadingLabel;
|
||||
|
||||
private Label unassignedTargetsLabel;
|
||||
|
||||
private VerticalLayout groupsLegend;
|
||||
|
||||
/**
|
||||
* Initializes a new GroupsLegendLayout
|
||||
*/
|
||||
GroupsLegendLayout(final I18N i18n) {
|
||||
this.i18n = i18n;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
|
||||
totalTargetsLabel = createTotalTargetsLabel();
|
||||
unassignedTargetsLabel = createUnassignedTargetsLabel();
|
||||
loadingLabel = createLoadingLabel();
|
||||
loadingLabel.setVisible(false);
|
||||
|
||||
groupsLegend = new VerticalLayout();
|
||||
groupsLegend.setStyleName("groups-legend");
|
||||
|
||||
addComponent(totalTargetsLabel);
|
||||
addComponent(loadingLabel);
|
||||
addComponent(unassignedTargetsLabel);
|
||||
addComponent(groupsLegend);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
groupsLegend.addComponent(createGroupTargetsLabel());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the display of the legend and total targets.
|
||||
*/
|
||||
public void reset() {
|
||||
totalTargetsLabel.setVisible(false);
|
||||
populateGroupsLegendByTargetCounts(Collections.emptyList());
|
||||
}
|
||||
|
||||
private static Label createTotalTargetsLabel() {
|
||||
final Label label = new LabelBuilder().visible(false).name("").buildLabel();
|
||||
label.addStyleName("rollout-target-count-title");
|
||||
label.setImmediate(true);
|
||||
label.setSizeUndefined();
|
||||
return label;
|
||||
}
|
||||
|
||||
private Label createLoadingLabel() {
|
||||
final Label label = new LabelBuilder().visible(false).name("").buildLabel();
|
||||
label.addStyleName("rollout-target-count-loading");
|
||||
label.setImmediate(true);
|
||||
label.setSizeUndefined();
|
||||
label.setValue(i18n.get("label.rollout.calculating"));
|
||||
return label;
|
||||
}
|
||||
|
||||
private static Label createUnassignedTargetsLabel() {
|
||||
final Label label = new LabelBuilder().visible(false).name("").buildLabel();
|
||||
label.addStyleName("rollout-group-unassigned");
|
||||
label.setSizeUndefined();
|
||||
return label;
|
||||
}
|
||||
|
||||
private static Label createGroupTargetsLabel() {
|
||||
final Label label = new LabelBuilder().visible(false).name("").buildLabel();
|
||||
label.addStyleName("rollout-group-count");
|
||||
label.setSizeUndefined();
|
||||
return label;
|
||||
}
|
||||
|
||||
private String getTotalTargetMessage(final long totalTargetsCount) {
|
||||
return i18n.get("label.target.filter.count") + totalTargetsCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display an indication that the legend is being calculated.
|
||||
* When the loading process is done one of the populate methods should be called.
|
||||
*/
|
||||
public void displayLoading() {
|
||||
populateGroupsLegendByTargetCounts(Collections.emptyList());
|
||||
loadingLabel.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the total targets or hides the label when null is supplied.
|
||||
*
|
||||
* @param totalTargets
|
||||
* null to hide the label or a count to be displayed as total
|
||||
* targets message
|
||||
*/
|
||||
public void populateTotalTargets(Long totalTargets) {
|
||||
if (totalTargets == null) {
|
||||
totalTargetsLabel.setVisible(false);
|
||||
} else {
|
||||
totalTargetsLabel.setVisible(true);
|
||||
totalTargetsLabel.setValue(getTotalTargetMessage(totalTargets));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the legend based on a list of anonymous groups. They can't have
|
||||
* unassigned targets.
|
||||
*
|
||||
* @param targetsPerGroup
|
||||
* list of target counts
|
||||
*/
|
||||
public void populateGroupsLegendByTargetCounts(final List<Long> targetsPerGroup) {
|
||||
loadingLabel.setVisible(false);
|
||||
|
||||
for (int i = 0; i < groupsLegend.getComponentCount(); i++) {
|
||||
final Component component = groupsLegend.getComponent(i);
|
||||
final Label label = (Label) component;
|
||||
if (targetsPerGroup.size() > i) {
|
||||
final Long targetCount = targetsPerGroup.get(i);
|
||||
label.setValue(
|
||||
getTargetsInGroupMessage(targetCount, i18n.get("textfield.rollout.group.default.name", i + 1)));
|
||||
label.setVisible(true);
|
||||
} else {
|
||||
label.setValue("");
|
||||
label.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
unassignedTargetsLabel.setValue("");
|
||||
unassignedTargetsLabel.setVisible(false);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the legend based on a groups validation and a list of groups
|
||||
* that is used for resolving their names. Positions of the groups in the
|
||||
* groups list and the validation need to be in correct order. Can have
|
||||
* unassigned targets that are displayed on top of the groups list which
|
||||
* results in one group less to be displayed
|
||||
*
|
||||
* @param validation
|
||||
* A rollout validation object that contains a list of target
|
||||
* counts and the total targets
|
||||
* @param groups
|
||||
* List of groups with their name
|
||||
*/
|
||||
public void populateGroupsLegendByValidation(final RolloutGroupsValidation validation, final List<RolloutGroupCreate> groups) {
|
||||
loadingLabel.setVisible(false);
|
||||
if (validation == null) {
|
||||
return;
|
||||
}
|
||||
List<Long> targetsPerGroup = validation.getTargetsPerGroup();
|
||||
final long unassigned = validation.getTotalTargets() - validation.getTargetsInGroups();
|
||||
final int labelsToUpdate = (unassigned > 0) ? (groupsLegend.getComponentCount() - 1)
|
||||
: groupsLegend.getComponentCount();
|
||||
for (int i = 0; i < groupsLegend.getComponentCount(); i++) {
|
||||
final Component component = groupsLegend.getComponent(i);
|
||||
final Label label = (Label) component;
|
||||
if (targetsPerGroup.size() > i && groups.size() > i && labelsToUpdate > i) {
|
||||
final Long targetCount = targetsPerGroup.get(i);
|
||||
final String groupName = groups.get(i).build().getName();
|
||||
|
||||
label.setValue(getTargetsInGroupMessage(targetCount, groupName));
|
||||
label.setVisible(true);
|
||||
} else {
|
||||
label.setValue("");
|
||||
label.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (unassigned > 0) {
|
||||
unassignedTargetsLabel.setValue(getTargetsInGroupMessage(unassigned, "Unassigned"));
|
||||
unassignedTargetsLabel.setVisible(true);
|
||||
} else {
|
||||
unassignedTargetsLabel.setValue("");
|
||||
unassignedTargetsLabel.setVisible(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the legend based on a list of groups.
|
||||
*
|
||||
* @param groups
|
||||
* List of groups with their name
|
||||
*/
|
||||
public void populateGroupsLegendByGroups(final List<RolloutGroup> groups) {
|
||||
loadingLabel.setVisible(false);
|
||||
|
||||
for (int i = 0; i < groupsLegend.getComponentCount(); i++) {
|
||||
final Component component = groupsLegend.getComponent(i);
|
||||
final Label label = (Label) component;
|
||||
if (groups.size() > i) {
|
||||
final int targetCount = groups.get(i).getTotalTargets();
|
||||
final String groupName = groups.get(i).getName();
|
||||
|
||||
label.setValue(getTargetsInGroupMessage((long) targetCount, groupName));
|
||||
label.setVisible(true);
|
||||
} else {
|
||||
label.setValue("");
|
||||
label.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String getTargetsInGroupMessage(final Long targets, final String groupName) {
|
||||
return i18n.get("label.rollout.targets.in.group", targets, groupName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,6 +37,7 @@ import java.util.Set;
|
||||
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||
@@ -87,6 +88,8 @@ public class RolloutListGrid extends AbstractGrid {
|
||||
|
||||
private static final String UPDATE_OPTION = "Update";
|
||||
|
||||
private static final String COPY_OPTION = "Copy";
|
||||
|
||||
private static final String PAUSE_OPTION = "Pause";
|
||||
|
||||
private static final String RUN_OPTION = "Run";
|
||||
@@ -130,11 +133,12 @@ public class RolloutListGrid extends AbstractGrid {
|
||||
RolloutListGrid(final I18N 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 EntityFactory entityFactory, final UiProperties uiProperties,
|
||||
final TargetFilterQueryManagement targetFilterQueryManagement) {
|
||||
super(i18n, eventBus, permissionChecker);
|
||||
this.rolloutManagement = rolloutManagement;
|
||||
this.addUpdateRolloutWindow = new AddUpdateRolloutWindowLayout(rolloutManagement, targetManagement,
|
||||
uiNotification, uiProperties, entityFactory, i18n, eventBus);
|
||||
uiNotification, uiProperties, entityFactory, i18n, eventBus, targetFilterQueryManagement);
|
||||
this.uiNotification = uiNotification;
|
||||
this.rolloutUIState = rolloutUIState;
|
||||
}
|
||||
@@ -230,6 +234,10 @@ public class RolloutListGrid extends AbstractGrid {
|
||||
rolloutGridContainer.addContainerProperty(UPDATE_OPTION, String.class, FontAwesome.EDIT.getHtml(), false,
|
||||
false);
|
||||
}
|
||||
if (permissionChecker.hasRolloutCreatePermission()) {
|
||||
rolloutGridContainer.addContainerProperty(COPY_OPTION, String.class, FontAwesome.COPY.getHtml(), false,
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -262,6 +270,10 @@ public class RolloutListGrid extends AbstractGrid {
|
||||
} else {
|
||||
getColumn(PAUSE_OPTION).setMaximumWidth(60);
|
||||
}
|
||||
if (permissionChecker.hasRolloutCreatePermission()) {
|
||||
getColumn(COPY_OPTION).setMinimumWidth(25);
|
||||
getColumn(COPY_OPTION).setMaximumWidth(25);
|
||||
}
|
||||
|
||||
getColumn(VAR_TOTAL_TARGETS_COUNT_STATUS).setMinimumWidth(280);
|
||||
}
|
||||
@@ -289,10 +301,13 @@ public class RolloutListGrid extends AbstractGrid {
|
||||
if (permissionChecker.hasRolloutUpdatePermission()) {
|
||||
getColumn(UPDATE_OPTION).setHeaderCaption(i18n.get("header.action.update"));
|
||||
}
|
||||
if (permissionChecker.hasRolloutCreatePermission()) {
|
||||
getColumn(COPY_OPTION).setHeaderCaption(i18n.get("header.action.copy"));
|
||||
}
|
||||
|
||||
HeaderCell join;
|
||||
if (permissionChecker.hasRolloutUpdatePermission()) {
|
||||
join = getDefaultHeaderRow().join(RUN_OPTION, PAUSE_OPTION, UPDATE_OPTION);
|
||||
join = getDefaultHeaderRow().join(RUN_OPTION, PAUSE_OPTION, UPDATE_OPTION, COPY_OPTION);
|
||||
} else {
|
||||
join = getDefaultHeaderRow().join(RUN_OPTION, PAUSE_OPTION);
|
||||
}
|
||||
@@ -323,6 +338,9 @@ public class RolloutListGrid extends AbstractGrid {
|
||||
if (permissionChecker.hasRolloutUpdatePermission()) {
|
||||
columnList.add(UPDATE_OPTION);
|
||||
}
|
||||
if (permissionChecker.hasRolloutCreatePermission()) {
|
||||
columnList.add(COPY_OPTION);
|
||||
}
|
||||
|
||||
columnList.add(VAR_CREATED_DATE);
|
||||
columnList.add(VAR_CREATED_USER);
|
||||
@@ -377,6 +395,10 @@ public class RolloutListGrid extends AbstractGrid {
|
||||
getColumn(UPDATE_OPTION)
|
||||
.setRenderer(new HtmlButtonRenderer(clickEvent -> updateRollout((Long) clickEvent.getItemId())));
|
||||
}
|
||||
if (permissionChecker.hasRolloutCreatePermission()) {
|
||||
getColumn(COPY_OPTION)
|
||||
.setRenderer(new HtmlButtonRenderer(clickEvent -> copyRollout((Long) clickEvent.getItemId())));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -430,12 +452,19 @@ public class RolloutListGrid extends AbstractGrid {
|
||||
}
|
||||
|
||||
private void updateRollout(final Long rolloutId) {
|
||||
final CommonDialogWindow addTargetWindow = addUpdateRolloutWindow.getWindow(rolloutId);
|
||||
final CommonDialogWindow addTargetWindow = addUpdateRolloutWindow.getWindow(rolloutId, false);
|
||||
addTargetWindow.setCaption(i18n.get("caption.update.rollout"));
|
||||
UI.getCurrent().addWindow(addTargetWindow);
|
||||
addTargetWindow.setVisible(Boolean.TRUE);
|
||||
}
|
||||
|
||||
private void copyRollout(final Long rolloutId) {
|
||||
final CommonDialogWindow addTargetWindow = addUpdateRolloutWindow.getWindow(rolloutId, true);
|
||||
addTargetWindow.setCaption(i18n.get("caption.create.rollout"));
|
||||
UI.getCurrent().addWindow(addTargetWindow);
|
||||
addTargetWindow.setVisible(Boolean.TRUE);
|
||||
}
|
||||
|
||||
private String getDescription(final CellReference cell) {
|
||||
|
||||
String description = null;
|
||||
|
||||
@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.ui.rollout.rollout;
|
||||
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.ui.SpPermissionChecker;
|
||||
import org.eclipse.hawkbit.ui.UiProperties;
|
||||
@@ -43,11 +44,12 @@ public class RolloutListHeader extends AbstractGridHeader {
|
||||
RolloutListHeader(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 I18N i18n) {
|
||||
final UiProperties uiProperties, final EntityFactory entityFactory, final I18N i18n,
|
||||
final TargetFilterQueryManagement targetFilterQueryManagement) {
|
||||
super(permissionChecker, rolloutUIState, i18n);
|
||||
this.eventBus = eventBus;
|
||||
this.addUpdateRolloutWindow = new AddUpdateRolloutWindowLayout(rolloutManagement, targetManagement,
|
||||
uiNotification, uiProperties, entityFactory, i18n, eventBus);
|
||||
uiNotification, uiProperties, entityFactory, i18n, eventBus, targetFilterQueryManagement);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.ui.rollout.rollout;
|
||||
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.ui.SpPermissionChecker;
|
||||
import org.eclipse.hawkbit.ui.UiProperties;
|
||||
@@ -31,11 +32,12 @@ public class RolloutListView extends AbstractGridLayout {
|
||||
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 I18N i18n) {
|
||||
final UiProperties uiProperties, final EntityFactory entityFactory, final I18N i18n,
|
||||
final TargetFilterQueryManagement targetFilterQueryManagement) {
|
||||
super(new RolloutListHeader(permissionChecker, rolloutUIState, eventBus, rolloutManagement, targetManagement,
|
||||
uiNotification, uiProperties, entityFactory, i18n),
|
||||
uiNotification, uiProperties, entityFactory, i18n, targetFilterQueryManagement),
|
||||
new RolloutListGrid(i18n, eventBus, rolloutManagement, uiNotification, rolloutUIState,
|
||||
permissionChecker, targetManagement, entityFactory, uiProperties));
|
||||
permissionChecker, targetManagement, entityFactory, uiProperties, targetFilterQueryManagement));
|
||||
|
||||
buildLayout();
|
||||
}
|
||||
|
||||
@@ -212,6 +212,14 @@ public final class SPUIStyleDefinitions {
|
||||
* Query validator icon -error style.
|
||||
*/
|
||||
public static final String ERROR_ICON = "error-icon";
|
||||
/**
|
||||
* Rollout groups
|
||||
*/
|
||||
public static final String ROLLOUT_GROUPS = "rollout-groups";
|
||||
/**
|
||||
* Rollout groups pie chart
|
||||
*/
|
||||
public static final String ROLLOUT_GROUPS_CHART = "groups-pie-chart";
|
||||
/**
|
||||
* Rollout action type layout style.
|
||||
*/
|
||||
|
||||
@@ -174,6 +174,21 @@ public final class UIComponentIdProvider {
|
||||
*/
|
||||
public static final String ACTION_DETAILS_SOFT_ID = "action.details.soft.group";
|
||||
|
||||
/**
|
||||
* Start type of rollout manual radio button
|
||||
*/
|
||||
public static final String ROLLOUT_START_MANUAL_ID = "rollout.start.manual.radio";
|
||||
|
||||
/**
|
||||
* Start type of rollout auto start radio button
|
||||
*/
|
||||
public static final String ROLLOUT_START_AUTO_ID = "rollout.start.auto.radio";
|
||||
|
||||
/**
|
||||
* Start type of rollout scheduled start radio button
|
||||
*/
|
||||
public static final String ROLLOUT_START_SCHEDULED_ID = "rollout.start.scheduled.start.radio";
|
||||
|
||||
/**
|
||||
* ID - Label.
|
||||
*/
|
||||
@@ -723,6 +738,21 @@ public final class UIComponentIdProvider {
|
||||
*/
|
||||
public static final String ROLLOUT_ERROR_THRESOLD_ID = "rollout.error.thresold.id";
|
||||
|
||||
/**
|
||||
* Rollout group target percentage id.
|
||||
*/
|
||||
public static final String ROLLOUT_GROUP_TARGET_PERC_ID = "rollout.group.target.percentage.id";
|
||||
|
||||
/**
|
||||
* Rollout group add button id
|
||||
*/
|
||||
public static final String ROLLOUT_GROUP_ADD_ID = "rollout.group.add.id";
|
||||
|
||||
/**
|
||||
* Rollout group remove button id
|
||||
*/
|
||||
public static final String ROLLOUT_GROUP_REMOVE_ID = "rollout.group.remove.id";
|
||||
|
||||
/**
|
||||
* Rollout distribution set combo id.
|
||||
*/
|
||||
@@ -735,7 +765,18 @@ public final class UIComponentIdProvider {
|
||||
* Rollout target filter query combo id.
|
||||
*/
|
||||
public static final String ROLLOUT_TARGET_FILTER_COMBO_ID = "rollout.target.filter.combo.id";
|
||||
|
||||
/**
|
||||
* Rollout groups id
|
||||
*/
|
||||
public static final String ROLLOUT_GROUPS = "rollout.groups";
|
||||
/**
|
||||
* Rollout simple tab id
|
||||
*/
|
||||
public static final String ROLLOUT_SIMPLE_TAB = "rollout.create.tabs.simple";
|
||||
/**
|
||||
* Rollout advanced tab id
|
||||
*/
|
||||
public static final String ROLLOUT_ADVANCED_TAB = "rollout.create.tabs.advanced";
|
||||
/**
|
||||
* Rollout action button id.
|
||||
*/
|
||||
@@ -752,10 +793,15 @@ public final class UIComponentIdProvider {
|
||||
public static final String ROLLOUT_PAUSE_BUTTON_ID = ROLLOUT_ACTION_ID + ".10";
|
||||
|
||||
/**
|
||||
* Rollout resume button id.
|
||||
* Rollout update button id.
|
||||
*/
|
||||
public static final String ROLLOUT_UPDATE_BUTTON_ID = ROLLOUT_ACTION_ID + ".11";
|
||||
|
||||
/**
|
||||
* Rollout copy button id.
|
||||
*/
|
||||
public static final String ROLLOUT_COPY_BUTTON_ID = ROLLOUT_ACTION_ID + ".12";
|
||||
|
||||
/**
|
||||
* Rollout status label id.
|
||||
*/
|
||||
@@ -771,6 +817,16 @@ public final class UIComponentIdProvider {
|
||||
*/
|
||||
public static final String ROLLOUT_ERROR_THRESOLD_OPTION_ID = "rollout.error.thresold.option.id";
|
||||
|
||||
/**
|
||||
* Rollout groups options id.
|
||||
*/
|
||||
public static final String ROLLOUT_GROUPS_OPTION_ID = "rollout.groups.option.id";
|
||||
|
||||
/**
|
||||
* Rollout groups definition button
|
||||
*/
|
||||
public static final String ROLLOUT_GROUPS_DEF_BUTTON_ID = "rollout.groups.definition.button.id";
|
||||
|
||||
/**
|
||||
* Rollout target filter query value text area id.
|
||||
*/
|
||||
|
||||
@@ -7,95 +7,207 @@
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
@mixin rollout {
|
||||
.rollout-option-group{
|
||||
font-size:12px;
|
||||
font-weight:400;
|
||||
margin-left:8px;
|
||||
}
|
||||
|
||||
.rollout-action-type-layout {
|
||||
.v-caption-padding-right-style{
|
||||
padding-right:0px !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.v-context-menu .v-context-menu-item-basic-icon-container{
|
||||
height:0px !important;
|
||||
width:0px !important;
|
||||
}
|
||||
|
||||
.v-context-menu .v-context-menu-item-basic{
|
||||
background-color: #feffff !important;
|
||||
border-radius: 4px;
|
||||
font-family : $app-font-family;
|
||||
font-size : $app-text-font-size;
|
||||
font-weight : normal;
|
||||
font-style : normal;
|
||||
}
|
||||
|
||||
|
||||
.v-context-menu{
|
||||
background-color: #feffff !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.v-context-menu .v-context-menu-item-basic:focus, .v-context-menu .v-context-menu-item-basic-submenu:focus, .v-context-menu .v-context-menu-item-basic-open {
|
||||
@include valo-gradient($color: $hawkbit-primary-color);
|
||||
background-color: $hawkbit-primary-color !important;
|
||||
color: #e8eef3;
|
||||
height: 30px;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.disable-action-type-layout{
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.action-type-padding{
|
||||
padding: 0 0px !important;
|
||||
}
|
||||
|
||||
.rollout-caption-links{
|
||||
font-weight: 400;
|
||||
height: 25px ;
|
||||
padding: 0px 4px ;
|
||||
}
|
||||
|
||||
.rollout-target-count-message{
|
||||
color: $info-message-color-grey;
|
||||
}
|
||||
|
||||
.rollout-table{
|
||||
.v-table-cell-wrapper {
|
||||
cursor: default;
|
||||
.rollout-option-group {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.rollout-action-type-layout {
|
||||
.v-caption-padding-right-style {
|
||||
padding-right: 0px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.v-context-menu .v-context-menu-item-basic-icon-container {
|
||||
height: 0px !important;
|
||||
width: 0px !important;
|
||||
}
|
||||
|
||||
.v-context-menu .v-context-menu-item-basic {
|
||||
background-color: #feffff !important;
|
||||
border-radius: 4px;
|
||||
font-family: $app-font-family;
|
||||
font-size: $app-text-font-size;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.v-context-menu {
|
||||
background-color: #feffff !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.v-context-menu .v-context-menu-item-basic:focus, .v-context-menu .v-context-menu-item-basic-submenu:focus, .v-context-menu .v-context-menu-item-basic-open {
|
||||
@include valo-gradient($color: $hawkbit-primary-color);
|
||||
background-color: $hawkbit-primary-color !important;
|
||||
color: #e8eef3;
|
||||
height: 30px;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.disable-action-type-layout {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.action-type-padding {
|
||||
padding: 0 0px !important;
|
||||
}
|
||||
|
||||
.rollout-caption-links {
|
||||
font-weight: 400;
|
||||
height: 25px;
|
||||
padding: 0px 4px;
|
||||
}
|
||||
|
||||
.rollout-target-count-message {
|
||||
color: $info-message-color-grey;
|
||||
}
|
||||
|
||||
.rollout-table {
|
||||
.v-table-cell-wrapper {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.v-grid-cell.centeralign {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.v-grid-cell {
|
||||
font-size: $v-font-size--small !important;
|
||||
height: 34px !important;
|
||||
}
|
||||
|
||||
.v-grid-row {
|
||||
height: 34px !important;
|
||||
}
|
||||
|
||||
.v-grid-cell.frozen {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.v-grid-cell.frozen + th {
|
||||
border-left: $v-grid-border-size solid $widget-border-color;
|
||||
}
|
||||
|
||||
.v-button-boldhide {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.groups-pie-chart {
|
||||
float: right;
|
||||
|
||||
svg {
|
||||
.pie {
|
||||
stroke: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.v-grid-cell.centeralign {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.v-grid-cell {
|
||||
font-size: $v-font-size--small !important;
|
||||
height: 34px !important;
|
||||
}
|
||||
|
||||
.v-grid-row{
|
||||
height: 34px !important;
|
||||
}
|
||||
|
||||
.v-grid-cell.frozen{
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.v-grid-cell.frozen + th{
|
||||
border-left: $v-grid-border-size solid $widget-border-color ;
|
||||
}
|
||||
|
||||
.v-button-boldhide{
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
/*
|
||||
First child is intended for unassigned targets and should not be repeated.
|
||||
*/
|
||||
.pie:nth-child(1) { fill: #CBCBCB }
|
||||
|
||||
.pie:nth-child(16n+2) { fill: #E20015 }
|
||||
.pie:nth-child(16n+3) { fill: #B90276 }
|
||||
.pie:nth-child(16n+4) { fill: #50237F }
|
||||
.pie:nth-child(16n+5) { fill: #005691 }
|
||||
.pie:nth-child(16n+6) { fill: #008ECF }
|
||||
.pie:nth-child(16n+7) { fill: #00A8B0 }
|
||||
.pie:nth-child(16n+8) { fill: #78BE20 }
|
||||
.pie:nth-child(16n+9) { fill: #006249 }
|
||||
.pie:nth-child(16n+10) { fill: #F07F8A }
|
||||
.pie:nth-child(16n+11) { fill: #DC80BA }
|
||||
.pie:nth-child(16n+12) { fill: #A791BF }
|
||||
.pie:nth-child(16n+13) { fill: #7FAAC8 }
|
||||
.pie:nth-child(16n+14) { fill: #7FC6E7 }
|
||||
.pie:nth-child(16n+15) { fill: #7FD3D7 }
|
||||
.pie:nth-child(16n+16) { fill: #BBDE8F }
|
||||
.pie:nth-child(16n+17) { fill: #7FB0A4 }
|
||||
|
||||
.pie-info {
|
||||
pointer-events: none;
|
||||
|
||||
rect {
|
||||
fill: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
text {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
fill: $hawkbit-primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.rollout-groups {
|
||||
|
||||
.v-button {
|
||||
margin: 0 3px 3px 3px;
|
||||
}
|
||||
|
||||
.rollout-group-name {
|
||||
border-left: 5px solid #000000;
|
||||
}
|
||||
.v-gridlayout-slot:nth-child(16n+8) .rollout-group-name { border-left-color: #E20015 }
|
||||
.v-gridlayout-slot:nth-child(16n+14) .rollout-group-name { border-left-color: #B90276 }
|
||||
.v-gridlayout-slot:nth-child(16n+20) .rollout-group-name { border-left-color: #50237F }
|
||||
.v-gridlayout-slot:nth-child(16n+26) .rollout-group-name { border-left-color: #005691 }
|
||||
.v-gridlayout-slot:nth-child(16n+32) .rollout-group-name { border-left-color: #008ECF }
|
||||
.v-gridlayout-slot:nth-child(16n+38) .rollout-group-name { border-left-color: #00A8B0 }
|
||||
.v-gridlayout-slot:nth-child(16n+44) .rollout-group-name { border-left-color: #78BE20 }
|
||||
.v-gridlayout-slot:nth-child(16n+50) .rollout-group-name { border-left-color: #006249 }
|
||||
.v-gridlayout-slot:nth-child(16n+56) .rollout-group-name { border-left-color: #F07F8A }
|
||||
.v-gridlayout-slot:nth-child(16n+62) .rollout-group-name { border-left-color: #DC80BA }
|
||||
.v-gridlayout-slot:nth-child(16n+68) .rollout-group-name { border-left-color: #A791BF }
|
||||
.v-gridlayout-slot:nth-child(16n+74) .rollout-group-name { border-left-color: #7FAAC8 }
|
||||
.v-gridlayout-slot:nth-child(16n+80) .rollout-group-name { border-left-color: #7FC6E7 }
|
||||
.v-gridlayout-slot:nth-child(16n+86) .rollout-group-name { border-left-color: #7FD3D7 }
|
||||
.v-gridlayout-slot:nth-child(16n+92) .rollout-group-name { border-left-color: #BBDE8F }
|
||||
.v-gridlayout-slot:nth-child(16n+98) .rollout-group-name { border-left-color: #7FB0A4 }
|
||||
}
|
||||
|
||||
.rollout-target-count-title {
|
||||
}
|
||||
|
||||
.rollout-target-count-loading {
|
||||
color: #ed473b;
|
||||
}
|
||||
|
||||
.rollout-group-unassigned {
|
||||
border-left: 5px solid #CBCBCB;
|
||||
padding-left: 5px;
|
||||
margin-bottom: 2px;
|
||||
font-size: $v-font-size * .7;
|
||||
}
|
||||
|
||||
.groups-legend {
|
||||
.v-slot .rollout-group-count {
|
||||
border-left: 5px solid transparent;
|
||||
padding-left: 5px;
|
||||
font-size: $v-font-size * .7;
|
||||
}
|
||||
|
||||
.v-slot:nth-child(16n+1) .rollout-group-count { border-left-color: #E20015 }
|
||||
.v-slot:nth-child(16n+2) .rollout-group-count { border-left-color: #B90276 }
|
||||
.v-slot:nth-child(16n+3) .rollout-group-count { border-left-color: #50237F }
|
||||
.v-slot:nth-child(16n+4) .rollout-group-count { border-left-color: #005691 }
|
||||
.v-slot:nth-child(16n+5) .rollout-group-count { border-left-color: #008ECF }
|
||||
.v-slot:nth-child(16n+6) .rollout-group-count { border-left-color: #00A8B0 }
|
||||
.v-slot:nth-child(16n+7) .rollout-group-count { border-left-color: #78BE20 }
|
||||
.v-slot:nth-child(16n+8) .rollout-group-count { border-left-color: #006249 }
|
||||
.v-slot:nth-child(16n+9) .rollout-group-count { border-left-color: #F07F8A }
|
||||
.v-slot:nth-child(16n+10) .rollout-group-count { border-left-color: #DC80BA }
|
||||
.v-slot:nth-child(16n+11) .rollout-group-count { border-left-color: #A791BF }
|
||||
.v-slot:nth-child(16n+12) .rollout-group-count { border-left-color: #7FAAC8 }
|
||||
.v-slot:nth-child(16n+13) .rollout-group-count { border-left-color: #7FC6E7 }
|
||||
.v-slot:nth-child(16n+14) .rollout-group-count { border-left-color: #7FD3D7 }
|
||||
.v-slot:nth-child(16n+15) .rollout-group-count { border-left-color: #BBDE8F }
|
||||
.v-slot:nth-child(16n+16) .rollout-group-count { border-left-color: #7FB0A4 }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ header.action=Actions
|
||||
header.action.run=Run
|
||||
header.action.pause=Pause
|
||||
header.action.update=Edit
|
||||
header.action.copy=Copy
|
||||
header.status=Status
|
||||
|
||||
# event container
|
||||
@@ -514,12 +515,17 @@ header.rolloutgroup.target.message = Messages
|
||||
rollout.group.label.target.truncated = {0} targets has been truncated in the list due the target size limit of {1}
|
||||
|
||||
|
||||
prompt.groups = Groups
|
||||
prompt.number.of.groups = Number of groups
|
||||
prompt.tigger.threshold = Trigger threshold
|
||||
prompt.error.threshold = Error threshold
|
||||
prompt.distribution.set = Distribution Set
|
||||
button.rollout.groups.def.button = Define groups
|
||||
button.rollout.add.group = Add Group
|
||||
caption.configure.rollout = Configure Rollout
|
||||
caption.configure.rollout.groups = Configure Deployment Groups
|
||||
caption.update.rollout = Update Rollout
|
||||
caption.create.rollout = Create new 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
|
||||
@@ -536,6 +542,26 @@ label.target.per.group = Targets per group :
|
||||
message.dist.already.assigned = Distribution {0} is already assigned to target
|
||||
message.error.creating.rollout = Server error. Error creating Rollout. Please contact the administrator
|
||||
message.error.starting.rollout = Server error. Error starting Rollout. Please contact the administrator
|
||||
caption.rollout.group.definition.desc = Define which groups the Rollout should have.
|
||||
header.target.percentage = Target percentage
|
||||
textfield.target.percentage = Target percentage
|
||||
textfield.rollout.group.default.name = Group {0}
|
||||
message.rollout.group.name.invalid = Enter a group name with a length between 1 and 64
|
||||
caption.rollout.tabs.simple = Number of Groups
|
||||
caption.rollout.tabs.advanced = Advanced Group definition
|
||||
caption.rollout.generate.groups = Generate the groups automatically with the specified thresholds.
|
||||
caption.rollout.action.type = Action type
|
||||
message.rollout.remaining.targets.error = Not all targets are addressed
|
||||
textfield.rollout.copied.name = Copy of {0}
|
||||
label.rollout.targets.in.group = {0} in {1}
|
||||
caption.rollout.start.type = Start type
|
||||
caption.rollout.start.manual = Manual
|
||||
caption.rollout.start.manual.desc = The user starts the rollout manually.
|
||||
caption.rollout.start.auto = Auto
|
||||
caption.rollout.start.auto.desc = The rollout is started as soon as it is created.
|
||||
caption.rollout.start.scheduled = Scheduled
|
||||
caption.rollout.start.scheduled.desc = The rollout starts as soon as it is ready and the set time has passed.
|
||||
label.rollout.calculating = Calculating groups ...
|
||||
#rollout - end
|
||||
|
||||
#Menu
|
||||
|
||||
Reference in New Issue
Block a user