|
|
|
|
@@ -9,6 +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;
|
|
|
|
|
@@ -17,43 +20,55 @@ import java.util.Map;
|
|
|
|
|
import java.util.Objects;
|
|
|
|
|
import java.util.Optional;
|
|
|
|
|
import java.util.Set;
|
|
|
|
|
import java.time.ZoneOffset;
|
|
|
|
|
import java.util.concurrent.CompletableFuture;
|
|
|
|
|
import java.util.concurrent.CompletionStage;
|
|
|
|
|
import java.util.function.Function;
|
|
|
|
|
import java.util.function.Supplier;
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
import java.util.stream.Stream;
|
|
|
|
|
|
|
|
|
|
import jakarta.annotation.security.RolesAllowed;
|
|
|
|
|
|
|
|
|
|
import com.vaadin.flow.component.AttachEvent;
|
|
|
|
|
import com.vaadin.flow.component.ClickEvent;
|
|
|
|
|
import com.vaadin.flow.component.Component;
|
|
|
|
|
import com.vaadin.flow.component.ComponentEventListener;
|
|
|
|
|
import com.vaadin.flow.component.Key;
|
|
|
|
|
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.ClickEvent;
|
|
|
|
|
import com.vaadin.flow.component.ComponentEventListener;
|
|
|
|
|
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;
|
|
|
|
|
import com.vaadin.flow.component.grid.Grid;
|
|
|
|
|
import com.vaadin.flow.component.html.Input;
|
|
|
|
|
import com.vaadin.flow.component.html.Span;
|
|
|
|
|
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.component.orderedlayout.VerticalLayout;
|
|
|
|
|
import com.vaadin.flow.component.select.Select;
|
|
|
|
|
import com.vaadin.flow.component.tabs.TabSheet;
|
|
|
|
|
import com.vaadin.flow.component.textfield.TextArea;
|
|
|
|
|
import com.vaadin.flow.component.textfield.TextField;
|
|
|
|
|
import com.vaadin.flow.data.renderer.ComponentRenderer;
|
|
|
|
|
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 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.distributionset.MgmtActionType;
|
|
|
|
|
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
|
|
|
|
|
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentRequestBody;
|
|
|
|
|
import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTag;
|
|
|
|
|
import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTagRequestBodyPut;
|
|
|
|
|
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
|
|
|
|
|
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetRequestBody;
|
|
|
|
|
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
|
|
|
|
|
@@ -65,8 +80,9 @@ import org.eclipse.hawkbit.ui.simple.view.util.Filter;
|
|
|
|
|
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;
|
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
import org.springframework.http.ResponseEntity;
|
|
|
|
|
import org.springframework.util.ObjectUtils;
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
|
|
@PageTitle("Targets")
|
|
|
|
|
@Route(value = "targets", layout = MainLayout.class)
|
|
|
|
|
@@ -92,13 +108,14 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
|
|
|
|
grid.addColumn(MgmtTarget::getTargetTypeName).setHeader(Constants.TYPE).setAutoWidth(true);
|
|
|
|
|
|
|
|
|
|
grid.setItemDetailsRenderer(new ComponentRenderer<>(
|
|
|
|
|
() -> new TargetDetails(hawkbitClient), TargetDetails::setItem));
|
|
|
|
|
() -> new TargetDetailedView(hawkbitClient), TargetDetailedView::setItem));
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
(query, filter) -> hawkbitClient.getTargetRestApi()
|
|
|
|
|
.getTargets(
|
|
|
|
|
query.getOffset(), query.getPageSize(), Constants.NAME_ASC,
|
|
|
|
|
filter)
|
|
|
|
|
filter
|
|
|
|
|
)
|
|
|
|
|
.getBody()
|
|
|
|
|
.getContent()
|
|
|
|
|
.stream(),
|
|
|
|
|
@@ -107,7 +124,8 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
|
|
|
|
selectionGrid.getSelectedItems().forEach(toDelete ->
|
|
|
|
|
hawkbitClient.getTargetRestApi().deleteTarget(toDelete.getControllerId()));
|
|
|
|
|
return CompletableFuture.completedFuture(null);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Function<SelectionGrid<MgmtTarget, String>, CompletionStage<Void>> assignHandler = source -> new AssignDialog(
|
|
|
|
|
hawkbitClient, source.getSelectedItems()).result();
|
|
|
|
|
@@ -161,7 +179,8 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
|
|
|
|
"controllerid", controllerId.getOptionalValue(),
|
|
|
|
|
"targettype.name", type.getSelectedItems().stream().map(MgmtTargetType::getName)
|
|
|
|
|
.toList(),
|
|
|
|
|
"tag", tag.getSelectedItems()));
|
|
|
|
|
"tag", tag.getSelectedItems()
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -298,6 +317,34 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static class TargetDetailedView extends TabSheet {
|
|
|
|
|
|
|
|
|
|
private final TargetDetails targetDetails;
|
|
|
|
|
private final TargetAssignedInstalled targetAssignedInstalled;
|
|
|
|
|
private final TargetTags targetTags;
|
|
|
|
|
private final TargetActions targetActions;
|
|
|
|
|
|
|
|
|
|
private TargetDetailedView(HawkbitMgmtClient hawkbitClient) {
|
|
|
|
|
targetDetails = new TargetDetails(hawkbitClient);
|
|
|
|
|
targetAssignedInstalled = new TargetAssignedInstalled(hawkbitClient);
|
|
|
|
|
targetTags = new TargetTags(hawkbitClient);
|
|
|
|
|
targetActions = new TargetActions(hawkbitClient);
|
|
|
|
|
setWidthFull();
|
|
|
|
|
|
|
|
|
|
add("Details", targetDetails);
|
|
|
|
|
add("Assigned / Installed", targetAssignedInstalled);
|
|
|
|
|
add("Tags", targetTags);
|
|
|
|
|
add("Action History", targetActions);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setItem(final MgmtTarget target) {
|
|
|
|
|
this.targetDetails.setItem(target);
|
|
|
|
|
this.targetAssignedInstalled.setItem(target);
|
|
|
|
|
this.targetTags.setItem(target);
|
|
|
|
|
this.targetActions.setItem(target);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static class TargetDetails extends FormLayout {
|
|
|
|
|
|
|
|
|
|
private final transient HawkbitMgmtClient hawkbitClient;
|
|
|
|
|
@@ -308,6 +355,7 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
|
|
|
|
private final TextField lastModifiedAt = Utils.textField(Constants.LAST_MODIFIED_AT);
|
|
|
|
|
private final TextField securityToken = Utils.textField(Constants.SECURITY_TOKEN);
|
|
|
|
|
private final TextArea targetAttributes = new TextArea(Constants.ATTRIBUTES);
|
|
|
|
|
private MgmtTarget target;
|
|
|
|
|
|
|
|
|
|
private TargetDetails(HawkbitMgmtClient hawkbitClient) {
|
|
|
|
|
this.hawkbitClient = hawkbitClient;
|
|
|
|
|
@@ -316,7 +364,8 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
|
|
|
|
description,
|
|
|
|
|
createdBy, createdAt,
|
|
|
|
|
lastModifiedBy, lastModifiedAt,
|
|
|
|
|
securityToken, targetAttributes)
|
|
|
|
|
securityToken, targetAttributes
|
|
|
|
|
)
|
|
|
|
|
.forEach(field -> {
|
|
|
|
|
field.setReadOnly(true);
|
|
|
|
|
add(field);
|
|
|
|
|
@@ -327,6 +376,11 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setItem(final MgmtTarget target) {
|
|
|
|
|
this.target = target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onAttach(AttachEvent attachEvent) {
|
|
|
|
|
description.setValue(target.getDescription() == null ? "N/A" : target.getDescription());
|
|
|
|
|
createdBy.setValue(target.getCreatedBy());
|
|
|
|
|
createdAt.setValue(new Date(target.getCreatedAt()).toString());
|
|
|
|
|
@@ -335,14 +389,384 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
|
|
|
|
securityToken.setValue(target.getSecurityToken());
|
|
|
|
|
var response = hawkbitClient.getTargetRestApi().getAttributes(target.getControllerId());
|
|
|
|
|
if (response.getStatusCode().is2xxSuccessful()) {
|
|
|
|
|
targetAttributes.setValue(Objects.requireNonNullElse(response.getBody(), Collections.emptyMap()).entrySet().stream().map(entry -> entry.getKey() + ": " +
|
|
|
|
|
entry.getValue()).collect(Collectors.joining("\n")));
|
|
|
|
|
targetAttributes.setValue(Objects.requireNonNullElse(response.getBody(), Collections.emptyMap()).entrySet().stream()
|
|
|
|
|
.map(entry -> entry.getKey() + ": " +
|
|
|
|
|
entry.getValue()).collect(Collectors.joining("\n")));
|
|
|
|
|
} else {
|
|
|
|
|
targetAttributes.setValue("Error occurred fetching attributes from server: " + response.getStatusCode());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 MgmtTarget target;
|
|
|
|
|
|
|
|
|
|
private TargetAssignedInstalled(HawkbitMgmtClient hawkbitClient) {
|
|
|
|
|
this.hawkbitClient = hawkbitClient;
|
|
|
|
|
assigned.setReadOnly(true);
|
|
|
|
|
installed.setReadOnly(true);
|
|
|
|
|
assigned.setWidthFull();
|
|
|
|
|
installed.setWidthFull();
|
|
|
|
|
add(assigned, installed);
|
|
|
|
|
setResponsiveSteps(new FormLayout.ResponsiveStep("0", 2));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setItem(final MgmtTarget target) {
|
|
|
|
|
this.target = target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onAttach(AttachEvent attachEvent) {
|
|
|
|
|
updateDistributionSetInfo(
|
|
|
|
|
() -> hawkbitClient.getTargetRestApi().getInstalledDistributionSet(target.getControllerId()),
|
|
|
|
|
installed
|
|
|
|
|
);
|
|
|
|
|
updateDistributionSetInfo(
|
|
|
|
|
() -> hawkbitClient.getTargetRestApi().getAssignedDistributionSet(target.getControllerId()),
|
|
|
|
|
assigned
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void updateDistributionSetInfo(Supplier<ResponseEntity<MgmtDistributionSet>> supplier, TextArea textArea) {
|
|
|
|
|
Optional.ofNullable(supplier.get())
|
|
|
|
|
.map(ResponseEntity<MgmtDistributionSet>::getBody)
|
|
|
|
|
.ifPresent(value -> {
|
|
|
|
|
final String description = """
|
|
|
|
|
Name: %s
|
|
|
|
|
Version: %s
|
|
|
|
|
%s
|
|
|
|
|
""".replaceAll("\n", System.lineSeparator());
|
|
|
|
|
textArea.setValue(description.formatted(
|
|
|
|
|
value.getName(),
|
|
|
|
|
value.getVersion(),
|
|
|
|
|
value.getModules().stream().map(module -> module.getTypeName() + ": " + module.getVersion())
|
|
|
|
|
.collect(Collectors.joining(System.lineSeparator()))
|
|
|
|
|
));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static class TargetTags extends VerticalLayout {
|
|
|
|
|
|
|
|
|
|
private final transient HawkbitMgmtClient hawkbitClient;
|
|
|
|
|
private final ComboBox<MgmtTag> tagSelector = new ComboBox<>(TAG);
|
|
|
|
|
private final HorizontalLayout tagsArea = new HorizontalLayout();
|
|
|
|
|
private Registration changeListener;
|
|
|
|
|
private MgmtTarget target;
|
|
|
|
|
|
|
|
|
|
private TargetTags(HawkbitMgmtClient hawkbitClient) {
|
|
|
|
|
this.hawkbitClient = hawkbitClient;
|
|
|
|
|
setWidthFull();
|
|
|
|
|
setPadding(false);
|
|
|
|
|
setSpacing(true);
|
|
|
|
|
setAlignItems(FlexComponent.Alignment.STRETCH);
|
|
|
|
|
setJustifyContentMode(JustifyContentMode.CENTER);
|
|
|
|
|
|
|
|
|
|
final HorizontalLayout tagSelectorLayout = buildTagSelectionLayout(hawkbitClient);
|
|
|
|
|
add(tagSelectorLayout);
|
|
|
|
|
|
|
|
|
|
tagsArea.setWrap(true);
|
|
|
|
|
tagsArea.setWidthFull();
|
|
|
|
|
add(tagsArea);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private HorizontalLayout buildTagSelectionLayout(HawkbitMgmtClient hawkbitClient) {
|
|
|
|
|
final Button createTagButton = new Button("Create Tag");
|
|
|
|
|
createTagButton.addClickListener(event -> {
|
|
|
|
|
new CreateTagDialog(hawkbitClient, () -> tagSelector.setItems(fetchAvailableTags())).result();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
tagSelector.setWidthFull();
|
|
|
|
|
tagSelector.setItemLabelGenerator(MgmtTag::getName);
|
|
|
|
|
tagSelector.setClearButtonVisible(true);
|
|
|
|
|
tagSelector.setItems(fetchAvailableTags());
|
|
|
|
|
|
|
|
|
|
final HorizontalLayout tagSelectorLayout = new HorizontalLayout(tagSelector, createTagButton);
|
|
|
|
|
tagSelectorLayout.setWidthFull();
|
|
|
|
|
tagSelectorLayout.setSpacing(true);
|
|
|
|
|
tagSelectorLayout.setAlignItems(Alignment.END);
|
|
|
|
|
tagSelectorLayout.setJustifyContentMode(JustifyContentMode.CENTER);
|
|
|
|
|
return tagSelectorLayout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setItem(final MgmtTarget target) {
|
|
|
|
|
this.target = target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onAttach(AttachEvent attachEvent) {
|
|
|
|
|
if (changeListener != null) {
|
|
|
|
|
changeListener.remove();
|
|
|
|
|
}
|
|
|
|
|
changeListener = tagSelector.addValueChangeListener(event -> {
|
|
|
|
|
if (event.getValue() != null) {
|
|
|
|
|
hawkbitClient.getTargetTagRestApi().assignTarget(event.getValue().getId(), target.getControllerId());
|
|
|
|
|
refreshTargetTagsList(target);
|
|
|
|
|
tagSelector.clear();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
refreshTargetTagsList(target);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void refreshTargetTagsList(MgmtTarget target) {
|
|
|
|
|
tagsArea.removeAll();
|
|
|
|
|
|
|
|
|
|
final List<MgmtTag> tagList = Optional.ofNullable(hawkbitClient.getTargetRestApi().getTags(target.getControllerId()).getBody())
|
|
|
|
|
.orElse(Collections.emptyList());
|
|
|
|
|
|
|
|
|
|
for (MgmtTag tag : tagList) {
|
|
|
|
|
tagsArea.add(buildTargetTagBadge(tag, target.getControllerId()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Span buildTargetTagBadge(MgmtTag tag, String controllerId) {
|
|
|
|
|
Button clearButton = new Button(VaadinIcon.CLOSE_SMALL.create());
|
|
|
|
|
clearButton.addThemeVariants(ButtonVariant.LUMO_CONTRAST, ButtonVariant.LUMO_TERTIARY_INLINE);
|
|
|
|
|
clearButton.getStyle().set("margin-inline-start", "var(--lumo-space-xs)");
|
|
|
|
|
clearButton.getElement().setAttribute("aria-label", "Clear filter: " + tag.getName());
|
|
|
|
|
clearButton.getElement().setAttribute("title", "Clear filter: " + tag.getName());
|
|
|
|
|
|
|
|
|
|
Span pill = new Span();
|
|
|
|
|
pill.getElement().getThemeList().add("badge pill");
|
|
|
|
|
pill.getStyle().set("background-color", tag.getColour());
|
|
|
|
|
pill.getStyle().set("margin-inline-end", "var(--lumo-space-xs)");
|
|
|
|
|
|
|
|
|
|
Span badge = new Span(pill, new Span(tag.getName()), clearButton);
|
|
|
|
|
badge.getElement().getThemeList().add("badge contrast pill");
|
|
|
|
|
|
|
|
|
|
clearButton.addClickListener(event -> {
|
|
|
|
|
hawkbitClient.getTargetTagRestApi().unassignTarget(tag.getId(), controllerId);
|
|
|
|
|
badge.getElement().removeFromParent();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return badge;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<MgmtTag> fetchAvailableTags() {
|
|
|
|
|
List<MgmtTag> tags = new ArrayList<>();
|
|
|
|
|
int fetched = 0;
|
|
|
|
|
int offset = 0;
|
|
|
|
|
do {
|
|
|
|
|
List<MgmtTag> page = Optional.ofNullable(
|
|
|
|
|
hawkbitClient.getTargetTagRestApi().getTargetTags(offset, 50, Constants.NAME_ASC, null).getBody())
|
|
|
|
|
.map(PagedList::getContent)
|
|
|
|
|
.orElse(Collections.emptyList());
|
|
|
|
|
tags.addAll(page);
|
|
|
|
|
fetched = page.size();
|
|
|
|
|
offset += fetched;
|
|
|
|
|
} while (fetched > 0);
|
|
|
|
|
return tags;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static class TargetActions extends Grid<TargetActions.ActionStatusEntry> {
|
|
|
|
|
|
|
|
|
|
private final transient HawkbitMgmtClient hawkbitClient;
|
|
|
|
|
private MgmtTarget target;
|
|
|
|
|
|
|
|
|
|
private TargetActions(HawkbitMgmtClient hawkbitClient) {
|
|
|
|
|
this.hawkbitClient = hawkbitClient;
|
|
|
|
|
setWidthFull();
|
|
|
|
|
addColumn(new ComponentRenderer<Component, ActionStatusEntry>(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<Component, ActionStatusEntry>(ActionStatusEntry::getForceTypeIcon)).setHeader("Type")
|
|
|
|
|
.setAutoWidth(true)
|
|
|
|
|
.setFlexGrow(0);
|
|
|
|
|
addColumn(new ComponentRenderer<Component, ActionStatusEntry>(ActionStatusEntry::getActionsLayout)).setHeader("Actions")
|
|
|
|
|
.setAutoWidth(true)
|
|
|
|
|
.setFlexGrow(0);
|
|
|
|
|
addColumn(new ComponentRenderer<Component, ActionStatusEntry>(ActionStatusEntry::getForceQuitLayout)).setHeader("Force Quit")
|
|
|
|
|
.setAutoWidth(true)
|
|
|
|
|
.setFlexGrow(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setItem(final MgmtTarget target) {
|
|
|
|
|
this.target = target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<ActionStatusEntry> fetchActions() {
|
|
|
|
|
return this.hawkbitClient.getTargetRestApi().getActionHistory(target.getControllerId(), 0, 30, null, null)
|
|
|
|
|
.getBody()
|
|
|
|
|
.getContent()
|
|
|
|
|
.stream()
|
|
|
|
|
.map(action -> new ActionStatusEntry(action, () -> setItems(fetchActions())))
|
|
|
|
|
.filter(value -> value.action != null)
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onAttach(AttachEvent attachEvent) {
|
|
|
|
|
setItems(fetchActions());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class ActionStatusEntry {
|
|
|
|
|
|
|
|
|
|
MgmtAction action;
|
|
|
|
|
MgmtDistributionSet distributionSet;
|
|
|
|
|
Runnable onUpdate;
|
|
|
|
|
|
|
|
|
|
public ActionStatusEntry(MgmtAction mgmtAction, Runnable onUpdate) {
|
|
|
|
|
this.action = hawkbitClient.getActionRestApi().getAction(mgmtAction.getId()).getBody();
|
|
|
|
|
this.onUpdate = onUpdate;
|
|
|
|
|
if (action == null) {
|
|
|
|
|
LoggerFactory.getLogger(ActionStatusEntry.class).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) {
|
|
|
|
|
LoggerFactory.getLogger(ActionStatusEntry.class).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() {
|
|
|
|
|
HorizontalLayout layout = new HorizontalLayout();
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String getDistributionSetName() {
|
|
|
|
|
return distributionSet != null ? distributionSet.getName() : "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() {
|
|
|
|
|
HorizontalLayout actionsLayout = new HorizontalLayout();
|
|
|
|
|
actionsLayout.setSpacing(true);
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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() {
|
|
|
|
|
HorizontalLayout forceQuitLayout = new HorizontalLayout();
|
|
|
|
|
forceQuitLayout.setSpacing(true);
|
|
|
|
|
forceQuitLayout.setPadding(true);
|
|
|
|
|
forceQuitLayout.setWidthFull();
|
|
|
|
|
forceQuitLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static class RegisterDialog extends Utils.BaseDialog<Void> {
|
|
|
|
|
|
|
|
|
|
private final Select<MgmtTargetType> type;
|
|
|
|
|
@@ -362,12 +786,15 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
|
|
|
|
.getTargetTypes(0, 30, Constants.NAME_ASC, null)
|
|
|
|
|
.getBody()
|
|
|
|
|
.getContent()
|
|
|
|
|
.toArray(new MgmtTargetType[0]));
|
|
|
|
|
.toArray(new MgmtTargetType[0])
|
|
|
|
|
);
|
|
|
|
|
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(
|
|
|
|
|
CONTROLLER_ID,
|
|
|
|
|
e -> register.setEnabled(!e.getHasValue().isEmpty())
|
|
|
|
|
);
|
|
|
|
|
controllerId.focus();
|
|
|
|
|
name = Utils.textField(Constants.NAME);
|
|
|
|
|
name.setWidthFull();
|
|
|
|
|
@@ -433,7 +860,8 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
|
|
|
|
.getDistributionSets(0, 30, Constants.NAME_ASC, null)
|
|
|
|
|
.getBody())
|
|
|
|
|
.map(body -> body.getContent().toArray(new MgmtDistributionSet[0]))
|
|
|
|
|
.orElseGet(() -> new MgmtDistributionSet[0]));
|
|
|
|
|
.orElseGet(() -> new MgmtDistributionSet[0])
|
|
|
|
|
);
|
|
|
|
|
distributionSet.setRequiredIndicatorVisible(true);
|
|
|
|
|
distributionSet.setItemLabelGenerator(distributionSetO ->
|
|
|
|
|
distributionSetO.getName() + ":" + distributionSetO.getVersion());
|
|
|
|
|
@@ -490,7 +918,52 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static class CreateTagDialog extends Utils.BaseDialog<Void> {
|
|
|
|
|
|
|
|
|
|
private final TextField name;
|
|
|
|
|
private final TextArea description = new TextArea(Constants.DESCRIPTION);
|
|
|
|
|
|
|
|
|
|
private CreateTagDialog(final HawkbitMgmtClient hawkbitClient, Runnable onSuccess) {
|
|
|
|
|
super("Create Tag");
|
|
|
|
|
|
|
|
|
|
FormLayout formLayout = new FormLayout();
|
|
|
|
|
formLayout.setResponsiveSteps(new FormLayout.ResponsiveStep("0", 1));
|
|
|
|
|
final Button create = Utils.tooltip(new Button("Create"), "Create (Enter)");
|
|
|
|
|
final Button cancel = Utils.tooltip(new Button("Cancel"), "Cancel (Esc)");
|
|
|
|
|
|
|
|
|
|
Input colorInput = new Input();
|
|
|
|
|
colorInput.setType("color");
|
|
|
|
|
name = Utils.textField("Tag Name", e -> create.setEnabled(!e.getHasValue().isEmpty()));
|
|
|
|
|
formLayout.add(name);
|
|
|
|
|
formLayout.add(description);
|
|
|
|
|
formLayout.addFormItem(colorInput, "Color Selection");
|
|
|
|
|
add(formLayout);
|
|
|
|
|
|
|
|
|
|
create.setEnabled(false);
|
|
|
|
|
create.addClickShortcut(Key.ENTER);
|
|
|
|
|
create.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
|
|
|
|
create.addClickListener(e -> {
|
|
|
|
|
hawkbitClient.getTargetTagRestApi().createTargetTags(List.of(
|
|
|
|
|
new MgmtTagRequestBodyPut()
|
|
|
|
|
.setName(name.getValue())
|
|
|
|
|
.setDescription(description.getValue())
|
|
|
|
|
.setColour(colorInput.getValue())
|
|
|
|
|
));
|
|
|
|
|
onSuccess.run();
|
|
|
|
|
close();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
cancel.addClickListener(e -> close());
|
|
|
|
|
cancel.addClickShortcut(Key.ESCAPE);
|
|
|
|
|
|
|
|
|
|
getFooter().add(cancel);
|
|
|
|
|
getFooter().add(create);
|
|
|
|
|
open();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static class TargetStatusCell extends HorizontalLayout {
|
|
|
|
|
|
|
|
|
|
private TargetStatusCell(MgmtTarget target) {
|
|
|
|
|
MgmtPollStatus pollStatus = target.getPollStatus();
|
|
|
|
|
String targetUpdateStatus = Optional.ofNullable(target.getUpdateStatus()).orElse("unknown");
|
|
|
|
|
|