Cleanup TargetManagement (#2601)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-08-08 13:13:28 +03:00
committed by GitHub
parent 5217297c24
commit bff77ac224
35 changed files with 433 additions and 2032 deletions

View File

@@ -1,81 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository;
import java.util.Collection;
import lombok.Data;
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.
*/
@Data
public class FilterParams {
private final Collection<TargetUpdateStatus> filterByStatus;
private final Boolean overdueState;
private final String filterBySearchText;
private final Boolean selectTargetWithNoTag;
private final String[] filterByTagNames;
private final Long filterByDistributionId;
private final Boolean selectTargetWithNoTargetType;
private final Long filterByTargetType;
/**
* Constructor for the filter parameters of a Simple Filter.
*
* @param filterByInstalledOrAssignedDistributionSetId 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(final Collection<TargetUpdateStatus> filterByStatus, final Boolean overdueState,
final String filterBySearchText, final Long filterByInstalledOrAssignedDistributionSetId,
final Boolean selectTargetWithNoTag, final String... filterByTagNames) {
this.filterByStatus = filterByStatus;
this.overdueState = overdueState;
this.filterBySearchText = filterBySearchText;
this.filterByDistributionId = filterByInstalledOrAssignedDistributionSetId;
this.selectTargetWithNoTag = selectTargetWithNoTag;
this.filterByTagNames = filterByTagNames;
this.selectTargetWithNoTargetType = false;
this.filterByTargetType = null;
}
/**
* Constructor for the filter parameters of a Type Filter.
*
* @param filterBySearchText if set, a filter is added for the given search text
* @param filterByInstalledOrAssignedDistributionSetId if set, a filter is added for the given
* {@link DistributionSet#getId()}
* @param selectTargetWithNoType if true, a filter is added with no type
* @param filterByType if set, a filter is added for the given target type
*/
public FilterParams(final String filterBySearchText, final Long filterByInstalledOrAssignedDistributionSetId,
final Boolean selectTargetWithNoType, final Long filterByType) {
this.filterBySearchText = filterBySearchText;
this.filterByDistributionId = filterByInstalledOrAssignedDistributionSetId;
this.filterByStatus = null;
this.overdueState = null;
this.selectTargetWithNoTag = false;
this.filterByTagNames = null;
this.selectTargetWithNoTargetType = selectTargetWithNoType;
this.filterByTargetType = filterByType;
}
}

View File

@@ -51,7 +51,6 @@ import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -81,104 +80,60 @@ public interface TargetManagement<T extends Target>
}
/**
* Counts number of targets with the given distribution set assigned.
* Get controller attributes of given {@link Target}.
*
* @param distributionSetId to search for
* @return number of found {@link Target}s.
* @throws EntityNotFoundException if distribution set with given ID does not exist
*/
@PreAuthorize(HAS_AUTH_READ_DISTRIBUTION_SET_AND_READ_TARGET)
long countByAssignedDistributionSet(long distributionSetId);
/**
* Count {@link Target}s for all the given filter parameters.
*
* @param filterParams the filters to apply; only filters are enabled that have non-null
* value; filters are AND-gated
* @return the found number {@link Target}s
* @throws EntityNotFoundException if distribution set with given ID does not exist
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
long countByFilters(@NotNull final FilterParams filterParams);
/**
* Get the count of targets with the given distribution set id.
*
* @param distributionSetId to search for
* @return number of found {@link Target}s.
* @throws EntityNotFoundException if distribution set with given ID does not exist
*/
@PreAuthorize(HAS_AUTH_READ_DISTRIBUTION_SET_AND_READ_TARGET)
long countByInstalledDistributionSet(long distributionSetId);
/**
* Checks if there is already a {@link Target} that has the given distribution
* set Id assigned or installed.
*
* @param distributionSetId to search for
* @return <code>true</code> if a {@link Target} exists.
* @throws EntityNotFoundException if distribution set with given ID does not exist
*/
@PreAuthorize(HAS_AUTH_READ_DISTRIBUTION_SET_AND_READ_TARGET)
boolean existsByInstalledOrAssignedDistributionSet(long distributionSetId);
/**
* Count {@link TargetFilterQuery}s for given target filter query with UPDATE permission.
*
* @param rsql filter definition in RSQL syntax
* @return the found number of {@link Target}s
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
long countByRsqlAndUpdatable(@NotEmpty String rsql);
/**
* Count all targets for given {@link TargetFilterQuery} and that are compatible
* with the passed {@link DistributionSetType}.
*
* @param rsql filter definition in RSQL syntax
* @param distributionSetIdTypeId ID of the {@link DistributionSetType} the targets need to be
* compatible with
* @return the found number of{@link Target}s
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
long countByRsqlAndCompatible(@NotEmpty String rsql, @NotNull Long distributionSetIdTypeId);
/**
* Count all targets with failed actions for specific Rollout and that are
* compatible with the passed {@link DistributionSetType} and created after
* given timestamp
*
* @param rolloutId rolloutId of the rollout to be retried.
* @param dsTypeId ID of the {@link DistributionSetType} the targets need to be
* compatible with
* @return the found number of{@link Target}s
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
long countByFailedInRollout(@NotEmpty String rolloutId, @NotNull Long dsTypeId);
/**
* Count {@link TargetFilterQuery}s for given target filter query.
*
* @param targetFilterQueryId {@link TargetFilterQuery#getId()}
* @return the found number of {@link Target}s
* @throws EntityNotFoundException if {@link TargetFilterQuery} with given ID does not exist
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
long countByTargetFilterQuery(long targetFilterQueryId);
/**
* Deletes target with the given controller ID.
*
* @param controllerId the controller ID of the target to be deleted
* @param controllerId of the target
* @return controller attributes as key/value pairs
* @throws EntityNotFoundException if target with given ID does not exist
*/
@PreAuthorize(HAS_AUTH_DELETE_TARGET)
void deleteByControllerID(@NotEmpty String controllerId);
@PreAuthorize(HAS_AUTH_READ_TARGET)
Map<String, String> getControllerAttributes(@NotEmpty String controllerId);
/**
* Finds all targets for all the given parameter {@link TargetFilterQuery} and
* that don't have the specified distribution set in their action history and
* are compatible with the passed {@link DistributionSetType}.
* Verify if a target matches a specific target filter query, does not have a
* specific DS already assigned and is compatible with it.
*
* @param controllerId of the {@link org.eclipse.hawkbit.repository.model.Target} to check
* @param distributionSetId of the {@link org.eclipse.hawkbit.repository.model.DistributionSet} to consider
* @param targetFilterQuery to execute
* @return true if it matches
*/
@PreAuthorize(HAS_AUTH_READ_DISTRIBUTION_SET_AND_READ_TARGET)
boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
@NotNull String controllerId, long distributionSetId, @NotNull String targetFilterQuery);
/**
* Find {@link Target}s based a given IDs.
*
* @param controllerIDs to look for.
* @return List of found{@link Target}s
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
List<Target> getByControllerId(@NotEmpty Collection<String> controllerIDs);
/**
* Gets a {@link Target} based a given controller id and includes the details specified by the details key.
*
* @param controllerId to look for.
* @param detailsKey the key of the details to include, e.g. {@link #DETAILS_AUTO_CONFIRMATION_STATUS}
* @return {@link Target}
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
Target getWithDetails(@NotEmpty String controllerId, String detailsKey);
@PreAuthorize(HAS_AUTH_READ_TARGET)
default Target getWithDetails(@NotEmpty String controllerId) {
return getWithDetails(controllerId, DETAILS_BASE);
}
@PreAuthorize(HAS_AUTH_READ_TARGET)
default Target getWithAutoConfigurationStatus(@NotEmpty String controllerId) {
return getWithDetails(controllerId, DETAILS_AUTO_CONFIRMATION_STATUS);
}
/**
* Finds all targets for all the given parameter {@link TargetFilterQuery} and that don't have the specified distribution
* set in their action history and are compatible with the passed {@link DistributionSetType}.
*
* @param distributionSetId id of the {@link DistributionSet}
* @param rsql filter definition in RSQL syntax
@@ -191,27 +146,12 @@ public interface TargetManagement<T extends Target>
long distributionSetId, @NotNull String rsql, @NotNull Pageable pageable);
/**
* Counts all targets for all the given parameter {@link TargetFilterQuery} and
* that don't have the specified distribution set in their action history and
* are compatible with the passed {@link DistributionSetType}.
*
* @param distributionSetId id of the {@link DistributionSet}
* @param rsql filter definition in RSQL syntax
* @return the count of found {@link Target}s
* @throws EntityNotFoundException if distribution set with given ID does not exist
*/
@PreAuthorize(HAS_AUTH_READ_DISTRIBUTION_SET_AND_READ_TARGET)
long countByRsqlAndNonDSAndCompatibleAndUpdatable(long distributionSetId, @NotNull String rsql);
/**
* Finds all targets for all the given parameter {@link TargetFilterQuery} and
* that are not assigned to one of the {@link RolloutGroup}s and are compatible
* with the passed {@link DistributionSetType}.
* Finds all targets for all the given parameter {@link TargetFilterQuery} and that are not assigned to one of the {@link RolloutGroup}s
* and are compatible with the passed {@link DistributionSetType}.
*
* @param groups the list of {@link RolloutGroup}s
* @param rsql filter definition in RSQL syntax
* @param distributionSetType type of the {@link DistributionSet} the targets must be compatible
* withs
* @param distributionSetType type of the {@link DistributionSet} the targets must be compatible withs
* @param pageable the pageable to enhance the query for paging and sorting
* @return a page of the found {@link Target}s
*/
@@ -221,23 +161,8 @@ public interface TargetManagement<T extends Target>
@NotNull Pageable pageable);
/**
* Counts all targets for all the given parameter {@link TargetFilterQuery} and
* that are not assigned to one of the {@link RolloutGroup}s and are compatible
* with the passed {@link DistributionSetType}.
*
* @param rsql filter definition in RSQL syntax
* @param groups the list of {@link RolloutGroup}s
* @param distributionSetType type of the {@link DistributionSet} the targets must be compatible with
* @return count of the found {@link Target}s
*/
@PreAuthorize(HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ)
long countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
@NotNull String rsql, @NotEmpty Collection<Long> groups, @NotNull DistributionSetType distributionSetType);
/**
* Finds all targets with failed actions for specific Rollout and that are not
* assigned to one of the retried {@link RolloutGroup}s and are compatible with
* the passed {@link DistributionSetType}.
* Finds all targets with failed actions for specific Rollout and that are not assigned to one of the retried {@link RolloutGroup}s and
* are compatible with the passed {@link DistributionSetType}.
*
* @param rolloutId rolloutId of the rollout to be retried.
* @param groups the list of {@link RolloutGroup}s
@@ -248,28 +173,12 @@ public interface TargetManagement<T extends Target>
Slice<Target> findByFailedRolloutAndNotInRolloutGroups(
@NotNull String rolloutId, @NotEmpty Collection<Long> groups, @NotNull Pageable pageable);
/**
* Counts all targets with failed actions for specific Rollout and that are not
* assigned to one of the {@link RolloutGroup}s and are compatible with the
* passed {@link DistributionSetType}.
*
* @param rolloutId rolloutId of the rollout to be retried.
* @param groups the list of {@link RolloutGroup}s
* @return count of the found {@link Target}s
*/
@PreAuthorize(HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ)
long countByFailedRolloutAndNotInRolloutGroups(@NotNull String rolloutId, @NotEmpty Collection<Long> groups);
@PreAuthorize(HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ)
Slice<Target> findByRsqlAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable(
final long rolloutId, @NotNull String rsql, @NotNull DistributionSetType distributionSetType, @NotNull Pageable pageable);
@PreAuthorize(HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ)
long countByActionsInRolloutGroup(final long rolloutGroupId);
/**
* Finds all targets of the provided {@link RolloutGroup} that have no Action
* for the RolloutGroup.
* Finds all targets of the provided {@link RolloutGroup} that have no Action for the RolloutGroup.
*
* @param group the {@link RolloutGroup}
* @param pageable the pageable to enhance the query for paging and sorting
@@ -297,8 +206,8 @@ public interface TargetManagement<T extends Target>
* @param rsql the specification to filter the result set
* @param pageable page parameter
* @return the found {@link Target}s, never {@code null}
* @throws RSQLParameterUnsupportedFieldException if a field in the RSQL string is used but not provided by the
* given {@code fieldNameProvider}
* @throws RSQLParameterUnsupportedFieldException if a field in the RSQL string is used but not provided
* by the given {@code fieldNameProvider}
* @throws RSQLParameterSyntaxException if the RSQL syntax is wrong
* @throws EntityNotFoundException if distribution set with given ID does not exist
*/
@@ -306,13 +215,66 @@ public interface TargetManagement<T extends Target>
Page<Target> findByAssignedDistributionSetAndRsql(long distributionSetId, @NotNull String rsql, @NotNull Pageable pageable);
/**
* Find {@link Target}s based a given IDs.
* Count all targets for given {@link TargetFilterQuery} and that are compatible
* with the passed {@link DistributionSetType}.
*
* @param controllerIDs to look for.
* @return List of found{@link Target}s
* @param rsql filter definition in RSQL syntax
* @param distributionSetIdTypeId ID of the {@link DistributionSetType} the targets need to be
* compatible with
* @return the found number of{@link Target}s
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
List<Target> getByControllerId(@NotEmpty Collection<String> controllerIDs);
long countByRsqlAndCompatible(@NotEmpty String rsql, @NotNull Long distributionSetIdTypeId);
/**
* Count all targets with failed actions for specific Rollout and that are compatible with the passed {@link DistributionSetType} and
* created after given timestamp
*
* @param rolloutId rolloutId of the rollout to be retried.
* @param dsTypeId ID of the {@link DistributionSetType} the targets need to be compatible with
* @return the found number of{@link Target}s
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
long countByFailedInRollout(@NotEmpty String rolloutId, @NotNull Long dsTypeId);
/**
* Counts all targets for all the given parameter {@link TargetFilterQuery} and that don't have the specified distribution set in their
* action history and are compatible with the passed {@link DistributionSetType}.
*
* @param distributionSetId id of the {@link DistributionSet}
* @param rsql filter definition in RSQL syntax
* @return the count of found {@link Target}s
* @throws EntityNotFoundException if distribution set with given ID does not exist
*/
@PreAuthorize(HAS_AUTH_READ_DISTRIBUTION_SET_AND_READ_TARGET)
long countByRsqlAndNonDsAndCompatibleAndUpdatable(long distributionSetId, @NotNull String rsql);
/**
* Counts all targets for all the given parameter {@link TargetFilterQuery} and that are not assigned to one of the {@link RolloutGroup}s
* and are compatible with the passed {@link DistributionSetType}.
*
* @param rsql filter definition in RSQL syntax
* @param groups the list of {@link RolloutGroup}s
* @param distributionSetType type of the {@link DistributionSet} the targets must be compatible with
* @return count of the found {@link Target}s
*/
@PreAuthorize(HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ)
long countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
@NotNull String rsql, @NotEmpty Collection<Long> groups, @NotNull DistributionSetType distributionSetType);
/**
* Counts all targets with failed actions for specific Rollout and that are not assigned to one of the {@link RolloutGroup}s and are
* compatible with the passed {@link DistributionSetType}.
*
* @param rolloutId rolloutId of the rollout to be retried.
* @param groups the list of {@link RolloutGroup}s
* @return count of the found {@link Target}s
*/
@PreAuthorize(HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ)
long countByFailedRolloutAndNotInRolloutGroups(@NotNull String rolloutId, @NotEmpty Collection<Long> groups);
@PreAuthorize(HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ)
long countByActionsInRolloutGroup(final long rolloutGroupId);
/**
* Find a {@link Target} based a given ID.
@@ -323,44 +285,6 @@ public interface TargetManagement<T extends Target>
@PreAuthorize(HAS_AUTH_READ_TARGET)
Optional<Target> getByControllerId(@NotEmpty String controllerId);
/**
* Gets a {@link Target} based a given controller id and includes the details specified by the details key.
*
* @param controllerId to look for.
* @param detailsKey the key of the details to include, e.g. {@link #DETAILS_AUTO_CONFIRMATION_STATUS}
* @return {@link Target}
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
Target getWithDetails(@NotEmpty String controllerId, String detailsKey);
@PreAuthorize(HAS_AUTH_READ_TARGET)
default Target getWithDetails(@NotEmpty String controllerId) {
return getWithDetails(controllerId, DETAILS_BASE);
}
@PreAuthorize(HAS_AUTH_READ_TARGET)
default Target getWithAutoConfigurationStatus(@NotEmpty String controllerId) {
return getWithDetails(controllerId, DETAILS_AUTO_CONFIRMATION_STATUS);
}
@PreAuthorize(HAS_AUTH_READ_TARGET)
default Target getWithTags(@NotEmpty String controllerId) {
return getWithDetails(controllerId, DETAILS_TAGS);
}
/**
* Filter {@link Target}s for all the given parameters. If all parameters except
* pageable are null, all available {@link Target}s are returned.
*
* @param filterParams the filters to apply; only filters are enabled that have non-null
* value; filters are AND-gated
* @param pageable page parameters
* @return the found {@link Target}s
* @throws EntityNotFoundException if distribution set with given ID does not exist
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
Slice<Target> findByFilters(@NotNull FilterParams filterParams, @NotNull Pageable pageable);
/**
* retrieves {@link Target}s by the installed {@link DistributionSet}.
*
@@ -388,30 +312,6 @@ public interface TargetManagement<T extends Target>
@PreAuthorize(HAS_AUTH_READ_DISTRIBUTION_SET_AND_READ_TARGET)
Page<Target> findByInstalledDistributionSetAndRsql(long distributionSetId, @NotNull String rsql, @NotNull Pageable pageReq);
/**
* Retrieves the {@link Target} which have a certain {@link TargetUpdateStatus}.
*
* @param status the {@link TargetUpdateStatus} to be filtered on
* @param pageable page parameter
* @return the found {@link Target}s
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
Page<Target> findByUpdateStatus(@NotNull TargetUpdateStatus status, @NotNull Pageable pageable);
/**
* Retrieves all target based on {@link TargetFilterQuery}.
*
* @param targetFilterQueryId {@link TargetFilterQuery#getId()}
* @param pageable pagination parameter
* @return the found {@link Target}s, never {@code null}
* @throws EntityNotFoundException if {@link TargetFilterQuery} with given ID does not exist.
* @throws RSQLParameterUnsupportedFieldException if a field in the RSQL string is used but not provided by the
* given {@code fieldNameProvider}
* @throws RSQLParameterSyntaxException if the RSQL syntax is wrong
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
Slice<Target> findByTargetFilterQuery(long targetFilterQueryId, @NotNull Pageable pageable);
/**
* Find targets by tag name.
*
@@ -438,39 +338,35 @@ public interface TargetManagement<T extends Target>
@PreAuthorize(HAS_AUTH_READ_TARGET)
Page<Target> findByRsqlAndTag(@NotNull String rsql, long tagId, @NotNull Pageable pageable);
/**
* Verify if a target matches a specific target filter query, does not have a
* specific DS already assigned and is compatible with it.
*
* @param controllerId of the {@link org.eclipse.hawkbit.repository.model.Target} to check
* @param distributionSetId of the {@link org.eclipse.hawkbit.repository.model.DistributionSet} to consider
* @param targetFilterQuery to execute
* @return true if it matches
*/
@PreAuthorize(HAS_AUTH_READ_DISTRIBUTION_SET_AND_READ_TARGET)
boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
@NotNull String controllerId, long distributionSetId, @NotNull String targetFilterQuery);
/**
* Initiates {@link TargetType} assignment to given {@link Target}s. If some targets in the list have the {@link TargetType}
* not yet assigned, they will get assigned. If all targets are already of that type, there will be no un-assignment.
* Deletes target with the given controller ID.
*
* @param controllerIds to set the type to
* @param typeId to assign targets to
* @return {@link TargetTypeAssignmentResult} with all meta-data of the assignment outcome.
* @throws EntityNotFoundException if target type with given id does not exist
* @param controllerId the controller ID of the target to be deleted
* @throws EntityNotFoundException if target with given ID does not exist
*/
@PreAuthorize(HAS_AUTH_UPDATE_TARGET)
TargetTypeAssignmentResult assignType(@NotEmpty Collection<String> controllerIds, @NotNull Long typeId);
@PreAuthorize(HAS_AUTH_DELETE_TARGET)
void deleteByControllerId(@NotEmpty String controllerId);
/**
* Initiates {@link TargetType} un-assignment to given {@link Target}s. The type of the targets will be set to {@code null}
* Assign a {@link TargetType} assignment to given {@link Target}.
*
* @param controllerIds to remove the type from
* @return {@link TargetTypeAssignmentResult} with all meta-data of the assignment outcome.
* @param controllerId to un-assign for
* @param targetTypeId Target type id
* @return the unassigned target
* @throws EntityNotFoundException if TargetType with given target ID does not exist
*/
@PreAuthorize(HAS_AUTH_UPDATE_TARGET)
TargetTypeAssignmentResult unassignType(@NotEmpty Collection<String> controllerIds);
Target assignType(@NotEmpty String controllerId, @NotNull Long targetTypeId);
/**
* Un-assign a {@link TargetType} assignment to given {@link Target}.
*
* @param controllerId to un-assign for
* @return the unassigned target
*/
@PreAuthorize(HAS_AUTH_UPDATE_TARGET)
Target unassignType(@NotEmpty String controllerId);
/**
* Assign a {@link TargetTag} assignment to given {@link Target}s.
@@ -495,6 +391,16 @@ public interface TargetManagement<T extends Target>
@PreAuthorize(HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
List<Target> assignTag(@NotEmpty Collection<String> controllerIds, long targetTagId);
/**
* Finds a single target tags its id.
*
* @param controllerId of the {@link Target}
* @return the found Tag set
* @throws EntityNotFoundException if target with given ID does not exist
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
Set<TargetTag> getTags(@NotEmpty String controllerId);
/**
* Un-assign a {@link TargetTag} assignment to given {@link Target}s.
*
@@ -519,24 +425,22 @@ public interface TargetManagement<T extends Target>
List<Target> unassignTag(@NotEmpty Collection<String> controllerIds, long targetTagId);
/**
* Un-assign a {@link TargetType} assignment to given {@link Target}.
* Assigns the target group of the targets matching the provided rsql filter.
*
* @param controllerId to un-assign for
* @return the unassigned target
* @param group target group parameter
* @param rsql rsql filter for {@link Target}
*/
@PreAuthorize(HAS_AUTH_UPDATE_TARGET)
Target unassignType(@NotEmpty String controllerId);
void assignTargetGroupWithRsql(String group, @NotNull String rsql);
/**
* Assign a {@link TargetType} assignment to given {@link Target}.
* Assigns the provided group to the targets which are in the provided list of controllerIds.
*
* @param controllerId to un-assign for
* @param targetTypeId Target type id
* @return the unassigned target
* @throws EntityNotFoundException if TargetType with given target ID does not exist
* @param group target group parameter
* @param controllerIds list of targets
*/
@PreAuthorize(HAS_AUTH_UPDATE_TARGET)
Target assignType(@NotEmpty String controllerId, @NotNull Long targetTypeId);
void assignTargetsWithGroup(String group, @NotEmpty List<String> controllerIds);
/**
* Finds targets by group or subgroup.
@@ -557,82 +461,6 @@ public interface TargetManagement<T extends Target>
@PreAuthorize(HAS_AUTH_READ_TARGET)
List<String> findGroups();
/**
* Assigns the target group of the targets matching the provided rsql filter.
*
* @param group target group parameter
* @param rsql rsql filter for {@link Target}
*/
@PreAuthorize(HAS_AUTH_UPDATE_TARGET)
void assignTargetGroupWithRsql(String group, @NotNull String rsql);
/**
* Assigns the provided group to the targets which are in the provided list of controllerIds.
*
* @param group target group parameter
* @param controllerIds list of targets
*/
@PreAuthorize(HAS_AUTH_UPDATE_TARGET)
void assignTargetsWithGroup(String group, @NotEmpty List<String> controllerIds);
/**
* Verifies that {@link Target} with given controller ID exists in the repository.
*
* @param controllerId of target
* @return {@code true} if target with given ID exists
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
boolean existsByControllerId(@NotEmpty String controllerId);
/**
* Finds a single target tags its id.
*
* @param controllerId of the {@link Target}
* @return the found Tag set
* @throws EntityNotFoundException if target with given ID does not exist
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
Set<TargetTag> getTags(@NotEmpty String controllerId);
/**
* Get controller attributes of given {@link Target}.
*
* @param controllerId of the target
* @return controller attributes as key/value pairs
* @throws EntityNotFoundException if target with given ID does not exist
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
Map<String, String> getControllerAttributes(@NotEmpty String controllerId);
/**
* Trigger given {@link Target} to update its attributes.
*
* @param controllerId of the target
* @throws EntityNotFoundException if target with given ID does not exist
*/
@PreAuthorize(HAS_AUTH_UPDATE_TARGET)
void requestControllerAttributes(@NotEmpty String controllerId);
/**
* Check if update of given {@link Target} attributes is already requested.
*
* @param controllerId of target
* @return {@code true}: update of controller attributes triggered.
* {@code false}: update of controller attributes not requested.
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
boolean isControllerAttributesRequested(@NotEmpty String controllerId);
/**
* Retrieves {@link Target}s where
* {@link #isControllerAttributesRequested(String)}.
*
* @param pageable page parameter
* @return the found {@link Target}s
*/
@PreAuthorize(HAS_AUTH_READ_TARGET)
Page<Target> findByControllerAttributesRequested(@NotNull Pageable pageable);
/**
* Creates or updates a meta-data value.
*
@@ -718,8 +546,8 @@ public interface TargetManagement<T extends Target>
// truncate controller ID to max name length (if too big)
name = ObjectUtils.isEmpty(builder.name)
? controllerId != null && controllerId.length() > NamedEntity.NAME_MAX_SIZE
? controllerId.substring(0, NamedEntity.NAME_MAX_SIZE)
: controllerId
? controllerId.substring(0, NamedEntity.NAME_MAX_SIZE)
: controllerId
: builder.name;
securityToken = ObjectUtils.isEmpty(builder.securityToken)
? SecurityTokenGeneratorHolder.getInstance().generateToken()

View File

@@ -55,9 +55,7 @@ public interface Action extends TenantAwareBaseEntity {
DistributionSet getDistributionSet();
/**
* @return <code>true</code> when action is in state
* {@link Status#CANCELING} or {@link Status#CANCELED}, false
* otherwise
* @return <code>true</code> when action is in state {@link Status#CANCELING} or {@link Status#CANCELED}, false otherwise
*/
default boolean isCancelingOrCanceled() {
return Status.CANCELING == getStatus() || Status.CANCELED == getStatus();
@@ -340,4 +338,4 @@ public interface Action extends TenantAwareBaseEntity {
DOWNLOAD_ONLY
}
}
}

