Improve Simple UI (#2554)
* feat[Simple-UI]: add action status list * fix[Simple-UI]: various ui issues * chore: add devtool * feat[Simple-UI: add DS metadata * feat[Simple-UI]: add sort in DS view * feat[Simple-UI]: add created at to DS view * style[Simple-UI]: remove id from DS view * feat[Simple-UI]: add rsql filter & url filter * feat[Simple-UI]: if one ds in result show details * feat[Simple-UI]: add filter from url to targetview * feat[Simple-UI]: add link from target details view to DS * feat[Simple-UI]: add sort & version on target view * refacto[Simple-UI]: linkted text area * feat[Simple-UI]: dynamic homepage depending on permissions * feat[Simple-UI]: sort by newest version * feat[Simple-UI]: add target address * feat[Simple-UI]: sort by last modified target * fix[Simple-UI]: securityToken null if no permission * fix[Simple-UI]: green circle on bad update * feat[Simple-UI]: use local date format * docs: update user config * fix: tag filter * feat[Simple-UI]: search on multiple attributes * refacto: rename TargetActions -> TargetActionsHistory * refacto: move TargetActionsHistory to a new file
This commit is contained in:
committed by
GitHub
parent
2b66449ff1
commit
d2b8e74056
@@ -1 +1,7 @@
|
||||
/* Import your application global css files here or add the styles directly to this file */
|
||||
a.nocolor:link {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
a.nocolor:visited {
|
||||
color: inherit;
|
||||
}
|
||||
@@ -9,8 +9,11 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.simple;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.UI;
|
||||
import com.vaadin.flow.component.Unit;
|
||||
import com.vaadin.flow.component.applayout.AppLayout;
|
||||
import com.vaadin.flow.component.applayout.DrawerToggle;
|
||||
@@ -47,9 +50,12 @@ import org.eclipse.hawkbit.ui.simple.view.TargetView;
|
||||
*/
|
||||
public class MainLayout extends AppLayout {
|
||||
|
||||
static final List<Class<? extends Component>> DEFAULT_VIEW_PRIORITY = List.of(TargetView.class, DistributionSetView.class,
|
||||
SoftwareModuleView.class, RolloutView.class);
|
||||
private final transient AuthenticatedUser authenticatedUser;
|
||||
private final AccessAnnotationChecker accessChecker;
|
||||
private H2 viewTitle;
|
||||
private transient Optional<Class<? extends Component>> defaultView;
|
||||
|
||||
public MainLayout(final AuthenticatedUser authenticatedUser, final AccessAnnotationChecker accessChecker) {
|
||||
this.authenticatedUser = authenticatedUser;
|
||||
@@ -68,6 +74,9 @@ public class MainLayout extends AppLayout {
|
||||
Optional.ofNullable(getContent().getClass().getAnnotation(PageTitle.class))
|
||||
.map(PageTitle::value)
|
||||
.orElse(""));
|
||||
if (UI.getCurrent().getActiveViewLocation().getPath().isEmpty()) {
|
||||
defaultView.ifPresent(c -> UI.getCurrent().navigate(c));
|
||||
}
|
||||
}
|
||||
|
||||
private void addHeaderContent() {
|
||||
@@ -81,7 +90,7 @@ public class MainLayout extends AppLayout {
|
||||
}
|
||||
|
||||
private void addDrawerContent() {
|
||||
final H1 appName = new H1("hawkBit UI (Experimental!)");
|
||||
final H1 appName = new H1("hawkBit UI");
|
||||
final HorizontalLayout layout = new HorizontalLayout();
|
||||
layout.setPadding(true);
|
||||
layout.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
||||
@@ -117,6 +126,7 @@ public class MainLayout extends AppLayout {
|
||||
if (accessChecker.hasAccess(AboutView.class)) {
|
||||
nav.addItem(new SideNavItem("About", AboutView.class, VaadinIcon.INFO_CIRCLE.create()));
|
||||
}
|
||||
defaultView = DEFAULT_VIEW_PRIORITY.stream().filter(accessChecker::hasAccess).findFirst();
|
||||
return nav;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Contributors to the Eclipse Foundation
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
|
||||
package org.eclipse.hawkbit.ui.simple;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import com.vaadin.flow.i18n.DefaultI18NProvider;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class SimpleI18NProvider extends DefaultI18NProvider {
|
||||
|
||||
SimpleI18NProvider() {
|
||||
super(Arrays.stream(Locale.getAvailableLocales()).toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Contributors to the Eclipse Foundation
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
|
||||
package org.eclipse.hawkbit.ui.simple;
|
||||
|
||||
import com.vaadin.flow.server.ServiceInitEvent;
|
||||
import com.vaadin.flow.server.VaadinServiceInitListener;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@SpringComponent
|
||||
public class VaadinServiceInit implements VaadinServiceInitListener {
|
||||
|
||||
@Override
|
||||
public void serviceInit(ServiceInitEvent event) {
|
||||
// cache zoneId of client as soon as possible
|
||||
event.getSource().addUIInitListener(uiEvent -> {
|
||||
uiEvent.getUI().getPage().retrieveExtendedClientDetails(details -> {});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Contributors to the Eclipse Foundation
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
|
||||
package org.eclipse.hawkbit.ui.simple.component;
|
||||
|
||||
import static org.eclipse.hawkbit.ui.simple.view.Constants.STATUS;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.vaadin.flow.component.AttachEvent;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.Unit;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
import com.vaadin.flow.component.icon.Icon;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.data.renderer.ComponentRenderer;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionRequestBodyPut;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
|
||||
import org.eclipse.hawkbit.ui.simple.HawkbitMgmtClient;
|
||||
import org.eclipse.hawkbit.ui.simple.view.TargetView;
|
||||
import org.eclipse.hawkbit.ui.simple.view.util.Utils;
|
||||
|
||||
@Slf4j
|
||||
public class TargetActionsHistory extends Grid<TargetActionsHistory.ActionStatusEntry> {
|
||||
|
||||
private final transient HawkbitMgmtClient hawkbitClient;
|
||||
private transient MgmtTarget target;
|
||||
private final TargetView.TargetActionsHistoryLayout.ActionStepsGrid actionStepsGrid;
|
||||
|
||||
public TargetActionsHistory(final HawkbitMgmtClient hawkbitClient, TargetView.TargetActionsHistoryLayout.ActionStepsGrid actionStepsGrid) {
|
||||
this.hawkbitClient = hawkbitClient;
|
||||
setWidthFull();
|
||||
addColumn(new ComponentRenderer<>(ActionStatusEntry::getStatusIcon)).setHeader(STATUS).setAutoWidth(true).setFlexGrow(0);
|
||||
addColumn(ActionStatusEntry::getDistributionSetName).setHeader("Distribution Set").setAutoWidth(true);
|
||||
addColumn(Utils.localDateTimeRenderer(ActionStatusEntry::getLastModifiedAt))
|
||||
.setHeader("Last Modified")
|
||||
.setAutoWidth(true)
|
||||
.setFlexGrow(0)
|
||||
.setComparator(ActionStatusEntry::getLastModifiedAt);
|
||||
|
||||
addColumn(new ComponentRenderer<>(ActionStatusEntry::getForceTypeIcon)).setHeader("Type").setAutoWidth(true).setFlexGrow(0);
|
||||
addColumn(new ComponentRenderer<>(ActionStatusEntry::getActionsLayout)).setHeader("Actions").setAutoWidth(true).setFlexGrow(0);
|
||||
addColumn(new ComponentRenderer<>(ActionStatusEntry::getForceQuitLayout)).setHeader("Force Quit").setAutoWidth(true)
|
||||
.setFlexGrow(0);
|
||||
addItemClickListener(e -> actionStepsGrid.setActionId(e.getItem().action.getId()));
|
||||
this.actionStepsGrid = actionStepsGrid;
|
||||
}
|
||||
|
||||
public void setItem(final MgmtTarget target) {
|
||||
this.target = target;
|
||||
this.actionStepsGrid.setTarget(target);
|
||||
}
|
||||
|
||||
private List<ActionStatusEntry> fetchActions() {
|
||||
return hawkbitClient.getTargetRestApi().getActionHistory(target.getControllerId(), null, 0, 30, null)
|
||||
.getBody()
|
||||
.getContent()
|
||||
.stream()
|
||||
.map(action -> new ActionStatusEntry(action, () -> setItems(fetchActions())))
|
||||
.filter(value -> value.action != null)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttach(AttachEvent attachEvent) {
|
||||
List<ActionStatusEntry> actionStatusEntries = fetchActions();
|
||||
setItems(actionStatusEntries);
|
||||
actionStatusEntries.stream().findFirst().ifPresentOrElse(e -> {
|
||||
// select first action in the list by default
|
||||
asSingleSelect().setValue(e);
|
||||
actionStepsGrid.setActionId(e.action.getId());
|
||||
}, () -> actionStepsGrid.setActionId(null));
|
||||
}
|
||||
|
||||
protected class ActionStatusEntry {
|
||||
|
||||
final MgmtAction action;
|
||||
final Runnable onUpdate;
|
||||
MgmtDistributionSet distributionSet;
|
||||
|
||||
public ActionStatusEntry(final MgmtAction mgmtAction, final Runnable onUpdate) {
|
||||
this.action = hawkbitClient.getActionRestApi().getAction(mgmtAction.getId()).getBody();
|
||||
this.onUpdate = onUpdate;
|
||||
if (action == null) {
|
||||
log.error("Unable to fetch the action with id : {}", mgmtAction.getId());
|
||||
return;
|
||||
}
|
||||
this.action.getLink("distributionset").ifPresent(link -> {
|
||||
try {
|
||||
Long dsId = Long.parseLong(link.getHref().substring(link.getHref().lastIndexOf("/") + 1));
|
||||
this.distributionSet = hawkbitClient.getDistributionSetRestApi().getDistributionSet(dsId).getBody();
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("Error parsing distribution set ID", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isActive() {
|
||||
return action.getStatus().equals(MgmtAction.ACTION_PENDING);
|
||||
}
|
||||
|
||||
private boolean isCancelingOrCanceled() {
|
||||
return action.getType().equals(MgmtAction.ACTION_CANCEL);
|
||||
}
|
||||
|
||||
public Component getStatusIcon() {
|
||||
final HorizontalLayout layout = new HorizontalLayout();
|
||||
final Icon icon;
|
||||
if (isActive()) {
|
||||
if (isCancelingOrCanceled()) {
|
||||
icon = Utils.tooltip(VaadinIcon.ADJUST.create(), "Pending Cancellation");
|
||||
icon.setColor("red");
|
||||
} else {
|
||||
icon = Utils.tooltip(VaadinIcon.ADJUST.create(), "Pending Update");
|
||||
icon.setColor("orange");
|
||||
}// todo getDetailStatus should return an enum from src/main/java/org/eclipse/hawkbit/repository/model/Action.java
|
||||
} else if (action.getType().equals(MgmtAction.ACTION_UPDATE) && action.getDetailStatus().equals("finished")) {
|
||||
icon = Utils.tooltip(VaadinIcon.CHECK_CIRCLE.create(), "Updated");
|
||||
icon.setColor("green");
|
||||
} else {
|
||||
icon = Utils.tooltip(VaadinIcon.CLOSE_CIRCLE.create(), "Canceled");
|
||||
icon.setColor("red");
|
||||
}
|
||||
|
||||
icon.addClassNames(LumoUtility.IconSize.SMALL);
|
||||
layout.add(icon);
|
||||
layout.setWidth(50, Unit.PIXELS);
|
||||
layout.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
||||
return layout;
|
||||
}
|
||||
|
||||
public String getDistributionSetName() {
|
||||
return Optional.ofNullable(distributionSet).map(d -> d.getName() + ":" + d.getVersion()).orElse(
|
||||
"Distribution Set not found");
|
||||
}
|
||||
|
||||
public Long getLastModifiedAt() {
|
||||
return action.getLastModifiedAt();
|
||||
}
|
||||
|
||||
public Icon getForceTypeIcon() {
|
||||
Icon icon = switch (action.getForceType()) {
|
||||
case FORCED -> VaadinIcon.BOLT.create();
|
||||
case TIMEFORCED -> VaadinIcon.USER_CLOCK.create();
|
||||
case SOFT -> VaadinIcon.USER_CHECK.create();
|
||||
case DOWNLOAD_ONLY -> VaadinIcon.DOWNLOAD.create();
|
||||
};
|
||||
return Utils.tooltip(icon, action.getForceType().getName());
|
||||
}
|
||||
|
||||
public HorizontalLayout getActionsLayout() {
|
||||
final HorizontalLayout actionsLayout = new HorizontalLayout();
|
||||
actionsLayout.setSpacing(true);
|
||||
|
||||
final Button cancelButton = Utils.tooltip(new Button(VaadinIcon.CLOSE.create()), "Cancel Action");
|
||||
if (isActive() && !isCancelingOrCanceled()) {
|
||||
cancelButton.addClickListener(e -> {
|
||||
String message = "Are you sure you want to cancel the action ?";
|
||||
promptForConfirmAction(
|
||||
message, onUpdate,
|
||||
() -> hawkbitClient.getTargetRestApi().cancelAction(target.getControllerId(), action.getId(), false))
|
||||
.open();
|
||||
});
|
||||
} else {
|
||||
cancelButton.setEnabled(false);
|
||||
}
|
||||
|
||||
final Button forceButton = Utils.tooltip(new Button(VaadinIcon.BOLT.create()), "Force Action");
|
||||
if (isActive() && !isCancelingOrCanceled() && action.getForceType() != MgmtActionType.FORCED) {
|
||||
forceButton.addClickListener(e -> {
|
||||
String message = "Are you sure you want to force the action ?";
|
||||
promptForConfirmAction(
|
||||
message, onUpdate, () -> {
|
||||
MgmtActionRequestBodyPut setForced = new MgmtActionRequestBodyPut();
|
||||
setForced.setForceType(MgmtActionType.FORCED);
|
||||
hawkbitClient.getTargetRestApi()
|
||||
.updateAction(target.getControllerId(), action.getId(), setForced);
|
||||
}
|
||||
).open();
|
||||
});
|
||||
} else {
|
||||
forceButton.setEnabled(false);
|
||||
}
|
||||
|
||||
actionsLayout.add(cancelButton, forceButton);
|
||||
return actionsLayout;
|
||||
}
|
||||
|
||||
public HorizontalLayout getForceQuitLayout() {
|
||||
final HorizontalLayout forceQuitLayout = new HorizontalLayout();
|
||||
forceQuitLayout.setSpacing(true);
|
||||
forceQuitLayout.setPadding(true);
|
||||
forceQuitLayout.setWidthFull();
|
||||
forceQuitLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
||||
|
||||
final Button forceQuitButton = Utils.tooltip(new Button(VaadinIcon.CLOSE.create()), "Force Cancel");
|
||||
forceQuitButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY_INLINE);
|
||||
|
||||
if (isActive() && isCancelingOrCanceled()) {
|
||||
forceQuitButton.addClickListener(e -> {
|
||||
String message = "Are you sure you want to force cancel the action ?";
|
||||
promptForConfirmAction(
|
||||
message, onUpdate,
|
||||
() -> hawkbitClient.getTargetRestApi().cancelAction(target.getControllerId(), action.getId(), true)).open();
|
||||
});
|
||||
} else {
|
||||
forceQuitButton.setEnabled(false);
|
||||
}
|
||||
|
||||
forceQuitLayout.add(forceQuitButton);
|
||||
return forceQuitLayout;
|
||||
}
|
||||
|
||||
private static ConfirmDialog promptForConfirmAction(String message, Runnable refreshActions, Runnable actionConsumer) {
|
||||
final ConfirmDialog dialog = new ConfirmDialog();
|
||||
dialog.setHeader("Confirm Action");
|
||||
dialog.setText(message);
|
||||
|
||||
dialog.setCancelable(true);
|
||||
dialog.addCancelListener(event -> dialog.close());
|
||||
|
||||
dialog.setConfirmButtonTheme(ButtonVariant.LUMO_ERROR.getVariantName());
|
||||
dialog.setConfirmText("Confirm");
|
||||
dialog.addConfirmListener(event -> {
|
||||
actionConsumer.run();
|
||||
refreshActions.run();
|
||||
dialog.close();
|
||||
});
|
||||
return dialog;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,13 @@
|
||||
package org.eclipse.hawkbit.ui.simple.view;
|
||||
|
||||
// java:S1214 - implementations of Constants interface extends other classes, so if make this class we shall go for static imports
|
||||
// which is not not better
|
||||
// which is not not better
|
||||
@SuppressWarnings("java:S1214")
|
||||
public interface Constants {
|
||||
|
||||
// properties
|
||||
String ID = "Id";
|
||||
String ADDRESS = "Address";
|
||||
String NAME = "Name";
|
||||
String DESCRIPTION = "Description";
|
||||
String VERSION = "Version";
|
||||
@@ -54,7 +55,10 @@ public interface Constants {
|
||||
String CANCEL = "Cancel";
|
||||
String CANCEL_ESC = "Cancel (Esc)";
|
||||
|
||||
String CREATED_AT_DESC = "createdAt:desc";
|
||||
|
||||
String NAME_ASC = "name:asc";
|
||||
String NAME_DESC = "name:desc";
|
||||
|
||||
String NOT_AVAILABLE_NULL = "n/a (null)";
|
||||
}
|
||||
@@ -11,15 +11,17 @@ package org.eclipse.hawkbit.ui.simple.view;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.vaadin.flow.component.grid.GridSortOrder;
|
||||
import com.vaadin.flow.data.provider.SortDirection;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
|
||||
import com.vaadin.flow.component.Component;
|
||||
@@ -64,25 +66,32 @@ public class DistributionSetView extends TableView<MgmtDistributionSet, Long> {
|
||||
public DistributionSetView(final HawkbitMgmtClient hawkbitClient) {
|
||||
super(
|
||||
new DistributionSetFilter(hawkbitClient),
|
||||
new DistributionSetRawFilter(),
|
||||
new SelectionGrid.EntityRepresentation<>(MgmtDistributionSet.class, MgmtDistributionSet::getId) {
|
||||
|
||||
private final DistributionSetDetails details = new DistributionSetDetails(hawkbitClient);
|
||||
|
||||
@Override
|
||||
protected void addColumns(Grid<MgmtDistributionSet> grid) {
|
||||
grid.addColumn(MgmtDistributionSet::getId).setHeader(Constants.ID).setAutoWidth(true);
|
||||
grid.addColumn(MgmtDistributionSet::getName).setHeader(Constants.NAME).setAutoWidth(true);
|
||||
grid.addColumn(MgmtDistributionSet::getVersion).setHeader(Constants.VERSION).setAutoWidth(true);
|
||||
grid.addColumn(MgmtDistributionSet::getTypeName).setHeader(Constants.TYPE).setAutoWidth(true);
|
||||
var createdAtCol = grid.addColumn(Utils.localDateTimeRenderer(MgmtDistributionSet::getCreatedAt)).setHeader(
|
||||
Constants.CREATED_AT).setAutoWidth(true).setKey("createdAt").setSortable(true);
|
||||
grid.addColumn(MgmtDistributionSet::getName).setHeader(Constants.NAME).setAutoWidth(true).setKey("name").setSortable(
|
||||
true);
|
||||
grid.addColumn(MgmtDistributionSet::getVersion).setHeader(Constants.VERSION).setAutoWidth(true).setKey("version")
|
||||
.setSortable(true);
|
||||
grid.addColumn(MgmtDistributionSet::getTypeName).setHeader(Constants.TYPE).setAutoWidth(true).setKey("typename")
|
||||
.setSortable(true);
|
||||
grid.sort(List.of(new GridSortOrder<>(createdAtCol, SortDirection.DESCENDING)));
|
||||
|
||||
grid.setItemDetailsRenderer(new ComponentRenderer<>(
|
||||
() -> details, DistributionSetDetails::setItem));
|
||||
}
|
||||
},
|
||||
(query, rsqlFilter) -> Optional.ofNullable(
|
||||
hawkbitClient.getDistributionSetRestApi()
|
||||
.getDistributionSets(rsqlFilter, query.getOffset(), query.getPageSize(), Constants.NAME_ASC)
|
||||
.getBody())
|
||||
hawkbitClient.getDistributionSetRestApi()
|
||||
.getDistributionSets(rsqlFilter, query.getOffset(), query.getPageSize(), Utils.getSortParam(query
|
||||
.getSortOrders()))
|
||||
.getBody())
|
||||
.stream().flatMap(body -> body.getContent().stream()),
|
||||
e -> new CreateDialog(hawkbitClient).result(),
|
||||
selectionGrid -> {
|
||||
@@ -90,7 +99,7 @@ public class DistributionSetView extends TableView<MgmtDistributionSet, Long> {
|
||||
distributionSet -> hawkbitClient.getDistributionSetRestApi()
|
||||
.deleteDistributionSet(distributionSet.getId()));
|
||||
return CompletableFuture.completedFuture(null);
|
||||
});
|
||||
}, null);
|
||||
}
|
||||
|
||||
private static SelectionGrid<MgmtSoftwareModule, Long> selectSoftwareModuleGrid() {
|
||||
@@ -109,42 +118,66 @@ public class DistributionSetView extends TableView<MgmtDistributionSet, Long> {
|
||||
});
|
||||
}
|
||||
|
||||
private static class DistributionSetFilter implements Filter.Rsql {
|
||||
private static class DistributionSetRawFilter implements Filter.Rsql, Filter.RsqlRw {
|
||||
|
||||
private final TextField name = Utils.textField("Name");
|
||||
|
||||
private DistributionSetRawFilter() {
|
||||
name.setPlaceholder("<rsql filter>");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Component> components() {
|
||||
return List.of(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String filter() {
|
||||
return name.getOptionalValue().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilter(String filter) {
|
||||
name.setValue(filter);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DistributionSetFilter implements Filter.Rsql {
|
||||
|
||||
private final TextField textFilter = Utils.textField("Filter");
|
||||
private final CheckboxGroup<MgmtDistributionSetType> type = new CheckboxGroup<>("Type");
|
||||
private final CheckboxGroup<MgmtTag> tag = new CheckboxGroup<>("Tag");
|
||||
|
||||
private DistributionSetFilter(final HawkbitMgmtClient hawkbitClient) {
|
||||
name.setPlaceholder("<name filter>");
|
||||
textFilter.setPlaceholder("<name/version filter>");
|
||||
type.setItemLabelGenerator(MgmtDistributionSetType::getName);
|
||||
type.setItems(Optional.ofNullable(
|
||||
hawkbitClient.getDistributionSetTypeRestApi()
|
||||
.getDistributionSetTypes(null, 0, 20, Constants.NAME_ASC)
|
||||
.getBody())
|
||||
hawkbitClient.getDistributionSetTypeRestApi()
|
||||
.getDistributionSetTypes(null, 0, 20, Constants.NAME_ASC)
|
||||
.getBody())
|
||||
.map(PagedList::getContent)
|
||||
.orElseGet(Collections::emptyList));
|
||||
tag.setItemLabelGenerator(MgmtTag::getName);
|
||||
tag.setItems(Optional.ofNullable(
|
||||
hawkbitClient.getDistributionSetTagRestApi()
|
||||
.getDistributionSetTags(null, 0, 20, Constants.NAME_ASC)
|
||||
.getBody())
|
||||
hawkbitClient.getDistributionSetTagRestApi()
|
||||
.getDistributionSetTags(null, 0, 20, Constants.NAME_ASC)
|
||||
.getBody())
|
||||
.map(PagedList::getContent)
|
||||
.orElseGet(Collections::emptyList));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Component> components() {
|
||||
return List.of(name, type);
|
||||
return List.of(textFilter, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String filter() {
|
||||
return Filter.filter(
|
||||
Map.of(
|
||||
"name", name.getOptionalValue(),
|
||||
List.of("version", "name"), textFilter.getOptionalValue().map(s -> "*" + s + "*"),
|
||||
"type", type.getSelectedItems().stream().map(MgmtDistributionSetType::getKey).toList(),
|
||||
"tag", tag.getSelectedItems()));
|
||||
"tag", tag.getSelectedItems().stream().map(MgmtTag::getName).toList()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,6 +190,7 @@ public class DistributionSetView extends TableView<MgmtDistributionSet, Long> {
|
||||
private final TextField createdAt = Utils.textField("Created at");
|
||||
private final TextField lastModifiedBy = Utils.textField("Last modified by");
|
||||
private final TextField lastModifiedAt = Utils.textField("Last modified at");
|
||||
private final TextArea metadata = new TextArea("Metadata");
|
||||
private final SelectionGrid<MgmtSoftwareModule, Long> softwareModulesGrid = selectSoftwareModuleGrid();
|
||||
|
||||
private DistributionSetDetails(final HawkbitMgmtClient hawkbitClient) {
|
||||
@@ -164,9 +198,9 @@ public class DistributionSetView extends TableView<MgmtDistributionSet, Long> {
|
||||
|
||||
description.setMinLength(2);
|
||||
Stream.of(
|
||||
description,
|
||||
createdBy, createdAt,
|
||||
lastModifiedBy, lastModifiedAt)
|
||||
description,
|
||||
createdBy, createdAt,
|
||||
lastModifiedBy, lastModifiedAt, metadata)
|
||||
.forEach(field -> {
|
||||
field.setReadOnly(true);
|
||||
add(field);
|
||||
@@ -181,9 +215,14 @@ public class DistributionSetView extends TableView<MgmtDistributionSet, Long> {
|
||||
private void setItem(final MgmtDistributionSet distributionSet) {
|
||||
description.setValue(distributionSet.getDescription());
|
||||
createdBy.setValue(distributionSet.getCreatedBy());
|
||||
createdAt.setValue(new Date(distributionSet.getCreatedAt()).toString());
|
||||
createdAt.setValue(Utils.localDateTimeFromTs(distributionSet.getCreatedAt()));
|
||||
lastModifiedBy.setValue(distributionSet.getLastModifiedBy());
|
||||
lastModifiedAt.setValue(new Date(distributionSet.getLastModifiedAt()).toString());
|
||||
lastModifiedAt.setValue(Utils.localDateTimeFromTs(distributionSet.getLastModifiedAt()));
|
||||
metadata.setValue(Optional.ofNullable(
|
||||
hawkbitClient.getDistributionSetRestApi().getMetadata(distributionSet.getId()).getBody())
|
||||
.map(body -> body.getContent().stream()
|
||||
.map(b -> String.format("%s: %s\n", b.getKey(), b.getValue())).collect(
|
||||
Collectors.joining())).orElse(""));
|
||||
|
||||
softwareModulesGrid.setItems(query -> Optional.ofNullable(
|
||||
hawkbitClient.getDistributionSetRestApi()
|
||||
@@ -214,9 +253,9 @@ public class DistributionSetView extends TableView<MgmtDistributionSet, Long> {
|
||||
"Type",
|
||||
this::readyToCreate,
|
||||
Optional.ofNullable(
|
||||
hawkbitClient.getDistributionSetTypeRestApi()
|
||||
.getDistributionSetTypes(null, 0, 30, Constants.NAME_ASC)
|
||||
.getBody())
|
||||
hawkbitClient.getDistributionSetTypeRestApi()
|
||||
.getDistributionSetTypes(null, 0, 30, Constants.CREATED_AT_DESC)
|
||||
.getBody())
|
||||
.map(body -> body.getContent().toArray(new MgmtDistributionSetType[0]))
|
||||
.orElseGet(() -> new MgmtDistributionSetType[0]));
|
||||
type.focus();
|
||||
@@ -262,15 +301,15 @@ public class DistributionSetView extends TableView<MgmtDistributionSet, Long> {
|
||||
create.addClickListener(e -> {
|
||||
close();
|
||||
final long distributionSetId = Optional.ofNullable(
|
||||
hawkbitClient.getDistributionSetRestApi()
|
||||
.createDistributionSets(
|
||||
List.of((MgmtDistributionSetRequestBodyPost) new MgmtDistributionSetRequestBodyPost()
|
||||
.setType(type.getValue().getKey())
|
||||
.setName(name.getValue())
|
||||
.setVersion(version.getValue())
|
||||
.setDescription(description.getValue())
|
||||
.setRequiredMigrationStep(requiredMigrationStep.getValue())))
|
||||
.getBody())
|
||||
hawkbitClient.getDistributionSetRestApi()
|
||||
.createDistributionSets(
|
||||
List.of((MgmtDistributionSetRequestBodyPost) new MgmtDistributionSetRequestBodyPost()
|
||||
.setType(type.getValue().getKey())
|
||||
.setName(name.getValue())
|
||||
.setVersion(version.getValue())
|
||||
.setDescription(description.getValue())
|
||||
.setRequiredMigrationStep(requiredMigrationStep.getValue())))
|
||||
.getBody())
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
.findFirst()
|
||||
@@ -281,7 +320,7 @@ public class DistributionSetView extends TableView<MgmtDistributionSet, Long> {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"java:S1171", "java:S3599"})
|
||||
@SuppressWarnings({ "java:S1171", "java:S3599" })
|
||||
private static class AddSoftwareModulesDialog extends Utils.BaseDialog<Void> {
|
||||
|
||||
private final transient Set<MgmtSoftwareModule> softwareModules = Collections.synchronizedSet(new HashSet<>());
|
||||
@@ -296,19 +335,22 @@ public class DistributionSetView extends TableView<MgmtDistributionSet, Long> {
|
||||
});
|
||||
|
||||
final Component addRemoveControls = Utils.addRemoveControls(
|
||||
v -> new Utils.BaseDialog<Void>("Add Software Modules") {{
|
||||
final SoftwareModuleView softwareModulesView = new SoftwareModuleView(false, hawkbitClient);
|
||||
add(softwareModulesView);
|
||||
final Button addBtn = new Button("Add");
|
||||
addBtn.addClickListener(e -> {
|
||||
softwareModules.addAll(softwareModulesView.getSelection());
|
||||
softwareModulesGrid.refreshGrid(false);
|
||||
close();
|
||||
});
|
||||
addBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
getFooter().add(addBtn);
|
||||
open();
|
||||
}}.result(),
|
||||
v -> new Utils.BaseDialog<Void>("Add Software Modules") {
|
||||
|
||||
{
|
||||
final SoftwareModuleView softwareModulesView = new SoftwareModuleView(false, hawkbitClient);
|
||||
add(softwareModulesView);
|
||||
final Button addBtn = new Button("Add");
|
||||
addBtn.addClickListener(e -> {
|
||||
softwareModules.addAll(softwareModulesView.getSelection());
|
||||
softwareModulesGrid.refreshGrid(false);
|
||||
close();
|
||||
});
|
||||
addBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
getFooter().add(addBtn);
|
||||
open();
|
||||
}
|
||||
}.result(),
|
||||
v -> {
|
||||
Utils.remove(softwareModulesGrid.getSelectedItems(), softwareModules, MgmtSoftwareModule::getId);
|
||||
softwareModulesGrid.refreshGrid(false);
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
package org.eclipse.hawkbit.ui.simple.view;
|
||||
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -61,7 +60,7 @@ import org.springframework.util.ObjectUtils;
|
||||
@Route(value = "rollouts", layout = MainLayout.class)
|
||||
@RolesAllowed({ "ROLLOUT_READ" })
|
||||
@Uses(Icon.class)
|
||||
@SuppressWarnings({"java:S1171", "java:S3599"})
|
||||
@SuppressWarnings({ "java:S1171", "java:S3599" })
|
||||
public class RolloutView extends TableView<MgmtRolloutResponseBody, Long> {
|
||||
|
||||
public RolloutView(final HawkbitMgmtClient hawkbitClient) {
|
||||
@@ -118,33 +117,45 @@ public class RolloutView extends TableView<MgmtRolloutResponseBody, Long> {
|
||||
|
||||
private void init(final MgmtRolloutResponseBody rollout) {
|
||||
if ("READY".equalsIgnoreCase(rollout.getStatus())) {
|
||||
add(Utils.tooltip(new Button(VaadinIcon.START_COG.create()) {{
|
||||
addClickListener(v -> {
|
||||
hawkbitClient.getRolloutRestApi().start(rollout.getId());
|
||||
refresh();
|
||||
});
|
||||
}}, "Start"));
|
||||
add(Utils.tooltip(new Button(VaadinIcon.START_COG.create()) {
|
||||
|
||||
{
|
||||
addClickListener(v -> {
|
||||
hawkbitClient.getRolloutRestApi().start(rollout.getId());
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
}, "Start"));
|
||||
} else if ("RUNNING".equalsIgnoreCase(rollout.getStatus())) {
|
||||
add(Utils.tooltip(new Button(VaadinIcon.PAUSE.create()) {{
|
||||
addClickListener(v -> {
|
||||
hawkbitClient.getRolloutRestApi().pause(rollout.getId());
|
||||
refresh();
|
||||
});
|
||||
}}, "Pause"));
|
||||
add(Utils.tooltip(new Button(VaadinIcon.PAUSE.create()) {
|
||||
|
||||
{
|
||||
addClickListener(v -> {
|
||||
hawkbitClient.getRolloutRestApi().pause(rollout.getId());
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
}, "Pause"));
|
||||
} else if ("PAUSED".equalsIgnoreCase(rollout.getStatus())) {
|
||||
add(Utils.tooltip(new Button(VaadinIcon.START_COG.create()) {{
|
||||
addClickListener(v -> {
|
||||
hawkbitClient.getRolloutRestApi().resume(rollout.getId());
|
||||
refresh();
|
||||
});
|
||||
}}, "Resume"));
|
||||
add(Utils.tooltip(new Button(VaadinIcon.START_COG.create()) {
|
||||
|
||||
{
|
||||
addClickListener(v -> {
|
||||
hawkbitClient.getRolloutRestApi().resume(rollout.getId());
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
}, "Resume"));
|
||||
}
|
||||
add(Utils.tooltip(new Button(VaadinIcon.TRASH.create()) {{
|
||||
addClickListener(v -> {
|
||||
hawkbitClient.getRolloutRestApi().delete(rollout.getId());
|
||||
grid.getDataProvider().refreshAll();
|
||||
});
|
||||
}}, "Cancel and Remove"));
|
||||
add(Utils.tooltip(new Button(VaadinIcon.TRASH.create()) {
|
||||
|
||||
{
|
||||
addClickListener(v -> {
|
||||
hawkbitClient.getRolloutRestApi().delete(rollout.getId());
|
||||
grid.getDataProvider().refreshAll();
|
||||
});
|
||||
}
|
||||
}, "Cancel and Remove"));
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
@@ -197,11 +208,11 @@ public class RolloutView extends TableView<MgmtRolloutResponseBody, Long> {
|
||||
description.setMinLength(2);
|
||||
groupGrid = createGroupGrid();
|
||||
Stream.of(
|
||||
description,
|
||||
createdBy, createdAt,
|
||||
lastModifiedBy, lastModifiedAt,
|
||||
targetFilter, distributionSet,
|
||||
actonType, startAt)
|
||||
description,
|
||||
createdBy, createdAt,
|
||||
lastModifiedBy, lastModifiedAt,
|
||||
targetFilter, distributionSet,
|
||||
actonType, startAt)
|
||||
.forEach(field -> {
|
||||
field.setReadOnly(true);
|
||||
add(field);
|
||||
@@ -219,9 +230,9 @@ public class RolloutView extends TableView<MgmtRolloutResponseBody, Long> {
|
||||
private void setItem(final MgmtRolloutResponseBody rollout) {
|
||||
description.setValue(rollout.getDescription());
|
||||
createdBy.setValue(rollout.getCreatedBy());
|
||||
createdAt.setValue(new Date(rollout.getCreatedAt()).toString());
|
||||
createdAt.setValue(Utils.localDateTimeFromTs(rollout.getCreatedAt()));
|
||||
lastModifiedBy.setValue(rollout.getLastModifiedBy());
|
||||
lastModifiedAt.setValue(new Date(rollout.getLastModifiedAt()).toString());
|
||||
lastModifiedAt.setValue(Utils.localDateTimeFromTs(rollout.getLastModifiedAt()));
|
||||
targetFilter.setValue(rollout.getTargetFilterQuery());
|
||||
final MgmtDistributionSet distributionSetMgmt = hawkbitClient.getDistributionSetRestApi()
|
||||
.getDistributionSet(rollout.getDistributionSetId()).getBody();
|
||||
@@ -232,18 +243,18 @@ public class RolloutView extends TableView<MgmtRolloutResponseBody, Long> {
|
||||
case SOFT -> Constants.SOFT;
|
||||
case FORCED -> Constants.FORCED;
|
||||
case DOWNLOAD_ONLY -> Constants.DOWNLOAD_ONLY;
|
||||
case TIMEFORCED -> "Scheduled at " + new Date(rollout.getForcetime());
|
||||
case TIMEFORCED -> "Scheduled at " + Utils.localDateTimeFromTs(rollout.getForcetime());
|
||||
});
|
||||
startAt.setValue(ObjectUtils.isEmpty(rollout.getStartAt()) ? "" : new Date(rollout.getStartAt()).toString());
|
||||
startAt.setValue(ObjectUtils.isEmpty(rollout.getStartAt()) ? "" : Utils.localDateTimeFromTs(rollout.getStartAt()));
|
||||
dynamic.setValue(rollout.isDynamic());
|
||||
|
||||
groupGrid.setItems(query -> Optional.ofNullable(
|
||||
hawkbitClient.getRolloutRestApi()
|
||||
.getRolloutGroups(
|
||||
rollout.getId(),
|
||||
null, query.getOffset(), query.getPageSize(),
|
||||
null, "full")
|
||||
.getBody())
|
||||
hawkbitClient.getRolloutRestApi()
|
||||
.getRolloutGroups(
|
||||
rollout.getId(),
|
||||
null, query.getOffset(), query.getPageSize(),
|
||||
null, "full")
|
||||
.getBody())
|
||||
.stream().flatMap(body -> body.getContent().stream())
|
||||
.skip(query.getOffset())
|
||||
.limit(query.getPageSize()));
|
||||
@@ -259,7 +270,8 @@ public class RolloutView extends TableView<MgmtRolloutResponseBody, Long> {
|
||||
grid.addColumn(MgmtRolloutGroupResponseBody::getId).setHeader(Constants.ID).setAutoWidth(true);
|
||||
grid.addColumn(MgmtRolloutGroupResponseBody::getName).setHeader(Constants.NAME).setAutoWidth(true);
|
||||
grid.addColumn(MgmtRolloutGroupResponseBody::getTotalTargets).setHeader(Constants.TARGET_COUNT).setAutoWidth(true);
|
||||
grid.addColumn(MgmtRolloutGroupResponseBody::getTotalTargetsPerStatus).setHeader(Constants.STATS).setAutoWidth(true);
|
||||
grid.addColumn(MgmtRolloutGroupResponseBody::getTotalTargetsPerStatus).setHeader(Constants.STATS).setAutoWidth(
|
||||
true);
|
||||
grid.addColumn(MgmtRolloutGroupResponseBody::getStatus).setHeader(Constants.STATUS).setAutoWidth(true);
|
||||
}
|
||||
});
|
||||
@@ -291,22 +303,21 @@ public class RolloutView extends TableView<MgmtRolloutResponseBody, Long> {
|
||||
"Distribution Set",
|
||||
this::readyToCreate,
|
||||
Optional.ofNullable(
|
||||
hawkbitClient.getDistributionSetRestApi()
|
||||
.getDistributionSets(null, 0, 30, Constants.NAME_ASC)
|
||||
.getBody())
|
||||
hawkbitClient.getDistributionSetRestApi()
|
||||
.getDistributionSets(null, 0, 30, Constants.CREATED_AT_DESC)
|
||||
.getBody())
|
||||
.map(body -> body.getContent().toArray(new MgmtDistributionSet[0]))
|
||||
.orElseGet(() -> new MgmtDistributionSet[0]));
|
||||
distributionSet.setRequiredIndicatorVisible(true);
|
||||
distributionSet.setItemLabelGenerator(distributionSetO ->
|
||||
distributionSetO.getName() + ":" + distributionSetO.getVersion());
|
||||
distributionSet.setItemLabelGenerator(distributionSetO -> distributionSetO.getName() + ":" + distributionSetO.getVersion());
|
||||
distributionSet.setWidthFull();
|
||||
targetFilter = new Select<>(
|
||||
"Target Filter",
|
||||
this::readyToCreate,
|
||||
Optional.ofNullable(
|
||||
hawkbitClient.getTargetFilterQueryRestApi()
|
||||
.getFilters(null, 0, 30, Constants.NAME_ASC, null)
|
||||
.getBody())
|
||||
hawkbitClient.getTargetFilterQueryRestApi()
|
||||
.getFilters(null, 0, 30, Constants.NAME_ASC, null)
|
||||
.getBody())
|
||||
.map(body -> body.getContent().toArray(new MgmtTargetFilterQuery[0]))
|
||||
.orElseGet(() -> new MgmtTargetFilterQuery[0]));
|
||||
targetFilter.setRequiredIndicatorVisible(true);
|
||||
@@ -323,20 +334,18 @@ public class RolloutView extends TableView<MgmtRolloutResponseBody, Long> {
|
||||
startType.setLabel(Constants.START_TYPE);
|
||||
startType.setItems(StartType.values());
|
||||
startType.setValue(StartType.MANUAL);
|
||||
final ComponentRenderer<Component, StartType> startTypeRenderer = new ComponentRenderer<>(startTypeO ->
|
||||
switch (startTypeO) {
|
||||
case MANUAL -> new Text(Constants.MANUAL);
|
||||
case AUTO -> new Text(Constants.AUTO);
|
||||
case SCHEDULED -> startAt;
|
||||
});
|
||||
final ComponentRenderer<Component, StartType> startTypeRenderer = new ComponentRenderer<>(startTypeO -> switch (startTypeO) {
|
||||
case MANUAL -> new Text(Constants.MANUAL);
|
||||
case AUTO -> new Text(Constants.AUTO);
|
||||
case SCHEDULED -> startAt;
|
||||
});
|
||||
startType.setRenderer(startTypeRenderer);
|
||||
startType.addValueChangeListener(e -> startType.setRenderer(startTypeRenderer));
|
||||
startType.setItemLabelGenerator(startTypeO ->
|
||||
switch (startTypeO) {
|
||||
case MANUAL -> Constants.MANUAL;
|
||||
case AUTO -> Constants.AUTO;
|
||||
case SCHEDULED -> "Scheduled" + (startAt.isEmpty() ? "" : " at " + startAt.getValue());
|
||||
});
|
||||
startType.setItemLabelGenerator(startTypeO -> switch (startTypeO) {
|
||||
case MANUAL -> Constants.MANUAL;
|
||||
case AUTO -> Constants.AUTO;
|
||||
case SCHEDULED -> "Scheduled" + (startAt.isEmpty() ? "" : " at " + startAt.getValue());
|
||||
});
|
||||
startType.setWidthFull();
|
||||
|
||||
final Div percentSuffix = new Div();
|
||||
@@ -377,12 +386,8 @@ public class RolloutView extends TableView<MgmtRolloutResponseBody, Long> {
|
||||
}
|
||||
|
||||
private void readyToCreate(final Object v) {
|
||||
final boolean createEnabled = !name.isEmpty() &&
|
||||
!distributionSet.isEmpty() &&
|
||||
!targetFilter.isEmpty() &&
|
||||
!groupNumber.isEmpty() &&
|
||||
!triggerThreshold.isEmpty() &&
|
||||
!errorThreshold.isEmpty();
|
||||
final boolean createEnabled = !name.isEmpty() && !distributionSet.isEmpty() && !targetFilter.isEmpty() && !groupNumber
|
||||
.isEmpty() && !triggerThreshold.isEmpty() && !errorThreshold.isEmpty();
|
||||
if (create.isEnabled() != createEnabled) {
|
||||
create.setEnabled(createEnabled);
|
||||
}
|
||||
@@ -400,16 +405,12 @@ public class RolloutView extends TableView<MgmtRolloutResponseBody, Long> {
|
||||
request.setType(actionType.getValue());
|
||||
if (actionType.getValue() == MgmtActionType.TIMEFORCED) {
|
||||
request.setForcetime(
|
||||
forceTime.isEmpty() ?
|
||||
System.currentTimeMillis() :
|
||||
forceTime.getValue().toEpochSecond(ZoneOffset.UTC) * 1000);
|
||||
forceTime.isEmpty() ? System.currentTimeMillis() : forceTime.getValue().toEpochSecond(ZoneOffset.UTC) * 1000);
|
||||
}
|
||||
switch (startType.getValue()) {
|
||||
case AUTO -> request.setStartAt(System.currentTimeMillis());
|
||||
case SCHEDULED -> request.setStartAt(
|
||||
startAt.isEmpty() ?
|
||||
System.currentTimeMillis() :
|
||||
startAt.getValue().toEpochSecond(ZoneOffset.UTC) * 1000);
|
||||
startAt.isEmpty() ? System.currentTimeMillis() : startAt.getValue().toEpochSecond(ZoneOffset.UTC) * 1000);
|
||||
case MANUAL -> {
|
||||
// do nothing, will be started manually
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -93,9 +92,9 @@ public class SoftwareModuleView extends TableView<MgmtSoftwareModule, Long> {
|
||||
}
|
||||
},
|
||||
(query, rsqlFilter) -> Optional.ofNullable(
|
||||
hawkbitClient.getSoftwareModuleRestApi()
|
||||
.getSoftwareModules(rsqlFilter, query.getOffset(), query.getPageSize(), Constants.NAME_ASC)
|
||||
.getBody())
|
||||
hawkbitClient.getSoftwareModuleRestApi()
|
||||
.getSoftwareModules(rsqlFilter, query.getOffset(), query.getPageSize(), Constants.NAME_ASC)
|
||||
.getBody())
|
||||
.stream().map(PagedList::getContent).flatMap(List::stream),
|
||||
isParent ? v -> new CreateDialog(hawkbitClient).result() : null,
|
||||
isParent ? selectionGrid -> {
|
||||
@@ -132,9 +131,9 @@ public class SoftwareModuleView extends TableView<MgmtSoftwareModule, Long> {
|
||||
name.setPlaceholder("<name filter>");
|
||||
type.setItemLabelGenerator(MgmtSoftwareModuleType::getName);
|
||||
type.setItems(Optional.ofNullable(
|
||||
hawkbitClient.getSoftwareModuleTypeRestApi()
|
||||
.getTypes(null, 0, 20, Constants.NAME_ASC)
|
||||
.getBody())
|
||||
hawkbitClient.getSoftwareModuleTypeRestApi()
|
||||
.getTypes(null, 0, 20, Constants.NAME_ASC)
|
||||
.getBody())
|
||||
.map(PagedList::getContent)
|
||||
.orElseGet(Collections::emptyList));
|
||||
}
|
||||
@@ -184,14 +183,14 @@ public class SoftwareModuleView extends TableView<MgmtSoftwareModule, Long> {
|
||||
private void setItem(final MgmtSoftwareModule softwareModule) {
|
||||
description.setValue(softwareModule.getDescription());
|
||||
createdBy.setValue(softwareModule.getCreatedBy());
|
||||
createdAt.setValue(new Date(softwareModule.getCreatedAt()).toString());
|
||||
createdAt.setValue(Utils.localDateTimeFromTs(softwareModule.getCreatedAt()));
|
||||
lastModifiedBy.setValue(softwareModule.getLastModifiedBy());
|
||||
lastModifiedAt.setValue(new Date(softwareModule.getLastModifiedAt()).toString());
|
||||
lastModifiedAt.setValue(Utils.localDateTimeFromTs(softwareModule.getLastModifiedAt()));
|
||||
|
||||
artifactGrid.setItems(query -> Optional.ofNullable(
|
||||
hawkbitClient.getSoftwareModuleRestApi()
|
||||
.getArtifacts(softwareModule.getId(), null, null)
|
||||
.getBody())
|
||||
hawkbitClient.getSoftwareModuleRestApi()
|
||||
.getArtifacts(softwareModule.getId(), null, null)
|
||||
.getBody())
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
.skip(query.getOffset())
|
||||
@@ -220,9 +219,9 @@ public class SoftwareModuleView extends TableView<MgmtSoftwareModule, Long> {
|
||||
Constants.TYPE,
|
||||
this::readyToCreate,
|
||||
Optional.ofNullable(
|
||||
hawkbitClient.getSoftwareModuleTypeRestApi()
|
||||
.getTypes(null, 0, 30, Constants.NAME_ASC)
|
||||
.getBody())
|
||||
hawkbitClient.getSoftwareModuleTypeRestApi()
|
||||
.getTypes(null, 0, 30, Constants.NAME_ASC)
|
||||
.getBody())
|
||||
.map(body -> body.getContent().toArray(new MgmtSoftwareModuleType[0]))
|
||||
.orElseGet(() -> new MgmtSoftwareModuleType[0]));
|
||||
type.setWidthFull();
|
||||
@@ -253,9 +252,9 @@ public class SoftwareModuleView extends TableView<MgmtSoftwareModule, Long> {
|
||||
if (Boolean.TRUE.equals(createDistributionSet.getValue()) && distType.isEmpty()) {
|
||||
distType.setItems(
|
||||
Optional.ofNullable(
|
||||
hawkbitClient.getDistributionSetTypeRestApi()
|
||||
.getDistributionSetTypes(null, 0, 30, Constants.NAME_ASC)
|
||||
.getBody())
|
||||
hawkbitClient.getDistributionSetTypeRestApi()
|
||||
.getDistributionSetTypes(null, 0, 30, Constants.NAME_ASC)
|
||||
.getBody())
|
||||
.map(body -> body.getContent().toArray(new MgmtDistributionSetType[0]))
|
||||
.orElseGet(() -> new MgmtDistributionSetType[0]));
|
||||
}
|
||||
@@ -286,8 +285,8 @@ public class SoftwareModuleView extends TableView<MgmtSoftwareModule, Long> {
|
||||
}
|
||||
|
||||
private void readyToCreate(final Object v) {
|
||||
final boolean createEnabled = !type.isEmpty() && !name.isEmpty() && !version.isEmpty() &&
|
||||
(!createDistributionSet.getValue() || !distType.isEmpty());
|
||||
final boolean createEnabled = !type.isEmpty() && !name.isEmpty() && !version.isEmpty() && (!createDistributionSet
|
||||
.getValue() || !distType.isEmpty());
|
||||
if (create.isEnabled() != createEnabled) {
|
||||
create.setEnabled(createEnabled);
|
||||
}
|
||||
@@ -297,30 +296,30 @@ public class SoftwareModuleView extends TableView<MgmtSoftwareModule, Long> {
|
||||
create.addClickListener(e -> {
|
||||
close();
|
||||
final long softwareModuleId = Optional.ofNullable(
|
||||
hawkbitClient.getSoftwareModuleRestApi().createSoftwareModules(
|
||||
List.of(new MgmtSoftwareModuleRequestBodyPost()
|
||||
.setType(type.getValue().getKey())
|
||||
.setName(name.getValue())
|
||||
.setVersion(version.getValue())
|
||||
.setVendor(vendor.getValue())
|
||||
.setDescription(description.getValue())
|
||||
.setEncrypted(enableArtifactEncryption.getValue())))
|
||||
.getBody())
|
||||
hawkbitClient.getSoftwareModuleRestApi().createSoftwareModules(
|
||||
List.of(new MgmtSoftwareModuleRequestBodyPost()
|
||||
.setType(type.getValue().getKey())
|
||||
.setName(name.getValue())
|
||||
.setVersion(version.getValue())
|
||||
.setVendor(vendor.getValue())
|
||||
.setDescription(description.getValue())
|
||||
.setEncrypted(enableArtifactEncryption.getValue())))
|
||||
.getBody())
|
||||
.stream().flatMap(Collection::stream)
|
||||
.findFirst()
|
||||
.orElseThrow()
|
||||
.getId();
|
||||
if (Boolean.TRUE.equals(createDistributionSet.getValue())) {
|
||||
final long distributionSetId = Optional.ofNullable(
|
||||
hawkbitClient.getDistributionSetRestApi()
|
||||
.createDistributionSets(
|
||||
List.of((MgmtDistributionSetRequestBodyPost) new MgmtDistributionSetRequestBodyPost()
|
||||
.setType(distType.getValue().getKey())
|
||||
.setName(name.getValue())
|
||||
.setVersion(version.getValue())
|
||||
.setDescription(description.getValue())
|
||||
.setRequiredMigrationStep(distRequiredMigrationStep.getValue())))
|
||||
.getBody())
|
||||
hawkbitClient.getDistributionSetRestApi()
|
||||
.createDistributionSets(
|
||||
List.of((MgmtDistributionSetRequestBodyPost) new MgmtDistributionSetRequestBodyPost()
|
||||
.setType(distType.getValue().getKey())
|
||||
.setName(name.getValue())
|
||||
.setVersion(version.getValue())
|
||||
.setDescription(description.getValue())
|
||||
.setRequiredMigrationStep(distRequiredMigrationStep.getValue())))
|
||||
.getBody())
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
.findFirst()
|
||||
|
||||
@@ -9,11 +9,9 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.simple.view;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -27,6 +25,8 @@ import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.vaadin.flow.data.provider.ListDataProvider;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
|
||||
import com.vaadin.flow.component.AttachEvent;
|
||||
@@ -39,7 +39,6 @@ import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.checkbox.CheckboxGroup;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
|
||||
import com.vaadin.flow.component.datetimepicker.DateTimePicker;
|
||||
import com.vaadin.flow.component.dependency.Uses;
|
||||
import com.vaadin.flow.component.formlayout.FormLayout;
|
||||
@@ -60,11 +59,10 @@ import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.shared.Registration;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtPollStatus;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionRequestBodyPut;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionStatus;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentRequestBody;
|
||||
@@ -78,7 +76,9 @@ import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryReq
|
||||
import org.eclipse.hawkbit.mgmt.json.model.targettype.MgmtTargetType;
|
||||
import org.eclipse.hawkbit.ui.simple.HawkbitMgmtClient;
|
||||
import org.eclipse.hawkbit.ui.simple.MainLayout;
|
||||
import org.eclipse.hawkbit.ui.simple.component.TargetActionsHistory;
|
||||
import org.eclipse.hawkbit.ui.simple.view.util.Filter;
|
||||
import org.eclipse.hawkbit.ui.simple.view.util.LinkedTextArea;
|
||||
import org.eclipse.hawkbit.ui.simple.view.util.SelectionGrid;
|
||||
import org.eclipse.hawkbit.ui.simple.view.util.TableView;
|
||||
import org.eclipse.hawkbit.ui.simple.view.util.Utils;
|
||||
@@ -89,33 +89,46 @@ import org.springframework.util.ObjectUtils;
|
||||
@Route(value = "targets", layout = MainLayout.class)
|
||||
@RolesAllowed({ "TARGET_READ" })
|
||||
@Uses(Icon.class)
|
||||
public class TargetView extends TableView<MgmtTarget, String> {
|
||||
public class TargetView extends TableView<TargetView.TargetWithDs, String> {
|
||||
|
||||
public static final String STATUS = "Status";
|
||||
public static final String UPDATE = "Sync";
|
||||
public static final String CONTROLLER_ID = "Controller Id";
|
||||
public static final String FILTER = "Filter";
|
||||
public static final String TAG = "Tag";
|
||||
|
||||
public TargetView(final HawkbitMgmtClient hawkbitClient) {
|
||||
super(
|
||||
new RawFilter(hawkbitClient), new SimpleFilter(hawkbitClient),
|
||||
new SelectionGrid.EntityRepresentation<>(MgmtTarget.class, MgmtTarget::getControllerId) {
|
||||
new SelectionGrid.EntityRepresentation<>(TargetWithDs.class, TargetWithDs::getControllerId) {
|
||||
|
||||
@Override
|
||||
protected void addColumns(final Grid<MgmtTarget> grid) {
|
||||
protected void addColumns(final Grid<TargetWithDs> grid) {
|
||||
grid.addColumn(new ComponentRenderer<>(TargetStatusCell::new))
|
||||
.setHeader(STATUS)
|
||||
.setAutoWidth(true)
|
||||
.setFlexGrow(0);
|
||||
grid.addColumn(MgmtTarget::getControllerId).setHeader(CONTROLLER_ID).setAutoWidth(true);
|
||||
grid.addColumn(MgmtTarget::getName).setHeader(Constants.NAME).setAutoWidth(true);
|
||||
grid.addColumn(MgmtTarget::getTargetTypeName).setHeader(Constants.TYPE).setAutoWidth(true);
|
||||
.setFlexGrow(0).setKey("lastControllerRequestAt").setSortable(true);
|
||||
grid.addColumn(new ComponentRenderer<>(TargetUpdateStatusCell::new))
|
||||
.setHeader(UPDATE)
|
||||
.setAutoWidth(true)
|
||||
.setFlexGrow(0).setKey("updateStatus").setSortable(true);
|
||||
grid.addColumn(MgmtTarget::getControllerId).setHeader(CONTROLLER_ID).setAutoWidth(true).setKey("id").setSortable(true);
|
||||
grid.addColumn(Utils.localDateTimeRenderer(MgmtTarget::getLastModifiedAt)).setHeader(LAST_MODIFIED_AT).setAutoWidth(
|
||||
true).setKey("lastModifiedAt").setSortable(true);
|
||||
grid.addColumn(MgmtTarget::getName).setHeader(Constants.NAME).setAutoWidth(true).setKey("name").setSortable(true);
|
||||
grid.addColumn(MgmtTarget::getTargetTypeName).setHeader(Constants.TYPE).setAutoWidth(true).setKey("targetType")
|
||||
.setSortable(true);
|
||||
grid.addColumn(TargetWithDs::getDsName).setHeader(Constants.DISTRIBUTION_SET).setAutoWidth(true);
|
||||
grid.addColumn(TargetWithDs::getDsVersion).setHeader(Constants.VERSION).setAutoWidth(true).setKey("installedds")
|
||||
.setSortable(true);
|
||||
}
|
||||
},
|
||||
(query, filter) -> hawkbitClient.getTargetRestApi()
|
||||
.getTargets(filter, query.getOffset(), query.getPageSize(), Constants.NAME_ASC)
|
||||
.getTargets(filter, query.getOffset(), query.getPageSize(), Utils.getSortParam(query.getSortOrders(),
|
||||
"lastModifiedAt:desc"))
|
||||
.getBody()
|
||||
.getContent()
|
||||
.stream(),
|
||||
.stream().map(m -> TargetWithDs.from(hawkbitClient, m)),
|
||||
source -> new RegisterDialog(hawkbitClient).result(),
|
||||
selectionGrid -> {
|
||||
selectionGrid.getSelectedItems()
|
||||
@@ -129,8 +142,8 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
}
|
||||
);
|
||||
|
||||
final Function<SelectionGrid<MgmtTarget, String>, CompletionStage<Void>> assignHandler =
|
||||
source -> new AssignDialog(hawkbitClient, source.getSelectedItems()).result();
|
||||
final Function<SelectionGrid<TargetWithDs, String>, CompletionStage<Void>> assignHandler = source -> new AssignDialog(
|
||||
hawkbitClient, source.getSelectedItems()).result();
|
||||
|
||||
final Button assignBtn = Utils.tooltip(new Button(VaadinIcon.LINK.create()), "Assign");
|
||||
assignBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
@@ -142,15 +155,15 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
|
||||
private final HawkbitMgmtClient hawkbitClient;
|
||||
|
||||
private final TextField controllerId;
|
||||
private final TextField textFilter;
|
||||
private final CheckboxGroup<MgmtTargetType> type;
|
||||
private final CheckboxGroup<MgmtTag> tag;
|
||||
|
||||
private SimpleFilter(final HawkbitMgmtClient hawkbitClient) {
|
||||
this.hawkbitClient = hawkbitClient;
|
||||
|
||||
controllerId = Utils.textField(CONTROLLER_ID);
|
||||
controllerId.setPlaceholder("<controller id filter>");
|
||||
textFilter = Utils.textField(FILTER);
|
||||
textFilter.setPlaceholder("<controller id/name filter>");
|
||||
type = new CheckboxGroup<>(Constants.TYPE);
|
||||
type.setItemLabelGenerator(MgmtTargetType::getName);
|
||||
tag = new CheckboxGroup<>(TAG);
|
||||
@@ -160,13 +173,13 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
@Override
|
||||
public List<Component> components() {
|
||||
final List<Component> components = new LinkedList<>();
|
||||
components.add(controllerId);
|
||||
components.add(textFilter);
|
||||
type.setItems(hawkbitClient.getTargetTypeRestApi().getTargetTypes(null, 0, 20, Constants.NAME_ASC).getBody().getContent());
|
||||
if (!type.getValue().isEmpty()) {
|
||||
if (!((ListDataProvider) type.getDataProvider()).getItems().isEmpty()) {
|
||||
components.add(type);
|
||||
}
|
||||
tag.setItems(hawkbitClient.getTargetTagRestApi().getTargetTags(null, 0, 20, Constants.NAME_ASC).getBody().getContent());
|
||||
if (!tag.isEmpty()) {
|
||||
if (!((ListDataProvider) tag.getDataProvider()).getItems().isEmpty()) {
|
||||
components.add(tag);
|
||||
}
|
||||
return components;
|
||||
@@ -176,15 +189,15 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
public String filter() {
|
||||
return Filter.filter(
|
||||
Map.of(
|
||||
"controllerid", controllerId.getOptionalValue(),
|
||||
List.of("controllerid", "name"), textFilter.getOptionalValue().map(s -> "*" + s + "*"),
|
||||
"targettype.name", type.getSelectedItems().stream().map(MgmtTargetType::getName)
|
||||
.toList(),
|
||||
"tag", tag.getSelectedItems()));
|
||||
"tag", tag.getSelectedItems().stream().map(MgmtTag::getName).toList()));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "java:S1171", "java:S3599" })
|
||||
private static class RawFilter implements Filter.Rsql {
|
||||
private static class RawFilter implements Filter.Rsql, Filter.RsqlRw {
|
||||
|
||||
private final TextField textFilter = new TextField("Raw Filter", "<raw filter>");
|
||||
private final VerticalLayout layout = new VerticalLayout();
|
||||
@@ -209,8 +222,8 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
});
|
||||
savedFilters.setEmptySelectionAllowed(true);
|
||||
savedFilters.setItems(listFilters(hawkbitClient));
|
||||
savedFilters.setItemLabelGenerator(query ->
|
||||
Optional.ofNullable(query).map(MgmtTargetFilterQuery::getName).orElse("<select saved filter>"));
|
||||
savedFilters.setItemLabelGenerator(query -> Optional.ofNullable(query).map(MgmtTargetFilterQuery::getName).orElse(
|
||||
"<select saved filter>"));
|
||||
savedFilters.setWidthFull();
|
||||
|
||||
textFilter.setWidthFull();
|
||||
@@ -237,25 +250,27 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
}
|
||||
|
||||
private ComponentEventListener<ClickEvent<Button>> createBtnListener(HawkbitMgmtClient hawkbitClient) {
|
||||
return e ->
|
||||
new Utils.BaseDialog<Void>("Create New Filter") {{
|
||||
final Button finishBtn = Utils.tooltip(new Button("Save"), "Save (Enter)");
|
||||
final TextField name = Utils.textField(Constants.NAME, e -> finishBtn.setEnabled(!e.getHasValue().isEmpty()));
|
||||
name.focus();
|
||||
finishBtn.addClickShortcut(Key.ENTER);
|
||||
finishBtn.setEnabled(false);
|
||||
finishBtn.addClickListener(e -> {
|
||||
final MgmtTargetFilterQueryRequestBody createRequest = new MgmtTargetFilterQueryRequestBody();
|
||||
createRequest.setName(name.getValue());
|
||||
createRequest.setQuery(textFilter.getValue());
|
||||
hawkbitClient.getTargetFilterQueryRestApi().createFilter(createRequest);
|
||||
savedFilters.setItems(listFilters(hawkbitClient));
|
||||
close();
|
||||
});
|
||||
getFooter().add(finishBtn);
|
||||
add(name);
|
||||
open();
|
||||
}};
|
||||
return e -> new Utils.BaseDialog<Void>("Create New Filter") {
|
||||
|
||||
{
|
||||
final Button finishBtn = Utils.tooltip(new Button("Save"), "Save (Enter)");
|
||||
final TextField name = Utils.textField(Constants.NAME, e -> finishBtn.setEnabled(!e.getHasValue().isEmpty()));
|
||||
name.focus();
|
||||
finishBtn.addClickShortcut(Key.ENTER);
|
||||
finishBtn.setEnabled(false);
|
||||
finishBtn.addClickListener(e -> {
|
||||
final MgmtTargetFilterQueryRequestBody createRequest = new MgmtTargetFilterQueryRequestBody();
|
||||
createRequest.setName(name.getValue());
|
||||
createRequest.setQuery(textFilter.getValue());
|
||||
hawkbitClient.getTargetFilterQueryRestApi().createFilter(createRequest);
|
||||
savedFilters.setItems(listFilters(hawkbitClient));
|
||||
close();
|
||||
});
|
||||
getFooter().add(finishBtn);
|
||||
add(name);
|
||||
open();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ComponentEventListener<ClickEvent<Button>> updateBtnListener(HawkbitMgmtClient hawkbitClient) {
|
||||
@@ -265,34 +280,37 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
return;
|
||||
}
|
||||
|
||||
new Utils.BaseDialog<Void>("Update Filter") {{
|
||||
final Button finishBtn = Utils.tooltip(new Button("Update"), "Update (Enter)");
|
||||
finishBtn.setEnabled(false);
|
||||
new Utils.BaseDialog<Void>("Update Filter") {
|
||||
|
||||
final TextField name = Utils.textField(Constants.NAME, e -> finishBtn.setEnabled(!e.getHasValue().isEmpty()));
|
||||
name.focus();
|
||||
name.setValue(selected.getName());
|
||||
{
|
||||
final Button finishBtn = Utils.tooltip(new Button("Update"), "Update (Enter)");
|
||||
finishBtn.setEnabled(false);
|
||||
|
||||
final TextArea filterValue = new TextArea("Filter Value");
|
||||
filterValue.setReadOnly(true);
|
||||
filterValue.setValue(textFilter.getValue());
|
||||
filterValue.setWidthFull();
|
||||
final TextField name = Utils.textField(Constants.NAME, e -> finishBtn.setEnabled(!e.getHasValue().isEmpty()));
|
||||
name.focus();
|
||||
name.setValue(selected.getName());
|
||||
|
||||
finishBtn.addClickShortcut(Key.ENTER);
|
||||
finishBtn.addClickListener(e -> {
|
||||
final MgmtTargetFilterQueryRequestBody updateRequest = new MgmtTargetFilterQueryRequestBody();
|
||||
updateRequest.setName(name.getValue());
|
||||
updateRequest.setQuery(textFilter.getValue());
|
||||
hawkbitClient.getTargetFilterQueryRestApi().updateFilter(selected.getId(), updateRequest);
|
||||
savedFilters.setItems(listFilters(hawkbitClient));
|
||||
close();
|
||||
});
|
||||
getFooter().add(finishBtn);
|
||||
final TextArea filterValue = new TextArea("Filter Value");
|
||||
filterValue.setReadOnly(true);
|
||||
filterValue.setValue(textFilter.getValue());
|
||||
filterValue.setWidthFull();
|
||||
|
||||
add(name);
|
||||
add(filterValue);
|
||||
open();
|
||||
}};
|
||||
finishBtn.addClickShortcut(Key.ENTER);
|
||||
finishBtn.addClickListener(e -> {
|
||||
final MgmtTargetFilterQueryRequestBody updateRequest = new MgmtTargetFilterQueryRequestBody();
|
||||
updateRequest.setName(name.getValue());
|
||||
updateRequest.setQuery(textFilter.getValue());
|
||||
hawkbitClient.getTargetFilterQueryRestApi().updateFilter(selected.getId(), updateRequest);
|
||||
savedFilters.setItems(listFilters(hawkbitClient));
|
||||
close();
|
||||
});
|
||||
getFooter().add(finishBtn);
|
||||
|
||||
add(name);
|
||||
add(filterValue);
|
||||
open();
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -305,6 +323,11 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
public String filter() {
|
||||
return textFilter.getOptionalValue().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilter(String filter) {
|
||||
textFilter.setValue(filter);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class TargetDetailedView extends TabSheet {
|
||||
@@ -312,26 +335,26 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
private final TargetDetails targetDetails;
|
||||
private final TargetAssignedInstalled targetAssignedInstalled;
|
||||
private final TargetTags targetTags;
|
||||
private final TargetActions targetActions;
|
||||
private final TargetActionsHistoryLayout targetActionsHistoryLayout;
|
||||
|
||||
private TargetDetailedView(final HawkbitMgmtClient hawkbitClient) {
|
||||
targetDetails = new TargetDetails(hawkbitClient);
|
||||
targetAssignedInstalled = new TargetAssignedInstalled(hawkbitClient);
|
||||
targetTags = new TargetTags(hawkbitClient);
|
||||
targetActions = new TargetActions(hawkbitClient);
|
||||
targetActionsHistoryLayout = new TargetActionsHistoryLayout(hawkbitClient);
|
||||
setWidthFull();
|
||||
|
||||
add("Details", targetDetails);
|
||||
add("Assigned / Installed", targetAssignedInstalled);
|
||||
add("Tags", targetTags);
|
||||
add("Action History", targetActions);
|
||||
add("Action History", targetActionsHistoryLayout);
|
||||
}
|
||||
|
||||
private void setItem(final MgmtTarget target) {
|
||||
this.targetDetails.setItem(target);
|
||||
this.targetAssignedInstalled.setItem(target);
|
||||
this.targetTags.setItem(target);
|
||||
this.targetActions.setItem(target);
|
||||
this.targetActionsHistoryLayout.setItem(target);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,6 +369,7 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
private final TextField securityToken = Utils.textField(Constants.SECURITY_TOKEN);
|
||||
private final TextField lastPoll = Utils.textField(Constants.LAST_POLL);
|
||||
private final TextField group = Utils.textField(Constants.GROUP);
|
||||
private final TextField targetAddress = Utils.textField(Constants.ADDRESS);
|
||||
private final TextArea targetAttributes = new TextArea(Constants.ATTRIBUTES);
|
||||
private transient MgmtTarget target;
|
||||
|
||||
@@ -353,11 +377,11 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
this.hawkbitClient = hawkbitClient;
|
||||
description.setMinLength(2);
|
||||
Stream.of(
|
||||
description,
|
||||
createdBy, createdAt,
|
||||
lastModifiedBy, lastModifiedAt,
|
||||
securityToken, lastPoll, targetAttributes, group
|
||||
)
|
||||
description,
|
||||
createdBy, createdAt,
|
||||
lastModifiedBy, lastModifiedAt,
|
||||
securityToken, lastPoll, group, targetAddress, targetAttributes
|
||||
)
|
||||
.forEach(field -> {
|
||||
field.setReadOnly(true);
|
||||
add(field);
|
||||
@@ -375,14 +399,15 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
protected void onAttach(final AttachEvent attachEvent) {
|
||||
description.setValue(target.getDescription() == null ? "N/A" : target.getDescription());
|
||||
createdBy.setValue(target.getCreatedBy());
|
||||
createdAt.setValue(new Date(target.getCreatedAt()).toString());
|
||||
createdAt.setValue(Utils.localDateTimeFromTs(target.getCreatedAt()));
|
||||
lastModifiedBy.setValue(target.getLastModifiedBy());
|
||||
lastModifiedAt.setValue(new Date(target.getLastModifiedAt()).toString());
|
||||
securityToken.setValue(target.getSecurityToken());
|
||||
lastModifiedAt.setValue(Utils.localDateTimeFromTs(target.getLastModifiedAt()));
|
||||
securityToken.setValue(Objects.requireNonNullElse(target.getSecurityToken(), ""));
|
||||
group.setValue(target.getGroup() != null ? target.getGroup() : "");
|
||||
targetAddress.setValue(target.getAddress() != null ? target.getAddress() : "");
|
||||
|
||||
final MgmtPollStatus pollStatus = target.getPollStatus();
|
||||
lastPoll.setValue(pollStatus == null ? NOT_AVAILABLE_NULL : new Date(pollStatus.getLastRequestAt()).toString());
|
||||
lastPoll.setValue(pollStatus == null ? NOT_AVAILABLE_NULL : Utils.localDateTimeFromTs(pollStatus.getLastRequestAt()));
|
||||
final ResponseEntity<MgmtTargetAttributes> response = hawkbitClient.getTargetRestApi().getAttributes(target.getControllerId());
|
||||
if (response.getStatusCode().is2xxSuccessful()) {
|
||||
targetAttributes.setValue(Objects.requireNonNullElse(response.getBody(), Collections.emptyMap()).entrySet().stream()
|
||||
@@ -397,14 +422,12 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
private static class TargetAssignedInstalled extends FormLayout {
|
||||
|
||||
private final transient HawkbitMgmtClient hawkbitClient;
|
||||
private final TextArea assigned = new TextArea("Assigned Distribution Set");
|
||||
private final TextArea installed = new TextArea("Installed Distribution Set");
|
||||
private final LinkedTextArea assigned = new LinkedTextArea("Assigned Distribution Set", "/distribution_sets?");
|
||||
private final LinkedTextArea installed = new LinkedTextArea("Installed Distribution Set", "/distribution_sets?");
|
||||
private transient MgmtTarget target;
|
||||
|
||||
private TargetAssignedInstalled(HawkbitMgmtClient hawkbitClient) {
|
||||
this.hawkbitClient = hawkbitClient;
|
||||
assigned.setReadOnly(true);
|
||||
installed.setReadOnly(true);
|
||||
assigned.setWidthFull();
|
||||
installed.setWidthFull();
|
||||
add(assigned, installed);
|
||||
@@ -421,22 +444,23 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
updateDistributionSetInfo(() -> hawkbitClient.getTargetRestApi().getAssignedDistributionSet(target.getControllerId()), assigned);
|
||||
}
|
||||
|
||||
private void updateDistributionSetInfo(Supplier<ResponseEntity<MgmtDistributionSet>> supplier, TextArea textArea) {
|
||||
private void updateDistributionSetInfo(Supplier<ResponseEntity<MgmtDistributionSet>> supplier, LinkedTextArea textArea) {
|
||||
Optional.ofNullable(supplier.get())
|
||||
.map(ResponseEntity<MgmtDistributionSet>::getBody)
|
||||
.ifPresent(value -> {
|
||||
.ifPresentOrElse(value -> {
|
||||
final String description = """
|
||||
Name: %s
|
||||
Version: %s
|
||||
%s
|
||||
""".replace("\n", System.lineSeparator());
|
||||
textArea.setValue(description.formatted(
|
||||
textArea.setValueWithLink(description.formatted(
|
||||
value.getName(),
|
||||
value.getVersion(),
|
||||
value.getModules().stream().map(module -> module.getTypeName() + ": " + module.getVersion())
|
||||
.collect(Collectors.joining(System.lineSeparator()))
|
||||
));
|
||||
});
|
||||
), "q=id%3D%3D" + value.getId().toString());
|
||||
},
|
||||
() -> textArea.setValueWithLink("", null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,8 +490,8 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
|
||||
private HorizontalLayout buildTagSelectionLayout(HawkbitMgmtClient hawkbitClient) {
|
||||
final Button createTagButton = new Button("Create Tag");
|
||||
createTagButton.addClickListener(event ->
|
||||
new CreateTagDialog(hawkbitClient, () -> tagSelector.setItems(fetchAvailableTags())).result());
|
||||
createTagButton.addClickListener(event -> new CreateTagDialog(hawkbitClient, () -> tagSelector.setItems(fetchAvailableTags()))
|
||||
.result());
|
||||
|
||||
tagSelector.setWidthFull();
|
||||
tagSelector.setItemLabelGenerator(MgmtTag::getName);
|
||||
@@ -541,7 +565,7 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
int offset = 0;
|
||||
do {
|
||||
List<MgmtTag> page = Optional.ofNullable(
|
||||
hawkbitClient.getTargetTagRestApi().getTargetTags(null, offset, 50, Constants.NAME_ASC).getBody())
|
||||
hawkbitClient.getTargetTagRestApi().getTargetTags(null, offset, 50, Constants.NAME_ASC).getBody())
|
||||
.map(PagedList::getContent)
|
||||
.orElse(Collections.emptyList());
|
||||
tags.addAll(page);
|
||||
@@ -552,198 +576,105 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
}
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
private static class TargetActions extends Grid<TargetActions.ActionStatusEntry> {
|
||||
public static class TargetActionsHistoryLayout extends VerticalLayout {
|
||||
|
||||
private final transient HawkbitMgmtClient hawkbitClient;
|
||||
private transient MgmtTarget target;
|
||||
private final TargetActionsHistory targetActionsHistory;
|
||||
|
||||
private TargetActions(final HawkbitMgmtClient hawkbitClient) {
|
||||
this.hawkbitClient = hawkbitClient;
|
||||
setWidthFull();
|
||||
addColumn(new ComponentRenderer<>(ActionStatusEntry::getStatusIcon)).setHeader(STATUS).setAutoWidth(true).setFlexGrow(0);
|
||||
addColumn(ActionStatusEntry::getDistributionSetName).setHeader("Distribution Set").setAutoWidth(true);
|
||||
addColumn(ActionStatusEntry::getLastModifiedAt)
|
||||
.setHeader("Last Modified")
|
||||
.setAutoWidth(true)
|
||||
.setFlexGrow(0)
|
||||
.setComparator(ActionStatusEntry::getLastModifiedAt);
|
||||
addColumn(new ComponentRenderer<>(ActionStatusEntry::getForceTypeIcon)).setHeader("Type").setAutoWidth(true).setFlexGrow(0);
|
||||
addColumn(new ComponentRenderer<>(ActionStatusEntry::getActionsLayout)).setHeader("Actions").setAutoWidth(true).setFlexGrow(0);
|
||||
addColumn(new ComponentRenderer<>(ActionStatusEntry::getForceQuitLayout)).setHeader("Force Quit").setAutoWidth(true).setFlexGrow(0);
|
||||
public TargetActionsHistoryLayout(HawkbitMgmtClient hawkbitMgmtClient) {
|
||||
ActionStepsGrid actionStepsGrid = new ActionStepsGrid(hawkbitMgmtClient);
|
||||
targetActionsHistory = new TargetActionsHistory(hawkbitMgmtClient, actionStepsGrid);
|
||||
add(targetActionsHistory);
|
||||
add(actionStepsGrid);
|
||||
}
|
||||
|
||||
private void setItem(final MgmtTarget target) {
|
||||
this.target = target;
|
||||
public void setItem(MgmtTarget target) {
|
||||
targetActionsHistory.setItem(target);
|
||||
}
|
||||
|
||||
private List<ActionStatusEntry> fetchActions() {
|
||||
return hawkbitClient.getTargetRestApi().getActionHistory(target.getControllerId(), null, 0, 30, null)
|
||||
.getBody()
|
||||
.getContent()
|
||||
.stream()
|
||||
.map(action -> new ActionStatusEntry(action, () -> setItems(fetchActions())))
|
||||
.filter(value -> value.action != null)
|
||||
.toList();
|
||||
}
|
||||
public static class ActionStepsGrid extends Grid<ActionStepsGrid.ActionStepEntry> {
|
||||
|
||||
@Override
|
||||
protected void onAttach(AttachEvent attachEvent) {
|
||||
setItems(fetchActions());
|
||||
}
|
||||
private final transient HawkbitMgmtClient hawkbitClient;
|
||||
private transient MgmtTarget target;
|
||||
private transient Long actionId;
|
||||
|
||||
private class ActionStatusEntry {
|
||||
private ActionStepsGrid(final HawkbitMgmtClient hawkbitClient) {
|
||||
|
||||
final MgmtAction action;
|
||||
final Runnable onUpdate;
|
||||
MgmtDistributionSet distributionSet;
|
||||
this.hawkbitClient = hawkbitClient;
|
||||
setWidthFull();
|
||||
addColumn(new ComponentRenderer<>(ActionStepEntry::getStatusIcon)).setHeader(STATUS).setAutoWidth(true)
|
||||
.setFlexGrow(0);
|
||||
addColumn(Utils.localDateTimeRenderer(ActionStepEntry::getLastModifiedAt)).setHeader("Time")
|
||||
.setAutoWidth(true).setFlexGrow(0).setComparator(ActionStepEntry::getLastModifiedAt);
|
||||
addColumn(new ComponentRenderer<>(ActionStepEntry::getMessage)).setHeader("Message").setAutoWidth(true).setFlexGrow(0);
|
||||
}
|
||||
|
||||
public ActionStatusEntry(final MgmtAction mgmtAction, final Runnable onUpdate) {
|
||||
this.action = hawkbitClient.getActionRestApi().getAction(mgmtAction.getId()).getBody();
|
||||
this.onUpdate = onUpdate;
|
||||
if (action == null) {
|
||||
log.error("Unable to fetch the action with id : {}", mgmtAction.getId());
|
||||
return;
|
||||
private List<ActionStepEntry> fetchActionSteps() {
|
||||
if (actionId == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
this.action.getLink("distributionset").ifPresent(link -> {
|
||||
try {
|
||||
Long dsId = Long.parseLong(link.getHref().substring(link.getHref().lastIndexOf("/") + 1));
|
||||
this.distributionSet = hawkbitClient.getDistributionSetRestApi().getDistributionSet(dsId).getBody();
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("Error parsing distribution set ID", e);
|
||||
return hawkbitClient.getTargetRestApi()
|
||||
.getActionStatusList(target.getControllerId(), actionId, 0, 30, null).getBody().getContent()
|
||||
.stream().map(ActionStepEntry::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttach(AttachEvent attachEvent) {
|
||||
setItems(fetchActionSteps());
|
||||
}
|
||||
|
||||
public void setActionId(Long id) {
|
||||
actionId = id;
|
||||
setItems(fetchActionSteps());
|
||||
}
|
||||
|
||||
public void setTarget(MgmtTarget target) {
|
||||
this.target = target;
|
||||
actionId = null;
|
||||
}
|
||||
|
||||
private static class ActionStepEntry extends Object {
|
||||
|
||||
final MgmtActionStatus status;
|
||||
|
||||
public ActionStepEntry(final MgmtActionStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Long getLastModifiedAt() {
|
||||
return status.getReportedAt();
|
||||
}
|
||||
|
||||
public Component getStatusIcon() {
|
||||
final HorizontalLayout layout = new HorizontalLayout();
|
||||
final Icon icon;
|
||||
|
||||
switch (status.getType()) {
|
||||
case FINISHED -> icon = Utils.iconColored(VaadinIcon.CHECK_CIRCLE, "Finished", "green");
|
||||
case ERROR -> icon = Utils.iconColored(VaadinIcon.CLOSE_CIRCLE, "Error", "red");
|
||||
case WARNING -> icon = Utils.iconColored(VaadinIcon.WARNING, "Warning", "orange");
|
||||
case RUNNING -> icon = Utils.iconColored(VaadinIcon.ADJUST, "Running", "green");
|
||||
case RETRIEVED -> icon = Utils.iconColored(VaadinIcon.CIRCLE_THIN, "Retrieved", "green");
|
||||
case CANCELED -> icon = Utils.iconColored(VaadinIcon.CLOSE_CIRCLE_O, "Canceled", "gray");
|
||||
case CANCELING -> icon = Utils.iconColored(VaadinIcon.CLOSE_CIRCLE, "Cancelling", "brown");
|
||||
case DOWNLOAD -> icon = Utils.iconColored(VaadinIcon.CLOUD_DOWNLOAD_O, "Download", "teal");
|
||||
case DOWNLOADED -> icon = Utils.iconColored(VaadinIcon.CLOUD_DOWNLOAD, "Downloaded", "purple");
|
||||
case WAIT_FOR_CONFIRMATION ->
|
||||
icon = Utils.iconColored(VaadinIcon.QUESTION_CIRCLE, "Wait for confirmation", "coral");
|
||||
default -> icon = Utils.iconColored(VaadinIcon.CIRCLE_THIN, status.getType().getName().toLowerCase(),
|
||||
"black");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isActive() {
|
||||
return action.getStatus().equals(MgmtAction.ACTION_PENDING);
|
||||
}
|
||||
|
||||
private boolean isCancelingOrCanceled() {
|
||||
return action.getType().equals(MgmtAction.ACTION_CANCEL);
|
||||
}
|
||||
|
||||
public Component getStatusIcon() {
|
||||
final HorizontalLayout layout = new HorizontalLayout();
|
||||
final Icon icon;
|
||||
if (isActive()) {
|
||||
if (isCancelingOrCanceled()) {
|
||||
icon = Utils.tooltip(VaadinIcon.ADJUST.create(), "Pending Cancellation");
|
||||
icon.setColor("red");
|
||||
} else {
|
||||
icon = Utils.tooltip(VaadinIcon.ADJUST.create(), "Pending Update");
|
||||
icon.setColor("orange");
|
||||
}
|
||||
} else if (action.getType().equals(MgmtAction.ACTION_UPDATE)) {
|
||||
icon = Utils.tooltip(VaadinIcon.CHECK_CIRCLE.create(), "Updated");
|
||||
icon.setColor("green");
|
||||
} else {
|
||||
icon = Utils.tooltip(VaadinIcon.CLOSE_CIRCLE.create(), "Canceled");
|
||||
icon.setColor("red");
|
||||
icon.addClassNames(LumoUtility.IconSize.SMALL);
|
||||
layout.add(icon);
|
||||
layout.setWidth(50, Unit.PIXELS);
|
||||
layout.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
||||
return layout;
|
||||
}
|
||||
|
||||
icon.addClassNames(LumoUtility.IconSize.SMALL);
|
||||
layout.add(icon);
|
||||
layout.setWidth(50, Unit.PIXELS);
|
||||
layout.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
||||
return layout;
|
||||
}
|
||||
|
||||
public String getDistributionSetName() {
|
||||
return Optional.ofNullable(distributionSet).map(MgmtDistributionSet::getName).orElse("Distribution Set not found");
|
||||
}
|
||||
|
||||
public Instant getLastModifiedAt() {
|
||||
return Instant.ofEpochMilli(action.getLastModifiedAt());
|
||||
}
|
||||
|
||||
public Icon getForceTypeIcon() {
|
||||
Icon icon = switch (action.getForceType()) {
|
||||
case FORCED -> VaadinIcon.BOLT.create();
|
||||
case TIMEFORCED -> VaadinIcon.USER_CLOCK.create();
|
||||
case SOFT -> VaadinIcon.USER_CHECK.create();
|
||||
case DOWNLOAD_ONLY -> VaadinIcon.DOWNLOAD.create();
|
||||
};
|
||||
return Utils.tooltip(icon, action.getForceType().getName());
|
||||
}
|
||||
|
||||
public HorizontalLayout getActionsLayout() {
|
||||
final HorizontalLayout actionsLayout = new HorizontalLayout();
|
||||
actionsLayout.setSpacing(true);
|
||||
|
||||
final Button cancelButton = Utils.tooltip(new Button(VaadinIcon.CLOSE.create()), "Cancel Action");
|
||||
if (isActive() && !isCancelingOrCanceled()) {
|
||||
cancelButton.addClickListener(e -> {
|
||||
String message = "Are you sure you want to cancel the action ?";
|
||||
promptForConfirmAction(
|
||||
message, onUpdate,
|
||||
() -> hawkbitClient.getTargetRestApi().cancelAction(target.getControllerId(), action.getId(), false)).open();
|
||||
});
|
||||
} else {
|
||||
cancelButton.setEnabled(false);
|
||||
public VerticalLayout getMessage() {
|
||||
return new VerticalLayout(status.getMessages().stream().map(Span::new).toArray(Span[]::new));
|
||||
}
|
||||
|
||||
final Button forceButton = Utils.tooltip(new Button(VaadinIcon.BOLT.create()), "Force Action");
|
||||
if (isActive() && !isCancelingOrCanceled() && action.getForceType() != MgmtActionType.FORCED) {
|
||||
forceButton.addClickListener(e -> {
|
||||
String message = "Are you sure you want to force the action ?";
|
||||
promptForConfirmAction(
|
||||
message, onUpdate, () -> {
|
||||
MgmtActionRequestBodyPut setForced = new MgmtActionRequestBodyPut();
|
||||
setForced.setForceType(MgmtActionType.FORCED);
|
||||
hawkbitClient.getTargetRestApi().updateAction(target.getControllerId(), action.getId(), setForced);
|
||||
}
|
||||
).open();
|
||||
});
|
||||
} else {
|
||||
forceButton.setEnabled(false);
|
||||
}
|
||||
|
||||
actionsLayout.add(cancelButton, forceButton);
|
||||
return actionsLayout;
|
||||
}
|
||||
|
||||
public HorizontalLayout getForceQuitLayout() {
|
||||
final HorizontalLayout forceQuitLayout = new HorizontalLayout();
|
||||
forceQuitLayout.setSpacing(true);
|
||||
forceQuitLayout.setPadding(true);
|
||||
forceQuitLayout.setWidthFull();
|
||||
forceQuitLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
||||
|
||||
final Button forceQuitButton = Utils.tooltip(new Button(VaadinIcon.CLOSE.create()), "Force Cancel");
|
||||
forceQuitButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY_INLINE);
|
||||
|
||||
if (isActive() && isCancelingOrCanceled()) {
|
||||
forceQuitButton.addClickListener(e -> {
|
||||
String message = "Are you sure you want to force cancel the action ?";
|
||||
promptForConfirmAction(
|
||||
message, onUpdate,
|
||||
() -> hawkbitClient.getTargetRestApi().cancelAction(target.getControllerId(), action.getId(), true)).open();
|
||||
});
|
||||
} else {
|
||||
forceQuitButton.setEnabled(false);
|
||||
}
|
||||
|
||||
forceQuitLayout.add(forceQuitButton);
|
||||
return forceQuitLayout;
|
||||
}
|
||||
|
||||
private static ConfirmDialog promptForConfirmAction(String message, Runnable refreshActions, Runnable actionConsumer) {
|
||||
final ConfirmDialog dialog = new ConfirmDialog();
|
||||
dialog.setHeader("Confirm Action");
|
||||
dialog.setText(message);
|
||||
|
||||
dialog.setCancelable(true);
|
||||
dialog.addCancelListener(event -> dialog.close());
|
||||
|
||||
dialog.setConfirmButtonTheme(ButtonVariant.LUMO_ERROR.getVariantName());
|
||||
dialog.setConfirmText("Confirm");
|
||||
dialog.addConfirmListener(event -> {
|
||||
actionConsumer.run();
|
||||
refreshActions.run();
|
||||
dialog.close();
|
||||
});
|
||||
return dialog;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -772,7 +703,7 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
type.setWidthFull();
|
||||
type.setEmptySelectionAllowed(true);
|
||||
type.setItemLabelGenerator(item -> item == null ? "" : item.getName());
|
||||
controllerId = Utils.textField(CONTROLLER_ID,e -> register.setEnabled(!e.getHasValue().isEmpty()));
|
||||
controllerId = Utils.textField(FILTER, e -> register.setEnabled(!e.getHasValue().isEmpty()));
|
||||
controllerId.focus();
|
||||
name = Utils.textField(Constants.NAME);
|
||||
name.setWidthFull();
|
||||
@@ -812,7 +743,7 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
request.setTargetType(type.getValue().getId());
|
||||
}
|
||||
hawkbitClient.getTargetRestApi().createTargets(
|
||||
List.of(request))
|
||||
List.of(request))
|
||||
.getBody()
|
||||
.stream()
|
||||
.findFirst()
|
||||
@@ -830,22 +761,21 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
private final DateTimePicker forceTime = new DateTimePicker("Force Time");
|
||||
private final Button assign = new Button("Assign");
|
||||
|
||||
private AssignDialog(final HawkbitMgmtClient hawkbitClient, Set<MgmtTarget> selectedTargets) {
|
||||
private AssignDialog(final HawkbitMgmtClient hawkbitClient, Set<TargetWithDs> selectedTargets) {
|
||||
super("Assign Distribution Set");
|
||||
|
||||
distributionSet = new Select<>(
|
||||
"Distribution Set",
|
||||
this::readyToAssign,
|
||||
Optional.ofNullable(
|
||||
hawkbitClient.getDistributionSetRestApi()
|
||||
.getDistributionSets(null, 0, 30, Constants.NAME_ASC)
|
||||
.getBody())
|
||||
hawkbitClient.getDistributionSetRestApi()
|
||||
.getDistributionSets(null, 0, 500, Constants.CREATED_AT_DESC)
|
||||
.getBody())
|
||||
.map(body -> body.getContent().toArray(new MgmtDistributionSet[0]))
|
||||
.orElseGet(() -> new MgmtDistributionSet[0])
|
||||
);
|
||||
distributionSet.setRequiredIndicatorVisible(true);
|
||||
distributionSet.setItemLabelGenerator(distributionSetO ->
|
||||
distributionSetO.getName() + ":" + distributionSetO.getVersion());
|
||||
distributionSet.setItemLabelGenerator(distributionSetO -> distributionSetO.getName() + ":" + distributionSetO.getVersion());
|
||||
distributionSet.setWidthFull();
|
||||
|
||||
actionType = Utils.actionTypeControls(forceTime);
|
||||
@@ -874,7 +804,7 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
}
|
||||
}
|
||||
|
||||
private void addAssignClickListener(final HawkbitMgmtClient hawkbitClient, final Set<MgmtTarget> selectedTargets) {
|
||||
private void addAssignClickListener(final HawkbitMgmtClient hawkbitClient, final Set<TargetWithDs> selectedTargets) {
|
||||
assign.addClickListener(e -> {
|
||||
close();
|
||||
|
||||
@@ -885,9 +815,7 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
request.setType(actionType.getValue());
|
||||
if (actionType.getValue() == MgmtActionType.TIMEFORCED) {
|
||||
request.setForcetime(
|
||||
forceTime.isEmpty() ?
|
||||
System.currentTimeMillis() :
|
||||
forceTime.getValue().toEpochSecond(ZoneOffset.UTC) * 1000);
|
||||
forceTime.isEmpty() ? System.currentTimeMillis() : forceTime.getValue().toEpochSecond(ZoneOffset.UTC) * 1000);
|
||||
}
|
||||
|
||||
requests.add(request);
|
||||
@@ -945,9 +873,31 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
|
||||
private TargetStatusCell(final MgmtTarget target) {
|
||||
final MgmtPollStatus pollStatus = target.getPollStatus();
|
||||
add(pollStatusIconMapper(pollStatus));
|
||||
setWidth(25, Unit.PIXELS);
|
||||
}
|
||||
|
||||
private Icon pollStatusIconMapper(MgmtPollStatus pollStatus) {
|
||||
final Icon pollIcon;
|
||||
if (pollStatus == null) {
|
||||
pollIcon = Utils.tooltip(VaadinIcon.QUESTION_CIRCLE.create(), "No Poll Status");
|
||||
} else if (pollStatus.isOverdue()) {
|
||||
pollIcon = Utils.tooltip(VaadinIcon.EXCLAMATION_CIRCLE.create(), "Overdue " + Utils.durationFromMillis(pollStatus
|
||||
.getLastRequestAt()));
|
||||
} else {
|
||||
pollIcon = Utils.tooltip(VaadinIcon.CLOCK.create(), "In Time " + Utils.durationFromMillis(pollStatus.getLastRequestAt()));
|
||||
}
|
||||
pollIcon.addClassNames(LumoUtility.IconSize.SMALL);
|
||||
return pollIcon;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TargetUpdateStatusCell extends HorizontalLayout {
|
||||
|
||||
private TargetUpdateStatusCell(final MgmtTarget target) {
|
||||
final String targetUpdateStatus = Optional.ofNullable(target.getUpdateStatus()).orElse("unknown");
|
||||
add(pollStatusIconMapper(pollStatus), targetUpdateStatusMapper(targetUpdateStatus));
|
||||
setWidth(50, Unit.PIXELS);
|
||||
add(targetUpdateStatusMapper(targetUpdateStatus));
|
||||
setWidth(25, Unit.PIXELS);
|
||||
}
|
||||
|
||||
private Icon targetUpdateStatusMapper(final String targetUpdateStatus) {
|
||||
@@ -967,23 +917,39 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
default -> "blue";
|
||||
};
|
||||
|
||||
final Icon statusIcon = Utils.tooltip(icon.create(), targetUpdateStatus);
|
||||
final Icon statusIcon = Utils.tooltip(icon.create(), targetUpdateStatus.replace("_", " "));
|
||||
statusIcon.setColor(color);
|
||||
statusIcon.addClassNames(LumoUtility.IconSize.SMALL);
|
||||
return statusIcon;
|
||||
}
|
||||
}
|
||||
|
||||
private Icon pollStatusIconMapper(MgmtPollStatus pollStatus) {
|
||||
final Icon pollIcon;
|
||||
if (pollStatus == null) {
|
||||
pollIcon = Utils.tooltip(VaadinIcon.QUESTION_CIRCLE.create(), "No Poll Status");
|
||||
} else if (pollStatus.isOverdue()) {
|
||||
pollIcon = Utils.tooltip(VaadinIcon.EXCLAMATION_CIRCLE.create(), "Overdue");
|
||||
} else {
|
||||
pollIcon = Utils.tooltip(VaadinIcon.CLOCK.create(), "In Time");
|
||||
}
|
||||
pollIcon.addClassNames(LumoUtility.IconSize.SMALL);
|
||||
return pollIcon;
|
||||
// todo change /targets api to reduce api calls ?
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class TargetWithDs extends MgmtTarget {
|
||||
|
||||
TargetWithDs() {
|
||||
super();
|
||||
}
|
||||
|
||||
Optional<MgmtDistributionSet> ds;
|
||||
static ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public static TargetWithDs from(final HawkbitMgmtClient hawkbitClient, MgmtTarget target) {
|
||||
TargetWithDs targetWithDs = objectMapper.convertValue(target, TargetWithDs.class);
|
||||
|
||||
targetWithDs.ds = Optional.ofNullable(hawkbitClient.getTargetRestApi().getInstalledDistributionSet(targetWithDs
|
||||
.getControllerId())
|
||||
.getBody());
|
||||
return targetWithDs;
|
||||
}
|
||||
|
||||
public String getDsVersion() {
|
||||
return ds.map(MgmtDistributionSet::getVersion).orElse("");
|
||||
}
|
||||
|
||||
public String getDsName() {
|
||||
return ds.map(MgmtDistributionSet::getName).orElse("");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,13 +28,19 @@ import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
public class Filter extends Div {
|
||||
|
||||
private transient Rsql rsql;
|
||||
private final transient Rsql secondaryRsql;
|
||||
private final transient Rsql primaryRsql;
|
||||
private final transient Div filtersDiv;
|
||||
|
||||
public Filter(final Consumer<String> changeListener, final Rsql primaryRsql, final Rsql secondaryOptionalRsql) {
|
||||
rsql = primaryRsql;
|
||||
this.primaryRsql = primaryRsql;
|
||||
secondaryRsql = secondaryOptionalRsql;
|
||||
|
||||
final HorizontalLayout layout = new HorizontalLayout();
|
||||
|
||||
@@ -42,7 +48,7 @@ public class Filter extends Div {
|
||||
addClassNames(LumoUtility.Padding.Horizontal.NONE, LumoUtility.Padding.Vertical.SMALL,
|
||||
LumoUtility.BoxSizing.BORDER);
|
||||
|
||||
final Div filtersDiv = new Div();
|
||||
filtersDiv = new Div();
|
||||
filtersDiv.setWidthFull();
|
||||
filtersDiv.add(primaryRsql.components());
|
||||
filtersDiv.addClassName(LumoUtility.Gap.SMALL);
|
||||
@@ -70,11 +76,7 @@ public class Filter extends Div {
|
||||
final Button toggleBtn = Utils.tooltip(new Button(VaadinIcon.FLIP_V.create()), "Toggle Search");
|
||||
toggleBtn.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
||||
toggleBtn.addClickListener(e -> {
|
||||
filtersDiv.removeAll();
|
||||
synchronized (this) { // toggle
|
||||
rsql = rsql == primaryRsql ? secondaryOptionalRsql : primaryRsql;
|
||||
}
|
||||
filtersDiv.add(rsql.components());
|
||||
toggle();
|
||||
changeListener.accept(primaryRsql.filter());
|
||||
});
|
||||
layout.add(toggleBtn);
|
||||
@@ -84,34 +86,59 @@ public class Filter extends Div {
|
||||
changeListener.accept(primaryRsql.filter());
|
||||
}
|
||||
|
||||
public static String filter(final Map<String, Object> keyToValues) {
|
||||
final Map<String, Object> normalized =
|
||||
new HashMap<>(keyToValues)
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(e -> {
|
||||
if (e.getValue() instanceof Optional<?> opt) {
|
||||
return opt.isPresent();
|
||||
} else {
|
||||
return e.getValue() != null;
|
||||
}
|
||||
})
|
||||
.filter(e -> !(e.getValue() instanceof Collection<?> coll && coll.isEmpty()))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
private void toggle() {
|
||||
// toggle
|
||||
filtersDiv.removeAll();
|
||||
synchronized (this) {
|
||||
rsql = rsql == primaryRsql ? secondaryRsql : primaryRsql;
|
||||
}
|
||||
filtersDiv.add(rsql.components());
|
||||
}
|
||||
|
||||
public void setFilter(String string, boolean allowToggle) {
|
||||
var otherFilter = rsql == primaryRsql ? secondaryRsql : primaryRsql;
|
||||
Stream<Filter.Rsql> rsqlFIlter;
|
||||
// logic to find the filter to use
|
||||
if (allowToggle) {
|
||||
rsqlFIlter = Stream.of(this.rsql);
|
||||
} else {
|
||||
rsqlFIlter = Stream.of(this.rsql, otherFilter);
|
||||
}
|
||||
rsqlFIlter.filter(RsqlRw.class::isInstance).findFirst().map(RsqlRw.class::cast).ifPresent(f -> {
|
||||
if (f == otherFilter) {
|
||||
toggle();
|
||||
}
|
||||
f.setFilter(string);
|
||||
});
|
||||
}
|
||||
|
||||
public static String filter(final Map<Object, Object> keyToValues) {
|
||||
final Map<Object, Object> normalized = new HashMap<>(keyToValues)
|
||||
.entrySet()
|
||||
.stream()
|
||||
.map(e -> {
|
||||
if (e.getValue() instanceof Optional<?> opt) {
|
||||
e.setValue(opt.orElse(null));
|
||||
}
|
||||
return e;
|
||||
})
|
||||
.filter(e -> !ObjectUtils.isEmpty(e))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
if (normalized.isEmpty()) {
|
||||
return null;
|
||||
} else if (normalized.size() == 1) {
|
||||
return normalized.entrySet().stream()
|
||||
.findFirst().map(e -> filter(e.getKey(), e.getValue())).orElse(null); // never return null!
|
||||
} else {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
normalized.forEach((k, v) -> {
|
||||
if (v instanceof Collection<?>) {
|
||||
sb.append('(').append(filter(k, v)).append(')');
|
||||
} else if (v instanceof Optional<?> opt) {
|
||||
sb.append(filter(k, opt.get()));
|
||||
} else {
|
||||
sb.append(filter(k, v));
|
||||
if (k instanceof Collection<?> keyList) {
|
||||
sb.append('(').append(
|
||||
keyList.stream().map(subKey -> filter((String) subKey, v))
|
||||
.collect(Collectors.joining(" or "))).append(")");
|
||||
} else if (k instanceof String key) {
|
||||
if (v instanceof Collection<?>) {
|
||||
sb.append('(').append(filter(key, v)).append(')');
|
||||
} else {
|
||||
sb.append(filter(key, v));
|
||||
}
|
||||
}
|
||||
sb.append(';');
|
||||
});
|
||||
@@ -158,4 +185,9 @@ public class Filter extends Div {
|
||||
|
||||
String filter();
|
||||
}
|
||||
|
||||
public interface RsqlRw {
|
||||
|
||||
void setFilter(String filter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Contributors to the Eclipse Foundation
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.simple.view.util;
|
||||
|
||||
import com.vaadin.flow.component.card.Card;
|
||||
import com.vaadin.flow.component.card.CardVariant;
|
||||
import com.vaadin.flow.component.html.Anchor;
|
||||
import com.vaadin.flow.component.html.Div;
|
||||
import com.vaadin.flow.component.html.Span;
|
||||
|
||||
public class LinkedTextArea extends Div {
|
||||
|
||||
String queryPrefix;
|
||||
Card card;
|
||||
|
||||
public LinkedTextArea(String title, String queryPrefix) {
|
||||
super();
|
||||
card = new Card();
|
||||
card.setTitle(title);
|
||||
this.queryPrefix = queryPrefix;
|
||||
}
|
||||
|
||||
public void setValueWithLink(String value, String query) {
|
||||
var span = new Span(value);
|
||||
span.setWhiteSpace(WhiteSpace.PRE_WRAP);
|
||||
card.add(span);
|
||||
card.addThemeVariants(CardVariant.LUMO_ELEVATED);
|
||||
if (query != null) {
|
||||
var a = new Anchor(queryPrefix + query, card);
|
||||
a.addClassName("nocolor");
|
||||
add(a);
|
||||
} else {
|
||||
add(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
package org.eclipse.hawkbit.ui.simple.view.util;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
@@ -47,7 +48,11 @@ public class SelectionGrid<T, ID> extends Grid<T> {
|
||||
final Stream<T> fetch = queryFn.apply(query, rsqlFilter);
|
||||
final Set<T> selected = getSelectedItems();
|
||||
if (selected == null || selected.isEmpty()) {
|
||||
return fetch;
|
||||
final List<T> fetchList = fetch.toList();
|
||||
if (fetchList.size() == 1) {
|
||||
this.setDetailsVisible(fetchList.get(0), true);
|
||||
}
|
||||
return fetchList.stream();
|
||||
} else {
|
||||
final Set<ID> selectedIds = new HashSet<>();
|
||||
selected.forEach(next -> selectedIds.add(entityRepresentation.idFn.apply(next)));
|
||||
@@ -61,10 +66,11 @@ public class SelectionGrid<T, ID> extends Grid<T> {
|
||||
} // else externally managed
|
||||
}
|
||||
|
||||
public void setRsqlFilter(final String rsqlFilter) {
|
||||
public void setRsqlFilter(final String rsqlFilter, boolean refreshGrid) {
|
||||
if (!Objects.equals(this.rsqlFilter, rsqlFilter)) {
|
||||
this.rsqlFilter = rsqlFilter;
|
||||
refreshGrid(true);
|
||||
if (refreshGrid)
|
||||
refreshGrid(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.UI;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.html.Div;
|
||||
@@ -28,17 +29,21 @@ import com.vaadin.flow.component.splitlayout.SplitLayoutVariant;
|
||||
import com.vaadin.flow.data.provider.Query;
|
||||
import com.vaadin.flow.data.renderer.ComponentRenderer;
|
||||
import com.vaadin.flow.function.SerializableFunction;
|
||||
import com.vaadin.flow.router.BeforeEnterEvent;
|
||||
import com.vaadin.flow.router.BeforeEnterObserver;
|
||||
import com.vaadin.flow.router.NavigationTrigger;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility;
|
||||
|
||||
import org.eclipse.hawkbit.ui.simple.view.Constants;
|
||||
|
||||
@SuppressWarnings("java:S119") // better readability
|
||||
public class TableView<T, ID> extends Div implements Constants {
|
||||
public class TableView<T, ID> extends Div implements Constants, BeforeEnterObserver {
|
||||
|
||||
private static final String COLOR = "color";
|
||||
private static final String VAR_LUMO_SECONDARY_TEXT_COLOR = "var(--lumo-secondary-text-color)";
|
||||
private static final String VAR_LUMO_PRIMARY_COLOR = "var(--lumo-primary-color)";
|
||||
private static final int DEFAULT_OPEN_POSITION_SIZE = 50;
|
||||
private static final String QUERY_PARAM_FILTER = "q";
|
||||
|
||||
protected SelectionGrid<T, ID> selectionGrid;
|
||||
private final Filter filter;
|
||||
@@ -83,8 +88,14 @@ public class TableView<T, ID> extends Div implements Constants {
|
||||
|
||||
filter = new Filter(
|
||||
(rsqlFilter) -> {
|
||||
selectionGrid.setRsqlFilter(rsqlFilter);
|
||||
closeDetailsPanel();
|
||||
if (rsqlFilter != null) {
|
||||
var queryParameters = UI.getCurrent().getActiveViewLocation()
|
||||
.getQueryParameters()
|
||||
.merging(QUERY_PARAM_FILTER, rsqlFilter);
|
||||
UI.getCurrent().navigate(this.getClass(), queryParameters);
|
||||
}
|
||||
selectionGrid.setRsqlFilter(rsqlFilter, true);
|
||||
}, rsql, alternativeRsql
|
||||
);
|
||||
gridLayout = new VerticalLayout(filter, splitLayout);
|
||||
@@ -147,4 +158,16 @@ public class TableView<T, ID> extends Div implements Constants {
|
||||
return button;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeEnter(BeforeEnterEvent event) {
|
||||
var params = event.getLocation().getQueryParameters();
|
||||
params.getSingleParameter(QUERY_PARAM_FILTER)
|
||||
.ifPresent(f -> {
|
||||
var newPage = event.getTrigger() == NavigationTrigger.UI_NAVIGATE;
|
||||
selectionGrid.setRsqlFilter(f, newPage);
|
||||
filter.setFilter(f, newPage);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -9,14 +9,31 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.simple.view.util;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.ToLongFunction;
|
||||
|
||||
import com.vaadin.flow.component.icon.Icon;
|
||||
import com.vaadin.flow.component.icon.IconFactory;
|
||||
import com.vaadin.flow.data.provider.QuerySortOrder;
|
||||
import com.vaadin.flow.data.provider.SortDirection;
|
||||
import com.vaadin.flow.data.renderer.LocalDateTimeRenderer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
|
||||
import org.eclipse.hawkbit.ui.simple.view.Constants;
|
||||
|
||||
@@ -44,6 +61,7 @@ import com.vaadin.flow.data.renderer.ComponentRenderer;
|
||||
import com.vaadin.flow.data.value.ValueChangeMode;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility;
|
||||
|
||||
@Slf4j
|
||||
public class Utils {
|
||||
|
||||
private Utils() {
|
||||
@@ -117,7 +135,8 @@ public class Utils {
|
||||
return layout;
|
||||
}
|
||||
|
||||
private static <T, ID> ConfirmDialog promptForDeleteConfirmation(Function<SelectionGrid<T, ID>, CompletionStage<Void>> removeHandler, SelectionGrid<T, ID> selectionGrid) {
|
||||
private static <T, ID> ConfirmDialog promptForDeleteConfirmation(Function<SelectionGrid<T, ID>, CompletionStage<Void>> removeHandler,
|
||||
SelectionGrid<T, ID> selectionGrid) {
|
||||
final ConfirmDialog dialog = new ConfirmDialog();
|
||||
dialog.setHeader("Confirm Deletion");
|
||||
dialog.setText("Are you sure you want to delete the selected items? This action cannot be undone.");
|
||||
@@ -139,7 +158,7 @@ public class Utils {
|
||||
public static <T> void remove(final Collection<T> remove, final Set<T> from, final Function<T, ?> idFn) {
|
||||
remove.forEach(toRemove -> {
|
||||
final Object id = idFn.apply(toRemove);
|
||||
for (final Iterator<T> i = from.iterator(); i.hasNext(); ) {
|
||||
for (final Iterator<T> i = from.iterator(); i.hasNext();) {
|
||||
if (idFn.apply(i.next()).equals(id)) {
|
||||
i.remove();
|
||||
}
|
||||
@@ -175,27 +194,31 @@ public class Utils {
|
||||
return component;
|
||||
}
|
||||
|
||||
public static Icon iconColored(final IconFactory component, final String text, final String color) {
|
||||
var icon = tooltip(component.create(), text);
|
||||
icon.setColor(color);
|
||||
return icon;
|
||||
}
|
||||
|
||||
public static Select<MgmtActionType> actionTypeControls(DateTimePicker forceTime) {
|
||||
|
||||
Select<MgmtActionType> actionType = new Select<>();
|
||||
actionType.setLabel(Constants.ACTION_TYPE);
|
||||
actionType.setItems(MgmtActionType.values());
|
||||
actionType.setValue(MgmtActionType.FORCED);
|
||||
final ComponentRenderer<Component, MgmtActionType> actionTypeRenderer = new ComponentRenderer<>(actionTypeO ->
|
||||
switch (actionTypeO) {
|
||||
case SOFT -> new Text(Constants.SOFT);
|
||||
case FORCED -> new Text(Constants.FORCED);
|
||||
case DOWNLOAD_ONLY -> new Text(Constants.DOWNLOAD_ONLY);
|
||||
case TIMEFORCED -> forceTime;
|
||||
});
|
||||
final ComponentRenderer<Component, MgmtActionType> actionTypeRenderer = new ComponentRenderer<>(actionTypeO -> switch (actionTypeO) {
|
||||
case SOFT -> new Text(Constants.SOFT);
|
||||
case FORCED -> new Text(Constants.FORCED);
|
||||
case DOWNLOAD_ONLY -> new Text(Constants.DOWNLOAD_ONLY);
|
||||
case TIMEFORCED -> forceTime;
|
||||
});
|
||||
actionType.addValueChangeListener(e -> actionType.setRenderer(actionTypeRenderer));
|
||||
actionType.setItemLabelGenerator(startTypeO ->
|
||||
switch (startTypeO) {
|
||||
case SOFT -> Constants.SOFT;
|
||||
case FORCED -> Constants.FORCED;
|
||||
case DOWNLOAD_ONLY -> Constants.DOWNLOAD_ONLY;
|
||||
case TIMEFORCED -> "Time Forced at " + (forceTime.isEmpty() ? "" : " " + forceTime.getValue());
|
||||
});
|
||||
actionType.setItemLabelGenerator(startTypeO -> switch (startTypeO) {
|
||||
case SOFT -> Constants.SOFT;
|
||||
case FORCED -> Constants.FORCED;
|
||||
case DOWNLOAD_ONLY -> Constants.DOWNLOAD_ONLY;
|
||||
case TIMEFORCED -> "Time Forced at " + (forceTime.isEmpty() ? "" : " " + forceTime.getValue());
|
||||
});
|
||||
actionType.setWidthFull();
|
||||
return actionType;
|
||||
}
|
||||
@@ -235,4 +258,58 @@ public class Utils {
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static ZoneId getZoneId() {
|
||||
CompletableFuture<ZoneId> zoneId = new CompletableFuture<>();
|
||||
UI.getCurrent().getPage().retrieveExtendedClientDetails(details -> {
|
||||
zoneId.complete(ZoneId.of(details.getTimeZoneId()));
|
||||
});
|
||||
try {
|
||||
return zoneId.get(1, TimeUnit.SECONDS);
|
||||
} catch (final InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (TimeoutException | ExecutionException ignored) {
|
||||
log.warn("failed to get zone");
|
||||
}
|
||||
return ZoneId.systemDefault();
|
||||
}
|
||||
|
||||
public static <G> LocalDateTimeRenderer<G> localDateTimeRenderer(ToLongFunction<G> f) {
|
||||
|
||||
return new LocalDateTimeRenderer<>((e) -> LocalDateTime.ofInstant(Instant.ofEpochMilli(f.applyAsLong(e)), getZoneId()),
|
||||
() -> DateTimeFormatter.ofLocalizedDateTime(
|
||||
FormatStyle.SHORT,
|
||||
FormatStyle.MEDIUM).withLocale(UI.getCurrent().getLocale()));
|
||||
}
|
||||
|
||||
public static String localDateTimeFromTs(long timestamp) {
|
||||
return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), getZoneId()).format(DateTimeFormatter.ofLocalizedDateTime(
|
||||
FormatStyle.SHORT,
|
||||
FormatStyle.MEDIUM).withLocale(UI.getCurrent().getLocale()));
|
||||
}
|
||||
|
||||
public static String getSortParam(List<QuerySortOrder> querySortOrders) {
|
||||
return getSortParam(querySortOrders, null);
|
||||
}
|
||||
|
||||
public static String getSortParam(List<QuerySortOrder> querySortOrders, String defaultSort) {
|
||||
if (!querySortOrders.isEmpty()) {
|
||||
QuerySortOrder firstSort = querySortOrders.get(0);
|
||||
String order = firstSort.getDirection() == SortDirection.ASCENDING ? "asc" : "desc";
|
||||
return String.format("%s:%s", firstSort.getSorted(), order);
|
||||
}
|
||||
return defaultSort;
|
||||
}
|
||||
|
||||
public static String durationFromMillis(Long time) {
|
||||
var duration = Duration.between(Instant.ofEpochMilli(time), Instant.now());
|
||||
var day = duration.toDaysPart();
|
||||
if (day > 2) {
|
||||
return day + "d";
|
||||
}
|
||||
return duration.withNanos(0).toString()
|
||||
.substring(2)
|
||||
.replaceFirst("(^\\d+[HMS]\\d*M*)", "$1")
|
||||
.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,5 +26,7 @@ spring.mustache.check-template-location=false
|
||||
vaadin.launch-browser=true
|
||||
# To improve the performance during development.
|
||||
# For more information https://vaadin.com/docs/flow/spring/tutorial-spring-configuration.html#special-configuration-parameters
|
||||
vaadin.whitelisted-packages=com.vaadin,org.vaadin,dev.hilla,org.eclipse.hawkbit
|
||||
vaadin.allowed-packages=com.vaadin,org.vaadin,dev.hilla,org.eclipse.hawkbit
|
||||
spring.application.name=Simple-UI
|
||||
server.servlet.session.persistent=false
|
||||
### Vaadin end ###
|
||||
Reference in New Issue
Block a user