Feature target type filter (#1197)

* Added Target type filter with drag and drop support

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Removed the unused enums and target type filter button class

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Resolved merge conflicts

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Fixed java doc issue with the method link in the comment

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Fixed the IN query overflow for target Type assignment

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Fixed Review comments

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>
This commit is contained in:
Anand Kumar
2021-10-27 15:24:09 +02:00
committed by GitHub
parent dea6fa3ce6
commit 70779d1ac7
38 changed files with 1409 additions and 398 deletions

View File

@@ -140,4 +140,4 @@ public enum TargetFields implements FieldNameProvider {
public String getFieldName() {
return fieldName;
}
}
}

View File

@@ -27,9 +27,11 @@ public class FilterParams {
private final Boolean selectTargetWithNoTag;
private final String[] filterByTagNames;
private final Long filterByDistributionId;
private final Boolean selectTargetWithNoTargetType;
private final Long filterByTargetType;
/**
* Constructor.
* Constructor for the filter parameters of a Simple Filter.
*
* @param filterByInstalledOrAssignedDistributionSetId
* if set, a filter is added for the given
@@ -56,6 +58,34 @@ public class FilterParams {
this.filterByDistributionId = filterByInstalledOrAssignedDistributionSetId;
this.selectTargetWithNoTag = selectTargetWithNoTag;
this.filterByTagNames = filterByTagNames;
this.selectTargetWithNoTargetType = false;
this.filterByTargetType = null;
}
/**
* Constructor for the filter parameters of a Type Filter.
*
* @param filterBySearchText
* if set, a filter is added for the given search text
* @param filterByInstalledOrAssignedDistributionSetId
* if set, a filter is added for the given
* {@link DistributionSet#getId()}
* @param selectTargetWithNoType
* if true, a filter is added with no type
* @param filterByType
* if set, a filter is added for the given target type
*/
public FilterParams(final String filterBySearchText, final Long filterByInstalledOrAssignedDistributionSetId,
final Boolean selectTargetWithNoType, final Long filterByType) {
this.filterBySearchText = filterBySearchText;
this.filterByDistributionId = filterByInstalledOrAssignedDistributionSetId;
this.filterByStatus = null;
this.overdueState = null;
this.selectTargetWithNoTag = false;
this.filterByTagNames = null;
this.selectTargetWithNoTargetType = selectTargetWithNoType;
this.filterByTargetType = filterByType;
}
/**
@@ -120,4 +150,23 @@ public class FilterParams {
public String[] getFilterByTagNames() {
return filterByTagNames;
}
/**
* Gets the flag indicating if no target type filter is used. <br>
* If set to <code>false</code> this filter is disabled.
*
* @return the flag indicating if no target type filter is used
*/
public Boolean getSelectTargetWithNoTargetType() {
return selectTargetWithNoTargetType;
}
/**
* Gets the target type
*
* @return the target type that are used to filter for
*/
public Long getFilterByTargetType() {
return filterByTargetType;
}
}

View File