View File

@@ -15,10 +15,8 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.hawkbit.repository.model.Action.Status;
/**
* Status information of an {@link Action} which can be provided by the
* {@link Target} or is added by the update server itself. This can be the start
* of the {@link Action} life cycle, the end and update notifications in
* between.
* Status information of an {@link Action} which can be provided by the {@link Target} or is added by the update server itself.
* This can be the start of the {@link Action} life cycle, the end and update notifications in between.
*/
public interface ActionStatus extends TenantAwareBaseEntity {
@@ -34,9 +32,8 @@ public interface ActionStatus extends TenantAwareBaseEntity {
Action getAction();
/**
* @return the {@link Status} of this {@link ActionStatus}. Caused
* potentially a transition change of the {@link #getAction()} if
* different from the previous {@link ActionStatus#getStatus()}.
* @return the {@link Status} of this {@link ActionStatus}. Caused potentially a transition change of the {@link #getAction()} if
* different from the previous {@link #getStatus()} call.
*/
Status getStatus();

View File

@@ -1,41 +0,0 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.model;
import java.util.List;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
/**
* Result object for {@link TargetType} assignments.
*/
@Getter
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class TargetTypeAssignmentResult extends AbstractAssignmentResult<Target> {
private final TargetType targetType;
/**
* Constructor.
*
* @param alreadyAssigned count of already assigned (ignored) elements
* @param assigned {@link List} of assigned {@link Target}s.
* @param unassigned {@link List} of unassigned {@link Target}s.
* @param targetType the assigned or unassigned tag
*/
public TargetTypeAssignmentResult(final int alreadyAssigned, final List<? extends Target> assigned,
final List<? extends Target> unassigned, final TargetType targetType) {
super(alreadyAssigned, assigned, unassigned);
this.targetType = targetType;
}
}

View File

@@ -9,33 +9,14 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
import java.util.Collections;
import java.util.Optional;
import jakarta.persistence.EntityManager;
import org.eclipse.hawkbit.repository.DistributionSetTagFields;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTagRepository;
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetTagSpecifications;
import org.eclipse.hawkbit.repository.jpa.specifications.TagSpecification;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "distribution-set-tag-management" }, matchIfMissing = true)

View File

@@ -211,7 +211,7 @@ class JpaTargetFilterQueryManagement
private void assertMaxTargetsQuota(final String query, final String filterName, final long dsId) {
QuotaHelper.assertAssignmentQuota(filterName,
targetManagement.countByRsqlAndNonDSAndCompatibleAndUpdatable(dsId, query),
targetManagement.countByRsqlAndNonDsAndCompatibleAndUpdatable(dsId, query),
quotaManagement.getMaxTargetsPerAutoAssignment(), Target.class, TargetFilterQuery.class, null);
}

View File

