Merge pull request #298 from bsinno/feature_target_filtering_supports_overdue

Feature target filtering supports overdue
This commit is contained in:
Michael Hirsch
2016-10-19 15:59:21 +02:00
committed by GitHub
40 changed files with 1305 additions and 330 deletions

View File

@@ -9,12 +9,16 @@
package org.eclipse.hawkbit.autoconfigure.repository;
import org.eclipse.hawkbit.EnableJpaRepository;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.jpa.rsql.VirtualPropertyResolver;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Auto-Configuration for enabling the REST-Resources.
* Auto-Configuration for enabling JPA repository.
*
*/
@Configuration
@@ -22,4 +26,14 @@ import org.springframework.context.annotation.Import;
@Import({ EnableJpaRepository.class })
public class JpaRepositoryAutoConfiguration {
/**
*
* @return returns a VirtualPropertyReplacer
*/
@Bean
@ConditionalOnMissingBean
public VirtualPropertyReplacer virtualPropertyReplacer() {
return new VirtualPropertyResolver();
}
}

View File

@@ -0,0 +1,184 @@
/**
* 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.repository;
import java.util.Collection;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
/**
* Encapsulates a set of filters that may be specified (optionally). Properties
* that are not specified (e.g. <code>null</code> for simple properties) When
* applied, these filters are AND-gated.
*
*/
public class FilterParams {
private Collection<TargetUpdateStatus> filterByStatus;
private Boolean overdueState;
private String filterBySearchText;
private Boolean selectTargetWithNoTag;
private String[] filterByTagNames;
private Long filterByDistributionId;
/**
* Constructor.
*
* @param filterByDistributionId
* if set, a filter is added for the given
* {@link DistributionSet#getId()}
* @param filterByStatus
* if set, a filter is added for target states included by the
* collection
* @param overdueState
* if set, a filter is added for overdued devices
* @param filterBySearchText
* if set, a filter is added for the given search text
* @param selectTargetWithNoTag
* if set, tag-filtering is enabled
* @param filterByTagNames
* if tag-filtering is enabled, a filter is added for the given
* tag-names
*/
public FilterParams(Long filterByDistributionId, Collection<TargetUpdateStatus> filterByStatus,
Boolean overdueState, String filterBySearchText, Boolean selectTargetWithNoTag,
String... filterByTagNames) {
this.filterByStatus = filterByStatus;
this.overdueState = overdueState;
this.filterBySearchText = filterBySearchText;
this.filterByDistributionId = filterByDistributionId;
this.selectTargetWithNoTag = selectTargetWithNoTag;
this.filterByTagNames = filterByTagNames;
}
/**
* Gets {@link DistributionSet#getId()} to filter the result. <br>
* If set to <code>null</code> this filter is disabled.
*
* @return {@link DistributionSet#getId()} to filter the result
*/
public Long getFilterByDistributionId() {
return filterByDistributionId;
}
/**
* Sets {@link DistributionSet#getId()} to filter the result.
*
* @param filterByDistributionId
* the distribution set id
*/
public void setFilterByDistributionId(Long filterByDistributionId) {
this.filterByDistributionId = filterByDistributionId;
}
/**
* Gets a collection of target states to filter for. <br>
* If set to <code>null</code> this filter is disabled.
*
* @return collection of target states to filter for
*/
public Collection<TargetUpdateStatus> getFilterByStatus() {
return filterByStatus;
}
/**
* Sets the collection of target states to filter for.
*
* @param filterByStatus
* collection of target update status
*/
public void setFilterByStatus(Collection<TargetUpdateStatus> filterByStatus) {
this.filterByStatus = filterByStatus;
}
/**
* Gets the flag for overdue filter; if set to <code>true</code>, the
* overdue filter is activated. Overdued targets a targets that did not
* respond during the configured intervals: poll_itvl + overdue_itvl. <br>
* If set to <code>null</code> this filter is disabled.
*
* @return flag for overdue filter activation
*/
public Boolean getOverdueState() {
return overdueState;
}
/**
* Sets the flag for overdue filter; if set to <code>true</code>, the
* overdue filter is activated.
*
* @param overdueState
* if the overdue filter should be activates
*/
public void setOverdueState(Boolean overdueState) {
this.overdueState = overdueState;
}
/**
* Gets the search text to filter for. This is used to find targets having
* the text anywhere in name or description <br>
* If set to <code>null</code> this filter is disabled.
*
* @return the search text to filter for
*/
public String getFilterBySearchText() {
return filterBySearchText;
}
/**
* Sets the search text to filter for.
*
* @param filterBySearchText
* search text
*/
public void setFilterBySearchText(String filterBySearchText) {
this.filterBySearchText = filterBySearchText;
}
/**
* Gets the flag indicating if tagging filter is used. <br>
* If set to <code>null</code> this filter is disabled.
*
* @return the flag indicating if tagging filter is used
*/
public Boolean getSelectTargetWithNoTag() {
return selectTargetWithNoTag;
}
/**
* Sets the flag indicating if tagging filter is used.
*
* @param selectTargetWithNoTag
* should the tagging filter be used?
*/
public void setSelectTargetWithNoTag(Boolean selectTargetWithNoTag) {
this.selectTargetWithNoTag = selectTargetWithNoTag;
}
/**
* Gets the tags that are used to filter for. The activation of this filter
* is done by {@link #setSelectTargetWithNoTag(Boolean)}.
*
* @return the tags that are used to filter for
*/
public String[] getFilterByTagNames() {
return filterByTagNames;
}
/**
* Sets the tags that are used to filter for.
*
* @param filterByTagNames
* array of tag names
*/
public void setFilterByTagNames(String[] filterByTagNames) {
this.filterByTagNames = filterByTagNames;
}
}

View File