@@ -8,14 +8,6 @@
*/
package org.eclipse.hawkbit.repository;
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;
@@ -35,12 +27,22 @@ import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.security.access.prepost.PreAuthorize;
import javax.validation.ConstraintViolationException;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* Management service for {@link Target}s.
*
@@ -106,11 +108,29 @@ public interface TargetManagement {
*
* @throws EntityNotFoundException
* if distribution set with given ID does not exist
*
* @deprecated this method {@link TargetManagement#countByFilters(FilterParams)} should be used instead.
*/
@Deprecated
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
long countByFilters(Collection<TargetUpdateStatus> status, Boolean overdueState, String searchText,
Long installedOrAssignedDistributionSetId, Boolean selectTargetWithNoTag, String... tagNames);
/**
* Count {@link Target}s for all the given filter parameters.
*
* @param filterParams
* the filters to apply; only filters are enabled that have
* non-null value; filters are AND-gated
*
* @return the found number {@link Target}s
*
* @throws EntityNotFoundException
* if distribution set with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
long countByFilters(@NotNull final FilterParams filterParams);
/**
* Counts number of targets with given with given distribution set Id
*
@@ -607,6 +627,38 @@ public interface TargetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
TargetTagAssignmentResult toggleTagAssignment(@NotEmpty Collection<String> controllerIds, @NotEmpty String tagName);
/**
* Initiates {@link TargetType} assignment to given {@link Target}s. If some
* targets in the list have the {@link TargetType} not yet assigned, they will
* get assigned. If all targets are already of that type, there will be no
* un-assignment.
*
* @param controllerIds
* to set the type to
* @param typeId
* to assign targets to
* @return {@link TargetTypeAssignmentResult} with all meta data of the
* assignment outcome.
*
* @throws EntityNotFoundException
* if target type with given id does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
TargetTypeAssignmentResult assignType(@NotEmpty Collection<String> controllerIds, @NotNull Long typeId);
/**
* Initiates {@link TargetType} un-assignment to given {@link Target}s. The type
* of the targets will be set to {@code null}
*
* @param controllerIds
* to remove the type from
* @return {@link TargetTypeAssignmentResult} with all meta data of the
* assignment outcome.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
TargetTypeAssignmentResult unAssignType(@NotEmpty Collection<String> controllerIds);
/**
* Un-assign a {@link TargetTag} assignment to given {@link Target}.
*
@@ -638,6 +690,8 @@ public interface TargetManagement {
*
* @param controllerID
* to un-assign for
* @param targetTypeId
* Target type id
* @return the unassigned target
*
* @throws EntityNotFoundException
@@ -854,4 +908,5 @@ public interface TargetManagement {
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
TargetMetadata updateMetadata(@NotEmpty String controllerId, @NotNull MetaData metadata);
}

View File

@@ -0,0 +1,42 @@
/**
* 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.repository.model;
import java.util.List;
/**
* Result object for {@link TargetType} assignments.
*
*/
public class TargetTypeAssignmentResult extends AbstractAssignmentResult<Target> {
private final TargetType targetType;
/**
* Constructor.
*
* @param alreadyAssigned
* count of already assigned (ignored) elements
* @param assigned
* {@link List} of assigned {@link Target}s.
* @param unassigned
* {@link List} of unassigned {@link Target}s.
* @param targetType
* the assigned or unassigned tag
*/
public TargetTypeAssignmentResult(final int alreadyAssigned, final List<? extends Target> assigned,
final List<? extends Target> unassigned, final TargetType targetType) {
super(alreadyAssigned, assigned, unassigned);
this.targetType = targetType;
}
public TargetType getTargetType() {
return targetType;
}
}

View File

@@ -30,6 +30,7 @@ import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetFilterQueryDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetTagDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetTypeDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TenantConfigurationDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent;
@@ -53,6 +54,8 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetFilterQueryCreat
import org.eclipse.hawkbit.repository.event.remote.entity.TargetFilterQueryUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TenantConfigurationCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TenantConfigurationUpdatedEvent;
@@ -150,6 +153,11 @@ public class EventType {
// rollout stopped due to invalidated distribution set
TYPES.put(43, RolloutStoppedEvent.class);
// target type
TYPES.put(44, TargetTypeCreatedEvent.class);
TYPES.put(45, TargetTypeUpdatedEvent.class);
TYPES.put(46, TargetTypeDeletedEvent.class);
}
private int value;

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa;
import static org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications.orderedByLinkedDistributionSet;
import com.google.common.collect.Lists;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
@@ -65,6 +66,7 @@ import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
@@ -449,11 +451,16 @@ public class JpaTargetManagement implements TargetManagement {
}
@Override
public long countByFilters(final Collection<TargetUpdateStatus> status, final Boolean overdueState,
final String searchText, final Long installedOrAssignedDistributionSetId,
final Boolean selectTargetWithNoTag, final String... tagNames) {
final List<Specification<JpaTarget>> specList = buildSpecificationList(new FilterParams(status, overdueState,
searchText, installedOrAssignedDistributionSetId, selectTargetWithNoTag, tagNames));
public long countByFilters(Collection<TargetUpdateStatus> status, Boolean overdueState, String searchText,
Long installedOrAssignedDistributionSetId, Boolean selectTargetWithNoTag,
String... tagNames) {
return countByFilters(new FilterParams(status, overdueState, searchText, installedOrAssignedDistributionSetId,
selectTargetWithNoTag, tagNames));
}
@Override
public long countByFilters(final FilterParams filterParams) {
final List<Specification<JpaTarget>> specList = buildSpecificationList(filterParams);
return countByCriteriaAPI(specList);
}
@@ -479,6 +486,13 @@ public class JpaTargetManagement implements TargetManagement {
specList.add(TargetSpecifications.hasTags(filterParams.getFilterByTagNames(),
filterParams.getSelectTargetWithNoTag()));
}
if (hasTypesFilterActive(filterParams)) {
specList.add(TargetSpecifications.hasTargetType(filterParams.getFilterByTargetType()));
} else if (hasNoTypeFilterActive(filterParams)) {
specList.add(TargetSpecifications.hasNoTargetType());
}
return specList;
}
@@ -490,6 +504,14 @@ public class JpaTargetManagement implements TargetManagement {
return isNoTagActive || isAtLeastOneTagActive;
}
private static boolean hasTypesFilterActive(final FilterParams filterParams) {
return filterParams.getFilterByTargetType() != null;
}
private static boolean hasNoTypeFilterActive(final FilterParams filterParams) {
return Boolean.TRUE.equals(filterParams.getSelectTargetWithNoTargetType());
}
private Slice<Target> findByCriteriaAPI(final Pageable pageable, final List<Specification<JpaTarget>> specList) {
if (CollectionUtils.isEmpty(specList)) {
return convertPage(targetRepository.findAllWithoutCount(pageable), pageable);
@@ -545,6 +567,59 @@ public class JpaTargetManagement implements TargetManagement {
return result;
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetTypeAssignmentResult assignType(final Collection<String> controllerIds, final Long typeId) {
final TargetType type = targetTypeRepository.findById(typeId)
.orElseThrow(() -> new EntityNotFoundException(TargetType.class, typeId));
final List<JpaTarget> targetsWithSameType = findTargetsByInSpecification(controllerIds,
TargetSpecifications.hasTargetType(typeId));
final List<JpaTarget> targetsWithoutSameType = findTargetsByInSpecification(controllerIds,
TargetSpecifications.hasTargetTypeNot(typeId));
// set new target type to all targets without that type
targetsWithoutSameType.forEach(target -> target.setTargetType(type));
final TargetTypeAssignmentResult result = new TargetTypeAssignmentResult(targetsWithSameType.size(),
Collections
.unmodifiableList(targetsWithoutSameType.stream().map(targetRepository::save).collect(Collectors.toList())),
Collections.emptyList(), type);
// no reason to persist the type
entityManager.detach(type);
return result;
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetTypeAssignmentResult unAssignType(final Collection<String> controllerIds) {
final List<JpaTarget> allTargets = findTargetsByInSpecification(controllerIds, null);
if (allTargets.size() < controllerIds.size()) {
throw new EntityNotFoundException(Target.class, controllerIds,
allTargets.stream().map(Target::getControllerId).collect(Collectors.toList()));
}
// set new target type to null for all targets
allTargets.forEach(target -> target.setTargetType(null));
return new TargetTypeAssignmentResult(0, Collections.emptyList(), Collections
.unmodifiableList(allTargets.stream().map(targetRepository::save).collect(Collectors.toList())), null);
}
private List<JpaTarget> findTargetsByInSpecification(Collection<String> controllerIds,
Specification<JpaTarget> specification) {
return Lists.partition(new ArrayList<>(controllerIds), Constants.MAX_ENTRIES_IN_STATEMENT).stream()
.map(ids -> targetRepository.findAll(TargetSpecifications.hasControllerIdIn(ids).and(specification)))
.flatMap(List::stream).collect(Collectors.toList());
}
@Override
@Transactional
@Retryable(include = {
@@ -625,7 +700,7 @@ public class JpaTargetManagement implements TargetManagement {
/**
* Applies orderedByLinkedDistributionSet spec for order and adds additional
* specs based on filters
*
*
* @param orderByDistributionId
* distribution set used for order
* @param filterParams

View File

@@ -8,12 +8,6 @@
*/
package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.TargetTypeFields;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
@@ -42,9 +36,14 @@ import org.springframework.orm.jpa.vendor.Database;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* JPA implementation of {@link TargetTypeManagement}.
*

View File

@@ -392,12 +392,16 @@ public final class TargetSpecifications {
return (targetRoot, query, cb) -> {
// Since the targetRoot is changed by joining we need to get the
// isNull predicate first
final Predicate targetTypeIsNull = targetRoot.get(JpaTarget_.targetType).isNull();
final Predicate targetTypeIsNull = getTargetTypeIsNullPredicate(targetRoot);
return cb.or(targetTypeIsNull, cb.equal(getDsTypeIdPath(targetRoot), distributionSetTypeId));
};
}
private static Predicate getTargetTypeIsNullPredicate(Root<JpaTarget> targetRoot) {
return targetRoot.get(JpaTarget_.targetType).isNull();
}
/**
* {@link Specification} for retrieving {@link Target}s that are NOT compatible
* with given {@link DistributionSetType}. Compatibility is evaluated by
@@ -532,13 +536,39 @@ public final class TargetSpecifications {
}
/**
* {@link Specification} for retrieving {@link Target}s that have a
* {@link org.eclipse.hawkbit.repository.model.TargetType} assigned
* {@link Specification} for retrieving {@link Target}s by target type id
*
* @param typeId
* the id of the target type
*
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasTargetType() {
return (targetRoot, query, cb) -> cb.isNotNull(targetRoot.get(JpaTarget_.targetType));
public static Specification<JpaTarget> hasTargetType(final long typeId) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaTarget_.targetType).get(JpaTargetType_.id),
typeId);
}
/**
* {@link Specification} for retrieving {@link Target}s by target type id is equal to null
*
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasNoTargetType() {
return (targetRoot, query, cb) -> cb.isNull(targetRoot.get(JpaTarget_.targetType));
}
/**
* {@link Specification} for retrieving {@link Target}s that don't have target
* type assigned
*
* @param typeId
* the id of the target type
*
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasTargetTypeNot(final Long typeId) {
return (targetRoot, query, cb) -> cb.or(getTargetTypeIsNullPredicate(targetRoot),
cb.notEqual(targetRoot.get(JpaTarget_.targetType).get(JpaTargetType_.id), typeId));
}
/**

View File

@@ -26,6 +26,8 @@ import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
import org.eclipse.hawkbit.repository.test.TestConfiguration;
import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
import org.eclipse.hawkbit.repository.test.util.RolloutTestApprovalStrategy;
@@ -133,4 +135,10 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
return distributionSetManagement.toggleTagAssignment(
sets.stream().map(DistributionSet::getId).collect(Collectors.toList()), tag.getName());
}
protected TargetTypeAssignmentResult initiateTypeAssignment(final Collection<Target> targets, final TargetType type) {
return targetManagement.assignType(
targets.stream().map(Target::getControllerId).collect(Collectors.toList()), type.getId());
}
}

View File

@@ -58,7 +58,11 @@ class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final TargetTag targTagZ = targetTagManagement.create(entityFactory.tag().create().name("TargTag-Z"));
final TargetTag targTagW = targetTagManagement.create(entityFactory.tag().create().name("TargTag-W"));
final DistributionSet setA = testdataFactory.createDistributionSet("");
final DistributionSet setA = testdataFactory.createDistributionSet("A");
final DistributionSet setB = testdataFactory.createDistributionSet("B");
final TargetType targetTypeX = testdataFactory.createTargetType("TargetTypeX",
Collections.singletonList(setB.getType()));
final DistributionSet installedSet = testdataFactory.createDistributionSet("another");
@@ -92,12 +96,17 @@ class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final List<Target> targDs = testdataFactory.createTargets(100, targetDsDIdPref,
targetDsDIdPref.concat(" description"), lastTargetNull);
final String targetDsEIdPref = "targ-E";
final List<Target> targEs = testdataFactory.createTargetsWithType(100, targetDsEIdPref, targetTypeX);
final String assignedC = targCs.iterator().next().getControllerId();
assignDistributionSet(setA.getId(), assignedC);
final String assignedA = targAs.iterator().next().getControllerId();
assignDistributionSet(setA.getId(), assignedA);
final String assignedB = targBs.iterator().next().getControllerId();
assignDistributionSet(setA.getId(), assignedB);
final String assignedE = targEs.iterator().next().getControllerId();
assignDistributionSet(setB.getId(), assignedE);
final String installedC = targCs.iterator().next().getControllerId();
final Long actionId = getFirstAssignedActionId(assignDistributionSet(installedSet.getId(), assignedC));
@@ -136,7 +145,7 @@ class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
verifyThat1TargetHasAttributeValue("%c-attribute%", targAttribute.getControllerId());
verifyThat1TargetHasAttributeValue("%" + targAttributeId.getControllerId() + "%",
targAttributeId.getControllerId());
verifyThatRepositoryContains400Targets();
verifyThatRepositoryContains500Targets();
verifyThat200TargetsHaveTagD(targTagW, concat(targBs, targCs));
verifyThat100TargetsContainsGivenTextAndHaveTagAssigned(targTagY, targTagW, targBs);
verifyThat1TargetHasTagHasDescOrNameAndDs(targTagW, setA, targetManagement.getByControllerID(assignedC).get());
@@ -147,19 +156,19 @@ class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
verifyThat1TargetWithDescOrNameHasDS(setA, targetManagement.getByControllerID(assignedA).get());
List<Target> expected = concat(targAs, targBs, targCs, targDs);
expected.removeAll(targetManagement.getByControllerID(Arrays.asList(assignedA, assignedB, assignedC)));
verifyThat397TargetsAreInStatusUnknown(unknown, expected);
verifyThat496TargetsAreInStatusUnknown(unknown, expected);
expected = concat(targBs, targCs);
expected.removeAll(targetManagement.getByControllerID(Arrays.asList(assignedB, assignedC)));
verifyThat198TargetsAreInStatusUnknownAndHaveGivenTags(targTagY, targTagW, unknown, expected);
verfyThat0TargetsAreInStatusUnknownAndHaveDSAssigned(setA, unknown);
verifyThat0TargetsAreInStatusUnknownAndHaveDSAssigned(setA, unknown);
expected = concat(targAs);
expected.remove(targetManagement.getByControllerID(assignedA).get());
verifyThat99TargetsWithNameOrDescriptionAreInGivenStatus(unknown, expected);
expected = concat(targBs);
expected.remove(targetManagement.getByControllerID(assignedB).get());
verifyThat99TargetsWithGivenNameOrDescAndTagAreInStatusUnknown(targTagW, unknown, expected);
verifyThat3TargetsAreInStatusPending(pending,
targetManagement.getByControllerID(Arrays.asList(assignedA, assignedB, assignedC)));
verifyThat4TargetsAreInStatusPending(pending,
targetManagement.getByControllerID(Arrays.asList(assignedA, assignedB, assignedC, assignedE)));
verifyThat3TargetsWithGivenDSAreInPending(setA, pending,
targetManagement.getByControllerID(Arrays.asList(assignedA, assignedB, assignedC)));
verifyThat1TargetWithGivenNameOrDescAndDSIsInPending(setA, pending,
@@ -171,8 +180,13 @@ class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
verifyThat2TargetsWithGivenTagAreInPending(targTagW, pending,
targetManagement.getByControllerID(Arrays.asList(assignedB, assignedC)));
verifyThat200targetsWithGivenTagAreInStatusPendingorUnknown(targTagW, both, concat(targBs, targCs));
verfiyThat1TargetAIsInStatusPendingAndHasDSInstalled(installedSet, pending,
verifyThat1TargetAIsInStatusPendingAndHasDSInstalled(installedSet, pending,
targetManagement.getByControllerID(installedC).get());
verifyThat1TargetHasTypeAndDSAssigned(targetTypeX, setB, targetManagement.getByControllerID(assignedE).get());
verifyThatTargetsHasNoTypeAndDSAssignedOrInstalled(setA, targetManagement.getByControllerID(Arrays.asList(assignedA, assignedB, assignedC)));
verifyThatTargetsHasNoTypeAndDSAssignedOrInstalled(installedSet, targetManagement.getByControllerID(Collections.singletonList(installedC)));
verifyThat100TargetsContainsGivenTextAndHaveTypeAssigned(targetTypeX, targEs);
verifyThat400TargetsContainsGivenTextAndHaveNoTypeAssigned(concat(targAs, targBs, targCs, targDs));
expected = concat(targBs, targCs);
expected.removeAll(targetManagement.getByControllerID(Arrays.asList(assignedB, assignedC)));
@@ -180,354 +194,314 @@ class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
}
@Step
private void verfiyThat1TargetAIsInStatusPendingAndHasDSInstalled(final DistributionSet installedSet,
private void verifyThat1TargetAIsInStatusPendingAndHasDSInstalled(final DistributionSet installedSet,
final List<TargetUpdateStatus> pending, final Target expected) {
final FilterParams filterParams = new FilterParams(pending, null, null, installedSet.getId(), Boolean.FALSE);
final String query = "updatestatus==pending and installedds.name==" + installedSet.getName();
assertThat(targetManagement
.findByFilters(PAGE, new FilterParams(pending, null, null, installedSet.getId(), Boolean.FALSE))
.getContent())
.as("has number of elements").hasSize(1).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(pending, null, null,
installedSet.getId(), Boolean.FALSE)))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@Step
private void verifyThat200targetsWithGivenTagAreInStatusPendingorUnknown(final TargetTag targTagW,
final List<TargetUpdateStatus> both, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(both, null, null, null, Boolean.FALSE, targTagW.getName());
final String query = "(updatestatus==pending or updatestatus==unknown) and tag==" + targTagW.getName();
assertThat(targetManagement
.findByFilters(PAGE, new FilterParams(both, null, null, null, Boolean.FALSE, targTagW.getName()))
.getContent()).as("has number of elements").hasSize(200)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(both, null, null, null,
Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(200).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@Step
private void verifyThat2TargetsWithGivenTagAreInPending(final TargetTag targTagW,
final List<TargetUpdateStatus> pending, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(pending, null, null, null, Boolean.FALSE,
targTagW.getName());
final String query = "updatestatus==pending and tag==" + targTagW.getName();
assertThat(targetManagement
.findByFilters(PAGE, new FilterParams(pending, null, null, null, Boolean.FALSE, targTagW.getName()))
.getContent())
.as("has number of elements").hasSize(2).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(pending, null, null, null,
Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(2).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@Step
private void verifyThat2TargetsWithGivenTagAndDSIsInPending(final TargetTag targTagW, final DistributionSet setA,
final List<TargetUpdateStatus> pending, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(pending, null, null, setA.getId(), Boolean.FALSE,
targTagW.getName());
final String query = "updatestatus==pending and (assignedds.name==" + setA.getName() + " or installedds.name=="
+ setA.getName() + ") and tag==" + targTagW.getName();
assertThat(targetManagement
.findByFilters(PAGE,
new FilterParams(pending, null, null, setA.getId(), Boolean.FALSE, targTagW.getName()))
.getContent())
.as("has number of elements").hasSize(2).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(pending, null, null, setA.getId(),
Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(2).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@Step
private void verifyThat1TargetWithGivenNameOrDescAndTagAndDSIsInPending(final TargetTag targTagW,
final DistributionSet setA, final List<TargetUpdateStatus> pending, final Target expected) {
final FilterParams filterParams = new FilterParams(pending, null, "%targ-B%", setA.getId(), Boolean.FALSE,
targTagW.getName());
final String query = "updatestatus==pending and (assignedds.name==" + setA.getName() + " or installedds.name=="
+ setA.getName() + ") and (name==*targ-B* or description==*targ-B*) and tag==" + targTagW.getName();
assertThat(targetManagement
.findByFilters(PAGE,
new FilterParams(pending, null, "%targ-B%", setA.getId(), Boolean.FALSE, targTagW.getName()))
.getContent())
.as("has number of elements").hasSize(1).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(pending, null, "%targ-B%",
setA.getId(), Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@Step
private void verifyThat1TargetWithGivenNameOrDescAndDSIsInPending(final DistributionSet setA,
final List<TargetUpdateStatus> pending, final Target expected) {
final FilterParams filterParams = new FilterParams(pending, null, "%targ-A%", setA.getId(), Boolean.FALSE);
final String query = "updatestatus==pending and (assignedds.name==" + setA.getName() + " or installedds.name=="
+ setA.getName() + ") and (name==*targ-A* or description==*targ-A*)";
assertThat(targetManagement
.findByFilters(PAGE, new FilterParams(pending, null, "%targ-A%", setA.getId(), Boolean.FALSE))
.getContent())
.as("has number of elements").hasSize(1).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(pending, null, "%targ-A%",
setA.getId(), Boolean.FALSE)))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@Step
private void verifyThat3TargetsWithGivenDSAreInPending(final DistributionSet setA,
final List<TargetUpdateStatus> pending, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(pending, null, null, setA.getId(), Boolean.FALSE);
final String query = "updatestatus==pending and (assignedds.name==" + setA.getName() + " or installedds.name=="
+ setA.getName() + ")";
assertThat(targetManagement
.findByFilters(PAGE, new FilterParams(pending, null, null, setA.getId(), Boolean.FALSE)).getContent())
.as("has number of elements").hasSize(3).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(
targetManagement.countByFilters(pending, null, null, setA.getId(), Boolean.FALSE)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(3).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@Step
private void verifyThat3TargetsAreInStatusPending(final List<TargetUpdateStatus> pending,
private void verifyThat4TargetsAreInStatusPending(final List<TargetUpdateStatus> pending,
final List<Target> expected) {
final FilterParams filterParams = new FilterParams(pending, null, null, null, Boolean.FALSE);
final String query = "updatestatus==pending";
assertThat(targetManagement.findByFilters(PAGE, new FilterParams(pending, null, null, null, Boolean.FALSE))
.getContent())
.as("has number of elements").hasSize(3).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(pending, null, null, null,
Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(4).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@Step
private void verifyThat99TargetsWithGivenNameOrDescAndTagAreInStatusUnknown(final TargetTag targTagW,
final List<TargetUpdateStatus> unknown, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(unknown, null, "%targ-B%", null, Boolean.FALSE,
targTagW.getName());
final String query = "updatestatus==unknown and (name==*targ-B* or description==*targ-B*) and tag=="
+ targTagW.getName();
assertThat(targetManagement
.findByFilters(PAGE,
new FilterParams(unknown, null, "%targ-B%", null, Boolean.FALSE, targTagW.getName()))
.getContent()).as("has number of elements").hasSize(99)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(unknown, null, "%targ-B%", null,
Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(99).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@Step
private void verifyThat99TargetsWithNameOrDescriptionAreInGivenStatus(final List<TargetUpdateStatus> unknown,
final List<Target> expected) {
final FilterParams filterParams = new FilterParams(unknown, null, "%targ-A%", null, Boolean.FALSE);
final String query = "updatestatus==unknown and (name==*targ-A* or description==*targ-A*)";
assertThat(targetManagement
.findByFilters(PAGE, new FilterParams(unknown, null, "%targ-A%", null, Boolean.FALSE, new String[0]))
.getContent()).as("has number of elements").hasSize(99)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(
targetManagement.countByFilters(unknown, null, "%targ-A%", null, Boolean.FALSE)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(99).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@Step
private void verfyThat0TargetsAreInStatusUnknownAndHaveDSAssigned(final DistributionSet setA,
private void verifyThat0TargetsAreInStatusUnknownAndHaveDSAssigned(final DistributionSet setA,
final List<TargetUpdateStatus> unknown) {
final FilterParams filterParams = new FilterParams(unknown, null, null, setA.getId(), Boolean.FALSE);
final String query = "updatestatus==unknown and (assignedds.name==" + setA.getName() + " or installedds.name=="
+ setA.getName() + ")";
assertThat(targetManagement
.findByFilters(PAGE, new FilterParams(unknown, null, null, setA.getId(), Boolean.FALSE)).getContent())
.as("has number of elements").hasSize(0).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(
targetManagement.countByFilters(unknown, null, null, setA.getId(), Boolean.FALSE)))
.as("and filter query returns the same result")
.hasSize(targetManagement.findByRsql(PAGE, query).getContent().size());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(0).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and filter query returns the same result")
.hasSize(targetManagement.findByRsql(PAGE, query).getContent().size());
}
@Step
private void verifyThat198TargetsAreInStatusUnknownAndHaveGivenTags(final TargetTag targTagY,
final TargetTag targTagW, final List<TargetUpdateStatus> unknown, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(unknown, null, null, null, Boolean.FALSE, targTagY.getName(),
targTagW.getName());
final String query = "updatestatus==unknown and (tag==" + targTagY.getName() + " or tag==" + targTagW.getName()
+ ")";
assertThat(targetManagement.findByFilters(PAGE,
new FilterParams(unknown, null, null, null, Boolean.FALSE, targTagY.getName(), targTagW.getName()))
.getContent()).as("has number of elements").hasSize(198)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(unknown, null, null, null,
Boolean.FALSE, targTagY.getName(), targTagW.getName())))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(198).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@Step
private void verifyThat397TargetsAreInStatusUnknown(final List<TargetUpdateStatus> unknown,
private void verifyThat496TargetsAreInStatusUnknown(final List<TargetUpdateStatus> unknown,
final List<Target> expected) {
final FilterParams filterParams = new FilterParams(unknown, null, null, null, Boolean.FALSE);
final String query = "updatestatus==unknown";
assertThat(targetManagement.findByFilters(PAGE, new FilterParams(unknown, null, null, null, Boolean.FALSE))
.getContent()).as("has number of elements").hasSize(397)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(
targetManagement.countByFilters(unknown, null, null, null, Boolean.FALSE)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(496).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@Step
private void verifyThat198TargetsAreInStatusUnknownAndOverdue(final List<TargetUpdateStatus> unknown,
final List<Target> expected) {
final FilterParams filterParams = new FilterParams(unknown, Boolean.TRUE, null, null, Boolean.FALSE);
// be careful: simple filters are concatenated using AND-gating
final String query = "lastcontrollerrequestat=le=${overdue_ts};updatestatus==UNKNOWN";
assertThat(targetManagement
.findByFilters(PAGE, new FilterParams(unknown, Boolean.TRUE, null, null, Boolean.FALSE)).getContent())
.as("has number of elements").hasSize(198).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(
targetManagement.countByFilters(unknown, Boolean.TRUE, null, null, Boolean.FALSE)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(198).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@Step
private void verifyThat1TargetWithDescOrNameHasDS(final DistributionSet setA, final Target expected) {
final FilterParams filterParams = new FilterParams(null, null, "%targ-A%", setA.getId(), Boolean.FALSE);
final String query = "(name==*targ-A* or description==*targ-A*) and (assignedds.name==" + setA.getName()
+ " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement
.findByFilters(PAGE, new FilterParams(null, null, "%targ-A%", setA.getId(), Boolean.FALSE))
.getContent())
.as("has number of elements").hasSize(1).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(
targetManagement.countByFilters(null, null, "%targ-A%", setA.getId(), Boolean.FALSE)))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@Step
private void verifyThat3TargetsHaveDSAssigned(final DistributionSet setA, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(null, null, null, setA.getId(), Boolean.FALSE);
final String query = "assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName();
assertThat(targetManagement.findByFilters(PAGE, new FilterParams(null, null, null, setA.getId(), Boolean.FALSE))
.getContent())
.as("has number of elements").hasSize(3).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(
targetManagement.countByFilters(null, null, null, setA.getId(), Boolean.FALSE)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(3).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@Step
private void verifyThat0TargetsWithNameOrdescAndDSHaveTag(final TargetTag targTagX, final DistributionSet setA) {
final FilterParams filterParams = new FilterParams(null, null, "%targ-C%", setA.getId(), Boolean.FALSE,
targTagX.getName());
final String query = "(name==*targ-C* or description==*targ-C*) and tag==" + targTagX.getName()
+ " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement
.findByFilters(PAGE,
new FilterParams(null, null, "%targ-C%", setA.getId(), Boolean.FALSE, targTagX.getName()))
.getContent())
.as("has number of elements").hasSize(0).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(null, null, "%targ-C%",
setA.getId(), Boolean.FALSE, targTagX.getName())))
.as("and filter query returns the same result")
.hasSize(targetManagement.findByRsql(PAGE, query).getContent().size());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(0).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and filter query returns the same result")
.hasSize(targetManagement.findByRsql(PAGE, query).getContent().size());
}
@Step
private void verifyThat0TargetsWithTagAndDescOrNameHasDS(final TargetTag targTagW, final DistributionSet setA) {
final FilterParams filterParams = new FilterParams(null, null, "%targ-A%", setA.getId(), Boolean.FALSE,
targTagW.getName());
final String query = "(name==*targ-A* or description==*targ-A*) and tag==" + targTagW.getName()
+ " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement
.findByFilters(PAGE,
new FilterParams(null, null, "%targ-A%", setA.getId(), Boolean.FALSE, targTagW.getName()))
.getContent())
.as("has number of elements").hasSize(0).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(null, null, "%targ-A%",
setA.getId(), Boolean.FALSE, targTagW.getName())))
.as("and filter query returns the same result")
.hasSize(targetManagement.findByRsql(PAGE, query).getContent().size());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(0).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and filter query returns the same result")
.hasSize(targetManagement.findByRsql(PAGE, query).getContent().size());
}
@Step
private void verifyThat1TargetHasTagHasDescOrNameAndDs(final TargetTag targTagW, final DistributionSet setA,
final Target expected) {
final FilterParams filterParams = new FilterParams(null, null, "%targ-C%", setA.getId(), Boolean.FALSE,
targTagW.getName());
final String query = "(name==*targ-c* or description==*targ-C*) and tag==" + targTagW.getName()
+ " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement
.findByFilters(PAGE,
new FilterParams(null, null, "%targ-C%", setA.getId(), Boolean.FALSE, targTagW.getName()))
.getContent())
.as("has number of elements").hasSize(1).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(null, null, "%targ-C%",
setA.getId(), Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@Step
private void verifyThat1TargetHasNameAndId(final String name, final String controllerId) {
assertThat(targetManagement.findByFilters(PAGE, new FilterParams(null, null, name, null, Boolean.FALSE))
.getContent()).as("has number of elements").hasSize(1).as("that number is also returned by count query")
.hasSize(Ints
.saturatedCast(targetManagement.countByFilters(null, null, name, null, Boolean.FALSE)));
final FilterParams filterParamsByName = new FilterParams(null, null, name, null, Boolean.FALSE);
assertThat(targetManagement.findByFilters(PAGE, filterParamsByName).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParamsByName)));
assertThat(targetManagement.findByFilters(PAGE, new FilterParams(null, null, controllerId, null, Boolean.FALSE))
.getContent()).as("has number of elements").hasSize(1).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(
targetManagement.countByFilters(null, null, controllerId, null, Boolean.FALSE)));
final FilterParams filterParamsByControllerId = new FilterParams(null, null, controllerId, null, Boolean.FALSE);
assertThat(targetManagement.findByFilters(PAGE, filterParamsByControllerId).getContent())
.as("has number of elements").hasSize(1).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParamsByControllerId)));
}
@Step
private void verifyThat1TargetHasAttributeValue(final String value, final String controllerId) {
assertThat(targetManagement.findByFilters(PAGE, new FilterParams(null, null, value, null, Boolean.FALSE))
.getContent()).as("has number of elements").hasSize(1).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(
targetManagement.countByFilters(null, null, value, null, Boolean.FALSE)));
final FilterParams filterParams = new FilterParams(null, null, value, null, Boolean.FALSE);
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)));
}
@Step
private void verifyThat100TargetsContainsGivenTextAndHaveTagAssigned(final TargetTag targTagY,
final TargetTag targTagW, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(null, null, "%targ-B%", null, Boolean.FALSE,
targTagY.getName(), targTagW.getName());
final String query = "(name==*targ-B* or description==*targ-B*) and (tag==" + targTagY.getName() + " or tag=="
+ targTagW.getName() + ")";
assertThat(targetManagement.findByFilters(PAGE,
new FilterParams(null, null, "%targ-B%", null, Boolean.FALSE, targTagY.getName(), targTagW.getName()))
.getContent()).as("has number of elements").hasSize(100)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(null, null, "%targ-B%", null,
Boolean.FALSE, targTagY.getName(), targTagW.getName())))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(100).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@SafeVarargs
@@ -539,27 +513,66 @@ class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
@Step
private void verifyThat200TargetsHaveTagD(final TargetTag targTagD, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(null, null, null, null, Boolean.FALSE, targTagD.getName());
final String query = "tag==" + targTagD.getName();
assertThat(targetManagement
.findByFilters(PAGE, new FilterParams(null, null, null, null, Boolean.FALSE, targTagD.getName()))
.getContent()).as("Expected number of results is").hasSize(200)
.as("and is expected number of results is equal to ")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(null, null, null, null,
Boolean.FALSE, targTagD.getName())))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("Expected number of results is")
.hasSize(200).as("and is expected number of results is equal to ")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(PAGE, query).getContent());
}
@Step
private void verifyThatRepositoryContains400Targets() {
assertThat(targetManagement.findByFilters(PAGE, new FilterParams(null, null, null, null, null)).getContent())
.as("Overall we expect that many targets in the repository").hasSize(400)
private void verifyThatRepositoryContains500Targets() {
final FilterParams filterParams = new FilterParams(null, null, null, null, null);
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent())
.as("Overall we expect that many targets in the repository").hasSize(500)
.as("which is also reflected by repository count").hasSize(Ints.saturatedCast(targetManagement.count()))
.as("which is also reflected by call without specification")
.containsAll(targetManagement.findAll(PAGE).getContent());
}
@Step
private void verifyThat1TargetHasTypeAndDSAssigned(final TargetType type, final DistributionSet set,
final Target expected) {
final FilterParams filterParams = new FilterParams(null, set.getId(), Boolean.FALSE, type.getId());
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsExactly(expected);
}
@Step
private void verifyThatTargetsHasNoTypeAndDSAssignedOrInstalled(final DistributionSet set, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(null, set.getId(), Boolean.TRUE, null);
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(expected.size()).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsAll(expected);
}
@Step
private void verifyThat100TargetsContainsGivenTextAndHaveTypeAssigned(final TargetType targetType,
final List<Target> expected) {
final FilterParams filterParams = new FilterParams("%targ-E%", null, Boolean.FALSE, targetType.getId());
List<Target> filteredTargets = targetManagement.findByFilters(PAGE, filterParams).getContent();
assertThat(filteredTargets).as("has number of elements").hasSize(100)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)));
// Comparing the controller ids, as one of the targets was modified, so a 1:1
// comparison of the objects is not possible
assertThat(filteredTargets.stream().map(Target::getControllerId).collect(Collectors.toList()))
.containsAll(expected.stream().map(Target::getControllerId).collect(Collectors.toList()));
}
@Step
private void verifyThat400TargetsContainsGivenTextAndHaveNoTypeAssigned(final List<Target> expected) {
final FilterParams filterParams = new FilterParams("%targ-%", null, Boolean.TRUE, null);
assertThat(targetManagement.findByFilters(PAGE, filterParams).getContent()).as("has number of elements")
.hasSize(400).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countByFilters(filterParams)))
.as("and contains the following elements").containsAll(expected);
}
@Test
@@ -770,4 +783,28 @@ class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
.type(type);
return distributionSetManagement.create(dsCreate);
}
@Test
@Description("Verifies that targets with given target type are returned from repository.")
public void findTargetByTargetType() {
TargetType testType = testdataFactory.createTargetType("testType", Collections.singletonList(standardDsType));
List<Target> unassigned = testdataFactory.createTargets(9, "unassigned");
List<Target> assigned = testdataFactory.createTargetsWithType(11, "assigned", testType);
assertThat(targetManagement.findByFilters(PAGE,
new FilterParams(null,null, false, testType.getId())))
.as("Contains the targets with set type").containsAll(assigned)
.as("and that means the following expected amount").hasSize(11);
assertThat(targetManagement.countByFilters(new FilterParams(null,null, false, testType.getId())))
.as("Count the targets with set type").isEqualTo(11);
assertThat(targetManagement.findByFilters(PAGE,
new FilterParams(null, null, true, null)))
.as("Contains the targets without a type").containsAll(unassigned)
.as("and that means the following expected amount").hasSize(9);
assertThat(targetManagement.countByFilters(new FilterParams(null, null, true, null)))
.as("Counts the targets without a type").isEqualTo(9);
}
}

View File

@@ -28,6 +28,7 @@ import javax.validation.ConstraintViolationException;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.FilterParams;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.builder.TargetUpdate;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
@@ -39,6 +40,7 @@ import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreated
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
@@ -57,6 +59,7 @@ import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
@@ -752,7 +755,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
toggleTagAssignment(tagABCTargets, tagB);
toggleTagAssignment(tagABCTargets, tagC);
assertThat(targetManagement.countByFilters(null, null, null, null, Boolean.FALSE, "X"))
assertThat(targetManagement.countByFilters(new FilterParams(null, null, null, null, Boolean.FALSE, "X")))
.as("Target count is wrong").isEqualTo(0);
// search for targets with tag tagA
@@ -782,11 +785,11 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
checkTargetHasNotTags(tagCTargets, tagA, tagB);
// check again target lists refreshed from DB
assertThat(targetManagement.countByFilters(null, null, null, null, Boolean.FALSE, "A"))
assertThat(targetManagement.countByFilters(new FilterParams(null, null, null, null, Boolean.FALSE, "A")))
.as("Target count is wrong").isEqualTo(targetWithTagA.size());
assertThat(targetManagement.countByFilters(null, null, null, null, Boolean.FALSE, "B"))
assertThat(targetManagement.countByFilters(new FilterParams(null, null, null, null, Boolean.FALSE, "B")))
.as("Target count is wrong").isEqualTo(targetWithTagB.size());
assertThat(targetManagement.countByFilters(null, null, null, null, Boolean.FALSE, "C"))
assertThat(targetManagement.countByFilters(new FilterParams(null, null, null, null, Boolean.FALSE, "C")))
.as("Target count is wrong").isEqualTo(targetWithTagC.size());
}
@@ -1107,7 +1110,57 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
assertThat(targetFound1.get().getOptLockRevision()).isEqualTo(2);
assertThat(targetFound1.get().getTargetType().getId()).isEqualTo(targetType.getId());
}
@Test
@WithUser(allSpPermissions = true)
@Description("Tests the assignment of types to multiple targets.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 20),
@Expect(type = TargetTypeCreatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 29), @Expect(type = TargetDeletedEvent.class, count = 1) })
public void targetTypeBulkAssignments() {
final List<Target> typeATargets = testdataFactory.createTargets(10, "typeATargets", "first description");
final List<Target> typeBTargets = testdataFactory.createTargets(10, "typeBTargets", "first description");
// create a target type
final TargetType typeA = testdataFactory.createTargetType("A", Collections.singletonList(standardDsType));
final TargetType typeB = testdataFactory.createTargetType("B", Collections.singletonList(standardDsType));
// assign target type to target
TargetTypeAssignmentResult resultA = initiateTypeAssignment(typeATargets, typeA);
TargetTypeAssignmentResult resultB = initiateTypeAssignment(typeBTargets, typeB);
assertThat(resultA.getAssigned()).isEqualTo(10);
assertThat(resultB.getAssigned()).isEqualTo(10);
checkTargetsHaveType(typeATargets, typeA);
checkTargetsHaveType(typeBTargets, typeB);
// double assignment does not unassign
resultA = initiateTypeAssignment(typeATargets, typeA);
resultB = initiateTypeAssignment(typeBTargets, typeB);
assertThat(resultA.getAssigned()).isZero();
assertThat(resultB.getAssigned()).isZero();
assertThat(resultA.getAlreadyAssigned()).isEqualTo(10);
assertThat(resultB.getAlreadyAssigned()).isEqualTo(10);
checkTargetsHaveType(typeATargets, typeA);
checkTargetsHaveType(typeBTargets, typeB);
// verify that type assignment does not throw an error if target list includes an unknown id
targetManagement.deleteByControllerID(typeATargets.get(0).getControllerId());
final TargetTypeAssignmentResult resultC = initiateTypeAssignment(typeATargets, typeB);
assertThat(resultC.getAssigned()).isEqualTo(9);
assertThat(resultC.getAlreadyAssigned()).isZero();
checkTargetsHaveType(typeATargets, typeB);
}
private void checkTargetsHaveType(final List<Target> targets, final TargetType type) {
List<JpaTarget> foundTargets = targetRepository
.findAllById(targets.stream().map(Identifiable::getId).collect(Collectors.toList()));
for (final Target target : foundTargets) {
if (!type.getName().equals(type.getName())) {
fail(String.format("Target %s is not of type %s.", target, type));
}
}
}
@Test
@Description("Queries and loads the metadata related to a given target.")
public void findAllTargetMetadataByControllerId() {

View File

@@ -110,7 +110,7 @@ import org.springframework.test.context.TestPropertySource;
public abstract class AbstractIntegrationTest {
private static final Logger LOG = LoggerFactory.getLogger(AbstractIntegrationTest.class);
protected static final Pageable PAGE = PageRequest.of(0, 400, Sort.by(Direction.ASC, "id"));
protected static final Pageable PAGE = PageRequest.of(0, 500, Sort.by(Direction.ASC, "id"));
protected static final URI LOCALHOST = URI.create("http://127.0.0.1");

View File

@@ -8,9 +8,18 @@
*/
package org.eclipse.hawkbit.ui.common.builder;
import com.vaadin.data.Binder;
import com.vaadin.data.Binder.Binding;
import com.vaadin.data.Binder.BindingBuilder;
import com.vaadin.data.Validator;
import com.vaadin.data.ValueProvider;
import com.vaadin.server.Setter;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import org.eclipse.hawkbit.repository.model.NamedEntity;
import org.eclipse.hawkbit.repository.model.NamedVersionedEntity;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.Type;
import org.eclipse.hawkbit.ui.common.data.aware.ActionTypeAware;
import org.eclipse.hawkbit.ui.common.data.aware.DescriptionAware;
@@ -23,7 +32,6 @@ import org.eclipse.hawkbit.ui.common.data.aware.VersionAware;
import org.eclipse.hawkbit.ui.common.data.providers.AbstractProxyDataProvider;
import org.eclipse.hawkbit.ui.common.data.providers.DistributionSetStatelessDataProvider;
import org.eclipse.hawkbit.ui.common.data.providers.TargetFilterQueryDataProvider;
import org.eclipse.hawkbit.ui.common.data.providers.TargetTypeDataProvider;
import org.eclipse.hawkbit.ui.common.data.proxies.ProxyDistributionSet;
import org.eclipse.hawkbit.ui.common.data.proxies.ProxyDistributionSetInfo;
import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTargetFilterQuery;
@@ -39,17 +47,6 @@ import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.springframework.util.StringUtils;
import com.vaadin.data.Binder;
import com.vaadin.data.Binder.Binding;
import com.vaadin.data.Binder.BindingBuilder;
import com.vaadin.data.Validator;
import com.vaadin.data.ValueProvider;
import com.vaadin.server.Setter;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
/**
* Builder class for from components
*/

View File

@@ -8,18 +8,17 @@
*/
package org.eclipse.hawkbit.ui.common.data.filters;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import com.google.common.base.MoreObjects;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.ui.common.data.providers.TargetManagementStateDataProvider;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import com.google.common.base.MoreObjects;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
/**
* Filter params for {@link TargetManagementStateDataProvider}.
@@ -35,12 +34,14 @@ public class TargetManagementFilterParams implements Serializable {
private boolean noTagClicked;
private Collection<String> targetTags;
private Long targetFilterQueryId;
private boolean noTargetTypeClicked;
private Long targetTypeId;
/**
* Constructor for TargetManagementFilterParams to initialize
*/
public TargetManagementFilterParams() {
this(null, null, Collections.emptyList(), false, null, false, Collections.emptyList(), null);
this(null, null, Collections.emptyList(), false, null, false, Collections.emptyList(), null, false, null);
}
/**
@@ -66,7 +67,7 @@ public class TargetManagementFilterParams implements Serializable {
public TargetManagementFilterParams(final Long pinnedDistId, final String searchText,
final Collection<TargetUpdateStatus> targetUpdateStatusList, final boolean overdueState,
final Long distributionId, final boolean noTagClicked, final Collection<String> targetTags,
final Long targetFilterQueryId) {
final Long targetFilterQueryId, final boolean noTargetTypeClicked, final Long targetTypeId) {
this.pinnedDistId = pinnedDistId;
this.searchText = searchText;
this.targetUpdateStatusList = targetUpdateStatusList;
@@ -75,6 +76,8 @@ public class TargetManagementFilterParams implements Serializable {
this.noTagClicked = noTagClicked;
this.targetTags = targetTags;
this.targetFilterQueryId = targetFilterQueryId;
this.noTargetTypeClicked = noTargetTypeClicked;
this.targetTypeId = targetTypeId;
}
/**
@@ -93,6 +96,8 @@ public class TargetManagementFilterParams implements Serializable {
this.distributionId = filter.getDistributionId();
this.noTagClicked = filter.isNoTagClicked();
this.targetTags = filter.getTargetTags() != null ? new ArrayList<>(filter.getTargetTags()) : null;
this.noTargetTypeClicked = filter.isNoTargetTypeClicked();
this.targetTypeId = filter.getTargetTypeId();
this.targetFilterQueryId = filter.getTargetFilterQueryId();
}
@@ -119,7 +124,11 @@ public class TargetManagementFilterParams implements Serializable {
}
private boolean isAnyComplexFilterSelected() {
return distributionId != null || targetFilterQueryId != null;
return distributionId != null || targetFilterQueryId != null || isAnyTypeSelected();
}
private boolean isAnyTypeSelected(){
return targetTypeId != null || isNoTargetTypeClicked();
}
/**
@@ -278,6 +287,46 @@ public class TargetManagementFilterParams implements Serializable {
this.noTagClicked = noTagClicked;
}
/**
* Gets the status of no target Type clicked
*
* @return noTargetTypeClicked <code>true</code> if it is clicked, otherwise
* <code>false</code>
*/
public boolean isNoTargetTypeClicked() {
return noTargetTypeClicked;
}
/**
* Sets the state of no target type filter
*
* @param noTargetTypeClicked
* <code>true</code> if the tag is clicked, otherwise
* <code>false</code>
*/
public void setNoTargetTypeClicked(boolean noTargetTypeClicked) {
this.noTargetTypeClicked = noTargetTypeClicked;
}
/**
* Gets the targetTypeId
*
* @return targetTypeId for target type filter
*/
public Long getTargetTypeId() {
return targetTypeId;
}
/**
* Sets the targetType
*
* @param targetTypeId
* Id of targetType
*/
public void setTargetTypeId(Long targetTypeId) {
this.targetTypeId = targetTypeId;
}
// equals requires all fields in condition
@SuppressWarnings("squid:S1067")
@Override
@@ -296,13 +345,15 @@ public class TargetManagementFilterParams implements Serializable {
&& Objects.equals(this.getDistributionId(), other.getDistributionId())
&& Objects.equals(this.isNoTagClicked(), other.isNoTagClicked())
&& Objects.equals(this.getTargetTags(), other.getTargetTags())
&& Objects.equals(this.getTargetFilterQueryId(), other.getTargetFilterQueryId());
&& Objects.equals(this.getTargetFilterQueryId(), other.getTargetFilterQueryId())
&& Objects.equals(this.isNoTargetTypeClicked(), other.isNoTargetTypeClicked())
&& Objects.equals(this.getTargetTypeId(), other.getTargetTypeId());
}
@Override
public int hashCode() {
return Objects.hash(getPinnedDistId(), getSearchText(), getTargetUpdateStatusList(), isOverdueState(),
getDistributionId(), isNoTagClicked(), getTargetTags(), getTargetFilterQueryId());
getDistributionId(), isNoTagClicked(), getTargetTags(), getTargetFilterQueryId(), isNoTargetTypeClicked(), getTargetTypeId());
}
@Override
@@ -311,6 +362,7 @@ public class TargetManagementFilterParams implements Serializable {
.add("searchText", getSearchText()).add("targetUpdateStatusList", getTargetUpdateStatusList())
.add("overdueState", isOverdueState()).add("distributionId", getDistributionId())
.add("noTagClicked", isNoTagClicked()).add("targetTags", getTargetTags())
.add("targetFilterQueryId", getTargetFilterQueryId()).toString();
.add("targetFilterQueryId", getTargetFilterQueryId())
.add("noTargetTypeClicked", isNoTargetTypeClicked()).add("targetTypeId", getTargetTypeId()).toString();
}
}

View File

@@ -64,6 +64,8 @@ public class TargetManagementStateDataProvider
final boolean noTagClicked = filter.isNoTagClicked();
final String[] targetTags = filter.getTargetTags().toArray(new String[0]);
final Long targetFilterQueryId = filter.getTargetFilterQueryId();
final boolean noTargetTypeClicked = filter.isNoTargetTypeClicked();
final Long targetTypeId = filter.getTargetTypeId();
if (pinnedDistId != null) {
return targetManagement.findByFilterOrderByLinkedDistributionSet(pageRequest, pinnedDistId,
@@ -76,6 +78,12 @@ public class TargetManagementStateDataProvider
return targetManagement.findByTargetFilterQuery(pageRequest, targetFilterQueryId);
}
// Type Filter of Deployment Management view
if (targetTypeId != null || noTargetTypeClicked) {
return targetManagement.findByFilters(pageRequest, new FilterParams(searchText, distributionId, noTargetTypeClicked, targetTypeId));
}
// Simple Filter of Deployment Management view
return targetManagement.findByFilters(pageRequest, new FilterParams(targetUpdateStatusList, overdueState,
searchText, distributionId, noTagClicked, targetTags));
}
@@ -95,6 +103,8 @@ public class TargetManagementStateDataProvider
final Long distributionId = filter.getDistributionId();
final boolean noTagClicked = filter.isNoTagClicked();
final String[] targetTags = filter.getTargetTags().toArray(new String[0]);
final boolean noTargetTypeClicked = filter.isNoTargetTypeClicked();
final Long targetTypeId = filter.getTargetTypeId();
final Long targetFilterQueryId = filter.getTargetFilterQueryId();
if (filter.isAnyFilterSelected()) {
@@ -102,8 +112,14 @@ public class TargetManagementStateDataProvider
return targetManagement.countByTargetFilterQuery(targetFilterQueryId);
}
return targetManagement.countByFilters(targetUpdateStatusList, overdueState, searchText, distributionId,
noTagClicked, targetTags);
// Type Filter of Deployment Management view
if (targetTypeId != null || noTargetTypeClicked) {
return targetManagement.countByFilters(new FilterParams(searchText, distributionId, noTargetTypeClicked, targetTypeId));
}
// Simple Filter of Deployment Management view
return targetManagement.countByFilters(new FilterParams(targetUpdateStatusList, overdueState, searchText, distributionId,
noTagClicked, targetTags));
}
return targetManagement.count();

View File

@@ -31,6 +31,14 @@ public class TargetTypeDataProvider<T extends ProxyIdentifiableEntity>
private static final long serialVersionUID = 1L;
private final transient TargetTypeManagement targetTypeManagement;
/**
* Constructor
*
* @param targetTypeManagement
* TargetTypeManagement
* @param mapper
* Mapper
*/
public TargetTypeDataProvider(final TargetTypeManagement targetTypeManagement, IdentifiableEntityToProxyIdentifiableEntityMapper<T, TargetType> mapper) {
super(mapper, Sort.by(Direction.ASC, "name"));
this.targetTypeManagement = targetTypeManagement;

View File

@@ -12,5 +12,5 @@ package org.eclipse.hawkbit.ui.common.event;
* Enum constants for filter type
*/
public enum FilterType {
SEARCH, TYPE, TARGET_TYPE, TAG, NO_TAG, STATUS, OVERDUE, QUERY, DISTRIBUTION, MASTER;
SEARCH, TYPE, TAG, NO_TAG, TARGET_TYPE, NO_TARGET_TYPE, STATUS, OVERDUE, QUERY, DISTRIBUTION, MASTER;
}

View File

@@ -9,8 +9,7 @@
package org.eclipse.hawkbit.ui.common.filterlayout;
import com.vaadin.ui.Button;
import java.util.Collection;
import java.util.Map;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
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;
@@ -20,47 +19,57 @@ 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.management.targettag.filter.TargetTagFilterLayoutUiState;
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;
import java.util.Collection;
/**
* Class for defining the type filter buttons.
* Class for defining the target type filter buttons.
*/
public abstract class AbstractTargetTypeFilterButtons extends AbstractFilterButtons<ProxyTargetType, Void> {
private static final long serialVersionUID = 1L;
private final TagFilterLayoutUiState tagFilterLayoutUiState;
private final TargetTagFilterLayoutUiState targetTagFilterLayoutUiState;
protected final UINotification uiNotification;
private final Button noTargetTypeButton;
private final TargetTypeFilterButtonClick targetTypeFilterButtonClick;
private final transient TargetTypeManagement targetTypeManagement;
private final Button noTargetTypeButton;
private boolean preNoTargetTypeBtnState;
/**
* Constructor for AbstractTargetTypeFilterButtons
*
* @param uiDependencies
* {@link CommonUiDependencies}
* @param tagFilterLayoutUiState
* TagFilterLayoutUiState
* @param targetTagFilterLayoutUiState
* {@link TargetTagFilterLayoutUiState}
* @param targetTypeManagement
* TargetTypeManagement
*/
protected AbstractTargetTypeFilterButtons(final CommonUiDependencies uiDependencies,
final TagFilterLayoutUiState tagFilterLayoutUiState) {
final TargetTagFilterLayoutUiState targetTagFilterLayoutUiState,
final TargetTypeManagement targetTypeManagement) {
super(uiDependencies.getEventBus(), uiDependencies.getI18n(), uiDependencies.getUiNotification(),
uiDependencies.getPermChecker());
this.preNoTargetTypeBtnState = false;
this.uiNotification = uiDependencies.getUiNotification();
this.tagFilterLayoutUiState = tagFilterLayoutUiState;
this.targetTagFilterLayoutUiState = targetTagFilterLayoutUiState;
this.targetTypeManagement = targetTypeManagement;
this.noTargetTypeButton = buildNoTargetTypeButton();
this.targetTypeFilterButtonClick = new TargetTypeFilterButtonClick(this::onFilterChangedEvent);
}
@Override
protected TargetTypeFilterButtonClick getFilterButtonClickBehaviour() {
return targetTypeFilterButtonClick;
}
private Button buildNoTargetTypeButton() {
final Button noTargetType = SPUIComponentProvider.getButton(
getFilterButtonIdPrefix() + "." + SPUIDefinitions.NO_TARGET_TYPE_BUTTON_ID,
@@ -71,24 +80,51 @@ public abstract class AbstractTargetTypeFilterButtons extends AbstractFilterButt
final ProxyTargetType proxyTargetType = new ProxyTargetType();
proxyTargetType.setNoTargetType(true);
noTargetType.addClickListener(event -> getFilterButtonClickBehaviour().processFilterClick(proxyTargetType));
noTargetType.addStyleName("filter-drop-hint-layout");
return noTargetType;
}
@Override
protected TargetTypeFilterButtonClick getFilterButtonClickBehaviour(){
return targetTypeFilterButtonClick;
/**
* @return the noTargetType Button component
*/
public Button getNoTargetTypeButton() {
return noTargetTypeButton;
}
private void onFilterChangedEvent(final ProxyTargetType targetType,
final ClickBehaviourType clickType) {
final Long targetTypeId = ClickBehaviourType.CLICKED == clickType ? targetType.getId()
private void onFilterChangedEvent(ProxyTargetType proxyTargetType, ClickBehaviourType clickType) {
getDataCommunicator().reset();
final boolean isNoTargetTypeActive = proxyTargetType.isNoTargetType() && clickType == ClickBehaviourType.CLICKED;
if (isNoTargetTypeActive) {
getNoTargetTypeButton().addStyleName(SPUIStyleDefinitions.SP_NO_TAG_BTN_CLICKED_STYLE);
} else {
getNoTargetTypeButton().removeStyleName(SPUIStyleDefinitions.SP_NO_TAG_BTN_CLICKED_STYLE);
}
if (preNoTargetTypeBtnState != isNoTargetTypeActive){
publishNoTargetTypeChangedEvent(isNoTargetTypeActive);
}
final Long targetTypeId = ClickBehaviourType.CLICKED == clickType ? proxyTargetType.getId()
: null;
publishFilterChangedEvent(targetTypeId);
preNoTargetTypeBtnState = isNoTargetTypeActive;
}
private void publishNoTargetTypeChangedEvent(final boolean isNoTargetTypeActivated) {
eventBus.publish(EventTopics.FILTER_CHANGED, this, new FilterChangedEventPayload<>(ProxyTarget.class,
FilterType.NO_TARGET_TYPE, isNoTargetTypeActivated, EventView.DEPLOYMENT));
targetTagFilterLayoutUiState.setNoTargetTypeClicked(isNoTargetTypeActivated);
}
private void publishFilterChangedEvent(final Long targetTypeId) {
eventBus.publish(EventTopics.FILTER_CHANGED, this, new FilterChangedEventPayload<>(ProxyTarget.class,
FilterType.TARGET_TYPE, targetTypeId, EventView.DEPLOYMENT));
targetTagFilterLayoutUiState.setClickedTargetTypeFilterId(targetTypeId);
}
/**
@@ -105,52 +141,92 @@ public abstract class AbstractTargetTypeFilterButtons extends AbstractFilterButt
*/
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
* Type deletion operation.
*
* @param typeToDelete
* target type to delete
* @return true if delete target type has no exception
*/
public Button getNoTargetTypeButton() {
return noTargetTypeButton;
}
protected abstract boolean deleteTargetType(final ProxyTargetType typeToDelete);
@Override
public void restoreState() {
final Map<Long, String> targetTypesToRestore = tagFilterLayoutUiState.getClickedTagIdsWithName();
final Long targetFilterTypeIdToRestore = targetTagFilterLayoutUiState.getClickedTargetTypeFilterId();
if (!CollectionUtils.isEmpty(targetTypesToRestore)) {
removeNonExistingTargetTypes(targetTypesToRestore);
if (targetFilterTypeIdToRestore != null) {
if (targetTypeExists(targetFilterTypeIdToRestore)) {
targetTypeFilterButtonClick
.setPreviouslyClickedFilterId(targetTagFilterLayoutUiState.getClickedTargetTypeFilterId());
} else {
targetTagFilterLayoutUiState.setClickedTargetTypeFilterId(null);
}
}
if (tagFilterLayoutUiState.isNoTagClicked()) {
if (targetTagFilterLayoutUiState.isNoTargetTypeClicked()) {
getNoTargetTypeButton().addStyleName(SPUIStyleDefinitions.SP_NO_TAG_BTN_CLICKED_STYLE);
}
}
private void removeNonExistingTargetTypes(final Map<Long, String> targetTypeIdsWithName) {
final Collection<Long> targetTypeIds = targetTypeIdsWithName.keySet();
final Collection<Long> existingTargetTypeIds = filterExistingTargetTypeIds(targetTypeIds);
if (targetTypeIds.size() != existingTargetTypeIds.size()) {
targetTypeIds.retainAll(existingTargetTypeIds);
/**
* Reset filter on target type updated
*
* @param updatedTargetTypeIds
* Collections of updated target type Ids
*/
public void resetFilterOnTargetTypeUpdated(Collection<Long> updatedTargetTypeIds) {
if (isClickedTargetTypeInIds(updatedTargetTypeIds)) {
publishFilterChangedEvent(targetTypeFilterButtonClick.getPreviouslyClickedFilterId());
}
}
/**
* Filters out non-existant target type by ids.
* Reset filter on target type deleted
*
* @param targetTypeIds
* provided target type ids
* @return filtered list of existing target type ids
* @param deletedTargetTargetTypeIds
* Collections of updated target type Ids
*/
protected abstract Collection<Long> filterExistingTargetTypeIds(final Collection<Long> targetTypeIds);
public void resetFilterOnTargetTypeDeleted(final Collection<Long> deletedTargetTargetTypeIds) {
if (isClickedTargetTypeInIds(deletedTargetTargetTypeIds)) {
targetTypeFilterButtonClick.setPreviouslyClickedFilterId(null);
publishFilterChangedEvent(null);
}
}
private boolean isClickedTargetTypeInIds(final Collection<Long> targetTypeIds) {
final Long clickedTargetTypeId = targetTypeFilterButtonClick.getPreviouslyClickedFilterId();
return clickedTargetTypeId != null && targetTypeIds.contains(clickedTargetTypeId);
}
/**
* Reevaluate filter
*/
public void reevaluateFilter() {
final Long clickedTargetTypeId = targetTypeFilterButtonClick.getPreviouslyClickedFilterId();
if (clickedTargetTypeId != null && !targetTypeExists(clickedTargetTypeId)) {
targetTypeFilterButtonClick.setPreviouslyClickedFilterId(null);
publishFilterChangedEvent(null);
}
}
private boolean targetTypeExists(Long targetTypeId) {
return targetTypeManagement.get(targetTypeId).isPresent();
}
/**
* Remove applied target type filter
*/
public void clearAppliedTargetTypeFilter() {
if (targetTagFilterLayoutUiState.isNoTargetTypeClicked()) {
targetTagFilterLayoutUiState.setNoTargetTypeClicked(false);
getNoTargetTypeButton().removeStyleName(SPUIStyleDefinitions.SP_NO_TAG_BTN_CLICKED_STYLE);
}
if (targetTypeFilterButtonClick.getPreviouslyClickedFilterId() != null) {
targetTypeFilterButtonClick.setPreviouslyClickedFilterId(null);
targetTagFilterLayoutUiState.setClickedTargetTypeFilterId(null);
}
}
}

View File

@@ -17,24 +17,61 @@ import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTargetType;
public class TargetTypeFilterButtonClick extends AbstractFilterSingleButtonClick<ProxyTargetType> {
private static final long serialVersionUID = 1L;
private boolean noTargetTypeBtnClicked;
private final transient BiConsumer<ProxyTargetType, ClickBehaviourType> filterChangedCallback;
TargetTypeFilterButtonClick(final BiConsumer<ProxyTargetType, ClickBehaviourType> filterChangedCallback) {
/**
* Constructor
*
* @param filterChangedCallback
* filterChangedCallback
*/
public TargetTypeFilterButtonClick(
final BiConsumer<ProxyTargetType, ClickBehaviourType> filterChangedCallback) {
this.filterChangedCallback = filterChangedCallback;
this.noTargetTypeBtnClicked = false;
}
@Override
public void processFilterClick(final ProxyTargetType clickedFilter) {
if (isFilterPreviouslyClicked(clickedFilter) || isNoTargetTypePreviouslyClicked(clickedFilter)) {
previouslyClickedFilterId = null;
filterUnClicked(clickedFilter);
} else {
previouslyClickedFilterId = clickedFilter.getId();
filterClicked(clickedFilter);
}
}
@Override
protected void filterUnClicked(ProxyTargetType clickedFilter) {
if (clickedFilter.isNoTargetType()){
noTargetTypeBtnClicked = false;
}
filterChangedCallback.accept(clickedFilter, ClickBehaviourType.UNCLICKED);
}
@Override
protected void filterClicked(ProxyTargetType clickedFilter) {
noTargetTypeBtnClicked = clickedFilter.isNoTargetType();
filterChangedCallback.accept(clickedFilter, ClickBehaviourType.CLICKED);
}
@Override
public boolean isFilterPreviouslyClicked(final ProxyTargetType clickedFilter) {
return false;
return (previouslyClickedFilterId != null && previouslyClickedFilterId.equals(clickedFilter.getId()));
}
private boolean isNoTargetTypePreviouslyClicked(ProxyTargetType clickedFilter) {
return clickedFilter.isNoTargetType() && isNoTargetTypeBtnClicked();
}
/**
* @return true if no target type button clicked
*/
public boolean isNoTargetTypeBtnClicked() {
return noTargetTypeBtnClicked;
}
}

View File

@@ -0,0 +1,52 @@
/**
* 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.grid.support.assignment;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.ui.SpPermissionChecker;
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.EntityModifiedEventPayload;
import org.eclipse.hawkbit.ui.common.event.EventTopics;
import org.vaadin.spring.events.EventBus;
/**
* Support for assigning/un-assigning targets to target type.
*
*/
public abstract class AbstractTargetsToTargetTypeAssignmentSupport extends AssignmentSupport<ProxyTarget, ProxyTargetType> {
private final SpPermissionChecker permChecker;
private final EventBus.UIEventBus eventBus;
protected AbstractTargetsToTargetTypeAssignmentSupport(final CommonUiDependencies uiDependencies) {
super(uiDependencies.getUiNotification(), uiDependencies.getI18n());
this.permChecker = uiDependencies.getPermChecker();
this.eventBus = uiDependencies.getEventBus();
}
@Override
public List<String> getMissingPermissionsForDrop() {
return permChecker.hasUpdateTargetPermission() ? Collections.emptyList()
: Collections.singletonList(SpPermission.UPDATE_TARGET);
}
protected void publishTypeAssignmentEvent(final List<ProxyTarget> sourceItemsToAssign) {
final List<Long> assignedTargetIds = sourceItemsToAssign.stream().map(ProxyIdentifiableEntity::getId)
.collect(Collectors.toList());
eventBus.publish(EventTopics.ENTITY_MODIFIED, this,
new EntityModifiedEventPayload(EntityModifiedEventPayload.EntityModifiedEventType.ENTITY_UPDATED,
ProxyTarget.class, assignedTargetIds));
}
}

View File

@@ -0,0 +1,83 @@
/**
* 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.grid.support.assignment;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.model.AbstractAssignmentResult;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.ui.common.CommonUiDependencies;
import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTarget;
import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTargetType;
import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider;
import org.springframework.util.CollectionUtils;
/**
* Support for un-assigning the {@link ProxyTarget} items from a {@link ProxyTargetType}.
*
*/
public class TargetsToNoTargetTypeAssignmentSupport extends AbstractTargetsToTargetTypeAssignmentSupport {
private final TargetManagement targetManagement;
private static final String CAPTION_TYPE = "caption.type";
private static final String CAPTION_TARGET = "caption.target";
/**
* Constructor for TargetsToTargetTypeAssignmentSupport
*
* @param uiDependencies
* {@link CommonUiDependencies}
* @param targetManagement
* TargetManagement
*/
public TargetsToNoTargetTypeAssignmentSupport(final CommonUiDependencies uiDependencies,
final TargetManagement targetManagement) {
super(uiDependencies);
this.targetManagement = targetManagement;
}
@Override
protected void performAssignment(List<ProxyTarget> sourceItemsToAssign, ProxyTargetType targetItem) {
if (hasRequiredPermissions()) {
final AbstractAssignmentResult<Target> typesAssignmentResult = initiateTargetTypeUnAssignment(
sourceItemsToAssign);
final String assignmentMsg = createAssignmentMessage(typesAssignmentResult, i18n.getMessage(CAPTION_TARGET),
i18n.getMessage(CAPTION_TYPE), "");
notification.displaySuccess(assignmentMsg);
publishTypeAssignmentEvent(sourceItemsToAssign);
}
}
/**
*
* @return false if required permissions are missing
*/
private boolean hasRequiredPermissions() {
final List<String> requiredPermissions = getMissingPermissionsForDrop();
if (!CollectionUtils.isEmpty(requiredPermissions)) {
notification
.displayValidationError(i18n.getMessage(UIMessageIdProvider.MESSAGE_ERROR_PERMISSION_INSUFFICIENT, requiredPermissions));
return false;
}
return true;
}
protected AbstractAssignmentResult<Target> initiateTargetTypeUnAssignment(final List<ProxyTarget> sourceItems) {
final Collection<String> controllerIdsToAssign = sourceItems.stream().map(ProxyTarget::getControllerId)
.collect(Collectors.toList());
return targetManagement.unAssignType(controllerIdsToAssign);
}
}

View File

@@ -0,0 +1,98 @@
/**
* 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.grid.support.assignment;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.model.AbstractAssignmentResult;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.ui.common.CommonUiDependencies;
import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTarget;
import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTargetType;
import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider;
/**
* Support for assigning the {@link ProxyTarget} items to {@link ProxyTargetType}.
*
*/
public class TargetsToTargetTypeAssignmentSupport extends AbstractTargetsToTargetTypeAssignmentSupport {
private final TargetManagement targetManagement;
private static final String CAPTION_TYPE = "caption.type";
private static final String CAPTION_TARGET = "caption.target";
/**
* Constructor for TargetsToTargetTypeAssignmentSupport
*
* @param uiDependencies
* {@link CommonUiDependencies}
* @param targetManagement
* TargetManagement
*/
public TargetsToTargetTypeAssignmentSupport(final CommonUiDependencies uiDependencies, final TargetManagement targetManagement) {
super(uiDependencies);
this.targetManagement = targetManagement;
}
@Override
protected List<ProxyTarget> getFilteredSourceItems(List<ProxyTarget> sourceItemsToAssign, ProxyTargetType targetItem) {
if (!isAssignmentValid(sourceItemsToAssign, targetItem)) {
return Collections.emptyList();
}
return sourceItemsToAssign;
}
/**
*
* @param sourceItemsToAssign
* @param targetItem
* @return false if some targets already have a type assigned
*/
private boolean isAssignmentValid(List<ProxyTarget> sourceItemsToAssign, ProxyTargetType targetItem) {
if(sourceItemsToAssign.size() > 1) {
List<ProxyTarget> targetsWithDifferentType = sourceItemsToAssign.stream().filter(
target -> target.getTypeInfo() != null && !target.getTypeInfo().getId().equals(targetItem.getId()))
.collect(Collectors.toList());
if (!targetsWithDifferentType.isEmpty()) {
notification.displayValidationError(i18n.getMessage(UIMessageIdProvider.MESSAGE_TARGET_TARGETTYPE_ASSIGNED));
return false;
}
}
return true;
}
@Override
protected void performAssignment(final List<ProxyTarget> sourceItemsToAssign, final ProxyTargetType targetItem) {
final Long typeId = targetItem.getId();
final AbstractAssignmentResult<Target> typesAssignmentResult = initiateTargetTypeAssignment(sourceItemsToAssign,
typeId);
final String assignmentMsg = createAssignmentMessage(typesAssignmentResult,
i18n.getMessage(CAPTION_TARGET),
i18n.getMessage(CAPTION_TYPE), targetItem.getName());
notification.displaySuccess(assignmentMsg);
publishTypeAssignmentEvent(sourceItemsToAssign);
}
private AbstractAssignmentResult<Target> initiateTargetTypeAssignment(final List<ProxyTarget> sourceItems,
final Long typeId) {
final Collection<String> controllerIdsToAssign = sourceItems.stream().map(ProxyTarget::getControllerId)
.collect(Collectors.toList());
return targetManagement.assignType(controllerIdsToAssign, typeId);
}
}

View File

@@ -0,0 +1,85 @@
/**
* 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.grid.support.assignment;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
import org.eclipse.hawkbit.ui.SpPermissionChecker;
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.EntityModifiedEventPayload;
import org.eclipse.hawkbit.ui.common.event.EventTopics;
import org.vaadin.spring.events.EventBus;
/**
* Support for assigning the {@link ProxyTargetType} items to {@link ProxyTarget}.
*
*/
public class TypeToTargetAssignmentSupport extends AssignmentSupport<ProxyTargetType, ProxyTarget> {
private final TargetManagement targetManagement;
private final EventBus.UIEventBus eventBus;
private final SpPermissionChecker permChecker;
private static final String CAPTION_TYPE = "caption.type";
private static final String CAPTION_TARGET = "caption.target";
/**
* Constructor for TypeToTargetAssignmentSupport
*
* @param uiDependencies
* {@link CommonUiDependencies}
* @param targetManagement
* TargetManagement
*/
public TypeToTargetAssignmentSupport(final CommonUiDependencies uiDependencies, final TargetManagement targetManagement) {
super(uiDependencies.getUiNotification(), uiDependencies.getI18n());
this.eventBus = uiDependencies.getEventBus();
this.permChecker = uiDependencies.getPermChecker();
this.targetManagement = targetManagement;
}
@Override
public List<String> getMissingPermissionsForDrop() {
return permChecker.hasUpdateTargetPermission() ? Collections.emptyList()
: Collections.singletonList(SpPermission.UPDATE_TARGET);
}
@Override
protected void performAssignment(List<ProxyTargetType> sourceItemsToAssign, ProxyTarget targetItem) {
final String controllerId = targetItem.getControllerId();
// multi-type assignment is not supported
final Long typeId = sourceItemsToAssign.get(0).getId();
TargetTypeAssignmentResult targetTypeAssignmentResult = initiateTargetTypeAssignment(typeId, controllerId);
final String assignmentMsg = createAssignmentMessage(targetTypeAssignmentResult, i18n.getMessage(CAPTION_TARGET),
i18n.getMessage(CAPTION_TYPE), sourceItemsToAssign.get(0).getName());
notification.displaySuccess(assignmentMsg);
publishTypeAssignmentEvent(sourceItemsToAssign);
}
protected TargetTypeAssignmentResult initiateTargetTypeAssignment(final Long targetType, final String controllerId) {
return targetManagement.assignType(Collections.singletonList(controllerId), targetType);
}
protected void publishTypeAssignmentEvent(final List<ProxyTargetType> sourceItemsToAssign) {
final List<Long> assignedTargetIds = sourceItemsToAssign.stream().map(ProxyIdentifiableEntity::getId)
.collect(Collectors.toList());
eventBus.publish(EventTopics.ENTITY_MODIFIED, this, new EntityModifiedEventPayload(
EntityModifiedEventPayload.EntityModifiedEventType.ENTITY_UPDATED, ProxyTarget.class, assignedTargetIds));
}
}

View File

@@ -18,8 +18,26 @@ public class TagFilterLayoutUiState extends HidableLayoutUiState {
private static final long serialVersionUID = 1L;
private boolean noTagClicked;
private boolean noTargetTypeClicked;
private final Map<Long, String> clickedTagIdsWithName = new HashMap<>();
/**
* @return True if no targetType is clicked or selected
*/
public boolean isNoTargetTypeClicked() {
return noTargetTypeClicked;
}
/**
* Sets the status of no targetType clicked
*
* @param noTargetTypeClicked
* boolean
*/
public void setNoTargetTypeClicked(boolean noTargetTypeClicked) {
this.noTargetTypeClicked = noTargetTypeClicked;
}
/**
* @return True if not tag is clicked or selected
*/

View File

@@ -134,6 +134,7 @@ public class TargetCountMessageLabel extends AbstractFooterSupport implements Co
appendSearchMsg(filterMessageBuilder, targetFilterParams.getSearchText());
appendDsMsg(filterMessageBuilder, targetFilterParams.getDistributionId());
appendCustomFilterQueryMsg(filterMessageBuilder, targetFilterParams.getTargetFilterQueryId());
appendTargetTypeFilterMsg(filterMessageBuilder, targetFilterParams.isNoTargetTypeClicked(), targetFilterParams.getTargetTypeId());
String filterMessage = filterMessageBuilder.toString().trim();
if (filterMessage.endsWith(",")) {
@@ -186,6 +187,12 @@ public class TargetCountMessageLabel extends AbstractFooterSupport implements Co
}
}
private void appendTargetTypeFilterMsg(final StringBuilder filterMessageBuilder, boolean noTargetTypeClicked, final Long targetTypeId) {
if (targetTypeId != null || noTargetTypeClicked) {
appendFilterMsg(filterMessageBuilder, i18n.getMessage("label.filter.target.type"));
}
}
/**
* Update pinning details
*

View File

@@ -50,6 +50,7 @@ import org.eclipse.hawkbit.ui.common.grid.support.SelectionSupport;
import org.eclipse.hawkbit.ui.common.grid.support.assignment.AssignmentSupport;
import org.eclipse.hawkbit.ui.common.grid.support.assignment.DistributionSetsToTargetAssignmentSupport;
import org.eclipse.hawkbit.ui.common.grid.support.assignment.TargetTagsToTargetAssignmentSupport;
import org.eclipse.hawkbit.ui.common.grid.support.assignment.TypeToTargetAssignmentSupport;
import org.eclipse.hawkbit.ui.management.dstable.DistributionGridLayoutUiState;
import org.eclipse.hawkbit.ui.management.miscs.DeploymentAssignmentWindowController;
import org.eclipse.hawkbit.ui.management.targettag.filter.TargetTagFilterLayoutUiState;
@@ -157,9 +158,12 @@ public class TargetGrid extends AbstractGrid<ProxyTarget, TargetManagementFilter
uiDependencies, systemSecurityContext, configManagement, assignmentController);
final TargetTagsToTargetAssignmentSupport targetTagsToTargetAssignment = new TargetTagsToTargetAssignmentSupport(
uiDependencies, targetManagement);
final TypeToTargetAssignmentSupport typeToTargetAssignmentSupport = new TypeToTargetAssignmentSupport(
uiDependencies, targetManagement);
sourceTargetAssignmentStrategies.put(UIComponentIdProvider.DIST_TABLE_ID, distributionsToTargetAssignment);
sourceTargetAssignmentStrategies.put(UIComponentIdProvider.TARGET_TAG_TABLE_ID, targetTagsToTargetAssignment);
sourceTargetAssignmentStrategies.put(UIComponentIdProvider.TARGET_TYPE_TABLE_ID, typeToTargetAssignmentSupport);
setDragAndDropSupportSupport(new DragAndDropSupport<>(this, i18n, uiDependencies.getUiNotification(),
sourceTargetAssignmentStrategies, eventBus));
@@ -263,6 +267,10 @@ public class TargetGrid extends AbstractGrid<ProxyTarget, TargetManagementFilter
targetTagFilterLayoutUiState.isNoTagClicked());
getFilterSupport().addMapping(FilterType.TAG, TargetManagementFilterParams::setTargetTags,
targetTagFilterLayoutUiState.getClickedTagIdsWithName().values());
getFilterSupport().addMapping(FilterType.NO_TARGET_TYPE, TargetManagementFilterParams::setNoTargetTypeClicked,
targetTagFilterLayoutUiState.isNoTargetTypeClicked());
getFilterSupport().addMapping(FilterType.TARGET_TYPE, TargetManagementFilterParams::setTargetTypeId,
targetTagFilterLayoutUiState.getClickedTargetTypeFilterId());
getFilterSupport().addMapping(FilterType.QUERY, TargetManagementFilterParams::setTargetFilterQueryId,
targetTagFilterLayoutUiState.getClickedTargetFilterQueryId());
getFilterSupport().addMapping(FilterType.DISTRIBUTION, TargetManagementFilterParams::setDistributionId,
@@ -280,28 +288,23 @@ public class TargetGrid extends AbstractGrid<ProxyTarget, TargetManagementFilter
}
/**
* Update filter on custom tab selection
* Update filter on filter tab selection
*/
public void onCustomTabSelected() {
public void resetAllFilters(){
getFilter().ifPresent(filter -> {
filter.setDistributionId(null);
filter.setNoTagClicked(false);
filter.setNoTargetTypeClicked(false);
filter.setOverdueState(false);
filter.setSearchText(null);
filter.setTargetTags(Collections.emptyList());
filter.setTargetUpdateStatusList(Collections.emptyList());
filter.setTargetTypeId(null);
filter.setTargetFilterQueryId(null);
getFilterSupport().refreshFilter();
});
}
/**
* Update filter on simple tab selection
*/
public void onSimpleTabSelected() {
getFilterSupport().updateFilter(TargetManagementFilterParams::setTargetFilterQueryId, null);
}
@Override
public void addColumns() {
addNameColumn();

View File

@@ -140,7 +140,8 @@ public class TargetGridHeader extends AbstractEntityGridHeader {
super.restoreState();
if (targetTagFilterLayoutUiState.isCustomFilterTabSelected()) {
onSimpleFilterReset();
onFilterReset();
disabledSearchIcon();
}
if (isBulkUploadInProgress()) {
@@ -153,12 +154,10 @@ public class TargetGridHeader extends AbstractEntityGridHeader {
}
/**
* Reset the distribution set filer drop area support
* Reset the distribution set filer drop area support and the search
*/
public void onSimpleFilterReset() {
public void onFilterReset() {
getSearchHeaderSupport().resetSearch();
getSearchHeaderSupport().disableSearch();
distributionSetFilterDropAreaSupport.reset();
}
@@ -248,4 +247,10 @@ public class TargetGridHeader extends AbstractEntityGridHeader {
public void enableSearchIcon() {
getSearchHeaderSupport().enableSearch();
}
/**
* Disable search icon in the search header
*/
public void disabledSearchIcon() {
getSearchHeaderSupport().disableSearch();
}
}

View File

@@ -204,15 +204,21 @@ public class TargetGridLayout extends AbstractGridComponentLayout {
*/
public void onTargetFilterTabChanged(final TargetFilterTabChangedEventPayload eventPayload) {
final boolean isCustomFilterTabSelected = TargetFilterTabChangedEventPayload.CUSTOM == eventPayload;
final boolean isTargetTypeFilterTabSelected = TargetFilterTabChangedEventPayload.TARGET_TYPE == eventPayload;
final boolean isSimpleTypeFilterTabSelected = TargetFilterTabChangedEventPayload.SIMPLE == eventPayload;
targetGridHeader.onFilterReset();
if (isCustomFilterTabSelected) {
targetGridHeader.onSimpleFilterReset();
targetGrid.onCustomTabSelected();
} else {
targetGridHeader.disabledSearchIcon();
}
if(isTargetTypeFilterTabSelected){
targetGridHeader.enableSearchIcon();
}
if (isSimpleTypeFilterTabSelected){
targetGridHeader.enableSearchIcon();
targetGrid.onSimpleTabSelected();
}
targetGrid.resetAllFilters();
countMessageLabel.updateFilteredCount();
}

View File

@@ -38,6 +38,8 @@ public class TargetWindowLayout extends AbstractEntityWindowLayout<ProxyTarget>
*
* @param i18n
* I18N
* @param targetTypeManagement
* TargetTypeManagement
*/
public TargetWindowLayout(final VaadinMessageSource i18n, final TargetTypeManagement targetTypeManagement) {
super();

View File

@@ -8,12 +8,9 @@
*/
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;
@@ -40,6 +37,11 @@ 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.
*/
@@ -77,7 +79,8 @@ public class MultipleTargetFilter extends Accordion {
this.filterByButtons = new TargetTagFilterButtons(uiDependencies, targetTagManagement, targetManagement,
targetTagFilterLayoutUiState, targetTagWindowBuilder);
this.targetTypeFilterButtons = new TargetTypeFilterButtons(uiDependencies, targetTypeManagement, targetTagFilterLayoutUiState, targetTypeWindowBuilder);
this.targetTypeFilterButtons = new TargetTypeFilterButtons(uiDependencies, targetTypeManagement, targetManagement,
targetTagFilterLayoutUiState, targetTypeWindowBuilder);
this.filterByStatusFooter = new FilterByStatusLayout(i18n, eventBus, targetTagFilterLayoutUiState);
this.simpleFilterTab = buildSimpleFilterTab();
this.targetTypeFilterTab = buildTargetTypeFilterTab();
@@ -98,8 +101,7 @@ public class MultipleTargetFilter extends Accordion {
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();
.viewAware(layoutViewAware).entityModifiedAwareSupports(getTargetTypeModifiedAwareSupports()).build();
init();
addTabs();
@@ -165,7 +167,8 @@ public class MultipleTargetFilter extends Accordion {
private List<EntityModifiedAwareSupport> getTargetTypeModifiedAwareSupports() {
return Arrays.asList(EntityModifiedGridRefreshAwareSupport.of(targetTypeFilterButtons::refreshAll),
EntityModifiedGenericSupport.of(null, null, null));
EntityModifiedGenericSupport.of(null, targetTypeFilterButtons::resetFilterOnTargetTypeUpdated,
targetTypeFilterButtons::resetFilterOnTargetTypeDeleted));
}
private List<EntityModifiedAwareSupport> getFilterQueryModifiedAwareSupports() {
@@ -191,9 +194,9 @@ public class MultipleTargetFilter extends Accordion {
public void selectedTabChanged() {
final String selectedTabId = getTab(getSelectedTab()).getId();
if (UIComponentIdProvider.SIMPLE_FILTER_ACCORDION_TAB.equals(selectedTabId)) {
customFilterTab.clearAppliedTargetFilterQuery();
targetTypeFilterButtons.clearAppliedTargetTypeFilter();
targetTagFilterLayoutUiState.setCustomFilterTabSelected(false);
targetTagFilterLayoutUiState.setTargetTypeFilterTabSelected(false);
@@ -201,13 +204,19 @@ public class MultipleTargetFilter extends Accordion {
eventBus.publish(EventTopics.TARGET_FILTER_TAB_CHANGED, this, TargetFilterTabChangedEventPayload.SIMPLE);
}
if (UIComponentIdProvider.TARGET_TYPE_FILTER_ACCORDION_TAB.equals(selectedTabId)){
customFilterTab.clearAppliedTargetFilterQuery();
filterByButtons.clearTargetTagFilters();
filterByStatusFooter.clearStatusAndOverdueFilters();
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();
targetTypeFilterButtons.clearAppliedTargetTypeFilter();
targetTagFilterLayoutUiState.setCustomFilterTabSelected(true);
targetTagFilterLayoutUiState.setTargetTypeFilterTabSelected(false);
@@ -223,20 +232,14 @@ public class MultipleTargetFilter extends Accordion {
public void restoreState() {
if (targetTagFilterLayoutUiState.isCustomFilterTabSelected()) {
customFilterTab.restoreState();
setSelectedTab(customFilterTab);
}
if (targetTagFilterLayoutUiState.isCustomFilterTabSelected()) {
filterByButtons.restoreState();
filterByStatusFooter.restoreState();
setSelectedTab(simpleFilterTab);
}
if (targetTagFilterLayoutUiState.isTargetTypeFilterTabSelected()) {
filterByButtons.restoreState();
filterByStatusFooter.restoreState();
} else if (targetTagFilterLayoutUiState.isTargetTypeFilterTabSelected()){
targetTypeFilterButtons.restoreState();
setSelectedTab(targetTypeFilterTab);
} else {
filterByButtons.restoreState();
filterByStatusFooter.restoreState();
setSelectedTab(simpleFilterTab);
}
}
@@ -246,6 +249,7 @@ public class MultipleTargetFilter extends Accordion {
public void onViewEnter() {
filterByButtons.reevaluateFilter();
customFilterTab.reevaluateFilter();
targetTypeFilterButtons.reevaluateFilter();
}
/**

View File

@@ -21,11 +21,19 @@ public class TargetTagFilterLayoutUiState extends TagFilterLayoutUiState {
private static final long serialVersionUID = 1L;
private Long clickedTargetFilterQueryId;
private Long clickedTargetTypeFilterId;
private final Collection<TargetUpdateStatus> clickedTargetUpdateStatusFilters = new ArrayList<>();
private boolean isOverdueFilterClicked;
private boolean isCustomFilterTabSelected;
private boolean isTargetTypeFilterTabSelected;
public Long getClickedTargetTypeFilterId() {
return clickedTargetTypeFilterId;
}
public void setClickedTargetTypeFilterId(Long clickedTargetTypeFilterId) {
this.clickedTargetTypeFilterId = clickedTargetTypeFilterId;
}
/**
* @return Id of clicked target filter query

View File

@@ -8,11 +8,12 @@
*/
package org.eclipse.hawkbit.ui.management.targettag.filter;
import com.vaadin.ui.Button;
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 com.vaadin.ui.dnd.DropTargetExtension;
import com.vaadin.ui.dnd.event.DropEvent;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.exception.TargetTypeInUseException;
import org.eclipse.hawkbit.ui.common.CommonUiDependencies;
@@ -25,31 +26,59 @@ 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.common.grid.support.DragAndDropSupport;
import org.eclipse.hawkbit.ui.common.grid.support.assignment.AssignmentSupport;
import org.eclipse.hawkbit.ui.common.grid.support.assignment.TargetsToNoTargetTypeAssignmentSupport;
import org.eclipse.hawkbit.ui.common.grid.support.assignment.TargetsToTargetTypeAssignmentSupport;
import org.eclipse.hawkbit.ui.common.layout.listener.EntityDraggingListener;
import org.eclipse.hawkbit.ui.management.targettag.targettype.TargetTypeWindowBuilder;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 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);
private final transient TargetManagement targetManagement;
private final transient TargetTypeManagement targetTypeManagement;
private final transient TargetTypeWindowBuilder targetTypeWindowBuilder;
private final transient CommonUiDependencies uiDependencies;
private transient EntityDraggingListener draggingListener;
TargetTypeFilterButtons(final CommonUiDependencies uiDependencies,
final TargetTypeManagement targetTypeManagement, final TargetManagement targetManagement, final TargetTagFilterLayoutUiState targetTagFilterLayoutUiState,
final TargetTypeWindowBuilder targetTypeWindowBuilder) {
super(uiDependencies, targetTagFilterLayoutUiState, targetTypeManagement);
this.targetManagement = targetManagement;
this.targetTypeManagement = targetTypeManagement;
this.targetTypeWindowBuilder = targetTypeWindowBuilder;
this.uiDependencies = uiDependencies;
final Map<String, AssignmentSupport<?, ProxyTargetType>> sourceTargetAssignmentStrategies = new HashMap<>();
final TargetsToTargetTypeAssignmentSupport targetsToTargetTypeAssignment = new TargetsToTargetTypeAssignmentSupport(uiDependencies,
targetManagement);
sourceTargetAssignmentStrategies.put(UIComponentIdProvider.TARGET_TABLE_ID, targetsToTargetTypeAssignment);
setDropSupportToNoType();
setDragAndDropSupportSupport(new DragAndDropSupport<>(this, i18n, uiNotification,
sourceTargetAssignmentStrategies, eventBus));
getDragAndDropSupportSupport().ignoreSelection(true);
getDragAndDropSupportSupport().addDragAndDrop();
init();
setDataProvider(
@@ -74,7 +103,17 @@ public class TargetTypeFilterButtons extends AbstractTargetTypeFilterButtons {
@Override
protected boolean deleteFilterButtons(Collection<ProxyTargetType> filterButtonsToDelete) {
final ProxyTargetType targetTypeToDelete = filterButtonsToDelete.iterator().next();
return deleteTargetType(targetTypeToDelete);
final String targetTypeToDeleteName = targetTypeToDelete.getName();
final Long targetTypeToDeleteId = targetTypeToDelete.getId();
final Long clickedTargetTypeId = getFilterButtonClickBehaviour().getPreviouslyClickedFilterId();
if (clickedTargetTypeId != null && clickedTargetTypeId.equals(targetTypeToDeleteId)) {
uiNotification.displayValidationError(i18n.getMessage("message.targettype.delete", targetTypeToDeleteName));
} else {
return deleteTargetType(targetTypeToDelete);
}
return false;
}
@Override
@@ -113,12 +152,8 @@ public class TargetTypeFilterButtons extends AbstractTargetTypeFilterButtons {
LOG.trace("Target type already in use exception: {}", exception.getMessage());
uiNotification.displayValidationError(i18n.getMessage(exception.getMessage()));
}
return false;
}
@Override
protected Collection<Long> filterExistingTargetTypeIds(Collection<Long> targetTypeIds) {
return targetTypeManagement.get(targetTypeIds).stream().map(Identifiable::getId).collect(Collectors.toSet());
return false;
}
@Override
@@ -131,4 +166,43 @@ public class TargetTypeFilterButtons extends AbstractTargetTypeFilterButtons {
return permissionChecker.hasUpdateRepositoryPermission();
}
private void setDropSupportToNoType() {
final TargetsToNoTargetTypeAssignmentSupport targetsToNoTargetTypeAssignmentSupport = new TargetsToNoTargetTypeAssignmentSupport(
uiDependencies, targetManagement);
final DropTargetExtension<Button> dropExtension = new DropTargetExtension<>(getNoTargetTypeButton());
dropExtension.addDropListener(event -> {
List<ProxyTarget> droppedTargets = getDroppedTargets(event);
targetsToNoTargetTypeAssignmentSupport.assignSourceItemsToTargetItem(droppedTargets, null);
});
addDropStylingListener();
}
private void addDropStylingListener() {
if (draggingListener == null) {
draggingListener = new EntityDraggingListener(eventBus,
Collections.singletonList(UIComponentIdProvider.TARGET_TABLE_ID), getNoTargetTypeButton());
}
draggingListener.subscribe();
}
private static List<ProxyTarget> getDroppedTargets(final DropEvent<?> dropEvent) {
final List<ProxyTarget> list = new ArrayList<>();
dropEvent.getDragSourceExtension().ifPresent(dragSource -> {
final Object dragData = dragSource.getDragData();
if (dragData instanceof ProxyTarget) {
list.add((ProxyTarget) dragData);
}
if (dragData instanceof List
&& ((List<?>) dragData).stream().allMatch(element -> element instanceof ProxyTarget)) {
list.addAll(((List<?>) dragData).stream().map(element -> (ProxyTarget) element)
.collect(Collectors.toList()));
}
});
return list;
}
}

View File

@@ -8,10 +8,6 @@
*/
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;
@@ -26,6 +22,11 @@ import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType;
import org.eclipse.hawkbit.ui.common.targettype.ProxyTargetTypeValidator;
import org.springframework.util.StringUtils;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Controller for update target type window
*/

View File

@@ -24,6 +24,7 @@ import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleTypeDeletedEven
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetFilterQueryDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetTagDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetTypeDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTagCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTagUpdatedEvent;
@@ -40,6 +41,8 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetFilterQueryCreat
import org.eclipse.hawkbit.repository.event.remote.entity.TargetFilterQueryUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.ui.SpPermissionChecker;
import org.eclipse.hawkbit.ui.push.event.ActionChangedEvent;
@@ -71,7 +74,8 @@ public class HawkbitEventPermissionChecker implements UIEventPermissionChecker {
Arrays.asList(TargetCreatedEvent.class, TargetUpdatedEvent.class, TargetDeletedEvent.class,
TargetTagCreatedEvent.class, TargetTagUpdatedEvent.class, TargetTagDeletedEvent.class,
ActionChangedEvent.class, TargetFilterQueryCreatedEvent.class,
TargetFilterQueryUpdatedEvent.class, TargetFilterQueryDeletedEvent.class),
TargetFilterQueryUpdatedEvent.class, TargetFilterQueryDeletedEvent.class,
TargetTypeCreatedEvent.class, TargetTypeUpdatedEvent.class, TargetTypeDeletedEvent.class),
permChecker::hasTargetReadPermission);
eventPermissionRules.put(Arrays.asList(DistributionSetCreatedEvent.class, DistributionSetUpdatedEvent.class,

View File

@@ -20,6 +20,7 @@ import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleTypeDeletedEven
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetFilterQueryDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetTagDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetTypeDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTagCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTagUpdatedEvent;
@@ -36,6 +37,8 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetFilterQueryCreat
import org.eclipse.hawkbit.repository.event.remote.entity.TargetFilterQueryUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.ui.common.data.proxies.ProxyAction;
import org.eclipse.hawkbit.ui.common.data.proxies.ProxyDistributionSet;
@@ -45,6 +48,7 @@ import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule;
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.data.proxies.ProxyType;
import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload.EntityModifiedEventType;
import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayloadIdentifier;
@@ -134,6 +138,13 @@ public class HawkbitEventProvider implements UIEventProvider {
ProxyTargetFilterQuery.class, EntityModifiedEventType.ENTITY_UPDATED));
EVENTS.put(TargetFilterQueryDeletedEvent.class, new EntityModifiedEventPayloadIdentifier(
ProxyTargetFilterQuery.class, EntityModifiedEventType.ENTITY_REMOVED));
EVENTS.put(TargetTypeCreatedEvent.class, new EntityModifiedEventPayloadIdentifier(
ProxyTargetType.class, EntityModifiedEventType.ENTITY_ADDED));
EVENTS.put(TargetTypeUpdatedEvent.class, new EntityModifiedEventPayloadIdentifier(
ProxyTargetType.class, EntityModifiedEventType.ENTITY_UPDATED));
EVENTS.put(TargetTypeDeletedEvent.class, new EntityModifiedEventPayloadIdentifier(
ProxyTargetType.class, EntityModifiedEventType.ENTITY_REMOVED));
}
@Override

View File

@@ -161,6 +161,8 @@ public final class UIMessageIdProvider {
public static final String MESSAGE_ERROR = "message.error";
public static final String MESSAGE_TARGET_TARGETTYPE_ASSIGNED = "message.target.targetype.assign.error";
public static final String MESSAGE_ERROR_PERMISSION_INSUFFICIENT = "message.permission.insufficient";
public static final String MESSAGE_ERROR_TARGET_TYPE_INCOMPATIBLE = "message.target.type.incompatible";

View File

@@ -22,7 +22,11 @@
.filter-drop-hint-layout.show-drop-hint {
border: 1px dashed $hawkbit-primary-color !important;
}
.filter-drop-hint-layout.show-drop-hint.v-button-dragtarget{
background-color: desaturate(lighten($hawkbit-primary-color, 50%), 20%);
}
.filter-drop-hint-layout.show-drop-hint.v-horizontallayout-drag-center{
background-color: desaturate(lighten($hawkbit-primary-color, 50%), 20%);
}

View File

@@ -226,6 +226,7 @@ label.filter.tags = Tags
label.filter.text = Search Text
label.filter.dist = Distribution
label.filter.custom = Custom
label.filter.target.type = Target Type
label.target.filter.truncated={0} targets has been truncated in the list due the target size limit of {1}, use filters to reduce the targets to be shown
label.unknown = Unknown
label.target.id = Controller Id
@@ -617,6 +618,7 @@ message.dist.type.notfound = Distribution set type for {0} was not found
message.assign.discard.success = All assignments are discarded successfully !
message.target.ds.assign.success = Assignment saved successfully !
message.target.ds.multiassign.error = Cannot assign multiple distribution sets to a target!
message.target.targetype.assign.error = One or more targets are already assigned to a target type
message.bulk.upload.assignment.failed = Distribution set assignment failed as distribution set no longer exists!
message.bulk.upload.result.success = Successful: {0}
message.bulk.upload.result.fail = Failed: {0}