diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java index 880e2a406..17ce5133c 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java @@ -12,12 +12,10 @@ import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; - import javax.validation.ConstraintViolationException; import javax.validation.Valid; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; - import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.repository.builder.TargetCreate; import org.eclipse.hawkbit.repository.builder.TargetUpdate; diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractTargetUpdateCreate.java b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractTargetUpdateCreate.java index 4ff522bc8..2e88a5e3e 100644 --- a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractTargetUpdateCreate.java +++ b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractTargetUpdateCreate.java @@ -10,12 +10,8 @@ package org.eclipse.hawkbit.repository.builder; import java.net.URI; import java.util.Optional; - -import org.eclipse.hawkbit.repository.TargetTypeManagement; import org.eclipse.hawkbit.repository.ValidString; -import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.InvalidTargetAddressException; -import org.eclipse.hawkbit.repository.model.TargetType; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.springframework.util.StringUtils; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java index 58fc9b41d..8c49f9d1f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java @@ -18,7 +18,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; - import javax.persistence.EntityManager; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; @@ -26,7 +25,6 @@ import javax.persistence.criteria.Expression; import javax.persistence.criteria.MapJoin; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; - import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.FilterParams; import org.eclipse.hawkbit.repository.QuotaManagement; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetTypeManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetTypeManagement.java index 884104568..028c9ea83 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetTypeManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetTypeManagement.java @@ -263,4 +263,5 @@ public class JpaTargetTypeManagement implements TargetTypeManagement { private static Page convertPage(final Page findAll, final Pageable pageable) { return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } + } diff --git a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java index 3ebfac15e..c5ebc3663 100644 --- a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java +++ b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java @@ -8,15 +8,14 @@ */ package org.eclipse.hawkbit.rest.exception; +import com.google.common.collect.Iterables; import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; - import javax.servlet.http.HttpServletRequest; import javax.validation.ConstraintViolationException; import javax.validation.ValidationException; - import org.apache.commons.lang3.exception.ExceptionUtils; import org.eclipse.hawkbit.exception.AbstractServerRtException; import org.eclipse.hawkbit.exception.SpServerError; @@ -32,8 +31,6 @@ import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.multipart.MultipartException; -import com.google.common.collect.Iterables; - /** * General controller advice for exception handling. */ diff --git a/hawkbit-rest/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/util/JsonBuilder.java b/hawkbit-rest/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/util/JsonBuilder.java index de3f63f0e..fb613df1b 100644 --- a/hawkbit-rest/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/util/JsonBuilder.java +++ b/hawkbit-rest/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/util/JsonBuilder.java @@ -471,7 +471,7 @@ public abstract class JsonBuilder { return builder.toString(); } - + public static String targetTypes(final List types) throws JSONException { final JSONArray result = new JSONArray(); @@ -494,8 +494,8 @@ public abstract class JsonBuilder { } return result.toString(); - } - + } + public static String targetTypesCreatableFieldsOnly(final List types) throws JSONException { final JSONArray result = new JSONArray(); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/mappers/TargetTypeToProxyTargetTypeMapper.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/mappers/TargetTypeToProxyTargetTypeMapper.java new file mode 100644 index 000000000..a4c62e3a5 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/mappers/TargetTypeToProxyTargetTypeMapper.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2021 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.ui.common.data.mappers; + +import org.eclipse.hawkbit.repository.model.TargetType; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTargetType; + +/** + * Maps {@link TargetType} entities, fetched from backend, to the {@link ProxyTargetType} + * entities. + * + * @param + * Generic type of TargetType + */ +public class TargetTypeToProxyTargetTypeMapper extends AbstractNamedEntityToProxyNamedEntityMapper { + + @Override + public ProxyTargetType map(final T targetType) { + final ProxyTargetType proxyTargetType = new ProxyTargetType(); + + mapNamedEntityAttributes(targetType, proxyTargetType); + + proxyTargetType.setColour(targetType.getColour()); + + return proxyTargetType; + } + +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/providers/TargetTypeDataProvider.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/providers/TargetTypeDataProvider.java index 444e2d220..ae0b230bf 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/providers/TargetTypeDataProvider.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/providers/TargetTypeDataProvider.java @@ -22,7 +22,7 @@ import org.springframework.util.StringUtils; * Data provider for {@link TargetTypeManagement}, which dynamically loads a * batch of {@link TargetType} entities from backend and maps them to * corresponding output type. - * + * * @param * output type */ diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/proxies/ProxyTargetType.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/proxies/ProxyTargetType.java new file mode 100644 index 000000000..48c110666 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/proxies/ProxyTargetType.java @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2021 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.ui.common.data.proxies; + +import com.google.common.base.MoreObjects; +import java.util.Set; +import org.eclipse.hawkbit.repository.model.TargetType; + +import java.util.Objects; + +/** + * Proxy for {@link TargetType}. + */ +public class ProxyTargetType extends ProxyFilterButton { + + private static final long serialVersionUID = 1L; + + private boolean isNoTargetType; + + private Set selectedDsTypes; + + /** + * Constructor + */ + public ProxyTargetType() { + } + + /** + * Constructor for ProxyTargetType + * + * @param id + * Type id + * @param name + * Type name + * @param colour + * Type colour + */ + public ProxyTargetType(final Long id, final String name, final String colour) { + setId(id); + setName(name); + setColour(colour); + } + + public boolean isNoTargetType() { + return isNoTargetType; + } + + public void setNoTargetType(boolean noTargetType) { + isNoTargetType = noTargetType; + } + + /** + * Gets the selected distribution sets types + * + * @return selectedDsTypes + */ + public Set getSelectedDsTypes() { + return selectedDsTypes; + } + + /** + * Sets the selectedDsTypes + * + * @param selectedDsTypes + * Selected distribution sets types + */ + public void setSelectedDsTypes(final Set selectedDsTypes) { + this.selectedDsTypes = selectedDsTypes; + } + + @Override + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ProxyTargetType other = (ProxyTargetType) obj; + return Objects.equals(this.getId(), other.getId()) && Objects.equals(this.getName(), other.getName()) + && Objects.equals(this.getColour(), other.getColour()); + } + + @Override + public int hashCode() { + return Objects.hash(getId(), getName(), getColour()); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("id", getId()).add("name", getName()).add("color", getColour()) + .toString(); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/event/FilterType.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/event/FilterType.java index ba6b053f4..ff64c5f4a 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/event/FilterType.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/event/FilterType.java @@ -12,5 +12,5 @@ package org.eclipse.hawkbit.ui.common.event; * Enum constants for filter type */ public enum FilterType { - SEARCH, TYPE, TAG, NO_TAG, STATUS, OVERDUE, QUERY, DISTRIBUTION, MASTER; + SEARCH, TYPE, TARGET_TYPE, TAG, NO_TAG, STATUS, OVERDUE, QUERY, DISTRIBUTION, MASTER; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/event/TargetFilterTabChangedEventPayload.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/event/TargetFilterTabChangedEventPayload.java index 2181e47f7..a3b6756eb 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/event/TargetFilterTabChangedEventPayload.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/event/TargetFilterTabChangedEventPayload.java @@ -12,5 +12,5 @@ package org.eclipse.hawkbit.ui.common.event; * Enum constants for target filter tab change event */ public enum TargetFilterTabChangedEventPayload { - SIMPLE, CUSTOM; + SIMPLE, CUSTOM, TARGET_TYPE; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/filterlayout/AbstractTagFilterButtons.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/filterlayout/AbstractTagFilterButtons.java index 8c97eb4de..594814bf7 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/filterlayout/AbstractTagFilterButtons.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/filterlayout/AbstractTagFilterButtons.java @@ -8,11 +8,13 @@ */ package org.eclipse.hawkbit.ui.common.filterlayout; +import com.vaadin.ui.Button; +import com.vaadin.ui.UI; +import com.vaadin.ui.Window; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Set; - import org.eclipse.hawkbit.ui.common.CommonUiDependencies; import org.eclipse.hawkbit.ui.common.data.proxies.ProxyIdentifiableEntity; import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTag; @@ -32,10 +34,6 @@ import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider; import org.eclipse.hawkbit.ui.utils.UINotification; import org.springframework.util.CollectionUtils; -import com.vaadin.ui.Button; -import com.vaadin.ui.UI; -import com.vaadin.ui.Window; - /** * Class for defining the tag filter buttons. */ diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/filterlayout/AbstractTargetTypeFilterButtons.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/filterlayout/AbstractTargetTypeFilterButtons.java new file mode 100644 index 000000000..8663b075f --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/filterlayout/AbstractTargetTypeFilterButtons.java @@ -0,0 +1,156 @@ +/** + * Copyright (c) 2021 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.ui.common.filterlayout; + +import com.vaadin.ui.Button; +import java.util.Collection; +import java.util.Map; +import org.eclipse.hawkbit.ui.common.CommonUiDependencies; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyIdentifiableEntity; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTarget; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTargetType; +import org.eclipse.hawkbit.ui.common.event.EventTopics; +import org.eclipse.hawkbit.ui.common.event.EventView; +import org.eclipse.hawkbit.ui.common.event.FilterChangedEventPayload; +import org.eclipse.hawkbit.ui.common.event.FilterType; +import org.eclipse.hawkbit.ui.common.filterlayout.AbstractFilterButtonClickBehaviour.ClickBehaviourType; +import org.eclipse.hawkbit.ui.common.state.TagFilterLayoutUiState; +import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; +import org.eclipse.hawkbit.ui.decorators.SPUITagButtonStyle; +import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; +import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; +import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider; +import org.eclipse.hawkbit.ui.utils.UINotification; +import org.springframework.util.CollectionUtils; + +/** + * Class for defining the type filter buttons. + */ +public abstract class AbstractTargetTypeFilterButtons extends AbstractFilterButtons { + private static final long serialVersionUID = 1L; + + private final TagFilterLayoutUiState tagFilterLayoutUiState; + + protected final UINotification uiNotification; + private final Button noTargetTypeButton; + + private final TargetTypeFilterButtonClick targetTypeFilterButtonClick; + + /** + * Constructor for AbstractTargetTypeFilterButtons + * + * @param uiDependencies + * {@link CommonUiDependencies} + * @param tagFilterLayoutUiState + * TagFilterLayoutUiState + */ + protected AbstractTargetTypeFilterButtons(final CommonUiDependencies uiDependencies, + final TagFilterLayoutUiState tagFilterLayoutUiState) { + super(uiDependencies.getEventBus(), uiDependencies.getI18n(), uiDependencies.getUiNotification(), + uiDependencies.getPermChecker()); + + this.uiNotification = uiDependencies.getUiNotification(); + this.tagFilterLayoutUiState = tagFilterLayoutUiState; + this.noTargetTypeButton = buildNoTargetTypeButton(); + this.targetTypeFilterButtonClick = new TargetTypeFilterButtonClick(this::onFilterChangedEvent); + } + + private Button buildNoTargetTypeButton() { + final Button noTargetType = SPUIComponentProvider.getButton( + getFilterButtonIdPrefix() + "." + SPUIDefinitions.NO_TARGET_TYPE_BUTTON_ID, + i18n.getMessage(UIMessageIdProvider.LABEL_NO_TARGET_TYPE), + i18n.getMessage(UIMessageIdProvider.TOOLTIP_CLICK_TO_FILTER), "button-no-tag", false, null, + SPUITagButtonStyle.class); + + final ProxyTargetType proxyTargetType = new ProxyTargetType(); + proxyTargetType.setNoTargetType(true); + + return noTargetType; + } + + @Override + protected TargetTypeFilterButtonClick getFilterButtonClickBehaviour(){ + return targetTypeFilterButtonClick; + } + + private void onFilterChangedEvent(final ProxyTargetType targetType, + final ClickBehaviourType clickType) { + final Long targetTypeId = ClickBehaviourType.CLICKED == clickType ? targetType.getId() + : null; + publishFilterChangedEvent(targetTypeId); + } + + private void publishFilterChangedEvent(final Long targetTypeId) { + eventBus.publish(EventTopics.FILTER_CHANGED, this, new FilterChangedEventPayload<>(ProxyTarget.class, + FilterType.TARGET_TYPE, targetTypeId, EventView.DEPLOYMENT)); + } + + /** + * Provides type of the master entity. + * + * @return type of the master entity + */ + protected abstract Class getFilterMasterEntityType(); + + /** + * Provides event view filter. + * + * @return event view filter. + */ + protected abstract EventView getView(); + + /** + * Target type deletion operation. + * + * @param targetTypeToDelete + * target type to delete + * + * @return true if target type is deleted, in error case false. + */ + protected abstract boolean deleteTargetType(final ProxyTargetType targetTypeToDelete); + + /** + * @return Button component of no target type + */ + public Button getNoTargetTypeButton() { + return noTargetTypeButton; + } + + @Override + public void restoreState() { + final Map targetTypesToRestore = tagFilterLayoutUiState.getClickedTagIdsWithName(); + + if (!CollectionUtils.isEmpty(targetTypesToRestore)) { + removeNonExistingTargetTypes(targetTypesToRestore); + } + + if (tagFilterLayoutUiState.isNoTagClicked()) { + getNoTargetTypeButton().addStyleName(SPUIStyleDefinitions.SP_NO_TAG_BTN_CLICKED_STYLE); + } + } + + private void removeNonExistingTargetTypes(final Map targetTypeIdsWithName) { + final Collection targetTypeIds = targetTypeIdsWithName.keySet(); + final Collection existingTargetTypeIds = filterExistingTargetTypeIds(targetTypeIds); + if (targetTypeIds.size() != existingTargetTypeIds.size()) { + targetTypeIds.retainAll(existingTargetTypeIds); + } + + } + + /** + * Filters out non-existant target type by ids. + * + * @param targetTypeIds + * provided target type ids + * @return filtered list of existing target type ids + */ + protected abstract Collection filterExistingTargetTypeIds(final Collection targetTypeIds); + +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/filterlayout/TargetTypeFilterButtonClick.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/filterlayout/TargetTypeFilterButtonClick.java new file mode 100644 index 000000000..b2e82cbbe --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/filterlayout/TargetTypeFilterButtonClick.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2021 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.ui.common.filterlayout; + +import java.util.function.BiConsumer; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTargetType; + +/** + * Button click behaviour of target type filter buttons layout. + */ +public class TargetTypeFilterButtonClick extends AbstractFilterSingleButtonClick { + private static final long serialVersionUID = 1L; + + private final transient BiConsumer filterChangedCallback; + + TargetTypeFilterButtonClick(final BiConsumer filterChangedCallback) { + this.filterChangedCallback = filterChangedCallback; + } + + @Override + protected void filterUnClicked(ProxyTargetType clickedFilter) { + filterChangedCallback.accept(clickedFilter, ClickBehaviourType.UNCLICKED); + } + + @Override + protected void filterClicked(ProxyTargetType clickedFilter) { + filterChangedCallback.accept(clickedFilter, ClickBehaviourType.CLICKED); + } + + @Override + public boolean isFilterPreviouslyClicked(final ProxyTargetType clickedFilter) { + return false; + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/targettype/ProxyTargetTypeValidator.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/targettype/ProxyTargetTypeValidator.java new file mode 100644 index 000000000..40ebda8c2 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/targettype/ProxyTargetTypeValidator.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2021 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.ui.common.targettype; + +import java.util.function.BooleanSupplier; + +import org.eclipse.hawkbit.ui.common.EntityValidator; +import org.eclipse.hawkbit.ui.common.CommonUiDependencies; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTargetType; +import org.springframework.util.StringUtils; + + +/** + * Validator used in TargetType window controllers to validate {@link ProxyTargetType}. + */ +public class ProxyTargetTypeValidator extends EntityValidator { + + /** + * Constructor + * + * @param uiDependencies + * {@link CommonUiDependencies} + */ + public ProxyTargetTypeValidator(final CommonUiDependencies uiDependencies) { + super(uiDependencies); + } + + public boolean isEntityValid(final ProxyTargetType entity, final BooleanSupplier duplicateCheck) { + if (!StringUtils.hasText(entity.getName())) { + displayValidationError("message.error.missing.typename"); + return false; + } + + if (duplicateCheck.getAsBoolean()) { + final String trimmedName = StringUtils.trimWhitespace(entity.getName()); + displayValidationError("message.type.duplicate.check", trimmedName); + return false; + } + + return true; + } + +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/DeploymentView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/DeploymentView.java index 326ed7fa2..0b60a3f94 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/DeploymentView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/DeploymentView.java @@ -8,11 +8,17 @@ */ package org.eclipse.hawkbit.ui.management; +import com.vaadin.server.Page; +import com.vaadin.server.Page.BrowserWindowResizeEvent; +import com.vaadin.server.Page.BrowserWindowResizeListener; +import com.vaadin.spring.annotation.SpringView; +import com.vaadin.spring.annotation.UIScope; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Layout; import java.util.Arrays; import java.util.EnumMap; import java.util.Map; import java.util.concurrent.Executor; - import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.DistributionSetInvalidationManagement; import org.eclipse.hawkbit.repository.DistributionSetManagement; @@ -52,14 +58,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.vaadin.spring.events.EventBus.UIEventBus; -import com.vaadin.server.Page; -import com.vaadin.server.Page.BrowserWindowResizeEvent; -import com.vaadin.server.Page.BrowserWindowResizeListener; -import com.vaadin.spring.annotation.SpringView; -import com.vaadin.spring.annotation.UIScope; -import com.vaadin.ui.HorizontalLayout; -import com.vaadin.ui.Layout; - /** * Target status and deployment management view */ @@ -107,8 +105,8 @@ public class DeploymentView extends AbstractEventListenersAwareView implements B if (permChecker.hasTargetReadPermission()) { this.targetTagFilterLayout = new TargetTagFilterLayout(uiDependencies, managementUIState, - targetFilterQueryManagement, targetTagManagement, targetManagement, - managementUIState.getTargetTagFilterLayoutUiState()); + targetFilterQueryManagement, targetTypeManagement, targetTagManagement, targetManagement, + managementUIState.getTargetTagFilterLayoutUiState(), distributionSetTypeManagement); this.targetGridLayout = new TargetGridLayout(uiDependencies, targetManagement, targetTypeManagement, deploymentManagement, uiProperties, targetTagManagement, distributionSetManagement, uiExecutor, configManagement, diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/TargetTagWindowBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/TargetTagWindowBuilder.java index e025d3753..d51062bba 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/TargetTagWindowBuilder.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/TargetTagWindowBuilder.java @@ -46,13 +46,13 @@ public class TargetTagWindowBuilder extends AbstractEntityWindowBuilder(uiDependencies))); + new TagWindowLayout<>(uiDependencies))); } @Override public Window getWindowForUpdate(final ProxyTag proxyTag) { return getWindowForEntity(proxyTag, new UpdateTargetTagWindowController(uiDependencies, targetTagManagement, - new TagWindowLayout(uiDependencies))); + new TagWindowLayout<>(uiDependencies))); } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/MultipleTargetFilter.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/MultipleTargetFilter.java index 3546eca48..d1d2a5834 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/MultipleTargetFilter.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/MultipleTargetFilter.java @@ -8,16 +8,21 @@ */ package org.eclipse.hawkbit.ui.management.targettag.filter; +import com.vaadin.ui.Accordion; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; import java.util.Arrays; import java.util.List; - import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.TargetTagManagement; +import org.eclipse.hawkbit.repository.TargetTypeManagement; import org.eclipse.hawkbit.ui.common.CommonUiDependencies; import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTag; import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTarget; import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTargetFilterQuery; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTargetType; import org.eclipse.hawkbit.ui.common.event.EventLayout; import org.eclipse.hawkbit.ui.common.event.EventLayoutViewAware; import org.eclipse.hawkbit.ui.common.event.EventTopics; @@ -29,16 +34,12 @@ import org.eclipse.hawkbit.ui.common.layout.listener.GridActionsVisibilityListen import org.eclipse.hawkbit.ui.common.layout.listener.support.EntityModifiedGenericSupport; import org.eclipse.hawkbit.ui.common.layout.listener.support.EntityModifiedGridRefreshAwareSupport; import org.eclipse.hawkbit.ui.management.targettag.TargetTagWindowBuilder; +import org.eclipse.hawkbit.ui.management.targettag.targettype.TargetTypeWindowBuilder; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; import org.vaadin.spring.events.EventBus.UIEventBus; -import com.vaadin.ui.Accordion; -import com.vaadin.ui.Alignment; -import com.vaadin.ui.VerticalLayout; -import com.vaadin.ui.themes.ValoTheme; - /** * Target filter tabsheet with 'simple' and 'complex' filter options. */ @@ -51,33 +52,41 @@ public class MultipleTargetFilter extends Accordion { private final TargetTagFilterLayoutUiState targetTagFilterLayoutUiState; private final VerticalLayout simpleFilterTab; + private final VerticalLayout targetTypeFilterTab; + private final TargetTagFilterButtons filterByButtons; + private final TargetTypeFilterButtons targetTypeFilterButtons; private final FilterByStatusLayout filterByStatusFooter; private final TargetFilterQueryButtons customFilterTab; - private final transient GridActionsVisibilityListener gridActionsVisibilityListener; + private final transient GridActionsVisibilityListener targetTagGridActionsVisibilityListener; + private final transient GridActionsVisibilityListener targetTypeGridActionsVisibilityListener; private final transient EntityModifiedListener entityTagModifiedListener; private final transient EntityModifiedListener entityFilterQueryModifiedListener; + private final transient EntityModifiedListener entityTargetTypeModifiedListener; + MultipleTargetFilter(final CommonUiDependencies uiDependencies, - final TargetFilterQueryManagement targetFilterQueryManagement, - final TargetTagManagement targetTagManagement, final TargetManagement targetManagement, - final TargetTagFilterLayoutUiState targetTagFilterLayoutUiState, - final TargetTagWindowBuilder targetTagWindowBuilder) { + final TargetFilterQueryManagement targetFilterQueryManagement, + final TargetTagManagement targetTagManagement, final TargetManagement targetManagement, + final TargetTagFilterLayoutUiState targetTagFilterLayoutUiState, + final TargetTagWindowBuilder targetTagWindowBuilder, final TargetTypeWindowBuilder targetTypeWindowBuilder, final TargetTypeManagement targetTypeManagement) { this.i18n = uiDependencies.getI18n(); this.eventBus = uiDependencies.getEventBus(); this.targetTagFilterLayoutUiState = targetTagFilterLayoutUiState; this.filterByButtons = new TargetTagFilterButtons(uiDependencies, targetTagManagement, targetManagement, targetTagFilterLayoutUiState, targetTagWindowBuilder); + this.targetTypeFilterButtons = new TargetTypeFilterButtons(uiDependencies, targetTypeManagement, targetTagFilterLayoutUiState, targetTypeWindowBuilder); this.filterByStatusFooter = new FilterByStatusLayout(i18n, eventBus, targetTagFilterLayoutUiState); this.simpleFilterTab = buildSimpleFilterTab(); + this.targetTypeFilterTab = buildTargetTypeFilterTab(); this.customFilterTab = new TargetFilterQueryButtons(i18n, eventBus, targetFilterQueryManagement, targetTagFilterLayoutUiState); final EventLayoutViewAware layoutViewAware = new EventLayoutViewAware(EventLayout.TARGET_TAG_FILTER, EventView.DEPLOYMENT); - this.gridActionsVisibilityListener = new GridActionsVisibilityListener(eventBus, layoutViewAware, + this.targetTagGridActionsVisibilityListener = new GridActionsVisibilityListener(eventBus, layoutViewAware, filterByButtons::hideActionColumns, filterByButtons::showEditColumn, filterByButtons::showDeleteColumn); this.entityTagModifiedListener = new EntityModifiedListener.Builder<>(eventBus, ProxyTag.class) .parentEntityType(ProxyTarget.class).viewAware(layoutViewAware) @@ -86,6 +95,12 @@ public class MultipleTargetFilter extends Accordion { ProxyTargetFilterQuery.class).viewAware(layoutViewAware) .entityModifiedAwareSupports(getFilterQueryModifiedAwareSupports()).build(); + this.targetTypeGridActionsVisibilityListener = new GridActionsVisibilityListener(eventBus, layoutViewAware, + targetTypeFilterButtons::hideActionColumns, targetTypeFilterButtons::showEditColumn, targetTypeFilterButtons::showDeleteColumn); + this.entityTargetTypeModifiedListener = new EntityModifiedListener.Builder<>(eventBus, ProxyTargetType.class) + .parentEntityType(ProxyTarget.class).viewAware(layoutViewAware) + .entityModifiedAwareSupports(getTargetTypeModifiedAwareSupports()).build(); + init(); addTabs(); addSelectedTabChangeListener(event -> selectedTabChanged()); @@ -119,11 +134,40 @@ public class MultipleTargetFilter extends Accordion { return simpleTab; } + private VerticalLayout buildTargetTypeFilterTab() { + final VerticalLayout targetTypeTab = new VerticalLayout(); + targetTypeTab.setSpacing(false); + targetTypeTab.setMargin(false); + targetTypeTab.setSizeFull(); + targetTypeTab.setCaption(i18n.getMessage("caption.filter.type")); + targetTypeTab.addStyleName(SPUIStyleDefinitions.TARGET_TYPE_FILTER_HEADER); + + final VerticalLayout targetTypeGridLayout = new VerticalLayout(); + targetTypeGridLayout.setSpacing(false); + targetTypeGridLayout.setMargin(false); + targetTypeGridLayout.setSizeFull(); + targetTypeGridLayout.setId(UIComponentIdProvider.TARGET_TYPE_DROP_AREA_ID); + targetTypeGridLayout.addComponent(targetTypeFilterButtons.getNoTargetTypeButton()); + targetTypeGridLayout.addComponent(targetTypeFilterButtons); + targetTypeGridLayout.setComponentAlignment(targetTypeFilterButtons, Alignment.MIDDLE_CENTER); + targetTypeGridLayout.setExpandRatio(targetTypeFilterButtons, 1.0F); + + targetTypeTab.addComponent(targetTypeGridLayout); + targetTypeTab.setExpandRatio(targetTypeGridLayout, 1.0F); + + return targetTypeTab; + } + private List getTagModifiedAwareSupports() { return Arrays.asList(EntityModifiedGridRefreshAwareSupport.of(filterByButtons::refreshAll), EntityModifiedGenericSupport.of(null, null, filterByButtons::resetFilterOnTagsDeleted)); } + private List getTargetTypeModifiedAwareSupports() { + return Arrays.asList(EntityModifiedGridRefreshAwareSupport.of(targetTypeFilterButtons::refreshAll), + EntityModifiedGenericSupport.of(null, null, null)); + } + private List getFilterQueryModifiedAwareSupports() { return Arrays.asList(EntityModifiedGridRefreshAwareSupport.of(customFilterTab::refreshAll), EntityModifiedGenericSupport.of(null, customFilterTab::reselectFilterOnTfqUpdated, @@ -138,6 +182,7 @@ public class MultipleTargetFilter extends Accordion { private void addTabs() { addTab(simpleFilterTab).setId(UIComponentIdProvider.SIMPLE_FILTER_ACCORDION_TAB); addTab(customFilterTab).setId(UIComponentIdProvider.CUSTOM_FILTER_ACCORDION_TAB); + addTab(targetTypeFilterTab).setId(UIComponentIdProvider.TARGET_TYPE_FILTER_ACCORDION_TAB); } /** @@ -146,20 +191,30 @@ public class MultipleTargetFilter extends Accordion { public void selectedTabChanged() { final String selectedTabId = getTab(getSelectedTab()).getId(); + if (UIComponentIdProvider.SIMPLE_FILTER_ACCORDION_TAB.equals(selectedTabId)) { customFilterTab.clearAppliedTargetFilterQuery(); targetTagFilterLayoutUiState.setCustomFilterTabSelected(false); + targetTagFilterLayoutUiState.setTargetTypeFilterTabSelected(false); eventBus.publish(EventTopics.TARGET_FILTER_TAB_CHANGED, this, TargetFilterTabChangedEventPayload.SIMPLE); - } else { + } + if (UIComponentIdProvider.TARGET_TYPE_FILTER_ACCORDION_TAB.equals(selectedTabId)){ + targetTagFilterLayoutUiState.setTargetTypeFilterTabSelected(true); + targetTagFilterLayoutUiState.setCustomFilterTabSelected(false); + eventBus.publish(EventTopics.TARGET_FILTER_TAB_CHANGED, this, TargetFilterTabChangedEventPayload.TARGET_TYPE); + } + if (UIComponentIdProvider.CUSTOM_FILTER_ACCORDION_TAB.equals(selectedTabId)){ filterByButtons.clearTargetTagFilters(); filterByStatusFooter.clearStatusAndOverdueFilters(); targetTagFilterLayoutUiState.setCustomFilterTabSelected(true); + targetTagFilterLayoutUiState.setTargetTypeFilterTabSelected(false); eventBus.publish(EventTopics.TARGET_FILTER_TAB_CHANGED, this, TargetFilterTabChangedEventPayload.CUSTOM); } + } /** @@ -170,12 +225,19 @@ public class MultipleTargetFilter extends Accordion { customFilterTab.restoreState(); setSelectedTab(customFilterTab); - } else { + } + if (targetTagFilterLayoutUiState.isCustomFilterTabSelected()) { filterByButtons.restoreState(); filterByStatusFooter.restoreState(); setSelectedTab(simpleFilterTab); } + if (targetTagFilterLayoutUiState.isTargetTypeFilterTabSelected()) { + filterByButtons.restoreState(); + filterByStatusFooter.restoreState(); + + setSelectedTab(targetTypeFilterTab); + } } /** @@ -190,17 +252,21 @@ public class MultipleTargetFilter extends Accordion { * Subscribe event listeners */ public void subscribeListeners() { - gridActionsVisibilityListener.subscribe(); + targetTagGridActionsVisibilityListener.subscribe(); entityTagModifiedListener.subscribe(); entityFilterQueryModifiedListener.subscribe(); + targetTypeGridActionsVisibilityListener.subscribe(); + entityTargetTypeModifiedListener.subscribe(); } /** * Unsubscribe event listeners */ public void unsubscribeListeners() { - gridActionsVisibilityListener.unsubscribe(); + targetTagGridActionsVisibilityListener.unsubscribe(); entityTagModifiedListener.unsubscribe(); entityFilterQueryModifiedListener.unsubscribe(); + targetTypeGridActionsVisibilityListener.unsubscribe(); + entityTargetTypeModifiedListener.unsubscribe(); } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/TargetTagFilterHeader.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/TargetTagFilterHeader.java index 4dc7f3c8e..63433a327 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/TargetTagFilterHeader.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/TargetTagFilterHeader.java @@ -13,6 +13,7 @@ import org.eclipse.hawkbit.ui.common.event.EventLayout; import org.eclipse.hawkbit.ui.common.event.EventView; import org.eclipse.hawkbit.ui.common.grid.header.AbstractFilterHeader; import org.eclipse.hawkbit.ui.management.targettag.TargetTagWindowBuilder; +import org.eclipse.hawkbit.ui.management.targettag.targettype.TargetTypeWindowBuilder; import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider; @@ -28,23 +29,26 @@ public class TargetTagFilterHeader extends AbstractFilterHeader { private final transient TargetTagWindowBuilder targetTagWindowBuilder; + private final transient TargetTypeWindowBuilder targetTypeWindowBuilder; + + /** * Constructor for TargetTagFilterHeader - * - * @param uiDependencies + * @param uiDependencies * {@link CommonUiDependencies} * @param targetTagFilterLayoutUiState * TargetTagFilterLayoutUiState * @param targetTagWindowBuilder - * TargetTagWindowBuilder + * @param targetTypeWindowBuilder */ public TargetTagFilterHeader(final CommonUiDependencies uiDependencies, - final TargetTagFilterLayoutUiState targetTagFilterLayoutUiState, - final TargetTagWindowBuilder targetTagWindowBuilder) { + final TargetTagFilterLayoutUiState targetTagFilterLayoutUiState, + final TargetTagWindowBuilder targetTagWindowBuilder, TargetTypeWindowBuilder targetTypeWindowBuilder) { super(uiDependencies.getI18n(), uiDependencies.getPermChecker(), uiDependencies.getEventBus()); this.targetTagFilterLayoutUiState = targetTagFilterLayoutUiState; this.targetTagWindowBuilder = targetTagWindowBuilder; + this.targetTypeWindowBuilder = targetTypeWindowBuilder; buildHeader(); } @@ -70,11 +74,17 @@ public class TargetTagFilterHeader extends AbstractFilterHeader { @Override protected Window getWindowForAdd() { + if (targetTagFilterLayoutUiState.isTargetTypeFilterTabSelected()) { + return targetTypeWindowBuilder.getWindowForAdd(); + } return targetTagWindowBuilder.getWindowForAdd(); } @Override protected String getAddEntityWindowCaptionMsgKey() { + if (targetTagFilterLayoutUiState.isTargetTypeFilterTabSelected()) { + return UIMessageIdProvider.CAPTION_TYPE; + } return UIMessageIdProvider.CAPTION_TAG; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/TargetTagFilterLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/TargetTagFilterLayout.java index 290afa245..ac9d9a6bb 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/TargetTagFilterLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/TargetTagFilterLayout.java @@ -8,9 +8,11 @@ */ package org.eclipse.hawkbit.ui.management.targettag.filter; +import org.eclipse.hawkbit.repository.DistributionSetTypeManagement; import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.TargetTagManagement; +import org.eclipse.hawkbit.repository.TargetTypeManagement; import org.eclipse.hawkbit.ui.common.CommonUiDependencies; import org.eclipse.hawkbit.ui.common.event.EventTopics; import org.eclipse.hawkbit.ui.common.event.TargetFilterTabChangedEventPayload; @@ -20,6 +22,7 @@ import org.eclipse.hawkbit.ui.management.ManagementUIState; import org.eclipse.hawkbit.ui.management.targettag.TargetTagWindowBuilder; import com.vaadin.ui.ComponentContainer; +import org.eclipse.hawkbit.ui.management.targettag.targettype.TargetTypeWindowBuilder; /** * Target Tag filter layout. @@ -49,16 +52,20 @@ public class TargetTagFilterLayout extends AbstractFilterLayout { * TargetTagFilterLayoutUiState */ public TargetTagFilterLayout(final CommonUiDependencies uiDependencies, final ManagementUIState managementUIState, - final TargetFilterQueryManagement targetFilterQueryManagement, - final TargetTagManagement targetTagManagement, final TargetManagement targetManagement, - final TargetTagFilterLayoutUiState targetTagFilterLayoutUiState) { + final TargetFilterQueryManagement targetFilterQueryManagement, + final TargetTypeManagement targetTypeManagement, + final TargetTagManagement targetTagManagement, final TargetManagement targetManagement, + final TargetTagFilterLayoutUiState targetTagFilterLayoutUiState, final DistributionSetTypeManagement distributionSetTypeManagement) { final TargetTagWindowBuilder targetTagWindowBuilder = new TargetTagWindowBuilder(uiDependencies, targetTagManagement); + final TargetTypeWindowBuilder targetTypeWindowBuilder = new TargetTypeWindowBuilder(uiDependencies, + targetTypeManagement, distributionSetTypeManagement); + this.targetTagFilterHeader = new TargetTagFilterHeader(uiDependencies, targetTagFilterLayoutUiState, - targetTagWindowBuilder); + targetTagWindowBuilder, targetTypeWindowBuilder); this.multipleTargetFilter = new MultipleTargetFilter(uiDependencies, targetFilterQueryManagement, - targetTagManagement, targetManagement, targetTagFilterLayoutUiState, targetTagWindowBuilder); + targetTagManagement, targetManagement, targetTagFilterLayoutUiState, targetTagWindowBuilder, targetTypeWindowBuilder, targetTypeManagement); this.filterTabChangedListener = new GenericEventListener<>(uiDependencies.getEventBus(), EventTopics.TARGET_FILTER_TAB_CHANGED, this::onTargetFilterTabChanged); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/TargetTagFilterLayoutUiState.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/TargetTagFilterLayoutUiState.java index db7d9f01d..8b80cedab 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/TargetTagFilterLayoutUiState.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/TargetTagFilterLayoutUiState.java @@ -24,6 +24,8 @@ public class TargetTagFilterLayoutUiState extends TagFilterLayoutUiState { private final Collection clickedTargetUpdateStatusFilters = new ArrayList<>(); private boolean isOverdueFilterClicked; private boolean isCustomFilterTabSelected; + private boolean isTargetTypeFilterTabSelected; + /** * @return Id of clicked target filter query @@ -94,4 +96,13 @@ public class TargetTagFilterLayoutUiState extends TagFilterLayoutUiState { public void setCustomFilterTabSelected(final boolean isCustomFilterTabSelected) { this.isCustomFilterTabSelected = isCustomFilterTabSelected; } + + public boolean isTargetTypeFilterTabSelected() { + return isTargetTypeFilterTabSelected; + } + + public void setTargetTypeFilterTabSelected(boolean targetTypeFilterTabSelected) { + isTargetTypeFilterTabSelected = targetTypeFilterTabSelected; + } + } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/TargetTypeFilterButtons.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/TargetTypeFilterButtons.java new file mode 100644 index 000000000..5775d01dd --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/filter/TargetTypeFilterButtons.java @@ -0,0 +1,134 @@ +/** + * Copyright (c) 2021 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.ui.management.targettag.filter; + +import com.vaadin.ui.UI; +import com.vaadin.ui.Window; +import java.util.Collection; +import java.util.stream.Collectors; +import org.eclipse.hawkbit.repository.Identifiable; +import org.eclipse.hawkbit.repository.TargetTypeManagement; +import org.eclipse.hawkbit.repository.exception.TargetTypeInUseException; +import org.eclipse.hawkbit.ui.common.CommonUiDependencies; +import org.eclipse.hawkbit.ui.common.data.mappers.TargetTypeToProxyTargetTypeMapper; +import org.eclipse.hawkbit.ui.common.data.providers.TargetTypeDataProvider; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyIdentifiableEntity; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTarget; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTargetType; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload; +import org.eclipse.hawkbit.ui.common.event.EventTopics; +import org.eclipse.hawkbit.ui.common.event.EventView; +import org.eclipse.hawkbit.ui.common.filterlayout.AbstractTargetTypeFilterButtons; +import org.eclipse.hawkbit.ui.common.state.TagFilterLayoutUiState; +import org.eclipse.hawkbit.ui.management.targettag.targettype.TargetTypeWindowBuilder; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Target Type filter buttons table. + */ +@SuppressWarnings("squid:S2160") +public class TargetTypeFilterButtons extends AbstractTargetTypeFilterButtons { + private static final long serialVersionUID = 1L; + + private final transient TargetTypeManagement targetTypeManagement; + private final transient TargetTypeWindowBuilder targetTypeWindowBuilder; + + private static final Logger LOG = LoggerFactory.getLogger(TargetTypeFilterButtons.class); + + TargetTypeFilterButtons(final CommonUiDependencies uiDependencies, + final TargetTypeManagement targetTypeManagement, final TagFilterLayoutUiState tagFilterLayoutUiState, + final TargetTypeWindowBuilder targetTypeWindowBuilder) { + super(uiDependencies, tagFilterLayoutUiState); + + this.targetTypeManagement = targetTypeManagement; + this.targetTypeWindowBuilder = targetTypeWindowBuilder; + + init(); + setDataProvider( + new TargetTypeDataProvider<>(targetTypeManagement, new TargetTypeToProxyTargetTypeMapper<>())); + } + + @Override + public String getGridId() { + return UIComponentIdProvider.TARGET_TYPE_TABLE_ID; + } + + @Override + protected String getMessageKeyEntityTypeSing() { + return "caption.entity.target.type"; + } + + @Override + protected String getMessageKeyEntityTypePlur() { + return "caption.entity.target.types"; + } + + @Override + protected boolean deleteFilterButtons(Collection filterButtonsToDelete) { + final ProxyTargetType targetTypeToDelete = filterButtonsToDelete.iterator().next(); + return deleteTargetType(targetTypeToDelete); + } + + @Override + protected String getFilterButtonIdPrefix() { + return UIComponentIdProvider.TARGET_TYPE_ID_PREFIXS; + } + + @Override + protected void editButtonClickListener(ProxyTargetType clickedFilter) { + final Window updateWindow = targetTypeWindowBuilder.getWindowForUpdate(clickedFilter); + + updateWindow.setCaption(i18n.getMessage("caption.update", i18n.getMessage("caption.type"))); + UI.getCurrent().addWindow(updateWindow); + updateWindow.setVisible(Boolean.TRUE); + } + + @Override + protected Class getFilterMasterEntityType() { + return ProxyTarget.class; + } + + @Override + protected EventView getView() { + return EventView.DEPLOYMENT; + } + + @Override + protected boolean deleteTargetType(ProxyTargetType targetTypeToDelete) { + try{ + targetTypeManagement.delete(targetTypeToDelete.getId()); + eventBus.publish(EventTopics.ENTITY_MODIFIED, this, + new EntityModifiedEventPayload(EntityModifiedEventPayload.EntityModifiedEventType.ENTITY_REMOVED, getFilterMasterEntityType(), + ProxyTargetType.class, targetTypeToDelete.getId())); + return true; + } catch (TargetTypeInUseException exception){ + LOG.trace("Target type already in use exception: {}", exception.getMessage()); + uiNotification.displayValidationError(i18n.getMessage(exception.getMessage())); + } + return false; + } + + @Override + protected Collection filterExistingTargetTypeIds(Collection targetTypeIds) { + return targetTypeManagement.get(targetTypeIds).stream().map(Identifiable::getId).collect(Collectors.toSet()); + } + + @Override + protected boolean isDeletionAllowed() { + return permissionChecker.hasDeleteTargetPermission(); + } + + @Override + protected boolean isEditAllowed() { + return permissionChecker.hasUpdateRepositoryPermission(); + } + +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/AddTargetTypeWindowController.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/AddTargetTypeWindowController.java new file mode 100644 index 000000000..773393f62 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/AddTargetTypeWindowController.java @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2021 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.ui.management.targettag.targettype; + +import java.util.stream.Collectors; +import org.eclipse.hawkbit.repository.TargetTypeManagement; +import org.eclipse.hawkbit.repository.model.TargetType; +import org.eclipse.hawkbit.ui.common.AbstractAddNamedEntityWindowController; +import org.eclipse.hawkbit.ui.common.CommonUiDependencies; +import org.eclipse.hawkbit.ui.common.EntityWindowLayout; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyIdentifiableEntity; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTarget; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTargetType; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType; +import org.eclipse.hawkbit.ui.common.targettype.ProxyTargetTypeValidator; +import org.springframework.util.StringUtils; + +/** + * Add target type window controller + */ +public class AddTargetTypeWindowController + extends AbstractAddNamedEntityWindowController { + + private final TargetTypeWindowLayout layout; + private final ProxyTargetTypeValidator validator; + private final TargetTypeManagement targetTypeManagement; + + /** + * Constructor for AddTargetTypeWindowController + * + * @param uiDependencies + * {@link CommonUiDependencies} + * @param targetTypeManagement + * targetTypeManagement + * @param layout + * TargetTypeWindowLayout + */ + public AddTargetTypeWindowController(final CommonUiDependencies uiDependencies, + final TargetTypeManagement targetTypeManagement, final TargetTypeWindowLayout layout) { + super(uiDependencies); + + this.targetTypeManagement = targetTypeManagement; + this.layout = layout; + this.validator = new ProxyTargetTypeValidator(uiDependencies); + } + + @Override + public EntityWindowLayout getLayout() { + return layout; + } + + @Override + protected ProxyTargetType buildEntityFromProxy(final ProxyTargetType proxyEntity) { + return new ProxyTargetType(); + } + + @Override + protected TargetType persistEntityInRepository(final ProxyTargetType entity) { + return targetTypeManagement.create(getEntityFactory().targetType().create() + .name(entity.getName()).description(entity.getDescription()).colour(entity.getColour()) + .compatible(entity.getSelectedDsTypes().stream().map(ProxyType::getId) + .collect(Collectors.toSet()))); + } + + @Override + protected Class getEntityClass() { + return ProxyTargetType.class; + } + + @Override + protected Class getParentEntityClass() { + return ProxyTarget.class; + } + + @Override + protected boolean isEntityValid(final ProxyTargetType entity) { + final String trimmedName = StringUtils.trimWhitespace(entity.getName()); + return validator.isEntityValid(entity, () -> targetTypeManagement.getByName(trimmedName).isPresent()); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/DsTypeSelectedGrid.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/DsTypeSelectedGrid.java new file mode 100644 index 000000000..b579036ba --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/DsTypeSelectedGrid.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2021 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.ui.management.targettag.targettype; + +import com.vaadin.ui.Grid; +import com.vaadin.ui.themes.ValoTheme; +import org.eclipse.hawkbit.ui.common.builder.GridComponentBuilder; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType; +import org.eclipse.hawkbit.ui.common.grid.AbstractGrid; +import org.eclipse.hawkbit.ui.common.grid.selection.RangeSelectionModel; +import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; + +/** + * Distribution Set Selected Type grid which is shown on the Target Type + * Create/Update popup layout. + */ +public class DsTypeSelectedGrid extends Grid { + private static final long serialVersionUID = 1L; + + private final VaadinMessageSource i18n; + + /** + * Constructor for DsTypeSelectedGrid + * + * @param i18n + * VaadinMessageSource + */ + public DsTypeSelectedGrid(final VaadinMessageSource i18n) { + this.i18n = i18n; + + init(); + } + + private void init() { + setSizeFull(); + setHeightUndefined(); + addStyleName(ValoTheme.TABLE_NO_HORIZONTAL_LINES); + addStyleName(ValoTheme.TABLE_NO_STRIPES); + addStyleName(ValoTheme.TABLE_NO_VERTICAL_LINES); + addStyleName(ValoTheme.TABLE_SMALL); + // used to deactivate cell text selection by user + addStyleName(AbstractGrid.MULTI_SELECT_STYLE); + + setId(SPUIDefinitions.TWIN_TABLE_SELECTED_ID); + setSelectionModel(new RangeSelectionModel<>(i18n)); + + addColumns(); + } + + private void addColumns() { + GridComponentBuilder.addColumn(this, ProxyType::getName).setId(UIComponentIdProvider.DIST_TYPE_TABLE_SELECTED_ID) + .setCaption(i18n.getMessage("header.dt.twintable.selected")) + .setDescriptionGenerator(ProxyType::getDescription); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/DsTypeSourceGrid.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/DsTypeSourceGrid.java new file mode 100644 index 000000000..6a0fd9dad --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/DsTypeSourceGrid.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2021 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.ui.management.targettag.targettype; + +import com.vaadin.ui.Grid; +import com.vaadin.ui.themes.ValoTheme; +import org.eclipse.hawkbit.ui.common.builder.GridComponentBuilder; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType; +import org.eclipse.hawkbit.ui.common.grid.AbstractGrid; +import org.eclipse.hawkbit.ui.common.grid.selection.RangeSelectionModel; +import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; + +/** + * Distribution Set Source Type grid which is shown on the Target Type + * Create/Update popup layout. + */ +public class DsTypeSourceGrid extends Grid { + private static final long serialVersionUID = 1L; + + private final VaadinMessageSource i18n; + + /** + * Constructor for DsTypeSourceGrid + * + * @param i18n + * VaadinMessageSource + */ + public DsTypeSourceGrid(final VaadinMessageSource i18n) { + this.i18n = i18n; + + init(); + } + + private void init() { + setSizeFull(); + setHeightUndefined(); + addStyleName(ValoTheme.TABLE_NO_HORIZONTAL_LINES); + addStyleName(ValoTheme.TABLE_NO_STRIPES); + addStyleName(ValoTheme.TABLE_NO_VERTICAL_LINES); + addStyleName(ValoTheme.TABLE_SMALL); + // used to deactivate cell text selection by user + addStyleName(AbstractGrid.MULTI_SELECT_STYLE); + + setId(SPUIDefinitions.TWIN_TABLE_SOURCE_ID); + setSelectionModel(new RangeSelectionModel<>(i18n)); + + addColumns(); + } + + private void addColumns() { + GridComponentBuilder.addColumn(this, ProxyType::getName).setId(UIComponentIdProvider.DIST_TYPE_TABLE_SOURCE_ID) + .setCaption(i18n.getMessage("header.dt.twintable.available")) + .setDescriptionGenerator(ProxyType::getDescription); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/TargetTypeDsTypeSelectLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/TargetTypeDsTypeSelectLayout.java new file mode 100644 index 000000000..85264a0cf --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/TargetTypeDsTypeSelectLayout.java @@ -0,0 +1,179 @@ +/** + * Copyright (c) 2021 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.ui.management.targettag.targettype; + +import com.google.common.collect.Sets; +import com.vaadin.icons.VaadinIcons; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.Button; +import com.vaadin.ui.Component; +import com.vaadin.ui.CustomField; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.VerticalLayout; +import org.eclipse.hawkbit.repository.DistributionSetTypeManagement; +import org.eclipse.hawkbit.repository.model.DistributionSetType; +import org.eclipse.hawkbit.ui.common.data.mappers.TypeToProxyTypeMapper; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyIdentifiableEntity; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType; +import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; +import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorder; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; +import org.springframework.data.domain.PageRequest; +import org.springframework.util.CollectionUtils; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Layout for the distribution sets select grids for managing Target + * Types on the Distributions View. + */ +public class TargetTypeDsTypeSelectLayout extends CustomField> { + private static final long serialVersionUID = 1L; + + private static final int MAX_DS_TYPE_QUERY = 500; + + private final VaadinMessageSource i18n; + + private final transient TypeToProxyTypeMapper dsTypeToProxyTypeMapper; + + private DsTypeSelectedGrid selectedGrid; + private DsTypeSourceGrid sourceGrid; + + private final List allDsTypes; + private Set selectedDsTypes; + + private final HorizontalLayout layout; + + /** + * Constructor + * + * @param i18n + * VaadinMessageSource + * @param distributionSetTypeManagement + * distributionSetTypeManagement + */ + public TargetTypeDsTypeSelectLayout(final VaadinMessageSource i18n, + final DistributionSetTypeManagement distributionSetTypeManagement) { + this.i18n = i18n; + this.dsTypeToProxyTypeMapper = new TypeToProxyTypeMapper<>(); + + this.allDsTypes = distributionSetTypeManagement.findAll(PageRequest.of(0, MAX_DS_TYPE_QUERY)) + .map(dsTypeToProxyTypeMapper::map).getContent(); + this.selectedDsTypes = new HashSet<>(); + + + this.layout = new HorizontalLayout(); + this.layout.setSpacing(false); + this.layout.setMargin(false); + this.layout.setSizeFull(); + this.layout.setWidth("400px"); + + buildLayout(); + } + + private void buildLayout() { + final VerticalLayout selectButtonLayout = new VerticalLayout(); + selectButtonLayout.setSpacing(false); + selectButtonLayout.setMargin(false); + + final Button selectButton = SPUIComponentProvider.getButton(UIComponentIdProvider.SELECT_DIST_TYPE, "", "", + "arrow-button", true, VaadinIcons.FORWARD, SPUIButtonStyleNoBorder.class); + selectButton.addClickListener(event -> addDsTypeToSelectedGrid()); + + final Button unSelectButton = SPUIComponentProvider.getButton(UIComponentIdProvider.UNSELECT_DIST_TYPE, "", "", "arrow-button", + true, VaadinIcons.BACKWARDS, SPUIButtonStyleNoBorder.class); + unSelectButton.addClickListener(event -> removeDsTypeFromSelectedGrid()); + + selectButtonLayout.addComponent(selectButton); + selectButtonLayout.addComponent(unSelectButton); + selectButtonLayout.setComponentAlignment(selectButton, Alignment.MIDDLE_CENTER); + selectButtonLayout.setComponentAlignment(unSelectButton, Alignment.MIDDLE_CENTER); + + sourceGrid = buildSourceGrid(); + selectedGrid = buildSelectedGrid(); + + layout.addComponent(sourceGrid); + layout.addComponent(selectButtonLayout); + layout.addComponent(selectedGrid); + layout.setComponentAlignment(sourceGrid, Alignment.MIDDLE_LEFT); + layout.setComponentAlignment(selectButtonLayout, Alignment.MIDDLE_CENTER); + layout.setComponentAlignment(selectedGrid, Alignment.MIDDLE_RIGHT); + layout.setExpandRatio(sourceGrid, 0.45F); + layout.setExpandRatio(selectButtonLayout, 0.07F); + layout.setExpandRatio(selectedGrid, 0.48F); + } + + private void addDsTypeToSelectedGrid() { + final Set selectedSourceDsTypes = sourceGrid.getSelectedItems(); + if (CollectionUtils.isEmpty(selectedSourceDsTypes)) { + return; + } + + setValue(Sets.union(selectedDsTypes, selectedSourceDsTypes).immutableCopy()); + } + + private void removeDsTypeFromSelectedGrid() { + final Set selectedSelectedDsTypes = selectedGrid.getSelectedItems(); + if (CollectionUtils.isEmpty(selectedSelectedDsTypes)) { + return; + } + + setValue(Sets.difference(selectedDsTypes, selectedSelectedDsTypes).immutableCopy()); + } + + private DsTypeSourceGrid buildSourceGrid() { + final DsTypeSourceGrid grid = new DsTypeSourceGrid(i18n); + grid.setItems(allDsTypes); + + if (!CollectionUtils.isEmpty(allDsTypes)) { + grid.select(allDsTypes.get(0)); + } + + return grid; + } + + private DsTypeSelectedGrid buildSelectedGrid() { + final DsTypeSelectedGrid dsTypeSelectedGrid = new DsTypeSelectedGrid(i18n); + dsTypeSelectedGrid.setItems(selectedDsTypes); + return dsTypeSelectedGrid; + } + + @Override + public Set getValue() { + return selectedDsTypes; + } + + @Override + protected Component initContent() { + return layout; + } + + @Override + protected void doSetValue(final Set value) { + if (value == null) { + return; + } + + selectedDsTypes = value; + + selectedGrid.setItems(selectedDsTypes); + sourceGrid.setItems(getSourceDsTypes()); + } + + private List getSourceDsTypes() { + final Set selectedDsTypeIds = selectedDsTypes.stream().map(ProxyIdentifiableEntity::getId) + .collect(Collectors.toSet()); + return allDsTypes.stream().filter(dsType -> !selectedDsTypeIds.contains(dsType.getId())) + .collect(Collectors.toList()); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/TargetTypeWindowBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/TargetTypeWindowBuilder.java new file mode 100644 index 000000000..af5fa3774 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/TargetTypeWindowBuilder.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2021 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.ui.management.targettag.targettype; + +import com.vaadin.ui.Window; +import org.eclipse.hawkbit.repository.DistributionSetTypeManagement; +import org.eclipse.hawkbit.repository.TargetTypeManagement; +import org.eclipse.hawkbit.ui.common.AbstractEntityWindowBuilder; +import org.eclipse.hawkbit.ui.common.CommonDialogWindow; +import org.eclipse.hawkbit.ui.common.CommonUiDependencies; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTargetType; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; + +/** + * Builder for target type window + */ +public class TargetTypeWindowBuilder extends AbstractEntityWindowBuilder { + + private final TargetTypeManagement targetTypeManagement; + private final DistributionSetTypeManagement dsTypeManagement; + + /** + * Constructor for TargetTypeWindowBuilder + * + * @param uiDependencies + * {@link CommonUiDependencies} + * @param dsTypeManagement + * DistributionSetTypeManagement + * @param targetTypeManagement + * TargetTypeManagement + */ + public TargetTypeWindowBuilder(final CommonUiDependencies uiDependencies, final TargetTypeManagement targetTypeManagement, + final DistributionSetTypeManagement dsTypeManagement) { + super(uiDependencies); + + this.targetTypeManagement = targetTypeManagement; + this.dsTypeManagement = dsTypeManagement; + } + + @Override + protected String getWindowId() { + return UIComponentIdProvider.TAG_POPUP_ID; + } + + @Override + public Window getWindowForAdd() { + CommonDialogWindow window = getWindowForNewEntity(new AddTargetTypeWindowController(uiDependencies, targetTypeManagement, + new TargetTypeWindowLayout(uiDependencies, dsTypeManagement))); + window.hideMandatoryExplanation(); + return window; + } + + @Override + public Window getWindowForUpdate(final ProxyTargetType proxyType) { + CommonDialogWindow window = getWindowForEntity(proxyType, new UpdateTargetTypeWindowController(uiDependencies, targetTypeManagement, + new TargetTypeWindowLayout(uiDependencies, dsTypeManagement))); + window.hideMandatoryExplanation(); + return window; + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/TargetTypeWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/TargetTypeWindowLayout.java new file mode 100644 index 000000000..3210c59f9 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/TargetTypeWindowLayout.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2021 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.ui.management.targettag.targettype; + +import com.vaadin.ui.ComponentContainer; +import org.eclipse.hawkbit.repository.DistributionSetTypeManagement; +import org.eclipse.hawkbit.ui.common.CommonUiDependencies; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTargetType; +import org.eclipse.hawkbit.ui.management.tag.TagWindowLayout; + +/** + * Target type window layout + */ +public class TargetTypeWindowLayout extends TagWindowLayout { + private final TargetTypeWindowLayoutComponentBuilder targetTypeWindowLayoutComponentBuilder; + + private final TargetTypeDsTypeSelectLayout targetTypeDsTypeSelectLayout; + + /** + * Constructor for TargetTypeWindowLayout + * + * @param uiDependencies + * {@link CommonUiDependencies} + * @param distributionSetTypeManagement + * DistributionSetTypeManagement + */ + public TargetTypeWindowLayout(final CommonUiDependencies uiDependencies, + final DistributionSetTypeManagement distributionSetTypeManagement) { + super(uiDependencies); + + this.targetTypeWindowLayoutComponentBuilder = new TargetTypeWindowLayoutComponentBuilder(i18n, distributionSetTypeManagement); + + this.targetTypeDsTypeSelectLayout = targetTypeWindowLayoutComponentBuilder.createTargetTypeDsSelectLayout(binder); + + this.colorPickerComponent.getColorPickerBtn().setCaption(i18n.getMessage("label.choose.type.color")); + } + + @Override + public ComponentContainer getRootComponent() { + final ComponentContainer rootLayout = super.getRootComponent(); + + rootLayout.addComponent(targetTypeDsTypeSelectLayout); + + return rootLayout; + } + +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/TargetTypeWindowLayoutComponentBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/TargetTypeWindowLayoutComponentBuilder.java new file mode 100644 index 000000000..99b810ccd --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/TargetTypeWindowLayoutComponentBuilder.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2021 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.ui.management.targettag.targettype; + +import com.vaadin.data.Binder; +import org.eclipse.hawkbit.repository.DistributionSetTypeManagement; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTargetType; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; + +/** + * Builder for target type window layout component + */ +public class TargetTypeWindowLayoutComponentBuilder { + + private final VaadinMessageSource i18n; + private final DistributionSetTypeManagement distributionSetTypeManagement; + + /** + * Constructor for TargetTypeWindowLayoutComponentBuilder + * + * @param i18n + * VaadinMessageSource + * @param distributionSetTypeManagement + * distributionSetTypeManagement + */ + public TargetTypeWindowLayoutComponentBuilder(final VaadinMessageSource i18n, + final DistributionSetTypeManagement distributionSetTypeManagement) { + this.i18n = i18n; + this.distributionSetTypeManagement = distributionSetTypeManagement; + } + + /** + * Create target type layout + * + * @param binder + * Vaadin binder + * + * @return layout of target type distribution set selection + */ + public TargetTypeDsTypeSelectLayout createTargetTypeDsSelectLayout(final Binder binder) { + + final TargetTypeDsTypeSelectLayout targetTypeDsTypeSelectLayout = new TargetTypeDsTypeSelectLayout(i18n, distributionSetTypeManagement); + targetTypeDsTypeSelectLayout.setRequiredIndicatorVisible(false); + + binder.forField(targetTypeDsTypeSelectLayout) + .bind(ProxyTargetType::getSelectedDsTypes, ProxyTargetType::setSelectedDsTypes); + + return targetTypeDsTypeSelectLayout; + } + +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/UpdateTargetTypeWindowController.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/UpdateTargetTypeWindowController.java new file mode 100644 index 000000000..55c0e80e1 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/targettype/UpdateTargetTypeWindowController.java @@ -0,0 +1,131 @@ +/** + * Copyright (c) 2021 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.ui.management.targettag.targettype; + +import java.util.Collections; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import org.eclipse.hawkbit.repository.TargetTypeManagement; +import org.eclipse.hawkbit.repository.model.DistributionSetType; +import org.eclipse.hawkbit.repository.model.TargetType; +import org.eclipse.hawkbit.ui.common.AbstractEntityWindowLayout; +import org.eclipse.hawkbit.ui.common.AbstractUpdateNamedEntityWindowController; +import org.eclipse.hawkbit.ui.common.CommonUiDependencies; +import org.eclipse.hawkbit.ui.common.data.mappers.TypeToProxyTypeMapper; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyIdentifiableEntity; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTarget; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTargetType; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType; +import org.eclipse.hawkbit.ui.common.targettype.ProxyTargetTypeValidator; +import org.springframework.util.StringUtils; + +/** + * Controller for update target type window + */ +public class UpdateTargetTypeWindowController + extends AbstractUpdateNamedEntityWindowController { + + private final TargetTypeManagement targetTypeManagement; + private final TypeToProxyTypeMapper dsTypeToProxyTypeMapper; + private final TargetTypeWindowLayout layout; + private final ProxyTargetTypeValidator validator; + + private String nameBeforeEdit; + + /** + * Constructor for UpdateTargetTypeWindowController + * + * @param uiDependencies + * {@link CommonUiDependencies} + * @param targetTypeManagement + * TargetTypeManagement + * @param layout + * TargetTypeWindowLayout + */ + public UpdateTargetTypeWindowController(final CommonUiDependencies uiDependencies, + final TargetTypeManagement targetTypeManagement, + final TargetTypeWindowLayout layout) { + super(uiDependencies); + + this.targetTypeManagement = targetTypeManagement; + this.dsTypeToProxyTypeMapper = new TypeToProxyTypeMapper<>(); + this.layout = layout; + this.validator = new ProxyTargetTypeValidator(uiDependencies); + } + + @Override + public AbstractEntityWindowLayout getLayout() { + return layout; + } + + @Override + protected ProxyTargetType buildEntityFromProxy(final ProxyTargetType proxyEntity) { + final ProxyTargetType dsType = new ProxyTargetType(); + dsType.setId(proxyEntity.getId()); + dsType.setName(proxyEntity.getName()); + dsType.setDescription(proxyEntity.getDescription()); + dsType.setColour(proxyEntity.getColour()); + dsType.setSelectedDsTypes(getDsTypesByDsTypeId(proxyEntity.getId())); + nameBeforeEdit = proxyEntity.getName(); + + return dsType; + } + + private Set getDsTypesByDsTypeId(final Long id) { + Optional targetType = targetTypeManagement.get(id); + return targetType.map(type -> type.getCompatibleDistributionSetTypes().stream() + .map(dsTypeToProxyTypeMapper::map).collect(Collectors.toSet())).orElse(Collections.emptySet()); + + } + + @Override + protected TargetType persistEntityInRepository(final ProxyTargetType entity) { + + Set dsTypesIds = getDsTypesByDsTypeId(entity.getId()).stream().map(ProxyType::getId).collect(Collectors.toSet()); + + Set selectedDsIds = entity.getSelectedDsTypes().stream().map(ProxyType::getId).collect(Collectors.toSet()); + + Set dsTypesForRemoval = getDsTypesByDsTypeId(entity.getId()).stream().map(ProxyType::getId) + .filter(dsType -> !selectedDsIds.contains(dsType)).collect(Collectors.toSet()); + Set dsTypesForAdd = selectedDsIds.stream() + .filter(dsType -> !dsTypesIds.contains(dsType)).collect(Collectors.toSet()); + + dsTypesForRemoval.forEach(dsType -> targetTypeManagement.unassignDistributionSetType(entity.getId(), dsType)); + + if (!dsTypesForAdd.isEmpty()) { + targetTypeManagement.assignCompatibleDistributionSetTypes(entity.getId(), dsTypesForAdd); + } + + return targetTypeManagement.update(getEntityFactory().targetType().update(entity.getId()) + .name(entity.getName()).description(entity.getDescription()).colour(entity.getColour())); + + } + + @Override + protected Class getEntityClass() { + return ProxyTargetType.class; + } + + @Override + protected Class getParentEntityClass() { + return ProxyTarget.class; + } + + @Override + protected boolean isEntityValid(final ProxyTargetType entity) { + final String trimmedName = StringUtils.trimWhitespace(entity.getName()); + return validator.isEntityValid(entity, + () -> hasNamedChanged(trimmedName) && targetTypeManagement.getByName(trimmedName).isPresent()); + } + + private boolean hasNamedChanged(final String trimmedName) { + return !nameBeforeEdit.equals(trimmedName); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUIDefinitions.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUIDefinitions.java index 4aab5dffa..9c52db20e 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUIDefinitions.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUIDefinitions.java @@ -262,6 +262,11 @@ public final class SPUIDefinitions { */ public static final String NO_TAG_BUTTON_ID = "no_tag_button"; + /** + * Id of "NO TARGET TYPE" button. + */ + public static final String NO_TARGET_TYPE_BUTTON_ID = "no_target_type_button"; + /** * DELETE column/button. */ diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUIStyleDefinitions.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUIStyleDefinitions.java index bcb814f65..a5c0d0d27 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUIStyleDefinitions.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUIStyleDefinitions.java @@ -116,6 +116,11 @@ public final class SPUIStyleDefinitions { */ public static final String SIMPLE_FILTER_HEADER = "simple-tag-filter-header"; + /** + * Target type filter header layout. + */ + public static final String TARGET_TYPE_FILTER_HEADER = "target-type-filter-header"; + /** * Simple tag filter header layout. */ diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java index c8d4f5901..755fd0893 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java @@ -580,6 +580,16 @@ public final class UIComponentIdProvider { */ public static final String TARGET_TAG_DROP_AREA_ID = "target.tag.drop.area"; + /** + * ID-Target tag table. + */ + public static final String TARGET_TYPE_TABLE_ID = "target.type.tableId"; + + /** + * ID-Target type table drop area. + */ + public static final String TARGET_TYPE_DROP_AREA_ID = "target.type.drop.area"; + /** * ID-Distibution tag table. */ @@ -929,6 +939,11 @@ public final class UIComponentIdProvider { */ public static final String SIMPLE_FILTER_ACCORDION_TAB = "simple.filter.accordion.tab"; + /** + * ID - target type filter- Accordion-Tab + */ + public static final String TARGET_TYPE_FILTER_ACCORDION_TAB = "target.type.filter.accordion.tab"; + /** * ID - custom filter- Accordion-Tab */ @@ -1337,6 +1352,22 @@ public final class UIComponentIdProvider { * Table multiselect for selecting DistType */ public static final String SELECT_DIST_TYPE = "select-dist-type"; + + /** + * Table multiselect for unselecting DistType + */ + public static final String UNSELECT_DIST_TYPE = "unselect-dist-type"; + + /** + * ID for DistType source value table + */ + public static final String DIST_TYPE_TABLE_SOURCE_ID = "dsTypeSourceId"; + + /** + * ID for DistType selected value table + */ + public static final String DIST_TYPE_TABLE_SELECTED_ID = "dsTypeSelectedId"; + /** * ID for download anonymous checkbox */ @@ -1528,6 +1559,10 @@ public final class UIComponentIdProvider { * Target tag button id prefix. */ public static final String TARGET_TAG_ID_PREFIXS = "target.tag"; + /** + * Target type button id prefix. + */ + public static final String TARGET_TYPE_ID_PREFIXS = "target.type"; /** * Distribution tag button id prefix. */ diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIMessageIdProvider.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIMessageIdProvider.java index 1f9b2010a..c1cae84dd 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIMessageIdProvider.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIMessageIdProvider.java @@ -89,10 +89,14 @@ public final class UIMessageIdProvider { public static final String CAPTION_TAG = "caption.tag"; + public static final String CAPTION_TYPE = "caption.type"; + public static final String LABEL_DROP_AREA_UPLOAD = "label.drop.area.upload"; public static final String LABEL_NO_TAG = "label.no.tag"; + public static final String LABEL_NO_TARGET_TYPE = "label.no.target.type"; + public static final String LABEL_CREATE_FILTER = "label.create.filter"; public static final String LABEL_EDIT_FILTER = "label.edit.filter"; diff --git a/hawkbit-ui/src/main/resources/messages.properties b/hawkbit-ui/src/main/resources/messages.properties index ea6969d76..aa14750de 100644 --- a/hawkbit-ui/src/main/resources/messages.properties +++ b/hawkbit-ui/src/main/resources/messages.properties @@ -70,6 +70,8 @@ header.first.delete.swmodule.type.table = Software Module Type header.second.delete.swmodule.type.table = Discard header.dist.twintable.selected=Selected header.dist.twintable.available=Available +header.dt.twintable.available=Distribution set types +header.dt.twintable.selected=Compatible distribution set types header.target.installed = Installed header.target.assigned = Assigned header.type=Type @@ -112,6 +114,7 @@ caption.entity.missing.error = Entity is missing caption.new.softwaremodule.application = Configure New Application caption.new.softwaremodule.os = Configure New OS caption.filter.simple = Simple Filter +caption.filter.type = Type Filter caption.filter.custom = Custom Filter caption.metadata = Metadata caption.rollout.name = Rollout name @@ -177,6 +180,7 @@ label.menu = Menu label.active = Active label.action.id = Action Id label.no.tag = NO TAG +label.no.target.type = NO TARGET TYPE label.action.forced = Forced label.action.type = Type label.action.soft = Soft @@ -408,6 +412,7 @@ message.cannot.delete = Cannot be deleted message.check.softwaremodule = Please provide both name and version! message.duplicate.softwaremodule = {0} : {1} already exists! message.tag.delete = Please deselect the tag {0} before deleting +message.targettype.delete = Please deselect the target type {0} before deleting message.dist.type.check.delete = Please deselect the distribution type {0} before deleting message.cannot.delete.default.dstype = Default distribution set type cannot be deleted message.swmodule.type.check.delete = Please deselect the Software Module type {0} before deleting @@ -480,6 +485,7 @@ message.error.missing.filtername = Please select target filter name message.error.missing.controllerId = Missing Controller Id message.error.missing.nameorversion = Missing Name or Version message.error.missing.nameorversionortype = Missing Name or Version or Type +message.error.missing.typename = Please select target type name message.error.distributionset.invalid = Action not allowed for invalid distribution set {0} message.type.delete = Please unclick the type {0}, then try to delete message.error.dist.set.type.update= Distribution set type is already assigned to set(s) and cannot be changed @@ -806,6 +812,8 @@ color.not.exists=There is no mapping for the provided colour {0} caption.entity.target.tag = Target Tag caption.entity.target.tags = Target Tags +caption.entity.target.type = Target Type +caption.entity.target.types = Target Types caption.entity.distribution.tag = Distribution Tag caption.entity.distribution.tags = Distribution Tags caption.entity.distribution.type = Distribution set Type