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; package org.eclipse.hawkbit.autoconfigure.repository;
import org.eclipse.hawkbit.EnableJpaRepository; 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.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.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
/** /**
* Auto-Configuration for enabling the REST-Resources. * Auto-Configuration for enabling JPA repository.
* *
*/ */
@Configuration @Configuration
@@ -22,4 +26,14 @@ import org.springframework.context.annotation.Import;
@Import({ EnableJpaRepository.class }) @Import({ EnableJpaRepository.class })
public class JpaRepositoryAutoConfiguration { 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. * Count {@link Target}s for all the given filter parameters.
* *
* @param status * @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. * Set to <code>null</code> in case this is not required.
* @param searchText * @param searchText
* to find targets having the text anywhere in name or * to find targets having the text anywhere in name or
@@ -86,7 +90,7 @@ public interface TargetManagement {
* @return the found number {@link Target}s * @return the found number {@link Target}s
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @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); Long installedOrAssignedDistributionSetId, Boolean selectTargetWithNoTag, String... tagNames);
/** /**
@@ -212,10 +216,12 @@ public interface TargetManagement {
* *
* @param pageRequest * @param pageRequest
* the pageRequest to enhance the query for paging and sorting * the pageRequest to enhance the query for paging and sorting
*
* @param filterByStatus * @param filterByStatus
* find targets having this {@link TargetUpdateStatus}s. Set to * find targets having this {@link TargetUpdateStatus}s. Set to
* <code>null</code> in case this is not required. * <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 * @param filterBySearchText
* to find targets having the text anywhere in name or * to find targets having the text anywhere in name or
* description. Set <code>null</code> in case this is not * description. Set <code>null</code> in case this is not
@@ -234,7 +240,7 @@ public interface TargetManagement {
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
List<TargetIdName> findAllTargetIdsByFilters(@NotNull Pageable pageRequest, List<TargetIdName> findAllTargetIdsByFilters(@NotNull Pageable pageRequest,
Collection<TargetUpdateStatus> filterByStatus, String filterBySearchText, Collection<TargetUpdateStatus> filterByStatus, Boolean overdueState, String filterBySearchText,
Long installedOrAssignedDistributionSetId, Boolean selectTargetWithNoTag, String... filterByTagNames); Long installedOrAssignedDistributionSetId, Boolean selectTargetWithNoTag, String... filterByTagNames);
/** /**
@@ -314,7 +320,7 @@ public interface TargetManagement {
* given {@code fieldNameProvider} * given {@code fieldNameProvider}
* @throws RSQLParameterSyntaxException * @throws RSQLParameterSyntaxException
* if the RSQL syntax is wrong * if the RSQL syntax is wrong
* *
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET)
Page<Target> findTargetByAssignedDistributionSet(@NotNull Long distributionSetID, @NotNull String rsqlParam, Page<Target> findTargetByAssignedDistributionSet(@NotNull Long distributionSetID, @NotNull String rsqlParam,
@@ -368,6 +374,9 @@ public interface TargetManagement {
* @param status * @param status
* find targets having this {@link TargetUpdateStatus}s. Set to * find targets having this {@link TargetUpdateStatus}s. Set to
* <code>null</code> in case this is not required. * <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 * @param searchText
* to find targets having the text anywhere in name or * to find targets having the text anywhere in name or
* description. Set <code>null</code> in case this is not * description. Set <code>null</code> in case this is not
@@ -386,7 +395,7 @@ public interface TargetManagement {
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Slice<Target> findTargetByFilters(@NotNull Pageable pageable, Collection<TargetUpdateStatus> status, 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); String... tagNames);
/** /**
@@ -415,7 +424,7 @@ public interface TargetManagement {
* @param pageable * @param pageable
* page parameter * page parameter
* @return the found {@link Target}s, never {@code null} * @return the found {@link Target}s, never {@code null}
* *
* @throws RSQLParameterUnsupportedFieldException * @throws RSQLParameterUnsupportedFieldException
* if a field in the RSQL string is used but not provided by the * if a field in the RSQL string is used but not provided by the
* given {@code fieldNameProvider} * given {@code fieldNameProvider}
@@ -460,9 +469,9 @@ public interface TargetManagement {
* in string notation * in string notation
* @param pageable * @param pageable
* pagination parameter * pagination parameter
* *
* @return the found {@link Target}s, never {@code null} * @return the found {@link Target}s, never {@code null}
* *
* @throws RSQLParameterUnsupportedFieldException * @throws RSQLParameterUnsupportedFieldException
* if a field in the RSQL string is used but not provided by the * if a field in the RSQL string is used but not provided by the
* given {@code fieldNameProvider} * given {@code fieldNameProvider}
@@ -481,9 +490,9 @@ public interface TargetManagement {
* the specification for the query * the specification for the query
* @param pageable * @param pageable
* pagination parameter * pagination parameter
* *
* @return the found {@link Target}s, never {@code null} * @return the found {@link Target}s, never {@code null}
* *
* @throws RSQLParameterUnsupportedFieldException * @throws RSQLParameterUnsupportedFieldException
* if a field in the RSQL string is used but not provided by the * if a field in the RSQL string is used but not provided by the
* given {@code fieldNameProvider} * given {@code fieldNameProvider}
@@ -511,33 +520,15 @@ public interface TargetManagement {
* the page request to page the result set * the page request to page the result set
* @param orderByDistributionId * @param orderByDistributionId
* {@link DistributionSet#getId()} to be ordered by * {@link DistributionSet#getId()} to be ordered by
* @param filterByDistributionId * @param filterParams
* {@link DistributionSet#getId()} to be filter the result. Set * the filters to apply; only filters are enabled that have
* to <code>null</code> in case this is not required. * non-null value; filters are AND-gated
* @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
* @return a paged result {@link Page} of the {@link Target}s in a defined * @return a paged result {@link Page} of the {@link Target}s in a defined
* order. * order.
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Slice<Target> findTargetsAllOrderByLinkedDistributionSet(@NotNull Pageable pageable, Slice<Target> findTargetsAllOrderByLinkedDistributionSet(@NotNull Pageable pageable,
@NotNull Long orderByDistributionId, Long filterByDistributionId, @NotNull Long orderByDistributionId, FilterParams filterParams);
Collection<TargetUpdateStatus> filterByStatus, String filterBySearchText, Boolean selectTargetWithNoTag,
String... filterByTagNames);
/** /**
* retrieves a list of {@link Target}s by their controller ID with details, * 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> <artifactId>fest-assert</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </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> </dependencies>
<build> <build>

View File

@@ -172,7 +172,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
} }
/** /**
* *
* @return the singleton instance of the * @return the singleton instance of the
* {@link AfterTransactionCommitExecutorHolder} * {@link AfterTransactionCommitExecutorHolder}
*/ */
@@ -234,7 +234,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* {@link JpaSystemManagement} bean. * {@link JpaSystemManagement} bean.
* *
* @return a new {@link SystemManagement} * @return a new {@link SystemManagement}
*/ */
@Bean @Bean
@@ -245,7 +245,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* {@link JpaReportManagement} bean. * {@link JpaReportManagement} bean.
* *
* @return a new {@link ReportManagement} * @return a new {@link ReportManagement}
*/ */
@Bean @Bean
@@ -256,7 +256,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* {@link JpaDistributionSetManagement} bean. * {@link JpaDistributionSetManagement} bean.
* *
* @return a new {@link DistributionSetManagement} * @return a new {@link DistributionSetManagement}
*/ */
@Bean @Bean
@@ -267,7 +267,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* {@link JpaTenantStatsManagement} bean. * {@link JpaTenantStatsManagement} bean.
* *
* @return a new {@link TenantStatsManagement} * @return a new {@link TenantStatsManagement}
*/ */
@Bean @Bean
@@ -280,7 +280,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* {@link JpaTenantConfigurationManagement} bean. * {@link JpaTenantConfigurationManagement} bean.
* *
* @return a new {@link TenantConfigurationManagement} * @return a new {@link TenantConfigurationManagement}
*/ */
@Bean @Bean
@@ -291,7 +291,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* {@link JpaTenantConfigurationManagement} bean. * {@link JpaTenantConfigurationManagement} bean.
* *
* @return a new {@link TenantConfigurationManagement} * @return a new {@link TenantConfigurationManagement}
*/ */
@Bean @Bean
@@ -302,7 +302,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* {@link JpaTargetFilterQueryManagement} bean. * {@link JpaTargetFilterQueryManagement} bean.
* *
* @return a new {@link TargetFilterQueryManagement} * @return a new {@link TargetFilterQueryManagement}
*/ */
@Bean @Bean
@@ -313,7 +313,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* {@link JpaTagManagement} bean. * {@link JpaTagManagement} bean.
* *
* @return a new {@link TagManagement} * @return a new {@link TagManagement}
*/ */
@Bean @Bean
@@ -324,7 +324,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* {@link JpaSoftwareManagement} bean. * {@link JpaSoftwareManagement} bean.
* *
* @return a new {@link SoftwareManagement} * @return a new {@link SoftwareManagement}
*/ */
@Bean @Bean
@@ -335,7 +335,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* {@link JpaRolloutManagement} bean. * {@link JpaRolloutManagement} bean.
* *
* @return a new {@link RolloutManagement} * @return a new {@link RolloutManagement}
*/ */
@Bean @Bean
@@ -346,7 +346,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* {@link JpaRolloutGroupManagement} bean. * {@link JpaRolloutGroupManagement} bean.
* *
* @return a new {@link RolloutGroupManagement} * @return a new {@link RolloutGroupManagement}
*/ */
@Bean @Bean
@@ -357,7 +357,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* {@link JpaDeploymentManagement} bean. * {@link JpaDeploymentManagement} bean.
* *
* @return a new {@link DeploymentManagement} * @return a new {@link DeploymentManagement}
*/ */
@Bean @Bean
@@ -368,7 +368,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* {@link JpaControllerManagement} bean. * {@link JpaControllerManagement} bean.
* *
* @return a new {@link ControllerManagement} * @return a new {@link ControllerManagement}
*/ */
@Bean @Bean
@@ -379,7 +379,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* {@link JpaArtifactManagement} bean. * {@link JpaArtifactManagement} bean.
* *
* @return a new {@link ArtifactManagement} * @return a new {@link ArtifactManagement}
*/ */
@@ -391,7 +391,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* {@link JpaEntityFactory} bean. * {@link JpaEntityFactory} bean.
* *
* @return a new {@link EntityFactory} * @return a new {@link EntityFactory}
*/ */
@Bean @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.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; 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.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType; import org.eclipse.hawkbit.repository.model.Action.ActionType;
@@ -135,6 +136,9 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Autowired @Autowired
private SystemSecurityContext systemSecurityContext; private SystemSecurityContext systemSecurityContext;
@Autowired
private VirtualPropertyReplacer virtualPropertyReplacer;
@Override @Override
@Transactional(isolation = Isolation.READ_COMMITTED) @Transactional(isolation = Isolation.READ_COMMITTED)
@Modifying @Modifying
@@ -607,8 +611,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
return convertAcPage(actions, pageable); return convertAcPage(actions, pageable);
} }
private static Specification<JpaAction> createSpecificationFor(final Target target, final String rsqlParam) { private Specification<JpaAction> createSpecificationFor(final Target target, final String rsqlParam) {
final Specification<JpaAction> spec = RSQLUtility.parse(rsqlParam, ActionFields.class); final Specification<JpaAction> spec = RSQLUtility.parse(rsqlParam, ActionFields.class, virtualPropertyReplacer);
return (root, query, cb) -> cb.and(spec.toPredicate(root, query, cb), return (root, query, cb) -> cb.and(spec.toPredicate(root, query, cb),
cb.equal(root.get(JpaAction_.target), target)); 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.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; 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.DistributionSetSpecification;
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetTypeSpecification; import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetTypeSpecification;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder; import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
@@ -110,6 +111,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Autowired @Autowired
private TenantAware tenantAware; private TenantAware tenantAware;
@Autowired
private VirtualPropertyReplacer virtualPropertyReplacer;
@Override @Override
public DistributionSet findDistributionSetByIdWithDetails(final Long distid) { public DistributionSet findDistributionSetByIdWithDetails(final Long distid) {
return distributionSetRepository.findOne(DistributionSetSpecification.byId(distid)); return distributionSetRepository.findOne(DistributionSetSpecification.byId(distid));
@@ -286,7 +290,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
public Page<DistributionSetType> findDistributionSetTypesAll(final String rsqlParam, final Pageable pageable) { public Page<DistributionSetType> findDistributionSetTypesAll(final String rsqlParam, final Pageable pageable) {
final Specification<JpaDistributionSetType> spec = RSQLUtility.parse(rsqlParam, final Specification<JpaDistributionSetType> spec = RSQLUtility.parse(rsqlParam,
DistributionSetTypeFields.class); DistributionSetTypeFields.class, virtualPropertyReplacer);
return convertDsTPage(distributionSetTypeRepository.findAll(spec, pageable)); 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, public Page<DistributionSet> findDistributionSetsAll(final String rsqlParam, final Pageable pageReq,
final Boolean deleted) { 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); final List<Specification<JpaDistributionSet>> specList = new ArrayList<>(2);
if (deleted != null) { 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 * Method to get the latest distribution set based on ds ID after the
* metadata changes for that distribution set. * metadata changes for that distribution set.
* *
* @param distributionSet * @param distributionSet
* Distribution set * Distribution set
*/ */
@@ -564,7 +569,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
final String rsqlParam, final Pageable pageable) { final String rsqlParam, final Pageable pageable) {
final Specification<JpaDistributionSetMetadata> spec = RSQLUtility.parse(rsqlParam, final Specification<JpaDistributionSetMetadata> spec = RSQLUtility.parse(rsqlParam,
DistributionSetMetadataFields.class); DistributionSetMetadataFields.class, virtualPropertyReplacer);
return convertMdPage( return convertMdPage(
distributionSetMetadataRepository 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.model.RolloutTargetGroup_; import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup_;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; 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;
import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus; import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
@@ -70,6 +71,9 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
@Autowired @Autowired
private EntityManager entityManager; private EntityManager entityManager;
@Autowired
private VirtualPropertyReplacer virtualPropertyReplacer;
@Override @Override
public RolloutGroup findRolloutGroupById(final Long rolloutGroupId) { public RolloutGroup findRolloutGroupById(final Long rolloutGroupId) {
return rolloutGroupRepository.findOne(rolloutGroupId); return rolloutGroupRepository.findOne(rolloutGroupId);
@@ -92,7 +96,8 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
public Page<RolloutGroup> findRolloutGroupsAll(final Rollout rollout, final String rsqlParam, public Page<RolloutGroup> findRolloutGroupsAll(final Rollout rollout, final String rsqlParam,
final Pageable pageable) { final Pageable pageable) {
final Specification<JpaRolloutGroup> specification = RSQLUtility.parse(rsqlParam, RolloutGroupFields.class); final Specification<JpaRolloutGroup> specification = RSQLUtility.parse(rsqlParam, RolloutGroupFields.class,
virtualPropertyReplacer);
return convertPage( return convertPage(
rolloutGroupRepository rolloutGroupRepository
@@ -145,7 +150,8 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
public Page<Target> findRolloutGroupTargets(final RolloutGroup rolloutGroup, final String rsqlParam, public Page<Target> findRolloutGroupTargets(final RolloutGroup rolloutGroup, final String rsqlParam,
final Pageable pageable) { 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) -> { return convertTPage(targetRepository.findAll((root, query, criteriaBuilder) -> {
final ListJoin<JpaTarget, RolloutTargetGroup> rolloutTargetJoin = root.join(JpaTarget_.rolloutTargetGroup); 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.RolloutGroupActionEvaluator;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupConditionEvaluator; import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupConditionEvaluator;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; 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;
import org.eclipse.hawkbit.repository.model.Action.ActionType; import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -114,6 +115,9 @@ public class JpaRolloutManagement implements RolloutManagement {
@Autowired @Autowired
private CacheWriteNotify cacheWriteNotify; private CacheWriteNotify cacheWriteNotify;
@Autowired
private VirtualPropertyReplacer virtualPropertyReplacer;
@Autowired @Autowired
@Qualifier("asyncExecutor") @Qualifier("asyncExecutor")
private Executor executor; private Executor executor;
@@ -150,7 +154,8 @@ public class JpaRolloutManagement implements RolloutManagement {
@Override @Override
public Page<Rollout> findAllWithDetailedStatusByPredicate(final String rsqlParam, final Pageable pageable) { 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); final Page<JpaRollout> findAll = rolloutRepository.findAll(specification, pageable);
setRolloutStatusDetails(findAll); 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.JpaSoftwareModule_;
import org.eclipse.hawkbit.repository.jpa.model.SwMetadataCompositeKey; import org.eclipse.hawkbit.repository.jpa.model.SwMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; 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.SoftwareModuleSpecification;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder; import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.model.AssignedSoftwareModule; import org.eclipse.hawkbit.repository.model.AssignedSoftwareModule;
@@ -106,6 +107,9 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Autowired @Autowired
private ArtifactManagement artifactManagement; private ArtifactManagement artifactManagement;
@Autowired
private VirtualPropertyReplacer virtualPropertyReplacer;
@Override @Override
@Modifying @Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED) @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@@ -312,7 +316,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override @Override
public Page<SoftwareModule> findSoftwareModulesByPredicate(final String rsqlParam, final Pageable pageable) { 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); return convertSmPage(softwareModuleRepository.findAll(spec, pageable), pageable);
} }
@@ -320,7 +325,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override @Override
public Page<SoftwareModuleType> findSoftwareModuleTypesAll(final String rsqlParam, final Pageable pageable) { 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); return convertSmTPage(softwareModuleTypeRepository.findAll(spec, pageable), pageable);
} }
@@ -606,7 +612,7 @@ public class JpaSoftwareManagement implements SoftwareManagement {
final String rsqlParam, final Pageable pageable) { final String rsqlParam, final Pageable pageable) {
final Specification<JpaSoftwareModuleMetadata> spec = RSQLUtility.parse(rsqlParam, final Specification<JpaSoftwareModuleMetadata> spec = RSQLUtility.parse(rsqlParam,
SoftwareModuleMetadataFields.class); SoftwareModuleMetadataFields.class, virtualPropertyReplacer);
return convertSmMdPage( return convertSmMdPage(
softwareModuleMetadataRepository softwareModuleMetadataRepository
.findAll( .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.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; 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.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
@@ -74,6 +75,9 @@ public class JpaTagManagement implements TagManagement {
@Autowired @Autowired
private AfterTransactionCommitExecutor afterCommit; private AfterTransactionCommitExecutor afterCommit;
@Autowired
private VirtualPropertyReplacer virtualPropertyReplacer;
@Override @Override
public TargetTag findTargetTag(final String name) { public TargetTag findTargetTag(final String name) {
return targetTagRepository.findByNameEquals(name); return targetTagRepository.findByNameEquals(name);
@@ -145,7 +149,7 @@ public class JpaTagManagement implements TagManagement {
@Override @Override
public Page<TargetTag> findAllTargetTags(final String rsqlParam, final Pageable pageable) { 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); return convertTPage(targetTagRepository.findAll(spec, pageable), pageable);
} }
@@ -276,7 +280,8 @@ public class JpaTagManagement implements TagManagement {
@Override @Override
public Page<DistributionSetTag> findAllDistributionSetTags(final String rsqlParam, final Pageable pageable) { 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); 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.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; 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.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification; import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -49,6 +50,9 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
@Autowired @Autowired
private TargetFilterQueryRepository targetFilterQueryRepository; private TargetFilterQueryRepository targetFilterQueryRepository;
@Autowired
private VirtualPropertyReplacer virtualPropertyReplacer;
@Override @Override
@Modifying @Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED) @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@@ -95,7 +99,8 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
public Page<TargetFilterQuery> findTargetFilterQueryByFilter(@NotNull Pageable pageable, String rsqlFilter) { public Page<TargetFilterQuery> findTargetFilterQueryByFilter(@NotNull Pageable pageable, String rsqlFilter) {
List<Specification<JpaTargetFilterQuery>> specList = Collections.emptyList(); List<Specification<JpaTargetFilterQuery>> specList = Collections.emptyList();
if (!Strings.isNullOrEmpty(rsqlFilter)) { 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); return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
} }
@@ -114,7 +119,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
specList.add(TargetFilterQuerySpecification.byAutoAssignDS(distributionSet)); specList.add(TargetFilterQuerySpecification.byAutoAssignDS(distributionSet));
} }
if (!Strings.isNullOrEmpty(rsqlFilter)) { 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); return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
} }
@@ -156,7 +161,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
@Override @Override
public boolean verifyTargetFilterQuerySyntax(final String query) { public boolean verifyTargetFilterQuerySyntax(final String query) {
RSQLUtility.parse(query, TargetFields.class); RSQLUtility.parse(query, TargetFields.class, virtualPropertyReplacer);
return true; return true;
} }

View File

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

View File

@@ -25,10 +25,12 @@ import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
import org.apache.commons.lang3.text.StrLookup;
import org.eclipse.hawkbit.repository.FieldNameProvider; import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.FieldValueConverter; import org.eclipse.hawkbit.repository.FieldValueConverter;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException; import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException; import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.SimpleTypeConverter; import org.springframework.beans.SimpleTypeConverter;
@@ -47,9 +49,8 @@ import cz.jirutka.rsql.parser.ast.RSQLOperators;
import cz.jirutka.rsql.parser.ast.RSQLVisitor; import cz.jirutka.rsql.parser.ast.RSQLVisitor;
/** /**
* A utility class which is able to parse RSQL strings into an spring data * A utility class which is able to parse RSQL strings into an spring data {@link Specification} which then can be
* {@link Specification} which then can be enhanced sql queries to filter * enhanced sql queries to filter entities. RSQL parser library: https://github.com/jirutka/rsql-parser
* entities. RSQL parser library: https://github.com/jirutka/rsql-parser
* *
* <ul> * <ul>
* <li>Equal to : ==</li> * <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 operator : =gt= or ></li>
* <li>Greater than or equal to : =ge= or >=</li> * <li>Greater than or equal to : =ge= or >=</li>
* </ul> * </ul>
* <p>
* Examples of RSQL expressions in both FIQL-like and alternative notation: * Examples of RSQL expressions in both FIQL-like and alternative notation:
* <ul> * <ul>
* <li>version==2.0.0</li> * <li>version==2.0.0</li>
* <li>name==targetId1;description==plugAndPlay</li> * <li>name==targetId1;description==plugAndPlay</li>
* <li>name==targetId1 and 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,description==plugAndPlay,updateStatus==UNKNOWN</li>
* <li>name==targetId1 or description==plugAndPlay or updateStatus==UNKNOWN</li> * <li>name==targetId1 or description==plugAndPlay or updateStatus==UNKNOWN</li>
* </ul> * </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 { public final class RSQLUtility {
@@ -90,6 +99,9 @@ public final class RSQLUtility {
* @param fieldNameProvider * @param fieldNameProvider
* the enum class type which implements the * the enum class type which implements the
* {@link FieldNameProvider} * {@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 * @return an specification which can be used with JPA
* @throws RSQLParameterUnsupportedFieldException * @throws RSQLParameterUnsupportedFieldException
* if a field in the RSQL string is used but not provided by the * 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 * if the RSQL syntax is wrong
*/ */
public static <A extends Enum<A> & FieldNameProvider, T> Specification<T> parse(final String rsql, public static <A extends Enum<A> & FieldNameProvider, T> Specification<T> parse(final String rsql,
final Class<A> fieldNameProvider) { final Class<A> fieldNameProvider, VirtualPropertyReplacer virtualPropertyReplacer) {
return new RSQLSpecification<>(rsql.toLowerCase(), fieldNameProvider); return new RSQLSpecification<>(rsql.toLowerCase(), fieldNameProvider, virtualPropertyReplacer);
} }
/** /**
@@ -130,10 +142,13 @@ public final class RSQLUtility {
private final String rsql; private final String rsql;
private final Class<A> enumType; 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.rsql = rsql;
this.enumType = enumType; this.enumType = enumType;
this.virtualPropertyReplacer = virtualPropertyReplacer;
} }
@Override @Override
@@ -141,7 +156,8 @@ public final class RSQLUtility {
final Node rootNode = parseRsql(rsql); 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); final List<Predicate> accept = rootNode.<List<Predicate>, String> accept(jpqQueryRSQLVisitor);
if (accept != null && !accept.isEmpty()) { if (accept != null && !accept.isEmpty()) {
@@ -171,13 +187,16 @@ public final class RSQLUtility {
private final Root<T> root; private final Root<T> root;
private final CriteriaBuilder cb; private final CriteriaBuilder cb;
private final Class<A> enumType; private final Class<A> enumType;
private final VirtualPropertyReplacer virtualPropertyReplacer;
private final SimpleTypeConverter simpleTypeConverter; 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.root = root;
this.cb = cb; this.cb = cb;
this.enumType = enumType; this.enumType = enumType;
this.virtualPropertyReplacer = virtualPropertyReplacer;
simpleTypeConverter = new SimpleTypeConverter(); simpleTypeConverter = new SimpleTypeConverter();
} }
@@ -199,7 +218,7 @@ public final class RSQLUtility {
return toSingleList(cb.conjunction()); return toSingleList(cb.conjunction());
} }
private List<Predicate> toSingleList(final Predicate predicate) { private static List<Predicate> toSingleList(final Predicate predicate) {
return Collections.singletonList(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, private Object convertValueIfNecessary(final ComparisonNode node, final A fieldName, final String value,
final Path<Object> fieldPath) { final Path<Object> fieldPath) {
// in case the value of an rsql query e.g. type==application is an // in case the value of an rsql query e.g. type==application is an
// enum we need to // enum we need to handle it separately because JPA needs the
// handle it separately because JPA needs the correct java-type to // correct java-type to build an expression. So String and numeric
// build an // values JPA can do it by it's own but not for classes like enums.
// expression. So String and numeric values JPA can do it by it's // So we need to transform the given value string into the enum
// own but not for
// classes like enums. So we need to transform the given value
// string into the enum
// class. // class.
final Class<? extends Object> javaType = fieldPath.getJavaType(); final Class<? extends Object> javaType = fieldPath.getJavaType();
if (javaType != null && javaType.isEnum()) { if (javaType != null && javaType.isEnum()) {
@@ -399,15 +415,14 @@ public final class RSQLUtility {
// Exception squid:S2095 - see // Exception squid:S2095 - see
// https://jira.sonarsource.com/browse/SONARJAVA-1478 // https://jira.sonarsource.com/browse/SONARJAVA-1478
@SuppressWarnings({ "rawtypes", "unchecked", "squid:S2095" }) @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 Object> javaType) {
final Class<? extends Enum> tmpEnumType = (Class<? extends Enum>) javaType; final Class<? extends Enum> tmpEnumType = (Class<? extends Enum>) javaType;
try { try {
return Enum.valueOf(tmpEnumType, value.toUpperCase()); return Enum.valueOf(tmpEnumType, value.toUpperCase());
} catch (final IllegalArgumentException e) { } catch (final IllegalArgumentException e) {
// we could not transform the given string value into the enum // we could not transform the given string value into the enum
// type, so ignore // type, so ignore it and return null and do not filter
// it and return null and do not filter
LOGGER.info("given value {} cannot be transformed into the correct enum type {}", value.toUpperCase(), LOGGER.info("given value {} cannot be transformed into the correct enum type {}", value.toUpperCase(),
javaType); javaType);
LOGGER.debug("value cannot be transformed to an enum", e); 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, private List<Predicate> mapToPredicate(final ComparisonNode node, final Path<Object> fieldPath,
final List<String> values, final List<Object> transformedValues, final A enumField) { final List<String> values, final List<Object> transformedValues, final A enumField) {
// only 'equal' and 'notEqual' can handle transformed value like // only 'equal' and 'notEqual' can handle transformed value like
// enums. The JPA API // enums. The JPA API cannot handle object types for greaterThan etc
// cannot handle object types for greaterThan etc methods. // methods.
final Object transformedValue = transformedValues.get(0); 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 List<Predicate> singleList = new ArrayList<>();
final Predicate mapPredicate = mapToMapPredicate(node, fieldPath, enumField); final Predicate mapPredicate = mapToMapPredicate(node, fieldPath, enumField);
@@ -541,12 +562,12 @@ public final class RSQLUtility {
return cb.notEqual(fieldPath, transformedValue); return cb.notEqual(fieldPath, transformedValue);
} }
private String escapeValueToSQL(final String transformedValue) { private static String escapeValueToSQL(final String transformedValue) {
return transformedValue.replace("%", "\\%").replace(LIKE_WILDCARD, '%'); return transformedValue.replace("%", "\\%").replace(LIKE_WILDCARD, '%');
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private <Y> Path<Y> pathOfString(final Path<?> path) { private static <Y> Path<Y> pathOfString(final Path<?> path) {
return (Path<Y>) 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 Specification} for retrieving {@link Target}s by "equal to given
* {@link TargetUpdateStatus}". * {@link TargetUpdateStatus}".
* *
* @param updateStatus * @param updateStatus
* to be filtered on * to be filtered on
* @param fetch * @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 * {@link Specification} for retrieving {@link Target}s by
* "like controllerId or like description or like ip address". * "like controllerId or like description or like ip address".
* *
* @param searchText * @param searchText
* to be filtered on * to be filtered on
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
@@ -132,7 +156,7 @@ public final class TargetSpecifications {
/** /**
* {@link Specification} for retrieving {@link Target}s by * {@link Specification} for retrieving {@link Target}s by
* "like controllerId". * "like controllerId".
* *
* @param distributionId * @param distributionId
* to be filtered on * to be filtered on
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
@@ -169,7 +193,7 @@ public final class TargetSpecifications {
/** /**
* {@link Specification} for retrieving {@link Target}s by * {@link Specification} for retrieving {@link Target}s by
* "has no tag names"or "has at least on of the given tag names". * "has no tag names"or "has at least on of the given tag names".
* *
* @param tagNames * @param tagNames
* to be filtered on * to be filtered on
* @param selectTargetWithNoTag * @param selectTargetWithNoTag
@@ -202,7 +226,7 @@ public final class TargetSpecifications {
/** /**
* {@link Specification} for retrieving {@link Target}s by assigned * {@link Specification} for retrieving {@link Target}s by assigned
* distribution set. * distribution set.
* *
* @param distributionSetId * @param distributionSetId
* the ID of the distribution set which must be assigned * the ID of the distribution set which must be assigned
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
@@ -234,7 +258,7 @@ public final class TargetSpecifications {
/** /**
* {@link Specification} for retrieving {@link Target}s by assigned * {@link Specification} for retrieving {@link Target}s by assigned
* distribution set. * distribution set.
* *
* @param distributionSetId * @param distributionSetId
* the ID of the distribution set which must be assigned * the ID of the distribution set which must be assigned
* @return the {@link Target} {@link Specification} * @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 static org.fest.assertions.api.Assertions.assertThat;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@@ -17,6 +18,7 @@ import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; 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.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus; import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
@@ -53,26 +55,42 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final DistributionSet installedSet = testdataFactory.createDistributionSet("another"); 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"; final String targetDsAIdPref = "targ-A";
List<Target> targAs = targetManagement.createTargets( List<Target> targAs = new ArrayList<Target>();
testdataFactory.generateTargets(100, targetDsAIdPref, targetDsAIdPref.concat(" description"))); 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(); targAs = targetManagement.toggleTagAssignment(targAs, targTagX).getAssignedEntity();
final String targetDsBIdPref = "targ-B"; final String targetDsBIdPref = "targ-B";
List<Target> targBs = targetManagement.createTargets( List<Target> targBs = new ArrayList<Target>();
testdataFactory.generateTargets(100, targetDsBIdPref, targetDsBIdPref.concat(" description"))); 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, targTagY).getAssignedEntity();
targBs = targetManagement.toggleTagAssignment(targBs, targTagW).getAssignedEntity(); targBs = targetManagement.toggleTagAssignment(targBs, targTagW).getAssignedEntity();
final String targetDsCIdPref = "targ-C"; final String targetDsCIdPref = "targ-C";
List<Target> targCs = targetManagement.createTargets( List<Target> targCs = new ArrayList<Target>();
testdataFactory.generateTargets(100, targetDsCIdPref, targetDsCIdPref.concat(" description"))); 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, targTagZ).getAssignedEntity();
targCs = targetManagement.toggleTagAssignment(targCs, targTagW).getAssignedEntity(); targCs = targetManagement.toggleTagAssignment(targCs, targTagW).getAssignedEntity();
final String targetDsDIdPref = "targ-D"; final String targetDsDIdPref = "targ-D";
final List<Target> targDs = targetManagement.createTargets( List<Target> targDs = new ArrayList<Target>();
testdataFactory.generateTargets(100, targetDsDIdPref, targetDsDIdPref.concat(" description"))); 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(); final String assignedC = targCs.iterator().next().getControllerId();
deploymentManagement.assignDistributionSet(setA.getId(), assignedC); deploymentManagement.assignDistributionSet(setA.getId(), assignedC);
@@ -148,6 +166,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
verifyThat200targetsWithGivenTagAreInStatusPendingorUnknown(targTagW, both, concat(targBs, targCs)); verifyThat200targetsWithGivenTagAreInStatusPendingorUnknown(targTagW, both, concat(targBs, targCs));
verfiyThat1TargetAIsInStatusPendingAndHasDSInstalled(installedSet, pending, verfiyThat1TargetAIsInStatusPendingAndHasDSInstalled(installedSet, pending,
targetManagement.findTargetByControllerID(installedC)); targetManagement.findTargetByControllerID(installedC));
expected = concat(targBs, targCs);
expected.removeAll(targetManagement.findTargetByControllerID(Lists.newArrayList(assignedB, assignedC)));
verifyThat198TargetsAreInStatusUnknownAndOverdue(unknown, expected);
} }
@Step @Step
@@ -157,10 +179,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final String query = "updatestatus==pending and installedds.name==" + installedSet.getName(); final String query = "updatestatus==pending and installedds.name==" + installedSet.getName();
assertThat(targetManagement 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) .getContent()).as("has number of elements").hasSize(1)
.as("that number is also returned by count query") .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]))) installedSet.getId(), Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsExactly(expected) .as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result") .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 .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent()); .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) Boolean.FALSE, new String[0])).as("has number of elements").hasSize(1)
.as("and contains the following elements").containsExactly(expectedIdName) .as("and contains the following elements").containsExactly(expectedIdName)
.as("and NAMED filter query returns the same result").containsAll(targetManagement .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(); final String query = "(updatestatus==pending or updatestatus==unknown) and tag==" + targTagW.getName();
assertThat(targetManagement.findTargetByFilters(pageReq, both, null, null, Boolean.FALSE, targTagW.getName()) assertThat(targetManagement
.getContent()).as("has number of elements").hasSize(200) .findTargetByFilters(pageReq, both, null, null, null, Boolean.FALSE, targTagW.getName()).getContent())
.as("that number is also returned by count query") .as("has number of elements").hasSize(200).as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(both, null, null, .hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(both, null, null, null,
Boolean.FALSE, targTagW.getName()))) Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsAll(expected) .as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result") .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 .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent()); .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") 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(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq, .containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
@@ -217,10 +239,11 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final List<TargetIdName> expectedIdNames = convertToIdNames(expected); final List<TargetIdName> expectedIdNames = convertToIdNames(expected);
final String query = "updatestatus==pending and tag==" + targTagW.getName(); 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) .getContent()).as("has number of elements").hasSize(2)
.as("that number is also returned by count query") .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()))) Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsAll(expected) .as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result") .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 .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent()); .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") 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(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq, .containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
@@ -243,18 +266,18 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
+ setA.getName() + ") and tag==" + targTagW.getName(); + setA.getName() + ") and tag==" + targTagW.getName();
assertThat(targetManagement 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) .getContent()).as("has number of elements").hasSize(2)
.as("that number is also returned by count query") .as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(pending, null, setA.getId(), .hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(pending, null, null,
Boolean.FALSE, targTagW.getName()))) setA.getId(), Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsAll(expected) .as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result") .as("and filter query returns the same result")
.containsAll(targetManagement.findTargetsAll(query, pageReq).getContent()) .containsAll(targetManagement.findTargetsAll(query, pageReq).getContent())
.as("and NAMED filter query returns the same result").containsAll(targetManagement .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent()); .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") 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(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq, .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==" 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(); + setA.getName() + ") and (name==*targ-B* or description==*targ-B*) and tag==" + targTagW.getName();
assertThat(targetManagement assertThat(targetManagement.findTargetByFilters(pageReq, pending, null, "%targ-B%", setA.getId(), Boolean.FALSE,
.findTargetByFilters(pageReq, pending, "%targ-B%", setA.getId(), Boolean.FALSE, targTagW.getName()) targTagW.getName()).getContent()).as("has number of elements").hasSize(1)
.getContent()).as("has number of elements").hasSize(1)
.as("that number is also returned by count query") .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()))) setA.getId(), Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsExactly(expected) .as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result") .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 .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent()); .findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, "%targ-B%", setA.getId(), Boolean.FALSE, assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, null, "%targ-B%", setA.getId(),
targTagW.getName())).as("has number of elements").hasSize(1).as("and contains the following elements") Boolean.FALSE, targTagW.getName())).as("has number of elements").hasSize(1)
.containsExactly(expectedIdName).as("and NAMED filter query returns the same result") .as("and contains the following elements").containsExactly(expectedIdName)
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq, .as("and NAMED filter query returns the same result").containsAll(targetManagement
new JpaTargetFilterQuery("test", query))); .findAllTargetIdsByTargetFilterQuery(pageReq, new JpaTargetFilterQuery("test", query)));
} }
@Step @Step
@@ -295,10 +317,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
+ setA.getName() + ") and (name==*targ-A* or description==*targ-A*)"; + setA.getName() + ") and (name==*targ-A* or description==*targ-A*)";
assertThat(targetManagement 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) .getContent()).as("has number of elements").hasSize(1)
.as("that number is also returned by count query") .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]))) setA.getId(), Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsExactly(expected) .as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result") .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 .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent()); .findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, "%targ-A%", setA.getId(), Boolean.FALSE, assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, null, "%targ-A%", setA.getId(),
new String[0])).as("has number of elements").hasSize(1).as("and contains the following elements") Boolean.FALSE, new String[0])).as("has number of elements").hasSize(1)
.containsExactly(expectedIdName).as("and NAMED filter query returns the same result") .as("and contains the following elements").containsExactly(expectedIdName)
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq, .as("and NAMED filter query returns the same result").containsAll(targetManagement
new JpaTargetFilterQuery("test", query))); .findAllTargetIdsByTargetFilterQuery(pageReq, new JpaTargetFilterQuery("test", query)));
} }
@Step @Step
@@ -321,17 +343,17 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
+ setA.getName() + ")"; + setA.getName() + ")";
assertThat(targetManagement assertThat(targetManagement
.findTargetByFilters(pageReq, pending, null, setA.getId(), Boolean.FALSE, new String[0]).getContent()) .findTargetByFilters(pageReq, pending, null, null, setA.getId(), Boolean.FALSE, new String[0])
.as("has number of elements").hasSize(3).as("that number is also returned by count query") .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(), .hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(pending, null, null,
Boolean.FALSE, new String[0]))) setA.getId(), Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsAll(expected) .as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result") .as("and filter query returns the same result")
.containsAll(targetManagement.findTargetsAll(query, pageReq).getContent()) .containsAll(targetManagement.findTargetsAll(query, pageReq).getContent())
.as("and NAMED filter query returns the same result").containsAll(targetManagement .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent()); .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") 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(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq, .containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
@@ -344,10 +366,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final List<TargetIdName> expectedIdNames = convertToIdNames(expected); final List<TargetIdName> expectedIdNames = convertToIdNames(expected);
final String query = "updatestatus==pending"; final String query = "updatestatus==pending";
assertThat(targetManagement.findTargetByFilters(pageReq, pending, null, null, Boolean.FALSE, new String[0]) assertThat(targetManagement
.getContent()).as("has number of elements").hasSize(3) .findTargetByFilters(pageReq, pending, null, null, null, Boolean.FALSE, new String[0]).getContent())
.as("that number is also returned by count query") .as("has number of elements").hasSize(3).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, new String[0]))) Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsAll(expected) .as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result") .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 .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent()); .findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat( assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, pending, null, null, null, Boolean.FALSE,
targetManagement.findAllTargetIdsByFilters(pageReq, pending, null, null, Boolean.FALSE, new String[0])) new String[0])).as("has number of elements").hasSize(3).as("and contains the following elements")
.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(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq, .containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
new JpaTargetFilterQuery("test", query))); new JpaTargetFilterQuery("test", query)));
@@ -371,18 +392,18 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
+ targTagW.getName(); + targTagW.getName();
assertThat(targetManagement 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) .getContent()).as("has number of elements").hasSize(99)
.as("that number is also returned by count query") .as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(unknown, "%targ-B%", null, .hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(unknown, null, "%targ-B%",
Boolean.FALSE, targTagW.getName()))) null, Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsAll(expected) .as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result") .as("and filter query returns the same result")
.containsAll(targetManagement.findTargetsAll(query, pageReq).getContent()) .containsAll(targetManagement.findTargetsAll(query, pageReq).getContent())
.as("and NAMED filter query returns the same result").containsAll(targetManagement .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent()); .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") 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(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq, .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*)"; final String query = "updatestatus==unknown and (name==*targ-A* or description==*targ-A*)";
assertThat(targetManagement assertThat(targetManagement
.findTargetByFilters(pageReq, unknown, "%targ-A%", null, Boolean.FALSE, new String[0]).getContent()) .findTargetByFilters(pageReq, unknown, null, "%targ-A%", null, Boolean.FALSE, new String[0])
.as("has number of elements").hasSize(99).as("that number is also returned by count query") .getContent()).as("has number of elements").hasSize(99)
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(unknown, "%targ-A%", null, .as("that number is also returned by count query").hasSize(Ints.saturatedCast(targetManagement
Boolean.FALSE, new String[0]))) .countTargetByFilters(unknown, null, "%targ-A%", null, Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsAll(expected) .as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result") .as("and filter query returns the same result")
.containsAll(targetManagement.findTargetsAll(query, pageReq).getContent()) .containsAll(targetManagement.findTargetsAll(query, pageReq).getContent())
.as("and NAMED filter query returns the same result").containsAll(targetManagement .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent()); .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") 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(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq, .containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
@@ -420,16 +441,16 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
+ setA.getName() + ")"; + setA.getName() + ")";
assertThat(targetManagement assertThat(targetManagement
.findTargetByFilters(pageReq, unknown, null, setA.getId(), Boolean.FALSE, new String[0]).getContent()) .findTargetByFilters(pageReq, unknown, null, null, setA.getId(), Boolean.FALSE, new String[0])
.as("has number of elements").hasSize(0).as("that number is also returned by count query") .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(), .hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(unknown, null, null,
Boolean.FALSE, new String[0]))) setA.getId(), Boolean.FALSE, new String[0])))
.as("and filter query returns the same result") .as("and filter query returns the same result")
.hasSize(targetManagement.findTargetsAll(query, pageReq).getContent().size()) .hasSize(targetManagement.findTargetsAll(query, pageReq).getContent().size())
.as("and NAMED filter query returns the same result").hasSize(targetManagement .as("and NAMED filter query returns the same result").hasSize(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent().size()); .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) new String[0])).as("has number of elements").hasSize(0)
.as("and NAMED filter query returns the same result").containsAll(targetManagement .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findAllTargetIdsByTargetFilterQuery(pageReq, new JpaTargetFilterQuery("test", query))); .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() final String query = "updatestatus==unknown and (tag==" + targTagY.getName() + " or tag==" + targTagW.getName()
+ ")"; + ")";
assertThat(targetManagement.findTargetByFilters(pageReq, unknown, null, null, Boolean.FALSE, targTagY.getName(), assertThat(targetManagement.findTargetByFilters(pageReq, unknown, null, null, null, Boolean.FALSE,
targTagW.getName()).getContent()).as("has number of elements").hasSize(198) targTagY.getName(), targTagW.getName()).getContent()).as("has number of elements").hasSize(198)
.as("that number is also returned by count query") .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()))) Boolean.FALSE, targTagY.getName(), targTagW.getName())))
.as("and contains the following elements").containsAll(expected) .as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result") .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 .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent()); .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) targTagY.getName(), targTagW.getName())).as("has number of elements").hasSize(198)
.as("and contains the following elements").containsAll(expectedIdNames) .as("and contains the following elements").containsAll(expectedIdNames)
.as("and NAMED filter query returns the same result").containsAll(targetManagement .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 List<TargetIdName> expectedIdNames = convertToIdNames(expected);
final String query = "updatestatus==unknown"; final String query = "updatestatus==unknown";
assertThat(targetManagement.findTargetByFilters(pageReq, unknown, null, null, Boolean.FALSE, new String[0]) assertThat(targetManagement
.getContent()).as("has number of elements").hasSize(397) .findTargetByFilters(pageReq, unknown, null, null, null, Boolean.FALSE, new String[0]).getContent())
.as("that number is also returned by count query") .as("has number of elements").hasSize(397).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, new String[0]))) Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsAll(expected) .as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result") .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 .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent()); .findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat( assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, unknown, null, null, null, Boolean.FALSE,
targetManagement.findAllTargetIdsByFilters(pageReq, unknown, null, null, Boolean.FALSE, new String[0])) new String[0])).as("has number of elements").hasSize(397).as("and contains the following elements")
.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(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq, .containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
new JpaTargetFilterQuery("test", query))); new JpaTargetFilterQuery("test", query)));
@@ -492,10 +538,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
+ " or installedds.name==" + setA.getName() + ")"; + " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement 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) .getContent()).as("has number of elements").hasSize(1)
.as("that number is also returned by count query") .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]))) setA.getId(), Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsExactly(expected) .as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result") .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 .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent()); .findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, "%targ-A%", setA.getId(), Boolean.FALSE, assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, null, "%targ-A%", setA.getId(),
new String[0])).as("has number of elements").hasSize(1).as("and contains the following elements") Boolean.FALSE, new String[0])).as("has number of elements").hasSize(1)
.containsExactly(expectedIdName).as("and NAMED filter query returns the same result") .as("and contains the following elements").containsExactly(expectedIdName)
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq, .as("and NAMED filter query returns the same result").containsAll(targetManagement
new JpaTargetFilterQuery("test", query))); .findAllTargetIdsByTargetFilterQuery(pageReq, new JpaTargetFilterQuery("test", query)));
} }
@Step @Step
@@ -515,18 +561,19 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final List<TargetIdName> expectedIdNames = convertToIdNames(expected); final List<TargetIdName> expectedIdNames = convertToIdNames(expected);
final String query = "assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName(); 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) .getContent()).as("has number of elements").hasSize(3)
.as("that number is also returned by count query") .as("that number is also returned by count query")
.hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(null, null, setA.getId(), .hasSize(Ints.saturatedCast(targetManagement.countTargetByFilters(null, null, null,
Boolean.FALSE, new String[0]))) setA.getId(), Boolean.FALSE, new String[0])))
.as("and contains the following elements").containsAll(expected) .as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result") .as("and filter query returns the same result")
.containsAll(targetManagement.findTargetsAll(query, pageReq).getContent()) .containsAll(targetManagement.findTargetsAll(query, pageReq).getContent())
.as("and NAMED filter query returns the same result").containsAll(targetManagement .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent()); .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") 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(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq, .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() final String query = "(name==*targ-C* or description==*targ-C*) and tag==" + targTagX.getName()
+ " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")"; + " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement 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) .getContent()).as("has number of elements").hasSize(0)
.as("that number is also returned by count query") .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()))) setA.getId(), Boolean.FALSE, targTagX.getName())))
.as("and filter query returns the same result") .as("and filter query returns the same result")
.hasSize(targetManagement.findTargetsAll(query, pageReq).getContent().size()) .hasSize(targetManagement.findTargetsAll(query, pageReq).getContent().size())
.as("and NAMED filter query returns the same result").hasSize(targetManagement .as("and NAMED filter query returns the same result").hasSize(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent().size()); .findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent().size());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, "%targ-C%", setA.getId(), Boolean.FALSE, assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, null, "%targ-C%", setA.getId(),
targTagX.getName())).as("has number of elements").hasSize(0) Boolean.FALSE, targTagX.getName())).as("has number of elements").hasSize(0)
.as("and NAMED filter query returns the same result").containsAll(targetManagement .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findAllTargetIdsByTargetFilterQuery(pageReq, new JpaTargetFilterQuery("test", query))); .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() final String query = "(name==*targ-A* or description==*targ-A*) and tag==" + targTagW.getName()
+ " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")"; + " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement 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) .getContent()).as("has number of elements").hasSize(0)
.as("that number is also returned by count query") .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()))) setA.getId(), Boolean.FALSE, targTagW.getName())))
.as("and filter query returns the same result") .as("and filter query returns the same result")
.hasSize(targetManagement.findTargetsAll(query, pageReq).getContent().size()) .hasSize(targetManagement.findTargetsAll(query, pageReq).getContent().size())
.as("and NAMED filter query returns the same result").hasSize(targetManagement .as("and NAMED filter query returns the same result").hasSize(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent().size()); .findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent().size());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, "%targ-A%", setA.getId(), Boolean.FALSE, assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, null, "%targ-A%", setA.getId(),
targTagW.getName())).as("has number of elements").hasSize(0) Boolean.FALSE, targTagW.getName())).as("has number of elements").hasSize(0)
.as("and NAMED filter query returns the same result").containsAll(targetManagement .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findAllTargetIdsByTargetFilterQuery(pageReq, new JpaTargetFilterQuery("test", query))); .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() final String query = "(name==*targ-c* or description==*targ-C*) and tag==" + targTagW.getName()
+ " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")"; + " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement 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) .getContent()).as("has number of elements").hasSize(1)
.as("that number is also returned by count query") .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()))) setA.getId(), Boolean.FALSE, targTagW.getName())))
.as("and contains the following elements").containsExactly(expected) .as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result") .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 .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent()); .findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent());
assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, "%targ-C%", setA.getId(), Boolean.FALSE, assertThat(targetManagement.findAllTargetIdsByFilters(pageReq, null, null, "%targ-C%", setA.getId(),
targTagW.getName())).as("has number of elements").hasSize(1).as("and contains the following elements") Boolean.FALSE, targTagW.getName())).as("has number of elements").hasSize(1)
.containsExactly(expectedIdName).as("and NAMED filter query returns the same result") .as("and contains the following elements").containsExactly(expectedIdName)
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq, .as("and NAMED filter query returns the same result").containsAll(targetManagement
new JpaTargetFilterQuery("test", query))); .findAllTargetIdsByTargetFilterQuery(pageReq, new JpaTargetFilterQuery("test", query)));
} }
@Step @Step
@@ -606,10 +653,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final List<TargetIdName> expectedIdNames = convertToIdNames(expected); final List<TargetIdName> expectedIdNames = convertToIdNames(expected);
final String query = "(name==*targ-B* or description==*targ-B*) and (tag==" + targTagY.getName() + " or tag==" final String query = "(name==*targ-B* or description==*targ-B*) and (tag==" + targTagY.getName() + " or tag=="
+ targTagW.getName() + ")"; + 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) targTagY.getName(), targTagW.getName()).getContent()).as("has number of elements").hasSize(100)
.as("that number is also returned by count query") .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()))) Boolean.FALSE, targTagY.getName(), targTagW.getName())))
.as("and contains the following elements").containsAll(expected) .as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result") .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 .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent()); .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) targTagY.getName(), targTagW.getName())).as("has number of elements").hasSize(100)
.as("and contains the following elements").containsAll(expectedIdNames) .as("and contains the following elements").containsAll(expectedIdNames)
.as("and NAMED filter query returns the same result").containsAll(targetManagement .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) { private void verifyThat200TargetsHaveTagD(final TargetTag targTagD, final List<Target> expected) {
final List<TargetIdName> expectedIdNames = convertToIdNames(expected); final List<TargetIdName> expectedIdNames = convertToIdNames(expected);
final String query = "tag==" + targTagD.getName(); final String query = "tag==" + targTagD.getName();
assertThat(targetManagement.findTargetByFilters(pageReq, null, null, null, Boolean.FALSE, targTagD.getName()) assertThat(targetManagement
.getContent()).as("Expected number of results is").hasSize(200) .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 ") .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()))) Boolean.FALSE, targTagD.getName())))
.as("and contains the following elements").containsAll(expected) .as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result") .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 .as("and NAMED filter query returns the same result").containsAll(targetManagement
.findTargetsAll(new JpaTargetFilterQuery("test", query), pageReq).getContent()); .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") 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(expectedIdNames).as("and NAMED filter query returns the same result")
.containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq, .containsAll(targetManagement.findAllTargetIdsByTargetFilterQuery(pageReq,
@@ -657,12 +705,13 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
@Step @Step
private void verifyThatRepositoryContains400Targets() { private void verifyThatRepositoryContains400Targets() {
assertThat(targetManagement.findTargetByFilters(pageReq, null, null, null, null, new String[0]).getContent()) assertThat(
.as("Overall we expect that many targets in the repository").hasSize(400) targetManagement.findTargetByFilters(pageReq, null, null, null, null, null, new String[0]).getContent())
.as("which is also reflected by repository count") .as("Overall we expect that many targets in the repository").hasSize(400)
.hasSize(Ints.saturatedCast(targetManagement.countTargetsAll())) .as("which is also reflected by repository count")
.as("which is also reflected by call without specification") .hasSize(Ints.saturatedCast(targetManagement.countTargetsAll()))
.containsAll(targetManagement.findTargetsAll(pageReq).getContent()); .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"); targInstalled = sendUpdateActionStatusToTargets(ds, targInstalled, Status.FINISHED, "installed");
final Slice<Target> result = targetManagement.findTargetsAllOrderByLinkedDistributionSet(pageReq, ds.getId(), 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()); 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 @Test
@Description("Verfies that targets with given assigned DS are returned from repository.") @Description("Verfies that targets with given assigned DS are returned from repository.")
public void findTargetByAssignedDistributionSet() { public void findTargetByAssignedDistributionSet() {

View File

@@ -601,7 +601,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
targetManagement.toggleTagAssignment(tagABCTargets, tagB); targetManagement.toggleTagAssignment(tagABCTargets, tagB);
targetManagement.toggleTagAssignment(tagABCTargets, tagC); 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); .as("Target count is wrong").isEqualTo(0);
// search for targets with tag tagA // search for targets with tag tagA
@@ -631,11 +631,11 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
checkTargetHasNotTags(tagCTargets, tagA, tagB); checkTargetHasNotTags(tagCTargets, tagA, tagB);
// check again target lists refreshed from DB // 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()); .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()); .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()); .as("Target count is wrong").isEqualTo(targetWithTagC.size());
} }
@@ -752,7 +752,8 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
final String[] tagNames = null; final String[] tagNames = null;
final List<Target> targetsListWithNoTag = targetManagement 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(50).as("Total targets").isEqualTo(targetManagement.findAllTargetIds().size());
assertThat(25).as("Targets with no tag").isEqualTo(targetsListWithNoTag.size()); assertThat(25).as("Targets with no tag").isEqualTo(targetsListWithNoTag.size());

View File

@@ -9,14 +9,9 @@
package org.eclipse.hawkbit.repository.jpa.rsql; package org.eclipse.hawkbit.repository.jpa.rsql;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.*;
import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.*;
import static org.mockito.Matchers.eq; import static org.powermock.api.mockito.PowerMockito.mockStatic;
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 javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery; 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.FieldNameProvider;
import org.eclipse.hawkbit.repository.SoftwareModuleFields; import org.eclipse.hawkbit.repository.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.TargetFields; 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.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException; 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.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.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; 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.Features;
import ru.yandex.qatools.allure.annotations.Stories; import ru.yandex.qatools.allure.annotations.Stories;
@RunWith(MockitoJUnitRunner.class) @RunWith(PowerMockRunner.class)
@Features("Component Tests - Repository") @Features("Component Tests - Repository")
@Stories("RSQL search utility") @Stories("RSQL search utility")
@PrepareForTest(TimestampCalculator.class)
// TODO: fully document tests -> @Description for long text and reasonable // TODO: fully document tests -> @Description for long text and reasonable
// method name as short text // method name as short text
public class RSQLUtilityTest { public class RSQLUtilityTest {
@Spy
private VirtualPropertyResolver macroResolver = new VirtualPropertyResolver();
@Mock
private TenantConfigurationManagement confMgmt;
@Mock @Mock
private Root<Object> baseSoftwareModuleRootMock; private Root<Object> baseSoftwareModuleRootMock;
@@ -59,11 +69,16 @@ public class RSQLUtilityTest {
@Mock @Mock
private Attribute attribute; 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 @Test
public void wrongRsqlSyntaxThrowSyntaxException() { public void wrongRsqlSyntaxThrowSyntaxException() {
final String wrongRSQL = "name==abc;d"; final String wrongRSQL = "name==abc;d";
try { try {
RSQLUtility.parse(wrongRSQL, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(wrongRSQL, SoftwareModuleFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock); criteriaQueryMock, criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax"); fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterSyntaxException e) { } catch (final RSQLParameterSyntaxException e) {
@@ -75,7 +90,7 @@ public class RSQLUtilityTest {
final String wrongRSQL = "unknownField==abc"; final String wrongRSQL = "unknownField==abc";
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class); when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
try { try {
RSQLUtility.parse(wrongRSQL, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(wrongRSQL, SoftwareModuleFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock); criteriaQueryMock, criteriaBuilderMock);
fail("Missing an expected RSQLParameterUnsupportedFieldException because of unknown RSQL field"); fail("Missing an expected RSQLParameterUnsupportedFieldException because of unknown RSQL field");
} catch (final RSQLParameterUnsupportedFieldException e) { } catch (final RSQLParameterUnsupportedFieldException e) {
@@ -87,7 +102,8 @@ public class RSQLUtilityTest {
public void wrongRsqlMapSyntaxThrowSyntaxException() { public void wrongRsqlMapSyntaxThrowSyntaxException() {
String wrongRSQL = TargetFields.ATTRIBUTE + "==abc"; String wrongRSQL = TargetFields.ATTRIBUTE + "==abc";
try { try {
RSQLUtility.parse(wrongRSQL, TargetFields.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock, RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock,
criteriaBuilderMock); criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax"); fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterUnsupportedFieldException e) { } catch (final RSQLParameterUnsupportedFieldException e) {
@@ -95,7 +111,8 @@ public class RSQLUtilityTest {
wrongRSQL = TargetFields.ATTRIBUTE + ".unkwon.wrong==abc"; wrongRSQL = TargetFields.ATTRIBUTE + ".unkwon.wrong==abc";
try { try {
RSQLUtility.parse(wrongRSQL, TargetFields.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock, RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock,
criteriaBuilderMock); criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax"); fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterUnsupportedFieldException e) { } catch (final RSQLParameterUnsupportedFieldException e) {
@@ -103,7 +120,7 @@ public class RSQLUtilityTest {
wrongRSQL = DistributionSetFields.METADATA + "==abc"; wrongRSQL = DistributionSetFields.METADATA + "==abc";
try { try {
RSQLUtility.parse(wrongRSQL, DistributionSetFields.class).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(wrongRSQL, DistributionSetFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock); criteriaQueryMock, criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax"); fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterUnsupportedFieldException e) { } catch (final RSQLParameterUnsupportedFieldException e) {
@@ -115,7 +132,8 @@ public class RSQLUtilityTest {
public void wrongRsqlSubEntitySyntaxThrowSyntaxException() { public void wrongRsqlSubEntitySyntaxThrowSyntaxException() {
String wrongRSQL = TargetFields.ASSIGNEDDS + "==abc"; String wrongRSQL = TargetFields.ASSIGNEDDS + "==abc";
try { try {
RSQLUtility.parse(wrongRSQL, TargetFields.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock, RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock,
criteriaBuilderMock); criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax"); fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterUnsupportedFieldException e) { } catch (final RSQLParameterUnsupportedFieldException e) {
@@ -123,7 +141,8 @@ public class RSQLUtilityTest {
wrongRSQL = TargetFields.ASSIGNEDDS + ".unknownField==abc"; wrongRSQL = TargetFields.ASSIGNEDDS + ".unknownField==abc";
try { try {
RSQLUtility.parse(wrongRSQL, TargetFields.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock, RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock,
criteriaBuilderMock); criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax"); fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterUnsupportedFieldException e) { } catch (final RSQLParameterUnsupportedFieldException e) {
@@ -131,7 +150,8 @@ public class RSQLUtilityTest {
wrongRSQL = TargetFields.ASSIGNEDDS + ".unknownField.ToMuch==abc"; wrongRSQL = TargetFields.ASSIGNEDDS + ".unknownField.ToMuch==abc";
try { try {
RSQLUtility.parse(wrongRSQL, TargetFields.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock, RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock,
criteriaBuilderMock); criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax"); fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterUnsupportedFieldException e) { } catch (final RSQLParameterUnsupportedFieldException e) {
@@ -146,11 +166,11 @@ public class RSQLUtilityTest {
when(baseSoftwareModuleRootMock.get("version")).thenReturn(baseSoftwareModuleRootMock); when(baseSoftwareModuleRootMock.get("version")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class); when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.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)); .thenReturn(mock(Predicate.class));
// test // test
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock); criteriaQueryMock, criteriaBuilderMock);
// verfication // verfication
@@ -164,12 +184,12 @@ public class RSQLUtilityTest {
when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock); when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class); when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.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)); .thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock)))) when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock))))
.thenReturn(pathOfString(baseSoftwareModuleRootMock)); .thenReturn(pathOfString(baseSoftwareModuleRootMock));
// test // test
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock); criteriaQueryMock, criteriaBuilderMock);
// verfication // verfication
@@ -185,12 +205,12 @@ public class RSQLUtilityTest {
when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock); when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class); when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.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)); .thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock)))) when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock))))
.thenReturn(pathOfString(baseSoftwareModuleRootMock)); .thenReturn(pathOfString(baseSoftwareModuleRootMock));
// test // test
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock); criteriaQueryMock, criteriaBuilderMock);
// verfication // verfication
@@ -206,10 +226,10 @@ public class RSQLUtilityTest {
when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock); when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class); when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.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)); .thenReturn(mock(Predicate.class));
// test // test
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock); criteriaQueryMock, criteriaBuilderMock);
// verfication // verfication
@@ -226,7 +246,8 @@ public class RSQLUtilityTest {
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class)); when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
// test // test
RSQLUtility.parse(correctRsql, TestFieldEnum.class).toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock, RSQLUtility.parse(correctRsql, TestFieldEnum.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock,
criteriaBuilderMock); criteriaBuilderMock);
// verfication // verfication
@@ -244,7 +265,7 @@ public class RSQLUtilityTest {
try { try {
// test // test
RSQLUtility.parse(correctRsql, TestFieldEnum.class).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(correctRsql, TestFieldEnum.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaBuilderMock); criteriaQueryMock, criteriaBuilderMock);
fail("missing RSQLParameterUnsupportedFieldException for wrong enum value"); fail("missing RSQLParameterUnsupportedFieldException for wrong enum value");
} catch (final RSQLParameterUnsupportedFieldException e) { } 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") @SuppressWarnings("unchecked")
private <Y> Path<Y> pathOfString(final Path<?> path) { private <Y> Path<Y> pathOfString(final Path<?> path) {
return (Path<Y>) path; return (Path<Y>) path;
@@ -262,10 +344,8 @@ public class RSQLUtilityTest {
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see * @see org.eclipse.hawkbit.server.rest.resource.model.FieldNameProvider# getFieldName()
* org.eclipse.hawkbit.server.rest.resource.model.FieldNameProvider#
* getFieldName()
*/ */
@Override @Override
public String getFieldName() { 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.TenancyCacheManager;
import org.eclipse.hawkbit.cache.TenantAwareCacheManager; import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
import org.eclipse.hawkbit.repository.jpa.model.helper.EventBusHolder; 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.JpaTestRepositoryManagement;
import org.eclipse.hawkbit.repository.test.util.TestRepositoryManagement; import org.eclipse.hawkbit.repository.test.util.TestRepositoryManagement;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory; import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
@@ -129,4 +131,13 @@ public class TestConfiguration implements AsyncConfigurer {
return new SimpleAsyncUncaughtExceptionHandler(); 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) { 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 */ /* Distribution is already assigned */
notification.displayValidationError(i18n.get("message.dist.inuse", notification.displayValidationError(i18n.get("message.dist.inuse",
HawkbitCommonUtil.concatStrings(":", ds.getName(), ds.getVersion()))); HawkbitCommonUtil.concatStrings(":", ds.getName(), ds.getVersion())));

View File

@@ -14,5 +14,39 @@ package org.eclipse.hawkbit.ui.management.event;
* *
*/ */
public enum ManagementUIEvent { 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 { 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); message.append(HawkbitCommonUtil.SP_STRING_PIPE);
final String status = i18n.get("label.filter.status"); 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 tags = i18n.get("label.filter.tags");
final String text = i18n.get("label.filter.text"); final String text = i18n.get("label.filter.text");
final String dists = i18n.get("label.filter.dist"); 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")); final StringBuilder filterMesgBuf = new StringBuilder(i18n.get("label.filter"));
filterMesgBuf.append(HawkbitCommonUtil.SP_STRING_SPACE); filterMesgBuf.append(HawkbitCommonUtil.SP_STRING_SPACE);
filterMesgBuf.append(getStatusMsg(targFilParams.getClickedStatusTargetTags(), status)); filterMesgBuf.append(getStatusMsg(targFilParams.getClickedStatusTargetTags(), status));
filterMesgBuf.append(getOverdueStateMsg(targFilParams.isOverdueFilterEnabled(), overdue));
filterMesgBuf filterMesgBuf
.append(getTagsMsg(targFilParams.isNoTagSelected(), targFilParams.getClickedTargetTags(), tags)); .append(getTagsMsg(targFilParams.isNoTagSelected(), targFilParams.getClickedTargetTags(), tags));
filterMesgBuf.append( filterMesgBuf.append(
@@ -220,6 +222,17 @@ public class CountMessageLabel extends Label {
return status.isEmpty() ? HawkbitCommonUtil.SP_STRING_SPACE : param; 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. * Get Tags Message.
* *

View File

@@ -31,6 +31,8 @@ public class TargetTableFilters implements Serializable {
private final List<String> clickedTargetTags = new ArrayList<>(); private final List<String> clickedTargetTags = new ArrayList<>();
private final List<TargetUpdateStatus> clickedStatusTargetTags = new ArrayList<>(); private final List<TargetUpdateStatus> clickedStatusTargetTags = new ArrayList<>();
private boolean isOverdueFilterEnabled = Boolean.FALSE;
private String searchText; private String searchText;
private DistributionSetIdName distributionSet; private DistributionSetIdName distributionSet;
private Long pinnedDistId; private Long pinnedDistId;
@@ -130,7 +132,8 @@ public class TargetTableFilters implements Serializable {
* {@code false} * {@code false}
*/ */
public boolean hasFilter() { public boolean hasFilter() {
return isFilteredByTextOrDs() || hasTagsSelected() || isFilteredByStatusOrCustomFilter(); return isFilteredByTextOrDs() || hasTagsSelected() || isFilteredByStatusOrCustomFilter()
|| isOverdueFilterEnabled();
} }
private boolean hasTagsSelected() { private boolean hasTagsSelected() {
@@ -160,4 +163,12 @@ public class TargetTableFilters implements Serializable {
this.targetFilterQuery = targetFilterQuery; 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; 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.apache.commons.lang3.ArrayUtils.isEmpty;
import static org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil.isNotNullOrEmpty; import static org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil.isNotNullOrEmpty;
import static org.eclipse.hawkbit.ui.utils.SPUIDefinitions.TARGET_TABLE_CREATE_AT_SORT_ORDER; 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.List;
import java.util.Map; import java.util.Map;
import org.eclipse.hawkbit.repository.FilterParams;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.TargetManagement; 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 Sort sort = new Sort(TARGET_TABLE_CREATE_AT_SORT_ORDER, "createdAt");
private transient Collection<TargetUpdateStatus> status; private transient Collection<TargetUpdateStatus> status;
private transient Boolean overdueState;
private String[] targetTags; private String[] targetTags;
private Long distributionId; private Long distributionId;
private String searchText; private String searchText;
@@ -81,6 +84,7 @@ public class TargetBeanQuery extends AbstractBeanQuery<ProxyTarget> {
if (isNotNullOrEmpty(queryConfig)) { if (isNotNullOrEmpty(queryConfig)) {
status = (Collection<TargetUpdateStatus>) queryConfig.get(SPUIDefinitions.FILTER_BY_STATUS); 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); targetTags = (String[]) queryConfig.get(SPUIDefinitions.FILTER_BY_TAG);
noTagClicked = (Boolean) queryConfig.get(SPUIDefinitions.FILTER_BY_NO_TAG); noTagClicked = (Boolean) queryConfig.get(SPUIDefinitions.FILTER_BY_NO_TAG);
distributionId = (Long) queryConfig.get(SPUIDefinitions.FILTER_BY_DISTRIBUTION); distributionId = (Long) queryConfig.get(SPUIDefinitions.FILTER_BY_DISTRIBUTION);
@@ -114,17 +118,17 @@ public class TargetBeanQuery extends AbstractBeanQuery<ProxyTarget> {
if (pinnedDistId != null) { if (pinnedDistId != null) {
targetBeans = getTargetManagement().findTargetsAllOrderByLinkedDistributionSet( targetBeans = getTargetManagement().findTargetsAllOrderByLinkedDistributionSet(
new OffsetBasedPageRequest(startIndex, SPUIDefinitions.PAGE_SIZE, sort), pinnedDistId, 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) { } else if (null != targetFilterQuery) {
targetBeans = getTargetManagement().findTargetsAll(targetFilterQuery, targetBeans = getTargetManagement().findTargetsAll(targetFilterQuery,
new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort)); new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort));
} else if (!anyFilterSelected()) { } else if (!isAnyFilterSelected()) {
targetBeans = getTargetManagement().findTargetsAll( targetBeans = getTargetManagement().findTargetsAll(
new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort)); new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort));
} else { } else {
targetBeans = getTargetManagement().findTargetByFilters( targetBeans = getTargetManagement().findTargetByFilters(
new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort), status, 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) { for (final Target targ : targetBeans) {
final ProxyTarget prxyTarget = new ProxyTarget(); final ProxyTarget prxyTarget = new ProxyTarget();
@@ -170,31 +174,27 @@ public class TargetBeanQuery extends AbstractBeanQuery<ProxyTarget> {
return true; return true;
} }
private boolean isOverdueFilterEnabled() {
return Boolean.TRUE.equals(overdueState);
}
@Override @Override
protected void saveBeans(final List<ProxyTarget> addedTargets, final List<ProxyTarget> modifiedTargets, protected void saveBeans(final List<ProxyTarget> addedTargets, final List<ProxyTarget> modifiedTargets,
final List<ProxyTarget> removedTargets) { final List<ProxyTarget> removedTargets) {
// CRUD operations on Target will be done through repository methods // 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 @Override
public int size() { public int size() {
final long totSize = getTargetManagement().countTargetsAll(); final long totSize = getTargetManagement().countTargetsAll();
long size; long size;
if (null != targetFilterQuery) { if (null != targetFilterQuery) {
size = getTargetManagement().countTargetByTargetFilterQuery(targetFilterQuery); size = getTargetManagement().countTargetByTargetFilterQuery(targetFilterQuery);
} else if (!anyFilterSelected()) { } else if (!isAnyFilterSelected()) {
size = totSize; size = totSize;
} else { } else {
size = getTargetManagement().countTargetByFilters(status, searchText, distributionId, noTagClicked, size = getTargetManagement().countTargetByFilters(status, overdueState, searchText, distributionId,
targetTags); noTagClicked, targetTags);
} }
final ManagementUIState tmpManagementUIState = getManagementUIState(); final ManagementUIState tmpManagementUIState = getManagementUIState();
@@ -209,6 +209,11 @@ public class TargetBeanQuery extends AbstractBeanQuery<ProxyTarget> {
return (int) size; return (int) size;
} }
private boolean isAnyFilterSelected() {
final boolean isFilterSelected = isTagSelected() || isOverdueFilterEnabled();
return isFilterSelected || CollectionUtils.isNotEmpty(status) || distributionId != null || !isNullOrEmpty(searchText);
}
private TargetManagement getTargetManagement() { private TargetManagement getTargetManagement() {
if (targetManagement == null) { if (targetManagement == null) {
targetManagement = SpringContextHelper.getBean(TargetManagement.class); targetManagement = SpringContextHelper.getBean(TargetManagement.class);

View File

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

View File

@@ -54,16 +54,20 @@ public class FilterByStatusLayout extends VerticalLayout implements Button.Click
@Autowired @Autowired
private ManagementUIState managementUIState; private ManagementUIState managementUIState;
private static final String OVERDUE_CAPTION = "overdue";
private Button unknown; private Button unknown;
private Button inSync; private Button inSync;
private Button pending; private Button pending;
private Button error; private Button error;
private Button registered; private Button registered;
private Button overdue;
private Boolean unknownBtnClicked = false; private Boolean unknownBtnClicked = false;
private Boolean errorBtnClicked = false; private Boolean errorBtnClicked = false;
private Boolean pendingBtnClicked = false; private Boolean pendingBtnClicked = false;
private Boolean inSyncBtnClicked = false; private Boolean inSyncBtnClicked = false;
private Boolean registeredBtnClicked = false; private Boolean registeredBtnClicked = false;
private Boolean overdueBtnClicked = false;
private Button buttonClicked; private Button buttonClicked;
private static final String BTN_CLICKED = "btnClicked"; private static final String BTN_CLICKED = "btnClicked";
@@ -103,7 +107,17 @@ public class FilterByStatusLayout extends VerticalLayout implements Button.Click
buttonLayout.addComponent(registered); buttonLayout.addComponent(registered);
buttonLayout.setComponentAlignment(registered, Alignment.MIDDLE_CENTER); buttonLayout.setComponentAlignment(registered, Alignment.MIDDLE_CENTER);
addComponent(buttonLayout); 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, registered = SPUIComponentProvider.getButton(UIComponentIdProvider.REGISTERED_STATUS_ICON,
TargetUpdateStatus.REGISTERED.toString(), i18n.get("tooltip.status.registered"), TargetUpdateStatus.REGISTERED.toString(), i18n.get("tooltip.status.registered"),
SPUIButtonDefinitions.SP_BUTTON_STATUS_STYLE, false, FontAwesome.SQUARE, SPUIButtonStyleSmall.class); 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(); applyStatusBtnStyle();
unknown.setData("filterStatusOne"); unknown.setData("filterStatusOne");
inSync.setData("filterStatusTwo"); inSync.setData("filterStatusTwo");
pending.setData("filterStatusThree"); pending.setData("filterStatusThree");
error.setData("filterStatusFour"); error.setData("filterStatusFour");
registered.setData("filterStatusFive"); registered.setData("filterStatusFive");
overdue.setData("filterStatusSix");
unknown.addClickListener(this); unknown.addClickListener(this);
inSync.addClickListener(this); inSync.addClickListener(this);
pending.addClickListener(this); pending.addClickListener(this);
error.addClickListener(this); error.addClickListener(this);
registered.addClickListener(this); registered.addClickListener(this);
overdue.addClickListener(this);
} }
/** /**
@@ -173,6 +196,7 @@ public class FilterByStatusLayout extends VerticalLayout implements Button.Click
pending.addStyleName("pendingBtn"); pending.addStyleName("pendingBtn");
error.addStyleName("errorBtn"); error.addStyleName("errorBtn");
registered.addStyleName("registeredBtn"); registered.addStyleName("registeredBtn");
overdue.addStyleName("overdueBtn");
} }
@Override @Override
@@ -188,6 +212,8 @@ public class FilterByStatusLayout extends VerticalLayout implements Button.Click
processErrorFilterStatus(); processErrorFilterStatus();
} else if (event.getButton().getCaption().equalsIgnoreCase(TargetUpdateStatus.REGISTERED.toString())) { } else if (event.getButton().getCaption().equalsIgnoreCase(TargetUpdateStatus.REGISTERED.toString())) {
processRegisteredFilterStatus(); 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); 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. * Process - COMMON PROCESS.
* *
* @param status * @param status
* as enum * as enum
* @param buttonReset * @param buttonReset
@@ -268,11 +311,12 @@ public class FilterByStatusLayout extends VerticalLayout implements Button.Click
inSync.removeStyleName(BTN_CLICKED); inSync.removeStyleName(BTN_CLICKED);
error.removeStyleName(BTN_CLICKED); error.removeStyleName(BTN_CLICKED);
pending.removeStyleName(BTN_CLICKED); pending.removeStyleName(BTN_CLICKED);
overdue.removeStyleName(BTN_CLICKED);
} }
/** /**
* Check if any status button in clicked. * Check if any status button in clicked.
* *
* @return * @return
*/ */
private boolean isStatusFilterApplied() { private boolean isStatusFilterApplied() {

View File

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

View File

@@ -189,6 +189,10 @@ public final class UIComponentIdProvider {
* REGISTERED_STATUS_ICON ID. * REGISTERED_STATUS_ICON ID.
*/ */
public static final String REGISTERED_STATUS_ICON = "registered.status.icon"; 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. * DROP filter icon id.
*/ */

View File

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

View File

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

View File

@@ -178,7 +178,7 @@
} }
.statusIconLightBlue { .statusIconLightBlue {
color: $bosch-color-light-blue; color: $signal-light-blue-color;
} }
.v-button-statusIconLightBlue:after { .v-button-statusIconLightBlue:after {
@@ -219,7 +219,7 @@
pointer-events: auto !important; pointer-events: auto !important;
} }
.blueSpinner{ .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; pointer-events: auto !important;
} }

View File

@@ -9,9 +9,9 @@
@mixin target-filter-query { @mixin target-filter-query {
.gwt-MenuBar-autocomplete { .gwt-MenuBar-autocomplete {
cursor: default; cursor: default;
} }
.gwt-MenuBar-autocomplete .gwt-MenuItem{ .gwt-MenuBar-autocomplete .gwt-MenuItem{
border-radius: 3px !important; border-radius: 3px !important;
cursor: pointer !important; cursor: pointer !important;
@@ -21,7 +21,7 @@
position: relative !important; position: relative !important;
white-space: nowrap !important; white-space: nowrap !important;
} }
.gwt-MenuBar-autocomplete .gwt-MenuItem-selected { .gwt-MenuBar-autocomplete .gwt-MenuItem-selected {
background-color: $hawkbit-primary-color; background-color: $hawkbit-primary-color;
background-image: linear-gradient(to bottom, #1b87e3 2%, #166ed5 98%) !important; 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.shown = Shown :
label.filter.targets = Filtered Targets : label.filter.targets = Filtered Targets :
label.filter.status = Status, label.filter.status = Status,
label.filter.overdue = Overdue,
label.filter.tags = Tags, label.filter.tags = Tags,
label.filter.text = Search Text label.filter.text = Search Text
label.filter.dist = Distribution, label.filter.dist = Distribution,
@@ -168,6 +169,7 @@ label.target.id = Controller Id :
label.target.ip = Controller IP : label.target.ip = Controller IP :
label.target.security.token = Security token : label.target.security.token = Security token :
label.filter.by.status = Filter by Status label.filter.by.status = Filter by Status
label.filter.by.overdue = Filter by Overdue
label.target.controller.attrs = <b>Controller attributes</b> label.target.controller.attrs = <b>Controller attributes</b>
label.target.lastpolldate = Last poll : label.target.lastpolldate = Last poll :
label.tag.name = Tag name label.tag.name = Tag name
@@ -203,6 +205,7 @@ tooltip.status.registered = Registered
tooltip.status.pending = Pending tooltip.status.pending = Pending
tooltip.status.error = Error tooltip.status.error = Error
tooltip.status.insync = In-sync tooltip.status.insync = In-sync
tooltip.status.overdue = Overdue
tooltip.delete.module = Select and delete Software Module tooltip.delete.module = Select and delete Software Module
tooltip.forced.item=Forced update action tooltip.forced.item=Forced update action
tooltip.soft.item=Soft update action which interacts with an user as a attempt update action for example 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> <org.easytesting.version>2.0M10</org.easytesting.version>
<allure.version>1.4.22</allure.version> <allure.version>1.4.22</allure.version>
<eclipselink.version>2.6.2</eclipselink.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> <pl.pragmatists.version>1.0.2</pl.pragmatists.version>
<guava.version>19.0</guava.version> <guava.version>19.0</guava.version>
<mariadb-java-client.version>1.5.3</mariadb-java-client.version> <mariadb-java-client.version>1.5.3</mariadb-java-client.version>
@@ -675,6 +675,18 @@
<version>${pl.pragmatists.version}</version> <version>${pl.pragmatists.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </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> <dependency>
<groupId>org.mariadb.jdbc</groupId> <groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId> <artifactId>mariadb-java-client</artifactId>