All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.imunity.furms.ui.views.site.connection.PendingRequestsView Maven / Gradle / Ivy

There is a newer version: 4.3.1
Show newest version
/*
 * Copyright (c) 2020 Bixbit s.c. All rights reserved.
 * See LICENSE file for licensing information.
 */

package io.imunity.furms.ui.views.site.connection;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.grid.ColumnTextAlign;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.html.Paragraph;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.progressbar.ProgressBar;
import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.router.Route;
import io.imunity.furms.api.site_agent_pending_message.SiteAgentConnectionService;
import io.imunity.furms.domain.site_agent.CorrelationId;
import io.imunity.furms.domain.sites.SiteId;
import io.imunity.furms.ui.components.DenseGrid;
import io.imunity.furms.ui.components.FurmsDialog;
import io.imunity.furms.ui.components.FurmsViewComponent;
import io.imunity.furms.ui.components.GridActionMenu;
import io.imunity.furms.ui.components.GridActionsButtonLayout;
import io.imunity.furms.ui.components.IconButton;
import io.imunity.furms.ui.components.MenuButton;
import io.imunity.furms.ui.components.PageTitle;
import io.imunity.furms.ui.components.ViewHeaderLayout;
import io.imunity.furms.ui.components.administrators.SearchLayout;
import io.imunity.furms.ui.user_context.UIContext;
import io.imunity.furms.ui.views.site.SiteAdminMenu;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.invoke.MethodHandles;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static com.vaadin.flow.component.icon.VaadinIcon.ANGLE_DOWN;
import static com.vaadin.flow.component.icon.VaadinIcon.ANGLE_RIGHT;
import static com.vaadin.flow.component.icon.VaadinIcon.PLAY;
import static com.vaadin.flow.component.icon.VaadinIcon.REFRESH;
import static com.vaadin.flow.component.icon.VaadinIcon.TRASH;
import static com.vaadin.flow.component.orderedlayout.FlexComponent.Alignment.CENTER;
import static io.imunity.furms.ui.utils.NotificationUtils.showErrorNotification;
import static io.imunity.furms.ui.utils.ResourceGetter.getCurrentResourceId;
import static io.imunity.furms.ui.utils.VaadinExceptionHandler.handleExceptions;
import static io.imunity.furms.utils.UTCTimeUtils.convertToZoneTime;

@Route(value = "site/admin/pending/requests", layout = SiteAdminMenu.class)
@PageTitle(key = "view.site-admin.pending-requests.page.title")
public class PendingRequestsView extends FurmsViewComponent {
	private final static Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
	private final static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");

	private final SiteAgentConnectionService siteAgentConnectionService;

	private final ZoneId browserZoneId;
	private final SiteId siteId;
	private final Grid grid;
	private final SearchLayout searchLayout;

	public PendingRequestsView(SiteAgentConnectionService siteAgentConnectionService) {
		UI ui = UI.getCurrent();
		this.siteId = new SiteId(getCurrentResourceId());
		this.siteAgentConnectionService = siteAgentConnectionService;
		this.searchLayout = new SearchLayout();
		searchLayout.addValueChangeGridReloader(this::loadGrid);
		this.browserZoneId = UIContext.getCurrent().getZone();
		Map checkboxes = new HashMap<>();
		grid = createPendingMessagesGrid(checkboxes, createMainContextMenu(checkboxes));

		VerticalLayout siteConnectionLayout = createSiteConnectionLayout(ui, siteId);

		ViewHeaderLayout viewHeaderLayout = new ViewHeaderLayout(
			getTranslation("view.site-admin.pending-requests.page.header")
		);

		getContent().add(viewHeaderLayout, siteConnectionLayout, searchLayout, grid);
		loadGrid();
	}

	private boolean rowContains(PendingMessageGridModel row, String value, SearchLayout searchLayout) {
		String lowerCaseValue = value.toLowerCase();
		return searchLayout.getSearchText().isEmpty()
			|| row.operationType.toLowerCase().contains(lowerCaseValue)
			|| row.status.toLowerCase().contains(lowerCaseValue)
			|| String.valueOf(row.sentAt).contains(lowerCaseValue)
			|| String.valueOf(row.ackAt).contains(lowerCaseValue)
			|| String.valueOf(row.retryAmount).contains(lowerCaseValue);
	}