@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.jpa.JpaManagementHelper.combineWithAnd;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -35,14 +34,9 @@ import jakarta.persistence.criteria.Root;
import jakarta.persistence.metamodel.MapAttribute;
import jakarta.validation.constraints.NotEmpty;
import org.apache.commons.collections4.ListUtils;
import org.eclipse.hawkbit.repository.FilterParams;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TimestampCalculator;
import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
@@ -52,9 +46,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetFilterQueryRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
@@ -66,12 +58,8 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
@@ -82,7 +70,6 @@ import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;
/**
@@ -100,9 +87,7 @@ public class JpaTargetManagement
private final QuotaManagement quotaManagement;
private final TargetTypeRepository targetTypeRepository;
private final RolloutGroupRepository rolloutGroupRepository;
private final TargetFilterQueryRepository targetFilterQueryRepository;
private final TargetTagRepository targetTagRepository;
private final TenantAware tenantAware;
@SuppressWarnings("java:S107")
protected JpaTargetManagement(
@@ -110,83 +95,41 @@ public class JpaTargetManagement
final JpaDistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
final TargetTypeRepository targetTypeRepository,
final RolloutGroupRepository rolloutGroupRepository,
final TargetFilterQueryRepository targetFilterQueryRepository,
final TargetTagRepository targetTagRepository,
final TenantAware tenantAware) {
final TargetTagRepository targetTagRepository) {
super(jpaRepository, entityManager);
this.distributionSetManagement = distributionSetManagement;
this.quotaManagement = quotaManagement;
this.targetTypeRepository = targetTypeRepository;
this.rolloutGroupRepository = rolloutGroupRepository;
this.targetFilterQueryRepository = targetFilterQueryRepository;
this.targetTagRepository = targetTagRepository;
this.tenantAware = tenantAware;
}
@Override
public long countByAssignedDistributionSet(final long distributionSetId) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distributionSetId));
return jpaRepository.count(TargetSpecifications.hasAssignedDistributionSet(validDistSet.getId()));
public Map<String, String> getControllerAttributes(final String controllerId) {
return getMap(controllerId, JpaTarget_.controllerAttributes);
}
@Override
public long countByFilters(final FilterParams filterParams) {
final List<Specification<JpaTarget>> specList = buildSpecificationList(filterParams);
return JpaManagementHelper.countBySpec(jpaRepository, specList);
}
@Override
public long countByInstalledDistributionSet(final long distributionSetId) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distributionSetId));
return jpaRepository.count(TargetSpecifications.hasInstalledDistributionSet(validDistSet.getId()));
}
@Override
public boolean existsByInstalledOrAssignedDistributionSet(final long distributionSetId) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distributionSetId));
return jpaRepository.exists(TargetSpecifications.hasInstalledOrAssignedDistributionSet(validDistSet.getId()));
}
@Override
public long countByRsqlAndUpdatable(String rsql) {
public boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
final String controllerId, final long distributionSetId, final String targetFilterQuery) {
RsqlUtility.getInstance().validateRsqlFor(targetFilterQuery, TargetFields.class, JpaTarget.class);
final DistributionSet ds = distributionSetManagement.get(distributionSetId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, distributionSetId));
final Long distSetTypeId = ds.getType().getId();
final List<Specification<JpaTarget>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class));
return jpaRepository.count(AccessController.Operation.UPDATE, combineWithAnd(specList));
RsqlUtility.getInstance().buildRsqlSpecification(targetFilterQuery, TargetFields.class),
TargetSpecifications.hasNotDistributionSetInActions(distributionSetId),
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId),
TargetSpecifications.hasControllerId(controllerId));
final Specification<JpaTarget> combinedSpecification = Objects
.requireNonNull(SpecificationsBuilder.combineWithAnd(specList));
return jpaRepository.exists(AccessController.Operation.UPDATE, combinedSpecification);
}
@Override
public long countByRsqlAndCompatible(final String rsql, final Long distributionSetIdTypeId) {
final List<Specification<JpaTarget>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
TargetSpecifications.isCompatibleWithDistributionSetType(distributionSetIdTypeId));
return JpaManagementHelper.countBySpec(jpaRepository, specList);
}
@Override
public long countByFailedInRollout(final String rolloutId, final Long dsTypeId) {
final List<Specification<JpaTarget>> specList = List.of(TargetSpecifications.failedActionsForRollout(rolloutId));
return JpaManagementHelper.countBySpec(jpaRepository, specList);
}
@Override
public long countByTargetFilterQuery(final long targetFilterQueryId) {
final TargetFilterQuery targetFilterQuery = targetFilterQueryRepository
.findById(targetFilterQueryId)
.orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId));
return countByRsql(targetFilterQuery.getQuery());
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteByControllerID(final String controllerId) {
jpaRepository.delete(getByControllerIdAndThrowIfNotFound(controllerId));
}
@Override
public Slice<Target> findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(final long distributionSetId, final String rsql,
final Pageable pageable) {
public Slice<Target> findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(
final long distributionSetId, final String rsql, final Pageable pageable) {
final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final Long distSetTypeId = jpaDistributionSet.getType().getId();
@@ -202,16 +145,18 @@ public class JpaTargetManagement
}
@Override
public long countByRsqlAndNonDSAndCompatibleAndUpdatable(final long distributionSetId, final String rsql) {
final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final Long distSetTypeId = jpaDistributionSet.getType().getId();
public List<Target> getByControllerId(final Collection<String> controllerIDs) {
return Collections.unmodifiableList(jpaRepository.findAll(TargetSpecifications.byControllerIdWithAssignedDsInJoin(controllerIDs)));
}
return jpaRepository.count(
AccessController.Operation.UPDATE,
combineWithAnd(List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
TargetSpecifications.hasNotDistributionSetInActions(distributionSetId),
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId))));
@Override
public Optional<Target> getByControllerId(final String controllerId) {
return jpaRepository.findByControllerId(controllerId).map(Target.class::cast);
}
@Override
public Target getWithDetails(final String controllerId, final String detailsKey) {
return jpaRepository.getWithDetailsByControllerId(controllerId, "Target." + detailsKey);
}
@Override
@@ -227,16 +172,6 @@ public class JpaTargetManagement
.map(Target.class::cast);
}
@Override
public long countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
final String rsql, final Collection<Long> groups, final DistributionSetType dsType) {
return jpaRepository.count(AccessController.Operation.UPDATE,
combineWithAnd(List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
TargetSpecifications.isNotInRolloutGroups(groups),
TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId()))));
}
@Override
public Slice<Target> findByFailedRolloutAndNotInRolloutGroups(String rolloutId, Collection<Long> groups, Pageable pageable) {
final List<Specification<JpaTarget>> specList = List.of(
@@ -245,14 +180,6 @@ public class JpaTargetManagement
return JpaManagementHelper.findAllWithCountBySpec(jpaRepository, specList, pageable);
}
@Override
public long countByFailedRolloutAndNotInRolloutGroups(String rolloutId, Collection<Long> groups) {
final List<Specification<JpaTarget>> specList = List.of(
TargetSpecifications.failedActionsForRollout(rolloutId),
TargetSpecifications.isNotInRolloutGroups(groups));
return JpaManagementHelper.countBySpec(jpaRepository, specList);
}
@Override
public Slice<Target> findByRsqlAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable(
final long rolloutId, final String rsql, final DistributionSetType distributionSetType, final Pageable pageable) {
@@ -266,11 +193,6 @@ public class JpaTargetManagement
.map(Target.class::cast);
}
@Override
public long countByActionsInRolloutGroup(final long rolloutGroupId) {
return jpaRepository.count(TargetSpecifications.isInActionRolloutGroup(rolloutGroupId));
}
@Override
public Slice<Target> findByInRolloutGroupWithoutAction(final long group, final Pageable pageable) {
if (!rolloutGroupRepository.existsById(group)) {
@@ -301,27 +223,6 @@ public class JpaTargetManagement
return JpaManagementHelper.findAllWithCountBySpec(jpaRepository, specList, pageable);
}
@Override
public List<Target> getByControllerId(final Collection<String> controllerIDs) {
return Collections.unmodifiableList(jpaRepository.findAll(TargetSpecifications.byControllerIdWithAssignedDsInJoin(controllerIDs)));
}
@Override
public Optional<Target> getByControllerId(final String controllerId) {
return jpaRepository.findByControllerId(controllerId).map(Target.class::cast);
}
@Override
public Target getWithDetails(final String controllerId, final String detailsKey) {
return jpaRepository.getWithDetailsByControllerId(controllerId, "Target." + detailsKey);
}
@Override
public Slice<Target> findByFilters(final FilterParams filterParams, final Pageable pageable) {
final List<Specification<JpaTarget>> specList = buildSpecificationList(filterParams);
return JpaManagementHelper.findAllWithoutCountBySpec(jpaRepository, specList, pageable);
}
@Override
public Page<Target> findByInstalledDistributionSet(final long distributionSetId, final Pageable pageReq) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
@@ -341,23 +242,6 @@ public class JpaTargetManagement
return JpaManagementHelper.findAllWithCountBySpec(jpaRepository, specList, pageable);
}
@Override
public Page<Target> findByUpdateStatus(final TargetUpdateStatus status, final Pageable pageable) {
return JpaManagementHelper.findAllWithCountBySpec(
jpaRepository, List.of(TargetSpecifications.hasTargetUpdateStatus(status)), pageable);
}
@Override
public Slice<Target> findByTargetFilterQuery(final long targetFilterQueryId, final Pageable pageable) {
final TargetFilterQuery targetFilterQuery = targetFilterQueryRepository.findById(targetFilterQueryId)
.orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId));
return JpaManagementHelper.findAllWithoutCountBySpec(
jpaRepository, List.of(RsqlUtility.getInstance().buildRsqlSpecification(
targetFilterQuery.getQuery(), TargetFields.class)), pageable
);
}
@Override
public Page<Target> findByTag(final long tagId, final Pageable pageable) {
throwEntityNotFoundExceptionIfTagDoesNotExist(tagId);
@@ -376,44 +260,61 @@ public class JpaTargetManagement
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetTypeAssignmentResult assignType(final Collection<String> controllerIds, final Long typeId) {
final JpaTargetType type = targetTypeRepository
.findById(typeId)
.orElseThrow(() -> new EntityNotFoundException(TargetType.class, typeId));
public long countByRsqlAndCompatible(final String rsql, final Long distributionSetIdTypeId) {
final List<Specification<JpaTarget>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
TargetSpecifications.isCompatibleWithDistributionSetType(distributionSetIdTypeId));
return JpaManagementHelper.countBySpec(jpaRepository, specList);
}
final List<JpaTarget> targetsWithSameType = findTargetsByInSpecification(controllerIds, TargetSpecifications.hasTargetType(typeId));
final List<JpaTarget> targetsWithoutSameType =
findTargetsByInSpecification(controllerIds, TargetSpecifications.hasTargetTypeNot(typeId));
@Override
public long countByFailedInRollout(final String rolloutId, final Long dsTypeId) {
final List<Specification<JpaTarget>> specList = List.of(TargetSpecifications.failedActionsForRollout(rolloutId));
return JpaManagementHelper.countBySpec(jpaRepository, specList);
}
// set new target type to all targets without that type
targetsWithoutSameType.forEach(target -> target.setTargetType(type));
@Override
public long countByRsqlAndNonDsAndCompatibleAndUpdatable(final long distributionSetId, final String rsql) {
final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final Long distSetTypeId = jpaDistributionSet.getType().getId();
final TargetTypeAssignmentResult result = new TargetTypeAssignmentResult(
targetsWithSameType.size(), jpaRepository.saveAll(targetsWithoutSameType), Collections.emptyList(), type);
return jpaRepository.count(
AccessController.Operation.UPDATE,
combineWithAnd(List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
TargetSpecifications.hasNotDistributionSetInActions(distributionSetId),
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId))));
}
// no reason to persist the type
entityManager.detach(type);
return result;
@Override
public long countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
final String rsql, final Collection<Long> groups, final DistributionSetType dsType) {
return jpaRepository.count(AccessController.Operation.UPDATE,
combineWithAnd(List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
TargetSpecifications.isNotInRolloutGroups(groups),
TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId()))));
}
@Override
public long countByFailedRolloutAndNotInRolloutGroups(String rolloutId, Collection<Long> groups) {
final List<Specification<JpaTarget>> specList = List.of(
TargetSpecifications.failedActionsForRollout(rolloutId),
TargetSpecifications.isNotInRolloutGroups(groups));
return JpaManagementHelper.countBySpec(jpaRepository, specList);
}
@Override
public long countByActionsInRolloutGroup(final long rolloutGroupId) {
return jpaRepository.count(TargetSpecifications.isInActionRolloutGroup(rolloutGroupId));
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetTypeAssignmentResult unassignType(final Collection<String> controllerIds) {
final List<JpaTarget> allTargets = findTargetsByInSpecification(controllerIds, null);
if (allTargets.size() < controllerIds.size()) {
throw new EntityNotFoundException(Target.class, controllerIds, allTargets.stream().map(Target::getControllerId).toList());
}
// set new target type to null for all targets
allTargets.forEach(target -> target.setTargetType(null));
return new TargetTypeAssignmentResult(0, Collections.emptyList(), jpaRepository.saveAll(allTargets), null);
public void deleteByControllerId(final String controllerId) {
jpaRepository.delete(getByControllerIdAndThrowIfNotFound(controllerId));
}
@Override
@@ -445,6 +346,12 @@ public class JpaTargetManagement
});
}
@Override
public Set<TargetTag> getTags(@NotEmpty String controllerId) {
// the method has PreAuthorized by itself
return ((JpaTarget) getWithDetails(controllerId, DETAILS_TAGS)).getTags();
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
@@ -474,16 +381,6 @@ public class JpaTargetManagement
});
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target unassignType(final String controllerId) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
target.setTargetType(null);
return jpaRepository.save(target);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
@@ -500,23 +397,15 @@ public class JpaTargetManagement
}
@Override
public Page<Target> findTargetsByGroup(String group, final boolean withSubgroups, final Pageable pageable) {
if (withSubgroups) {
// search for eq(group) and like(group%)
return JpaManagementHelper
.findAllWithCountBySpec(jpaRepository, List.of(TargetSpecifications.eqOrSubTargetGroup(group)), pageable);
} else {
return JpaManagementHelper
.findAllWithCountBySpec(jpaRepository, List.of(TargetSpecifications.eqTargetGroup(group)), pageable);
}
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target unassignType(final String controllerId) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
target.setTargetType(null);
return jpaRepository.save(target);
}
@Override
public List<String> findGroups() {
return jpaRepository.findDistinctGroups();
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
@@ -547,69 +436,23 @@ public class JpaTargetManagement
controllerIds.forEach(in::value);
entityManager.createQuery(criteriaQuery.set("group", group).where(in)).executeUpdate();
}
@Override
public boolean existsByControllerId(final String controllerId) {
return jpaRepository.exists(TargetSpecifications.hasControllerId(controllerId));
public Page<Target> findTargetsByGroup(String group, final boolean withSubgroups, final Pageable pageable) {
if (withSubgroups) {
// search for eq(group) and like(group%)
return JpaManagementHelper
.findAllWithCountBySpec(jpaRepository, List.of(TargetSpecifications.eqOrSubTargetGroup(group)), pageable);
} else {
return JpaManagementHelper
.findAllWithCountBySpec(jpaRepository, List.of(TargetSpecifications.eqTargetGroup(group)), pageable);
}
}
@Override
public boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
final String controllerId, final long distributionSetId, final String targetFilterQuery) {
RsqlUtility.getInstance().validateRsqlFor(targetFilterQuery, TargetFields.class, JpaTarget.class);
final DistributionSet ds = distributionSetManagement.get(distributionSetId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, distributionSetId));
final Long distSetTypeId = ds.getType().getId();
final List<Specification<JpaTarget>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(targetFilterQuery, TargetFields.class),
TargetSpecifications.hasNotDistributionSetInActions(distributionSetId),
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId),
TargetSpecifications.hasControllerId(controllerId));
final Specification<JpaTarget> combinedSpecification = Objects
.requireNonNull(SpecificationsBuilder.combineWithAnd(specList));
return jpaRepository.exists(AccessController.Operation.UPDATE, combinedSpecification);
}
@Override
public Set<TargetTag> getTags(@NotEmpty String controllerId) {
// the method has PreAuthorized by itself
return ((JpaTarget) getWithTags(controllerId)).getTags();
}
@Override
public Map<String, String> getControllerAttributes(final String controllerId) {
return getMap(controllerId, JpaTarget_.controllerAttributes);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void requestControllerAttributes(final String controllerId) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
jpaRepository.getAccessController()
.ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, target));
target.setRequestControllerAttributes(true);
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() ->
EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new TargetAttributesRequestedEvent(
tenantAware.getCurrentTenant(), target.getId(), JpaTarget.class, target.getControllerId(),
target.getAddress() != null ? target.getAddress().toString() : null
)));
}
@Override
public boolean isControllerAttributesRequested(final String controllerId) {
return getByControllerIdAndThrowIfNotFound(controllerId).isRequestControllerAttributes();
}
@Override
public Page<Target> findByControllerAttributesRequested(final Pageable pageable) {
return JpaManagementHelper.findAllWithCountBySpec(
jpaRepository, List.of(TargetSpecifications.hasRequestControllerAttributesTrue()), pageable);
public List<String> findGroups() {
return jpaRepository.findDistinctGroups();
}
@Override
@@ -695,22 +538,6 @@ public class JpaTargetManagement
.collect(Collectors.toMap(entry -> (String) entry[0], entry -> (String) entry[1], (v1, v2) -> v1, LinkedHashMap::new));
}
private static boolean hasTagsFilterActive(final FilterParams filterParams) {
final boolean isNoTagActive = Boolean.TRUE.equals(filterParams.getSelectTargetWithNoTag());
final boolean isAtLeastOneTagActive = filterParams.getFilterByTagNames() != null
&& filterParams.getFilterByTagNames().length > 0;
return isNoTagActive || isAtLeastOneTagActive;
}
private static boolean hasTypesFilterActive(final FilterParams filterParams) {
return filterParams.getFilterByTargetType() != null;
}
private static boolean hasNoTypeFilterActive(final FilterParams filterParams) {
return Boolean.TRUE.equals(filterParams.getSelectTargetWithNoTargetType());
}
private static Collection<String> notFound(final Collection<String> controllerIds, final List<JpaTarget> foundTargets) {
final Map<String, JpaTarget> foundTargetMap = foundTargets.stream()
.collect(Collectors.toMap(Target::getControllerId, Function.identity()));
@@ -730,43 +557,6 @@ public class JpaTargetManagement
QuotaHelper.assertAssignmentQuota(targetId, requested, limit, "Metadata", Target.class.getSimpleName(), null);
}
private List<Specification<JpaTarget>> buildSpecificationList(final FilterParams filterParams) {
final List<Specification<JpaTarget>> specList = new ArrayList<>();
if ((filterParams.getFilterByStatus() != null) && !filterParams.getFilterByStatus().isEmpty()) {
specList.add(TargetSpecifications.hasTargetUpdateStatus(filterParams.getFilterByStatus()));
}
if (filterParams.getOverdueState() != null && filterParams.getOverdueState()) {
specList.add(TargetSpecifications.isOverdue(TimestampCalculator.calculateOverdueTimestamp()));
}
if (filterParams.getFilterByDistributionId() != null) {
final DistributionSet validDistSet = distributionSetManagement
.getOrElseThrowException(filterParams.getFilterByDistributionId());
specList.add(TargetSpecifications.hasInstalledOrAssignedDistributionSet(validDistSet.getId()));
}
if (!ObjectUtils.isEmpty(filterParams.getFilterBySearchText())) {
specList.add(TargetSpecifications.likeControllerIdOrName(filterParams.getFilterBySearchText()));
}
if (hasTagsFilterActive(filterParams)) {
specList.add(TargetSpecifications.hasTags(filterParams.getFilterByTagNames(),
filterParams.getSelectTargetWithNoTag()));
}
if (hasTypesFilterActive(filterParams)) {
specList.add(TargetSpecifications.hasTargetType(filterParams.getFilterByTargetType()));
} else if (hasNoTypeFilterActive(filterParams)) {
specList.add(TargetSpecifications.hasNoTargetType());
}
return specList;
}
private List<JpaTarget> findTargetsByInSpecification(final Collection<String> controllerIds, final Specification<JpaTarget> specification) {
return ListUtils.partition(new ArrayList<>(controllerIds), Constants.MAX_ENTRIES_IN_STATEMENT).stream()
.map(ids -> jpaRepository.findAll(TargetSpecifications.hasControllerIdIn(ids).and(specification)))
.flatMap(List::stream).toList();
}
private List<Target> updateTag(
final Collection<String> controllerIds, final long targetTagId, final Consumer<Collection<String>> notFoundHandler,
final BiFunction<JpaTargetTag, JpaTarget, Target> updater) {
@@ -776,8 +566,7 @@ public class JpaTargetManagement
jpaRepository.findByControllerId(controllerIds.iterator().next())
.map(List::of)
.orElseGet(Collections::emptyList) :
jpaRepository
.findAll(TargetSpecifications.byControllerIdWithTagsInJoin(controllerIds));
jpaRepository.findAll(TargetSpecifications.byControllerIdWithTagsInJoin(controllerIds));
if (targets.size() < controllerIds.size()) {
final Collection<String> notFound = notFound(controllerIds, targets);
if (notFoundHandler == null) {

View File

@@ -78,10 +78,6 @@ public final class ActionSpecifications {
isNull ? cb.isNull(root.get(JpaAction_.weight)) : cb.isNotNull(root.get(JpaAction_.weight)));
}
public static Specification<JpaAction> byDistributionSetId(final Long distributionSetId) {
return (root, query, cb) -> cb.equal(root.get(JpaAction_.distributionSet).get(AbstractJpaBaseEntity_.id), distributionSetId);
}
public static Specification<JpaAction> byDistributionSetIdAndActive(final Long distributionSetId) {
return (root, query, cb) -> cb.and(
cb.equal(root.get(JpaAction_.distributionSet).get(AbstractJpaBaseEntity_.id), distributionSetId),

View File

@@ -77,23 +77,6 @@ public final class DistributionSetSpecification {
return (dsRoot, query, cb) -> cb.equal(dsRoot.get(JpaDistributionSet_.valid), isValid);
}
/**
* {@link Specification} for retrieving {@link DistributionSet} with given {@link DistributionSet#getId()}.
*
* @param distid to search
* @return the {@link DistributionSet} {@link Specification}
*/
public static Specification<JpaDistributionSet> byIdFetch(final Long distid) {
return (dsRoot, query, cb) -> {
final Predicate predicate = cb.equal(dsRoot.get(AbstractJpaBaseEntity_.id), distid);
dsRoot.fetch(JpaDistributionSet_.modules, JoinType.LEFT);
dsRoot.fetch(JpaDistributionSet_.type, JoinType.LEFT);
query.distinct(true);
return predicate;
};
}
/**
* {@link Specification} for retrieving {@link DistributionSet} with given {@link DistributionSet#getId()}s.
*
@@ -162,16 +145,6 @@ public final class DistributionSetSpecification {
return (dsRoot, query, cb) -> cb.equal(dsRoot.get(JpaDistributionSet_.type).get(AbstractJpaBaseEntity_.id), typeId);
}
/**
* {@link Specification} for retrieving {@link DistributionSet} for given id collection of {@link DistributionSet#getType()}.
*
* @param typeIds id collection of distribution set type to search
* @return the {@link DistributionSet} {@link Specification}
*/
public static Specification<JpaDistributionSet> hasType(final Collection<Long> typeIds) {
return (dsRoot, query, cb) -> dsRoot.get(JpaDistributionSet_.type).get(AbstractJpaBaseEntity_.id).in(typeIds);
}
/**
* {@link Specification} for retrieving {@link DistributionSet}s by tag.
*

View File

@@ -1,29 +0,0 @@
/**
* Copyright (c) 2023 Bosch.IO GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.specifications;
import jakarta.validation.constraints.NotEmpty;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaNamedEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.springframework.data.jpa.domain.Specification;
/**
* Utility class for {@link JpaDistributionSetTag}s {@link Specification}s. The class provides Spring Data JPQL Specifications.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class DistributionSetTagSpecifications {
public static Specification<JpaDistributionSetTag> byName(@NotEmpty final String name) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(AbstractJpaNamedEntity_.name), name);
}
}

View File

@@ -9,6 +9,8 @@
*/
package org.eclipse.hawkbit.repository.jpa.specifications;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaNamedEntity_;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaTypeEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
@@ -18,25 +20,11 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.springframework.data.jpa.domain.Specification;
/**
* Specifications class for {@link DistributionSetType}s. The class provides
* Spring Data JPQL Specifications.
* Specifications class for {@link DistributionSetType}s. The class provides Spring Data JPQL Specifications.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class DistributionSetTypeSpecification {
private DistributionSetTypeSpecification() {
// utility class
}
/**
* {@link Specification} for retrieving {@link DistributionSetType}s with
* DELETED attribute <code>false</code> - i.e. is not deleted.
*
* @return the {@link DistributionSetType} {@link Specification}
*/
public static Specification<JpaDistributionSetType> isNotDeleted() {
return (targetRoot, query, cb) -> cb.equal(targetRoot.<Boolean> get(JpaDistributionSetType_.deleted), false);
}
/**
* {@link Specification} for retrieving {@link DistributionSetType} with
* given {@link DistributionSetType#getName()} including fetching the
@@ -60,4 +48,4 @@ public final class DistributionSetTypeSpecification {
public static Specification<JpaDistributionSetType> byKey(final String key) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(AbstractJpaTypeEntity_.key), key);
}
}
}

View File

@@ -11,91 +11,25 @@ package org.eclipse.hawkbit.repository.jpa.specifications;
import jakarta.persistence.criteria.ListJoin;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaNamedEntity_;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaNamedVersionedEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.springframework.data.jpa.domain.Specification;
/**
* Specifications class for {@link SoftwareModule}s. The class provides Spring
* Data JPQL Specifications
* Specifications class for {@link SoftwareModule}s. The class provides Spring Data JPQL Specifications
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class SoftwareModuleSpecification {
private SoftwareModuleSpecification() {
// utility class
}
/**
* {@link Specification} for retrieving {@link SoftwareModule} with given
* {@link DistributionSet#getId()}.
*
* @param swModuleId to search
* @return the {@link SoftwareModule} {@link Specification}
*/
public static Specification<JpaSoftwareModule> byId(final Long swModuleId) {
return (swRoot, query, cb) -> cb.equal(swRoot.get(AbstractJpaBaseEntity_.id), swModuleId);
}
public static Specification<JpaSoftwareModule> byAssignedToDs(final Long dsId) {
return (swRoot, query, cb) -> {
final ListJoin<JpaSoftwareModule, JpaDistributionSet> join = swRoot.join(JpaSoftwareModule_.assignedTo);
return cb.equal(join.get(AbstractJpaBaseEntity_.ID), dsId);
};
}
/**
* {@link Specification} for retrieving {@link SoftwareModule}s with
* DELETED attribute <code>false</code> - i.e. is not deleted.
*
* @return the {@link SoftwareModule} {@link Specification}
*/
public static Specification<JpaSoftwareModule> isNotDeleted() {
return (swRoot, query, cb) -> cb.equal(swRoot.get(JpaSoftwareModule_.deleted), false);
}
/**
* {@link Specification} for retrieving {@link SoftwareModule}s by "like
* name and like version".
*
* @param name to be filtered on
* @param version to be filtered on
* @return the {@link SoftwareModule} {@link Specification}
*/
public static Specification<JpaSoftwareModule> likeNameAndVersion(final String name, final String version) {
return (smRoot, query, cb) -> cb.and(
cb.like(cb.lower(smRoot.get(AbstractJpaNamedEntity_.name)), name.toLowerCase()),
cb.like(cb.lower(smRoot.get(AbstractJpaNamedVersionedEntity_.version)), version.toLowerCase()));
}
/**
* {@link Specification} for retrieving {@link SoftwareModule}s by "like
* name or like version".
*
* @param type to be filtered on
* @return the {@link SoftwareModule} {@link Specification}
*/
public static Specification<JpaSoftwareModule> equalType(final Long type) {
return (smRoot, query, cb) -> cb.equal(
smRoot.get(JpaSoftwareModule_.type).get(AbstractJpaBaseEntity_.id), type);
}
/**
* {@link Specification} for fetching {@link SoftwareModule}s type.
*
* @return the {@link SoftwareModule} {@link Specification}
*/
public static Specification<JpaSoftwareModule> fetchType() {
return (smRoot, query, cb) -> {
if (!query.getResultType().isAssignableFrom(Long.class)) {
smRoot.fetch(JpaSoftwareModule_.type);
}
return cb.conjunction();
};
}
}