@@ -67,7 +67,11 @@ public interface TargetManagement {
* Count {@link Target}s for all the given filter parameters.
*
* @param status
* find targets having on of these {@link TargetUpdateStatus}s.
* find targets having one of these {@link TargetUpdateStatus}s.
* Set to <code>null</code> in case this is not required.
* @param overdueState
* find targets that are overdue (targets that did not respond
* during the configured intervals: poll_itvl + overdue_itvl).
* Set to <code>null</code> in case this is not required.
* @param searchText
* to find targets having the text anywhere in name or
@@ -86,7 +90,7 @@ public interface TargetManagement {
* @return the found number {@link Target}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Long countTargetByFilters(Collection<TargetUpdateStatus> status, String searchText,
Long countTargetByFilters(Collection<TargetUpdateStatus> status, Boolean overdueState, String searchText,
Long installedOrAssignedDistributionSetId, Boolean selectTargetWithNoTag, String... tagNames);
/**
@@ -212,10 +216,12 @@ public interface TargetManagement {
*
* @param pageRequest
* the pageRequest to enhance the query for paging and sorting
*
* @param filterByStatus
* find targets having this {@link TargetUpdateStatus}s. Set to
* <code>null</code> in case this is not required.
* @param overdueState
* find targets that are overdue (targets that did not respond
* during the configured intervals: poll_itvl + overdue_itvl).
* @param filterBySearchText
* to find targets having the text anywhere in name or
* description. Set <code>null</code> in case this is not
@@ -234,7 +240,7 @@ public interface TargetManagement {
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
List<TargetIdName> findAllTargetIdsByFilters(@NotNull Pageable pageRequest,
Collection<TargetUpdateStatus> filterByStatus, String filterBySearchText,
Collection<TargetUpdateStatus> filterByStatus, Boolean overdueState, String filterBySearchText,
Long installedOrAssignedDistributionSetId, Boolean selectTargetWithNoTag, String... filterByTagNames);
/**
@@ -314,7 +320,7 @@ public interface TargetManagement {
* given {@code fieldNameProvider}
* @throws RSQLParameterSyntaxException
* if the RSQL syntax is wrong
*
*
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET)
Page<Target> findTargetByAssignedDistributionSet(@NotNull Long distributionSetID, @NotNull String rsqlParam,
@@ -368,6 +374,9 @@ public interface TargetManagement {
* @param status
* find targets having this {@link TargetUpdateStatus}s. Set to
* <code>null</code> in case this is not required.
* @param overdueState
* find targets that are overdue (targets that did not respond
* during the configured intervals: poll_itvl + overdue_itvl).
* @param searchText
* to find targets having the text anywhere in name or
* description. Set <code>null</code> in case this is not
@@ -386,7 +395,7 @@ public interface TargetManagement {
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Slice<Target> findTargetByFilters(@NotNull Pageable pageable, Collection<TargetUpdateStatus> status,
String searchText, Long installedOrAssignedDistributionSetId, Boolean selectTargetWithNoTag,
Boolean overdueState, String searchText, Long installedOrAssignedDistributionSetId, Boolean selectTargetWithNoTag,
String... tagNames);
/**
@@ -415,7 +424,7 @@ public interface TargetManagement {
* @param pageable
* page parameter
* @return the found {@link Target}s, never {@code null}
*
*
* @throws RSQLParameterUnsupportedFieldException
* if a field in the RSQL string is used but not provided by the
* given {@code fieldNameProvider}
@@ -460,9 +469,9 @@ public interface TargetManagement {
* in string notation
* @param pageable
* pagination parameter
*
*
* @return the found {@link Target}s, never {@code null}
*
*
* @throws RSQLParameterUnsupportedFieldException
* if a field in the RSQL string is used but not provided by the
* given {@code fieldNameProvider}
@@ -481,9 +490,9 @@ public interface TargetManagement {
* the specification for the query
* @param pageable
* pagination parameter
*
*
* @return the found {@link Target}s, never {@code null}
*
*
* @throws RSQLParameterUnsupportedFieldException
* if a field in the RSQL string is used but not provided by the
* given {@code fieldNameProvider}
@@ -511,33 +520,15 @@ public interface TargetManagement {
* the page request to page the result set
* @param orderByDistributionId
* {@link DistributionSet#getId()} to be ordered by
* @param filterByDistributionId
* {@link DistributionSet#getId()} to be filter the result. Set
* to <code>null</code> in case this is not required.
* @param filterByStatus
* find targets having this {@link TargetUpdateStatus}s. Set to
* <code>null</code> in case this is not required.
* @param filterBySearchText
* to find targets having the text anywhere in name or
* description. Set <code>null</code> in case this is not
* required.
* @param installedOrAssignedDistributionSetId
* to find targets having the {@link DistributionSet} as
* installed or assigned. Set to <code>null</code> in case this
* is not required.
* @param filterByTagNames
* to find targets which are having any one in this tag names.
* Set <code>null</code> in case this is not required.
* @param selectTargetWithNoTag
* flag to select targets with no tag assigned
* @param filterParams
* the filters to apply; only filters are enabled that have
* non-null value; filters are AND-gated
* @return a paged result {@link Page} of the {@link Target}s in a defined
* order.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Slice<Target> findTargetsAllOrderByLinkedDistributionSet(@NotNull Pageable pageable,
@NotNull Long orderByDistributionId, Long filterByDistributionId,
Collection<TargetUpdateStatus> filterByStatus, String filterBySearchText, Boolean selectTargetWithNoTag,
String... filterByTagNames);
@NotNull Long orderByDistributionId, FilterParams filterParams);
/**
* retrieves a list of {@link Target}s by their controller ID with details,

View File

@@ -0,0 +1,28 @@
/**
* 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.repository.rsql;
/**
* Implementations map a placeholder to the associated value.
* <p>
* This is used in context of string replacement.
*/
@FunctionalInterface
public interface VirtualPropertyReplacer {
/**
* Looks up a placeholders and replaces them
*
* @param input
* the input string in which virtual properties should be
* replaced
* @return the result of the replacement
*/
String replace(String input);
}

View File

@@ -0,0 +1,58 @@
/**
* 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.repository.jpa;
import java.time.Duration;
import java.time.Instant;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantConfigurationManagementHolder;
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
/**
* Calculates non-persistent timestamps , e.g. the point a time a
* target is declared as overdue.<br>
* Therefore tenant specific configuration may be considered.
*
*/
public final class TimestampCalculator {
private TimestampCalculator() {
}
/**
* Calculates the overdue timestamp (<em>overdue_ts</em>) based on the
* current timestamp and the intervals for polling and poll-overdue:
* <p>
* <em>overdue_ts = now_ts - pollingInterval -
* pollingOverdueInterval</em>;<br>
* <em>pollingInterval</em> and <em>pollingOverdueInterval</em> are
* retrieved from tenant-specific system configuration.
*
* @return <em>overdue_ts</em> in milliseconds since Unix epoch as long
* value
*/
public static long calculateOverdueTimestamp() {
return Instant.now().toEpochMilli() - getDurationForKey(TenantConfigurationKey.POLLING_TIME_INTERVAL).toMillis()
- getDurationForKey(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL).toMillis();
}
private static Duration getDurationForKey(TenantConfigurationKey key) {
return DurationHelper.formattedStringToDuration(getRawStringForKey(key));
}
private static String getRawStringForKey(TenantConfigurationKey key) {
return getTenantConfigurationManagement().getConfigurationValue(key, String.class).getValue();
}
public static TenantConfigurationManagement getTenantConfigurationManagement() {
return TenantConfigurationManagementHolder.getInstance().getTenantConfigurationManagement();
}
}

View File

@@ -0,0 +1,74 @@
/**
* 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.repository.jpa.rsql;
import java.time.Duration;
import java.time.Instant;
import org.apache.commons.lang3.text.StrLookup;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.jpa.TimestampCalculator;
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantConfigurationManagementHolder;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
/**
* Adds macro capabilities to RSQL expressions that are used to filter for
* targets.
* <p>
* Some (virtual) properties do not have a representation in the database (in
* general these properties are time-related, or more explicitly, they deal with
* time intervals).<br>
* Such a virtual property needs to be calculated on Java-side before it may be
* used in a target filter query that is passed to the database. Therefore a
* placeholder is used in the RSQL expression that is expanded when the RSQL is
* parsed
* <p>
* A virtual property may either be a system value like the current date (aka
* <em>now_ts</em>) or a value derived from (tenant-specific) system
* configuration (e.g. <em>overdue_ts</em>).
* <p>
* Known values are:<br>
* <ul>
* <li><em>now_ts</em>: maps to system UTC time in milliseconds since Unix epoch
* as long value</li>
* <li><em>overdue_ts</em>: is a calculated value: <em>overdue_ts = now_ts -
* pollingInterval - pollingOverdueInterval</em>; pollingInterval and
* pollingOverdueInterval are retrieved from tenant-specific system
* configuration.</li>
* </ul>
*/
public class VirtualPropertyResolver extends StrLookup<String> implements VirtualPropertyReplacer {
private StrSubstitutor substitutor;
@Override
public String lookup(String rhs) {
String resolved = null;
if ("now_ts".equalsIgnoreCase(rhs)) {
resolved = String.valueOf(Instant.now().toEpochMilli());
} else if ("overdue_ts".equalsIgnoreCase(rhs)) {
resolved = String.valueOf(TimestampCalculator.calculateOverdueTimestamp());
}
return resolved;
}
@Override
public String replace(String input) {
if (substitutor == null) {
substitutor = new StrSubstitutor(this, StrSubstitutor.DEFAULT_PREFIX, StrSubstitutor.DEFAULT_SUFFIX,
StrSubstitutor.DEFAULT_ESCAPE);
}
return substitutor.replace(input);
}
}

View File

@@ -141,6 +141,16 @@
<artifactId>fest-assert</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@@ -172,7 +172,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
}
/**
*
*
* @return the singleton instance of the
* {@link AfterTransactionCommitExecutorHolder}
*/
@@ -234,7 +234,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* {@link JpaSystemManagement} bean.
*
*
* @return a new {@link SystemManagement}
*/
@Bean
@@ -245,7 +245,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* {@link JpaReportManagement} bean.
*
*
* @return a new {@link ReportManagement}
*/
@Bean
@@ -256,7 +256,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* {@link JpaDistributionSetManagement} bean.
*
*
* @return a new {@link DistributionSetManagement}
*/
@Bean
@@ -267,7 +267,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* {@link JpaTenantStatsManagement} bean.
*
*
* @return a new {@link TenantStatsManagement}
*/
@Bean
@@ -280,7 +280,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* {@link JpaTenantConfigurationManagement} bean.
*
*
* @return a new {@link TenantConfigurationManagement}
*/
@Bean
@@ -291,7 +291,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* {@link JpaTenantConfigurationManagement} bean.
*
*
* @return a new {@link TenantConfigurationManagement}
*/
@Bean
@@ -302,7 +302,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* {@link JpaTargetFilterQueryManagement} bean.
*
*
* @return a new {@link TargetFilterQueryManagement}
*/
@Bean
@@ -313,7 +313,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* {@link JpaTagManagement} bean.
*
*
* @return a new {@link TagManagement}
*/
@Bean
@@ -324,7 +324,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* {@link JpaSoftwareManagement} bean.
*
*
* @return a new {@link SoftwareManagement}
*/
@Bean
@@ -335,7 +335,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* {@link JpaRolloutManagement} bean.
*
*
* @return a new {@link RolloutManagement}
*/
@Bean
@@ -346,7 +346,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* {@link JpaRolloutGroupManagement} bean.
*
*
* @return a new {@link RolloutGroupManagement}
*/
@Bean
@@ -357,7 +357,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* {@link JpaDeploymentManagement} bean.
*
*
* @return a new {@link DeploymentManagement}
*/
@Bean
@@ -368,7 +368,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* {@link JpaControllerManagement} bean.
*
*
* @return a new {@link ControllerManagement}
*/
@Bean
@@ -379,7 +379,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* {@link JpaArtifactManagement} bean.
*
*
* @return a new {@link ArtifactManagement}
*/
@@ -391,7 +391,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* {@link JpaEntityFactory} bean.
*
*
* @return a new {@link EntityFactory}
*/
@Bean

View File

@@ -54,6 +54,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
@@ -135,6 +136,9 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Autowired
private SystemSecurityContext systemSecurityContext;
@Autowired
private VirtualPropertyReplacer virtualPropertyReplacer;
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
@Modifying
@@ -607,8 +611,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
return convertAcPage(actions, pageable);
}
private static Specification<JpaAction> createSpecificationFor(final Target target, final String rsqlParam) {
final Specification<JpaAction> spec = RSQLUtility.parse(rsqlParam, ActionFields.class);
private Specification<JpaAction> createSpecificationFor(final Target target, final String rsqlParam) {
final Specification<JpaAction> spec = RSQLUtility.parse(rsqlParam, ActionFields.class, virtualPropertyReplacer);
return (root, query, cb) -> cb.and(spec.toPredicate(root, query, cb),
cb.equal(root.get(JpaAction_.target), target));
}

View File

@@ -42,6 +42,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetSpecification;
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetTypeSpecification;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
@@ -110,6 +111,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Autowired
private TenantAware tenantAware;
@Autowired
private VirtualPropertyReplacer virtualPropertyReplacer;
@Override
public DistributionSet findDistributionSetByIdWithDetails(final Long distid) {
return distributionSetRepository.findOne(DistributionSetSpecification.byId(distid));
@@ -286,7 +290,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
public Page<DistributionSetType> findDistributionSetTypesAll(final String rsqlParam, final Pageable pageable) {
final Specification<JpaDistributionSetType> spec = RSQLUtility.parse(rsqlParam,
DistributionSetTypeFields.class);
DistributionSetTypeFields.class, virtualPropertyReplacer);
return convertDsTPage(distributionSetTypeRepository.findAll(spec, pageable));
}
@@ -352,7 +356,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
public Page<DistributionSet> findDistributionSetsAll(final String rsqlParam, final Pageable pageReq,
final Boolean deleted) {
final Specification<JpaDistributionSet> spec = RSQLUtility.parse(rsqlParam, DistributionSetFields.class);
final Specification<JpaDistributionSet> spec = RSQLUtility.parse(rsqlParam, DistributionSetFields.class,
virtualPropertyReplacer);
final List<Specification<JpaDistributionSet>> specList = new ArrayList<>(2);
if (deleted != null) {
@@ -528,7 +533,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
/**
* Method to get the latest distribution set based on ds ID after the
* metadata changes for that distribution set.
*
*
* @param distributionSet
* Distribution set
*/
@@ -564,7 +569,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
final String rsqlParam, final Pageable pageable) {
final Specification<JpaDistributionSetMetadata> spec = RSQLUtility.parse(rsqlParam,
DistributionSetMetadataFields.class);
DistributionSetMetadataFields.class, virtualPropertyReplacer);
return convertMdPage(
distributionSetMetadataRepository

View File

@@ -33,6 +33,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup;
import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup_;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
@@ -70,6 +71,9 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
@Autowired
private EntityManager entityManager;
@Autowired
private VirtualPropertyReplacer virtualPropertyReplacer;
@Override
public RolloutGroup findRolloutGroupById(final Long rolloutGroupId) {
return rolloutGroupRepository.findOne(rolloutGroupId);
@@ -92,7 +96,8 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
public Page<RolloutGroup> findRolloutGroupsAll(final Rollout rollout, final String rsqlParam,
final Pageable pageable) {
final Specification<JpaRolloutGroup> specification = RSQLUtility.parse(rsqlParam, RolloutGroupFields.class);
final Specification<JpaRolloutGroup> specification = RSQLUtility.parse(rsqlParam, RolloutGroupFields.class,
virtualPropertyReplacer);
return convertPage(
rolloutGroupRepository
@@ -145,7 +150,8 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
public Page<Target> findRolloutGroupTargets(final RolloutGroup rolloutGroup, final String rsqlParam,
final Pageable pageable) {
final Specification<JpaTarget> rsqlSpecification = RSQLUtility.parse(rsqlParam, TargetFields.class);
final Specification<JpaTarget> rsqlSpecification = RSQLUtility.parse(rsqlParam, TargetFields.class,
virtualPropertyReplacer);
return convertTPage(targetRepository.findAll((root, query, criteriaBuilder) -> {
final ListJoin<JpaTarget, RolloutTargetGroup> rolloutTargetJoin = root.join(JpaTarget_.rolloutTargetGroup);

View File

@@ -32,6 +32,7 @@ import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupActionEvaluator;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupConditionEvaluator;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -114,6 +115,9 @@ public class JpaRolloutManagement implements RolloutManagement {
@Autowired
private CacheWriteNotify cacheWriteNotify;
@Autowired
private VirtualPropertyReplacer virtualPropertyReplacer;
@Autowired
@Qualifier("asyncExecutor")
private Executor executor;
@@ -150,7 +154,8 @@ public class JpaRolloutManagement implements RolloutManagement {
@Override
public Page<Rollout> findAllWithDetailedStatusByPredicate(final String rsqlParam, final Pageable pageable) {
final Specification<JpaRollout> specification = RSQLUtility.parse(rsqlParam, RolloutFields.class);
final Specification<JpaRollout> specification = RSQLUtility.parse(rsqlParam, RolloutFields.class,
virtualPropertyReplacer);
final Page<JpaRollout> findAll = rolloutRepository.findAll(specification, pageable);
setRolloutStatusDetails(findAll);

View File

@@ -45,6 +45,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule_;
import org.eclipse.hawkbit.repository.jpa.model.SwMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleSpecification;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.model.AssignedSoftwareModule;
@@ -106,6 +107,9 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Autowired
private ArtifactManagement artifactManagement;
@Autowired
private VirtualPropertyReplacer virtualPropertyReplacer;
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@@ -312,7 +316,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override
public Page<SoftwareModule> findSoftwareModulesByPredicate(final String rsqlParam, final Pageable pageable) {
final Specification<JpaSoftwareModule> spec = RSQLUtility.parse(rsqlParam, SoftwareModuleFields.class);
final Specification<JpaSoftwareModule> spec = RSQLUtility.parse(rsqlParam, SoftwareModuleFields.class,
virtualPropertyReplacer);
return convertSmPage(softwareModuleRepository.findAll(spec, pageable), pageable);
}
@@ -320,7 +325,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override
public Page<SoftwareModuleType> findSoftwareModuleTypesAll(final String rsqlParam, final Pageable pageable) {
final Specification<JpaSoftwareModuleType> spec = RSQLUtility.parse(rsqlParam, SoftwareModuleTypeFields.class);
final Specification<JpaSoftwareModuleType> spec = RSQLUtility.parse(rsqlParam, SoftwareModuleTypeFields.class,
virtualPropertyReplacer);
return convertSmTPage(softwareModuleTypeRepository.findAll(spec, pageable), pageable);
}
@@ -606,7 +612,7 @@ public class JpaSoftwareManagement implements SoftwareManagement {
final String rsqlParam, final Pageable pageable) {
final Specification<JpaSoftwareModuleMetadata> spec = RSQLUtility.parse(rsqlParam,
SoftwareModuleMetadataFields.class);
SoftwareModuleMetadataFields.class, virtualPropertyReplacer);
return convertSmMdPage(
softwareModuleMetadataRepository
.findAll(

View File

@@ -30,6 +30,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.tenancy.TenantAware;
@@ -74,6 +75,9 @@ public class JpaTagManagement implements TagManagement {
@Autowired
private AfterTransactionCommitExecutor afterCommit;
@Autowired
private VirtualPropertyReplacer virtualPropertyReplacer;
@Override
public TargetTag findTargetTag(final String name) {
return targetTagRepository.findByNameEquals(name);
@@ -145,7 +149,7 @@ public class JpaTagManagement implements TagManagement {
@Override
public Page<TargetTag> findAllTargetTags(final String rsqlParam, final Pageable pageable) {
final Specification<JpaTargetTag> spec = RSQLUtility.parse(rsqlParam, TagFields.class);
final Specification<JpaTargetTag> spec = RSQLUtility.parse(rsqlParam, TagFields.class, virtualPropertyReplacer);
return convertTPage(targetTagRepository.findAll(spec, pageable), pageable);
}
@@ -276,7 +280,8 @@ public class JpaTagManagement implements TagManagement {
@Override
public Page<DistributionSetTag> findAllDistributionSetTags(final String rsqlParam, final Pageable pageable) {
final Specification<JpaDistributionSetTag> spec = RSQLUtility.parse(rsqlParam, TagFields.class);
final Specification<JpaDistributionSetTag> spec = RSQLUtility.parse(rsqlParam, TagFields.class,
virtualPropertyReplacer);
return convertDsPage(distributionSetTagRepository.findAll(spec, pageable), pageable);
}

View File

@@ -18,6 +18,7 @@ import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -49,6 +50,9 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
@Autowired
private TargetFilterQueryRepository targetFilterQueryRepository;
@Autowired
private VirtualPropertyReplacer virtualPropertyReplacer;
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@@ -95,7 +99,8 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
public Page<TargetFilterQuery> findTargetFilterQueryByFilter(@NotNull Pageable pageable, String rsqlFilter) {
List<Specification<JpaTargetFilterQuery>> specList = Collections.emptyList();
if (!Strings.isNullOrEmpty(rsqlFilter)) {
specList = Collections.singletonList(RSQLUtility.parse(rsqlFilter, TargetFilterQueryFields.class));
specList = Collections.singletonList(
RSQLUtility.parse(rsqlFilter, TargetFilterQueryFields.class, virtualPropertyReplacer));
}
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
}
@@ -114,7 +119,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
specList.add(TargetFilterQuerySpecification.byAutoAssignDS(distributionSet));
}
if (!Strings.isNullOrEmpty(rsqlFilter)) {
specList.add(RSQLUtility.parse(rsqlFilter, TargetFilterQueryFields.class));
specList.add(RSQLUtility.parse(rsqlFilter, TargetFilterQueryFields.class, virtualPropertyReplacer));
}
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
}
@@ -156,7 +161,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
@Override
public boolean verifyTargetFilterQuerySyntax(final String query) {
RSQLUtility.parse(query, TargetFields.class);
RSQLUtility.parse(query, TargetFields.class, virtualPropertyReplacer);
return true;
}

View File

@@ -13,7 +13,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
@@ -29,6 +28,8 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.hawkbit.repository.FilterParams;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.eventbus.event.TargetDeletedEvent;
@@ -43,6 +44,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.model.Target;
@@ -68,7 +70,6 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.eventbus.EventBus;
@@ -104,6 +105,9 @@ public class JpaTargetManagement implements TargetManagement {
@Autowired
private AfterTransactionCommitExecutor afterCommit;
@Autowired
private VirtualPropertyReplacer virtualPropertyReplacer;
@Override
public Target findTargetByControllerID(final String controllerId) {
return targetRepository.findByControllerId(controllerId);
@@ -154,12 +158,15 @@ public class JpaTargetManagement implements TargetManagement {
@Override
public Slice<Target> findTargetsAll(final TargetFilterQuery targetFilterQuery, final Pageable pageable) {
return findTargetsBySpec(RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class), pageable);
return findTargetsBySpec(
RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class, virtualPropertyReplacer),
pageable);
}
@Override
public Page<Target> findTargetsAll(final String targetFilterQuery, final Pageable pageable) {
return findTargetsBySpec(RSQLUtility.parse(targetFilterQuery, TargetFields.class), pageable);
return findTargetsBySpec(RSQLUtility.parse(targetFilterQuery, TargetFields.class, virtualPropertyReplacer),
pageable);
}
private Page<Target> findTargetsBySpec(final Specification<JpaTarget> spec, final Pageable pageable) {
@@ -223,7 +230,8 @@ public class JpaTargetManagement implements TargetManagement {
public Page<Target> findTargetByAssignedDistributionSet(final Long distributionSetID, final String rsqlParam,
final Pageable pageReq) {
final Specification<JpaTarget> spec = RSQLUtility.parse(rsqlParam, TargetFields.class);
final Specification<JpaTarget> spec = RSQLUtility.parse(rsqlParam, TargetFields.class,
virtualPropertyReplacer);
return convertPage(
targetRepository
@@ -251,7 +259,8 @@ public class JpaTargetManagement implements TargetManagement {
public Page<Target> findTargetByInstalledDistributionSet(final Long distributionSetId, final String rsqlParam,
final Pageable pageable) {
final Specification<JpaTarget> spec = RSQLUtility.parse(rsqlParam, TargetFields.class);
final Specification<JpaTarget> spec = RSQLUtility.parse(rsqlParam, TargetFields.class,
virtualPropertyReplacer);
return convertPage(
targetRepository
@@ -269,42 +278,56 @@ public class JpaTargetManagement implements TargetManagement {
@Override
public Slice<Target> findTargetByFilters(final Pageable pageable, final Collection<TargetUpdateStatus> status,
final String searchText, final Long installedOrAssignedDistributionSetId,
final Boolean overdueState, final String searchText, final Long installedOrAssignedDistributionSetId,
final Boolean selectTargetWithNoTag, final String... tagNames) {
final List<Specification<JpaTarget>> specList = buildSpecificationList(status, searchText,
installedOrAssignedDistributionSetId, selectTargetWithNoTag, true, tagNames);
final List<Specification<JpaTarget>> specList = buildSpecificationList(
new FilterParams(installedOrAssignedDistributionSetId, status, overdueState, searchText,
selectTargetWithNoTag, tagNames),
true);
return findByCriteriaAPI(pageable, specList);
}
@Override
public Long countTargetByFilters(final Collection<TargetUpdateStatus> status, final String searchText,
final Long installedOrAssignedDistributionSetId, final Boolean selectTargetWithNoTag,
final String... tagNames) {
final List<Specification<JpaTarget>> specList = buildSpecificationList(status, searchText,
installedOrAssignedDistributionSetId, selectTargetWithNoTag, true, tagNames);
public Long countTargetByFilters(final Collection<TargetUpdateStatus> status, final Boolean overdueState,
final String searchText, final Long installedOrAssignedDistributionSetId,
final Boolean selectTargetWithNoTag, final String... tagNames) {
final List<Specification<JpaTarget>> specList = buildSpecificationList(
new FilterParams(installedOrAssignedDistributionSetId, status, overdueState, searchText,
selectTargetWithNoTag, tagNames),
true);
return countByCriteriaAPI(specList);
}
private static List<Specification<JpaTarget>> buildSpecificationList(final Collection<TargetUpdateStatus> status,
final String searchText, final Long installedOrAssignedDistributionSetId,
final Boolean selectTargetWithNoTag, final boolean fetch, final String... tagNames) {
final List<Specification<JpaTarget>> specList = new LinkedList<>();
if (status != null && !status.isEmpty()) {
specList.add(TargetSpecifications.hasTargetUpdateStatus(status, fetch));
private static List<Specification<JpaTarget>> buildSpecificationList(final FilterParams filterParams,
final boolean fetch) {
final List<Specification<JpaTarget>> specList = new ArrayList<>();
if (filterParams.getFilterByStatus() != null && !filterParams.getFilterByStatus().isEmpty()) {
specList.add(TargetSpecifications.hasTargetUpdateStatus(filterParams.getFilterByStatus(), fetch));
}
if (installedOrAssignedDistributionSetId != null) {
if (filterParams.getOverdueState() != null) {
specList.add(
TargetSpecifications.hasInstalledOrAssignedDistributionSet(installedOrAssignedDistributionSetId));
TargetSpecifications.isOverdue(TimestampCalculator.calculateOverdueTimestamp()));
}
if (!Strings.isNullOrEmpty(searchText)) {
specList.add(TargetSpecifications.likeNameOrDescriptionOrIp(searchText));
if (filterParams.getFilterByDistributionId() != null) {
specList.add(
TargetSpecifications
.hasInstalledOrAssignedDistributionSet(filterParams.getFilterByDistributionId()));
}
if (selectTargetWithNoTag != null && (selectTargetWithNoTag || (tagNames != null && tagNames.length > 0))) {
specList.add(TargetSpecifications.hasTags(tagNames, selectTargetWithNoTag));
if (StringUtils.isNotEmpty(filterParams.getFilterBySearchText())) {
specList.add(TargetSpecifications.likeNameOrDescriptionOrIp(filterParams.getFilterBySearchText()));
}
if (isHasTagsFilterActive(filterParams)) {
specList.add(TargetSpecifications.hasTags(filterParams.getFilterByTagNames(),
filterParams.getSelectTargetWithNoTag()));
}
return specList;
}
private static boolean isHasTagsFilterActive(final FilterParams filterParams) {
return filterParams.getSelectTargetWithNoTag() != null && (filterParams.getSelectTargetWithNoTag()
|| (filterParams.getFilterByTagNames() != null && filterParams.getFilterByTagNames().length > 0));
}
private Slice<Target> findByCriteriaAPI(final Pageable pageable, final List<Specification<JpaTarget>> specList) {
if (specList == null || specList.isEmpty()) {
return convertPage(criteriaNoCountDao.findAll(pageable, JpaTarget.class), pageable);
@@ -418,9 +441,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override
public Slice<Target> findTargetsAllOrderByLinkedDistributionSet(final Pageable pageable,
final Long orderByDistributionId, final Long filterByDistributionId,
final Collection<TargetUpdateStatus> filterByStatus, final String filterBySearchText,
final Boolean selectTargetWithNoTag, final String... filterByTagNames) {
final Long orderByDistributionId, final FilterParams filterParams) {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<JpaTarget> query = cb.createQuery(JpaTarget.class);
final Root<JpaTarget> targetRoot = query.from(JpaTarget.class);
@@ -443,8 +465,7 @@ public class JpaTargetManagement implements TargetManagement {
// build the specifications and then to predicates necessary by the
// given filters
final Predicate[] specificationsForMultiSelect = specificationsToPredicate(
buildSpecificationList(filterByStatus, filterBySearchText, filterByDistributionId,
selectTargetWithNoTag, true, filterByTagNames),
buildSpecificationList(filterParams, true),
targetRoot, query, cb);
// if we have some predicates then add it to the where clause of the
@@ -500,7 +521,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override
public List<TargetIdName> findAllTargetIdsByFilters(final Pageable pageRequest,
final Collection<TargetUpdateStatus> filterByStatus, final String filterBySearchText,
final Collection<TargetUpdateStatus> filterByStatus, final Boolean overdueState,
final String filterBySearchText,
final Long installedOrAssignedDistributionSetId, final Boolean selectTargetWithNoTag,
final String... filterByTagNames) {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
@@ -517,8 +539,9 @@ public class JpaTargetManagement implements TargetManagement {
targetRoot.get(JpaTarget_.controllerId), targetRoot.get(JpaTarget_.name), targetRoot.get(sortProperty));
final Predicate[] specificationsForMultiSelect = specificationsToPredicate(
buildSpecificationList(filterByStatus, filterBySearchText, installedOrAssignedDistributionSetId,
selectTargetWithNoTag, false, filterByTagNames),
buildSpecificationList(new FilterParams(installedOrAssignedDistributionSetId, filterByStatus,
overdueState, filterBySearchText,
selectTargetWithNoTag, filterByTagNames), false),
targetRoot, multiselect, cb);
// if we have some predicates then add it to the where clause of the
@@ -547,7 +570,8 @@ public class JpaTargetManagement implements TargetManagement {
final CriteriaQuery<Object[]> multiselect = query.multiselect(targetRoot.get(JpaTarget_.id),
targetRoot.get(JpaTarget_.controllerId), targetRoot.get(JpaTarget_.name), targetRoot.get(sortProperty));
final Specification<JpaTarget> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class);
final Specification<JpaTarget> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class,
virtualPropertyReplacer);
final Predicate[] specificationsForMultiSelect = specificationsToPredicate(Lists.newArrayList(spec), targetRoot,
multiselect, cb);
@@ -565,7 +589,8 @@ public class JpaTargetManagement implements TargetManagement {
public Page<Target> findAllTargetsByTargetFilterQueryAndNonDS(@NotNull final Pageable pageRequest,
final Long distributionSetId, @NotNull final TargetFilterQuery targetFilterQuery) {
final Specification<JpaTarget> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class);
final Specification<JpaTarget> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class,
virtualPropertyReplacer);
return findTargetsBySpec(
(root, cq,
@@ -578,7 +603,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override
public Long countTargetsByTargetFilterQueryAndNonDS(final Long distributionSetId,
@NotNull final TargetFilterQuery targetFilterQuery) {
final Specification<JpaTarget> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class);
final Specification<JpaTarget> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class,
virtualPropertyReplacer);
final List<Specification<JpaTarget>> specList = new ArrayList<>(2);
specList.add(spec);
specList.add(TargetSpecifications.hasNotDistributionSetInActions(distributionSetId));
@@ -650,13 +676,15 @@ public class JpaTargetManagement implements TargetManagement {
@Override
public Long countTargetByTargetFilterQuery(final TargetFilterQuery targetFilterQuery) {
final Specification<JpaTarget> specs = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class);
final Specification<JpaTarget> specs = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class,
virtualPropertyReplacer);
return targetRepository.count(specs);
}
@Override
public Long countTargetByTargetFilterQuery(final String targetFilterQuery) {
final Specification<JpaTarget> specs = RSQLUtility.parse(targetFilterQuery, TargetFields.class);
final Specification<JpaTarget> specs = RSQLUtility.parse(targetFilterQuery, TargetFields.class,
virtualPropertyReplacer);
return targetRepository.count(specs);
}

View File

@@ -25,10 +25,12 @@ import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.apache.commons.lang3.text.StrLookup;
import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.FieldValueConverter;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.SimpleTypeConverter;
@@ -47,9 +49,8 @@ import cz.jirutka.rsql.parser.ast.RSQLOperators;
import cz.jirutka.rsql.parser.ast.RSQLVisitor;
/**
* A utility class which is able to parse RSQL strings into an spring data
* {@link Specification} which then can be enhanced sql queries to filter
* entities. RSQL parser library: https://github.com/jirutka/rsql-parser
* A utility class which is able to parse RSQL strings into an spring data {@link Specification} which then can be
* enhanced sql queries to filter entities. RSQL parser library: https://github.com/jirutka/rsql-parser
*
* <ul>
* <li>Equal to : ==</li>
@@ -59,16 +60,24 @@ import cz.jirutka.rsql.parser.ast.RSQLVisitor;
* <li>Greater than operator : =gt= or ></li>
* <li>Greater than or equal to : =ge= or >=</li>
* </ul>
* <p>
* Examples of RSQL expressions in both FIQL-like and alternative notation:
* <ul>
* <li>version==2.0.0</li>
* <li>name==targetId1;description==plugAndPlay</li>
* <li>name==targetId1 and description==plugAndPlay</li>
* <li>name==targetId1;description==plugAndPlay</li>
* <li>name==targetId1 and description==plugAndPlay</li>
* <li>name==targetId1,description==plugAndPlay,updateStatus==UNKNOWN</li>
* <li>name==targetId1 or description==plugAndPlay or updateStatus==UNKNOWN</li>
* </ul>
* <p>
* There is also a mechanism that allows to refer to known macros that can resolved by an optional {@link StrLookup}
* (cp. {@link VirtualPropertyResolver}).<br>
* An example that queries for all overdue targets using the ${OVERDUE_TS} placeholder introduced by
* {@link VirtualPropertyResolver} looks like this:<br>
* <em>lastControllerRequestAt=le=${OVERDUE_TS}</em><br>
* It is possible to escape a macro expression by using a second '$': $${OVERDUE_TS} would prevent the ${OVERDUE_TS}
* token from being expanded.
*
*/
public final class RSQLUtility {
@@ -90,6 +99,9 @@ public final class RSQLUtility {
* @param fieldNameProvider
* the enum class type which implements the
* {@link FieldNameProvider}
* @param virtualPropertyReplacer
* holds the logic how the known macros have to be resolved; may
* be <code>null</code>
* @return an specification which can be used with JPA
* @throws RSQLParameterUnsupportedFieldException
* if a field in the RSQL string is used but not provided by the
@@ -98,8 +110,8 @@ public final class RSQLUtility {
* if the RSQL syntax is wrong
*/
public static <A extends Enum<A> & FieldNameProvider, T> Specification<T> parse(final String rsql,
final Class<A> fieldNameProvider) {
return new RSQLSpecification<>(rsql.toLowerCase(), fieldNameProvider);
final Class<A> fieldNameProvider, VirtualPropertyReplacer virtualPropertyReplacer) {
return new RSQLSpecification<>(rsql.toLowerCase(), fieldNameProvider, virtualPropertyReplacer);
}
/**
@@ -130,10 +142,13 @@ public final class RSQLUtility {
private final String rsql;
private final Class<A> enumType;
private final VirtualPropertyReplacer virtualPropertyReplacer;
private RSQLSpecification(final String rsql, final Class<A> enumType) {
private RSQLSpecification(final String rsql, final Class<A> enumType,
VirtualPropertyReplacer virtualPropertyReplacer) {
this.rsql = rsql;
this.enumType = enumType;
this.virtualPropertyReplacer = virtualPropertyReplacer;
}
@Override
@@ -141,7 +156,8 @@ public final class RSQLUtility {
final Node rootNode = parseRsql(rsql);
final JpqQueryRSQLVisitor<A, T> jpqQueryRSQLVisitor = new JpqQueryRSQLVisitor<>(root, cb, enumType);
final JpqQueryRSQLVisitor<A, T> jpqQueryRSQLVisitor = new JpqQueryRSQLVisitor<>(root, cb, enumType,
virtualPropertyReplacer);
final List<Predicate> accept = rootNode.<List<Predicate>, String> accept(jpqQueryRSQLVisitor);
if (accept != null && !accept.isEmpty()) {
@@ -171,13 +187,16 @@ public final class RSQLUtility {
private final Root<T> root;
private final CriteriaBuilder cb;
private final Class<A> enumType;
private final VirtualPropertyReplacer virtualPropertyReplacer;
private final SimpleTypeConverter simpleTypeConverter;
private JpqQueryRSQLVisitor(final Root<T> root, final CriteriaBuilder cb, final Class<A> enumType) {
private JpqQueryRSQLVisitor(final Root<T> root, final CriteriaBuilder cb, final Class<A> enumType,
VirtualPropertyReplacer virtualPropertyReplacer) {
this.root = root;
this.cb = cb;
this.enumType = enumType;
this.virtualPropertyReplacer = virtualPropertyReplacer;
simpleTypeConverter = new SimpleTypeConverter();
}
@@ -199,7 +218,7 @@ public final class RSQLUtility {
return toSingleList(cb.conjunction());
}
private List<Predicate> toSingleList(final Predicate predicate) {
private static List<Predicate> toSingleList(final Predicate predicate) {
return Collections.singletonList(predicate);
}
@@ -348,13 +367,10 @@ public final class RSQLUtility {
private Object convertValueIfNecessary(final ComparisonNode node, final A fieldName, final String value,
final Path<Object> fieldPath) {
// in case the value of an rsql query e.g. type==application is an
// enum we need to
// handle it separately because JPA needs the correct java-type to
// build an
// expression. So String and numeric values JPA can do it by it's
// own but not for
// classes like enums. So we need to transform the given value
// string into the enum
// enum we need to handle it separately because JPA needs the
// correct java-type to build an expression. So String and numeric
// values JPA can do it by it's own but not for classes like enums.
// So we need to transform the given value string into the enum
// class.
final Class<? extends Object> javaType = fieldPath.getJavaType();
if (javaType != null && javaType.isEnum()) {
@@ -399,15 +415,14 @@ public final class RSQLUtility {
// Exception squid:S2095 - see
// https://jira.sonarsource.com/browse/SONARJAVA-1478
@SuppressWarnings({ "rawtypes", "unchecked", "squid:S2095" })
private Object transformEnumValue(final ComparisonNode node, final String value,
private static Object transformEnumValue(final ComparisonNode node, final String value,
final Class<? extends Object> javaType) {
final Class<? extends Enum> tmpEnumType = (Class<? extends Enum>) javaType;
try {
return Enum.valueOf(tmpEnumType, value.toUpperCase());
} catch (final IllegalArgumentException e) {
// we could not transform the given string value into the enum
// type, so ignore
// it and return null and do not filter
// type, so ignore it and return null and do not filter
LOGGER.info("given value {} cannot be transformed into the correct enum type {}", value.toUpperCase(),
javaType);
LOGGER.debug("value cannot be transformed to an enum", e);
@@ -422,10 +437,16 @@ public final class RSQLUtility {
private List<Predicate> mapToPredicate(final ComparisonNode node, final Path<Object> fieldPath,
final List<String> values, final List<Object> transformedValues, final A enumField) {
// only 'equal' and 'notEqual' can handle transformed value like
// enums. The JPA API
// cannot handle object types for greaterThan etc methods.
// enums. The JPA API cannot handle object types for greaterThan etc
// methods.
final Object transformedValue = transformedValues.get(0);
final String value = values.get(0);
String value = values.get(0);
// if lookup is available, replace macros ...
if (virtualPropertyReplacer != null) {
value = virtualPropertyReplacer.replace(value);
}
final List<Predicate> singleList = new ArrayList<>();
final Predicate mapPredicate = mapToMapPredicate(node, fieldPath, enumField);
@@ -541,12 +562,12 @@ public final class RSQLUtility {
return cb.notEqual(fieldPath, transformedValue);
}
private String escapeValueToSQL(final String transformedValue) {
private static String escapeValueToSQL(final String transformedValue) {
return transformedValue.replace("%", "\\%").replace(LIKE_WILDCARD, '%');
}
@SuppressWarnings("unchecked")
private <Y> Path<Y> pathOfString(final Path<?> path) {
private static <Y> Path<Y> pathOfString(final Path<?> path) {
return (Path<Y>) path;
}
@@ -565,4 +586,5 @@ public final class RSQLUtility {
}
}
}

View File

@@ -89,7 +89,7 @@ public final class TargetSpecifications {
/**
* {@link Specification} for retrieving {@link Target}s by "equal to given
* {@link TargetUpdateStatus}".
*
*
* @param updateStatus
* to be filtered on
* @param fetch
@@ -113,10 +113,34 @@ public final class TargetSpecifications {
};
}
/**
* {@link Specification} for retrieving {@link Target}s that are overdue. A
* target is overdue if it did not respond during the configured
* intervals:<br>
* <em>poll_itvl + overdue_itvl</em>
*
* @param overdueTimestamp
* the calculated timestamp to compare with the last respond of a
* target (lastTargetQuery).<br>
* The <code>overdueTimestamp</code> has to be calculated with
* the following expression:<br>
* <em>overdueTimestamp = nowTimestamp - poll_itvl -
* overdue_itvl</em>
*
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> isOverdue(final long overdueTimestamp) {
return (targetRoot, query, cb) -> {
final Join<JpaTarget, JpaTargetInfo> targetInfoJoin = targetRoot.join(JpaTarget_.targetInfo);
return cb.lessThanOrEqualTo(
targetInfoJoin.get(JpaTargetInfo_.lastTargetQuery), overdueTimestamp);
};
}
/**
* {@link Specification} for retrieving {@link Target}s by
* "like controllerId or like description or like ip address".
*
*
* @param searchText
* to be filtered on
* @return the {@link Target} {@link Specification}
@@ -132,7 +156,7 @@ public final class TargetSpecifications {
/**
* {@link Specification} for retrieving {@link Target}s by
* "like controllerId".
*
*
* @param distributionId
* to be filtered on
* @return the {@link Target} {@link Specification}
@@ -169,7 +193,7 @@ public final class TargetSpecifications {
/**
* {@link Specification} for retrieving {@link Target}s by
* "has no tag names"or "has at least on of the given tag names".
*
*
* @param tagNames
* to be filtered on
* @param selectTargetWithNoTag
@@ -202,7 +226,7 @@ public final class TargetSpecifications {
/**
* {@link Specification} for retrieving {@link Target}s by assigned
* distribution set.
*
*
* @param distributionSetId
* the ID of the distribution set which must be assigned
* @return the {@link Target} {@link Specification}
@@ -234,7 +258,7 @@ public final class TargetSpecifications {
/**
* {@link Specification} for retrieving {@link Target}s by assigned
* distribution set.
*
*
* @param distributionSetId
* the ID of the distribution set which must be assigned
* @return the {@link Target} {@link Specification}

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa;
import static org.fest.assertions.api.Assertions.assertThat;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -17,6 +18,7 @@ import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.FilterParams;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
@@ -53,26 +55,42 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final DistributionSet installedSet = testdataFactory.createDistributionSet("another");
final Long lastTargetQueryNotOverdue = Instant.now().toEpochMilli();
final Long lastTargetQueryAlwaysOverdue = 0L;
final Long lastTargetNull = null;
final String targetDsAIdPref = "targ-A";
List<Target> targAs = targetManagement.createTargets(
testdataFactory.generateTargets(100, targetDsAIdPref, targetDsAIdPref.concat(" description")));
List<Target> targAs = new ArrayList<Target>();
for (Target t : testdataFactory.generateTargets(100, targetDsAIdPref, targetDsAIdPref.concat(" description"))) {
targAs.add(targetManagement.createTarget(t, TargetUpdateStatus.UNKNOWN, lastTargetQueryNotOverdue,
t.getTargetInfo().getAddress()));
}
targAs = targetManagement.toggleTagAssignment(targAs, targTagX).getAssignedEntity();
final String targetDsBIdPref = "targ-B";
List<Target> targBs = targetManagement.createTargets(
testdataFactory.generateTargets(100, targetDsBIdPref, targetDsBIdPref.concat(" description")));
List<Target> targBs = new ArrayList<Target>();
for (Target t : testdataFactory.generateTargets(100, targetDsBIdPref, targetDsBIdPref.concat(" description"))) {
targBs.add(targetManagement.createTarget(t, TargetUpdateStatus.UNKNOWN, lastTargetQueryAlwaysOverdue,
t.getTargetInfo().getAddress()));
}
targBs = targetManagement.toggleTagAssignment(targBs, targTagY).getAssignedEntity();
targBs = targetManagement.toggleTagAssignment(targBs, targTagW).getAssignedEntity();
final String targetDsCIdPref = "targ-C";
List<Target> targCs = targetManagement.createTargets(
testdataFactory.generateTargets(100, targetDsCIdPref, targetDsCIdPref.concat(" description")));
List<Target> targCs = new ArrayList<Target>();
for (Target t : testdataFactory.generateTargets(100, targetDsCIdPref, targetDsCIdPref.concat(" description"))) {
targCs.add(targetManagement.createTarget(t, TargetUpdateStatus.UNKNOWN, lastTargetQueryAlwaysOverdue,
t.getTargetInfo().getAddress()));
}
targCs = targetManagement.toggleTagAssignment(targCs, targTagZ).getAssignedEntity();
targCs = targetManagement.toggleTagAssignment(targCs, targTagW).getAssignedEntity();
final String targetDsDIdPref = "targ-D";
final List<Target> targDs = targetManagement.createTargets(
testdataFactory.generateTargets(100, targetDsDIdPref, targetDsDIdPref.concat(" description")));
List<Target> targDs = new ArrayList<Target>();
for (Target t : testdataFactory.generateTargets(100, targetDsDIdPref, targetDsDIdPref.concat(" description"))) {
targDs.add(targetManagement.createTarget(t, TargetUpdateStatus.UNKNOWN, lastTargetNull,
t.getTargetInfo().getAddress()));
}
final String assignedC = targCs.iterator().next().getControllerId();
deploymentManagement.assignDistributionSet(setA.getId(), assignedC);
@@ -148,6 +166,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
verifyThat200targetsWithGivenTagAreInStatusPendingorUnknown(targTagW, both, concat(targBs, targCs));
verfiyThat1TargetAIsInStatusPendingAndHasDSInstalled(installedSet, pending,
targetManagement.findTargetByControllerID(installedC));
expected = concat(targBs, targCs);
expected.removeAll(targetManagement.findTargetByControllerID(Lists.newArrayList(assignedB, assignedC)));
verifyThat198TargetsAreInStatusUnknownAndOverdue(unknown, expected);
}
@Step
@@ -157,10 +179,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final String query = "updatestatus==pending and installedds.name==" + installedSet.getName();
assertThat(targetManagement
.findTargetByFilters(pageReq, pending, null, installedSet.getId(), Boolean.FALSE, new String[0])
.findTargetByFilters(pageReq, pending, null, null, installedSet.getId(), Boolean.FALSE, new String[0])
.getContent()).as("has number of elements").hasSize(1)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(pending, null,
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(pending, null, null,
installedSet.getId(), Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
@@ -168,7 +190,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, null, installedSet.getId(),
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, null, null, installedSet.getId(),
Boolean.FALSE, new String[0])).as("has number of elements").hasSize(1)
.as("and contains the following elements").containsExactly(expectedIdName)
.as("and NAMED filter query returns the same result").containsAll(targetManagement
@@ -183,10 +205,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final String query = "(updatestatus==pending or updatestatus==unknown) and tag==" + targTagW.getName();
assertThat(targetManagement.findTargetByFilters(pageReq, both, null, null, Boolean.FALSE, targTagW.getName())
.getContent()).as("has number of elements").hasSize(200)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(both, null, null,
assertThat(targetManagement
.findTargetByFilters(pageReq, both, null, null, null, Boolean.FALSE, targTagW.getName()).getContent())
.as("has number of elements").hasSize(200).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(both, null, null, null,
Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
@@ -194,7 +216,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, both, null, null, Boolean.FALSE,
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, both, null, null, null, Boolean.FALSE,
targTagW.getName())).as("has number of elements").hasSize(200).as("and contains the following elements")
.containsAll(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
@@ -217,10 +239,11 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final List<TargetIdName> expectedIdNames = convertToIdNames(expected);
final String query = "updatestatus==pending and tag==" + targTagW.getName();
assertThat(targetManagement.findTargetByFilters(pageReq, pending, null, null, Boolean.FALSE, targTagW.getName())
assertThat(targetManagement
.findTargetByFilters(pageReq, pending, null, null, null, Boolean.FALSE, targTagW.getName())
.getContent()).as("has number of elements").hasSize(2)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(pending, null, null,
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(pending, null, null, null,
Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
@@ -228,7 +251,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, null, null, Boolean.FALSE,
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, null, null, null, Boolean.FALSE,
targTagW.getName())).as("has number of elements").hasSize(2).as("and contains the following elements")
.containsAll(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
@@ -243,18 +266,18 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
+ setA.getName() + ") and tag==" + targTagW.getName();
assertThat(targetManagement
.findTargetByFilters(pageReq, pending, null, setA.getId(), Boolean.FALSE, targTagW.getName())
.findTargetByFilters(pageReq, pending, null, null, setA.getId(), Boolean.FALSE, targTagW.getName())
.getContent()).as("has number of elements").hasSize(2)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(pending, null, setA.getId(),
Boolean.FALSE, targTagW.getName())))
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(pending, null, null,
setA.getId(), Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findTargetsAll(query, pageReq).getContent())
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, null, null, Boolean.FALSE,
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, null, null, null, Boolean.FALSE,
targTagW.getName())).as("has number of elements").hasSize(2).as("and contains the following elements")
.containsAll(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
@@ -268,11 +291,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final String query = "updatestatus==pending and (assignedds.name==" + setA.getName() + " or installedds.name=="
+ setA.getName() + ") and (name==*targ-B* or description==*targ-B*) and tag==" + targTagW.getName();
assertThat(targetManagement
.findTargetByFilters(pageReq, pending, "%targ-B%", setA.getId(), Boolean.FALSE, targTagW.getName())
.getContent()).as("has number of elements").hasSize(1)
assertThat(targetManagement.findTargetByFilters(pageReq, pending, null, "%targ-B%", setA.getId(), Boolean.FALSE,
targTagW.getName()).getContent()).as("has number of elements").hasSize(1)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(pending, "%targ-B%",
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(pending, null, "%targ-B%",
setA.getId(), Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
@@ -280,11 +302,11 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, "%targ-B%", setA.getId(), Boolean.FALSE,
targTagW.getName())).as("has number of elements").hasSize(1).as("and contains the following elements")
.containsExactly(expectedIdName).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
new JpaTargetFilterQuery("test", query)));
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, null, "%targ-B%", setA.getId(),
Boolean.FALSE, targTagW.getName())).as("has number of elements").hasSize(1)
.as("and contains the following elements").containsExactly(expectedIdName)
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findAllTargetIdsByTargetFilterQuery(pageReq, new JpaTargetFilterQuery("test", query)));
}
@Step
@@ -295,10 +317,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
+ setA.getName() + ") and (name==*targ-A* or description==*targ-A*)";
assertThat(targetManagement
.findTargetByFilters(pageReq, pending, "%targ-A%", setA.getId(), Boolean.FALSE, new String[0])
.findTargetByFilters(pageReq, pending, null, "%targ-A%", setA.getId(), Boolean.FALSE, new String[0])
.getContent()).as("has number of elements").hasSize(1)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(pending, "%targ-A%",
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(pending, null, "%targ-A%",
setA.getId(), Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
@@ -306,11 +328,11 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, "%targ-A%", setA.getId(), Boolean.FALSE,
new String[0])).as("has number of elements").hasSize(1).as("and contains the following elements")
.containsExactly(expectedIdName).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
new JpaTargetFilterQuery("test", query)));
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, null, "%targ-A%", setA.getId(),
Boolean.FALSE, new String[0])).as("has number of elements").hasSize(1)
.as("and contains the following elements").containsExactly(expectedIdName)
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findAllTargetIdsByTargetFilterQuery(pageReq, new JpaTargetFilterQuery("test", query)));
}
@Step
@@ -321,17 +343,17 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
+ setA.getName() + ")";
assertThat(targetManagement
.findTargetByFilters(pageReq, pending, null, setA.getId(), Boolean.FALSE, new String[0]).getContent())
.as("has number of elements").hasSize(3).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(pending, null, setA.getId(),
Boolean.FALSE, new String[0])))
.findTargetByFilters(pageReq, pending, null, null, setA.getId(), Boolean.FALSE, new String[0])
.getContent()).as("has number of elements").hasSize(3).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(pending, null, null,
setA.getId(), Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findTargetsAll(query, pageReq).getContent())
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, null, setA.getId(), Boolean.FALSE,
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, null, null, setA.getId(), Boolean.FALSE,
new String[0])).as("has number of elements").hasSize(3).as("and contains the following elements")
.containsAll(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
@@ -344,10 +366,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final List<TargetIdName> expectedIdNames = convertToIdNames(expected);
final String query = "updatestatus==pending";
assertThat(targetManagement.findTargetByFilters(pageReq, pending, null, null, Boolean.FALSE, new String[0])
.getContent()).as("has number of elements").hasSize(3)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(pending, null, null,
assertThat(targetManagement
.findTargetByFilters(pageReq, pending, null, null, null, Boolean.FALSE, new String[0]).getContent())
.as("has number of elements").hasSize(3).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(pending, null, null, null,
Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
@@ -355,9 +377,8 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(
targetManagement.findAllTargetIdsByFilters(pageReq, pending, null, null, Boolean.FALSE, new String[0]))
.as("has number of elements").hasSize(3).as("and contains the following elements")
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, null, null, null, Boolean.FALSE,
new String[0])).as("has number of elements").hasSize(3).as("and contains the following elements")
.containsAll(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
new JpaTargetFilterQuery("test", query)));
@@ -371,18 +392,18 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
+ targTagW.getName();
assertThat(targetManagement
.findTargetByFilters(pageReq, unknown, "%targ-B%", null, Boolean.FALSE, targTagW.getName())
.findTargetByFilters(pageReq, unknown, null, "%targ-B%", null, Boolean.FALSE, targTagW.getName())
.getContent()).as("has number of elements").hasSize(99)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(unknown, "%targ-B%", null,
Boolean.FALSE, targTagW.getName())))
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(unknown, null, "%targ-B%",
null, Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findTargetsAll(query, pageReq).getContent())
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, unknown, "%targ-B%", null, Boolean.FALSE,
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, unknown, null, "%targ-B%", null, Boolean.FALSE,
targTagW.getName())).as("has number of elements").hasSize(99).as("and contains the following elements")
.containsAll(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
@@ -396,17 +417,17 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final String query = "updatestatus==unknown and (name==*targ-A* or description==*targ-A*)";
assertThat(targetManagement
.findTargetByFilters(pageReq, unknown, "%targ-A%", null, Boolean.FALSE, new String[0]).getContent())
.as("has number of elements").hasSize(99).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(unknown, "%targ-A%", null,
Boolean.FALSE, new String[0])))
.findTargetByFilters(pageReq, unknown, null, "%targ-A%", null, Boolean.FALSE, new String[0])
.getContent()).as("has number of elements").hasSize(99)
.as("that number is also returned by count query").hasSize(Ints.saturatedCast(targetManagement
.countTargetByFilters(unknown, null, "%targ-A%", null, Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findTargetsAll(query, pageReq).getContent())
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, unknown, "%targ-A%", null, Boolean.FALSE,
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, unknown, null, "%targ-A%", null, Boolean.FALSE,
new String[0])).as("has number of elements").hasSize(99).as("and contains the following elements")
.containsAll(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
@@ -420,16 +441,16 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
+ setA.getName() + ")";
assertThat(targetManagement
.findTargetByFilters(pageReq, unknown, null, setA.getId(), Boolean.FALSE, new String[0]).getContent())
.as("has number of elements").hasSize(0).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(unknown, null, setA.getId(),
Boolean.FALSE, new String[0])))
.findTargetByFilters(pageReq, unknown, null, null, setA.getId(), Boolean.FALSE, new String[0])
.getContent()).as("has number of elements").hasSize(0).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(unknown, null, null,
setA.getId(), Boolean.FALSE, new String[0])))
.as("and filter query returns the same result")
.hasSize(targetManagement.findTargetsAll(query, pageReq).getContent().size())
.as("and NAMED filter query returns the same result").hasSize(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent().size());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, unknown, null, setA.getId(), Boolean.FALSE,
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, unknown, null, null, setA.getId(), Boolean.FALSE,
new String[0])).as("has number of elements").hasSize(0)
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findAllTargetIdsByTargetFilterQuery(pageReq, new JpaTargetFilterQuery("test", query)));
@@ -442,10 +463,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final String query = "updatestatus==unknown and (tag==" + targTagY.getName() + " or tag==" + targTagW.getName()
+ ")";
assertThat(targetManagement.findTargetByFilters(pageReq, unknown, null, null, Boolean.FALSE, targTagY.getName(),
targTagW.getName()).getContent()).as("has number of elements").hasSize(198)
assertThat(targetManagement.findTargetByFilters(pageReq, unknown, null, null, null, Boolean.FALSE,
targTagY.getName(), targTagW.getName()).getContent()).as("has number of elements").hasSize(198)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(unknown, null, null,
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(unknown, null, null, null,
Boolean.FALSE, targTagY.getName(), targTagW.getName())))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
@@ -453,7 +474,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, unknown, null, null, Boolean.FALSE,
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, unknown, null, null, null, Boolean.FALSE,
targTagY.getName(), targTagW.getName())).as("has number of elements").hasSize(198)
.as("and contains the following elements").containsAll(expectedIdNames)
.as("and NAMED filter query returns the same result").containsAll(targetManagement
@@ -466,10 +487,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final List<TargetIdName> expectedIdNames = convertToIdNames(expected);
final String query = "updatestatus==unknown";
assertThat(targetManagement.findTargetByFilters(pageReq, unknown, null, null, Boolean.FALSE, new String[0])
.getContent()).as("has number of elements").hasSize(397)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(unknown, null, null,
assertThat(targetManagement
.findTargetByFilters(pageReq, unknown, null, null, null, Boolean.FALSE, new String[0]).getContent())
.as("has number of elements").hasSize(397).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(unknown, null, null, null,
Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
@@ -477,9 +498,34 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(
targetManagement.findAllTargetIdsByFilters(pageReq, unknown, null, null, Boolean.FALSE, new String[0]))
.as("has number of elements").hasSize(397).as("and contains the following elements")
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, unknown, null, null, null, Boolean.FALSE,
new String[0])).as("has number of elements").hasSize(397).as("and contains the following elements")
.containsAll(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
new JpaTargetFilterQuery("test", query)));
}
@Step
private void verifyThat198TargetsAreInStatusUnknownAndOverdue(final List<TargetUpdateStatus> unknown,
final List<Target> expected) {
final List<TargetIdName> expectedIdNames = convertToIdNames(expected);
// be careful: simple filters are concatenated using AND-gating
final String query = "lastcontrollerrequestat=le=${overdue_ts};updatestatus==UNKNOWN";
assertThat(targetManagement
.findTargetByFilters(pageReq, unknown, Boolean.TRUE, null, null, Boolean.FALSE, new String[0])
.getContent()).as("has number of elements").hasSize(198)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(unknown, Boolean.TRUE, null,
null, Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findTargetsAll(query, pageReq).getContent())
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, unknown, Boolean.TRUE, null, null, Boolean.FALSE,
new String[0])).as("has number of elements").hasSize(198).as("and contains the following elements")
.containsAll(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
new JpaTargetFilterQuery("test", query)));
@@ -492,10 +538,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
+ " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement
.findTargetByFilters(pageReq, null, "%targ-A%", setA.getId(), Boolean.FALSE, new String[0])
.findTargetByFilters(pageReq, null, null, "%targ-A%", setA.getId(), Boolean.FALSE, new String[0])
.getContent()).as("has number of elements").hasSize(1)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(null, "%targ-A%",
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(null, null, "%targ-A%",
setA.getId(), Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
@@ -503,11 +549,11 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, "%targ-A%", setA.getId(), Boolean.FALSE,
new String[0])).as("has number of elements").hasSize(1).as("and contains the following elements")
.containsExactly(expectedIdName).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
new JpaTargetFilterQuery("test", query)));
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, null, "%targ-A%", setA.getId(),
Boolean.FALSE, new String[0])).as("has number of elements").hasSize(1)
.as("and contains the following elements").containsExactly(expectedIdName)
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findAllTargetIdsByTargetFilterQuery(pageReq, new JpaTargetFilterQuery("test", query)));
}
@Step
@@ -515,18 +561,19 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final List<TargetIdName> expectedIdNames = convertToIdNames(expected);
final String query = "assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName();
assertThat(targetManagement.findTargetByFilters(pageReq, null, null, setA.getId(), Boolean.FALSE, new String[0])
assertThat(targetManagement
.findTargetByFilters(pageReq, null, null, null, setA.getId(), Boolean.FALSE, new String[0])
.getContent()).as("has number of elements").hasSize(3)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(null, null, setA.getId(),
Boolean.FALSE, new String[0])))
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(null, null, null,
setA.getId(), Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findTargetsAll(query, pageReq).getContent())
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, null, setA.getId(), Boolean.FALSE,
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, null, null, setA.getId(), Boolean.FALSE,
new String[0])).as("has number of elements").hasSize(3).as("and contains the following elements")
.containsAll(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
@@ -538,18 +585,18 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final String query = "(name==*targ-C* or description==*targ-C*) and tag==" + targTagX.getName()
+ " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement
.findTargetByFilters(pageReq, null, "%targ-C%", setA.getId(), Boolean.FALSE, targTagX.getName())
.findTargetByFilters(pageReq, null, null, "%targ-C%", setA.getId(), Boolean.FALSE, targTagX.getName())
.getContent()).as("has number of elements").hasSize(0)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(null, "%targ-C%",
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(null, null, "%targ-C%",
setA.getId(), Boolean.FALSE, targTagX.getName())))
.as("and filter query returns the same result")
.hasSize(targetManagement.findTargetsAll(query, pageReq).getContent().size())
.as("and NAMED filter query returns the same result").hasSize(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent().size());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, "%targ-C%", setA.getId(), Boolean.FALSE,
targTagX.getName())).as("has number of elements").hasSize(0)
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, null, "%targ-C%", setA.getId(),
Boolean.FALSE, targTagX.getName())).as("has number of elements").hasSize(0)
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findAllTargetIdsByTargetFilterQuery(pageReq, new JpaTargetFilterQuery("test", query)));
}
@@ -559,18 +606,18 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final String query = "(name==*targ-A* or description==*targ-A*) and tag==" + targTagW.getName()
+ " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement
.findTargetByFilters(pageReq, null, "%targ-A%", setA.getId(), Boolean.FALSE, targTagW.getName())
.findTargetByFilters(pageReq, null, null, "%targ-A%", setA.getId(), Boolean.FALSE, targTagW.getName())
.getContent()).as("has number of elements").hasSize(0)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(null, "%targ-A%",
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(null, null, "%targ-A%",
setA.getId(), Boolean.FALSE, targTagW.getName())))
.as("and filter query returns the same result")
.hasSize(targetManagement.findTargetsAll(query, pageReq).getContent().size())
.as("and NAMED filter query returns the same result").hasSize(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent().size());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, "%targ-A%", setA.getId(), Boolean.FALSE,
targTagW.getName())).as("has number of elements").hasSize(0)
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, null, "%targ-A%", setA.getId(),
Boolean.FALSE, targTagW.getName())).as("has number of elements").hasSize(0)
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findAllTargetIdsByTargetFilterQuery(pageReq, new JpaTargetFilterQuery("test", query)));
}
@@ -582,10 +629,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final String query = "(name==*targ-c* or description==*targ-C*) and tag==" + targTagW.getName()
+ " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement
.findTargetByFilters(pageReq, null, "%targ-C%", setA.getId(), Boolean.FALSE, targTagW.getName())
.findTargetByFilters(pageReq, null, null, "%targ-C%", setA.getId(), Boolean.FALSE, targTagW.getName())
.getContent()).as("has number of elements").hasSize(1)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(null, "%targ-C%",
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(null, null, "%targ-C%",
setA.getId(), Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
@@ -593,11 +640,11 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, "%targ-C%", setA.getId(), Boolean.FALSE,
targTagW.getName())).as("has number of elements").hasSize(1).as("and contains the following elements")
.containsExactly(expectedIdName).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
new JpaTargetFilterQuery("test", query)));
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, null, "%targ-C%", setA.getId(),
Boolean.FALSE, targTagW.getName())).as("has number of elements").hasSize(1)
.as("and contains the following elements").containsExactly(expectedIdName)
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findAllTargetIdsByTargetFilterQuery(pageReq, new JpaTargetFilterQuery("test", query)));
}
@Step
@@ -606,10 +653,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final List<TargetIdName> expectedIdNames = convertToIdNames(expected);
final String query = "(name==*targ-B* or description==*targ-B*) and (tag==" + targTagY.getName() + " or tag=="
+ targTagW.getName() + ")";
assertThat(targetManagement.findTargetByFilters(pageReq, null, "%targ-B%", null, Boolean.FALSE,
assertThat(targetManagement.findTargetByFilters(pageReq, null, null, "%targ-B%", null, Boolean.FALSE,
targTagY.getName(), targTagW.getName()).getContent()).as("has number of elements").hasSize(100)
.as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(null, "%targ-B%", null,
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(null, null, "%targ-B%", null,
Boolean.FALSE, targTagY.getName(), targTagW.getName())))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
@@ -617,7 +664,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, "%targ-B%", null, Boolean.FALSE,
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, null, "%targ-B%", null, Boolean.FALSE,
targTagY.getName(), targTagW.getName())).as("has number of elements").hasSize(100)
.as("and contains the following elements").containsAll(expectedIdNames)
.as("and NAMED filter query returns the same result").containsAll(targetManagement
@@ -636,10 +683,11 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
private void verifyThat200TargetsHaveTagD(final TargetTag targTagD, final List<Target> expected) {
final List<TargetIdName> expectedIdNames = convertToIdNames(expected);
final String query = "tag==" + targTagD.getName();
assertThat(targetManagement.findTargetByFilters(pageReq, null, null, null, Boolean.FALSE, targTagD.getName())
.getContent()).as("Expected number of results is").hasSize(200)
assertThat(targetManagement
.findTargetByFilters(pageReq, null, null, null, null, Boolean.FALSE, targTagD.getName()).getContent())
.as("Expected number of results is").hasSize(200)
.as("and is expected number of results is equal to ")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(null, null, null,
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(null, null, null, null,
Boolean.FALSE, targTagD.getName())))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
@@ -647,7 +695,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
.as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, null, null, Boolean.FALSE,
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, null, null, null, Boolean.FALSE,
targTagD.getName())).as("has number of elements").hasSize(200).as("and contains the following elements")
.containsAll(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
@@ -657,12 +705,13 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
@Step
private void verifyThatRepositoryContains400Targets() {
assertThat(targetManagement.findTargetByFilters(pageReq, null, null, null, null, new String[0]).getContent())
.as("Overall we expect that many targets in the repository").hasSize(400)
.as("which is also reflected by repository count")
.hasSize(Ints.saturatedCast(targetManagement.countTargetsAll()))
.as("which is also reflected by call without specification")
.containsAll(targetManagement.findTargetsAll(pageReq).getContent());
assertThat(
targetManagement.findTargetByFilters(pageReq, null, null, null, null, null, new String[0]).getContent())
.as("Overall we expect that many targets in the repository").hasSize(400)
.as("which is also reflected by repository count")
.hasSize(Ints.saturatedCast(targetManagement.countTargetsAll()))
.as("which is also reflected by call without specification")
.containsAll(targetManagement.findTargetsAll(pageReq).getContent());
}
@@ -685,7 +734,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
targInstalled = sendUpdateActionStatusToTargets(ds, targInstalled, Status.FINISHED, "installed");
final Slice<Target> result = targetManagement.findTargetsAllOrderByLinkedDistributionSet(pageReq, ds.getId(),
null, null, null, Boolean.FALSE, new String[0]);
new FilterParams(null, null, null, null, Boolean.FALSE, new String[0]));
final Comparator<TenantAwareBaseEntity> byId = (e1, e2) -> Long.compare(e2.getId(), e1.getId());
@@ -702,6 +751,64 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
}
@Test
@Description("Tests the correct order of targets with applied overdue filter based on selected distribution set. The system expects to have an order based on installed, assigned DS.")
public void targetSearchWithOverdueFilterAndOrderByDistributionSet() {
final Long lastTargetQueryAlwaysOverdue = 0L;
final Long lastTargetQueryNotOverdue = Instant.now().toEpochMilli();
final Long lastTargetNull = null;
final Long[] overdueMix = { lastTargetQueryAlwaysOverdue, lastTargetQueryNotOverdue,
lastTargetQueryAlwaysOverdue, lastTargetNull, lastTargetQueryAlwaysOverdue };
List<Target> notAssignedToBeCreated = testdataFactory.generateTargets(overdueMix.length, "not",
"first description");
List<Target> targAssignedToBeCreated = testdataFactory.generateTargets(overdueMix.length, "assigned",
"first description");
List<Target> targInstalledToBeCreated = testdataFactory.generateTargets(overdueMix.length, "installed",
"first description");
List<Target> notAssigned = new ArrayList<>();
List<Target> targAssigned = new ArrayList<>();
List<Target> targInstalled = new ArrayList<>();
for (int i = 0; i < overdueMix.length; i++) {
notAssigned.add(targetManagement.createTarget(notAssignedToBeCreated.get(i), TargetUpdateStatus.UNKNOWN,
overdueMix[i], notAssignedToBeCreated.get(i).getTargetInfo().getAddress()));
targAssigned.add(targetManagement.createTarget(targAssignedToBeCreated.get(i), TargetUpdateStatus.UNKNOWN,
overdueMix[i], targAssignedToBeCreated.get(i).getTargetInfo().getAddress()));
targInstalled.add(targetManagement.createTarget(targInstalledToBeCreated.get(i), TargetUpdateStatus.UNKNOWN,
overdueMix[i], targInstalledToBeCreated.get(i).getTargetInfo().getAddress()));
}
final DistributionSet ds = testdataFactory.createDistributionSet("a");
targAssigned = deploymentManagement.assignDistributionSet(ds, targAssigned).getAssignedEntity();
targInstalled = deploymentManagement.assignDistributionSet(ds, targInstalled).getAssignedEntity();
targInstalled = sendUpdateActionStatusToTargets(ds, targInstalled, Status.FINISHED, "installed");
final Slice<Target> result = targetManagement.findTargetsAllOrderByLinkedDistributionSet(pageReq, ds.getId(),
new FilterParams(null, null, Boolean.TRUE, null, Boolean.FALSE, new String[0]));
final Comparator<TenantAwareBaseEntity> byId = (e1, e2) -> Long.compare(e2.getId(), e1.getId());
assertThat(result.getNumberOfElements()).isEqualTo(9);
final List<Target> expected = new ArrayList<>();
expected.addAll(targInstalled.stream().sorted(byId)
.filter(item -> lastTargetQueryAlwaysOverdue.equals(item.getTargetInfo().getLastTargetQuery()))
.collect(Collectors.toList()));
expected.addAll(targAssigned.stream().sorted(byId)
.filter(item -> lastTargetQueryAlwaysOverdue.equals(item.getTargetInfo().getLastTargetQuery()))
.collect(Collectors.toList()));
expected.addAll(notAssigned.stream().sorted(byId)
.filter(item -> lastTargetQueryAlwaysOverdue.equals(item.getTargetInfo().getLastTargetQuery()))
.collect(Collectors.toList()));
assertThat(result.getContent()).containsExactly(expected.toArray(new Target[0]));
}
@Test
@Description("Verfies that targets with given assigned DS are returned from repository.")
public void findTargetByAssignedDistributionSet() {

View File

@@ -601,7 +601,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
targetManagement.toggleTagAssignment(tagABCTargets, tagB);
targetManagement.toggleTagAssignment(tagABCTargets, tagC);
assertThat(targetManagement.countTargetByFilters(null, null, null, Boolean.FALSE, "X"))
assertThat(targetManagement.countTargetByFilters(null, null, null, null, Boolean.FALSE, "X"))
.as("Target count is wrong").isEqualTo(0);
// search for targets with tag tagA
@@ -631,11 +631,11 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
checkTargetHasNotTags(tagCTargets, tagA, tagB);
// check again target lists refreshed from DB
assertThat(targetManagement.countTargetByFilters(null, null, null, Boolean.FALSE, "A"))
assertThat(targetManagement.countTargetByFilters(null, null, null, null, Boolean.FALSE, "A"))
.as("Target count is wrong").isEqualTo(targetWithTagA.size());
assertThat(targetManagement.countTargetByFilters(null, null, null, Boolean.FALSE, "B"))
assertThat(targetManagement.countTargetByFilters(null, null, null, null, Boolean.FALSE, "B"))
.as("Target count is wrong").isEqualTo(targetWithTagB.size());
assertThat(targetManagement.countTargetByFilters(null, null, null, Boolean.FALSE, "C"))
assertThat(targetManagement.countTargetByFilters(null, null, null, null, Boolean.FALSE, "C"))
.as("Target count is wrong").isEqualTo(targetWithTagC.size());
}
@@ -752,7 +752,8 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
final String[] tagNames = null;
final List<Target> targetsListWithNoTag = targetManagement
.findTargetByFilters(new PageRequest(0, 500), null, null, null, Boolean.TRUE, tagNames).getContent();
.findTargetByFilters(new PageRequest(0, 500), null, null, null, null, Boolean.TRUE, tagNames)
.getContent();
assertThat(50).as("Total targets").isEqualTo(targetManagement.findAllTargetIds().size());
assertThat(25).as("Targets with no tag").isEqualTo(targetsListWithNoTag.size());

View File

@@ -9,14 +9,9 @@
package org.eclipse.hawkbit.repository.jpa.rsql;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
@@ -30,24 +25,39 @@ import org.eclipse.hawkbit.repository.DistributionSetFields;
import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.jpa.TimestampCalculator;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.Spy;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
@RunWith(MockitoJUnitRunner.class)
@RunWith(PowerMockRunner.class)
@Features("Component Tests - Repository")
@Stories("RSQL search utility")
@PrepareForTest(TimestampCalculator.class)
// TODO: fully document tests -> @Description for long text and reasonable
// method name as short text
public class RSQLUtilityTest {
@Spy
private VirtualPropertyResolver macroResolver = new VirtualPropertyResolver();
@Mock
private TenantConfigurationManagement confMgmt;
@Mock
private Root<Object> baseSoftwareModuleRootMock;
@@ -59,11 +69,16 @@ public class RSQLUtilityTest {
@Mock
private Attribute attribute;
private static final TenantConfigurationValue<String> TEST_POLLING_TIME_INTERVAL = TenantConfigurationValue
.<String>builder().value("00:05:00").build();
private static final TenantConfigurationValue<String> TEST_POLLING_OVERDUE_TIME_INTERVAL = TenantConfigurationValue
.<String>builder().value("00:07:37").build();
@Test
public void wrongRsqlSyntaxThrowSyntaxException() {
final String wrongRSQL = "name==abc;d";
try {
RSQLUtility.parse(wrongRSQL, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock,
RSQLUtility.parse(wrongRSQL, SoftwareModuleFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterSyntaxException e) {
@@ -75,7 +90,7 @@ public class RSQLUtilityTest {
final String wrongRSQL = "unknownField==abc";
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
try {
RSQLUtility.parse(wrongRSQL, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock,
RSQLUtility.parse(wrongRSQL, SoftwareModuleFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock);
fail("Missing an expected RSQLParameterUnsupportedFieldException because of unknown RSQL field");
} catch (final RSQLParameterUnsupportedFieldException e) {
@@ -87,7 +102,8 @@ public class RSQLUtilityTest {
public void wrongRsqlMapSyntaxThrowSyntaxException() {
String wrongRSQL = TargetFields.ATTRIBUTE + "==abc";
try {
RSQLUtility.parse(wrongRSQL, TargetFields.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock,
RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock,
criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterUnsupportedFieldException e) {
@@ -95,7 +111,8 @@ public class RSQLUtilityTest {
wrongRSQL = TargetFields.ATTRIBUTE + ".unkwon.wrong==abc";
try {
RSQLUtility.parse(wrongRSQL, TargetFields.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock,
RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock,
criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterUnsupportedFieldException e) {
@@ -103,7 +120,7 @@ public class RSQLUtilityTest {
wrongRSQL = DistributionSetFields.METADATA + "==abc";
try {
RSQLUtility.parse(wrongRSQL, DistributionSetFields.class).toPredicate(baseSoftwareModuleRootMock,
RSQLUtility.parse(wrongRSQL, DistributionSetFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterUnsupportedFieldException e) {
@@ -115,7 +132,8 @@ public class RSQLUtilityTest {
public void wrongRsqlSubEntitySyntaxThrowSyntaxException() {
String wrongRSQL = TargetFields.ASSIGNEDDS + "==abc";
try {
RSQLUtility.parse(wrongRSQL, TargetFields.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock,
RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock,
criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterUnsupportedFieldException e) {
@@ -123,7 +141,8 @@ public class RSQLUtilityTest {
wrongRSQL = TargetFields.ASSIGNEDDS + ".unknownField==abc";
try {
RSQLUtility.parse(wrongRSQL, TargetFields.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock,
RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock,
criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterUnsupportedFieldException e) {
@@ -131,7 +150,8 @@ public class RSQLUtilityTest {
wrongRSQL = TargetFields.ASSIGNEDDS + ".unknownField.ToMuch==abc";
try {
RSQLUtility.parse(wrongRSQL, TargetFields.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock,
RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock,
criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterUnsupportedFieldException e) {
@@ -146,11 +166,11 @@ public class RSQLUtilityTest {
when(baseSoftwareModuleRootMock.get("version")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class)))
when(criteriaBuilderMock.<String>greaterThanOrEqualTo(any(Expression.class), any(String.class)))
.thenReturn(mock(Predicate.class));
// test
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock,
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock);
// verfication
@@ -164,12 +184,12 @@ public class RSQLUtilityTest {
when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class)))
when(criteriaBuilderMock.<String>greaterThanOrEqualTo(any(Expression.class), any(String.class)))
.thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock))))
.thenReturn(pathOfString(baseSoftwareModuleRootMock));
// test
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock,
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock);
// verfication
@@ -185,12 +205,12 @@ public class RSQLUtilityTest {
when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class)))
when(criteriaBuilderMock.<String>greaterThanOrEqualTo(any(Expression.class), any(String.class)))
.thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock))))
.thenReturn(pathOfString(baseSoftwareModuleRootMock));
// test
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock,
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock);
// verfication
@@ -206,10 +226,10 @@ public class RSQLUtilityTest {
when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class)))
when(criteriaBuilderMock.<String>greaterThanOrEqualTo(any(Expression.class), any(String.class)))
.thenReturn(mock(Predicate.class));
// test
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock,
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock);
// verfication
@@ -226,7 +246,8 @@ public class RSQLUtilityTest {
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
// test
RSQLUtility.parse(correctRsql, TestFieldEnum.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock,
RSQLUtility.parse(correctRsql, TestFieldEnum.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock,
criteriaBuilderMock);
// verfication
@@ -244,7 +265,7 @@ public class RSQLUtilityTest {
try {
// test
RSQLUtility.parse(correctRsql, TestFieldEnum.class).toPredicate(baseSoftwareModuleRootMock,
RSQLUtility.parse(correctRsql, TestFieldEnum.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock);
fail("missing RSQLParameterUnsupportedFieldException for wrong enum value");
} catch (final RSQLParameterUnsupportedFieldException e) {
@@ -252,6 +273,67 @@ public class RSQLUtilityTest {
}
}
@Test
@Description("Tests the resolution of overdue_ts placeholder in context of a RSQL expression.")
public void correctRsqlWithOverdueMacro() {
reset(baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock);
final String overdueProp = "overdue_ts";
final String overduePropPlaceholder = "${" + overdueProp + "}";
final String correctRsql = "testfield=le=" + overduePropPlaceholder;
when(baseSoftwareModuleRootMock.get("testfield")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) String.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String>lessThanOrEqualTo(any(Expression.class), eq(overduePropPlaceholder)))
.thenReturn(mock(Predicate.class));
// test
RSQLUtility.parse(correctRsql, TestFieldEnum.class, setupMacroLookup())
.toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock);
// verification
verify(macroResolver).lookup(overdueProp);
// the macro is already replaced when passed to #lessThanOrEqualTo -> the method is never invoked with the
// placeholder:
verify(criteriaBuilderMock, never()).lessThanOrEqualTo(eq(pathOfString(baseSoftwareModuleRootMock)),
eq(overduePropPlaceholder));
}
@Test
@Description("Tests RSQL expression with an unknown placeholder.")
public void correctRsqlWithUnknownMacro() {
reset(baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock);
final String overdueProp = "unknown";
final String overduePropPlaceholder = "${" + overdueProp + "}";
final String correctRsql = "testfield=le=" + overduePropPlaceholder;
when(baseSoftwareModuleRootMock.get("testfield")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) String.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String>lessThanOrEqualTo(any(Expression.class), eq(overduePropPlaceholder)))
.thenReturn(mock(Predicate.class));
// test
Predicate result = RSQLUtility.parse(correctRsql, TestFieldEnum.class, setupMacroLookup())
.toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock);
// verification
verify(macroResolver).lookup(overdueProp);
// the macro is unknown and hence never replaced -> #lessThanOrEqualTo is invoked with the placeholder:
verify(criteriaBuilderMock).lessThanOrEqualTo(eq(pathOfString(baseSoftwareModuleRootMock)),
eq(overduePropPlaceholder));
}
public VirtualPropertyReplacer setupMacroLookup() {
when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class))
.thenReturn(TEST_POLLING_TIME_INTERVAL);
when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL, String.class))
.thenReturn(TEST_POLLING_OVERDUE_TIME_INTERVAL);
mockStatic(TimestampCalculator.class);
when(TimestampCalculator.getTenantConfigurationManagement()).thenReturn(confMgmt);
return macroResolver;
}
@SuppressWarnings("unchecked")
private <Y> Path<Y> pathOfString(final Path<?> path) {
return (Path<Y>) path;
@@ -262,10 +344,8 @@ public class RSQLUtilityTest {
/*
* (non-Javadoc)
*
* @see
* org.eclipse.hawkbit.server.rest.resource.model.FieldNameProvider#
* getFieldName()
*
* @see org.eclipse.hawkbit.server.rest.resource.model.FieldNameProvider# getFieldName()
*/
@Override
public String getFieldName() {

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.repository.jpa.rsql;
import static org.junit.Assert.*;
import static org.mockito.Mockito.when;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import java.time.Instant;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.jpa.TimestampCalculator;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
@Features("Unit Tests - Repository")
@Stories("Placeholder resolution for virtual properties")
@RunWith(PowerMockRunner.class)
@PrepareForTest(TimestampCalculator.class)
public class VirtualPropertyResolverTest {
@Spy
private VirtualPropertyResolver resolverUnderTest = new VirtualPropertyResolver();
@Mock
private TenantConfigurationManagement confMgmt;
private StrSubstitutor substitutor;
private Long nowTestTime;
private static final TenantConfigurationValue<String> TEST_POLLING_TIME_INTERVAL = TenantConfigurationValue
.<String> builder().value("00:05:00").build();
private static final TenantConfigurationValue<String> TEST_POLLING_OVERDUE_TIME_INTERVAL = TenantConfigurationValue
.<String> builder().value("00:07:37").build();
@Before
public void before() {
nowTestTime = Instant.now().toEpochMilli();
when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class))
.thenReturn(TEST_POLLING_TIME_INTERVAL);
when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL, String.class))
.thenReturn(TEST_POLLING_OVERDUE_TIME_INTERVAL);
mockStatic(TimestampCalculator.class);
when(TimestampCalculator.getTenantConfigurationManagement()).thenReturn(confMgmt);
substitutor = new StrSubstitutor(resolverUnderTest,
StrSubstitutor.DEFAULT_PREFIX,
StrSubstitutor.DEFAULT_SUFFIX, StrSubstitutor.DEFAULT_ESCAPE);
}
@Test
@Description("Tests resolution of NOW_TS by using a StrSubstitutor configured with the VirtualPropertyResolver.")
public void resolveNowTimestampPlaceholder() {
String placeholder = "${NOW_TS}";
String testString = "lhs=lt=" + placeholder;
String resolvedPlaceholders = substitutor.replace(testString);
assertThat("NOW_TS has to be resolved!", resolvedPlaceholders, not(containsString(placeholder)));
}
@Test
@Description("Tests resolution of OVERDUE_TS by using a StrSubstitutor configured with the VirtualPropertyResolver.")
public void resolveOverdueTimestampPlaceholder() {
String placeholder = "${OVERDUE_TS}";
String testString = "lhs=lt=" + placeholder;
String resolvedPlaceholders = substitutor.replace(testString);
assertThat("OVERDUE_TS has to be resolved!", resolvedPlaceholders, not(containsString(placeholder)));
}
@Test
@Description("Tests case insensititity of VirtualPropertyResolver.")
public void resolveOverdueTimestampPlaceholderLowerCase() {
String placeholder = "${overdue_ts}";
String testString = "lhs=lt=" + placeholder;
String resolvedPlaceholders = substitutor.replace(testString);
assertThat("overdue_ts has to be resolved!", resolvedPlaceholders, not(containsString(placeholder)));
}
@Test
@Description("Tests VirtualPropertyResolver with a placeholder unknown to VirtualPropertyResolver.")
public void handleUnknownPlaceholder() {
String placeholder = "${unknown}";
String testString = "lhs=lt=" + placeholder;
String resolvedPlaceholders = substitutor.replace(testString);
assertThat("unknown should not be resolved!", resolvedPlaceholders, containsString(placeholder));
}
@Test
@Description("Tests escape mechanism for placeholders (syntax is $${SOME_PLACEHOLDER}).")
public void handleEscapedPlaceholder() {
String placeholder = "${OVERDUE_TS}";
String escaptedPlaceholder = StrSubstitutor.DEFAULT_ESCAPE + placeholder;
String testString = "lhs=lt=" + escaptedPlaceholder;
String resolvedPlaceholders = substitutor.replace(testString);
assertThat("Escaped OVERDUE_TS should not be resolved!", resolvedPlaceholders, containsString(placeholder));
}
}

View File

@@ -17,6 +17,8 @@ import org.eclipse.hawkbit.cache.CacheConstants;
import org.eclipse.hawkbit.cache.TenancyCacheManager;
import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
import org.eclipse.hawkbit.repository.jpa.model.helper.EventBusHolder;
import org.eclipse.hawkbit.repository.jpa.rsql.VirtualPropertyResolver;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.test.util.JpaTestRepositoryManagement;
import org.eclipse.hawkbit.repository.test.util.TestRepositoryManagement;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
@@ -129,4 +131,13 @@ public class TestConfiguration implements AsyncConfigurer {
return new SimpleAsyncUncaughtExceptionHandler();
}
/**
*
* @return returns a VirtualPropertyReplacer
*/
@Bean
public VirtualPropertyReplacer virtualPropertyReplacer() {
return new VirtualPropertyResolver();
}
}

View File

@@ -399,7 +399,7 @@ public class DistributionSetTable extends AbstractNamedVersionTable<Distribution
}
private boolean validateSoftwareModule(final SoftwareModule sm, final DistributionSet ds) {
if (targetManagement.countTargetByFilters(null, null, ds.getId(), Boolean.FALSE, new String[] {}) > 0) {
if (targetManagement.countTargetByFilters(null, null, null, ds.getId(), Boolean.FALSE, new String[] {}) > 0) {
/* Distribution is already assigned */
notification.displayValidationError(i18n.get("message.dist.inuse",
HawkbitCommonUtil.concatStrings(":", ds.getName(), ds.getVersion())));

View File

@@ -14,5 +14,39 @@ package org.eclipse.hawkbit.ui.management.event;
*
*/
public enum ManagementUIEvent {
SHOW_COUNT_MESSAGE, UPDATE_COUNT, UPDATE_FILTERCOUNT_MESSAGE, HIDE_TARGET_TAG_LAYOUT, SHOW_TARGET_TAG_LAYOUT, HIDE_DISTRIBUTION_TAG_LAYOUT, SHOW_DISTRIBUTION_TAG_LAYOUT, MAX_ACTION_HISTORY, MIN_ACTION_HISTORY, CLOSE_SAVE_ACTIONS_WINDOW, UNASSIGN_TARGET_TAG, UNASSIGN_DISTRIBUTION_TAG, ASSIGN_TARGET_TAG, ASSIGN_DISTRIBUTION_TAG, TARGET_TABLE_FILTER, RESET_SIMPLE_FILTERS, RESET_TARGET_FILTER_QUERY
//
SHOW_COUNT_MESSAGE,
//
UPDATE_COUNT,
//
UPDATE_FILTERCOUNT_MESSAGE,
//
HIDE_TARGET_TAG_LAYOUT,
//
SHOW_TARGET_TAG_LAYOUT,
//
HIDE_DISTRIBUTION_TAG_LAYOUT,
//
SHOW_DISTRIBUTION_TAG_LAYOUT,
//
MAX_ACTION_HISTORY,
//
MIN_ACTION_HISTORY,
//
CLOSE_SAVE_ACTIONS_WINDOW,
//
UNASSIGN_TARGET_TAG,
//
UNASSIGN_DISTRIBUTION_TAG,
//
ASSIGN_TARGET_TAG,
//
ASSIGN_DISTRIBUTION_TAG,
//
TARGET_TABLE_FILTER,
//
RESET_SIMPLE_FILTERS,
//
RESET_TARGET_FILTER_QUERY
}

View File

@@ -14,5 +14,24 @@ package org.eclipse.hawkbit.ui.management.event;
*
*/
public enum TargetFilterEvent {
FILTER_BY_TEXT, FILTER_BY_TAG, REMOVE_FILTER_BY_TEXT, REMOVE_FILTER_BY_TAG, FILTER_BY_STATUS, FILTER_BY_DISTRIBUTION, REMOVE_FILTER_BY_STATUS, REMOVE_FILTER_BY_DISTRIBUTION, FILTER_BY_TARGET_FILTER_QUERY, REMOVE_FILTER_BY_TARGET_FILTER_QUERY
//
FILTER_BY_TEXT,
//
FILTER_BY_TAG,
//
REMOVE_FILTER_BY_TEXT,
//
REMOVE_FILTER_BY_TAG,
//
FILTER_BY_STATUS,
//
FILTER_BY_DISTRIBUTION,
//
REMOVE_FILTER_BY_STATUS,
//
REMOVE_FILTER_BY_DISTRIBUTION,
//
FILTER_BY_TARGET_FILTER_QUERY,
//
REMOVE_FILTER_BY_TARGET_FILTER_QUERY
}

View File

@@ -141,6 +141,7 @@ public class CountMessageLabel extends Label {
}
message.append(HawkbitCommonUtil.SP_STRING_PIPE);
final String status = i18n.get("label.filter.status");
final String overdue = i18n.get("label.filter.overdue");
final String tags = i18n.get("label.filter.tags");
final String text = i18n.get("label.filter.text");
final String dists = i18n.get("label.filter.dist");
@@ -148,6 +149,7 @@ public class CountMessageLabel extends Label {
final StringBuilder filterMesgBuf = new StringBuilder(i18n.get("label.filter"));
filterMesgBuf.append(HawkbitCommonUtil.SP_STRING_SPACE);
filterMesgBuf.append(getStatusMsg(targFilParams.getClickedStatusTargetTags(), status));
filterMesgBuf.append(getOverdueStateMsg(targFilParams.isOverdueFilterEnabled(), overdue));
filterMesgBuf
.append(getTagsMsg(targFilParams.isNoTagSelected(), targFilParams.getClickedTargetTags(), tags));
filterMesgBuf.append(
@@ -220,6 +222,17 @@ public class CountMessageLabel extends Label {
return status.isEmpty() ? HawkbitCommonUtil.SP_STRING_SPACE : param;
}
/**
* Get Overdue State Message.
*
* @param overdueState
* as flag
* @return String as msg.
*/
private static String getOverdueStateMsg(final boolean overdueState, final String param) {
return !overdueState ? HawkbitCommonUtil.SP_STRING_SPACE : param;
}
/**
* Get Tags Message.
*

View File

@@ -31,6 +31,8 @@ public class TargetTableFilters implements Serializable {
private final List<String> clickedTargetTags = new ArrayList<>();
private final List<TargetUpdateStatus> clickedStatusTargetTags = new ArrayList<>();
private boolean isOverdueFilterEnabled = Boolean.FALSE;
private String searchText;
private DistributionSetIdName distributionSet;
private Long pinnedDistId;
@@ -130,7 +132,8 @@ public class TargetTableFilters implements Serializable {
* {@code false}
*/
public boolean hasFilter() {
return isFilteredByTextOrDs() || hasTagsSelected() || isFilteredByStatusOrCustomFilter();
return isFilteredByTextOrDs() || hasTagsSelected() || isFilteredByStatusOrCustomFilter()
|| isOverdueFilterEnabled();
}
private boolean hasTagsSelected() {
@@ -160,4 +163,12 @@ public class TargetTableFilters implements Serializable {
this.targetFilterQuery = targetFilterQuery;
}
public boolean isOverdueFilterEnabled() {
return isOverdueFilterEnabled;
}
public void setOverdueFilterEnabled(boolean isOverdueFilterEnabled) {
this.isOverdueFilterEnabled = isOverdueFilterEnabled;
}
}

View File

@@ -8,6 +8,7 @@
*/
package org.eclipse.hawkbit.ui.management.targettable;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.apache.commons.lang3.ArrayUtils.isEmpty;
import static org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil.isNotNullOrEmpty;
import static org.eclipse.hawkbit.ui.utils.SPUIDefinitions.TARGET_TABLE_CREATE_AT_SORT_ORDER;
@@ -19,6 +20,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.eclipse.hawkbit.repository.FilterParams;
import org.apache.commons.collections4.CollectionUtils;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.TargetManagement;
@@ -52,6 +54,7 @@ public class TargetBeanQuery extends AbstractBeanQuery<ProxyTarget> {
private Sort sort = new Sort(TARGET_TABLE_CREATE_AT_SORT_ORDER, "createdAt");
private transient Collection<TargetUpdateStatus> status;
private transient Boolean overdueState;
private String[] targetTags;
private Long distributionId;
private String searchText;
@@ -81,6 +84,7 @@ public class TargetBeanQuery extends AbstractBeanQuery<ProxyTarget> {
if (isNotNullOrEmpty(queryConfig)) {
status = (Collection<TargetUpdateStatus>) queryConfig.get(SPUIDefinitions.FILTER_BY_STATUS);
overdueState = (Boolean) queryConfig.get(SPUIDefinitions.FILTER_BY_OVERDUE_STATE);
targetTags = (String[]) queryConfig.get(SPUIDefinitions.FILTER_BY_TAG);
noTagClicked = (Boolean) queryConfig.get(SPUIDefinitions.FILTER_BY_NO_TAG);
distributionId = (Long) queryConfig.get(SPUIDefinitions.FILTER_BY_DISTRIBUTION);
@@ -114,17 +118,17 @@ public class TargetBeanQuery extends AbstractBeanQuery<ProxyTarget> {
if (pinnedDistId != null) {
targetBeans = getTargetManagement().findTargetsAllOrderByLinkedDistributionSet(
new OffsetBasedPageRequest(startIndex, SPUIDefinitions.PAGE_SIZE, sort), pinnedDistId,
distributionId, status, searchText, noTagClicked, targetTags);
new FilterParams(distributionId, status, overdueState, searchText, noTagClicked, targetTags));
} else if (null != targetFilterQuery) {
targetBeans = getTargetManagement().findTargetsAll(targetFilterQuery,
new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort));
} else if (!anyFilterSelected()) {
} else if (!isAnyFilterSelected()) {
targetBeans = getTargetManagement().findTargetsAll(
new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort));
} else {
targetBeans = getTargetManagement().findTargetByFilters(
new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort), status,
searchText, distributionId, noTagClicked, targetTags);
overdueState, searchText, distributionId, noTagClicked, targetTags);
}
for (final Target targ : targetBeans) {
final ProxyTarget prxyTarget = new ProxyTarget();
@@ -170,31 +174,27 @@ public class TargetBeanQuery extends AbstractBeanQuery<ProxyTarget> {
return true;
}
private boolean isOverdueFilterEnabled() {
return Boolean.TRUE.equals(overdueState);
}
@Override
protected void saveBeans(final List<ProxyTarget> addedTargets, final List<ProxyTarget> modifiedTargets,
final List<ProxyTarget> removedTargets) {
// CRUD operations on Target will be done through repository methods
}
private Boolean anyFilterSelected() {
if (CollectionUtils.isEmpty(status) && distributionId == null && Strings.isNullOrEmpty(searchText)
&& !isTagSelected()) {
return false;
}
return true;
}
@Override
public int size() {
final long totSize = getTargetManagement().countTargetsAll();
long size;
if (null != targetFilterQuery) {
size = getTargetManagement().countTargetByTargetFilterQuery(targetFilterQuery);
} else if (!anyFilterSelected()) {
} else if (!isAnyFilterSelected()) {
size = totSize;
} else {
size = getTargetManagement().countTargetByFilters(status, searchText, distributionId, noTagClicked,
targetTags);
size = getTargetManagement().countTargetByFilters(status, overdueState, searchText, distributionId,
noTagClicked, targetTags);
}
final ManagementUIState tmpManagementUIState = getManagementUIState();
@@ -209,6 +209,11 @@ public class TargetBeanQuery extends AbstractBeanQuery<ProxyTarget> {
return (int) size;
}
private boolean isAnyFilterSelected() {
final boolean isFilterSelected = isTagSelected() || isOverdueFilterEnabled();
return isFilterSelected || CollectionUtils.isNotEmpty(status) || distributionId != null || !isNullOrEmpty(searchText);
}
private TargetManagement getTargetManagement() {
if (targetManagement == null) {
targetManagement = SpringContextHelper.getBean(TargetManagement.class);

View File

@@ -27,6 +27,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.collections4.CollectionUtils;
import org.eclipse.hawkbit.repository.FilterParams;
import org.eclipse.hawkbit.repository.SpPermissionChecker;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.eventbus.event.TargetDeletedEvent;
@@ -411,6 +412,9 @@ public class TargetTable extends AbstractTable<Target, TargetIdName> {
.getClickedStatusTargetTags();
queryConfig.put(SPUIDefinitions.FILTER_BY_STATUS, statusList);
}
if (managementUIState.getTargetTableFilters().isOverdueFilterEnabled()) {
queryConfig.put(SPUIDefinitions.FILTER_BY_OVERDUE_STATE, Boolean.TRUE);
}
return queryConfig;
}
@@ -461,7 +465,7 @@ public class TargetTable extends AbstractTable<Target, TargetIdName> {
/**
* Add listener to pin.
*
* @param pinBtn
* @param event
* as event
*/
private void addPinClickListener(final ClickEvent event) {
@@ -843,6 +847,7 @@ public class TargetTable extends AbstractTable<Target, TargetIdName> {
managementUIState.setTargetsCountAll(totalTargetsCount);
Collection<TargetUpdateStatus> status = null;
Boolean overdueState = null;
String[] targetTags = null;
Long distributionId = null;
String searchText = null;
@@ -854,6 +859,9 @@ public class TargetTable extends AbstractTable<Target, TargetIdName> {
if (isFilteredByStatus()) {
status = managementUIState.getTargetTableFilters().getClickedStatusTargetTags();
}
if (managementUIState.getTargetTableFilters().isOverdueFilterEnabled()) {
overdueState = managementUIState.getTargetTableFilters().isOverdueFilterEnabled();
}
if (managementUIState.getTargetTableFilters().getDistributionSet().isPresent()) {
distributionId = managementUIState.getTargetTableFilters().getDistributionSet().get().getId();
}
@@ -865,25 +873,29 @@ public class TargetTable extends AbstractTable<Target, TargetIdName> {
pinnedDistId = managementUIState.getTargetTableFilters().getPinnedDistId().get();
}
final long size = getTargetsCountWithFilter(totalTargetsCount, status, targetTags, distributionId, searchText,
noTagClicked, pinnedDistId);
final long size = getTargetsCountWithFilter(totalTargetsCount, pinnedDistId,
new FilterParams(distributionId, status, overdueState, searchText, noTagClicked, targetTags));
if (size > SPUIDefinitions.MAX_TABLE_ENTRIES) {
managementUIState.setTargetsTruncated(size - SPUIDefinitions.MAX_TABLE_ENTRIES);
}
}
private long getTargetsCountWithFilter(final long totalTargetsCount, final Collection<TargetUpdateStatus> status,
final String[] targetTags, final Long distributionId, final String searchText, final Boolean noTagClicked,
final Long pinnedDistId) {
private long getTargetsCountWithFilter(final long totalTargetsCount,
final Long pinnedDistId, final FilterParams filterParams) {
final long size;
if (managementUIState.getTargetTableFilters().getTargetFilterQuery().isPresent()) {
size = targetManagement.countTargetByTargetFilterQuery(
managementUIState.getTargetTableFilters().getTargetFilterQuery().get());
} else if (noFilterSelected(status, pinnedDistId, noTagClicked, targetTags, searchText)) {
} else if (noFilterSelected(filterParams.getFilterByStatus(), pinnedDistId,
filterParams.getSelectTargetWithNoTag(), filterParams.getFilterByTagNames(),
filterParams.getFilterBySearchText())) {
size = totalTargetsCount;
} else {
size = targetManagement.countTargetByFilters(status, searchText, distributionId, noTagClicked, targetTags);
size = targetManagement.countTargetByFilters(filterParams.getFilterByStatus(),
filterParams.getOverdueState(), filterParams.getFilterBySearchText(),
filterParams.getFilterByDistributionId(), filterParams.getSelectTargetWithNoTag(),
filterParams.getFilterByTagNames());
}
return size;
}

View File

@@ -54,16 +54,20 @@ public class FilterByStatusLayout extends VerticalLayout implements Button.Click
@Autowired
private ManagementUIState managementUIState;
private static final String OVERDUE_CAPTION = "overdue";
private Button unknown;
private Button inSync;
private Button pending;
private Button error;
private Button registered;
private Button overdue;
private Boolean unknownBtnClicked = false;
private Boolean errorBtnClicked = false;
private Boolean pendingBtnClicked = false;
private Boolean inSyncBtnClicked = false;
private Boolean registeredBtnClicked = false;
private Boolean overdueBtnClicked = false;
private Button buttonClicked;
private static final String BTN_CLICKED = "btnClicked";
@@ -103,7 +107,17 @@ public class FilterByStatusLayout extends VerticalLayout implements Button.Click
buttonLayout.addComponent(registered);
buttonLayout.setComponentAlignment(registered, Alignment.MIDDLE_CENTER);
addComponent(buttonLayout);
setComponentAlignment(buttonLayout, Alignment.MIDDLE_CENTER);
setComponentAlignment(buttonLayout, Alignment.MIDDLE_LEFT);
final HorizontalLayout overdueLayout = new HorizontalLayout();
final Label overdueLabel = new LabelBuilder().name(i18n.get("label.filter.by.overdue")).buildLabel();
overdueLayout.setStyleName("overdue-button-layout");
overdueLayout.addComponent(overdue);
overdueLayout.setComponentAlignment(overdue, Alignment.MIDDLE_LEFT);
overdueLayout.addComponent(overdueLabel);
overdueLayout.setComponentAlignment(overdueLabel, Alignment.MIDDLE_LEFT);
addComponent(overdueLayout);
setComponentAlignment(overdueLayout, Alignment.MIDDLE_LEFT);
}
@@ -129,6 +143,10 @@ public class FilterByStatusLayout extends VerticalLayout implements Button.Click
}
}
}
if (managementUIState.getTargetTableFilters().isOverdueFilterEnabled()) {
overdue.addStyleName(BTN_CLICKED);
overdueBtnClicked = Boolean.TRUE;
}
}
/**
@@ -150,18 +168,23 @@ public class FilterByStatusLayout extends VerticalLayout implements Button.Click
registered = SPUIComponentProvider.getButton(UIComponentIdProvider.REGISTERED_STATUS_ICON,
TargetUpdateStatus.REGISTERED.toString(), i18n.get("tooltip.status.registered"),
SPUIButtonDefinitions.SP_BUTTON_STATUS_STYLE, false, FontAwesome.SQUARE, SPUIButtonStyleSmall.class);
overdue = SPUIComponentProvider.getButton(UIComponentIdProvider.OVERDUE_STATUS_ICON,
OVERDUE_CAPTION, i18n.get("tooltip.status.overdue"),
SPUIButtonDefinitions.SP_BUTTON_STATUS_STYLE, false, FontAwesome.SQUARE, SPUIButtonStyleSmall.class);
applyStatusBtnStyle();
unknown.setData("filterStatusOne");
inSync.setData("filterStatusTwo");
pending.setData("filterStatusThree");
error.setData("filterStatusFour");
registered.setData("filterStatusFive");
overdue.setData("filterStatusSix");
unknown.addClickListener(this);
inSync.addClickListener(this);
pending.addClickListener(this);
error.addClickListener(this);
registered.addClickListener(this);
overdue.addClickListener(this);
}
/**
@@ -173,6 +196,7 @@ public class FilterByStatusLayout extends VerticalLayout implements Button.Click
pending.addStyleName("pendingBtn");
error.addStyleName("errorBtn");
registered.addStyleName("registeredBtn");
overdue.addStyleName("overdueBtn");
}
@Override
@@ -188,6 +212,8 @@ public class FilterByStatusLayout extends VerticalLayout implements Button.Click
processErrorFilterStatus();
} else if (event.getButton().getCaption().equalsIgnoreCase(TargetUpdateStatus.REGISTERED.toString())) {
processRegisteredFilterStatus();
} else if (event.getButton().getCaption().equalsIgnoreCase(OVERDUE_CAPTION)) {
processOverdueFilterStatus();
}
}
@@ -231,9 +257,26 @@ public class FilterByStatusLayout extends VerticalLayout implements Button.Click
processCommonFilterStatus(TargetUpdateStatus.REGISTERED, registeredBtnClicked);
}
/**
* Process - OVERDUE.
*/
private void processOverdueFilterStatus() {
overdueBtnClicked = !overdueBtnClicked;
managementUIState.getTargetTableFilters().setOverdueFilterEnabled(overdueBtnClicked);
if (overdueBtnClicked) {
buttonClicked.addStyleName(BTN_CLICKED);
eventBus.publish(this, TargetFilterEvent.FILTER_BY_STATUS);
} else {
buttonClicked.removeStyleName(BTN_CLICKED);
eventBus.publish(this, TargetFilterEvent.REMOVE_FILTER_BY_STATUS);
}
}
/**
* Process - COMMON PROCESS.
*
*
* @param status
* as enum
* @param buttonReset
@@ -268,11 +311,12 @@ public class FilterByStatusLayout extends VerticalLayout implements Button.Click
inSync.removeStyleName(BTN_CLICKED);
error.removeStyleName(BTN_CLICKED);
pending.removeStyleName(BTN_CLICKED);
overdue.removeStyleName(BTN_CLICKED);
}
/**
* Check if any status button in clicked.
*
*
* @return
*/
private boolean isStatusFilterApplied() {

View File

@@ -205,6 +205,12 @@ public final class SPUIDefinitions {
* Filter by status key.
*/
public static final String FILTER_BY_STATUS = "FilterByStatus";
/**
* Filter by overdue state key.
*/
public static final String FILTER_BY_OVERDUE_STATE = "FilterByOverdueState";
/**
* Filter by tag key.
*/
@@ -505,7 +511,7 @@ public final class SPUIDefinitions {
/**
* Get the locales
*
*
* @return the availableLocales
*/
public static Set<String> getAvailableLocales() {

View File

@@ -189,6 +189,10 @@ public final class UIComponentIdProvider {
* REGISTERED_STATUS_ICON ID.
*/
public static final String REGISTERED_STATUS_ICON = "registered.status.icon";
/**
* OVERDUE_STATUS_ICON ID.
*/
public static final String OVERDUE_STATUS_ICON = "overdue.status.icon";
/**
* DROP filter icon id.
*/

View File

@@ -13,8 +13,11 @@
$unknown-color: $status-unknown-color;
$in-sync-color: $signal-green-color;
$pending-color: $signal-yellow-color;
$error-color: $signal-red-color;
$registered-color: $bosch-color-light-blue;
$error-color: $signal-red-color;
$registered-color: $signal-light-blue-color;
//Overdue filter button color
$overdue-color: $signal-dark-blue-color;
//Filter by status buttons group alignment
.target-status-filters {
@@ -27,6 +30,10 @@
.status-button-layout {
height: 40px !important;
}
.overdue-button-layout {
height: 40px !important;
}
//Filter Button applied with below styles based on the color
.unknownBtn {
@@ -68,6 +75,16 @@
.errorBtn:active:after {
border-color: $error-color;
}
.overdueBtn {
background: $overdue-color;
border: solid 3px $overdue-color;
}
.overdueBtn:focus:after,
.overdueBtn:active:after {
border-color: $overdue-color;
}
.registeredBtn {
background: $registered-color;

View File

@@ -101,10 +101,13 @@ $details-tab-caption-font-scale: .8;
//Generic text style
$generic-text-font-scale: .85;
$status-unknown-color: #3085cb;
$status-unknown-color: #d5d5d5;
$signal-green-color: #6eb553;
$signal-yellow-color: #ff0;
$signal-light-blue-color: #3085cb;
$signal-dark-blue-color: #26547a;
$signal-red-color: #f00;
$signal-black-color: #000;
$signal-orange-color: #ffa500;
$grey-light: #d5d5d5;

View File

@@ -178,7 +178,7 @@
}
.statusIconLightBlue {
color: $bosch-color-light-blue;
color: $signal-light-blue-color;
}
.v-button-statusIconLightBlue:after {
@@ -219,7 +219,7 @@
pointer-events: auto !important;
}
.blueSpinner{
@include valo-spinner($size: $v-font-size--small,$color: $bosch-color-light-blue);
@include valo-spinner($size: $v-font-size--small,$color: $signal-light-blue-color);
pointer-events: auto !important;
}

View File

@@ -9,9 +9,9 @@
@mixin target-filter-query {
.gwt-MenuBar-autocomplete {
cursor: default;
cursor: default;
}
.gwt-MenuBar-autocomplete .gwt-MenuItem{
border-radius: 3px !important;
cursor: pointer !important;
@@ -21,7 +21,7 @@
position: relative !important;
white-space: nowrap !important;
}
.gwt-MenuBar-autocomplete .gwt-MenuItem-selected {
background-color: $hawkbit-primary-color;
background-image: linear-gradient(to bottom, #1b87e3 2%, #166ed5 98%) !important;

View File

@@ -148,6 +148,7 @@ label.filter.selected = Selected :
label.filter.shown = Shown :
label.filter.targets = Filtered Targets :
label.filter.status = Status,
label.filter.overdue = Overdue,
label.filter.tags = Tags,
label.filter.text = Search Text
label.filter.dist = Distribution,
@@ -168,6 +169,7 @@ label.target.id = Controller Id :
label.target.ip = Controller IP :
label.target.security.token = Security token :
label.filter.by.status = Filter by Status
label.filter.by.overdue = Filter by Overdue
label.target.controller.attrs = <b>Controller attributes</b>
label.target.lastpolldate = Last poll :
label.tag.name = Tag name
@@ -203,6 +205,7 @@ tooltip.status.registered = Registered
tooltip.status.pending = Pending
tooltip.status.error = Error
tooltip.status.insync = In-sync
tooltip.status.overdue = Overdue
tooltip.delete.module = Select and delete Software Module
tooltip.forced.item=Forced update action
tooltip.soft.item=Soft update action which interacts with an user as a attempt update action for example

14
pom.xml
View File

@@ -133,7 +133,7 @@
<org.easytesting.version>2.0M10</org.easytesting.version>
<allure.version>1.4.22</allure.version>
<eclipselink.version>2.6.2</eclipselink.version>
<org.powermock.version>1.5.4</org.powermock.version>
<org.powermock.version>1.6.5</org.powermock.version>
<pl.pragmatists.version>1.0.2</pl.pragmatists.version>
<guava.version>19.0</guava.version>
<mariadb-java-client.version>1.5.3</mariadb-java-client.version>
@@ -675,6 +675,18 @@
<version>${pl.pragmatists.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${org.powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${org.powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>