	private Component createMainContextMenu(Map checkboxes) {
		GridActionMenu contextMenu = new GridActionMenu();
		Dialog retryConfirmDialog = createMainConfirmDialog(
			checkboxes,
			correlationId -> siteAgentConnectionService.retry(siteId, correlationId),
			getTranslation("view.site-admin.pending-requests.page.dialog.retry.confirmation")
		);
		contextMenu.addItem(new MenuButton(
				getTranslation("view.site-admin.pending-requests.page.context-menu.retry"), PLAY),
			event -> retryConfirmDialog.open()
		);

		Dialog deleteConfirmDialog = createMainConfirmDialog(
			checkboxes,
			correlationId -> siteAgentConnectionService.delete(siteId, correlationId),
			getTranslation("view.site-admin.pending-requests.page.dialog.delete.confirmation"));
		contextMenu.addItem(new MenuButton(
				getTranslation("view.site-admin.pending-requests.page.context-menu.delete"), TRASH),
			event -> deleteConfirmDialog.open()
		);
		return contextMenu.getTarget();
	}

	private Dialog createMainConfirmDialog(Map checkboxes, Consumer consumer, String message) {
		FurmsDialog furmsDialog = new FurmsDialog(message);
		furmsDialog.addConfirmButtonClickListener(event -> {
			try {
				checkboxes.entrySet().stream()
					.filter(x -> x.getValue().getValue())
					.forEach(x -> consumer.accept(x.getKey()));
			} catch (Exception e){
				LOG.warn("Error: ", e);
				showErrorNotification(getTranslation("base.error.message"));
			}
			loadGrid();
		});
		return furmsDialog;
	}

	private Grid createPendingMessagesGrid(Map checkboxes, Component mainContextMenu) {
		Grid grid;
		grid = new DenseGrid<>(PendingMessageGridModel.class);

		grid.addComponentColumn(pendingMessageGridModel -> {
			Checkbox checkbox = new Checkbox();
			checkboxes.put(pendingMessageGridModel.id, checkbox);
			HorizontalLayout horizontalLayout = new HorizontalLayout(checkbox,
				grid.isDetailsVisible(pendingMessageGridModel) ? ANGLE_DOWN.create() : ANGLE_RIGHT.create(),
				new Paragraph(
					getTranslationOrDefault("view.site-admin.pending-requests.page.grid.operation-type." + pendingMessageGridModel.operationType,
							pendingMessageGridModel.operationType)
			));
			horizontalLayout.setAlignItems(CENTER);
			return horizontalLayout;
		})
			.setHeader(new HorizontalLayout(mainContextMenu, new Label(getTranslation("view.site-admin.pending-requests.page.grid.1"))))
			.setFlexGrow(2);
		grid.addColumn(model -> model.status)
			.setHeader(getTranslation("view.site-admin.pending-requests.page.grid.2"))
			.setSortable(true);
		grid.addColumn(model -> model.sentAt.format(dateTimeFormatter))
			.setHeader(getTranslation("view.site-admin.pending-requests.page.grid.3"))
			.setSortable(true);
		grid.addColumn(model -> Optional.ofNullable(model.ackAt)
				.map(ackTime -> ackTime.format(dateTimeFormatter))
				.orElse("")
		)
			.setHeader(getTranslation("view.site-admin.pending-requests.page.grid.4"))
			.setSortable(true);
		grid.addColumn(model -> model.retryAmount)
			.setHeader(getTranslation("view.site-admin.pending-requests.page.grid.5"))
			.setSortable(true);
		grid.addComponentColumn(this::createContextMenu)
			.setHeader(getTranslation("view.site-admin.pending-requests.page.grid.6"))
			.setTextAlign(ColumnTextAlign.END);
		grid.setItemDetailsRenderer(new ComponentRenderer<>(data -> {
			Paragraph json = new Paragraph(data.json);
			json.getStyle().set("font-family", "monospace");
			json.getStyle().set("word-wrap", "break-word");
			json.getElement().getStyle().set("white-space", "pre-wrap");
			return json;
		}));

		return grid;
	}
	

	private String getTranslationOrDefault(String key, String defaultString) {
		try {
			return super.getTranslation(key);
		} catch (MissingResourceException e) {
			LOG.error("Unable to resolve message key: {}. Using default {}", key, defaultString, e);
			return defaultString;
		}
	}

