Feature soft autoassignment (#789)

* Repository layer and database migration
* Changed target filter management to update auto assignment action type together with distribution set, extended mgmt API, adapted auto assign checker/scheduler, changed auto assign distribution set select table to combobox, added filter functionality (needs refactoring)
* Refactored auto assignment dialog window, added soft/forced option group, restructured action type option group layout
* Added forced icon to target filter table autoassignment cell for the forced auto assign actions
* First working draft of distribution set select combobox for auto assignment window
* Optimised filtering queries, added alphabetical sorting on distribution set name and version
* Refactoring of distribution bean query for lazy loading
* Distribution set combobox refactoring and comments
* Added verification of auto assign distribution set validity (completed, not deleted), exdended target filter query fields with auto assign action type field, added rsql filter tests, added repository layer tests
* Added mgmt API tests
* Changed target filter rest docu tests to include auto-assignment type
* Updated hawkbit docs with auto-assignment description
* Added debouncing to key and value input fields of metadata popup layout to get rid of unnecessary value change events, removed redundant set value call in common dialog window, minimizing the repaint components calls
* Fixed sonar findings
* Reverted changes of common dialog window validaton, setting the value of the field explicitly as before, until we rethink the whole concept of validaton mechanism
* Fixed review findings
* Removed rsql filtering by filter auto-assign action type, due to missing conversion of disallowed timeforced action type
* Small fix regarding usage of page request
* Updated sql script version for flyway
* Extended tests for soft deleted distribution sets for auto-assignment and filter string within distribution set specification builder

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com>
This commit is contained in:
Bondar Bogdan
2019-03-13 13:35:22 +01:00
committed by Stefan Behl
parent 185f88e19b
commit 664c467920
63 changed files with 1891 additions and 750 deletions

View File

@@ -108,6 +108,7 @@ Custom target filter overview and filter management.
- Custom target filter allows user to filter targets by defining custom query.
- Displays custom target filter list and user can search any particular filter.
- Create, update and delete features are supported for target filters.
- Auto assignment of a distribution set to filtered targets.
### How to Filter
The basic syntax to filter is: `field<basic_operator>value <composite_operator> field<basic_operator>value <...>`
@@ -139,3 +140,12 @@ name==CCU* and updatestatus==pending
(updatestatus!=error or updatestatus!=pending) and (name==\*CCU\* or description==\*CCU\*) | Gives all targets that either have the term CCU in their name or their description and that either have the _update status_ not in state error or pending.
![Target Filter Management view](../images/ui/target_filter.png)
### Auto assignment
It is possible to assign some distribution set with different action types (_forced_ or _soft_) to all targets that belong to the corresponding custom target filter, including the ones, that are registered later on.
In order to activate the auto-assignment, one should first click on _Auto assignment_ cell in Custom Filters table, and then check the corresponding checkbox. After that, the action type and distribution set for auto-assignment should be selected and confirmed.
As long as the auto-assignment stays active, the scheduler will try to assign selected distribution set to corresponding custom filter targets, that have never seen it before.
![Auto assignment](../images/ui/target_filter_auto_assignment.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

@@ -22,82 +22,98 @@ public enum SpServerError {
/**
*
*/
SP_REPO_ENTITY_ALRREADY_EXISTS("hawkbit.server.error.repo.entitiyAlreayExists", "The given entity already exists in database"),
SP_REPO_ENTITY_ALRREADY_EXISTS("hawkbit.server.error.repo.entitiyAlreayExists",
"The given entity already exists in database"),
/**
*
*/
SP_REPO_CONSTRAINT_VIOLATION("hawkbit.server.error.repo.constraintViolation", "The given entity cannot be saved due to Constraint Violation"),
SP_REPO_CONSTRAINT_VIOLATION("hawkbit.server.error.repo.constraintViolation",
"The given entity cannot be saved due to Constraint Violation"),
/**
*
*/
SP_REPO_INVALID_TARGET_ADDRESS("hawkbit.server.error.repo.invalidTargetAddress", "The target address is not well formed"),
SP_REPO_INVALID_TARGET_ADDRESS("hawkbit.server.error.repo.invalidTargetAddress",
"The target address is not well formed"),
/**
*
*/
SP_REPO_ENTITY_NOT_EXISTS("hawkbit.server.error.repo.entitiyNotFound", "The given entity does not exist in the repository"),
SP_REPO_ENTITY_NOT_EXISTS("hawkbit.server.error.repo.entitiyNotFound",
"The given entity does not exist in the repository"),
/**
*
*/
SP_REPO_CONCURRENT_MODIFICATION("hawkbit.server.error.repo.concurrentModification", "The given entity has been changed by another user/session"),
SP_REPO_CONCURRENT_MODIFICATION("hawkbit.server.error.repo.concurrentModification",
"The given entity has been changed by another user/session"),
/**
*
*/
SP_TARGET_ATTRIBUTES_INVALID("hawkbit.server.error.repo.invalidTargetAttributes", "The given target attributes are invalid"),
SP_TARGET_ATTRIBUTES_INVALID("hawkbit.server.error.repo.invalidTargetAttributes",
"The given target attributes are invalid"),
/**
*
*/
SP_REST_SORT_PARAM_SYNTAX("hawkbit.server.error.rest.param.sortParamSyntax", "The given sort paramter is not well formed"),
SP_REST_SORT_PARAM_SYNTAX("hawkbit.server.error.rest.param.sortParamSyntax",
"The given sort paramter is not well formed"),
/**
*
*/
SP_REST_RSQL_SEARCH_PARAM_SYNTAX("hawkbit.server.error.rest.param.rsqlParamSyntax", "The given search paramter is not well formed"),
SP_REST_RSQL_SEARCH_PARAM_SYNTAX("hawkbit.server.error.rest.param.rsqlParamSyntax",
"The given search paramter is not well formed"),
/**
*
*/
SP_REST_RSQL_PARAM_INVALID_FIELD("hawkbit.server.error.rest.param.rsqlInvalidField", "The given search parameter field does not exist"),
SP_REST_RSQL_PARAM_INVALID_FIELD("hawkbit.server.error.rest.param.rsqlInvalidField",
"The given search parameter field does not exist"),
/**
*
*/
SP_REST_SORT_PARAM_INVALID_FIELD("hawkbit.server.error.rest.param.invalidField", "The given sort parameter field does not exist"),
SP_REST_SORT_PARAM_INVALID_FIELD("hawkbit.server.error.rest.param.invalidField",
"The given sort parameter field does not exist"),
/**
*
*/
SP_REST_SORT_PARAM_INVALID_DIRECTION("hawkbit.server.error.rest.param.invalidDirection", "The given sort parameter direction does not exist"),
SP_REST_SORT_PARAM_INVALID_DIRECTION("hawkbit.server.error.rest.param.invalidDirection",
"The given sort parameter direction does not exist"),
/**
*
*/
SP_REST_BODY_NOT_READABLE("hawkbit.server.error.rest.body.notReadable", "The given request body is not well formed"),
SP_REST_BODY_NOT_READABLE("hawkbit.server.error.rest.body.notReadable",
"The given request body is not well formed"),
/**
*
*/
SP_ARTIFACT_UPLOAD_FAILED("hawkbit.server.error.artifact.uploadFailed", "Upload of artifact failed with internal server error."),
SP_ARTIFACT_UPLOAD_FAILED("hawkbit.server.error.artifact.uploadFailed",
"Upload of artifact failed with internal server error."),
/**
*
*/
SP_ARTIFACT_UPLOAD_FAILED_MD5_MATCH("hawkbit.server.error.artifact.uploadFailed.checksum.md5.match", "Upload of artifact failed as the provided MD5 checksum did not match with the provided artifact."),
SP_ARTIFACT_UPLOAD_FAILED_MD5_MATCH("hawkbit.server.error.artifact.uploadFailed.checksum.md5.match",
"Upload of artifact failed as the provided MD5 checksum did not match with the provided artifact."),
/**
*
*/
SP_ARTIFACT_UPLOAD_FAILED_SHA1_MATCH("hawkbit.server.error.artifact.uploadFailed.checksum.sha1.match", "Upload of artifact failed as the provided SHA1 checksum did not match with the provided artifact."),
SP_ARTIFACT_UPLOAD_FAILED_SHA1_MATCH("hawkbit.server.error.artifact.uploadFailed.checksum.sha1.match",
"Upload of artifact failed as the provided SHA1 checksum did not match with the provided artifact."),
/**
*
*/
SP_DS_CREATION_FAILED_MISSING_MODULE("hawkbit.server.error.distributionset.creationFailed.missingModule", "Creation if Distribution Set failed as module is missing that is configured as mandatory."),
SP_DS_CREATION_FAILED_MISSING_MODULE("hawkbit.server.error.distributionset.creationFailed.missingModule",
"Creation if Distribution Set failed as module is missing that is configured as mandatory."),
/**
*
@@ -107,12 +123,14 @@ public enum SpServerError {
/**
*
*/
SP_ARTIFACT_DELETE_FAILED("hawkbit.server.error.artifact.deleteFailed", "Deletion of artifact failed with internal server error."),
SP_ARTIFACT_DELETE_FAILED("hawkbit.server.error.artifact.deleteFailed",
"Deletion of artifact failed with internal server error."),
/**
*
*/
SP_ARTIFACT_LOAD_FAILED("hawkbit.server.error.artifact.loadFailed", "Load of artifact failed with internal server error."),
SP_ARTIFACT_LOAD_FAILED("hawkbit.server.error.artifact.loadFailed",
"Load of artifact failed with internal server error."),
/**
*
@@ -123,33 +141,39 @@ public enum SpServerError {
* error message, which describes that the action can not be canceled cause
* the action is inactive.
*/
SP_ACTION_NOT_CANCELABLE("hawkbit.server.error.action.notcancelable", "Only active actions which are in status pending are canceable."),
SP_ACTION_NOT_CANCELABLE("hawkbit.server.error.action.notcancelable",
"Only active actions which are in status pending are canceable."),
/**
* error message, which describes that the action can not be force quit
* cause the action is inactive.
*/
SP_ACTION_NOT_FORCE_QUITABLE("hawkbit.server.error.action.notforcequitable", "Only active actions which are in status pending can be force quit."),
SP_ACTION_NOT_FORCE_QUITABLE("hawkbit.server.error.action.notforcequitable",
"Only active actions which are in status pending can be force quit."),
/**
*
*/
SP_DS_INCOMPLETE("hawkbit.server.error.distributionset.incomplete", "Distribution set is assigned to a a target that is incomplete (i.e. mandatory modules are missing)"),
SP_DS_INCOMPLETE("hawkbit.server.error.distributionset.incomplete",
"Distribution set is assigned to a target that is incomplete (i.e. mandatory modules are missing)"),
/**
*
*/
SP_DS_TYPE_UNDEFINED("hawkbit.server.error.distributionset.type.undefined", "Distribution set type is not yet defined. Modules cannot be added until definition."),
SP_DS_TYPE_UNDEFINED("hawkbit.server.error.distributionset.type.undefined",
"Distribution set type is not yet defined. Modules cannot be added until definition."),
/**
*
*/
SP_DS_MODULE_UNSUPPORTED("hawkbit.server.error.distributionset.modules.unsupported", "Distribution set type does not contain the given module, i.e. is incompatible."),
SP_DS_MODULE_UNSUPPORTED("hawkbit.server.error.distributionset.modules.unsupported",
"Distribution set type does not contain the given module, i.e. is incompatible."),
/**
*
*/
SP_REPO_TENANT_NOT_EXISTS("hawkbit.server.error.repo.tenantNotExists", "The entity cannot be inserted due the tenant does not exists"),
SP_REPO_TENANT_NOT_EXISTS("hawkbit.server.error.repo.tenantNotExists",
"The entity cannot be inserted due the tenant does not exists"),
/**
*
@@ -159,12 +183,14 @@ public enum SpServerError {
/**
*
*/
SP_REPO_ENTITY_READ_ONLY("hawkbit.server.error.entityreadonly", "The given entity is read only and the change cannot be completed."),
SP_REPO_ENTITY_READ_ONLY("hawkbit.server.error.entityreadonly",
"The given entity is read only and the change cannot be completed."),
/**
*
*/
SP_CONFIGURATION_VALUE_INVALID("hawkbit.server.error.configValueInvalid", "The given configuration value is invalid."),
SP_CONFIGURATION_VALUE_INVALID("hawkbit.server.error.configValueInvalid",
"The given configuration value is invalid."),
/**
*
*/
@@ -173,22 +199,40 @@ public enum SpServerError {
/**
*
*/
SP_ROLLOUT_ILLEGAL_STATE("hawkbit.server.error.rollout.illegalstate", "The rollout is in the wrong state for the requested operation"),
SP_ROLLOUT_ILLEGAL_STATE("hawkbit.server.error.rollout.illegalstate",
"The rollout is in the wrong state for the requested operation"),
/**
*
*/
SP_ROLLOUT_VERIFICATION_FAILED("hawkbit.server.error.rollout.verificationFailed", "The rollout configuration could not be verified successfully"),
SP_ROLLOUT_VERIFICATION_FAILED("hawkbit.server.error.rollout.verificationFailed",
"The rollout configuration could not be verified successfully"),
/**
*
*/
SP_REPO_OPERATION_NOT_SUPPORTED("hawkbit.server.error.operation.notSupported", "Operation or method is (no longer) supported by service."),
SP_REPO_OPERATION_NOT_SUPPORTED("hawkbit.server.error.operation.notSupported",
"Operation or method is (no longer) supported by service."),
/**
* Error message informing that the maintenance schedule is invalid.
*/
SP_MAINTENANCE_SCHEDULE_INVALID("hawkbit.server.error.maintenanceScheduleInvalid", "Information for schedule, duration or timezone is missing; or there is no valid maintenance window available in future.");
SP_MAINTENANCE_SCHEDULE_INVALID("hawkbit.server.error.maintenanceScheduleInvalid",
"Information for schedule, duration or timezone is missing; or there is no valid maintenance window available in future."),
/**
* Error message informing that the action type for auto-assignment is
* invalid.
*/
SP_AUTO_ASSIGN_ACTION_TYPE_INVALID("hawkbit.server.error.repo.invalidAutoAssignActionType",
"The given action type for auto-assignment is invalid: allowed values are FORCED and SOFT"),
/**
* Error message informing that the distribution set for auto-assignment is
* invalid.
*/
SP_AUTO_ASSIGN_DISTRIBUTION_SET_INVALID("hawkbit.server.error.repo.invalidAutoAssignDistributionSet",
"The given distribution set for auto-assignment is invalid: it is either incomplete (i.e. mandatory modules are missing) or soft deleted");
private final String key;
private final String message;

View File

@@ -30,7 +30,7 @@ public enum TargetFilterQueryFields implements FieldNameProvider {
NAME("name"),
/**
* distribution set which is set as auto assign distribution set
* Distribution set for auto-assignment.
*/
AUTOASSIGNDISTRIBUTIONSET("autoAssignDistributionSet", "name", "version");

View File

@@ -23,7 +23,6 @@ import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
@@ -56,7 +55,7 @@ public interface RepositoryManagement<T, C, U> {
List<T> create(@NotNull @Valid Collection<C> creates);
/**
* Creates new {@link SoftwareModuleType}.
* Creates new {@link BaseEntity}.
*
* @param create
* to create

View File

@@ -18,9 +18,12 @@ import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryUpdate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignDistributionSetException;
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.data.domain.Page;
@@ -213,13 +216,14 @@ public interface TargetFilterQueryManagement {
TargetFilterQuery update(@NotNull @Valid TargetFilterQueryUpdate update);
/**
* Updates the the auto-assign {@link DistributionSet} of the addressed
* {@link TargetFilterQuery}.
* Updates the the auto-assign {@link DistributionSet} and sets default
* (FORCED) {@link ActionType} of the addressed {@link TargetFilterQuery}.
*
* @param queryId
* of the target filter query to be updated
* @param dsId
* to be updated or <code>null</code> in order to remove it
* together with the auto-assign {@link ActionType}
*
* @return the updated {@link TargetFilterQuery}
*
@@ -230,8 +234,47 @@ public interface TargetFilterQueryManagement {
* @throws QuotaExceededException
* if the query that is already associated with this filter
* query addresses too many targets (auto-assignments only)
*
* @throws InvalidAutoAssignDistributionSetException
* if the provided auto-assign {@link DistributionSet} is not
* valid (incomplete or soft deleted)
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
TargetFilterQuery updateAutoAssignDS(long queryId, Long dsId);
default TargetFilterQuery updateAutoAssignDS(final long queryId, final Long dsId) {
return updateAutoAssignDSWithActionType(queryId, dsId, null);
}
/**
* Updates the the auto-assign {@link DistributionSet} and
* {@link ActionType} of the addressed {@link TargetFilterQuery}.
*
* @param queryId
* of the target filter query to be updated
* @param dsId
* to be updated or <code>null</code> in order to remove it
* together with the auto-assign {@link ActionType}
* @param actionType
* to be updated or <code>null</code> for default (FORCED) if
* distribution set Id is present
*
* @return the updated {@link TargetFilterQuery}
*
* @throws EntityNotFoundException
* if either {@link TargetFilterQuery} and/or autoAssignDs are
* provided but not found
*
* @throws QuotaExceededException
* if the query that is already associated with this filter
* query addresses too many targets (auto-assignments only)
*
* @throws InvalidAutoAssignActionTypeException
* if the provided auto-assign {@link ActionType} is not valid
* (neither FORCED, nor SOFT)
*
* @throws InvalidAutoAssignDistributionSetException
* if the provided auto-assign {@link DistributionSet} is not
* valid (incomplete or soft deleted)
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
TargetFilterQuery updateAutoAssignDSWithActionType(long queryId, Long dsId, ActionType actionType);
}

View File

@@ -13,6 +13,7 @@ import java.util.Optional;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.NamedEntity;
@@ -40,20 +41,27 @@ public interface TargetFilterQueryCreate {
TargetFilterQueryCreate query(@Size(min = 1, max = TargetFilterQuery.QUERY_MAX_SIZE) @NotNull String query);
/**
* @param set
* @param distributionSet
* for {@link TargetFilterQuery#getAutoAssignDistributionSet()}
* @return updated builder instance
*/
default TargetFilterQueryCreate set(final DistributionSet set) {
return set(Optional.ofNullable(set).map(DistributionSet::getId).orElse(null));
default TargetFilterQueryCreate autoAssignDistributionSet(final DistributionSet distributionSet) {
return autoAssignDistributionSet(Optional.ofNullable(distributionSet).map(DistributionSet::getId).orElse(null));
}
/**
* @param setId
* @param dsId
* for {@link TargetFilterQuery#getAutoAssignDistributionSet()}
* @return updated builder instance
*/
TargetFilterQueryCreate set(long setId);
TargetFilterQueryCreate autoAssignDistributionSet(Long dsId);
/**
* @param actionType
* for {@link TargetFilterQuery#getAutoAssignActionType()}
* @return updated builder instance
*/
TargetFilterQueryCreate autoAssignActionType(ActionType actionType);
/**
* @return peek on current state of {@link TargetFilterQuery} in the builder

View File

@@ -88,7 +88,7 @@ public class EntityNotFoundException extends AbstractServerRtException {
* for the {@link MetaData} entry
*/
public EntityNotFoundException(final Class<? extends MetaData> type, final Long entityId, final String key) {
this(type.getSimpleName() + " for given entity {" + entityId + "} and with key {" + key + "} does not exist.");
this(type, String.valueOf(entityId), key);
}
/**
@@ -96,13 +96,13 @@ public class EntityNotFoundException extends AbstractServerRtException {
*
* @param type
* of the entity that was not found
* @param enityId
* @param entityId
* of the {@link BaseEntity} the {@link MetaData} was for
* @param key
* for the {@link MetaData} entry
*/
public EntityNotFoundException(final Class<? extends MetaData> type, final String enityId, final String key) {
this(type.getSimpleName() + " for given entity {" + enityId + "} and with key {" + key + "} does not exist.");
public EntityNotFoundException(final Class<? extends MetaData> type, final String entityId, final String key) {
this(type.getSimpleName() + " for given entity {" + entityId + "} and with key {" + key + "} does not exist.");
}
/**

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2018 Bosch Software Innovations 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.exception;
import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.exception.SpServerError;
/**
* Thrown if an action type for auto-assignment is neither 'forced', nor 'soft'.
*/
public class InvalidAutoAssignActionTypeException extends AbstractServerRtException {
private static final long serialVersionUID = 1L;
private static final SpServerError THIS_ERROR = SpServerError.SP_AUTO_ASSIGN_ACTION_TYPE_INVALID;
/**
* Default constructor.
*/
public InvalidAutoAssignActionTypeException() {
super(THIS_ERROR);
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2019 Bosch Software Innovations 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.exception;
import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.exception.SpServerError;
/**
* Thrown if a distribution set for auto-assignment is incomplete (i.e.
* mandatory modules are missing) or soft deleted.
*/
public class InvalidAutoAssignDistributionSetException extends AbstractServerRtException {
private static final long serialVersionUID = 1L;
private static final SpServerError THIS_ERROR = SpServerError.SP_AUTO_ASSIGN_DISTRIBUTION_SET_INVALID;
/**
* Default constructor.
*/
public InvalidAutoAssignDistributionSetException() {
super(THIS_ERROR);
}
}

View File

@@ -22,6 +22,7 @@ public final class DistributionSetFilter {
private Boolean isComplete;
private DistributionSetType type;
private String searchText;
private String filterString;
private Boolean selectDSWithNoTag;
private Collection<String> tagNames;
private String assignedTargetId;
@@ -61,6 +62,11 @@ public final class DistributionSetFilter {
return this;
}
public DistributionSetFilterBuilder setFilterString(final String filterString) {
this.filterString = filterString;
return this;
}
public DistributionSetFilterBuilder setSelectDSWithNoTag(final Boolean selectDSWithNoTag) {
this.selectDSWithNoTag = selectDSWithNoTag;
return this;
@@ -82,6 +88,7 @@ public final class DistributionSetFilter {
private final Boolean isComplete;
private final DistributionSetType type;
private final String searchText;
private final String filterString;
private final Boolean selectDSWithNoTag;
private final Collection<String> tagNames;
private final String assignedTargetId;
@@ -99,6 +106,7 @@ public final class DistributionSetFilter {
this.isComplete = builder.isComplete;
this.type = builder.type;
this.searchText = builder.searchText;
this.filterString = builder.filterString;
this.selectDSWithNoTag = builder.selectDSWithNoTag;
this.tagNames = builder.tagNames;
this.assignedTargetId = builder.assignedTargetId;
@@ -125,6 +133,10 @@ public final class DistributionSetFilter {
return searchText;
}
public String getFilterString() {
return filterString;
}
public Boolean getSelectDSWithNoTag() {
return selectDSWithNoTag;
}

View File

@@ -8,6 +8,12 @@
*/
package org.eclipse.hawkbit.repository.model;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
/**
* Managed filter entity.
*
@@ -38,6 +44,12 @@ public interface TargetFilterQuery extends TenantAwareBaseEntity {
*/
int QUERY_MAX_SIZE = 1024;
/**
* Allowed values for auto-assign action type
*/
Set<ActionType> ALLOWED_AUTO_ASSIGN_ACTION_TYPES = Collections
.unmodifiableSet(EnumSet.of(ActionType.FORCED, ActionType.SOFT));
/**
* @return name of the {@link TargetFilterQuery}.
*/
@@ -53,4 +65,9 @@ public interface TargetFilterQuery extends TenantAwareBaseEntity {
*/
DistributionSet getAutoAssignDistributionSet();
/**
* @return the auto assign {@link ActionType} if given.
*/
ActionType getAutoAssignActionType();
}

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.builder;
import java.util.Optional;
import org.eclipse.hawkbit.repository.ValidString;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.springframework.util.StringUtils;
/**
@@ -26,15 +27,26 @@ public abstract class AbstractTargetFilterQueryUpdateCreate<T> extends AbstractB
@ValidString
protected String query;
protected Long set;
protected Long distributionSetId;
public T set(final long set) {
this.set = set;
protected ActionType actionType;
public T autoAssignDistributionSet(final Long distributionSetId) {
this.distributionSetId = distributionSetId;
return (T) this;
}
public Optional<Long> getSet() {
return Optional.ofNullable(set);
public Optional<Long> getAutoAssignDistributionSetId() {
return Optional.ofNullable(distributionSetId);
}
public T autoAssignActionType(final ActionType actionType) {
this.actionType = actionType;
return (T) this;
}
public Optional<ActionType> getAutoAssignActionType() {
return Optional.ofNullable(actionType);
}
public T name(final String name) {

View File

@@ -267,7 +267,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
if (!assigned.isEmpty()) {
final Long[] dsIds = assigned.toArray(new Long[assigned.size()]);
distributionSetRepository.deleteDistributionSet(dsIds);
targetFilterQueryRepository.unsetAutoAssignDistributionSet(dsIds);
targetFilterQueryRepository.unsetAutoAssignDistributionSetAndActionType(dsIds);
}
// mark the rest as hard delete
@@ -276,6 +276,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
// hard delete the rest if exists
if (!toHardDelete.isEmpty()) {
targetFilterQueryRepository
.unsetAutoAssignDistributionSetAndActionType(toHardDelete.toArray(new Long[toHardDelete.size()]));
// don't give the delete statement an empty list, JPA/Oracle cannot
// handle the empty list
distributionSetRepository.deleteByIdIn(toHardDelete);
@@ -602,7 +604,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
private static List<Specification<JpaDistributionSet>> buildDistributionSetSpecifications(
final DistributionSetFilter distributionSetFilter) {
final List<Specification<JpaDistributionSet>> specList = Lists.newArrayListWithExpectedSize(7);
final List<Specification<JpaDistributionSet>> specList = Lists.newArrayListWithExpectedSize(8);
Specification<JpaDistributionSet> spec;
@@ -626,6 +628,14 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
specList.add(spec);
}
if (!StringUtils.isEmpty(distributionSetFilter.getFilterString())) {
final String[] dsFilterNameAndVersionEntries = getDsFilterNameAndVersionEntries(
distributionSetFilter.getFilterString().trim());
spec = DistributionSetSpecification.likeNameAndVersion(dsFilterNameAndVersionEntries[0],
dsFilterNameAndVersionEntries[1]);
specList.add(spec);
}
if (isDSWithNoTagSelected(distributionSetFilter) || isTagsSelected(distributionSetFilter)) {
spec = DistributionSetSpecification.hasTags(distributionSetFilter.getTagNames(),
distributionSetFilter.getSelectDSWithNoTag());
@@ -642,6 +652,19 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
return specList;
}
// the format of filter string is 'name:version'. 'name' and 'version'
// fields follow the starts_with semantic, that changes to equal for 'name'
// field when the semicolon is present
private static String[] getDsFilterNameAndVersionEntries(final String filterString) {
final int semicolonIndex = filterString.indexOf(':');
final String dsFilterName = semicolonIndex != -1 ? filterString.substring(0, semicolonIndex)
: (filterString + "%");
final String dsFilterVersion = semicolonIndex != -1 ? (filterString.substring(semicolonIndex + 1) + "%") : "%";
return new String[] { !StringUtils.isEmpty(dsFilterName) ? dsFilterName : "%", dsFilterVersion };
}
private void assertDistributionSetIsNotAssignedToTargets(final Long distributionSet) {
if (actionRepository.countByDistributionSetId(distributionSet) > 0) {
throw new EntityReadOnlyException(String.format(

View File

@@ -22,6 +22,8 @@ import org.eclipse.hawkbit.repository.builder.GenericTargetFilterQueryUpdate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryUpdate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignDistributionSetException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetFilterQueryCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
@@ -30,6 +32,7 @@ import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
@@ -88,7 +91,9 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
// enforce the 'max targets per auto assign' quota right here even if
// the result of the filter query can vary over time
create.getSet().flatMap(set -> create.getQuery()).ifPresent(this::assertMaxTargetsQuota);
if (create.getAutoAssignDistributionSetId().isPresent()) {
create.getQuery().ifPresent(this::assertMaxTargetsQuota);
}
return targetFilterQueryRepository.save(create.build());
}
@@ -217,24 +222,53 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
@Override
@Transactional
public TargetFilterQuery updateAutoAssignDS(final long queryId, final Long dsId) {
public TargetFilterQuery updateAutoAssignDSWithActionType(final long queryId, final Long dsId,
final ActionType actionType) {
final JpaTargetFilterQuery targetFilterQuery = findTargetFilterQueryOrThrowExceptionIfNotFound(queryId);
targetFilterQuery.setAutoAssignDistributionSet(
Optional.ofNullable(dsId).map(this::findDistributionSetAndThrowExceptionIfNotFound).orElse(null));
// we cannot be sure that the quota was enforced at creation time
// because the Target Filter Query REST API does not allow to specify an
// auto-assign distribution set when creating a target filter query
if (dsId != null) {
if (dsId == null) {
targetFilterQuery.setAutoAssignDistributionSet(null);
targetFilterQuery.setAutoAssignActionType(null);
} else {
// we cannot be sure that the quota was enforced at creation time
// because the Target Filter Query REST API does not allow to
// specify an
// auto-assign distribution set when creating a target filter query
assertMaxTargetsQuota(targetFilterQuery.getQuery());
final JpaDistributionSet distributionSetToAutoAssign = findDistributionSetAndThrowExceptionIfNotFound(dsId);
// must be completed and not soft deleted
verifyDistributionSetAndThrowExceptionIfNotValid(distributionSetToAutoAssign);
targetFilterQuery.setAutoAssignDistributionSet(distributionSetToAutoAssign);
// the action type is set to FORCED per default (when not explicitly
// specified)
targetFilterQuery.setAutoAssignActionType(sanitizeAutoAssignActionType(actionType));
}
return targetFilterQueryRepository.save(targetFilterQuery);
}
private static void verifyDistributionSetAndThrowExceptionIfNotValid(final DistributionSet distributionSet) {
if (!distributionSet.isComplete() || distributionSet.isDeleted()) {
throw new InvalidAutoAssignDistributionSetException();
}
}
private static ActionType sanitizeAutoAssignActionType(final ActionType actionType) {
if (actionType == null) {
return ActionType.FORCED;
}
if (!TargetFilterQuery.ALLOWED_AUTO_ASSIGN_ACTION_TYPES.contains(actionType)) {
throw new InvalidAutoAssignActionTypeException();
}
return actionType;
}
private JpaDistributionSet findDistributionSetAndThrowExceptionIfNotFound(final Long setId) {
return (JpaDistributionSet) distributionSetManagement.getWithDetails(setId)
return (JpaDistributionSet) distributionSetManagement.get(setId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, setId));
}

View File

@@ -45,15 +45,16 @@ public interface TargetFilterQueryRepository
Page<JpaTargetFilterQuery> findAll();
/**
* Sets the auto assign distribution sets to null which match the ds ids.
* Sets the auto assign distribution sets and action types to null which
* match the ds ids.
*
* @param dsIds
* distribution set ids to be set to null
*/
@Modifying
@Transactional
@Query("update JpaTargetFilterQuery d set d.autoAssignDistributionSet = NULL where d.autoAssignDistributionSet in :ids")
void unsetAutoAssignDistributionSet(@Param("ids") Long... dsIds);
@Query("update JpaTargetFilterQuery d set d.autoAssignDistributionSet = NULL, d.autoAssignActionType = NULL where d.autoAssignDistributionSet in :ids")
void unsetAutoAssignDistributionSetAndActionType(@Param("ids") Long... dsIds);
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety

View File

@@ -17,7 +17,8 @@ import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
import org.eclipse.hawkbit.repository.model.Target;
@@ -28,12 +29,9 @@ import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
/**
* Checks if targets need a new distribution set (DS) based on the target filter
@@ -52,7 +50,7 @@ public class AutoAssignChecker {
private final DeploymentManagement deploymentManagement;
private final TransactionTemplate transactionTemplate;
private final PlatformTransactionManager transactionManager;
/**
* Maximum for target filter queries with auto assign DS Maximum for targets
@@ -84,13 +82,7 @@ public class AutoAssignChecker {
this.targetFilterQueryManagement = targetFilterQueryManagement;
this.targetManagement = targetManagement;
this.deploymentManagement = deploymentManagement;
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("autoAssignDSToTargets");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
def.setReadOnly(false);
def.setIsolationLevel(Isolation.READ_COMMITTED.value());
transactionTemplate = new TransactionTemplate(transactionManager, def);
this.transactionManager = transactionManager;
}
/**
@@ -104,8 +96,7 @@ public class AutoAssignChecker {
final PageRequest pageRequest = PageRequest.of(0, PAGE_SIZE);
final Page<TargetFilterQuery> filterQueries = targetFilterQueryManagement
.findWithAutoAssignDS(pageRequest);
final Page<TargetFilterQuery> filterQueries = targetFilterQueryManagement.findWithAutoAssignDS(pageRequest);
for (final TargetFilterQuery filterQuery : filterQueries) {
checkByTargetFilterQueryAndAssignDS(filterQuery);
@@ -149,15 +140,17 @@ public class AutoAssignChecker {
*/
private int runTransactionalAssignment(final TargetFilterQuery targetFilterQuery, final Long dsId) {
final String actionMessage = String.format(ACTION_MESSAGE, targetFilterQuery.getName());
return transactionTemplate.execute(status -> {
final List<TargetWithActionType> targets = getTargetsWithActionType(targetFilterQuery.getQuery(), dsId,
PAGE_SIZE);
final int count = targets.size();
if (count > 0) {
deploymentManagement.assignDistributionSet(dsId, targets, actionMessage);
}
return count;
});
return DeploymentHelper.runInNewTransaction(transactionManager, "autoAssignDSToTargets",
Isolation.READ_COMMITTED.value(), status -> {
final List<TargetWithActionType> targets = getTargetsWithActionType(targetFilterQuery.getQuery(),
dsId, targetFilterQuery.getAutoAssignActionType(), PAGE_SIZE);
final int count = targets.size();
if (count > 0) {
deploymentManagement.assignDistributionSet(dsId, targets, actionMessage);
}
return count;
});
}
/**
@@ -169,17 +162,22 @@ public class AutoAssignChecker {
* @param dsId
* dsId the targets are not allowed to have in their action
* history
* @param type
* action type for targets auto assignment
* @param count
* maximum amount of targets to retrieve
* @return list of targets with action type
*/
private List<TargetWithActionType> getTargetsWithActionType(final String targetFilterQuery, final Long dsId,
final int count) {
final Page<Target> targets = targetManagement
.findByTargetFilterQueryAndNonDS(PageRequest.of(0, count), dsId, targetFilterQuery);
final ActionType type, final int count) {
final Page<Target> targets = targetManagement.findByTargetFilterQueryAndNonDS(PageRequest.of(0, count), dsId,
targetFilterQuery);
// the action type is set to FORCED per default (when not explicitly
// specified)
final ActionType autoAssignActionType = type == null ? ActionType.FORCED : type;
return targets.getContent().stream().map(t -> new TargetWithActionType(t.getControllerId(),
Action.ActionType.FORCED, RepositoryModelConstants.NO_FORCE_TIME)).collect(Collectors.toList());
autoAssignActionType, RepositoryModelConstants.NO_FORCE_TIME)).collect(Collectors.toList());
}
}

View File

@@ -12,8 +12,11 @@ import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.builder.AbstractTargetFilterQueryUpdateCreate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
/**
* Create/build implementation.
@@ -32,7 +35,8 @@ public class JpaTargetFilterQueryCreate extends AbstractTargetFilterQueryUpdateC
public JpaTargetFilterQuery build() {
return new JpaTargetFilterQuery(name, query,
getSet().map(this::findDistributionSetAndThrowExceptionIfNotFound).orElse(null));
getAutoAssignDistributionSetId().map(this::findDistributionSetAndThrowExceptionIfNotFound).orElse(null),
getAutoAssignActionType().filter(JpaTargetFilterQueryCreate::isAutoAssignActionTypeValid).orElse(null));
}
private DistributionSet findDistributionSetAndThrowExceptionIfNotFound(final Long setId) {
@@ -40,4 +44,12 @@ public class JpaTargetFilterQueryCreate extends AbstractTargetFilterQueryUpdateC
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, setId));
}
private static boolean isAutoAssignActionTypeValid(final ActionType actionType) {
if (!TargetFilterQuery.ALLOWED_AUTO_ASSIGN_ACTION_TYPES.contains(actionType)) {
throw new InvalidAutoAssignActionTypeException();
}
return true;
}
}

View File

@@ -23,10 +23,15 @@ import javax.validation.constraints.Size;
import org.eclipse.hawkbit.repository.event.remote.TargetFilterQueryDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetFilterQueryCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetFilterQueryUpdatedEvent;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.NamedEntity;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.persistence.annotations.ConversionValue;
import org.eclipse.persistence.annotations.Convert;
import org.eclipse.persistence.annotations.ObjectTypeConverter;
import org.eclipse.persistence.descriptors.DescriptorEvent;
/**
@@ -41,7 +46,7 @@ import org.eclipse.persistence.descriptors.DescriptorEvent;
@SuppressWarnings("squid:S2160")
public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
implements TargetFilterQuery, EventAwareEntity {
private static final long serialVersionUID = 7493966984413479089L;
private static final long serialVersionUID = 1L;
@Column(name = "name", length = NamedEntity.NAME_MAX_SIZE, nullable = false)
@Size(max = NamedEntity.NAME_MAX_SIZE)
@@ -57,6 +62,13 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
@JoinColumn(name = "auto_assign_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_filter_auto_assign_ds"))
private JpaDistributionSet autoAssignDistributionSet;
@Column(name = "auto_assign_action_type", nullable = true)
@ObjectTypeConverter(name = "autoAssignActionType", objectType = Action.ActionType.class, dataType = Integer.class, conversionValues = {
@ConversionValue(objectValue = "FORCED", dataValue = "0"),
@ConversionValue(objectValue = "SOFT", dataValue = "1") })
@Convert("autoAssignActionType")
private ActionType autoAssignActionType;
public JpaTargetFilterQuery() {
// Default constructor for JPA.
}
@@ -70,12 +82,15 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
* of the {@link TargetFilterQuery}.
* @param autoAssignDistributionSet
* of the {@link TargetFilterQuery}.
* @param autoAssignActionType
* of the {@link TargetFilterQuery}.
*/
public JpaTargetFilterQuery(final String name, final String query,
final DistributionSet autoAssignDistributionSet) {
public JpaTargetFilterQuery(final String name, final String query, final DistributionSet autoAssignDistributionSet,
final ActionType autoAssignActionType) {
this.name = name;
this.query = query;
this.autoAssignDistributionSet = (JpaDistributionSet) autoAssignDistributionSet;
this.autoAssignActionType = autoAssignActionType;
}
@Override
@@ -105,6 +120,15 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
this.autoAssignDistributionSet = distributionSet;
}
@Override
public ActionType getAutoAssignActionType() {
return autoAssignActionType;
}
public void setAutoAssignActionType(final ActionType actionType) {
this.autoAssignActionType = actionType;
}
@Override
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(

View File

@@ -120,6 +120,22 @@ public final class DistributionSetSpecification {
cb.like(cb.lower(targetRoot.<String> get(JpaDistributionSet_.description)), subString.toLowerCase()));
}
/**
* {@link Specification} for retrieving {@link DistributionSet}s by "like
* name and like version".
*
* @param name
* to be filtered on
* @param version
* to be filtered on
* @return the {@link DistributionSet} {@link Specification}
*/
public static Specification<JpaDistributionSet> likeNameAndVersion(final String name, final String version) {
return (targetRoot, query, cb) -> cb.and(
cb.like(cb.lower(targetRoot.<String> get(JpaDistributionSet_.name)), name.toLowerCase()),
cb.like(cb.lower(targetRoot.<String> get(JpaDistributionSet_.version)), version.toLowerCase()));
}
/**
* {@link Specification} for retrieving {@link DistributionSet}s by "has at
* least one of the given tag names".

View File

@@ -22,6 +22,7 @@ import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
@@ -83,10 +84,30 @@ public final class DeploymentHelper {
*/
public static <T> T runInNewTransaction(@NotNull final PlatformTransactionManager txManager,
final String transactionName, @NotNull final TransactionCallback<T> action) {
return runInNewTransaction(txManager, transactionName, Isolation.DEFAULT.value(), action);
}
/**
* Executes the modifying action in new transaction
*
* @param txManager
* transaction manager interface
* @param transactionName
* the name of the new transaction
* @param isolationLevel
* isolation level of the new transaction
* @param action
* the callback to execute in new tranaction
*
* @return the result of the action
*/
public static <T> T runInNewTransaction(@NotNull final PlatformTransactionManager txManager,
final String transactionName, final int isolationLevel, @NotNull final TransactionCallback<T> action) {
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName(transactionName);
def.setReadOnly(false);
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
def.setIsolationLevel(isolationLevel);
return new TransactionTemplate(txManager, def).execute(action);
}
}

View File

@@ -0,0 +1 @@
ALTER TABLE sp_target_filter_query ADD COLUMN auto_assign_action_type INTEGER;

View File

@@ -0,0 +1 @@
ALTER TABLE sp_target_filter_query ADD COLUMN auto_assign_action_type integer;

View File

@@ -0,0 +1 @@
ALTER TABLE sp_target_filter_query ADD COLUMN auto_assign_action_type integer;

View File

@@ -0,0 +1 @@
ALTER TABLE sp_target_filter_query ADD auto_assign_action_type INTEGER;

View File

@@ -14,10 +14,12 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.validation.ConstraintViolationException;
@@ -627,9 +629,10 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
.create(entityFactory.tag().create().name("DistributionSetTag-C"));
distributionSetTagManagement.create(entityFactory.tag().create().name("DistributionSetTag-D"));
List<DistributionSet> ds5Group1 = testdataFactory.createDistributionSets("", 5);
List<DistributionSet> dsGroup2 = testdataFactory.createDistributionSets("test2", 5);
DistributionSet dsDeleted = testdataFactory.createDistributionSet("deleted");
List<DistributionSet> dsGroup1 = testdataFactory.createDistributionSets("", 5);
final String dsGroup2Prefix = "test";
List<DistributionSet> dsGroup2 = testdataFactory.createDistributionSets(dsGroup2Prefix, 5);
DistributionSet dsDeleted = testdataFactory.createDistributionSet("testDeleted");
final DistributionSet dsInComplete = distributionSetManagement.create(entityFactory.distributionSet().create()
.name("notcomplete").version("1").type(standardDsType.getKey()));
@@ -649,185 +652,268 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
distributionSetManagement.delete(dsDeleted.getId());
dsDeleted = distributionSetManagement.get(dsDeleted.getId()).get();
ds5Group1 = toggleTagAssignment(ds5Group1, dsTagA).getAssignedEntity();
dsGroup1 = toggleTagAssignment(dsGroup1, dsTagA).getAssignedEntity();
dsTagA = distributionSetTagRepository.findByNameEquals(dsTagA.getName()).get();
ds5Group1 = toggleTagAssignment(ds5Group1, dsTagB).getAssignedEntity();
dsGroup1 = toggleTagAssignment(dsGroup1, dsTagB).getAssignedEntity();
dsTagA = distributionSetTagRepository.findByNameEquals(dsTagA.getName()).get();
dsGroup2 = toggleTagAssignment(dsGroup2, dsTagA).getAssignedEntity();
dsTagA = distributionSetTagRepository.findByNameEquals(dsTagA.getName()).get();
final List<DistributionSet> allDistributionSets = Stream
.of(dsGroup1, dsGroup2, Arrays.asList(dsDeleted, dsInComplete, dsNewType)).flatMap(Collection::stream)
.collect(Collectors.toList());
final List<DistributionSet> dsGroup1WithGroup2 = Stream.of(dsGroup1, dsGroup2).flatMap(Collection::stream)
.collect(Collectors.toList());
final int sizeOfAllDistributionSets = allDistributionSets.size();
// check setup
assertThat(distributionSetRepository.findAll()).hasSize(13);
assertThat(distributionSetRepository.findAll()).hasSize(sizeOfAllDistributionSets);
// Find all
List<DistributionSet> expected = Lists.newArrayListWithExpectedSize(13);
expected.addAll(ds5Group1);
expected.addAll(dsGroup2);
expected.add(dsDeleted);
expected.add(dsInComplete);
expected.add(dsNewType);
validateFindAll(allDistributionSets);
validateDeleted(dsDeleted, sizeOfAllDistributionSets - 1);
validateCompleted(dsInComplete, sizeOfAllDistributionSets - 1);
validateType(newType, dsNewType, sizeOfAllDistributionSets - 1);
validateSearchText(dsGroup2, "%" + dsGroup2Prefix);
validateFilterString(allDistributionSets, dsGroup2Prefix);
validateTags(dsTagA, dsTagB, dsTagC, dsGroup1WithGroup2, dsGroup1);
validateDeletedAndCompleted(dsGroup1WithGroup2, dsNewType, dsDeleted);
validateDeletedAndCompletedAndType(dsGroup1WithGroup2, dsDeleted, newType, dsNewType);
validateDeletedAndCompletedAndTypeAndSearchText(dsGroup2, newType, "%" + dsGroup2Prefix);
validateDeletedAndCompletedAndTypeAndFilterString(dsGroup1WithGroup2, dsDeleted, dsInComplete, dsNewType,
newType, ":1");
validateDeletedAndCompletedAndTypeAndSearchTextAndTag(dsGroup2, dsTagA, "%" + dsGroup2Prefix);
}
assertThat(distributionSetManagement
.findByDistributionSetFilter(PAGE, getDistributionSetFilterBuilder().build()).getContent()).hasSize(13)
.containsOnly(expected.toArray(new DistributionSet[0]));
@Step
private void validateFindAll(final List<DistributionSet> expectedDistributionsets) {
DistributionSetFilterBuilder distributionSetFilterBuilder;
assertThatFilterContainsOnlyGivenDistributionSets(getDistributionSetFilterBuilder(), expectedDistributionsets);
}
// search for not deleted
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setIsDeleted(Boolean.TRUE);
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(1);
@Step
private void validateDeleted(final DistributionSet deletedDistributionSet, final int notDeletedSize) {
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setIsDeleted(false);
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(12);
assertThatFilterContainsOnlyGivenDistributionSets(getDistributionSetFilterBuilder().setIsDeleted(Boolean.TRUE),
Arrays.asList(deletedDistributionSet));
// search for completed
expected = new ArrayList<>();
expected.addAll(ds5Group1);
expected.addAll(dsGroup2);
expected.add(dsDeleted);
expected.add(dsNewType);
assertThatFilterHasSizeAndDoesNotContainDistributionSet(
getDistributionSetFilterBuilder().setIsDeleted(Boolean.FALSE), notDeletedSize, deletedDistributionSet);
}
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setIsComplete(true);
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(12).containsOnly(expected.toArray(new DistributionSet[0]));
@Step
private void validateCompleted(final DistributionSet dsIncomplete, final int completedSize) {
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setIsComplete(Boolean.FALSE);
expected = new ArrayList<>();
expected.add(dsInComplete);
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(1).containsOnly(expected.toArray(new DistributionSet[0]));
assertThatFilterHasSizeAndDoesNotContainDistributionSet(
getDistributionSetFilterBuilder().setIsComplete(Boolean.TRUE), completedSize, dsIncomplete);
// search for type
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setType(newType);
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(1);
assertThatFilterContainsOnlyGivenDistributionSets(
getDistributionSetFilterBuilder().setIsComplete(Boolean.FALSE), Arrays.asList(dsIncomplete));
}
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setType(standardDsType);
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(12);
@Step
private void validateType(final DistributionSetType newType, final DistributionSet dsNewType,
final int standardDsTypeSize) {
// search for text
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setSearchText("%test2");
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(5);
assertThatFilterContainsOnlyGivenDistributionSets(getDistributionSetFilterBuilder().setType(newType),
Arrays.asList(dsNewType));
// search for tags
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setTagNames(Arrays.asList(dsTagA.getName()));
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(10);
assertThatFilterHasSizeAndDoesNotContainDistributionSet(
getDistributionSetFilterBuilder().setType(standardDsType), standardDsTypeSize, dsNewType);
}
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setTagNames(Arrays.asList(dsTagB.getName()));
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(5);
@Step
private void validateSearchText(final List<DistributionSet> withText, final String text) {
distributionSetFilterBuilder = getDistributionSetFilterBuilder()
.setTagNames(Arrays.asList(dsTagA.getName(), dsTagB.getName()));
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(10);
assertThatFilterContainsOnlyGivenDistributionSets(getDistributionSetFilterBuilder().setSearchText(text),
withText);
}
distributionSetFilterBuilder = getDistributionSetFilterBuilder()
.setTagNames(Arrays.asList(dsTagC.getName(), dsTagB.getName()));
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(5);
@Step
private void validateFilterString(final List<DistributionSet> allDistributionSets, final String dsNamePrefix) {
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setTagNames(Arrays.asList(dsTagC.getName()));
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(0);
final List<DistributionSet> withTestNamePrefix = allDistributionSets.stream()
.filter(ds -> ds.getName().startsWith(dsNamePrefix)).collect(Collectors.toList());
assertThatFilterContainsOnlyGivenDistributionSets(
getDistributionSetFilterBuilder().setFilterString(dsNamePrefix), withTestNamePrefix);
// combine deleted and complete
expected = new ArrayList<>();
expected.addAll(ds5Group1);
expected.addAll(dsGroup2);
expected.add(dsNewType);
final List<DistributionSet> withTestNameExact = withTestNamePrefix.stream()
.filter(ds -> ds.getName().equals(dsNamePrefix)).collect(Collectors.toList());
assertThatFilterContainsOnlyGivenDistributionSets(
getDistributionSetFilterBuilder().setFilterString(dsNamePrefix + ":"), withTestNameExact);
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setIsComplete(Boolean.TRUE)
.setIsDeleted(Boolean.FALSE);
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(11).containsOnly(expected.toArray(new DistributionSet[0]));
final List<DistributionSet> withTestNameExactAndVersionPrefix = withTestNameExact.stream()
.filter(ds -> ds.getVersion().startsWith("1")).collect(Collectors.toList());
assertThatFilterContainsOnlyGivenDistributionSets(
getDistributionSetFilterBuilder().setFilterString(dsNamePrefix + ":1"),
withTestNameExactAndVersionPrefix);
expected = Arrays.asList(dsInComplete);
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setIsComplete(Boolean.FALSE);
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(1).containsOnly(expected.toArray(new DistributionSet[0]));
final List<DistributionSet> dsWithExactNameAndVersion = withTestNameExactAndVersionPrefix.stream()
.filter(ds -> ds.getVersion().equals("1.0.0")).collect(Collectors.toList());
assertThat(dsWithExactNameAndVersion).hasSize(1);
assertThatFilterContainsOnlyGivenDistributionSets(
getDistributionSetFilterBuilder().setFilterString(dsNamePrefix + ":1.0.0"), dsWithExactNameAndVersion);
expected = Arrays.asList(dsDeleted);
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setIsComplete(Boolean.TRUE)
.setIsDeleted(Boolean.TRUE);
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(1).containsOnly(expected.toArray(new DistributionSet[0]));
final List<DistributionSet> withVersionPrefix = allDistributionSets.stream()
.filter(ds -> ds.getVersion().startsWith("1.0.")).collect(Collectors.toList());
assertThatFilterContainsOnlyGivenDistributionSets(getDistributionSetFilterBuilder().setFilterString(":1.0."),
withVersionPrefix);
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setIsDeleted(Boolean.TRUE)
.setIsComplete(Boolean.FALSE);
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(0);
final List<DistributionSet> withVersionExact = withVersionPrefix.stream()
.filter(ds -> ds.getVersion().equals("1.0.0")).collect(Collectors.toList());
assertThatFilterContainsOnlyGivenDistributionSets(getDistributionSetFilterBuilder().setFilterString(":1.0.0"),
withVersionExact);
// combine deleted and complete and type
expected = new ArrayList<>();
expected.addAll(ds5Group1);
expected.addAll(dsGroup2);
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setIsDeleted(Boolean.FALSE)
.setIsComplete(Boolean.TRUE).setType(standardDsType);
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(10).containsOnly(expected.toArray(new DistributionSet[0]));
assertThatFilterContainsOnlyGivenDistributionSets(getDistributionSetFilterBuilder().setFilterString(":"),
allDistributionSets);
expected = Arrays.asList(dsDeleted);
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setIsComplete(Boolean.TRUE)
.setType(standardDsType).setIsDeleted(Boolean.TRUE);
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(1).containsOnly(expected.toArray(new DistributionSet[0]));
assertThatFilterContainsOnlyGivenDistributionSets(getDistributionSetFilterBuilder().setFilterString(" : "),
allDistributionSets);
}
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setIsDeleted(Boolean.TRUE)
.setIsComplete(Boolean.FALSE).setType(standardDsType);
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(0);
@Step
private void validateTags(final DistributionSetTag dsTagA, final DistributionSetTag dsTagB,
final DistributionSetTag dsTagC, final List<DistributionSet> dsWithTagA,
final List<DistributionSet> dsWithTagB) {
expected = Arrays.asList(dsNewType);
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setIsComplete(Boolean.TRUE).setType(newType);
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(1).containsOnly(expected.toArray(new DistributionSet[0]));
assertThatFilterContainsOnlyGivenDistributionSets(
getDistributionSetFilterBuilder().setTagNames(Arrays.asList(dsTagA.getName())), dsWithTagA);
// combine deleted and complete and type and text
expected = dsGroup2;
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setIsComplete(Boolean.TRUE)
.setType(standardDsType).setSearchText("%test2");
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(5).containsOnly(expected.toArray(new DistributionSet[0]));
assertThatFilterContainsOnlyGivenDistributionSets(
getDistributionSetFilterBuilder().setTagNames(Arrays.asList(dsTagB.getName())), dsWithTagB);
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setIsComplete(Boolean.TRUE)
.setIsDeleted(Boolean.TRUE).setType(standardDsType).setSearchText("%test2");
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(0);
assertThatFilterContainsOnlyGivenDistributionSets(
getDistributionSetFilterBuilder().setTagNames(Arrays.asList(dsTagA.getName(), dsTagB.getName())),
dsWithTagA);
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setType(standardDsType).setSearchText("%test2")
.setIsComplete(false).setIsDeleted(false);
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(0);
assertThatFilterContainsOnlyGivenDistributionSets(
getDistributionSetFilterBuilder().setTagNames(Arrays.asList(dsTagC.getName(), dsTagB.getName())),
dsWithTagB);
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setType(newType).setSearchText("%test2")
.setIsComplete(Boolean.TRUE).setIsDeleted(false);
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(0);
assertThatFilterDoesNotContainAnyDistributionSet(
getDistributionSetFilterBuilder().setTagNames(Arrays.asList(dsTagC.getName())));
}
// combine deleted and complete and type and text and tag
expected = dsGroup2;
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setIsComplete(true).setType(standardDsType)
.setSearchText("%test2").setTagNames(Arrays.asList(dsTagA.getName()));
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(5).containsOnly(expected.toArray(new DistributionSet[0]));
@Step
private void validateDeletedAndCompleted(final List<DistributionSet> completedStandardType,
final DistributionSet dsNewType, final DistributionSet dsDeleted) {
distributionSetFilterBuilder = getDistributionSetFilterBuilder().setType(standardDsType).setSearchText("%test2")
.setTagNames(Arrays.asList(dsTagA.getName())).setIsComplete(Boolean.FALSE).setIsDeleted(Boolean.FALSE);
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, distributionSetFilterBuilder.build())
.getContent()).hasSize(0);
final List<DistributionSet> completedNotDeleted = new ArrayList<>(completedStandardType);
completedNotDeleted.add(dsNewType);
assertThatFilterContainsOnlyGivenDistributionSets(
getDistributionSetFilterBuilder().setIsComplete(Boolean.TRUE).setIsDeleted(Boolean.FALSE),
completedNotDeleted);
assertThatFilterContainsOnlyGivenDistributionSets(
getDistributionSetFilterBuilder().setIsComplete(Boolean.TRUE).setIsDeleted(Boolean.TRUE),
Arrays.asList(dsDeleted));
assertThatFilterDoesNotContainAnyDistributionSet(
getDistributionSetFilterBuilder().setIsComplete(Boolean.FALSE).setIsDeleted(Boolean.TRUE));
}
@Step
private void validateDeletedAndCompletedAndType(final List<DistributionSet> deletedAndCompletedAndStandardType,
final DistributionSet dsDeleted, final DistributionSetType newType, final DistributionSet dsNewType) {
assertThatFilterContainsOnlyGivenDistributionSets(getDistributionSetFilterBuilder().setIsDeleted(Boolean.FALSE)
.setIsComplete(Boolean.TRUE).setType(standardDsType), deletedAndCompletedAndStandardType);
assertThatFilterContainsOnlyGivenDistributionSets(getDistributionSetFilterBuilder().setIsComplete(Boolean.TRUE)
.setType(standardDsType).setIsDeleted(Boolean.TRUE), Arrays.asList(dsDeleted));
assertThatFilterDoesNotContainAnyDistributionSet(getDistributionSetFilterBuilder().setIsDeleted(Boolean.TRUE)
.setIsComplete(Boolean.FALSE).setType(standardDsType));
assertThatFilterContainsOnlyGivenDistributionSets(
getDistributionSetFilterBuilder().setIsComplete(Boolean.TRUE).setType(newType),
Arrays.asList(dsNewType));
}
@Step
private void validateDeletedAndCompletedAndTypeAndSearchText(
final List<DistributionSet> completedAndStandardTypeAndSearchText, final DistributionSetType newType,
final String text) {
assertThatFilterContainsOnlyGivenDistributionSets(getDistributionSetFilterBuilder().setIsComplete(Boolean.TRUE)
.setType(standardDsType).setSearchText(text), completedAndStandardTypeAndSearchText);
assertThatFilterDoesNotContainAnyDistributionSet(getDistributionSetFilterBuilder().setIsComplete(Boolean.TRUE)
.setIsDeleted(Boolean.TRUE).setType(standardDsType).setSearchText(text));
assertThatFilterDoesNotContainAnyDistributionSet(getDistributionSetFilterBuilder().setType(standardDsType)
.setSearchText(text).setIsComplete(Boolean.FALSE).setIsDeleted(Boolean.FALSE));
assertThatFilterDoesNotContainAnyDistributionSet(getDistributionSetFilterBuilder().setType(newType)
.setSearchText(text).setIsComplete(Boolean.TRUE).setIsDeleted(Boolean.FALSE));
}
@Step
private void validateDeletedAndCompletedAndTypeAndFilterString(
final List<DistributionSet> completedAndNotDeletedStandardTypeAndFilterString,
final DistributionSet dsDeleted, final DistributionSet dsInComplete, final DistributionSet dsNewType,
final DistributionSetType newType, final String filterString) {
final List<DistributionSet> completedAndStandardTypeAndFilterString = new ArrayList<>(
completedAndNotDeletedStandardTypeAndFilterString);
completedAndStandardTypeAndFilterString.add(dsDeleted);
assertThatFilterContainsOnlyGivenDistributionSets(getDistributionSetFilterBuilder().setIsComplete(Boolean.TRUE)
.setType(standardDsType).setFilterString(filterString), completedAndStandardTypeAndFilterString);
assertThatFilterContainsOnlyGivenDistributionSets(
getDistributionSetFilterBuilder().setIsComplete(Boolean.TRUE).setIsDeleted(Boolean.FALSE)
.setType(standardDsType).setFilterString(filterString),
completedAndNotDeletedStandardTypeAndFilterString);
assertThatFilterContainsOnlyGivenDistributionSets(getDistributionSetFilterBuilder().setIsComplete(Boolean.TRUE)
.setIsDeleted(Boolean.TRUE).setType(standardDsType).setFilterString(filterString),
Arrays.asList(dsDeleted));
assertThatFilterContainsOnlyGivenDistributionSets(getDistributionSetFilterBuilder().setType(standardDsType)
.setFilterString(filterString).setIsComplete(Boolean.FALSE).setIsDeleted(Boolean.FALSE),
Arrays.asList(dsInComplete));
assertThatFilterContainsOnlyGivenDistributionSets(getDistributionSetFilterBuilder().setType(newType)
.setFilterString(filterString).setIsComplete(Boolean.TRUE).setIsDeleted(Boolean.FALSE),
Arrays.asList(dsNewType));
}
@Step
private void validateDeletedAndCompletedAndTypeAndSearchTextAndTag(
final List<DistributionSet> completedAndStandartTypeAndSearchTextAndTagA, final DistributionSetTag dsTagA,
final String text) {
assertThatFilterContainsOnlyGivenDistributionSets(
getDistributionSetFilterBuilder().setIsComplete(Boolean.TRUE).setType(standardDsType)
.setSearchText(text).setTagNames(Arrays.asList(dsTagA.getName())),
completedAndStandartTypeAndSearchTextAndTagA);
assertThatFilterDoesNotContainAnyDistributionSet(getDistributionSetFilterBuilder().setType(standardDsType)
.setSearchText(text).setTagNames(Arrays.asList(dsTagA.getName())).setIsComplete(Boolean.FALSE)
.setIsDeleted(Boolean.FALSE));
}
private DistributionSetFilterBuilder getDistributionSetFilterBuilder() {
return new DistributionSetFilterBuilder();
}
private void assertThatFilterContainsOnlyGivenDistributionSets(final DistributionSetFilterBuilder filterBuilder,
final List<DistributionSet> distributionSets) {
final int expectedDsSize = distributionSets.size();
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, filterBuilder.build()).getContent())
.hasSize(expectedDsSize).containsOnly(distributionSets.toArray(new DistributionSet[expectedDsSize]));
}
private void assertThatFilterDoesNotContainAnyDistributionSet(final DistributionSetFilterBuilder filterBuilder) {
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, filterBuilder.build()).getContent())
.hasSize(0);
}
private void assertThatFilterHasSizeAndDoesNotContainDistributionSet(
final DistributionSetFilterBuilder filterBuilder, final int size, final DistributionSet ds) {
assertThat(distributionSetManagement.findByDistributionSetFilter(PAGE, filterBuilder.build()).getContent())
.hasSize(size).doesNotContain(ds);
}
@Test
@Description("Simple DS load without the related data that should be loaded lazy.")
public void findDistributionSetsWithoutLazy() {

View File

@@ -17,7 +17,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Iterator;
import java.util.Arrays;
import java.util.List;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
@@ -26,8 +26,11 @@ import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedE
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetFilterQueryCreatedEvent;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignDistributionSetException;
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
@@ -39,6 +42,7 @@ import org.springframework.data.domain.PageRequest;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Step;
import io.qameta.allure.Story;
/**
@@ -108,8 +112,9 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
final DistributionSet set = testdataFactory.createDistributionSet();
// creation is supposed to work as there is no distribution set
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(() -> targetFilterQueryManagement.create(
entityFactory.targetFilterQuery().create().name("testfilter").set(set.getId()).query("name==target*")));
assertThatExceptionOfType(QuotaExceededException.class)
.isThrownBy(() -> targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create()
.name("testfilter").autoAssignDistributionSet(set.getId()).query("name==target*")));
}
@Test
@@ -180,20 +185,76 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
}
@Test
@Description("Test assigning a distribution set")
@Description("Test assigning a distribution set for auto assignment with different action types")
public void assignDistributionSet() {
final String filterName = "target_filter_02";
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name(filterName).query("name==PendingTargets001"));
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), distributionSet.getId());
verifyAutoAssignmentWithDefaultActionType(filterName, targetFilterQuery, distributionSet);
verifyAutoAssignmentWithSoftActionType(filterName, targetFilterQuery, distributionSet);
verifyAutoAssignmentWithInvalidActionType(targetFilterQuery, distributionSet);
verifyAutoAssignmentWithIncompleteDs(targetFilterQuery);
verifyAutoAssignmentWithSoftDeletedDs(targetFilterQuery);
}
@Step
private void verifyAutoAssignmentWithDefaultActionType(final String filterName,
final TargetFilterQuery targetFilterQuery, final DistributionSet distributionSet) {
targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), distributionSet.getId());
verifyAutoAssignDsAndActionType(filterName, distributionSet, ActionType.FORCED);
}
@Step
private void verifyAutoAssignmentWithSoftActionType(final String filterName,
final TargetFilterQuery targetFilterQuery, final DistributionSet distributionSet) {
targetFilterQueryManagement.updateAutoAssignDSWithActionType(targetFilterQuery.getId(), distributionSet.getId(),
ActionType.SOFT);
verifyAutoAssignDsAndActionType(filterName, distributionSet, ActionType.SOFT);
}
@Step
private void verifyAutoAssignmentWithInvalidActionType(final TargetFilterQuery targetFilterQuery,
final DistributionSet distributionSet) {
// assigning a distribution set with TIMEFORCED action is supposed to
// fail as only FORCED and SOFT action types are allowed
assertThatExceptionOfType(InvalidAutoAssignActionTypeException.class).isThrownBy(
() -> targetFilterQueryManagement.updateAutoAssignDSWithActionType(targetFilterQuery.getId(),
distributionSet.getId(), ActionType.TIMEFORCED));
}
@Step
private void verifyAutoAssignmentWithIncompleteDs(final TargetFilterQuery targetFilterQuery) {
final DistributionSet incompleteDistributionSet = distributionSetManagement
.create(entityFactory.distributionSet().create().name("incomplete").version("1")
.type(testdataFactory.findOrCreateDefaultTestDsType()));
assertThatExceptionOfType(InvalidAutoAssignDistributionSetException.class)
.isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(),
incompleteDistributionSet.getId()));
}
@Step
private void verifyAutoAssignmentWithSoftDeletedDs(final TargetFilterQuery targetFilterQuery) {
final DistributionSet softDeletedDs = testdataFactory.createDistributionSet("softDeleted");
assignDistributionSet(softDeletedDs, testdataFactory.createTarget("forSoftDeletedDs"));
distributionSetManagement.delete(softDeletedDs.getId());
assertThatExceptionOfType(InvalidAutoAssignDistributionSetException.class).isThrownBy(
() -> targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), softDeletedDs.getId()));
}
private void verifyAutoAssignDsAndActionType(final String filterName, final DistributionSet distributionSet,
final ActionType actionType) {
final TargetFilterQuery tfq = targetFilterQueryManagement.getByName(filterName).get();
assertEquals("Returns correct distribution set", distributionSet, tfq.getAutoAssignDistributionSet());
assertEquals("Return correct action type", actionType, tfq.getAutoAssignActionType());
}
@Test
@@ -225,8 +286,8 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
final DistributionSet set = testdataFactory.createDistributionSet();
// creation is supposed to work as the query does not exceed the quota
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement.create(
entityFactory.targetFilterQuery().create().name("testfilter").set(set.getId()).query("name==foo"));
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement.create(entityFactory.targetFilterQuery()
.create().name("testfilter").autoAssignDistributionSet(set.getId()).query("name==foo"));
// update with a query string that addresses too many targets
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(() -> targetFilterQueryManagement
@@ -247,6 +308,7 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
// Check if target filter query is there
TargetFilterQuery tfq = targetFilterQueryManagement.getByName(filterName).get();
assertEquals("Returns correct distribution set", distributionSet, tfq.getAutoAssignDistributionSet());
assertEquals("Return correct action type", ActionType.FORCED, tfq.getAutoAssignActionType());
distributionSetManagement.delete(distributionSet.getId());
@@ -254,7 +316,7 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
tfq = targetFilterQueryManagement.getByName(filterName).get();
assertNotNull("Returns target filter query", tfq);
assertNull("Returns distribution set as null", tfq.getAutoAssignDistributionSet());
assertNull("Returns action type as null", tfq.getAutoAssignActionType());
}
@Test
@@ -275,6 +337,7 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
// Check if target filter query is there with the distribution set
TargetFilterQuery tfq = targetFilterQueryManagement.getByName(filterName).get();
assertEquals("Returns correct distribution set", distributionSet, tfq.getAutoAssignDistributionSet());
assertEquals("Return correct action type", ActionType.FORCED, tfq.getAutoAssignActionType());
distributionSetManagement.delete(distributionSet.getId());
@@ -286,66 +349,66 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
tfq = targetFilterQueryManagement.getByName(filterName).get();
assertNotNull("Returns target filter query", tfq);
assertNull("Returns distribution set as null", tfq.getAutoAssignDistributionSet());
assertNull("Returns action type as null", tfq.getAutoAssignActionType());
}
@Test
@Description("Test finding and auto assign distribution set")
public void findFiltersWithDistributionSet() {
final String filterName = "d";
assertEquals(0L, targetFilterQueryManagement.count());
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("a").query("name==*"));
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("b").query("name==*"));
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
final DistributionSet distributionSet2 = testdataFactory.createDistributionSet("2");
final TargetFilterQuery tfq = targetFilterQueryManagement.updateAutoAssignDS(
final TargetFilterQuery tfq = targetFilterQueryManagement.updateAutoAssignDSWithActionType(
targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("c").query("name==x")).getId(),
distributionSet.getId());
distributionSet.getId(), ActionType.SOFT);
final TargetFilterQuery tfq2 = targetFilterQueryManagement.updateAutoAssignDS(
targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name(filterName).query("name==z*")).getId(),
distributionSet2.getId());
assertEquals(4L, targetFilterQueryManagement.count());
// check if find works
Page<TargetFilterQuery> tfqList = targetFilterQueryManagement.findByAutoAssignDSAndRsql(PageRequest.of(0, 500),
distributionSet.getId(), null);
assertThat(1L).as("Target filter query").isEqualTo(tfqList.getTotalElements());
assertEquals("Returns correct target filter query", tfq.getId(), tfqList.iterator().next().getId());
verifyFindByDistributionSetAndRsql(distributionSet, null, tfq);
targetFilterQueryManagement.updateAutoAssignDS(tfq2.getId(), distributionSet.getId());
// check if find works for two
tfqList = targetFilterQueryManagement.findByAutoAssignDSAndRsql(PageRequest.of(0, 500), distributionSet.getId(),
null);
assertThat(2L).as("Target filter query count").isEqualTo(tfqList.getTotalElements());
Iterator<TargetFilterQuery> iterator = tfqList.iterator();
assertEquals("Returns correct target filter query 1", tfq.getId(), iterator.next().getId());
assertEquals("Returns correct target filter query 2", tfq2.getId(), iterator.next().getId());
verifyFindByDistributionSetAndRsql(distributionSet, null, tfq, tfq2);
// check if find works with name filter
tfqList = targetFilterQueryManagement.findByAutoAssignDSAndRsql(PageRequest.of(0, 500), distributionSet.getId(),
"name==" + filterName);
assertThat(1L).as("Target filter query count").isEqualTo(tfqList.getTotalElements());
assertEquals("Returns correct target filter query", tfq2.getId(), tfqList.iterator().next().getId());
// check if find works for all with auto assign DS
tfqList = targetFilterQueryManagement.findWithAutoAssignDS(PageRequest.of(0, 500));
assertThat(2L).as("Target filter query count").isEqualTo(tfqList.getTotalElements());
iterator = tfqList.iterator();
assertEquals("Returns correct target filter query 1", tfq.getId(), iterator.next().getId());
assertEquals("Returns correct target filter query 2", tfq2.getId(), iterator.next().getId());
verifyFindByDistributionSetAndRsql(distributionSet, "name==" + filterName, tfq2);
verifyFindForAllWithAutoAssignDs(tfq, tfq2);
}
@Step
private void verifyFindByDistributionSetAndRsql(final DistributionSet distributionSet, final String rsql,
final TargetFilterQuery... expectedFilterQueries) {
final Page<TargetFilterQuery> tfqList = targetFilterQueryManagement
.findByAutoAssignDSAndRsql(PageRequest.of(0, 500), distributionSet.getId(), rsql);
verifyExpectedFilterQueriesInList(tfqList, expectedFilterQueries);
}
private void verifyExpectedFilterQueriesInList(final Page<TargetFilterQuery> tfqList,
final TargetFilterQuery... expectedFilterQueries) {
assertThat(expectedFilterQueries.length).as("Target filter query count")
.isEqualTo((int) tfqList.getTotalElements());
assertThat(tfqList.map(TargetFilterQuery::getId)).containsExactly(
Arrays.stream(expectedFilterQueries).map(TargetFilterQuery::getId).toArray(Long[]::new));
}
@Step
private void verifyFindForAllWithAutoAssignDs(final TargetFilterQuery... expectedFilterQueries) {
final Page<TargetFilterQuery> tfqList = targetFilterQueryManagement
.findWithAutoAssignDS(PageRequest.of(0, 500));
verifyExpectedFilterQueriesInList(tfqList, expectedFilterQueries);
}
}

View File

@@ -1036,10 +1036,10 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
final Target target2 = createTargetWithMetadata("target2", 8);
final Page<TargetMetadata> metadataOfTarget1 = targetManagement
.findMetaDataByControllerId(new PageRequest(0, 100), target1.getControllerId());
.findMetaDataByControllerId(PageRequest.of(0, 100), target1.getControllerId());
final Page<TargetMetadata> metadataOfTarget2 = targetManagement
.findMetaDataByControllerId(new PageRequest(0, 100), target2.getControllerId());
.findMetaDataByControllerId(PageRequest.of(0, 100), target2.getControllerId());
assertThat(metadataOfTarget1.getNumberOfElements()).isEqualTo(10);
assertThat(metadataOfTarget1.getTotalElements()).isEqualTo(10);

View File

@@ -9,11 +9,13 @@
package org.eclipse.hawkbit.repository.jpa.autoassign;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignDistributionSetException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
@@ -80,7 +82,7 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
final Action rolloutCreatedAction = actionsByKnownTarget.stream()
.filter(action -> !action.getId().equals(manuallyAssignedActionId)).findAny().get();
assertThat(rolloutCreatedAction.getStatus()).isEqualTo(Status.RUNNING);
assertThat(rolloutCreatedAction.getActionType()).isEqualTo(ActionType.FORCED);
} finally {
tenantConfigurationManagement
.addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, false);
@@ -149,9 +151,13 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
// target filter query that matches first bunch of targets, that should
// fail
targetFilterQueryManagement.updateAutoAssignDS(targetFilterQueryManagement.create(
entityFactory.targetFilterQuery().create().name("filterA").query("id==" + targetDsFIdPref + "*"))
.getId(), setF.getId());
assertThatExceptionOfType(
InvalidAutoAssignDistributionSetException.class)
.isThrownBy(
() -> targetFilterQueryManagement.updateAutoAssignDS(
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create()
.name("filterA").query("id==" + targetDsFIdPref + "*")).getId(),
setF.getId()));
// target filter query that matches failed bunch of targets
targetFilterQueryManagement.updateAutoAssignDS(targetFilterQueryManagement.create(
@@ -204,4 +210,47 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
}
@Test
@Description("Test auto assignment of a distribution set with FORCED and SOFT action types")
public void checkAutoAssignWithDifferentActionTypes() {
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
final String targetDsAIdPref = "targA";
final String targetDsBIdPref = "targB";
final List<Target> targetsA = testdataFactory.createTargets(5, targetDsAIdPref,
targetDsAIdPref.concat(" description"));
final List<Target> targetsB = testdataFactory.createTargets(10, targetDsBIdPref,
targetDsBIdPref.concat(" description"));
final int targetsCount = targetsA.size() + targetsB.size();
targetFilterQueryManagement
.updateAutoAssignDSWithActionType(
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("filterA")
.query("id==" + targetDsAIdPref + "*")).getId(),
distributionSet.getId(), ActionType.FORCED);
targetFilterQueryManagement
.updateAutoAssignDSWithActionType(
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("filterB")
.query("id==" + targetDsBIdPref + "*")).getId(),
distributionSet.getId(), ActionType.SOFT);
autoAssignChecker.check();
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsA, targetsCount);
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsB, targetsCount);
verifyThatTargetsHaveAssignmentActionType(ActionType.FORCED, targetsA);
verifyThatTargetsHaveAssignmentActionType(ActionType.SOFT, targetsB);
}
@Step
private void verifyThatTargetsHaveAssignmentActionType(final ActionType actionType, final List<Target> targets) {
final List<Action> actions = targets.stream().map(Target::getControllerId).flatMap(
controllerId -> deploymentManagement.findActionsByTarget(controllerId, PAGE).getContent().stream())
.collect(Collectors.toList());
assertThat(actions).hasSize(targets.size());
assertThat(actions).allMatch(action -> action.getActionType().equals(actionType));
}
}

View File

@@ -0,0 +1,109 @@
/**
* Copyright (c) 2019 Bosch Software Innovations 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.jpa.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.Page;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
@Feature("Component Tests - Repository")
@Story("RSQL filter target filter query")
public class RSQLTargetFilterQueryFieldsTest extends AbstractJpaIntegrationTest {
private TargetFilterQuery filter1;
private TargetFilterQuery filter2;
@Before
public void setupBeforeTest() throws InterruptedException {
final String filterName1 = "filter_a";
final String filterName2 = "filter_b";
final String filterName3 = "filter_c";
final DistributionSet ds1 = testdataFactory.createDistributionSet("AutoAssignedDs_1");
final DistributionSet ds2 = testdataFactory.createDistributionSet("AutoAssignedDs_2");
filter1 = targetFilterQueryManagement.updateAutoAssignDSWithActionType(
targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name(filterName1).query("name==*")).getId(),
ds1.getId(), ActionType.SOFT);
filter2 = targetFilterQueryManagement.updateAutoAssignDS(
targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name(filterName2).query("name==*")).getId(),
ds2.getId());
targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name(filterName3).query("name==*"));
assertEquals(3L, targetFilterQueryManagement.count());
}
@Test
@Description("Test filter target filter query by id")
public void testFilterByParameterId() {
assertRSQLQuery(TargetFilterQueryFields.ID.name() + "==*", 3);
}
@Test
@Description("Test filter target filter query by name")
public void testFilterByParameterName() {
assertRSQLQuery(TargetFilterQueryFields.NAME.name() + "==" + filter1.getName(), 1);
assertRSQLQuery(TargetFilterQueryFields.NAME.name() + "==" + filter2.getName(), 1);
assertRSQLQuery(TargetFilterQueryFields.NAME.name() + "==filter_*", 3);
assertRSQLQuery(TargetFilterQueryFields.NAME.name() + "==noExist*", 0);
assertRSQLQuery(TargetFilterQueryFields.NAME.name() + "=in=(" + filter1.getName() + ",notexist)", 1);
assertRSQLQuery(TargetFilterQueryFields.NAME.name() + "=out=(" + filter1.getName() + ",notexist)", 2);
}
@Test
@Description("Test filter target filter query by auto assigned ds name")
public void testFilterByAutoAssignedDsName() {
assertRSQLQuery(TargetFilterQueryFields.AUTOASSIGNDISTRIBUTIONSET.name() + ".name=="
+ filter1.getAutoAssignDistributionSet().getName(), 1);
assertRSQLQuery(TargetFilterQueryFields.AUTOASSIGNDISTRIBUTIONSET.name() + ".name=="
+ filter2.getAutoAssignDistributionSet().getName(), 1);
assertRSQLQuery(TargetFilterQueryFields.AUTOASSIGNDISTRIBUTIONSET.name() + ".name==AutoAssignedDs_*", 2);
assertRSQLQuery(TargetFilterQueryFields.AUTOASSIGNDISTRIBUTIONSET.name() + ".name==noExist*", 0);
assertRSQLQuery(TargetFilterQueryFields.AUTOASSIGNDISTRIBUTIONSET.name() + ".name=in=("
+ filter1.getAutoAssignDistributionSet().getName() + ",notexist)", 1);
assertRSQLQuery(TargetFilterQueryFields.AUTOASSIGNDISTRIBUTIONSET.name() + ".name=out=("
+ filter1.getAutoAssignDistributionSet().getName() + ",notexist)", 1);
}
@Test
@Description("Test filter target filter query by auto assigned ds version")
public void testFilterByAutoAssignedDsVersion() {
assertRSQLQuery(TargetFilterQueryFields.AUTOASSIGNDISTRIBUTIONSET.name() + ".version=="
+ TestdataFactory.DEFAULT_VERSION, 2);
assertRSQLQuery(TargetFilterQueryFields.AUTOASSIGNDISTRIBUTIONSET.name() + ".version==*1*", 2);
assertRSQLQuery(TargetFilterQueryFields.AUTOASSIGNDISTRIBUTIONSET.name() + ".version==noExist*", 0);
assertRSQLQuery(TargetFilterQueryFields.AUTOASSIGNDISTRIBUTIONSET.name() + ".version=in=("
+ TestdataFactory.DEFAULT_VERSION + ",notexist)", 2);
assertRSQLQuery(TargetFilterQueryFields.AUTOASSIGNDISTRIBUTIONSET.name() + ".version=out=("
+ TestdataFactory.DEFAULT_VERSION + ",notexist)", 0);
}
private void assertRSQLQuery(final String rsqlParam, final long expectedFilterQueriesSize) {
final Page<TargetFilterQuery> findTargetFilterQueryPage = targetFilterQueryManagement.findByRsql(PAGE,
rsqlParam);
assertThat(findTargetFilterQueryPage).isNotNull();
assertThat(findTargetFilterQueryPage.getTotalElements()).isEqualTo(expectedFilterQueriesSize);
}
}

View File

@@ -72,7 +72,7 @@ public class RSQLTargetMetadataFieldsTest extends AbstractJpaIntegrationTest {
private void assertRSQLQuery(final String rsqlParam, final long expectedEntities) {
final Page<TargetMetadata> findEnitity = targetManagement
.findMetaDataByControllerIdAndRsql(new PageRequest(0, 100), controllerId, rsqlParam);
.findMetaDataByControllerIdAndRsql(PageRequest.of(0, 100), controllerId, rsqlParam);
final long countAllEntities = findEnitity.getTotalElements();
assertThat(findEnitity).isNotNull();
assertThat(countAllEntities).isEqualTo(expectedEntities);

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2018 Bosch Software Innovations 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.mgmt.json.model.targetfilter;
import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Request Body of DistributionSet Id and Action Type for target filter auto
* assignment operation.
*/
public class MgmtDistributionSetAutoAssignment extends MgmtId {
@JsonProperty(required = false)
private MgmtActionType type;
public MgmtActionType getType() {
return type;
}
public void setType(final MgmtActionType type) {
this.type = type;
}
}

View File

@@ -9,6 +9,7 @@
package org.eclipse.hawkbit.mgmt.json.model.targetfilter;
import org.eclipse.hawkbit.mgmt.json.model.MgmtBaseEntity;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
@@ -36,6 +37,9 @@ public class MgmtTargetFilterQuery extends MgmtBaseEntity {
@JsonProperty
private Long autoAssignDistributionSet;
@JsonProperty
private MgmtActionType autoAssignActionType;
public Long getFilterId() {
return filterId;
}
@@ -68,4 +72,12 @@ public class MgmtTargetFilterQuery extends MgmtBaseEntity {
this.autoAssignDistributionSet = autoAssignDistributionSet;
}
public MgmtActionType getAutoAssignActionType() {
return autoAssignActionType;
}
public void setAutoAssignActionType(final MgmtActionType actionType) {
this.autoAssignActionType = actionType;
}
}

View File

@@ -8,9 +8,9 @@
*/
package org.eclipse.hawkbit.mgmt.rest.api;
import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtDistributionSetAutoAssignment;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryRequestBody;
import org.springframework.hateoas.MediaTypes;
@@ -135,15 +135,16 @@ public interface MgmtTargetFilterQueryRestApi {
*
* @param filterId
* of the target to change
* @param dsId
* of the Id of the auto assign distribution set
* @param dsIdWithActionType
* id of the distribution set and the action type for auto
* assignment
* @return http status
*/
@PostMapping(value = "/{filterId}/autoAssignDS", consumes = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtTargetFilterQuery> postAssignedDistributionSet(@PathVariable("filterId") Long filterId,
@RequestBody MgmtId dsId);
@RequestBody MgmtDistributionSetAutoAssignment dsIdWithActionType);
/**
* Handles the DELETE request for removing the distribution set for auto

View File

@@ -57,6 +57,7 @@ public final class MgmtTargetFilterQueryMapper {
final DistributionSet distributionSet = filter.getAutoAssignDistributionSet();
if (distributionSet != null) {
targetRest.setAutoAssignDistributionSet(distributionSet.getId());
targetRest.setAutoAssignActionType(MgmtRestModelMapper.convertActionType(filter.getAutoAssignActionType()));
}
targetRest.add(linkTo(methodOn(MgmtTargetFilterQueryRestApi.class).getFilter(filter.getId())).withSelfRel());

View File

@@ -10,9 +10,9 @@ package org.eclipse.hawkbit.mgmt.rest.resource;
import java.util.List;
import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtDistributionSetAutoAssignment;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryRequestBody;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
@@ -126,9 +126,11 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA
@Override
public ResponseEntity<MgmtTargetFilterQuery> postAssignedDistributionSet(
@PathVariable("filterId") final Long filterId, @RequestBody final MgmtId dsId) {
@PathVariable("filterId") final Long filterId,
@RequestBody final MgmtDistributionSetAutoAssignment dsIdWithActionType) {
final TargetFilterQuery updateFilter = filterManagement.updateAutoAssignDS(filterId, dsId.getId());
final TargetFilterQuery updateFilter = filterManagement.updateAutoAssignDSWithActionType(filterId,
dsIdWithActionType.getId(), MgmtRestModelMapper.convertActionType(dsIdWithActionType.getType()));
final MgmtTargetFilterQuery response = MgmtTargetFilterQueryMapper.toResponse(updateFilter);
MgmtTargetFilterQueryMapper.addLinks(response);

View File

@@ -20,8 +20,12 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignDistributionSetException;
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.rest.exception.MessageNotReadableException;
@@ -34,6 +38,7 @@ import org.springframework.test.web.servlet.MvcResult;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Step;
import io.qameta.allure.Story;
/**
@@ -54,6 +59,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
private static final String JSON_PATH_FIELD_SIZE = ".size";
private static final String JSON_PATH_FIELD_TOTAL = ".total";
private static final String JSON_PATH_FIELD_AUTO_ASSIGN_DS = ".autoAssignDistributionSet";
private static final String JSON_PATH_FIELD_AUTO_ASSIGN_ACTION_TYPE = ".autoAssignActionType";
private static final String JSON_PATH_FIELD_EXCEPTION_CLASS = ".exceptionClass";
private static final String JSON_PATH_FIELD_ERROR_CODE = ".errorCode";
@@ -67,6 +73,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
private static final String JSON_PATH_ID = JSON_PATH_ROOT + JSON_PATH_FIELD_ID;
private static final String JSON_PATH_QUERY = JSON_PATH_ROOT + JSON_PATH_FIELD_QUERY;
private static final String JSON_PATH_AUTO_ASSIGN_DS = JSON_PATH_ROOT + JSON_PATH_FIELD_AUTO_ASSIGN_DS;
private static final String JSON_PATH_AUTO_ASSIGN_ACTION_TYPE = JSON_PATH_ROOT
+ JSON_PATH_FIELD_AUTO_ASSIGN_ACTION_TYPE;
private static final String JSON_PATH_EXCEPTION_CLASS = JSON_PATH_ROOT + JSON_PATH_FIELD_EXCEPTION_CLASS;
private static final String JSON_PATH_ERROR_CODE = JSON_PATH_ROOT + JSON_PATH_FIELD_ERROR_CODE;
@@ -113,9 +121,9 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()).content(body)
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.id", equalTo(tfq.getId().intValue())))
.andExpect(jsonPath("$.query", equalTo(filterQuery2)))
.andExpect(jsonPath("$.name", equalTo(filterName)));
.andExpect(jsonPath(JSON_PATH_ID, equalTo(tfq.getId().intValue())))
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(filterQuery2)))
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(filterName)));
final TargetFilterQuery tfqCheck = targetFilterQueryManagement.get(tfq.getId()).get();
assertThat(tfqCheck.getQuery()).isEqualTo(filterQuery2);
@@ -136,9 +144,9 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()).content(body)
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.id", equalTo(tfq.getId().intValue())))
.andExpect(jsonPath("$.query", equalTo(filterQuery)))
.andExpect(jsonPath("$.name", equalTo(filterName2)));
.andExpect(jsonPath(JSON_PATH_ID, equalTo(tfq.getId().intValue())))
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(filterQuery)))
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(filterName2)));
final TargetFilterQuery tfqCheck = targetFilterQueryManagement.get(tfq.getId()).get();
assertThat(tfqCheck.getQuery()).isEqualTo(filterQuery);
@@ -294,7 +302,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
// create targets
final int maxTargets = quotaManagement.getMaxTargetsPerAutoAssignment();
testdataFactory.createTargets(maxTargets + 1, "target%s");
testdataFactory.createTargets(maxTargets + 1, "target");
// create the filter query and the distribution set
final DistributionSet set = testdataFactory.createDistributionSet();
@@ -315,7 +323,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
// create targets
final int maxTargets = quotaManagement.getMaxTargetsPerAutoAssignment();
testdataFactory.createTargets(maxTargets + 1, "target%s");
testdataFactory.createTargets(maxTargets + 1, "target");
// create the filter query and the distribution set
final DistributionSet set = testdataFactory.createDistributionSet();
@@ -327,8 +335,10 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
.content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(targetFilterQueryManagement.get(filterQuery.getId()).get().getAutoAssignDistributionSet())
.isEqualTo(set);
final TargetFilterQuery updatedFilterQuery = targetFilterQueryManagement.get(filterQuery.getId()).get();
assertThat(updatedFilterQuery.getAutoAssignDistributionSet()).isEqualTo(set);
assertThat(updatedFilterQuery.getAutoAssignActionType()).isEqualTo(ActionType.FORCED);
// update the query of the filter query to trigger a quota hit
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId())
@@ -340,33 +350,131 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
}
@Test
@Description("Ensures that the distribution set auto-assignment works as intended with distribution set and action type validation")
public void setAutoAssignDistributionSetToTargetFilterQuery() throws Exception {
final String knownQuery = "name==test05";
final String knownName = "filter05";
final DistributionSet set = testdataFactory.createDistributionSet();
final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery);
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
.content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
verifyAutoAssignmentWithoutActionType(tfq, set);
assertThat(targetFilterQueryManagement.get(tfq.getId()).get().getAutoAssignDistributionSet()).isEqualTo(set);
verifyAutoAssignmentWithForcedActionType(tfq, set);
verifyAutoAssignmentWithSoftActionType(tfq, set);
verifyAutoAssignmentWithTimeForcedActionType(tfq, set);
verifyAutoAssignmentWithUnknownActionType(tfq, set);
verifyAutoAssignmentWithIncompleteDs(tfq);
verifyAutoAssignmentWithSoftDeletedDs(tfq);
}
@Step
private void verifyAutoAssignmentWithoutActionType(final TargetFilterQuery tfq, final DistributionSet set)
throws Exception {
verifyAutoAssignmentByActionType(tfq, set, null);
}
@Step
private void verifyAutoAssignmentWithForcedActionType(final TargetFilterQuery tfq, final DistributionSet set)
throws Exception {
verifyAutoAssignmentByActionType(tfq, set, MgmtActionType.FORCED);
}
@Step
private void verifyAutoAssignmentWithSoftActionType(final TargetFilterQuery tfq, final DistributionSet set)
throws Exception {
verifyAutoAssignmentByActionType(tfq, set, MgmtActionType.SOFT);
}
private void verifyAutoAssignmentByActionType(final TargetFilterQuery tfq, final DistributionSet set,
final MgmtActionType actionType) throws Exception {
final String hrefPrefix = "http://localhost" + MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/"
+ tfq.getId();
final String payload = actionType != null
? "{\"id\":" + set.getId() + ", \"type\":\"" + actionType.getName() + "\"}"
: "{\"id\":" + set.getId() + "}";
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
.content(payload).contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
final TargetFilterQuery updatedFilterQuery = targetFilterQueryManagement.get(tfq.getId()).get();
final MgmtActionType expectedActionType = actionType != null ? actionType : MgmtActionType.FORCED;
assertThat(updatedFilterQuery.getAutoAssignDistributionSet()).isEqualTo(set);
assertThat(updatedFilterQuery.getAutoAssignActionType())
.isEqualTo(MgmtRestModelMapper.convertActionType(expectedActionType));
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(knownName)))
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(knownQuery)))
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(tfq.getName())))
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(tfq.getQuery())))
.andExpect(jsonPath(JSON_PATH_AUTO_ASSIGN_DS, equalTo(set.getId().intValue())))
.andExpect(jsonPath(JSON_PATH_AUTO_ASSIGN_ACTION_TYPE, equalTo(expectedActionType.getName())))
.andExpect(jsonPath("$._links.self.href", equalTo(hrefPrefix)))
.andExpect(jsonPath("$._links.autoAssignDS.href", equalTo(hrefPrefix + "/autoAssignDS")));
}
@Step
private void verifyAutoAssignmentWithTimeForcedActionType(final TargetFilterQuery tfq, final DistributionSet set)
throws Exception {
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
.content("{\"id\":" + set.getId() + ", \"type\":\"" + MgmtActionType.TIMEFORCED.getName() + "\"}")
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isBadRequest())
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS,
equalTo(InvalidAutoAssignActionTypeException.class.getName())))
.andExpect(jsonPath(JSON_PATH_ERROR_CODE,
equalTo(SpServerError.SP_AUTO_ASSIGN_ACTION_TYPE_INVALID.getKey())));
}
@Step
private void verifyAutoAssignmentWithUnknownActionType(final TargetFilterQuery tfq, final DistributionSet set)
throws Exception {
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
.content("{\"id\":" + set.getId() + ", \"type\":\"unknown\"}").contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest())
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(MessageNotReadableException.class.getName())))
.andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_REST_BODY_NOT_READABLE.getKey())));
}
@Step
private void verifyAutoAssignmentWithIncompleteDs(final TargetFilterQuery tfq) throws Exception {
final DistributionSet incompleteDistributionSet = distributionSetManagement
.create(entityFactory.distributionSet().create().name("incomplete").version("1")
.type(testdataFactory.findOrCreateDefaultTestDsType()));
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
.content("{\"id\":" + incompleteDistributionSet.getId() + "}").contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest())
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS,
equalTo(InvalidAutoAssignDistributionSetException.class.getName())))
.andExpect(jsonPath(JSON_PATH_ERROR_CODE,
equalTo(SpServerError.SP_AUTO_ASSIGN_DISTRIBUTION_SET_INVALID.getKey())));
}
@Step
private void verifyAutoAssignmentWithSoftDeletedDs(final TargetFilterQuery tfq) throws Exception {
final DistributionSet softDeletedDs = testdataFactory.createDistributionSet("softDeleted");
assignDistributionSet(softDeletedDs, testdataFactory.createTarget("forSoftDeletedDs"));
distributionSetManagement.delete(softDeletedDs.getId());
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
.content("{\"id\":" + softDeletedDs.getId() + "}").contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest())
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS,
equalTo(InvalidAutoAssignDistributionSetException.class.getName())))
.andExpect(jsonPath(JSON_PATH_ERROR_CODE,
equalTo(SpServerError.SP_AUTO_ASSIGN_DISTRIBUTION_SET_INVALID.getKey())));
}
@Test
@Description("Ensures that the deletion of auto-assignment distribution set works as intended, deleting the auto-assignment action type as well")
public void deleteAutoAssignDistributionSetOfTargetFilterQuery() throws Exception {
final String knownQuery = "name==test06";
@@ -377,7 +485,10 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery);
targetFilterQueryManagement.updateAutoAssignDS(tfq.getId(), set.getId());
assertThat(targetFilterQueryManagement.get(tfq.getId()).get().getAutoAssignDistributionSet()).isEqualTo(set);
final TargetFilterQuery updatedFilterQuery = targetFilterQueryManagement.get(tfq.getId()).get();
assertThat(updatedFilterQuery.getAutoAssignDistributionSet()).isEqualTo(set);
assertThat(updatedFilterQuery.getAutoAssignActionType()).isEqualTo(ActionType.FORCED);
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS"))
.andExpect(status().isOk()).andExpect(jsonPath(JSON_PATH_NAME, equalTo(dsName)));
@@ -385,7 +496,10 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
mvc.perform(delete(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS"))
.andExpect(status().isNoContent());
assertThat(targetFilterQueryManagement.get(tfq.getId()).get().getAutoAssignDistributionSet()).isNull();
final TargetFilterQuery filterQueryWithDeletedDs = targetFilterQueryManagement.get(tfq.getId()).get();
assertThat(filterQueryWithDeletedDs.getAutoAssignDistributionSet()).isNull();
assertThat(filterQueryWithDeletedDs.getAutoAssignActionType()).isNull();
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS"))
.andExpect(status().isNoContent());

View File

@@ -1674,9 +1674,8 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
metaData1.put(new JSONObject().put("key", knownKey1).put("value", knownValue1));
metaData1.put(new JSONObject().put("key", knownKey2).put("value", knownValue2));
mvc.perform(
post("/rest/v1/targets/{targetId}/metadata", knownControllerId).accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON).content(metaData1.toString()))
mvc.perform(post("/rest/v1/targets/{targetId}/metadata", knownControllerId).accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON).content(metaData1.toString()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("[0]key", equalTo(knownKey1))).andExpect(jsonPath("[0]value", equalTo(knownValue1)))
@@ -1697,14 +1696,13 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
metaData2.put(new JSONObject().put("key", knownKey1 + i).put("value", knownValue1 + i));
}
mvc.perform(
post("/rest/v1/targets/{targetId}/metadata", knownControllerId).accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON).content(metaData2.toString()))
mvc.perform(post("/rest/v1/targets/{targetId}/metadata", knownControllerId).accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON).content(metaData2.toString()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isForbidden());
// verify that the number of meta data entries has not changed
// (we cannot use the PAGE constant here as it tries to sort by ID)
assertThat(targetManagement.findMetaDataByControllerId(new PageRequest(0, Integer.MAX_VALUE), knownControllerId)
assertThat(targetManagement.findMetaDataByControllerId(PageRequest.of(0, Integer.MAX_VALUE), knownControllerId)
.getTotalElements()).isEqualTo(metaData1.length());
}

View File

@@ -76,7 +76,8 @@ public class ResponseExceptionHandler {
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_CONCURRENT_MODIFICATION, HttpStatus.CONFLICT);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_MAINTENANCE_SCHEDULE_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_TARGET_ATTRIBUTES_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_AUTO_ASSIGN_ACTION_TYPE_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_AUTO_ASSIGN_DISTRIBUTION_SET_INVALID, HttpStatus.BAD_REQUEST);
}
private static HttpStatus getStatusOrDefault(final SpServerError error) {

View File

@@ -232,10 +232,10 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
+ SpPermission.READ_REPOSITORY + " and " + SpPermission.READ_TARGET)
public void getAutoAssignTargetFilterQueries() throws Exception {
final DistributionSet set = testdataFactory.createUpdatedDistributionSet();
targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("filter1").query("name==a").set(set));
targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("filter2").query("name==b").set(set));
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("filter1").query("name==a")
.autoAssignDistributionSet(set));
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("filter2").query("name==b")
.autoAssignDistributionSet(set));
mockMvc.perform(get(
MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{distributionSetId}/autoAssignTargetFilters",
@@ -257,8 +257,8 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
+ SpPermission.READ_REPOSITORY + " and " + SpPermission.READ_TARGET)
public void getAutoAssignTargetFilterQueriesWithParameters() throws Exception {
final DistributionSet set = testdataFactory.createUpdatedDistributionSet();
targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("filter1").query("name==a").set(set));
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("filter1").query("name==a")
.autoAssignDistributionSet(set));
mockMvc.perform(get(
MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + set.getId() + "/autoAssignTargetFilters")

View File

@@ -23,6 +23,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import java.util.HashMap;
import java.util.Map;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
@@ -76,7 +77,11 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
fieldWithPath("content[].query").description(MgmtApiModelProperties.TARGET_FILTER_QUERY),
fieldWithPath("content[].autoAssignDistributionSet")
.description(MgmtApiModelProperties.TARGET_FILTER_QUERY_AUTO_ASSIGN_DS_ID)
.type("Number"),
.type(JsonFieldType.NUMBER.toString()),
fieldWithPath("content[].autoAssignActionType")
.description(MgmtApiModelProperties.ACTION_FORCE_TYPE)
.type(JsonFieldType.STRING.toString())
.attributes(key("value").value("['forced', 'soft']")),
fieldWithPath("content[].createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
fieldWithPath("content[].createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
fieldWithPath("content[].lastModifiedAt")
@@ -180,7 +185,8 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
public void postAutoAssignDS() throws Exception {
final TargetFilterQuery tfq = createTargetFilterQuery();
final DistributionSet distributionSet = createDistributionSet();
final String filterByDistSet = "{\"id\":\"" + distributionSet.getId() + "\"}";
final String filterByDistSet = "{\"id\":\"" + distributionSet.getId() + "\", \"type\":\""
+ MgmtActionType.SOFT.getName() + "\"}";
this.mockMvc
.perform(
@@ -190,7 +196,10 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
.andDo(this.document.document(
pathParameters(parameterWithName("targetFilterQueryId")
.description(ApiModelPropertiesGeneric.ITEM_ID)),
requestFields(requestFieldWithPath("id").description(MgmtApiModelProperties.DS_ID)),
requestFields(requestFieldWithPath("id").description(MgmtApiModelProperties.DS_ID),
optionalRequestFieldWithPath("type")
.description(MgmtApiModelProperties.ACTION_FORCE_TYPE)
.attributes(key("value").value("['forced', 'soft']"))),
getResponseFieldTargetFilterQuery(false)));
}
@@ -213,7 +222,11 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
fieldWithPath(arrayPrefix + "name").description(ApiModelPropertiesGeneric.NAME),
fieldWithPath(arrayPrefix + "query").description(MgmtApiModelProperties.TARGET_FILTER_QUERY),
fieldWithPath(arrayPrefix + "autoAssignDistributionSet")
.description(MgmtApiModelProperties.TARGET_FILTER_QUERY_AUTO_ASSIGN_DS_ID).type("Number"),
.description(MgmtApiModelProperties.TARGET_FILTER_QUERY_AUTO_ASSIGN_DS_ID)
.type(JsonFieldType.NUMBER.toString()),
fieldWithPath(arrayPrefix + "autoAssignActionType")
.description(MgmtApiModelProperties.ACTION_FORCE_TYPE).type(JsonFieldType.STRING.toString())
.attributes(key("value").value("['forced', 'soft']")),
fieldWithPath(arrayPrefix + "createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
fieldWithPath(arrayPrefix + "createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
fieldWithPath(arrayPrefix + "lastModifiedAt").description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT),

View File

@@ -62,33 +62,24 @@ import com.vaadin.ui.renderers.ClickableRenderer.RendererClickEvent;
*
*/
public abstract class AbstractMetadataPopupLayout<E extends NamedEntity, M extends MetaData> extends CustomComponent {
private static final long serialVersionUID = 1L;
private static final String DELETE_BUTTON = "DELETE_BUTTON";
private static final long serialVersionUID = -1491218218453167613L;
private static final int INPUT_DEBOUNCE_TIMEOUT = 250;
protected static final String VALUE = "value";
protected static final String KEY = "key";
protected static final int MAX_METADATA_QUERY = 500;
protected VaadinMessageSource i18n;
private final UINotification uiNotification;
protected transient EventBus.UIEventBus eventBus;
private TextField keyTextField;
private TextArea valueTextArea;
private Button addIcon;
private Grid metaDataGrid;
private Label headerCaption;
private CommonDialogWindow metadataWindow;
private E selectedEntity;
@@ -238,7 +229,8 @@ public abstract class AbstractMetadataPopupLayout<E extends NamedEntity, M exten
final TextField keyField = new TextFieldBuilder(MetaData.KEY_MAX_SIZE).caption(i18n.getMessage("textfield.key"))
.required(true, i18n).id(UIComponentIdProvider.METADATA_KEY_FIELD_ID).buildTextComponent();
keyField.addTextChangeListener(this::onKeyChange);
keyField.setTextChangeEventMode(TextChangeEventMode.EAGER);
keyField.setTextChangeEventMode(TextChangeEventMode.LAZY);
keyField.setTextChangeTimeout(INPUT_DEBOUNCE_TIMEOUT);
keyField.setWidth("100%");
return keyField;
}
@@ -249,7 +241,8 @@ public abstract class AbstractMetadataPopupLayout<E extends NamedEntity, M exten
valueTextArea.setSizeFull();
valueTextArea.setHeight(100, Unit.PERCENTAGE);
valueTextArea.addTextChangeListener(this::onValueChange);
valueTextArea.setTextChangeEventMode(TextChangeEventMode.EAGER);
valueTextArea.setTextChangeEventMode(TextChangeEventMode.LAZY);
valueTextArea.setTextChangeTimeout(INPUT_DEBOUNCE_TIMEOUT);
return valueTextArea;
}

View File

@@ -292,7 +292,7 @@ public class CommonDialogWindow extends Window {
if (field instanceof CheckBox && originalValue == null) {
originalValue = Boolean.FALSE;
}
final Object currentValue = getCurrentVaue(currentChangedComponent, newValue, field);
final Object currentValue = getCurrentValue(currentChangedComponent, newValue, field);
if (!Objects.equals(originalValue, currentValue)) {
return true;
@@ -301,7 +301,7 @@ public class CommonDialogWindow extends Window {
return false;
}
private static Object getCurrentVaue(final Component currentChangedComponent, final Object newValue,
private static Object getCurrentValue(final Component currentChangedComponent, final Object newValue,
final AbstractField<?> field) {
Object currentValue = field.getValue();
if (field instanceof Table) {
@@ -333,7 +333,7 @@ public class CommonDialogWindow extends Window {
requiredComponents.addAll(allComponents.stream().filter(this::hasNullValidator).collect(Collectors.toList()));
for (final AbstractField field : requiredComponents) {
Object value = getCurrentVaue(currentChangedComponent, newValue, field);
Object value = getCurrentValue(currentChangedComponent, newValue, field);
if (Set.class.equals(field.getType())) {
value = emptyToNull((Collection<?>) value);
@@ -343,10 +343,12 @@ public class CommonDialogWindow extends Window {
return false;
}
// We need to loop through the entire loop for validity testing.
// Otherwise the UI will only mark the
// first field with errors and then stop. If there are several
// fields with errors, this is bad.
// We need to loop through all of components for validity testing.
// Otherwise the UI will only mark the first field with errors and
// then stop. Setting the value is necessary because not all
// required input fields have empty string validator, but emptiness
// is checked during isValid() call. Setting the value could be
// redundant, check if it could be removed in the future.
field.setValue(value);
if (!field.isValid()) {
valid = false;

View File

@@ -64,7 +64,7 @@ public class TargetMetadataDetailsLayout extends AbstractMetadataDetailsLayout {
}
selectedTargetId = target.getId();
final List<TargetMetadata> targetMetadataList = targetManagement
.findMetaDataByControllerId(new PageRequest(0, MAX_METADATA_QUERY), target.getControllerId())
.findMetaDataByControllerId(PageRequest.of(0, MAX_METADATA_QUERY), target.getControllerId())
.getContent();
if (targetMetadataList != null && !targetMetadataList.isEmpty()) {
targetMetadataList.forEach(this::setMetadataProperties);

View File

@@ -8,6 +8,8 @@
*/
package org.eclipse.hawkbit.ui.components;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
/**
*
*
@@ -15,7 +17,7 @@ package org.eclipse.hawkbit.ui.components;
*/
public class ProxyTargetFilter {
private static final long serialVersionUID = 6622060929679084419L;
private static final long serialVersionUID = 1L;
private String createdDate;
@@ -27,6 +29,7 @@ public class ProxyTargetFilter {
private String lastModifiedBy;
private String query;
private ProxyDistribution autoAssignDistributionSet;
private ActionType autoAssignActionType;
public String getCreatedDate() {
return createdDate;
@@ -95,7 +98,15 @@ public class ProxyTargetFilter {
return autoAssignDistributionSet;
}
public void setAutoAssignDistributionSet(ProxyDistribution autoAssignDistributionSet) {
public void setAutoAssignDistributionSet(final ProxyDistribution autoAssignDistributionSet) {
this.autoAssignDistributionSet = autoAssignDistributionSet;
}
public ActionType getAutoAssignActionType() {
return autoAssignActionType;
}
public void setAutoAssignActionType(final ActionType autoAssignActionType) {
this.autoAssignActionType = autoAssignActionType;
}
}

View File

@@ -8,14 +8,11 @@
*/
package org.eclipse.hawkbit.ui.distributions.dstable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter.DistributionSetFilterBuilder;
@@ -26,12 +23,15 @@ import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SpringContextHelper;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.util.StringUtils;
import org.vaadin.addons.lazyquerycontainer.AbstractBeanQuery;
import org.vaadin.addons.lazyquerycontainer.QueryDefinition;
import com.vaadin.data.util.filter.SimpleStringFilter;
/**
* Manage Distributions table bean query.
*
@@ -42,6 +42,7 @@ public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
private Sort sort = new Sort(Direction.ASC, "id");
private String searchText;
private String filterString;
private transient DistributionSetManagement distributionSetManagement;
private transient Page<DistributionSet> firstPageDistributionSets;
@@ -59,6 +60,17 @@ public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
final Object[] sortPropertyIds, final boolean[] sortStates) {
super(definition, queryConfig, sortPropertyIds, sortStates);
init(definition, queryConfig, sortPropertyIds, sortStates);
}
private void init(final QueryDefinition definition, final Map<String, Object> queryConfig,
final Object[] sortPropertyIds, final boolean[] sortStates) {
populateDataFromQueryConfig(queryConfig);
setFilterString(definition);
setupSorting(sortPropertyIds, sortStates);
}
private void populateDataFromQueryConfig(final Map<String, Object> queryConfig) {
if (HawkbitCommonUtil.isNotNullOrEmpty(queryConfig)) {
searchText = (String) queryConfig.get(SPUIDefinitions.FILTER_BY_TEXT);
if (!StringUtils.isEmpty(searchText)) {
@@ -72,13 +84,24 @@ public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
dsComplete = (Boolean) queryConfig.get(SPUIDefinitions.FILTER_BY_DS_COMPLETE);
}
}
}
private void setFilterString(final QueryDefinition definition) {
// if search text is set, we do not want to apply the filter
if (StringUtils.isEmpty(searchText)) {
filterString = definition.getFilters().stream().filter(SimpleStringFilter.class::isInstance)
.map(SimpleStringFilter.class::cast).map(SimpleStringFilter::getFilterString).findAny()
.orElse(null);
}
}
private void setupSorting(final Object[] sortPropertyIds, final boolean[] sortStates) {
if (sortStates != null && sortStates.length > 0) {
// Initialize sort
sort = new Sort(sortStates[0] ? Direction.ASC : Direction.DESC, (String) sortPropertyIds[0]);
// Add sort
for (int distId = 1; distId < sortPropertyIds.length; distId++) {
sort.and(new Sort(sortStates[distId] ? Direction.ASC : Direction.DESC,
sort = sort.and(new Sort(sortStates[distId] ? Direction.ASC : Direction.DESC,
(String) sortPropertyIds[distId]));
}
}
@@ -93,18 +116,11 @@ public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
protected List<ProxyDistribution> loadBeans(final int startIndex, final int count) {
Page<DistributionSet> distBeans;
final List<ProxyDistribution> proxyDistributions = new ArrayList<>();
if (startIndex == 0 && firstPageDistributionSets != null) {
distBeans = firstPageDistributionSets;
} else if (StringUtils.isEmpty(searchText)) {
// if no search filters available
distBeans = getDistributionSetManagement()
.findByCompleted(new OffsetBasedPageRequest(startIndex, count, sort), dsComplete);
} else {
final DistributionSetFilter distributionSetFilter = new DistributionSetFilterBuilder().setIsDeleted(false)
.setIsComplete(dsComplete).setSearchText(searchText).setSelectDSWithNoTag(Boolean.FALSE)
.setType(distributionSetType).build();
distBeans = getDistributionSetManagement().findByDistributionSetFilter(
PageRequest.of(startIndex / count, count, sort), distributionSetFilter);
distBeans = findDistBeans(PageRequest.of(startIndex / count, count, sort));
}
for (final DistributionSet distributionSet : distBeans) {
@@ -121,17 +137,7 @@ public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
@Override
public int size() {
if (StringUtils.isEmpty(searchText) && distributionSetType == null) {
// if no search filters available
firstPageDistributionSets = getDistributionSetManagement()
.findByCompleted(PageRequest.of(0, SPUIDefinitions.PAGE_SIZE, sort), dsComplete);
} else {
final DistributionSetFilter distributionSetFilter = new DistributionSetFilterBuilder().setIsDeleted(false)
.setIsComplete(dsComplete).setSearchText(searchText).setSelectDSWithNoTag(Boolean.FALSE)
.setType(distributionSetType).build();
firstPageDistributionSets = getDistributionSetManagement().findByDistributionSetFilter(
PageRequest.of(0, SPUIDefinitions.PAGE_SIZE, sort), distributionSetFilter);
}
firstPageDistributionSets = findDistBeans(PageRequest.of(0, SPUIDefinitions.PAGE_SIZE, sort));
final long size = firstPageDistributionSets.getTotalElements();
if (size > Integer.MAX_VALUE) {
@@ -141,16 +147,23 @@ public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
return (int) size;
}
private Page<DistributionSet> findDistBeans(final Pageable pageable) {
if (StringUtils.isEmpty(filterString) && StringUtils.isEmpty(searchText) && distributionSetType == null) {
return getDistributionSetManagement().findByCompleted(pageable, dsComplete);
} else {
final DistributionSetFilter distributionSetFilter = new DistributionSetFilterBuilder()
.setIsDeleted(Boolean.FALSE).setIsComplete(dsComplete).setSearchText(searchText)
.setFilterString(filterString).setSelectDSWithNoTag(Boolean.FALSE).setType(distributionSetType)
.build();
return getDistributionSetManagement().findByDistributionSetFilter(pageable, distributionSetFilter);
}
}
private DistributionSetManagement getDistributionSetManagement() {
if (distributionSetManagement == null) {
distributionSetManagement = SpringContextHelper.getBean(DistributionSetManagement.class);
}
return distributionSetManagement;
}
@SuppressWarnings("unchecked")
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
firstPageDistributionSets = (Page<DistributionSet>) in.readObject();
}
}

View File

@@ -0,0 +1,336 @@
/**
* Copyright (c) 2018 Bosch Software Innovations 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.filtermanagement;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.eclipse.hawkbit.ui.distributions.dstable.ManageDistBeanQuery;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.springframework.util.StringUtils;
import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory;
import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer;
import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition;
import org.vaadin.addons.lazyquerycontainer.LazyQueryView;
import org.vaadin.addons.lazyquerycontainer.QueryDefinition;
import org.vaadin.addons.lazyquerycontainer.QueryView;
import com.vaadin.data.Container;
import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.shared.ui.combobox.FilteringMode;
import com.vaadin.ui.ComboBox;
/**
* Creates a combobox in order to select the distribution set for a target
* filter query auto assignment.
*/
public class DistributionSetSelectComboBox extends ComboBox {
private static final long serialVersionUID = 1L;
private final VaadinMessageSource i18n;
private String selectedValueCaption;
private Long previousValue;
private String lastFilterString;
DistributionSetSelectComboBox(final VaadinMessageSource i18n) {
super();
this.i18n = i18n;
init();
initDataSource();
}
private void init() {
setScrollToSelectedItem(false);
setNullSelectionAllowed(false);
setSizeFull();
setId(UIComponentIdProvider.DIST_SET_SELECT_COMBO_ID);
setCaption(i18n.getMessage(UIMessageIdProvider.HEADER_DISTRIBUTION_SET));
}
private void initDataSource() {
final Container container = createContainer();
container.addContainerProperty(SPUILabelDefinitions.VAR_NAME_VERSION, String.class, null);
setItemCaptionMode(ItemCaptionMode.PROPERTY);
setItemCaptionPropertyId(SPUILabelDefinitions.VAR_NAME_VERSION);
setFilteringMode(FilteringMode.CONTAINS);
setContainerDataSource(container);
}
private static Container createContainer() {
final Map<String, Object> queryConfig = new HashMap<>();
queryConfig.put(SPUIDefinitions.FILTER_BY_DS_COMPLETE, Boolean.TRUE);
final BeanQueryFactory<ManageDistBeanQuery> distributionQF = new BeanQueryFactory<>(ManageDistBeanQuery.class);
distributionQF.setQueryConfiguration(queryConfig);
final LazyQueryDefinition distribtuinQD = new LazyQueryDefinition(false, SPUIDefinitions.PAGE_SIZE,
SPUILabelDefinitions.VAR_ID);
final QueryView distributionSetFilterLazyQueryView = new DistributionSetFilterQueryView(
new LazyQueryView(distribtuinQD, distributionQF));
distributionSetFilterLazyQueryView.sort(
new Object[] { SPUILabelDefinitions.VAR_NAME, SPUILabelDefinitions.VAR_VERSION },
new boolean[] { true, true });
return new LazyQueryContainer(distributionSetFilterLazyQueryView);
}
/**
* The custom QueryView implementation is only needed to modify the behavior
* when removing the filter (do not refresh the container). In all other
* cases the default LazyQueryView implementation is being reused.
*/
private static class DistributionSetFilterQueryView implements QueryView {
private final QueryView defaultQueryView;
DistributionSetFilterQueryView(final QueryView defaultQueryView) {
this.defaultQueryView = defaultQueryView;
}
@Override
public void addFilter(final Filter arg0) {
defaultQueryView.addFilter(arg0);
}
@Override
public int addItem() {
return defaultQueryView.addItem();
}
@Override
public void commit() {
defaultQueryView.commit();
}
@Override
public void discard() {
defaultQueryView.discard();
}
@Override
public List<Item> getAddedItems() {
return defaultQueryView.getAddedItems();
}
@Override
public Collection<Filter> getFilters() {
return defaultQueryView.getFilters();
}
@Override
public Item getItem(final int arg0) {
return defaultQueryView.getItem(arg0);
}
@Override
public List<?> getItemIdList() {
return defaultQueryView.getItemIdList();
}
@Override
public int getMaxCacheSize() {
return defaultQueryView.getMaxCacheSize();
}
@Override
public List<Item> getModifiedItems() {
return defaultQueryView.getModifiedItems();
}
@Override
public QueryDefinition getQueryDefinition() {
return defaultQueryView.getQueryDefinition();
}
@Override
public List<Item> getRemovedItems() {
return defaultQueryView.getRemovedItems();
}
@Override
public boolean isModified() {
return defaultQueryView.isModified();
}
@Override
public void refresh() {
defaultQueryView.refresh();
}
@Override
public void removeAllItems() {
defaultQueryView.removeAllItems();
}
/**
* Default implementation of the combobox removes the filter each time
* it builds the options during repaint. However, container should not
* be refreshed here (default LazyQueryView implementation), as this
* would clear all filtered cache entries following by multiple database
* queries.
*/
@Override
public void removeFilter(final Filter filter) {
defaultQueryView.getQueryDefinition().removeFilter(filter);
// no refresh here
}
@Override
public void removeFilters() {
defaultQueryView.removeFilters();
}
@Override
public void removeItem(final int arg0) {
defaultQueryView.removeItem(arg0);
}
@Override
public void setMaxCacheSize(final int arg0) {
defaultQueryView.setMaxCacheSize(arg0);
}
@Override
public int size() {
return defaultQueryView.size();
}
@Override
public void sort(final Object[] arg0, final boolean[] arg1) {
defaultQueryView.sort(arg0, arg1);
}
}
/**
* Overriden in order to get the selected distibution set's option caption
* (name:version) from container and preventing multiple calls by saving the
* selected Id.
*
* @param selectedItemId
* the Id of the selected distribution set
*/
@Override
public void setValue(final Object selectedItemId) {
if (selectedItemId != null) {
// Can happen during validation, leading to multiple database
// queries, in order to get the caption property
if (selectedItemId.equals(previousValue)) {
return;
}
selectedValueCaption = Optional.ofNullable(getContainerProperty(selectedItemId, getItemCaptionPropertyId()))
.map(Property::getValue).map(String.class::cast).orElse("");
}
super.setValue(selectedItemId);
previousValue = (Long) selectedItemId;
}
/**
* Overriden in order to return the caption for the selected distribution
* set from cache. Otherwise, it could lead to multiple database queries,
* trying to retrieve the caption from container, when it is not present in
* filtered options.
*
* @param itemId
* the Id of the selected distribution set
* @return the option caption (name:version) of the selected distribution
* set
*/
@Override
public String getItemCaption(final Object itemId) {
if (itemId != null && itemId.equals(getValue()) && !StringUtils.isEmpty(selectedValueCaption)) {
return selectedValueCaption;
}
return super.getItemCaption(itemId);
}
/**
* Overriden not to update the filter when the filterstring (value of
* combobox input) was not changed. Otherwise, it would lead to additional
* database requests during combobox page change while scrolling instead of
* retreiving items from container cache.
*
* @param filterString
* value of combobox input
* @param filteringMode
* the filtering mode (starts_with, contains)
* @return SimpleStringFilter to transfer filterstring in container
*/
@Override
protected Filter buildFilter(final String filterString, final FilteringMode filteringMode) {
if (filterStringIsNotChanged(filterString)) {
return null;
}
final Filter filter = super.buildFilter(filterString, filteringMode);
refreshContainerIfFilterStringBecomesEmpty(filterString);
lastFilterString = filterString;
return filter;
}
private boolean filterStringIsNotChanged(final String filterString) {
return !StringUtils.isEmpty(filterString) && !StringUtils.isEmpty(lastFilterString)
&& filterString.equals(lastFilterString);
}
private void refreshContainerIfFilterStringBecomesEmpty(final String filterString) {
if (StringUtils.isEmpty(filterString) && !StringUtils.isEmpty(lastFilterString)) {
refreshContainer();
}
}
/**
* Before setting the value of the selected distribution set we need to
* initialize the container and apply the right filter in order to limit the
* number of entities and save them in container cache. Otherwise, combobox
* will try to find the corresponding id from container, leading to multiple
* database queries.
*
* @param initialFilterString
* value of initial distribution set caption (name:version)
* @return the size of filtered options
*/
public int setInitialValueFilter(final String initialFilterString) {
final Filter filter = buildFilter(initialFilterString, getFilteringMode());
if (filter != null) {
final LazyQueryContainer container = (LazyQueryContainer) getContainerDataSource();
try {
container.addContainerFilter(filter);
return container.size();
} finally {
container.removeContainerFilter(filter);
}
}
return 0;
}
/**
* Refreshes the underlying container, clearing all the caches.
*/
public void refreshContainer() {
final LazyQueryContainer container = (LazyQueryContainer) getContainerDataSource();
container.refresh();
}
}

View File

@@ -1,158 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations 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.filtermanagement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.hawkbit.repository.event.remote.DistributionSetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.ui.distributions.dstable.ManageDistBeanQuery;
import org.eclipse.hawkbit.ui.distributions.state.ManageDistUIState;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions;
import org.eclipse.hawkbit.ui.utils.TableColumn;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory;
import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer;
import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition;
import org.vaadin.spring.events.EventBus.UIEventBus;
import org.vaadin.spring.events.EventScope;
import org.vaadin.spring.events.annotation.EventBusListenerMethod;
import com.vaadin.data.Container;
import com.vaadin.ui.Table;
import com.vaadin.ui.themes.ValoTheme;
/**
* Table for selecting a distribution set.
*/
public class DistributionSetSelectTable extends Table {
private static final long serialVersionUID = -4307487829435471759L;
private final VaadinMessageSource i18n;
private final ManageDistUIState manageDistUIState;
private Container container;
DistributionSetSelectTable(final VaadinMessageSource i18n, final UIEventBus eventBus,
final ManageDistUIState manageDistUIState) {
this.i18n = i18n;
this.manageDistUIState = manageDistUIState;
setStyleName("sp-table");
setSizeFull();
setSelectable(true);
setMultiSelect(false);
setImmediate(true);
addStyleName(ValoTheme.TABLE_NO_VERTICAL_LINES);
addStyleName(ValoTheme.TABLE_SMALL);
populateTableData();
setColumnCollapsingAllowed(false);
setColumnProperties();
setId(UIComponentIdProvider.DIST_SET_SELECT_TABLE_ID);
eventBus.subscribe(this);
}
@EventBusListenerMethod(scope = EventScope.UI)
void onEvents(final List<?> events) {
final Object firstEvent = events.get(0);
if (DistributionSetCreatedEvent.class.isInstance(firstEvent)
|| DistributionSetDeletedEvent.class.isInstance(firstEvent)) {
refreshDistributions();
}
}
private void populateTableData() {
container = createContainer();
addContainerproperties();
setContainerDataSource(container);
setColumnProperties();
}
protected Container createContainer() {
final Map<String, Object> queryConfiguration = prepareQueryConfigFilters();
final BeanQueryFactory<ManageDistBeanQuery> distributionQF = new BeanQueryFactory<>(ManageDistBeanQuery.class);
distributionQF.setQueryConfiguration(queryConfiguration);
return new LazyQueryContainer(
new LazyQueryDefinition(true, SPUIDefinitions.PAGE_SIZE, SPUILabelDefinitions.VAR_ID), distributionQF);
}
private void addContainerproperties() {
/* Create HierarchicalContainer container */
container.addContainerProperty(SPUILabelDefinitions.NAME, String.class, null);
container.addContainerProperty(SPUILabelDefinitions.VAR_VERSION, String.class, null);
}
private List<TableColumn> getVisbleColumns() {
final List<TableColumn> columnList = new ArrayList<>(2);
columnList.add(new TableColumn(SPUILabelDefinitions.NAME, i18n.getMessage("header.name"), 0.6F));
columnList.add(new TableColumn(SPUILabelDefinitions.VAR_VERSION, i18n.getMessage("header.version"), 0.4F));
return columnList;
}
private void setColumnProperties() {
setVisibleColumns(getVisbleColumns().stream().map(column -> {
setColumnHeader(column.getColumnPropertyId(), column.getColumnHeader());
setColumnExpandRatio(column.getColumnPropertyId(), column.getExpandRatio());
return column.getColumnPropertyId();
}).toArray());
}
private Map<String, Object> prepareQueryConfigFilters() {
final Map<String, Object> queryConfig = new HashMap<>();
manageDistUIState.getManageDistFilters().getSearchText()
.ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value));
if (null != manageDistUIState.getManageDistFilters().getClickedDistSetType()) {
queryConfig.put(SPUIDefinitions.FILTER_BY_DISTRIBUTION_SET_TYPE,
manageDistUIState.getManageDistFilters().getClickedDistSetType());
}
queryConfig.put(SPUIDefinitions.FILTER_BY_DS_COMPLETE, Boolean.TRUE);
return queryConfig;
}
private void refreshDistributions() {
final LazyQueryContainer dsContainer = (LazyQueryContainer) getContainerDataSource();
final int size = dsContainer.size();
if (size < SPUIDefinitions.MAX_TABLE_ENTRIES) {
refreshTablecontainer();
}
if (size != 0) {
setData(i18n.getMessage(UIMessageIdProvider.MESSAGE_DATA_AVAILABLE));
}
}
private Object getItemIdToSelect() {
return manageDistUIState.getSelectedDistributions().isEmpty() ? null
: manageDistUIState.getSelectedDistributions();
}
private void selectRow() {
setValue(getItemIdToSelect());
}
private void refreshTablecontainer() {
final LazyQueryContainer dsContainer = (LazyQueryContainer) getContainerDataSource();
dsContainer.refresh();
selectRow();
}
}

View File

@@ -8,27 +8,30 @@
*/
package org.eclipse.hawkbit.ui.filtermanagement;
import java.io.Serializable;
import java.util.function.Consumer;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.ui.common.CommonDialogWindow;
import org.eclipse.hawkbit.ui.common.builder.WindowBuilder;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorderWithIcon;
import org.eclipse.hawkbit.ui.distributions.state.ManageDistUIState;
import org.eclipse.hawkbit.ui.filtermanagement.event.CustomFilterUIEvent;
import org.eclipse.hawkbit.ui.management.miscs.AbstractActionTypeOptionGroupLayout.ActionTypeOption;
import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupAutoAssignmentLayout;
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider;
import org.eclipse.hawkbit.ui.utils.UINotification;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.vaadin.spring.events.EventBus;
import org.vaadin.spring.events.EventBus.UIEventBus;
import com.vaadin.data.Property;
import com.vaadin.server.FontAwesome;
import com.vaadin.server.Sizeable;
import com.vaadin.ui.Alignment;
@@ -44,59 +47,50 @@ import com.vaadin.ui.Window;
* Creates a dialog window to select the distribution set for a target filter
* query.
*/
public class DistributionSetSelectWindow
implements CommonDialogWindow.SaveDialogCloseListener, Property.ValueChangeListener {
private static final long serialVersionUID = 4752345414134989396L;
public class DistributionSetSelectWindow implements CommonDialogWindow.SaveDialogCloseListener {
private final VaadinMessageSource i18n;
private final DistributionSetSelectTable dsTable;
private final transient EventBus.UIEventBus eventBus;
private final transient TargetManagement targetManagement;
private final transient TargetFilterQueryManagement targetFilterQueryManagement;
private final UINotification notification;
private final EventBus.UIEventBus eventBus;
private final TargetManagement targetManagement;
private final TargetFilterQueryManagement targetFilterQueryManagement;
private CheckBox checkBox;
private ActionTypeOptionGroupAutoAssignmentLayout actionTypeOptionGroupLayout;
private DistributionSetSelectComboBox dsCombo;
private Long tfqId;
DistributionSetSelectWindow(final VaadinMessageSource i18n, final UIEventBus eventBus,
final TargetManagement targetManagement, final TargetFilterQueryManagement targetFilterQueryManagement,
final ManageDistUIState manageDistUIState) {
final UINotification notification, final TargetManagement targetManagement,
final TargetFilterQueryManagement targetFilterQueryManagement) {
this.i18n = i18n;
this.dsTable = new DistributionSetSelectTable(i18n, eventBus, manageDistUIState);
this.notification = notification;
this.eventBus = eventBus;
this.targetManagement = targetManagement;
this.targetFilterQueryManagement = targetFilterQueryManagement;
}
private VerticalLayout initView() {
final Label label = new Label(i18n.getMessage("label.auto.assign.description"));
final Label label = new Label(i18n.getMessage(UIMessageIdProvider.LABEL_AUTO_ASSIGNMENT_DESC));
checkBox = new CheckBox(i18n.getMessage("label.auto.assign.enable"));
checkBox = new CheckBox(i18n.getMessage(UIMessageIdProvider.LABEL_AUTO_ASSIGNMENT_ENABLE));
checkBox.setId(UIComponentIdProvider.DIST_SET_SELECT_ENABLE_ID);
checkBox.setImmediate(true);
checkBox.addValueChangeListener(this);
checkBox.addValueChangeListener(
event -> switchAutoAssignmentInputsVisibility((boolean) event.getProperty().getValue()));
setTableEnabled(false);
actionTypeOptionGroupLayout = new ActionTypeOptionGroupAutoAssignmentLayout(i18n);
dsCombo = new DistributionSetSelectComboBox(i18n);
final VerticalLayout verticalLayout = new VerticalLayout();
verticalLayout.addComponent(label);
verticalLayout.addComponent(checkBox);
verticalLayout.addComponent(dsTable);
verticalLayout.addComponent(actionTypeOptionGroupLayout);
verticalLayout.addComponent(dsCombo);
return verticalLayout;
}
private void setValue(final Long distSet) {
checkBox.setValue(distSet != null);
dsTable.setValue(distSet);
dsTable.setCurrentPageFirstItemId(distSet);
dsTable.setNullSelectionAllowed(false);
}
/**
* Shows a distribution set select window for the given target filter query
*
@@ -111,15 +105,13 @@ public class DistributionSetSelectWindow
final VerticalLayout verticalLayout = initView();
final DistributionSet distributionSet = tfq.getAutoAssignDistributionSet();
if (distributionSet != null) {
setValue(distributionSet.getId());
} else {
setValue(null);
}
final ActionType actionType = tfq.getAutoAssignActionType();
setInitialControlValues(distributionSet, actionType);
// build window after values are set to view elements
final CommonDialogWindow window = new WindowBuilder(SPUIDefinitions.CREATE_UPDATE_WINDOW)
.caption(i18n.getMessage("caption.select.auto.assign.dist")).content(verticalLayout)
.caption(i18n.getMessage(UIMessageIdProvider.CAPTION_SELECT_AUTO_ASSIGN_DS)).content(verticalLayout)
.layout(verticalLayout).i18n(i18n).saveDialogCloseListener(this).buildCommonDialogWindow();
window.setId(UIComponentIdProvider.DIST_SET_SELECT_WINDOW_ID);
@@ -128,25 +120,36 @@ public class DistributionSetSelectWindow
window.setVisible(true);
}
/**
* Is triggered when the checkbox value changes
*
* @param event
* change event
*/
@Override
public void valueChange(final Property.ValueChangeEvent event) {
if (checkBox.getValue()) {
setTableEnabled(true);
} else {
dsTable.select(null);
setTableEnabled(false);
private void setInitialControlValues(final DistributionSet distributionSet, final ActionType actionType) {
checkBox.setValue(distributionSet != null);
switchAutoAssignmentInputsVisibility(distributionSet != null);
final ActionTypeOption actionTypeToSet = ActionTypeOption.getOptionForActionType(actionType)
.orElse(ActionTypeOption.FORCED);
actionTypeOptionGroupLayout.getActionTypeOptionGroup().select(actionTypeToSet);
if (distributionSet != null) {
final String initialFilterString = HawkbitCommonUtil.getFormattedNameVersion(distributionSet.getName(),
distributionSet.getVersion());
final int filteredSize = dsCombo.setInitialValueFilter(initialFilterString);
if (filteredSize <= 0) {
notification.displayValidationError(
i18n.getMessage(UIMessageIdProvider.MESSAGE_SELECTED_DS_NOT_FOUND, initialFilterString));
dsCombo.refreshContainer();
dsCombo.setValue(null);
return;
}
}
dsCombo.setValue(distributionSet != null ? distributionSet.getId() : null);
}
private void setTableEnabled(final boolean enabled) {
dsTable.setEnabled(enabled);
dsTable.setRequired(enabled);
private void switchAutoAssignmentInputsVisibility(final boolean autoAssignmentEnabled) {
actionTypeOptionGroupLayout.setVisible(autoAssignmentEnabled);
dsCombo.setVisible(autoAssignmentEnabled);
dsCombo.setEnabled(autoAssignmentEnabled);
dsCombo.setRequired(autoAssignmentEnabled);
}
/**
@@ -164,7 +167,7 @@ public class DistributionSetSelectWindow
}
private boolean isAutoAssignmentEnabledAndDistributionSetSelected() {
return checkBox.getValue() && dsTable.getValue() != null;
return checkBox.getValue() && dsCombo.getValue() != null;
}
private boolean isAutoAssignmentDisabled() {
@@ -177,38 +180,34 @@ public class DistributionSetSelectWindow
*/
@Override
public void saveOrUpdate() {
if (checkBox.getValue() && dsTable.getValue() != null) {
updateTargetFilterQueryDS(tfqId, (Long) dsTable.getValue());
if (checkBox.getValue() && dsCombo.getValue() != null) {
final ActionType autoAssignActionType = ((ActionTypeOption) actionTypeOptionGroupLayout
.getActionTypeOptionGroup().getValue()).getActionType();
updateTargetFilterQueryDS(tfqId, (Long) dsCombo.getValue(), autoAssignActionType);
} else if (!checkBox.getValue()) {
updateTargetFilterQueryDS(tfqId, null);
updateTargetFilterQueryDS(tfqId, null, null);
}
}
private void updateTargetFilterQueryDS(final Long targetFilterQueryId, final Long dsId) {
private void updateTargetFilterQueryDS(final Long targetFilterQueryId, final Long dsId,
final ActionType actionType) {
final TargetFilterQuery tfq = targetFilterQueryManagement.get(targetFilterQueryId)
.orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId));
if (dsId != null) {
confirmWithConsequencesDialog(tfq, dsId);
confirmWithConsequencesDialog(tfq, dsId, actionType);
} else {
targetFilterQueryManagement.updateAutoAssignDS(targetFilterQueryId, null);
eventBus.publish(this, CustomFilterUIEvent.UPDATED_TARGET_FILTER_QUERY);
}
}
private void confirmWithConsequencesDialog(final TargetFilterQuery tfq, final Long dsId) {
final ConfirmConsequencesDialog dialog = new ConfirmConsequencesDialog(tfq, dsId, new ConfirmCallback() {
@Override
public void onConfirmResult(final boolean accepted) {
if (accepted) {
targetFilterQueryManagement.updateAutoAssignDS(tfq.getId(), dsId);
eventBus.publish(this, CustomFilterUIEvent.UPDATED_TARGET_FILTER_QUERY);
}
private void confirmWithConsequencesDialog(final TargetFilterQuery tfq, final Long dsId,
final ActionType actionType) {
final ConfirmConsequencesDialog dialog = new ConfirmConsequencesDialog(tfq, dsId, accepted -> {
if (accepted) {
targetFilterQueryManagement.updateAutoAssignDSWithActionType(tfq.getId(), dsId, actionType);
eventBus.publish(this, CustomFilterUIEvent.UPDATED_TARGET_FILTER_QUERY);
}
});
@@ -216,7 +215,6 @@ public class DistributionSetSelectWindow
UI.getCurrent().addWindow(dialog);
dialog.setVisible(true);
}
/**
@@ -224,26 +222,21 @@ public class DistributionSetSelectWindow
* the
*/
private class ConfirmConsequencesDialog extends Window implements Button.ClickListener {
private static final long serialVersionUID = 7738545414137389326L;
private static final long serialVersionUID = 1L;
private final TargetFilterQuery targetFilterQuery;
private final Long distributionSetId;
private Button okButton;
private final ConfirmCallback callback;
private final transient Consumer<Boolean> callback;
public ConfirmConsequencesDialog(final TargetFilterQuery targetFilterQuery, final Long dsId,
final ConfirmCallback callback) {
super(i18n.getMessage("caption.confirm.assign.consequences"));
final Consumer<Boolean> callback) {
super(i18n.getMessage(UIMessageIdProvider.CAPTION_CONFIRM_AUTO_ASSIGN_CONSEQUENCES));
this.callback = callback;
this.targetFilterQuery = targetFilterQuery;
this.distributionSetId = dsId;
init();
}
private void init() {
@@ -260,9 +253,11 @@ public class DistributionSetSelectWindow
targetFilterQuery.getQuery());
Label mainTextLabel;
if (targetsCount == 0) {
mainTextLabel = new Label(i18n.getMessage("message.confirm.assign.consequences.none"));
mainTextLabel = new Label(
i18n.getMessage(UIMessageIdProvider.MESSAGE_CONFIRM_AUTO_ASSIGN_CONSEQUENCES_NONE));
} else {
mainTextLabel = new Label(i18n.getMessage("message.confirm.assign.consequences.text", targetsCount));
mainTextLabel = new Label(i18n
.getMessage(UIMessageIdProvider.MESSAGE_CONFIRM_AUTO_ASSIGN_CONSEQUENCES_TEXT, targetsCount));
}
layout.addComponent(mainTextLabel);
@@ -273,7 +268,7 @@ public class DistributionSetSelectWindow
buttonsLayout.addStyleName("actionButtonsMargin");
layout.addComponent(buttonsLayout);
okButton = SPUIComponentProvider.getButton(UIComponentIdProvider.SAVE_BUTTON,
final Button okButton = SPUIComponentProvider.getButton(UIComponentIdProvider.SAVE_BUTTON,
i18n.getMessage(UIMessageIdProvider.BUTTON_OK), "", "", true, FontAwesome.SAVE,
SPUIButtonStyleNoBorderWithIcon.class);
okButton.setSizeUndefined();
@@ -292,24 +287,17 @@ public class DistributionSetSelectWindow
buttonsLayout.addComponent(cancelButton);
buttonsLayout.setComponentAlignment(cancelButton, Alignment.MIDDLE_LEFT);
buttonsLayout.setExpandRatio(cancelButton, 1.0F);
}
@Override
public void buttonClick(final Button.ClickEvent event) {
if (event.getButton().getId().equals(okButton.getId())) {
callback.onConfirmResult(true);
if (event.getButton().getId().equals(UIComponentIdProvider.SAVE_BUTTON)) {
callback.accept(true);
} else {
callback.onConfirmResult(false);
callback.accept(false);
}
close();
}
}
@FunctionalInterface
private interface ConfirmCallback extends Serializable {
void onConfirmResult(boolean accepted);
}
}

View File

@@ -17,7 +17,6 @@ import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.ui.AbstractHawkbitUI;
import org.eclipse.hawkbit.ui.SpPermissionChecker;
import org.eclipse.hawkbit.ui.UiProperties;
import org.eclipse.hawkbit.ui.distributions.state.ManageDistUIState;
import org.eclipse.hawkbit.ui.filtermanagement.event.CustomFilterUIEvent;
import org.eclipse.hawkbit.ui.filtermanagement.footer.TargetFilterCountMessageLabel;
import org.eclipse.hawkbit.ui.filtermanagement.state.FilterManagementUIState;
@@ -68,11 +67,10 @@ public class FilterManagementView extends VerticalLayout implements View {
final FilterManagementUIState filterManagementUIState,
final TargetFilterQueryManagement targetFilterQueryManagement, final SpPermissionChecker permissionChecker,
final UINotification notification, final UiProperties uiProperties, final EntityFactory entityFactory,
final AutoCompleteTextFieldComponent queryTextField, final ManageDistUIState manageDistUIState,
final TargetManagement targetManagement) {
final AutoCompleteTextFieldComponent queryTextField, final TargetManagement targetManagement) {
this.targetFilterHeader = new TargetFilterHeader(eventBus, filterManagementUIState, permissionChecker, i18n);
this.targetFilterTable = new TargetFilterTable(i18n, notification, eventBus, filterManagementUIState,
targetFilterQueryManagement, manageDistUIState, targetManagement, permissionChecker);
targetFilterQueryManagement, targetManagement, permissionChecker);
this.createNewFilterHeader = new CreateOrUpdateFilterHeader(i18n, eventBus, filterManagementUIState,
targetFilterQueryManagement, permissionChecker, notification, uiProperties, entityFactory,
queryTextField);
@@ -113,10 +111,10 @@ public class FilterManagementView extends VerticalLayout implements View {
if (custFilterUIEvent == CustomFilterUIEvent.TARGET_FILTER_DETAIL_VIEW) {
viewTargetFilterDetailLayout();
} else if (custFilterUIEvent == CustomFilterUIEvent.CREATE_NEW_FILTER_CLICK) {
this.getUI().access(() -> viewCreateTargetFilterLayout());
this.getUI().access(this::viewCreateTargetFilterLayout);
} else if (custFilterUIEvent == CustomFilterUIEvent.EXIT_CREATE_OR_UPDATE_FILTRER_VIEW
|| custFilterUIEvent == CustomFilterUIEvent.SHOW_FILTER_MANAGEMENT) {
UI.getCurrent().access(() -> viewListView());
UI.getCurrent().access(this::viewListView);
}
}

View File

@@ -104,6 +104,7 @@ public class TargetFilterBeanQuery extends AbstractBeanQuery<ProxyTargetFilter>
final DistributionSet distributionSet = tarFilterQuery.getAutoAssignDistributionSet();
if (distributionSet != null) {
proxyTarFilter.setAutoAssignDistributionSet(new ProxyDistribution(distributionSet));
proxyTarFilter.setAutoAssignActionType(tarFilterQuery.getAutoAssignActionType());
}
proxyTargetFilter.add(proxyTarFilter);
}

View File

@@ -16,12 +16,13 @@ import java.util.Map;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.ui.SpPermissionChecker;
import org.eclipse.hawkbit.ui.common.ConfirmationDialog;
import org.eclipse.hawkbit.ui.components.ProxyDistribution;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorder;
import org.eclipse.hawkbit.ui.distributions.state.ManageDistUIState;
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorderWithIcon;
import org.eclipse.hawkbit.ui.filtermanagement.event.CustomFilterUIEvent;
import org.eclipse.hawkbit.ui.filtermanagement.state.FilterManagementUIState;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
@@ -68,7 +69,7 @@ public class TargetFilterTable extends Table {
private final transient TargetFilterQueryManagement targetFilterQueryManagement;
private final DistributionSetSelectWindow dsSelectWindow;
private final transient DistributionSetSelectWindow dsSelectWindow;
private final SpPermissionChecker permChecker;
@@ -78,8 +79,8 @@ public class TargetFilterTable extends Table {
public TargetFilterTable(final VaadinMessageSource i18n, final UINotification notification,
final UIEventBus eventBus, final FilterManagementUIState filterManagementUIState,
final TargetFilterQueryManagement targetFilterQueryManagement, final ManageDistUIState manageDistUIState,
final TargetManagement targetManagement, final SpPermissionChecker permChecker) {
final TargetFilterQueryManagement targetFilterQueryManagement, final TargetManagement targetManagement,
final SpPermissionChecker permChecker) {
this.i18n = i18n;
this.notification = notification;
this.eventBus = eventBus;
@@ -87,8 +88,8 @@ public class TargetFilterTable extends Table {
this.targetFilterQueryManagement = targetFilterQueryManagement;
this.permChecker = permChecker;
this.dsSelectWindow = new DistributionSetSelectWindow(i18n, eventBus, targetManagement,
targetFilterQueryManagement, manageDistUIState);
this.dsSelectWindow = new DistributionSetSelectWindow(i18n, eventBus, notification, targetManagement,
targetFilterQueryManagement);
setStyleName("sp-table");
setSizeFull();
@@ -110,7 +111,7 @@ public class TargetFilterTable extends Table {
|| filterEvent == CustomFilterUIEvent.FILTER_BY_CUST_FILTER_TEXT_REMOVE
|| filterEvent == CustomFilterUIEvent.CREATE_TARGET_FILTER_QUERY
|| filterEvent == CustomFilterUIEvent.UPDATED_TARGET_FILTER_QUERY) {
UI.getCurrent().access(() -> refreshContainer());
UI.getCurrent().access(this::refreshContainer);
}
}
@@ -237,14 +238,25 @@ public class TargetFilterTable extends Table {
final Item row1 = getItem(itemId);
final ProxyDistribution distSet = (ProxyDistribution) row1
.getItemProperty(SPUILabelDefinitions.AUTO_ASSIGN_DISTRIBUTION_SET).getValue();
final ActionType actionType = (ActionType) row1.getItemProperty(SPUILabelDefinitions.AUTO_ASSIGN_ACTION_TYPE)
.getValue();
final String buttonId = "distSetButton";
Button updateIcon;
if (distSet == null) {
updateIcon = SPUIComponentProvider.getButton(buttonId, i18n.getMessage("button.no.auto.assignment"),
i18n.getMessage("button.auto.assignment.desc"), null, false, null, SPUIButtonStyleNoBorder.class);
updateIcon = SPUIComponentProvider.getButton(buttonId,
i18n.getMessage(UIMessageIdProvider.BUTTON_NO_AUTO_ASSIGNMENT),
i18n.getMessage(UIMessageIdProvider.BUTTON_AUTO_ASSIGNMENT_DESCRIPTION), null, false, null,
SPUIButtonStyleNoBorder.class);
} else {
updateIcon = SPUIComponentProvider.getButton(buttonId, distSet.getNameVersion(),
i18n.getMessage("button.auto.assignment.desc"), null, false, null, SPUIButtonStyleNoBorder.class);
updateIcon = actionType.equals(ActionType.FORCED)
? SPUIComponentProvider.getButton(buttonId, distSet.getNameVersion(),
i18n.getMessage(UIMessageIdProvider.BUTTON_AUTO_ASSIGNMENT_DESCRIPTION), null, false,
FontAwesome.BOLT, SPUIButtonStyleNoBorderWithIcon.class)
: SPUIComponentProvider.getButton(buttonId, distSet.getNameVersion(),
i18n.getMessage(UIMessageIdProvider.BUTTON_AUTO_ASSIGNMENT_DESCRIPTION), null, false, null,
SPUIButtonStyleNoBorder.class);
updateIcon.setSizeUndefined();
}
updateIcon.addClickListener(this::onClickOfDistributionSetButton);

View File

@@ -50,8 +50,8 @@ import org.eclipse.hawkbit.ui.management.event.ManagementUIEvent;
import org.eclipse.hawkbit.ui.management.event.PinUnpinEvent;
import org.eclipse.hawkbit.ui.management.event.RefreshDistributionTableByFilterEvent;
import org.eclipse.hawkbit.ui.management.event.SaveActionWindowEvent;
import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupLayout;
import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupLayout.ActionTypeOption;
import org.eclipse.hawkbit.ui.management.miscs.AbstractActionTypeOptionGroupLayout.ActionTypeOption;
import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupAssignmentLayout;
import org.eclipse.hawkbit.ui.management.miscs.MaintenanceWindowLayout;
import org.eclipse.hawkbit.ui.management.state.ManagementUIState;
import org.eclipse.hawkbit.ui.management.targettable.TargetTable;
@@ -125,7 +125,7 @@ public class DistributionTable extends AbstractNamedVersionTable<DistributionSet
private ConfirmationDialog confirmDialog;
private final ActionTypeOptionGroupLayout actionTypeOptionGroupLayout;
private final ActionTypeOptionGroupAssignmentLayout actionTypeOptionGroupLayout;
private final MaintenanceWindowLayout maintenanceWindowLayout;
@@ -145,7 +145,7 @@ public class DistributionTable extends AbstractNamedVersionTable<DistributionSet
this.targetTagManagement = targetTagManagement;
this.distributionSetManagement = distributionSetManagement;
this.deploymentManagement = deploymentManagement;
this.actionTypeOptionGroupLayout = new ActionTypeOptionGroupLayout(i18n);
this.actionTypeOptionGroupLayout = new ActionTypeOptionGroupAssignmentLayout(i18n);
this.maintenanceWindowLayout = new MaintenanceWindowLayout(i18n);
this.uiProperties = uiProperties;
notAllowedMsg = i18n.getMessage(UIMessageIdProvider.MESSAGE_ACTION_NOT_ALLOWED);
@@ -509,10 +509,10 @@ public class DistributionTable extends AbstractNamedVersionTable<DistributionSet
Long distId;
List<TargetIdName> targetIdSetList;
List<TargetIdName> tempIdList;
final ActionType actionType = ((ActionTypeOptionGroupLayout.ActionTypeOption) actionTypeOptionGroupLayout
.getActionTypeOptionGroup().getValue()).getActionType();
final long forcedTimeStamp = (((ActionTypeOptionGroupLayout.ActionTypeOption) actionTypeOptionGroupLayout
.getActionTypeOptionGroup().getValue()) == ActionTypeOption.AUTO_FORCED)
final ActionType actionType = ((ActionTypeOption) actionTypeOptionGroupLayout.getActionTypeOptionGroup()
.getValue()).getActionType();
final long forcedTimeStamp = (((ActionTypeOption) actionTypeOptionGroupLayout.getActionTypeOptionGroup()
.getValue()) == ActionTypeOption.AUTO_FORCED)
? actionTypeOptionGroupLayout.getForcedTimeDateField().getValue().getTime()
: RepositoryModelConstants.NO_FORCE_TIME;

View File

@@ -0,0 +1,124 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.miscs;
import java.util.Arrays;
import java.util.Optional;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.vaadin.hene.flexibleoptiongroup.FlexibleOptionGroup;
import org.vaadin.hene.flexibleoptiongroup.FlexibleOptionGroupItemComponent;
import com.vaadin.server.FontAwesome;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
/**
* Action type option group abstract layout.
*/
public abstract class AbstractActionTypeOptionGroupLayout extends HorizontalLayout {
private static final long serialVersionUID = 1L;
protected static final String STYLE_DIST_WINDOW_ACTIONTYPE = "dist-window-actiontype";
private static final String STYLE_DIST_WINDOW_ACTIONTYPE_LAYOUT = "dist-window-actiontype-horz-layout";
protected final VaadinMessageSource i18n;
protected FlexibleOptionGroup actionTypeOptionGroup;
/**
* Constructor
*
* @param i18n
* VaadinMessageSource
*/
protected AbstractActionTypeOptionGroupLayout(final VaadinMessageSource i18n) {
this.i18n = i18n;
init();
}
private void init() {
createOptionGroup();
setStyleName(STYLE_DIST_WINDOW_ACTIONTYPE_LAYOUT);
setSizeUndefined();
}
protected abstract void createOptionGroup();
protected void addForcedItemWithLabel() {
final FlexibleOptionGroupItemComponent forceItem = actionTypeOptionGroup
.getItemComponent(ActionTypeOption.FORCED);
forceItem.setStyleName(STYLE_DIST_WINDOW_ACTIONTYPE);
forceItem.setId(UIComponentIdProvider.SAVE_ACTION_RADIO_FORCED);
addComponent(forceItem);
final Label forceLabel = new Label();
forceLabel.setStyleName("statusIconPending");
forceLabel.setIcon(FontAwesome.BOLT);
forceLabel.setCaption(i18n.getMessage(UIMessageIdProvider.CAPTION_ACTION_FORCED));
forceLabel.setDescription(i18n.getMessage(UIMessageIdProvider.TOOLTIP_FORCED_ITEM));
forceLabel.setStyleName("padding-right-style");
addComponent(forceLabel);
}
protected void addSoftItemWithLabel() {
final FlexibleOptionGroupItemComponent softItem = actionTypeOptionGroup.getItemComponent(ActionTypeOption.SOFT);
softItem.setId(UIComponentIdProvider.ACTION_DETAILS_SOFT_ID);
softItem.setStyleName(STYLE_DIST_WINDOW_ACTIONTYPE);
addComponent(softItem);
final Label softLabel = new Label();
softLabel.setSizeFull();
softLabel.setCaption(i18n.getMessage(UIMessageIdProvider.CAPTION_ACTION_SOFT));
softLabel.setDescription(i18n.getMessage(UIMessageIdProvider.TOOLTIP_SOFT_ITEM));
softLabel.setStyleName("padding-right-style");
addComponent(softLabel);
}
/**
* To Set Default option for save.
*/
public void selectDefaultOption() {
actionTypeOptionGroup.select(ActionTypeOption.FORCED);
}
/**
* Enum which described the options for the action type
*
*/
public enum ActionTypeOption {
FORCED(ActionType.FORCED), SOFT(ActionType.SOFT), AUTO_FORCED(ActionType.TIMEFORCED);
private final ActionType actionType;
ActionTypeOption(final ActionType actionType) {
this.actionType = actionType;
}
public ActionType getActionType() {
return actionType;
}
/**
* Matches the action type to the option
*
* @param actionType
* the action type to get option for
* @return action type option if matches, otherwise empty Optional
*/
public static Optional<ActionTypeOption> getOptionForActionType(final ActionType actionType) {
return Arrays.stream(ActionTypeOption.values()).filter(option -> option.getActionType().equals(actionType))
.findFirst();
}
}
public FlexibleOptionGroup getActionTypeOptionGroup() {
return actionTypeOptionGroup;
}
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
* Copyright (c) 2019 Bosch Software Innovations 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
@@ -12,7 +12,6 @@ import java.time.LocalDateTime;
import java.util.Date;
import java.util.TimeZone;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
@@ -26,23 +25,15 @@ import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.server.FontAwesome;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.ui.DateField;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.themes.ValoTheme;
/**
* Action type option group layout.
* Action type option group layout for manual assignment.
*/
public class ActionTypeOptionGroupLayout extends HorizontalLayout {
public class ActionTypeOptionGroupAssignmentLayout extends AbstractActionTypeOptionGroupLayout {
private static final long serialVersionUID = 1L;
private static final String STYLE_DIST_WINDOW_ACTIONTYPE = "dist-window-actiontype";
private final VaadinMessageSource i18n;
private FlexibleOptionGroup actionTypeOptionGroup;
private DateField forcedTimeDateField;
/**
@@ -51,13 +42,9 @@ public class ActionTypeOptionGroupLayout extends HorizontalLayout {
* @param i18n
* VaadinMessageSource
*/
public ActionTypeOptionGroupLayout(final VaadinMessageSource i18n) {
this.i18n = i18n;
createOptionGroup();
public ActionTypeOptionGroupAssignmentLayout(final VaadinMessageSource i18n) {
super(i18n);
addValueChangeListener();
setStyleName("dist-window-actiontype-horz-layout");
setSizeUndefined();
}
private void addValueChangeListener() {
@@ -77,37 +64,20 @@ public class ActionTypeOptionGroupLayout extends HorizontalLayout {
});
}
private void createOptionGroup() {
@Override
protected void createOptionGroup() {
actionTypeOptionGroup = new FlexibleOptionGroup();
actionTypeOptionGroup.addItem(ActionTypeOption.SOFT);
actionTypeOptionGroup.addItem(ActionTypeOption.FORCED);
actionTypeOptionGroup.addItem(ActionTypeOption.AUTO_FORCED);
selectDefaultOption();
final FlexibleOptionGroupItemComponent forceItem = actionTypeOptionGroup
.getItemComponent(ActionTypeOption.FORCED);
forceItem.setStyleName(STYLE_DIST_WINDOW_ACTIONTYPE);
forceItem.setId(UIComponentIdProvider.SAVE_ACTION_RADIO_FORCED);
addComponent(forceItem);
final Label forceLabel = new Label();
forceLabel.setStyleName("statusIconPending");
forceLabel.setIcon(FontAwesome.BOLT);
forceLabel.setCaption(i18n.getMessage(UIMessageIdProvider.CAPTION_ACTION_FORCED));
forceLabel.setDescription(i18n.getMessage(UIMessageIdProvider.TOOLTIP_FORCED_ITEM));
forceLabel.setStyleName("padding-right-style");
addComponent(forceLabel);
final FlexibleOptionGroupItemComponent softItem = actionTypeOptionGroup.getItemComponent(ActionTypeOption.SOFT);
softItem.setId(UIComponentIdProvider.ACTION_DETAILS_SOFT_ID);
softItem.setStyleName(STYLE_DIST_WINDOW_ACTIONTYPE);
addComponent(softItem);
final Label softLabel = new Label();
softLabel.setSizeFull();
softLabel.setCaption(i18n.getMessage(UIMessageIdProvider.CAPTION_ACTION_SOFT));
softLabel.setDescription(i18n.getMessage(UIMessageIdProvider.TOOLTIP_SOFT_ITEM));
softLabel.setStyleName("padding-right-style");
addComponent(softLabel);
addForcedItemWithLabel();
addSoftItemWithLabel();
addAutoForceItemWithLabelAndDateField();
}
private void addAutoForceItemWithLabelAndDateField() {
final FlexibleOptionGroupItemComponent autoForceItem = actionTypeOptionGroup
.getItemComponent(ActionTypeOption.AUTO_FORCED);
autoForceItem.setStyleName(STYLE_DIST_WINDOW_ACTIONTYPE);
@@ -138,38 +108,7 @@ public class ActionTypeOptionGroupLayout extends HorizontalLayout {
addComponent(forcedTimeDateField);
}
/**
* To Set Default option for save.
*/
public void selectDefaultOption() {
actionTypeOptionGroup.select(ActionTypeOption.FORCED);
}
/**
* Enum which described the options for the action type
*
*/
public enum ActionTypeOption {
FORCED(ActionType.FORCED), SOFT(ActionType.SOFT), AUTO_FORCED(ActionType.TIMEFORCED);
private final ActionType actionType;
ActionTypeOption(final ActionType actionType) {
this.actionType = actionType;
}
public ActionType getActionType() {
return actionType;
}
}
public FlexibleOptionGroup getActionTypeOptionGroup() {
return actionTypeOptionGroup;
}
public DateField getForcedTimeDateField() {
return forcedTimeDateField;
}
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2019 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.miscs;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.vaadin.hene.flexibleoptiongroup.FlexibleOptionGroup;
/**
* Action type option group layout for auto assignment.
*/
public class ActionTypeOptionGroupAutoAssignmentLayout extends AbstractActionTypeOptionGroupLayout {
private static final long serialVersionUID = 1L;
/**
* Constructor
*
* @param i18n
* VaadinMessageSource
*/
public ActionTypeOptionGroupAutoAssignmentLayout(final VaadinMessageSource i18n) {
super(i18n);
}
@Override
protected void createOptionGroup() {
actionTypeOptionGroup = new FlexibleOptionGroup();
actionTypeOptionGroup.addItem(ActionTypeOption.SOFT);
actionTypeOptionGroup.addItem(ActionTypeOption.FORCED);
selectDefaultOption();
addForcedItemWithLabel();
addSoftItemWithLabel();
}
}

View File

@@ -66,7 +66,7 @@ public class TargetMetadataPopupLayout extends AbstractMetadataPopupLayout<Targe
@Override
protected List<MetaData> getMetadataList() {
return Collections.unmodifiableList(targetManagement
.findMetaDataByControllerId(new PageRequest(0, 500), getSelectedEntity().getControllerId())
.findMetaDataByControllerId(PageRequest.of(0, 500), getSelectedEntity().getControllerId())
.getContent());
}

View File

@@ -60,8 +60,8 @@ import org.eclipse.hawkbit.ui.management.event.TargetAddUpdateWindowEvent;
import org.eclipse.hawkbit.ui.management.event.TargetFilterEvent;
import org.eclipse.hawkbit.ui.management.event.TargetTableEvent;
import org.eclipse.hawkbit.ui.management.event.TargetTableEvent.TargetComponentEvent;
import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupLayout;
import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupLayout.ActionTypeOption;
import org.eclipse.hawkbit.ui.management.miscs.AbstractActionTypeOptionGroupLayout.ActionTypeOption;
import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupAssignmentLayout;
import org.eclipse.hawkbit.ui.management.miscs.MaintenanceWindowLayout;
import org.eclipse.hawkbit.ui.management.state.ManagementUIState;
import org.eclipse.hawkbit.ui.management.state.TargetTableFilters;
@@ -142,7 +142,7 @@ public class TargetTable extends AbstractTable<Target> {
private ConfirmationDialog confirmDialog;
private final ActionTypeOptionGroupLayout actionTypeOptionGroupLayout;
private final ActionTypeOptionGroupAssignmentLayout actionTypeOptionGroupLayout;
private final MaintenanceWindowLayout maintenanceWindowLayout;
@@ -159,7 +159,7 @@ public class TargetTable extends AbstractTable<Target> {
this.tagManagement = tagManagement;
this.deploymentManagement = deploymentManagement;
this.uiProperties = uiProperties;
this.actionTypeOptionGroupLayout = new ActionTypeOptionGroupLayout(i18n);
this.actionTypeOptionGroupLayout = new ActionTypeOptionGroupAssignmentLayout(i18n);
this.maintenanceWindowLayout = new MaintenanceWindowLayout(i18n);
setItemDescriptionGenerator(new AssignInstalledDSTooltipGenerator());
@@ -864,10 +864,10 @@ public class TargetTable extends AbstractTable<Target> {
Long distId;
List<TargetIdName> targetIdSetList;
List<TargetIdName> tempIdList;
final ActionType actionType = ((ActionTypeOptionGroupLayout.ActionTypeOption) actionTypeOptionGroupLayout
.getActionTypeOptionGroup().getValue()).getActionType();
final long forcedTimeStamp = (((ActionTypeOptionGroupLayout.ActionTypeOption) actionTypeOptionGroupLayout
.getActionTypeOptionGroup().getValue()) == ActionTypeOption.AUTO_FORCED)
final ActionType actionType = ((ActionTypeOption) actionTypeOptionGroupLayout.getActionTypeOptionGroup()
.getValue()).getActionType();
final long forcedTimeStamp = (((ActionTypeOption) actionTypeOptionGroupLayout.getActionTypeOptionGroup()
.getValue()) == ActionTypeOption.AUTO_FORCED)
? actionTypeOptionGroupLayout.getForcedTimeDateField().getValue().getTime()
: RepositoryModelConstants.NO_FORCE_TIME;

View File

@@ -48,8 +48,8 @@ import org.eclipse.hawkbit.ui.common.builder.TextAreaBuilder;
import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder;
import org.eclipse.hawkbit.ui.common.builder.WindowBuilder;
import org.eclipse.hawkbit.ui.filtermanagement.TargetFilterBeanQuery;
import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupLayout;
import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupLayout.ActionTypeOption;
import org.eclipse.hawkbit.ui.management.miscs.AbstractActionTypeOptionGroupLayout.ActionTypeOption;
import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupAssignmentLayout;
import org.eclipse.hawkbit.ui.rollout.event.RolloutEvent;
import org.eclipse.hawkbit.ui.rollout.groupschart.GroupsPieChart;
import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil;
@@ -112,7 +112,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
private static final String DENY_BUTTON_LABEL = "button.deny";
private final ActionTypeOptionGroupLayout actionTypeOptionGroupLayout;
private final ActionTypeOptionGroupAssignmentLayout actionTypeOptionGroupLayout;
private final AutoStartOptionGroupLayout autoStartOptionGroupLayout;
@@ -190,7 +190,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
final VaadinMessageSource i18n, final UIEventBus eventBus,
final TargetFilterQueryManagement targetFilterQueryManagement,
final RolloutGroupManagement rolloutGroupManagement, final QuotaManagement quotaManagement) {
actionTypeOptionGroupLayout = new ActionTypeOptionGroupLayout(i18n);
actionTypeOptionGroupLayout = new ActionTypeOptionGroupAssignmentLayout(i18n);
autoStartOptionGroupLayout = new AutoStartOptionGroupLayout(i18n);
this.rolloutManagement = rolloutManagement;
this.rolloutGroupManagement = rolloutGroupManagement;
@@ -371,8 +371,8 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
}
private ActionType getActionType() {
return ((ActionTypeOptionGroupLayout.ActionTypeOption) actionTypeOptionGroupLayout
.getActionTypeOptionGroup().getValue()).getActionType();
return ((ActionTypeOption) actionTypeOptionGroupLayout.getActionTypeOptionGroup().getValue())
.getActionType();
}
private AutoStartOptionGroupLayout.AutoStartOption getAutoStartOption() {
@@ -1078,8 +1078,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
}
private void setActionType(final Rollout rollout) {
for (final ActionTypeOptionGroupLayout.ActionTypeOption groupAction : ActionTypeOptionGroupLayout.ActionTypeOption
.values()) {
for (final ActionTypeOption groupAction : ActionTypeOption.values()) {
if (groupAction.getActionType() == rollout.getActionType()) {
actionTypeOptionGroupLayout.getActionTypeOptionGroup().setValue(groupAction);
final SimpleDateFormat format = new SimpleDateFormat(SPUIDefinitions.LAST_QUERY_DATE_FORMAT);

View File

@@ -133,6 +133,10 @@ public final class SPUILabelDefinitions {
* AUTO ASSIGN DISTRIBUTION SET ID
*/
public static final String AUTO_ASSIGN_DISTRIBUTION_SET = "autoAssignDistributionSet";
/**
* AUTO ASSIGN ACTION TYPE
*/
public static final String AUTO_ASSIGN_ACTION_TYPE = "autoAssignActionType";
/**
* ASSIGNED DISTRIBUTION Name and Version.
*/

View File

@@ -1179,9 +1179,9 @@ public final class UIComponentIdProvider {
public static final String FILTER_SEARCH_ICON_ID = "filter.search.icon";
/**
* Distribution set select table id
* Distribution set select combobox id
*/
public static final String DIST_SET_SELECT_TABLE_ID = "distribution.set.select.table";
public static final String DIST_SET_SELECT_COMBO_ID = "distribution.set.select.combo";
/**
* Distribution set select window id

View File

@@ -21,6 +21,12 @@ public final class UIMessageIdProvider {
public static final String BUTTON_SAVE = "button.save";
public static final String BUTTON_NO_AUTO_ASSIGNMENT = "button.no.auto.assignment";
public static final String BUTTON_AUTO_ASSIGNMENT_DESCRIPTION = "button.auto.assignment.desc";
public static final String HEADER_DISTRIBUTION_SET = "header.distributionset";
public static final String CAPTION_ACTION_FORCED = "label.action.forced";
public static final String CAPTION_ACTION_SOFT = "label.action.soft";
@@ -47,6 +53,10 @@ public final class UIMessageIdProvider {
public static final String CAPTION_ARTIFACT_DETAILS_OF = "caption.artifact.details.of";
public static final String CAPTION_SELECT_AUTO_ASSIGN_DS = "caption.select.auto.assign.dist";
public static final String CAPTION_CONFIRM_AUTO_ASSIGN_CONSEQUENCES = "caption.confirm.assign.consequences";
public static final String CAPTION_CONFIG_CREATE = "caption.config.create";
public static final String CAPTION_CONFIG_EDIT = "caption.config.edit";
@@ -59,6 +69,10 @@ public final class UIMessageIdProvider {
public static final String LABEL_CREATE_FILTER = "label.create.filter";
public static final String LABEL_AUTO_ASSIGNMENT_DESC = "label.auto.assign.description";
public static final String LABEL_AUTO_ASSIGNMENT_ENABLE = "label.auto.assign.enable";
public static final String MESSAGE_NO_DATA = "message.no.data";
public static final String MESSAGE_DATA_AVAILABLE = "message.data.available";
@@ -67,6 +81,12 @@ public final class UIMessageIdProvider {
public static final String MESSAGE_ACTION_NOT_ALLOWED = "message.action.not.allowed";
public static final String MESSAGE_SELECTED_DS_NOT_FOUND = "message.selected.distributionset.not.found";
public static final String MESSAGE_CONFIRM_AUTO_ASSIGN_CONSEQUENCES_NONE = "message.confirm.assign.consequences.none";
public static final String MESSAGE_CONFIRM_AUTO_ASSIGN_CONSEQUENCES_TEXT = "message.confirm.assign.consequences.text";
public static final String TOOLTIP_OVERDUE = "tooltip.overdue";
public static final String TOOLTIP_MAXIMIZE = "tooltip.maximize";

View File

@@ -228,7 +228,7 @@ label.target.controller.attrs = <b>Controller attributes</b>
label.target.attributes.update.pending = Update pending..
label.target.lastpolldate = Last poll :
label.tag.name = Tag name
label.configuration.auth.header = Allow targets to authenticate via a certificate authenticated by an reverse proxy
label.configuration.auth.header = Allow targets to authenticate via a certificate authenticated by a reverse proxy
label.configuration.auth.gatewaytoken = Allow a gateway to authenticate and manage multiple targets through a gateway security token
label.configuration.auth.targettoken = Allow targets to authenticate directly with their target security token
label.configuration.repository.autoclose.action = Autoclose running actions when a new distribution set is assigned
@@ -670,6 +670,7 @@ target.not.exists=Target {0} does not exists. Maybe the target was deleted.
targets.not.exists=Targets does not exists. Maybe the targets was deleted.
distributionsets.not.exists=Distribution sets do not exists. Maybe the sets were deleted.
message.selected.distributionset.not.found=Distribution set {0} does not exist in the repository, is incomplete or deleted. Please select a new one.
targettag.not.exists=Target tag {0} does not exists. Maybe the target tag was deleted.
caption.entity.target.tag = Target Tag