View File

@@ -1,36 +0,0 @@
/**
* Copyright (c) 2022 Bosch.IO GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.specifications;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType_;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.springframework.data.jpa.domain.Specification;
/**
* Specifications class for {@link SoftwareModuleType}s. The class provides
* Spring Data JPQL Specifications.
*/
public class SoftwareModuleTypeSpecification {
private SoftwareModuleTypeSpecification() {
// utility class
}
/**
* {@link Specification} for retrieving {@link SoftwareModuleType}s with
* DELETED attribute <code>false</code> - i.e. is not deleted.
*
* @return the {@link SoftwareModuleType} {@link Specification}
*/
public static Specification<JpaSoftwareModuleType> isNotDeleted() {
return (root, query, cb) -> cb.equal(root.get(JpaSoftwareModuleType_.deleted), false);
}
}

View File

@@ -1,48 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.specifications;
import jakarta.persistence.criteria.Join;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.springframework.data.jpa.domain.Specification;
/**
* Specifications class for {@link org.eclipse.hawkbit.repository.model.Tag}s.
* The class provides Spring Data JPQL Specifications.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class TagSpecification {
/**
* {@link Specification} for retrieving {@link DistributionSetTag}s by
* assigned {@link DistributionSet}.
*
* @param dsId of the distribution set
* @return the {@link JpaDistributionSetTag} {@link Specification}
*/
public static Specification<JpaDistributionSetTag> ofDistributionSet(final Long dsId) {
return (dsRoot, query, criteriaBuilder) -> {
final Join<JpaDistributionSetTag, JpaDistributionSet> tagJoin = dsRoot
.join(JpaDistributionSetTag_.assignedToDistributionSet);
query.distinct(true);
return criteriaBuilder.equal(tagJoin.get(AbstractJpaBaseEntity_.id), dsId);
};
}
}

View File

@@ -9,6 +9,8 @@
*/
package org.eclipse.hawkbit.repository.jpa.specifications;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -16,26 +18,11 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.data.jpa.domain.Specification;
/**
* Specifications class for {@link TargetFilterQuery}s. The class provides
* Spring Data JPQL Specifications.
* Specifications class for {@link TargetFilterQuery}s. The class provides Spring Data JPQL Specifications.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class TargetFilterQuerySpecification {
private TargetFilterQuerySpecification() {
// utility class
}
/**
* {@link Specification} for retrieving {@link JpaTargetFilterQuery}s based
* on is {@link JpaTargetFilterQuery#getQuery()}.
*
* @param queryValue the query of the filter
* @return the {@link JpaTargetFilterQuery} {@link Specification}
*/
public static Specification<JpaTargetFilterQuery> equalsQuery(final String queryValue) {
return (targetFilterQueryRoot, query, cb) -> cb.equal(targetFilterQueryRoot.get(JpaTargetFilterQuery_.query), queryValue);
}
/**
* {@link Specification} for retrieving {@link JpaTargetFilterQuery}s based
* on is {@link JpaTargetFilterQuery#getName()}.

View File

@@ -9,11 +9,9 @@
*/
package org.eclipse.hawkbit.repository.jpa.specifications;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.ListJoin;
@@ -27,7 +25,6 @@ import jakarta.validation.constraints.NotNull;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaNamedEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
@@ -108,16 +105,6 @@ public final class TargetSpecifications {
return (targetRoot, query, cb) -> targetRoot.get(AbstractJpaBaseEntity_.id).in(ids);
}
/**
* {@link Specification} for retrieving {@link Target}s that have the
* request controller attributes flag set
*
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasRequestControllerAttributesTrue() {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaTarget_.requestControllerAttributes), true);
}
/**
* {@link Specification} for retrieving {@link JpaTarget}s including {@link JpaTarget#getAssignedDistributionSet()}.
*
@@ -132,26 +119,6 @@ public final class TargetSpecifications {
};
}
/**
* {@link Specification} for retrieving {@link Target}s by "equal to any given {@link TargetUpdateStatus}".
*
* @param updateStatus to be filtered on
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasTargetUpdateStatus(final Collection<TargetUpdateStatus> updateStatus) {
return (targetRoot, query, cb) -> targetRoot.get(JpaTarget_.updateStatus).in(updateStatus);
}
/**
* {@link Specification} for retrieving {@link Target}s by "equal to given {@link TargetUpdateStatus}".
*
* @param updateStatus to be filtered on
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasTargetUpdateStatus(final TargetUpdateStatus updateStatus) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaTarget_.updateStatus), updateStatus);
}
/**
* {@link Specification} for retrieving {@link Target}s by "not equal to given {@link TargetUpdateStatus}".
*
@@ -162,36 +129,6 @@ public final class TargetSpecifications {
return (targetRoot, query, cb) -> cb.not(cb.equal(targetRoot.get(JpaTarget_.updateStatus), updateStatus));
}
/**
* {@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 response 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) ->
cb.lessThanOrEqualTo(targetRoot.get(JpaTarget_.lastTargetQuery), overdueTimestamp);
}
/**
* {@link Specification} for retrieving {@link Target}s by "like controllerId or like name".
*
* @param searchText to be filtered on
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> likeControllerIdOrName(final String searchText) {
return (targetRoot, query, cb) -> {
final String searchTextToLower = searchText.toLowerCase();
return cb.or(
cb.like(cb.lower(targetRoot.get(JpaTarget_.controllerId)), searchTextToLower),
cb.like(cb.lower(targetRoot.get(AbstractJpaNamedEntity_.name)), searchTextToLower));
};
}
public static Specification<JpaTarget> eqTargetGroup(final String targetGroup) {
return (targetRoot, query, criteriaBuilder) -> {
final String groupTextToLower = targetGroup.toLowerCase();
@@ -208,16 +145,6 @@ public final class TargetSpecifications {
};
}
/**
* {@link Specification} for retrieving {@link Target}s by "like controllerId".
*
* @param distributionId to be filtered on
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasInstalledOrAssignedDistributionSet(@NotNull final Long distributionId) {
return hasInstalledDistributionSet(distributionId).or(hasAssignedDistributionSet(distributionId));
}
/**
* Finds all targets by given {@link Target#getControllerId()}s and which are not yet assigned to given {@link DistributionSet}.
*
@@ -225,42 +152,14 @@ public final class TargetSpecifications {
* @param distributionId set that is not yet assigned
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasControllerIdAndAssignedDistributionSetIdNot(final List<String> tIDs,
@NotNull final Long distributionId) {
public static Specification<JpaTarget> hasControllerIdAndAssignedDistributionSetIdNot(
final List<String> tIDs, @NotNull final Long distributionId) {
return (targetRoot, query, cb) -> cb.and(targetRoot.get(JpaTarget_.controllerId).in(tIDs),
cb.or(
cb.notEqual(targetRoot.get(JpaTarget_.assignedDistributionSet).get(AbstractJpaBaseEntity_.id), distributionId),
cb.isNull(targetRoot.get(JpaTarget_.assignedDistributionSet))));
}
/**
* {@link Specification} for retrieving {@link Target}s based on a {@link TargetTag} name.
*
* @param tagName to search for
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasTagName(final String tagName) {
return (targetRoot, query, cb) -> {
final SetJoin<JpaTarget, JpaTargetTag> join = targetRoot.join(JpaTarget_.tags);
return cb.equal(join.get(AbstractJpaNamedEntity_.name), tagName);
};
}
/**
* {@link Specification} for retrieving {@link Target}s by "has no tag names"or "has at least on of the given tag names".
*
* @param tagNames to be filtered on
* @param selectTargetWithNoTag flag to get targets with no tag assigned
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasTags(final String[] tagNames, final Boolean selectTargetWithNoTag) {
return (targetRoot, query, cb) -> {
final Predicate predicate = getHasTagsPredicate(targetRoot, cb, selectTargetWithNoTag, tagNames);
query.distinct(true);
return predicate;
};
}
/**
* {@link Specification} for retrieving {@link Target}s by assigned distribution set.
*
@@ -297,7 +196,7 @@ public final class TargetSpecifications {
public static Specification<JpaTarget> isCompatibleWithDistributionSetType(final Long distributionSetTypeId) {
return (targetRoot, query, cb) -> {
// Since the targetRoot is changed by joining we need to get the isNull predicate first
final Predicate targetTypeIsNull = getTargetTypeIsNullPredicate(targetRoot);
final Predicate targetTypeIsNull = targetRoot.get(JpaTarget_.targetType).isNull();
return cb.or(targetTypeIsNull, cb.equal(getDsTypeIdPath(targetRoot), distributionSetTypeId));
};
}
@@ -409,36 +308,6 @@ public final class TargetSpecifications {
};
}
/**
* {@link Specification} for retrieving {@link Target}s by target type id
*
* @param typeId the id of the target type
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasTargetType(final long typeId) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaTarget_.targetType).get(AbstractJpaBaseEntity_.id), typeId);
}
/**
* {@link Specification} for retrieving {@link Target}s by target type id is equal to null
*
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasNoTargetType() {
return (targetRoot, query, cb) -> cb.isNull(targetRoot.get(JpaTarget_.targetType));
}
/**
* {@link Specification} for retrieving {@link Target}s that don't have target type assigned
*
* @param typeId the id of the target type
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasTargetTypeNot(final Long typeId) {
return (targetRoot, query, cb) -> cb.or(getTargetTypeIsNullPredicate(targetRoot),
cb.notEqual(targetRoot.get(JpaTarget_.targetType).get(AbstractJpaBaseEntity_.id), typeId));
}
public static Specification<JpaTarget> failedActionsForRollout(final String rolloutId) {
return (targetRoot, query, cb) -> {
final Join<JpaTarget, Action> targetActions = targetRoot.join("actions");
@@ -461,35 +330,6 @@ public final class TargetSpecifications {
};
}
private static Predicate getHasTagsPredicate(final Root<JpaTarget> targetRoot, final CriteriaBuilder cb,
final Boolean selectTargetWithNoTag, final String[] tagNames) {
final SetJoin<JpaTarget, JpaTargetTag> tags = targetRoot.join(JpaTarget_.tags, JoinType.LEFT);
final Path<String> exp = tags.get(AbstractJpaNamedEntity_.name);
final List<Predicate> hasTagsPredicates = new ArrayList<>();
if (isNoTagActive(selectTargetWithNoTag)) {
hasTagsPredicates.add(exp.isNull());
}
if (isAtLeastOneTagActive(tagNames)) {
hasTagsPredicates.add(exp.in((Object[]) tagNames));
}
return hasTagsPredicates.stream().reduce(cb::or)
.orElseThrow(() -> new RuntimeException("Neither NO_TAG, nor TAG target tag filter was provided!"));
}
private static boolean isNoTagActive(final Boolean selectTargetWithNoTag) {
return Boolean.TRUE.equals(selectTargetWithNoTag);
}
private static boolean isAtLeastOneTagActive(final String[] tagNames) {
return tagNames != null && tagNames.length > 0;
}
private static Predicate getTargetTypeIsNullPredicate(final Root<JpaTarget> targetRoot) {
return targetRoot.get(JpaTarget_.targetType).isNull();
}
private static Path<Long> getDsTypeIdPath(final Root<JpaTarget> root) {
final Join<JpaTarget, JpaTargetType> targetTypeJoin = root.join(JpaTarget_.targetType, JoinType.LEFT);
return targetTypeJoin.join(JpaTargetType_.distributionSetTypes, JoinType.LEFT).get(AbstractJpaBaseEntity_.id);

View File

@@ -31,16 +31,6 @@ import org.springframework.data.jpa.domain.Specification;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class TargetTypeSpecification {
/**
* {@link Specification} for retrieving {@link TargetType}s by controllerId
*
* @param ids to search for
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasIdIn(final Collection<Long> ids) {
return (targetRoot, query, cb) -> targetRoot.get(AbstractJpaBaseEntity_.id).in(ids);
}
/**
* {@link Specification} for retrieving {@link TargetType}s based on a {@link DistributionSetType} name.
*
@@ -73,14 +63,4 @@ public final class TargetTypeSpecification {
public static Specification<JpaTargetType> hasName(final String name) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(AbstractJpaNamedEntity_.name), name);
}
/**
* {@link Specification} for retrieving {@link TargetType}s by "like name".
*
* @param name to be filtered on
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> likeName(final String name) {
return (targetRoot, query, cb) -> cb.like(cb.lower(targetRoot.get(AbstractJpaNamedEntity_.name)), name.toLowerCase());
}
}

View File

@@ -32,7 +32,9 @@ import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
@@ -58,14 +60,12 @@ import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
import org.eclipse.hawkbit.repository.test.TestConfiguration;
import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
import org.eclipse.hawkbit.repository.test.util.RolloutTestApprovalStrategy;
@@ -191,9 +191,8 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
return distributionSetManagement.unassignTag(sets.stream().map(DistributionSet::getId).toList(), tag.getId());
}
protected TargetTypeAssignmentResult initiateTypeAssignment(final Collection<Target> targets, final TargetType type) {
return targetManagement.assignType(
targets.stream().map(Target::getControllerId).toList(), type.getId());
protected void initiateTypeAssignment(final Collection<Target> targets, final TargetType type) {
targets.stream().map(Target::getControllerId).forEach(id -> targetManagement.assignType(id, type.getId()));
}
protected void assertRollout(final Rollout rollout, final boolean dynamic, final Rollout.RolloutStatus status, final int groupCreated,
@@ -312,6 +311,10 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
return rolloutGroupRepository.findById(group.getId()).get();
}
protected static Specification<JpaAction> byDistributionSetId(final Long distributionSetId) {
return (root, query, cb) -> cb.equal(root.get(JpaAction_.distributionSet).get(AbstractJpaBaseEntity_.id), distributionSetId);
}
private JpaRollout refresh(final Rollout rollout) {
return rolloutRepository.findById(rollout.getId()).get();
}

View File

@@ -24,7 +24,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.eclipse.hawkbit.repository.FilterParams;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement.AutoAssignDistributionSetUpdate;
@@ -76,10 +75,6 @@ class TargetAccessControllerTest extends AbstractJpaIntegrationTest {
assertThat(targetManagement.findByRsql("id==*", Pageable.unpaged()).get().map(Identifiable::getId).toList())
.containsOnly(permittedTarget.getId());
// verify targetManagement#findByUpdateStatus
assertThat(targetManagement.findByUpdateStatus(TargetUpdateStatus.REGISTERED, Pageable.unpaged()).get()
.map(Identifiable::getId).toList()).containsOnly(permittedTarget.getId());
// verify targetManagement#getByControllerID
assertThat(targetManagement.getByControllerId(permittedTarget.getControllerId())).isPresent();
final String hiddenTargetControllerId = hiddenTarget.getControllerId();
@@ -106,19 +101,6 @@ class TargetAccessControllerTest extends AbstractJpaIntegrationTest {
.as("Target should not be found.")
.isInstanceOf(InsufficientPermissionException.class);
});
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.create(TargetFilterQueryManagement.Create.builder().name("test").query("id==*").build());
runAs(withUser("user", READ_TARGET + "/controllerId==" + permittedTarget.getControllerId()), () -> {
// verify targetManagement#findByTargetFilterQuery
assertThat(targetManagement.findByTargetFilterQuery(targetFilterQuery.getId(), Pageable.unpaged()).get()
.map(Identifiable::getId).toList()).containsOnly(permittedTarget.getId());
// verify targetManagement#findByTargetFilterQuery (used by UI)
assertThat(targetManagement.findByFilters(new FilterParams(null, null, null, null), Pageable.unpaged()).get()
.map(Identifiable::getId).toList()).containsOnly(permittedTarget.getId());
});
}
@Test
@@ -208,11 +190,6 @@ class TargetAccessControllerTest extends AbstractJpaIntegrationTest {
.create(Create.builder().controllerId("device02").updateStatus(TargetUpdateStatus.REGISTERED).build())
.getControllerId();
runAs(withUser("user", READ_TARGET + "/controllerId==" + permittedTarget.getControllerId()), () ->
// verify targetManagement#findByUpdateStatus before assignment
assertThat(targetManagement.findByUpdateStatus(TargetUpdateStatus.REGISTERED, Pageable.unpaged()).get()
.map(Identifiable::getId).toList()).containsOnly(permittedTarget.getId()));
runAs(withUser("user",
READ_TARGET + "/controllerId==" + permittedTarget.getControllerId(),
UPDATE_TARGET + "/controllerId==" + permittedTarget.getControllerId(),
@@ -221,14 +198,6 @@ class TargetAccessControllerTest extends AbstractJpaIntegrationTest {
assertThat(assignDistributionSet(dsId, permittedTarget.getControllerId()).getAssigned()).isEqualTo(1);
// assigning of not allowed target behaves as not found
assertThatThrownBy(() -> assignDistributionSet(dsId, hiddenTargetControllerId)).isInstanceOf(AssertionError.class);
// verify targetManagement#findByUpdateStatus(REGISTERED) after assignment
assertThat(targetManagement.findByUpdateStatus(TargetUpdateStatus.REGISTERED, Pageable.unpaged())
.getTotalElements()).isZero();
// verify targetManagement#findByUpdateStatus(PENDING) after assignment
assertThat(targetManagement.findByUpdateStatus(TargetUpdateStatus.PENDING, Pageable.unpaged()).get()
.map(Identifiable::getId).toList()).containsOnly(permittedTarget.getId());
});
}

View File

@@ -145,7 +145,7 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest {
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(10, 20), targetsCount);
// Count the number of targets that will be assigned with setA
assertThat(targetManagement.countByRsqlAndNonDSAndCompatibleAndUpdatable(setA.getId(), targetFilterQuery.getQuery()))
assertThat(targetManagement.countByRsqlAndNonDsAndCompatibleAndUpdatable(setA.getId(), targetFilterQuery.getQuery()))
.isEqualTo(15);
// Run the check
@@ -405,7 +405,7 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest {
final List<Long> compatibleTargets = Stream
.of(compatibleTargetsSingleType, compatibleTargetsMultiType, compatibleTargetsWithoutType)
.flatMap(Collection::stream).map(Target::getId).toList();
final long compatibleCount = targetManagement.countByRsqlAndNonDSAndCompatibleAndUpdatable(testDs.getId(),
final long compatibleCount = targetManagement.countByRsqlAndNonDsAndCompatibleAndUpdatable(testDs.getId(),
testFilter.getQuery());
assertThat(compatibleCount).isEqualTo(compatibleTargets.size());
@@ -498,7 +498,7 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest {
private Slice<Action> findActionsByDistributionSet(final Pageable pageable, final long distributionSetId) {
return actionRepository
.findAll(ActionSpecifications.byDistributionSetId(distributionSetId), pageable)
.findAll(byDistributionSetId(distributionSetId), pageable)
.map(Action.class::cast);
}
}

View File

@@ -95,7 +95,7 @@ class RepositoryEntityEventTest extends AbstractJpaIntegrationTest {
void targetDeletedEventIsPublished() throws InterruptedException {
final Target createdTarget = testdataFactory.createTarget("12345");
targetManagement.deleteByControllerID("12345");
targetManagement.deleteByControllerId("12345");
final TargetDeletedEvent targetDeletedEvent = eventListener.waitForEvent(TargetDeletedEvent.class);
assertThat(targetDeletedEvent).isNotNull();

View File

@@ -1788,7 +1788,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
distributionSetManagement.get(distributionSetId).orElseThrow(() ->
new EntityNotFoundException(DistributionSet.class, distributionSetId));
return actionRepository
.findAll(ActionSpecifications.byDistributionSetId(distributionSetId), pageable)
.findAll(byDistributionSetId(distributionSetId), pageable)
.map(Action.class::cast);
}

View File

@@ -958,11 +958,12 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
expectedTargetCountStatus.put(TotalTargetCountStatus.Status.FINISHED, 9L);
validateRolloutActionStatus(rolloutTwo.getId(), expectedTargetCountStatus);
changeStatusForAllRunningActions(rolloutTwo, Status.FINISHED);
final Page<Target> targetPage = targetManagement.findByUpdateStatus(TargetUpdateStatus.IN_SYNC, PAGE);
final List<Target> targetList = targetPage.getContent();
final List<? extends Target> targetList = findByUpdateStatus(TargetUpdateStatus.IN_SYNC, PAGE);
// 15 targets in finished/IN_SYNC status and same DS assigned
assertThat(targetList).hasSize(amountTargetsForRollout);
targetList.stream().map(Target::getControllerId).map(deploymentManagement::getAssignedDistributionSet)
targetList.stream()
.map(Target::getControllerId)
.map(deploymentManagement::getAssignedDistributionSet)
.forEach(d -> assertThat(d).contains(distributionSet));
}

View File

@@ -12,25 +12,18 @@ package org.eclipse.hawkbit.repository.jpa.management;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.FilterParams;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TargetTagManagement;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.junit.jupiter.api.Test;
/**
@@ -39,157 +32,6 @@ import org.junit.jupiter.api.Test;
*/
class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
private static final String SPACE_AND_DESCRIPTION = " description";
/**
* Verifies that targets with given target type are returned from repository.
*/
@Test
void findTargetByTargetType() {
final TargetType testType = testdataFactory.createTargetType("testType", Set.of(standardDsType));
final List<Target> unassigned = testdataFactory.createTargets(9, "unassigned");
final List<Target> assigned = testdataFactory.createTargetsWithType(11, "assigned", testType);
assertThat(targetManagement.findByFilters(new FilterParams(null, null, false, testType.getId()), PAGE))
.as("Contains the targets with set type").containsAll(assigned)
.as("and that means the following expected amount").hasSize(11);
assertThat(targetManagement.countByFilters(new FilterParams(null, null, false, testType.getId())))
.as("Count the targets with set type").isEqualTo(11);
assertThat(targetManagement.findByFilters(new FilterParams(null, null, true, null), PAGE))
.as("Contains the targets without a type").containsAll(unassigned)
.as("and that means the following expected amount").hasSize(9);
assertThat(targetManagement.countByFilters(new FilterParams(null, null, true, null)))
.as("Counts the targets without a type").isEqualTo(9);
}
/**
* Tests different parameter combinations for target search operations.
* and query definitions by RSQL (named and un-named).
*/
@Test
void targetSearchWithVariousFilterCombinations() {
final TargetTag targTagX = targetTagManagement.create(TargetTagManagement.Create.builder().name("TargTag-X").build());
final TargetTag targTagY = targetTagManagement.create(TargetTagManagement.Create.builder().name("TargTag-Y").build());
final TargetTag targTagZ = targetTagManagement.create(TargetTagManagement.Create.builder().name("TargTag-Z").build());
final TargetTag targTagW = targetTagManagement.create(TargetTagManagement.Create.builder().name("TargTag-W").build());
final DistributionSet setA = testdataFactory.createDistributionSet("A");
final DistributionSet setB = testdataFactory.createDistributionSet("B");
final TargetType targetTypeX = testdataFactory.createTargetType("TargetTypeX", Set.of(setB.getType()));
final DistributionSet installedSet = testdataFactory.createDistributionSet("another");
final Long lastTargetQueryNotOverdue = System.currentTimeMillis();
final Long lastTargetQueryAlwaysOverdue = 0L;
final String targetDsAIdPref = "targ-A";
List<Target> targAs = testdataFactory.createTargets(100, targetDsAIdPref,
targetDsAIdPref.concat(SPACE_AND_DESCRIPTION), lastTargetQueryNotOverdue);
targAs = assignTag(targAs, targTagX);
final Target targSpecialName = targetManagement
.update(TargetManagement.Update.builder().id(targAs.get(0).getId()).name("targ-A-special").build());
final String targetDsBIdPref = "targ-B";
List<Target> targBs = testdataFactory.createTargets(100, targetDsBIdPref,
targetDsBIdPref.concat(SPACE_AND_DESCRIPTION), lastTargetQueryAlwaysOverdue);
targBs = assignTag(targBs, targTagY);
targBs = assignTag(targBs, targTagW);
final String targetDsCIdPref = "targ-C";
List<Target> targCs = testdataFactory.createTargets(100, targetDsCIdPref,
targetDsCIdPref.concat(SPACE_AND_DESCRIPTION), lastTargetQueryAlwaysOverdue);
targCs = assignTag(targCs, targTagZ);
targCs = assignTag(targCs, targTagW);
final String targetDsDIdPref = "targ-D";
final List<Target> targDs = testdataFactory.createTargets(100, targetDsDIdPref,
targetDsDIdPref.concat(SPACE_AND_DESCRIPTION), null);
final String targetDsEIdPref = "targ-E";
final List<Target> targEs = testdataFactory.createTargetsWithType(100, targetDsEIdPref, targetTypeX);
final String assignedC = targCs.iterator().next().getControllerId();
assignDistributionSet(setA.getId(), assignedC);
final String assignedA = targAs.iterator().next().getControllerId();
assignDistributionSet(setA.getId(), assignedA);
final String assignedB = targBs.iterator().next().getControllerId();
assignDistributionSet(setA.getId(), assignedB);
final String assignedE = targEs.iterator().next().getControllerId();
assignDistributionSet(setB.getId(), assignedE);
final String installedC = targCs.iterator().next().getControllerId();
final Long actionId = getFirstAssignedActionId(assignDistributionSet(installedSet.getId(), assignedC));
// set one installed DS also
controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(actionId).status(Status.FINISHED).message("message"));
assignDistributionSet(setA.getId(), installedC);
final List<TargetUpdateStatus> unknown = List.of(TargetUpdateStatus.UNKNOWN);
final List<TargetUpdateStatus> pending = List.of(TargetUpdateStatus.PENDING);
final List<TargetUpdateStatus> both = List.of(TargetUpdateStatus.UNKNOWN, TargetUpdateStatus.PENDING);
// get final updated version of targets
targAs = targetManagement.getByControllerId(targAs.stream().map(Target::getControllerId).toList());
targBs = targetManagement.getByControllerId(targBs.stream().map(Target::getControllerId).toList());
targCs = targetManagement.getByControllerId(targCs.stream().map(Target::getControllerId).toList());
// try to find several targets with different filter settings
verifyThat1TargetHasNameAndId("targ-A-special", targSpecialName.getControllerId());
verifyThatRepositoryContains500Targets();
verifyThat200TargetsHaveTagD(targTagW, concat(targBs, targCs));
verifyThat100TargetsContainsGivenTextAndHaveTagAssigned(targTagY, targTagW, targBs);
verifyThat1TargetHasTagHasDescOrNameAndDs(targTagW, setA, targetManagement.getByControllerId(assignedC).get());
verifyThat0TargetsWithTagAndDescOrNameHasDS(targTagW, setA);
verifyThat0TargetsWithNameOrdescAndDSHaveTag(targTagX, setA);
verifyThat3TargetsHaveDSAssigned(setA, targetManagement.getByControllerId(Arrays.asList(assignedA, assignedB, assignedC)));
verifyThat1TargetWithDescOrNameHasDS(setA, targetManagement.getByControllerId(assignedA).get());
List<Target> expected = concat(targAs, targBs, targCs, targDs);
expected.removeAll(targetManagement.getByControllerId(Arrays.asList(assignedA, assignedB, assignedC)));
verifyThat496TargetsAreInStatusUnknown(unknown, expected);
expected = concat(targBs, targCs);
expected.removeAll(targetManagement.getByControllerId(Arrays.asList(assignedB, assignedC)));
verifyThat198TargetsAreInStatusUnknownAndHaveGivenTags(targTagY, targTagW, unknown, expected);
verifyThat0TargetsAreInStatusUnknownAndHaveDSAssigned(setA, unknown);
expected = concat(targAs);
expected.remove(targetManagement.getByControllerId(assignedA).get());
verifyThat99TargetsWithNameOrDescriptionAreInGivenStatus(unknown, expected);
expected = concat(targBs);
expected.remove(targetManagement.getByControllerId(assignedB).get());
verifyThat99TargetsWithGivenNameOrDescAndTagAreInStatusUnknown(targTagW, unknown, expected);
verifyThat4TargetsAreInStatusPending(pending,
targetManagement.getByControllerId(Arrays.asList(assignedA, assignedB, assignedC, assignedE)));
verifyThat3TargetsWithGivenDSAreInPending(setA, pending,
targetManagement.getByControllerId(Arrays.asList(assignedA, assignedB, assignedC)));
verifyThat1TargetWithGivenNameOrDescAndDSIsInPending(setA, pending,
targetManagement.getByControllerId(assignedA).get());
verifyThat1TargetWithGivenNameOrDescAndTagAndDSIsInPending(targTagW, setA, pending,
targetManagement.getByControllerId(assignedB).get());
verifyThat2TargetsWithGivenTagAndDSIsInPending(targTagW, setA, pending,
targetManagement.getByControllerId(Arrays.asList(assignedB, assignedC)));
verifyThat2TargetsWithGivenTagAreInPending(targTagW, pending,
targetManagement.getByControllerId(Arrays.asList(assignedB, assignedC)));
verifyThat200targetsWithGivenTagAreInStatusPendingOrUnknown(targTagW, both, concat(targBs, targCs));
verifyThat1TargetAIsInStatusPendingAndHasDSInstalled(installedSet, pending,
targetManagement.getByControllerId(installedC).get());
verifyThat1TargetHasTypeAndDSAssigned(targetTypeX, setB, targetManagement.getByControllerId(assignedE).get());
verifyThatTargetsHasNoTypeAndDSAssignedOrInstalled(setA,
targetManagement.getByControllerId(Arrays.asList(assignedA, assignedB, assignedC)));
verifyThatTargetsHasNoTypeAndDSAssignedOrInstalled(installedSet,
targetManagement.getByControllerId(Collections.singletonList(installedC)));
verifyThat100TargetsContainsGivenTextAndHaveTypeAssigned(targetTypeX, targEs);
verifyThat400TargetsContainsGivenTextAndHaveNoTypeAssigned(concat(targAs, targBs, targCs, targDs));
expected = concat(targBs, targCs);
expected.removeAll(targetManagement.getByControllerId(Arrays.asList(assignedB, assignedC)));
verifyThat198TargetsAreInStatusUnknownAndOverdue(unknown, expected);
}
/**
* Verifies that targets with given assigned DS are returned from repository.
*/
@@ -305,358 +147,4 @@ class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
.containsExactlyInAnyOrderElementsOf(testTargets).as("does not contain incompatible targets")
.doesNotContainAnyElementsOf(targetsWithIncompatibleType);
}
private void verifyThat1TargetAIsInStatusPendingAndHasDSInstalled(final DistributionSet installedSet,
final List<TargetUpdateStatus> pending, final Target expected) {
final FilterParams filterParams = new FilterParams(pending, null, null, installedSet.getId(), Boolean.FALSE);
final String query = "updatestatus==pending and installedds.name==" + installedSet.getName();
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat200targetsWithGivenTagAreInStatusPendingOrUnknown(final TargetTag targTagW,
final List<TargetUpdateStatus> both, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(both, null, null, null, Boolean.FALSE, targTagW.getName());
final String query = "(updatestatus==pending or updatestatus==unknown) and tag==" + targTagW.getName();
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(200).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat2TargetsWithGivenTagAreInPending(final TargetTag targTagW,
final List<TargetUpdateStatus> pending, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(pending, null, null, null, Boolean.FALSE,
targTagW.getName());
final String query = "updatestatus==pending and tag==" + targTagW.getName();
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(2).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat2TargetsWithGivenTagAndDSIsInPending(final TargetTag targTagW, final DistributionSet setA,
final List<TargetUpdateStatus> pending, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(pending, null, null, setA.getId(), Boolean.FALSE,
targTagW.getName());
final String query = "updatestatus==pending and (assignedds.name==" + setA.getName() + " or installedds.name=="
+ setA.getName() + ") and tag==" + targTagW.getName();
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(2).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat1TargetWithGivenNameOrDescAndTagAndDSIsInPending(final TargetTag targTagW,
final DistributionSet setA, final List<TargetUpdateStatus> pending, final Target expected) {
final FilterParams filterParams = new FilterParams(pending, null, "%targ-B%", setA.getId(), Boolean.FALSE,
targTagW.getName());
final String query = "updatestatus==pending and (assignedds.name==" + setA.getName() + " or installedds.name=="
+ setA.getName() + ") and (name==*targ-B* or description==*targ-B*) and tag==" + targTagW.getName();
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat1TargetWithGivenNameOrDescAndDSIsInPending(final DistributionSet setA,
final List<TargetUpdateStatus> pending, final Target expected) {
final FilterParams filterParams = new FilterParams(pending, null, "%targ-A%", setA.getId(), Boolean.FALSE);
final String query = "updatestatus==pending and (assignedds.name==" + setA.getName() + " or installedds.name=="
+ setA.getName() + ") and (name==*targ-A* or description==*targ-A*)";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat3TargetsWithGivenDSAreInPending(final DistributionSet setA,
final List<TargetUpdateStatus> pending, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(pending, null, null, setA.getId(), Boolean.FALSE);
final String query = "updatestatus==pending and (assignedds.name==" + setA.getName() + " or installedds.name=="
+ setA.getName() + ")";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(3).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat4TargetsAreInStatusPending(final List<TargetUpdateStatus> pending,
final List<Target> expected) {
final FilterParams filterParams = new FilterParams(pending, null, null, null, Boolean.FALSE);
final String query = "updatestatus==pending";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(4).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat99TargetsWithGivenNameOrDescAndTagAreInStatusUnknown(final TargetTag targTagW,
final List<TargetUpdateStatus> unknown, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(unknown, null, "%targ-B%", null, Boolean.FALSE,
targTagW.getName());
final String query = "updatestatus==unknown and (name==*targ-B* or description==*targ-B*) and tag=="
+ targTagW.getName();
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(99).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat99TargetsWithNameOrDescriptionAreInGivenStatus(final List<TargetUpdateStatus> unknown,
final List<Target> expected) {
final FilterParams filterParams = new FilterParams(unknown, null, "%targ-A%", null, Boolean.FALSE);
final String query = "updatestatus==unknown and (name==*targ-A* or description==*targ-A*)";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(99).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat0TargetsAreInStatusUnknownAndHaveDSAssigned(final DistributionSet setA,
final List<TargetUpdateStatus> unknown) {
final FilterParams filterParams = new FilterParams(unknown, null, null, setA.getId(), Boolean.FALSE);
final String query = "updatestatus==unknown and (assignedds.name==" + setA.getName() + " or installedds.name=="
+ setA.getName() + ")";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent())
.as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and filter query returns the same result")
.hasSize(targetManagement.findByRsql(query, PAGE).getContent().size())
.as("has number of elements")
.isEmpty();
}
private void verifyThat198TargetsAreInStatusUnknownAndHaveGivenTags(final TargetTag targTagY,
final TargetTag targTagW, final List<TargetUpdateStatus> unknown, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(unknown, null, null, null, Boolean.FALSE, targTagY.getName(),
targTagW.getName());
final String query = "updatestatus==unknown and (tag==" + targTagY.getName() + " or tag==" + targTagW.getName()
+ ")";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(198).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat496TargetsAreInStatusUnknown(final List<TargetUpdateStatus> unknown,
final List<Target> expected) {
final FilterParams filterParams = new FilterParams(unknown, null, null, null, Boolean.FALSE);
final String query = "updatestatus==unknown";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(496).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat198TargetsAreInStatusUnknownAndOverdue(final List<TargetUpdateStatus> unknown,
final List<Target> expected) {
final FilterParams filterParams = new FilterParams(unknown, Boolean.TRUE, null, null, Boolean.FALSE);
// be careful: simple filters are concatenated using AND-gating
final String query = "lastcontrollerrequestat=le=${overdue_ts};updatestatus==UNKNOWN";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(198).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat1TargetWithDescOrNameHasDS(final DistributionSet setA, final Target expected) {
final FilterParams filterParams = new FilterParams(null, null, "%targ-A%", setA.getId(), Boolean.FALSE);
final String query = "(name==*targ-A* or description==*targ-A*) and (assignedds.name==" + setA.getName()
+ " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat3TargetsHaveDSAssigned(final DistributionSet setA, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(null, null, null, setA.getId(), Boolean.FALSE);
final String query = "assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName();
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(3).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat0TargetsWithNameOrdescAndDSHaveTag(final TargetTag targTagX, final DistributionSet setA) {
final FilterParams filterParams = new FilterParams(null, null, "%targ-C%", setA.getId(), Boolean.FALSE,
targTagX.getName());
final String query = "(name==*targ-C* or description==*targ-C*) and tag==" + targTagX.getName()
+ " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent())
.as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and filter query returns the same result")
.hasSize(targetManagement.findByRsql(query, PAGE).getContent().size())
.as("has number of elements")
.isEmpty();
}
private void verifyThat0TargetsWithTagAndDescOrNameHasDS(final TargetTag targTagW, final DistributionSet setA) {
final FilterParams filterParams = new FilterParams(null, null, "%targ-A%", setA.getId(), Boolean.FALSE,
targTagW.getName());
final String query = "(name==*targ-A* or description==*targ-A*) and tag==" + targTagW.getName()
+ " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent())
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and filter query returns the same result")
.hasSize(targetManagement.findByRsql(query, PAGE).getContent().size())
.as("has number of elements")
.isEmpty();
}
private void verifyThat1TargetHasTagHasDescOrNameAndDs(final TargetTag targTagW, final DistributionSet setA,
final Target expected) {
final FilterParams filterParams = new FilterParams(null, null, "%targ-C%", setA.getId(), Boolean.FALSE,
targTagW.getName());
final String query = "(name==*targ-c* or description==*targ-C*) and tag==" + targTagW.getName()
+ " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat1TargetHasNameAndId(final String name, final String controllerId) {
final FilterParams filterParamsByName = new FilterParams(null, null, name, null, Boolean.FALSE);
assertThat(targetManagement.findByFilters(filterParamsByName, PAGE).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParamsByName));
final FilterParams filterParamsByControllerId = new FilterParams(null, null, controllerId, null, Boolean.FALSE);
assertThat(targetManagement.findByFilters(filterParamsByControllerId, PAGE).getContent())
.as("has number of elements").hasSize(1).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParamsByControllerId));
}
private void verifyThat100TargetsContainsGivenTextAndHaveTagAssigned(final TargetTag targTagY,
final TargetTag targTagW, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(null, null, "%targ-B%", null, Boolean.FALSE,
targTagY.getName(), targTagW.getName());
final String query = "(name==*targ-B* or description==*targ-B*) and (tag==" + targTagY.getName() + " or tag=="
+ targTagW.getName() + ")";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(100).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
@SafeVarargs
private List<Target> concat(final List<Target>... targets) {
final List<Target> result = new ArrayList<>();
Arrays.asList(targets).forEach(result::addAll);
return result;
}
private void verifyThat200TargetsHaveTagD(final TargetTag targTagD, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(null, null, null, null, Boolean.FALSE, targTagD.getName());
final String query = "tag==" + targTagD.getName();
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("Expected number of results is")
.hasSize(200).as("and is expected number of results is equal to ")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThatRepositoryContains500Targets() {
final FilterParams filterParams = new FilterParams(null, null, null, null, null);
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent())
.as("Overall we expect that many targets in the repository").hasSize(500)
.as("which is also reflected by repository count").hasSize((int) targetManagement.count())
.as("which is also reflected by call without specification")
.containsAll(targetManagement.findAll(PAGE).getContent());
}
private void verifyThat1TargetHasTypeAndDSAssigned(final TargetType type, final DistributionSet set,
final Target expected) {
final FilterParams filterParams = new FilterParams(null, set.getId(), Boolean.FALSE, type.getId());
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsExactly(expected);
}
private void verifyThatTargetsHasNoTypeAndDSAssignedOrInstalled(final DistributionSet set,
final List<Target> expected) {
final FilterParams filterParams = new FilterParams(null, set.getId(), Boolean.TRUE, null);
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(expected.size()).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected);
}
private void verifyThat100TargetsContainsGivenTextAndHaveTypeAssigned(final TargetType targetType,
final List<Target> expected) {
final FilterParams filterParams = new FilterParams("%targ-E%", null, Boolean.FALSE, targetType.getId());
final List<Target> filteredTargets = targetManagement.findByFilters(filterParams, PAGE).getContent();
assertThat(filteredTargets).as("has number of elements").hasSize(100)
.as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams));
// Comparing the controller ids, as one of the targets was modified, so
// a 1:1
// comparison of the objects is not possible
assertThat(filteredTargets.stream().map(Target::getControllerId).toList())
.containsAll(expected.stream().map(Target::getControllerId).toList());
}
private void verifyThat400TargetsContainsGivenTextAndHaveNoTypeAssigned(final List<Target> expected) {
final FilterParams filterParams = new FilterParams("%targ-%", null, Boolean.TRUE, null);
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(400).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected);
}
}

View File

@@ -14,11 +14,9 @@ import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.FilterParams;
import org.eclipse.hawkbit.repository.TargetManagement.Create;
import org.eclipse.hawkbit.repository.TargetManagement.Update;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.jupiter.api.Test;
@@ -29,42 +27,6 @@ import org.junit.jupiter.api.Test;
@Slf4j
class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void countByAssignedDistributionSetPermissionsCheck() {
assertPermissions(() -> targetManagement.countByAssignedDistributionSet(1L),
List.of(SpPermission.READ_TARGET, SpPermission.READ_REPOSITORY));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void countByFiltersPermissionsCheck() {
assertPermissions(() -> targetManagement.countByFilters(new FilterParams(null, null, null, null, null, null)),
List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void countByInstalledDistributionSetPermissionsCheck() {
assertPermissions(() -> targetManagement.countByInstalledDistributionSet(1L),
List.of(SpPermission.READ_TARGET, SpPermission.READ_REPOSITORY));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void existsByInstalledOrAssignedDistributionSetPermissionsCheck() {
assertPermissions(() -> targetManagement.existsByInstalledOrAssignedDistributionSet(1L),
List.of(SpPermission.READ_TARGET, SpPermission.READ_REPOSITORY));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -73,14 +35,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
assertPermissions(() -> targetManagement.countByRsql("controllerId==id"), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void countByRsqlAndUpdatablePermissionsCheck() {
assertPermissions(() -> targetManagement.countByRsqlAndUpdatable("controllerId==id"), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -138,21 +92,13 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void deleteByControllerIDPermissionsCheck() {
void deleteByControllerIdPermissionsCheck() {
assertPermissions(() -> {
targetManagement.deleteByControllerID("controllerId");
targetManagement.deleteByControllerId("controllerId");
return null;
}, List.of(SpPermission.DELETE_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void countByTargetFilterQueryPermissionsCheck() {
assertPermissions(() -> targetManagement.countByTargetFilterQuery(1L), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -166,8 +112,8 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void countByRsqlAndNonDSAndCompatibleAndUpdatablePermissionsCheck() {
assertPermissions(() -> targetManagement.countByRsqlAndNonDSAndCompatibleAndUpdatable(1L, "controllerId==id"),
void countByRsqlAndNonDsAndCompatibleAndUpdatablePermissionsCheck() {
assertPermissions(() -> targetManagement.countByRsqlAndNonDsAndCompatibleAndUpdatable(1L, "controllerId==id"),
List.of(SpPermission.READ_TARGET, SpPermission.READ_REPOSITORY));
}
@@ -262,15 +208,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
assertPermissions(() -> targetManagement.getByControllerId("controllerId"), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void findByFiltersPermissionsCheck() {
assertPermissions(() -> targetManagement.findByFilters(new FilterParams(null, null, null, null, null, null), PAGE),
List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -289,14 +226,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
List.of(SpPermission.READ_TARGET, SpPermission.READ_REPOSITORY));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void findByUpdateStatusPermissionsCheck() {
assertPermissions(() -> targetManagement.findByUpdateStatus(TargetUpdateStatus.IN_SYNC, PAGE), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -313,14 +242,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
assertPermissions(() -> targetManagement.findByRsql("controllerId==id", PAGE), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void findByTargetFilterQueryPermissionsCheck() {
assertPermissions(() -> targetManagement.findByTargetFilterQuery(1L, PAGE), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -337,14 +258,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
assertPermissions(() -> targetManagement.findByRsqlAndTag("controllerId==id", 1L, PAGE), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void assignTypePermissionsCheck() {
assertPermissions(() -> targetManagement.assignType(List.of("controllerId"), 1L), List.of(SpPermission.UPDATE_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -388,14 +301,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
List.of(SpPermission.UPDATE_TARGET, SpPermission.READ_REPOSITORY));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void unassignTypePermissionsCheck() {
assertPermissions(() -> targetManagement.unassignType(List.of("controllerId")), List.of(SpPermission.UPDATE_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -428,14 +333,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
assertPermissions(() -> targetManagement.get(List.of(1L)), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void existsByControllerIdPermissionsCheck() {
assertPermissions(() -> targetManagement.existsByControllerId("controllerId"), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -462,33 +359,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
assertPermissions(() -> targetManagement.getControllerAttributes("controllerId"), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void requestControllerAttributesPermissionsCheck() {
assertPermissions(() -> {
targetManagement.requestControllerAttributes("controllerId");
return null;
}, List.of(SpPermission.UPDATE_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void isControllerAttributesRequestedPermissionsCheck() {
assertPermissions(() -> targetManagement.isControllerAttributesRequested("controllerId"), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void findByControllerAttributesRequestedPermissionsCheck() {
assertPermissions(() -> targetManagement.findByControllerAttributesRequested(PAGE), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/

View File

@@ -26,13 +26,18 @@ import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.SetJoin;
import jakarta.validation.ConstraintViolationException;
import org.assertj.core.api.Assertions;
import org.awaitility.Awaitility;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.im.authentication.SpRole;
import org.eclipse.hawkbit.repository.FilterParams;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.TargetManagement.Create;
import org.eclipse.hawkbit.repository.TargetManagement.Update;
@@ -54,12 +59,16 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidTargetAddressException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaNamedEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -70,9 +79,7 @@ import org.eclipse.hawkbit.repository.model.Tag;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch;
@@ -81,6 +88,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
/**
* Feature: Component Tests - Repository<br/>
@@ -91,7 +99,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
private static final String WHITESPACE_ERROR = "target with whitespaces in controller id should not be created";
/**
* Verifies that management get access react as specified on calls for non existing entities by means
* Verifies that management get access react as specified on calls for non existing entities by means
* of Optional not present.
*/
@Test
@@ -104,8 +112,8 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
}
/**
* Verifies that management queries react as specified on calls for non existing entities
* by means of throwing EntityNotFoundException.
* Verifies that management queries react as specified on calls for non existing entities
* by means of throwing EntityNotFoundException.
*/
@Test
@ExpectEvents({
@@ -122,15 +130,10 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
verifyThrownExceptionBy(() -> targetManagement.findByTag(NOT_EXIST_IDL, PAGE), "TargetTag");
verifyThrownExceptionBy(() -> targetManagement.findByRsqlAndTag("name==*", NOT_EXIST_IDL, PAGE), "TargetTag");
verifyThrownExceptionBy(() -> targetManagement.countByAssignedDistributionSet(NOT_EXIST_IDL), "DistributionSet");
verifyThrownExceptionBy(() -> targetManagement.countByInstalledDistributionSet(NOT_EXIST_IDL), "DistributionSet");
verifyThrownExceptionBy(() -> targetManagement.existsByInstalledOrAssignedDistributionSet(NOT_EXIST_IDL), "DistributionSet");
verifyThrownExceptionBy(() -> targetManagement.countByTargetFilterQuery(NOT_EXIST_IDL), "TargetFilterQuery");
verifyThrownExceptionBy(() -> targetManagement.countByRsqlAndNonDSAndCompatibleAndUpdatable(NOT_EXIST_IDL, "name==*"),
verifyThrownExceptionBy(() -> targetManagement.countByRsqlAndNonDsAndCompatibleAndUpdatable(NOT_EXIST_IDL, "name==*"),
"DistributionSet");
verifyThrownExceptionBy(() -> targetManagement.deleteByControllerID(NOT_EXIST_ID), "Target");
verifyThrownExceptionBy(() -> targetManagement.deleteByControllerId(NOT_EXIST_ID), "Target");
verifyThrownExceptionBy(() -> targetManagement.delete(Collections.singletonList(NOT_EXIST_IDL)), "Target");
verifyThrownExceptionBy(
@@ -166,7 +169,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
*/
@Test
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) })
void getTargetSecurityTokenOnlyWithCorrectPermission() throws Exception {
void getTargetSecurityTokenOnlyWithCorrectPermission() {
final Target createdTarget = targetManagement
.create(Create.builder().controllerId("targetWithSecurityToken").securityToken("token").build());
@@ -280,10 +283,8 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
target = createTargetWithAttributes("4711");
assertThat(targetManagement.count()).as("target count is wrong").isEqualTo(1);
assertThat(targetManagement.existsByControllerId("4711")).isTrue();
targetManagement.delete(Collections.singletonList(target.getId()));
assertThat(targetManagement.count()).as("target count is wrong").isZero();
assertThat(targetManagement.existsByControllerId("4711")).isFalse();
final List<Long> targets = new ArrayList<>();
for (int i = 0; i < 5; i++) {
@@ -316,19 +317,6 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
final DistributionSet testDs1 = testdataFactory.createDistributionSet("test");
final DistributionSet testDs2 = testdataFactory.createDistributionSet("test2");
assertThat(targetManagement.countByAssignedDistributionSet(testDs1.getId()))
.as("For newly created distributions sets the assigned target count should be zero").isZero();
assertThat(targetManagement.countByInstalledDistributionSet(testDs1.getId()))
.as("For newly created distributions sets the installed target count should be zero").isZero();
assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(testDs1.getId()))
.as("Exists assigned or installed query should return false for new distribution sets").isFalse();
assertThat(targetManagement.countByAssignedDistributionSet(testDs2.getId()))
.as("For newly created distributions sets the assigned target count should be zero").isZero();
assertThat(targetManagement.countByInstalledDistributionSet(testDs2.getId()))
.as("For newly created distributions sets the installed target count should be zero").isZero();
assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(testDs2.getId()))
.as("For newly created distributions sets the assigned target count should be zero").isFalse();
createTargetWithAttributes("4711");
final long current = System.currentTimeMillis();
@@ -344,19 +332,6 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
Target target = targetManagement.getByControllerId("4711").orElseThrow(IllegalStateException::new);
// read data
assertThat(targetManagement.countByAssignedDistributionSet(testDs1.getId())).as("Target count is wrong")
.isZero();
assertThat(targetManagement.countByInstalledDistributionSet(testDs1.getId())).as("Target count is wrong")
.isEqualTo(1);
assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(testDs1.getId()))
.as("Target count is wrong").isTrue();
assertThat(targetManagement.countByAssignedDistributionSet(testDs2.getId())).as("Target count is wrong")
.isEqualTo(1);
assertThat(targetManagement.countByInstalledDistributionSet(testDs2.getId())).as("Target count is wrong")
.isZero();
assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(testDs2.getId()))
.as("Target count is wrong").isTrue();
assertThat(target.getLastTargetQuery()).as("Target query is not work").isGreaterThanOrEqualTo(current);
final DistributionSet assignedDs = deploymentManagement.getAssignedDistributionSet("4711")
@@ -401,8 +376,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
@ExpectEvents({
@Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1) })
void singleTargetIsInsertedIntoRepo() throws Exception {
void singleTargetIsInsertedIntoRepo() {
final String myCtrlID = "myCtrlID";
Target savedTarget = testdataFactory.createTarget(myCtrlID);
@@ -482,7 +456,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
}
}
targetManagement.deleteByControllerID(extra.getControllerId());
targetManagement.deleteByControllerId(extra.getControllerId());
final int numberToDelete = 50;
final Collection<? extends Target> targetsToDelete = firstList.subList(0, numberToDelete);
@@ -495,7 +469,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
assertThat(firstList.spliterator().getExactSizeIfKnown() - numberToDelete).as("Size of split list")
.isEqualTo(targetsLeft.spliterator().getExactSizeIfKnown());
Assertions.<Target>assertThat(targetsLeft).as("Not all undeleted found").doesNotContain(deletedTargets);
Assertions.<Target> assertThat(targetsLeft).as("Not all undeleted found").doesNotContain(deletedTargets);
}
/**
@@ -567,7 +541,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
assignTag(tagABCTargets, tagB);
assignTag(tagABCTargets, tagC);
assertThat(targetManagement.countByFilters(new FilterParams(null, null, null, null, Boolean.FALSE, "X")))
assertThat(countByFilters(new FilterParams(Boolean.FALSE, "X")))
.as("Target count is wrong").isZero();
// search for targets with tag tagA
@@ -597,11 +571,11 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
checkTargetHasNotTags(tagCTargets, tagA, tagB);
// check again target lists refreshed from DB
assertThat(targetManagement.countByFilters(new FilterParams(null, null, null, null, Boolean.FALSE, "A")))
assertThat(countByFilters(new FilterParams(Boolean.FALSE, "A")))
.as("Target count is wrong").isEqualTo(targetWithTagA.size());
assertThat(targetManagement.countByFilters(new FilterParams(null, null, null, null, Boolean.FALSE, "B")))
assertThat(countByFilters(new FilterParams(Boolean.FALSE, "B")))
.as("Target count is wrong").isEqualTo(targetWithTagB.size());
assertThat(targetManagement.countByFilters(new FilterParams(null, null, null, null, Boolean.FALSE, "C")))
assertThat(countByFilters(new FilterParams(Boolean.FALSE, "C")))
.as("Target count is wrong").isEqualTo(targetWithTagC.size());
}
@@ -682,12 +656,10 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
testdataFactory.createTargets(25, "target-id-B", "first description");
final String[] tagNames = null;
final List<Target> targetsListWithNoTag = targetManagement
.findByFilters(new FilterParams(null, null, null, null, Boolean.TRUE, tagNames), PAGE).getContent();
final long targetsListWithNoTag = countByFilters(new FilterParams(Boolean.TRUE, tagNames));
assertThat(targetManagement.count()).as("Total targets").isEqualTo(50L);
assertThat(targetsListWithNoTag).as("Targets with no tag").hasSize(25);
assertThat(targetsListWithNoTag).as("Targets with no tag").isEqualTo(25);
}
/**
@@ -758,16 +730,11 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
void verifyRequestControllerAttributes() {
final String knownControllerId = "KnownControllerId";
final Target target = createTargetWithAttributes(knownControllerId);
assertThat(targetManagement.findByControllerAttributesRequested(PAGE)).isEmpty();
assertThat(targetManagement.isControllerAttributesRequested(knownControllerId)).isFalse();
targetManagement.requestControllerAttributes(knownControllerId);
final Target updated = targetManagement.getByControllerId(knownControllerId).get();
assertThat(target.isRequestControllerAttributes()).isFalse();
assertThat(targetManagement.findByControllerAttributesRequested(PAGE).getContent()).contains(updated);
assertThat(targetManagement.isControllerAttributesRequested(knownControllerId)).isTrue();
targetManagement.update(Update.builder().id(target.getId()).requestControllerAttributes(true).build());
final Target updated = targetManagement.getByControllerId(knownControllerId).orElseThrow();
assertThat(updated.isRequestControllerAttributes()).isTrue();
}
/**
@@ -949,8 +916,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
@ExpectEvents({
@Expect(type = TargetCreatedEvent.class, count = 20),
@Expect(type = TargetTypeCreatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 29),
@Expect(type = TargetDeletedEvent.class, count = 1) })
@Expect(type = TargetUpdatedEvent.class, count = 20) })
void targetTypeBulkAssignments() {
final List<Target> typeATargets = testdataFactory.createTargets(10, "typeATargets", "first description");
final List<Target> typeBTargets = testdataFactory.createTargets(10, "typeBTargets", "first description");
@@ -960,30 +926,16 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
final TargetType typeB = testdataFactory.createTargetType("B", Set.of(standardDsType));
// assign target type to target
TargetTypeAssignmentResult resultA = initiateTypeAssignment(typeATargets, typeA);
TargetTypeAssignmentResult resultB = initiateTypeAssignment(typeBTargets, typeB);
assertThat(resultA.getAssigned()).isEqualTo(10);
assertThat(resultB.getAssigned()).isEqualTo(10);
initiateTypeAssignment(typeATargets, typeA);
initiateTypeAssignment(typeBTargets, typeB);
checkTargetsHaveType(typeATargets, typeA);
checkTargetsHaveType(typeBTargets, typeB);
// double assignment does not unassign
resultA = initiateTypeAssignment(typeATargets, typeA);
resultB = initiateTypeAssignment(typeBTargets, typeB);
assertThat(resultA.getAssigned()).isZero();
assertThat(resultB.getAssigned()).isZero();
assertThat(resultA.getAlreadyAssigned()).isEqualTo(10);
assertThat(resultB.getAlreadyAssigned()).isEqualTo(10);
initiateTypeAssignment(typeATargets, typeA);
initiateTypeAssignment(typeBTargets, typeB);
checkTargetsHaveType(typeATargets, typeA);
checkTargetsHaveType(typeBTargets, typeB);
// verify that type assignment does not throw an error if target list
// includes an unknown id
targetManagement.deleteByControllerID(typeATargets.get(0).getControllerId());
final TargetTypeAssignmentResult resultC = initiateTypeAssignment(typeATargets, typeB);
assertThat(resultC.getAssigned()).isEqualTo(9);
assertThat(resultC.getAlreadyAssigned()).isZero();
checkTargetsHaveType(typeATargets, typeB);
}
/**
@@ -1170,13 +1122,11 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
final DistributionSet ds = testdataFactory.createDistributionSet();
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
"notExisting", ds.getId(),"name==*")).isFalse();
"notExisting", ds.getId(), "name==*")).isFalse();
}
/**
* Tests action based aspects of the dynamic group assignment filters.
*/
/**
* Target matches filter no active action with ge weight.
*/
@Test
@@ -1344,6 +1294,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
.as("target with too long address should not be updated")
.isThrownBy(() -> targetManagement.update(targetUpdate));
}
private void createTargetWithInvalidControllerId() {
final Create targetCreateEmpty = Create.builder().controllerId("").build();
assertThatExceptionOfType(ConstraintViolationException.class)
@@ -1424,8 +1375,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
private void checkTargetHasNotTags(final Iterable<Target> targets, final TargetTag... tags) {
for (final Target tl : targets) {
targetManagement.getByControllerId(tl.getControllerId()).get();
targetManagement.getByControllerId(tl.getControllerId()).orElseThrow();
for (final Tag tag : tags) {
for (final Tag tt : getTargetTags(tl.getControllerId())) {
if (tag.getName().equals(tt.getName())) {
@@ -1438,7 +1388,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
private void insertMetadata(final String knownKey, final String knownValue, final Target target) {
targetManagement.createMetadata(target.getControllerId(), Map.of(knownKey, knownValue));
assertThat(targetManagement.getMetadata(target.getControllerId()).get(knownKey)).isEqualTo(knownValue);
assertThat(targetManagement.getMetadata(target.getControllerId())).containsEntry(knownKey, knownValue);
}
private void checkTargetsHaveType(final List<Target> targets, final TargetType type) {
@@ -1461,14 +1411,11 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
return target;
}
private Target createTargetWithTargetTypeAndMetadata(final String controllerId, final TargetType targetType, final int count) {
private void createTargetWithTargetTypeAndMetadata(final String controllerId, final TargetType targetType, final int count) {
final Target target = testdataFactory.createTarget(controllerId, controllerId, targetType);
for (int index = 1; index <= count; index++) {
insertMetadata("key" + index, controllerId + "-value" + index, target);
}
return target;
}
private void createAction(final Target target, final Rollout rollout, final Integer weight, final Action.Status status,
@@ -1499,4 +1446,65 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
assertThat(foundTargetsByMetadataAndControllerId.getContent().stream().map(Target::getControllerId))
.as("Targets found by RSQL filter have wrong controller ids").containsExactlyInAnyOrder(controllerIds);
}
}
private long countByFilters(final FilterParams filterParams) {
final List<Specification<JpaTarget>> specList = buildSpecificationList(filterParams);
return JpaManagementHelper.countBySpec(targetRepository, specList);
}
private List<Specification<JpaTarget>> buildSpecificationList(final FilterParams filterParams) {
final List<Specification<JpaTarget>> specList = new ArrayList<>();
if (hasTagsFilterActive(filterParams)) {
specList.add(hasTags(filterParams.filterByTagNames(), filterParams.selectTargetWithNoTag()));
}
return specList;
}
private static boolean hasTagsFilterActive(final FilterParams filterParams) {
final boolean isNoTagActive = Boolean.TRUE.equals(filterParams.selectTargetWithNoTag());
final boolean isAtLeastOneTagActive = filterParams.filterByTagNames() != null && filterParams.filterByTagNames().length > 0;
return isNoTagActive || isAtLeastOneTagActive;
}
/**
* {@link Specification} for retrieving {@link Target}s by "has no tag names"or "has at least on of the given tag names".
*
* @param tagNames to be filtered on
* @param selectTargetWithNoTag flag to get targets with no tag assigned
* @return the {@link Target} {@link Specification}
*/
private static Specification<JpaTarget> hasTags(final String[] tagNames, final Boolean selectTargetWithNoTag) {
return (targetRoot, query, cb) -> {
final Predicate predicate = getHasTagsPredicate(targetRoot, cb, selectTargetWithNoTag, tagNames);
query.distinct(true);
return predicate;
};
}
private static Predicate getHasTagsPredicate(
final Root<JpaTarget> targetRoot, final CriteriaBuilder cb, final Boolean selectTargetWithNoTag, final String[] tagNames) {
final SetJoin<JpaTarget, JpaTargetTag> tags = targetRoot.join(JpaTarget_.tags, JoinType.LEFT);
final Path<String> exp = tags.get(AbstractJpaNamedEntity_.name);
final List<Predicate> hasTagsPredicates = new ArrayList<>();
if (isNoTagActive(selectTargetWithNoTag)) {
hasTagsPredicates.add(exp.isNull());
}
if (isAtLeastOneTagActive(tagNames)) {
hasTagsPredicates.add(exp.in((Object[]) tagNames));
}
return hasTagsPredicates.stream().reduce(cb::or)
.orElseThrow(() -> new RuntimeException("Neither NO_TAG, nor TAG target tag filter was provided!"));
}
private static boolean isNoTagActive(final Boolean selectTargetWithNoTag) {
return Boolean.TRUE.equals(selectTargetWithNoTag);
}
private static boolean isAtLeastOneTagActive(final String[] tagNames) {
return tagNames != null && tagNames.length > 0;
}
private record FilterParams(Boolean selectTargetWithNoTag, String... filterByTagNames) {}
}

View File

@@ -31,8 +31,6 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.awaitility.Awaitility;
import org.awaitility.core.ConditionFactory;
import org.eclipse.hawkbit.repository.artifact.ArtifactRepository;
import org.eclipse.hawkbit.repository.artifact.exception.ArtifactStoreException;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ConfirmationManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
@@ -55,6 +53,8 @@ import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TargetTagManagement;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.artifact.ArtifactRepository;
import org.eclipse.hawkbit.repository.artifact.exception.ArtifactStoreException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
@@ -70,6 +70,7 @@ import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.TestConfiguration;
import org.eclipse.hawkbit.repository.test.matcher.EventVerifier;
import org.eclipse.hawkbit.security.SystemSecurityContext;
@@ -436,7 +437,8 @@ public abstract class AbstractIntegrationTest {
return ds.getModules().stream().filter(module -> module.getType().equals(type)).findAny();
}
protected Optional<? extends SoftwareModule> findFirstModuleByType(final DistributionSetManagement.Create dsCreate, final SoftwareModuleType type) {
protected Optional<? extends SoftwareModule> findFirstModuleByType(final DistributionSetManagement.Create dsCreate,
final SoftwareModuleType type) {
return dsCreate.getModules().stream().filter(module -> module.getType().equals(type)).findAny();
}
@@ -512,4 +514,8 @@ public abstract class AbstractIntegrationTest {
throw new ArtifactStoreException("Cannot create temp file", e);
}
}
protected List<? extends Target> findByUpdateStatus(final TargetUpdateStatus status, final Pageable pageable) {
return targetManagement.findAll(pageable).stream().filter(target -> status.equals(target.getUpdateStatus())).toList();
}
}