	private Component createContextMenu(PendingMessageGridModel model) {
		CorrelationId id = model.id;
		GridActionMenu contextMenu = new GridActionMenu();
		contextMenu.addItem(new MenuButton(
				getTranslation("view.site-admin.pending-requests.page.grid.context-menu.refresh"), REFRESH),
			event -> refreshDetails(model)
		);

		Dialog retryConfirmDialog = createConfirmDialog(
			() -> siteAgentConnectionService.retry(siteId, id),
			getTranslation("view.site-admin.pending-requests.page.grid.dialog.retry.confirmation")
		);
		contextMenu.addItem(new MenuButton(
				getTranslation("view.site-admin.pending-requests.page.grid.context-menu.retry"), PLAY),
			event -> retryConfirmDialog.open()
		);

		IconButton retryButton = new IconButton(PLAY.create());
		retryButton.addClickListener(event -> retryConfirmDialog.open());

		Dialog deleteConfirmDialog = createConfirmDialog(
			() -> siteAgentConnectionService.delete(siteId, id),
			getTranslation("view.site-admin.pending-requests.page.grid.dialog.delete.confirmation")
		);
		contextMenu.addItem(new MenuButton(
				getTranslation("view.site-admin.pending-requests.page.grid.context-menu.delete"), TRASH),
			event -> deleteConfirmDialog.open()
		);

		GridActionsButtonLayout gridActionsButtonLayout = new GridActionsButtonLayout(retryButton, contextMenu.getTarget());
		gridActionsButtonLayout.setAlignItems(CENTER);
		return gridActionsButtonLayout;
	}

	private void refreshDetails(PendingMessageGridModel model) {
		boolean details = grid.isDetailsVisible(model);
		Set pendingMessageGridModels = loadItems();
		grid.setItems(pendingMessageGridModels);
		pendingMessageGridModels.stream()
			.filter(m -> m.id.equals(model.id))
			.findAny()
			.ifPresent(m -> grid.setDetailsVisible(m, details));
	}

	private Dialog createConfirmDialog(Runnable operation, String message) {
		FurmsDialog furmsDialog = new FurmsDialog(message);
		furmsDialog.addConfirmButtonClickListener(event -> {
			try {
				operation.run();
			} catch (Exception e){
				LOG.warn("Error ", e);
				showErrorNotification(getTranslation("base.error.message"));
			}
			loadGrid();
		});
		return furmsDialog;
	}

	private void loadGrid() {
		Set collect = loadItems();
		grid.setItems(collect);
	}

	private Set loadItems() {
		return siteAgentConnectionService.findAll(siteId)
			.stream()
			.map(message ->
				PendingMessageGridModel.builder()
					.id(message.correlationId)
					.operationType(JsonPendingRequestParser.parseOperation(message.jsonContent))
					.status(message.utcAckAt == null ?
						getTranslation("view.site-admin.pending-requests.page.grid.status.pending") :
						getTranslation("view.site-admin.pending-requests.page.grid.status.ack")
					)
					.sentAt(convertToZoneTime(message.utcSentAt, browserZoneId).toLocalDateTime())
					.ackAt(Optional.ofNullable(message.utcAckAt)
						.map(ack -> convertToZoneTime(ack, browserZoneId).toLocalDateTime())
						.orElse(null)
					)
					.retryAmount(message.retryCount)
					.json(JsonPendingRequestParser.getPrettier(message.jsonContent))
					.build()
			)
			.filter(model -> rowContains(model, searchLayout.getSearchText(), searchLayout))
			.collect(Collectors.toSet());
	}

	private VerticalLayout createSiteConnectionLayout(UI ui, SiteId siteId) {
		Button button = new Button(getTranslation("view.site-admin.pending-requests.page.agent.connection"));
		Label resultLabel = new Label();

		ProgressBar progressBar = new ProgressBar();
		progressBar.setIndeterminate(true);
		progressBar.setVisible(false);
		progressBar.setWidth("10em");

		button.addClickListener(event -> handleExceptions(() -> siteAgentConnectionService.getSiteAgentStatus(siteId))
			.ifPresent(siteAgentStatus -> {
				resultLabel.setText("");
				progressBar.setVisible(true);
				siteAgentStatus.jobFuture.thenAcceptAsync(status -> ui.access(() -> {
					resultLabel.setText(getTranslation("view.site-admin.pending-requests.page.agent." + status.status.name()));
					progressBar.setVisible(false);
				}));
			}));

		VerticalLayout verticalLayout = new VerticalLayout(
			button,
			new HorizontalLayout(
				new Label(getTranslation("view.site-admin.pending-requests.page.agent.status")),
				progressBar,
				resultLabel
			)
		);
		verticalLayout.setPadding(false);
		return verticalLayout;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy