From bff77ac22490b0e45c821d761debbbb1032593a2 Mon Sep 17 00:00:00 2001 From: Avgustin Marinov Date: Fri, 8 Aug 2025 13:13:28 +0300 Subject: [PATCH] Cleanup TargetManagement (#2601) Signed-off-by: Avgustin Marinov --- .../ddi/rest/resource/DdiRootController.java | 3 +- .../rest/resource/DdiDeploymentBaseTest.java | 10 +- ...ssageDispatcherServiceIntegrationTest.java | 2 +- .../rest/resource/MgmtTargetResource.java | 11 +- .../MgmtDistributionSetResourceTest.java | 2 +- .../rest/resource/MgmtTargetResourceTest.java | 10 +- .../hawkbit/repository/FilterParams.java | 81 --- .../hawkbit/repository/TargetManagement.java | 480 ++++++---------- .../hawkbit/repository/model/Action.java | 6 +- .../repository/model/ActionStatus.java | 11 +- .../model/TargetTypeAssignmentResult.java | 41 -- .../JpaDistributionSetTagManagement.java | 19 - .../JpaTargetFilterQueryManagement.java | 2 +- .../jpa/management/JpaTargetManagement.java | 411 ++++---------- .../specifications/ActionSpecifications.java | 4 - .../DistributionSetSpecification.java | 27 - .../DistributionSetTagSpecifications.java | 29 - .../DistributionSetTypeSpecification.java | 22 +- .../SoftwareModuleSpecification.java | 74 +-- .../SoftwareModuleTypeSpecification.java | 36 -- .../jpa/specifications/TagSpecification.java | 48 -- .../TargetFilterQuerySpecification.java | 21 +- .../specifications/TargetSpecifications.java | 166 +----- .../TargetTypeSpecification.java | 20 - .../jpa/AbstractJpaIntegrationTest.java | 13 +- .../jpa/acm/TargetAccessControllerTest.java | 31 -- .../autoassign/AutoAssignCheckerIntTest.java | 6 +- .../jpa/event/RepositoryEntityEventTest.java | 2 +- .../management/DeploymentManagementTest.java | 2 +- .../jpa/management/RolloutManagementTest.java | 7 +- .../TargetManagementSearchTest.java | 512 ------------------ .../TargetManagementSecurityTest.java | 138 +---- .../jpa/management/TargetManagementTest.java | 198 +++---- .../test/util/AbstractIntegrationTest.java | 12 +- .../rest/AbstractRestIntegrationTest.java | 8 + 35 files changed, 433 insertions(+), 2032 deletions(-) delete mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/FilterParams.java delete mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetTypeAssignmentResult.java delete mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/DistributionSetTagSpecifications.java delete mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/SoftwareModuleTypeSpecification.java delete mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TagSpecification.java diff --git a/hawkbit-ddi/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java b/hawkbit-ddi/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java index bf7261b5c..50f4fdfe9 100644 --- a/hawkbit-ddi/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java +++ b/hawkbit-ddi/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java @@ -334,8 +334,7 @@ public class DdiRootController implements DdiRootControllerRestApi { final Target target = findTarget(controllerId); final Action action = findActionForTarget(actionId, target); - controllerManagement - .addCancelActionStatus(generateActionCancelStatus(feedback, target, action.getId(), entityFactory)); + controllerManagement.addCancelActionStatus(generateActionCancelStatus(feedback, target, action.getId(), entityFactory)); return ResponseEntity.ok().build(); } diff --git a/hawkbit-ddi/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java b/hawkbit-ddi/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java index f3ddfde3e..a3b9bdf0a 100644 --- a/hawkbit-ddi/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java +++ b/hawkbit-ddi/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java @@ -531,7 +531,7 @@ class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { implicitLock(ds3); findTargetAndAssertUpdateStatus(Optional.of(ds3), TargetUpdateStatus.PENDING, 3, Optional.empty()); - assertThat(targetManagement.findByUpdateStatus(TargetUpdateStatus.UNKNOWN, PageRequest.of(0, 10))).hasSize(2); + assertThat(findByUpdateStatus(TargetUpdateStatus.UNKNOWN, PageRequest.of(0, 10))).hasSize(2); // action1 done postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId1, getJsonClosedDeploymentActionFeedback(), status().isOk()); @@ -563,7 +563,7 @@ class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { assertThat(targetManagement.getByControllerId(DEFAULT_CONTROLLER_ID).get().getUpdateStatus()).isEqualTo(TargetUpdateStatus.UNKNOWN); assignDistributionSet(ds, Collections.singletonList(savedTarget)); final Action action = actionRepository - .findAll(ActionSpecifications.byDistributionSetId(ds.getId()), PAGE) + .findAll(byDistributionSetId(ds.getId()), PAGE) .map(Action.class::cast).getContent().get(0); postDeploymentFeedback(DEFAULT_CONTROLLER_ID, action.getId(), @@ -804,9 +804,9 @@ class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { } private void assertTargetCountByStatus(final int pending, final int error, final int inSync) { - assertThat(targetManagement.findByUpdateStatus(TargetUpdateStatus.PENDING, PageRequest.of(0, 10))).hasSize(pending); - assertThat(targetManagement.findByUpdateStatus(TargetUpdateStatus.ERROR, PageRequest.of(0, 10))).hasSize(error); - assertThat(targetManagement.findByUpdateStatus(TargetUpdateStatus.IN_SYNC, PageRequest.of(0, 10))).hasSize(inSync); + assertThat(findByUpdateStatus(TargetUpdateStatus.PENDING, PageRequest.of(0, 10))).hasSize(pending); + assertThat(findByUpdateStatus(TargetUpdateStatus.ERROR, PageRequest.of(0, 10))).hasSize(error); + assertThat(findByUpdateStatus(TargetUpdateStatus.IN_SYNC, PageRequest.of(0, 10))).hasSize(inSync); } private void assertActionStatusCount(final int total, final int running, final int warning, final int finished, final int canceled) { diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java index 762ff8052..c9a01574d 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java @@ -538,7 +538,7 @@ class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpServiceInt final String controllerId = TARGET_PREFIX + "sendDeleteMessage"; registerAndAssertTargetWithExistingTenant(controllerId); - targetManagement.deleteByControllerID(controllerId); + targetManagement.deleteByControllerId(controllerId); assertDeleteMessage(controllerId); } diff --git a/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResource.java b/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResource.java index 052149267..3c1eb6d11 100644 --- a/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResource.java +++ b/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResource.java @@ -142,14 +142,9 @@ public class MgmtTargetResource implements MgmtTargetRestApi { @Override @AuditLog(entity = "Target", type = AuditLog.Type.UPDATE, description = "Update Target") public ResponseEntity updateTarget(final String targetId, final MgmtTargetRequestBody targetRest) { - if (targetRest.getRequestAttributes() != null) { - if (Boolean.TRUE.equals(targetRest.getRequestAttributes())) { - targetManagement.requestControllerAttributes(targetId); - } else { - return ResponseEntity.badRequest().build(); - } + if (targetRest.getRequestAttributes() != null && !Boolean.TRUE.equals(targetRest.getRequestAttributes())) { + return ResponseEntity.badRequest().build(); } - final Target targetToUpdate = targetManagement.getByControllerId(targetId) .orElseThrow(() -> new EntityNotFoundException(Target.class, targetId)); final TargetType targetType = Optional.ofNullable(targetRest.getTargetType()) @@ -180,7 +175,7 @@ public class MgmtTargetResource implements MgmtTargetRestApi { @Override @AuditLog(entity = "Target", type = AuditLog.Type.DELETE, description = "Delete Target") public ResponseEntity deleteTarget(final String targetId) { - this.targetManagement.deleteByControllerID(targetId); + this.targetManagement.deleteByControllerId(targetId); log.debug("{} target deleted, return status {}", targetId, HttpStatus.OK); return ResponseEntity.ok().build(); } diff --git a/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResourceTest.java b/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResourceTest.java index cfe363dfe..1f9d88369 100644 --- a/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResourceTest.java +++ b/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResourceTest.java @@ -1440,7 +1440,7 @@ class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegrationTe assertThat( actionRepository - .findAll(ActionSpecifications.byDistributionSetId(createdDs.getId()), PAGE) + .findAll(byDistributionSetId(createdDs.getId()), PAGE) .map(Action.class::cast).getContent()).hasSize(1) .allMatch(action -> { if (!confirmationFlowActive) { diff --git a/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java b/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java index 1ebf2ea76..9c5c62e5c 100644 --- a/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java +++ b/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java @@ -1687,7 +1687,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest { assertThat( actionRepository - .findAll(ActionSpecifications.byDistributionSetId(set.getId()), PAGE) + .findAll(byDistributionSetId(set.getId()), PAGE) .map(Action.class::cast).getContent()).hasSize(1) .allMatch(action -> { if (!confirmationFlowActive) { @@ -2038,12 +2038,10 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest { knownControllerAttrs.put("b.2", "2"); testdataFactory.createTarget(knownTargetId); controllerManagement.updateControllerAttributes(knownTargetId, knownControllerAttrs, null); - assertThat(targetManagement.isControllerAttributesRequested(knownTargetId)).isFalse(); + assertThat(targetManagement.getByControllerId(knownTargetId).orElseThrow().isRequestControllerAttributes()).isFalse(); verifyAttributeUpdateCanBeRequested(knownTargetId); - verifyRequestAttributesAttributeIsOptional(knownTargetId); - verifyResettingRequestAttributesIsNotAllowed(knownTargetId); } @@ -2988,7 +2986,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest { .andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()); - assertThat(targetManagement.isControllerAttributesRequested(knownTargetId)).isTrue(); + assertThat(targetManagement.getByControllerId(knownTargetId).orElseThrow().isRequestControllerAttributes()).isTrue(); } private void verifyRequestAttributesAttributeIsOptional(final String knownTargetId) throws Exception { @@ -3008,7 +3006,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest { .andDo(MockMvcResultPrinter.print()) .andExpect(status().isBadRequest()); - assertThat(targetManagement.isControllerAttributesRequested(knownTargetId)).isTrue(); + assertThat(targetManagement.getByControllerId(knownTargetId).orElseThrow().isRequestControllerAttributes()).isTrue(); } private String getCreateTargetsListJsonString(final String controllerId, final String name, final String description) { diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/FilterParams.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/FilterParams.java deleted file mode 100644 index ef4fd6902..000000000 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/FilterParams.java +++ /dev/null @@ -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. null for simple properties) When - * applied, these filters are AND-gated. - */ -@Data -public class FilterParams { - - private final Collection 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 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; - } -} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java index 6e77442af..3442bd4d6 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java @@ -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 } /** - * 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 true 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 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 getByControllerId(@NotEmpty Collection 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 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 @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 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 Slice findByFailedRolloutAndNotInRolloutGroups( @NotNull String rolloutId, @NotEmpty Collection 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 groups); - @PreAuthorize(HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ) Slice 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 * @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 Page 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 getByControllerId(@NotEmpty Collection 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 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 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 @PreAuthorize(HAS_AUTH_READ_TARGET) Optional 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 findByFilters(@NotNull FilterParams filterParams, @NotNull Pageable pageable); - /** * retrieves {@link Target}s by the installed {@link DistributionSet}. * @@ -388,30 +312,6 @@ public interface TargetManagement @PreAuthorize(HAS_AUTH_READ_DISTRIBUTION_SET_AND_READ_TARGET) Page 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 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 findByTargetFilterQuery(long targetFilterQueryId, @NotNull Pageable pageable); - /** * Find targets by tag name. * @@ -438,39 +338,35 @@ public interface TargetManagement @PreAuthorize(HAS_AUTH_READ_TARGET) Page 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 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 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 @PreAuthorize(HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) List assignTag(@NotEmpty Collection 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 getTags(@NotEmpty String controllerId); + /** * Un-assign a {@link TargetTag} assignment to given {@link Target}s. * @@ -519,24 +425,22 @@ public interface TargetManagement List unassignTag(@NotEmpty Collection 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 controllerIds); /** * Finds targets by group or subgroup. @@ -557,82 +461,6 @@ public interface TargetManagement @PreAuthorize(HAS_AUTH_READ_TARGET) List 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 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 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 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 findByControllerAttributesRequested(@NotNull Pageable pageable); - /** * Creates or updates a meta-data value. * @@ -718,8 +546,8 @@ public interface TargetManagement // 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() diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Action.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Action.java index e7ace0b6e..3be04a175 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Action.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Action.java @@ -55,9 +55,7 @@ public interface Action extends TenantAwareBaseEntity { DistributionSet getDistributionSet(); /** - * @return true when action is in state - * {@link Status#CANCELING} or {@link Status#CANCELED}, false - * otherwise + * @return true 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 } -} +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ActionStatus.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ActionStatus.java index 24a432f64..da578ee43 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ActionStatus.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ActionStatus.java @@ -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(); diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetTypeAssignmentResult.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetTypeAssignmentResult.java deleted file mode 100644 index 2ae7ad1e6..000000000 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetTypeAssignmentResult.java +++ /dev/null @@ -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 { - - 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 assigned, - final List unassigned, final TargetType targetType) { - super(alreadyAssigned, assigned, unassigned); - this.targetType = targetType; - } -} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetTagManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetTagManagement.java index c3353435e..b49f3a69b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetTagManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetTagManagement.java @@ -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) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetFilterQueryManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetFilterQueryManagement.java index 376fc17cf..4a2ae9922 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetFilterQueryManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetFilterQueryManagement.java @@ -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); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetManagement.java index 7e4516b79..d51ae5f33 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetManagement.java @@ -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 getControllerAttributes(final String controllerId) { + return getMap(controllerId, JpaTarget_.controllerAttributes); } @Override - public long countByFilters(final FilterParams filterParams) { - final List> 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> 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 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> 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> 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 findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(final long distributionSetId, final String rsql, - final Pageable pageable) { + public Slice 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 getByControllerId(final Collection 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 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 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 findByFailedRolloutAndNotInRolloutGroups(String rolloutId, Collection groups, Pageable pageable) { final List> specList = List.of( @@ -245,14 +180,6 @@ public class JpaTargetManagement return JpaManagementHelper.findAllWithCountBySpec(jpaRepository, specList, pageable); } - @Override - public long countByFailedRolloutAndNotInRolloutGroups(String rolloutId, Collection groups) { - final List> specList = List.of( - TargetSpecifications.failedActionsForRollout(rolloutId), - TargetSpecifications.isNotInRolloutGroups(groups)); - return JpaManagementHelper.countBySpec(jpaRepository, specList); - } - @Override public Slice 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 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 getByControllerId(final Collection controllerIDs) { - return Collections.unmodifiableList(jpaRepository.findAll(TargetSpecifications.byControllerIdWithAssignedDsInJoin(controllerIDs))); - } - - @Override - public Optional 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 findByFilters(final FilterParams filterParams, final Pageable pageable) { - final List> specList = buildSpecificationList(filterParams); - return JpaManagementHelper.findAllWithoutCountBySpec(jpaRepository, specList, pageable); - } - @Override public Page 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 findByUpdateStatus(final TargetUpdateStatus status, final Pageable pageable) { - return JpaManagementHelper.findAllWithCountBySpec( - jpaRepository, List.of(TargetSpecifications.hasTargetUpdateStatus(status)), pageable); - } - - @Override - public Slice 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 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 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> specList = List.of( + RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class), + TargetSpecifications.isCompatibleWithDistributionSetType(distributionSetIdTypeId)); + return JpaManagementHelper.countBySpec(jpaRepository, specList); + } - final List targetsWithSameType = findTargetsByInSpecification(controllerIds, TargetSpecifications.hasTargetType(typeId)); - final List targetsWithoutSameType = - findTargetsByInSpecification(controllerIds, TargetSpecifications.hasTargetTypeNot(typeId)); + @Override + public long countByFailedInRollout(final String rolloutId, final Long dsTypeId) { + final List> 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 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 groups) { + final List> 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 controllerIds) { - final List 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 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 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 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 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> specList = List.of( - RsqlUtility.getInstance().buildRsqlSpecification(targetFilterQuery, TargetFields.class), - TargetSpecifications.hasNotDistributionSetInActions(distributionSetId), - TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId), - TargetSpecifications.hasControllerId(controllerId)); - - final Specification combinedSpecification = Objects - .requireNonNull(SpecificationsBuilder.combineWithAnd(specList)); - return jpaRepository.exists(AccessController.Operation.UPDATE, combinedSpecification); - } - - @Override - public Set getTags(@NotEmpty String controllerId) { - // the method has PreAuthorized by itself - return ((JpaTarget) getWithTags(controllerId)).getTags(); - } - - @Override - public Map 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 findByControllerAttributesRequested(final Pageable pageable) { - return JpaManagementHelper.findAllWithCountBySpec( - jpaRepository, List.of(TargetSpecifications.hasRequestControllerAttributesTrue()), pageable); + public List 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 notFound(final Collection controllerIds, final List foundTargets) { final Map 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> buildSpecificationList(final FilterParams filterParams) { - final List> 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 findTargetsByInSpecification(final Collection controllerIds, final Specification 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 updateTag( final Collection controllerIds, final long targetTagId, final Consumer> notFoundHandler, final BiFunction 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 notFound = notFound(controllerIds, targets); if (notFoundHandler == null) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/ActionSpecifications.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/ActionSpecifications.java index 63c90926b..c20936df7 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/ActionSpecifications.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/ActionSpecifications.java @@ -78,10 +78,6 @@ public final class ActionSpecifications { isNull ? cb.isNull(root.get(JpaAction_.weight)) : cb.isNotNull(root.get(JpaAction_.weight))); } - public static Specification byDistributionSetId(final Long distributionSetId) { - return (root, query, cb) -> cb.equal(root.get(JpaAction_.distributionSet).get(AbstractJpaBaseEntity_.id), distributionSetId); - } - public static Specification byDistributionSetIdAndActive(final Long distributionSetId) { return (root, query, cb) -> cb.and( cb.equal(root.get(JpaAction_.distributionSet).get(AbstractJpaBaseEntity_.id), distributionSetId), diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/DistributionSetSpecification.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/DistributionSetSpecification.java index 82acb99d0..a0f2cb936 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/DistributionSetSpecification.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/DistributionSetSpecification.java @@ -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 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 hasType(final Collection typeIds) { - return (dsRoot, query, cb) -> dsRoot.get(JpaDistributionSet_.type).get(AbstractJpaBaseEntity_.id).in(typeIds); - } - /** * {@link Specification} for retrieving {@link DistributionSet}s by tag. * diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/DistributionSetTagSpecifications.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/DistributionSetTagSpecifications.java deleted file mode 100644 index 99b431812..000000000 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/DistributionSetTagSpecifications.java +++ /dev/null @@ -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 byName(@NotEmpty final String name) { - return (targetRoot, query, cb) -> cb.equal(targetRoot.get(AbstractJpaNamedEntity_.name), name); - } -} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/DistributionSetTypeSpecification.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/DistributionSetTypeSpecification.java index e473656cc..8e20c1e73 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/DistributionSetTypeSpecification.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/DistributionSetTypeSpecification.java @@ -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 false - i.e. is not deleted. - * - * @return the {@link DistributionSetType} {@link Specification} - */ - public static Specification isNotDeleted() { - return (targetRoot, query, cb) -> cb.equal(targetRoot. 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 byKey(final String key) { return (targetRoot, query, cb) -> cb.equal(targetRoot.get(AbstractJpaTypeEntity_.key), key); } -} +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/SoftwareModuleSpecification.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/SoftwareModuleSpecification.java index 16287e1e5..0c66f7a9a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/SoftwareModuleSpecification.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/SoftwareModuleSpecification.java @@ -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 byId(final Long swModuleId) { - return (swRoot, query, cb) -> cb.equal(swRoot.get(AbstractJpaBaseEntity_.id), swModuleId); - } - public static Specification byAssignedToDs(final Long dsId) { return (swRoot, query, cb) -> { final ListJoin join = swRoot.join(JpaSoftwareModule_.assignedTo); return cb.equal(join.get(AbstractJpaBaseEntity_.ID), dsId); }; } - - /** - * {@link Specification} for retrieving {@link SoftwareModule}s with - * DELETED attribute false - i.e. is not deleted. - * - * @return the {@link SoftwareModule} {@link Specification} - */ - public static Specification 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 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 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 fetchType() { - return (smRoot, query, cb) -> { - if (!query.getResultType().isAssignableFrom(Long.class)) { - smRoot.fetch(JpaSoftwareModule_.type); - } - return cb.conjunction(); - }; - } } \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/SoftwareModuleTypeSpecification.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/SoftwareModuleTypeSpecification.java deleted file mode 100644 index c9a5d7109..000000000 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/SoftwareModuleTypeSpecification.java +++ /dev/null @@ -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 false - i.e. is not deleted. - * - * @return the {@link SoftwareModuleType} {@link Specification} - */ - public static Specification isNotDeleted() { - return (root, query, cb) -> cb.equal(root.get(JpaSoftwareModuleType_.deleted), false); - } -} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TagSpecification.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TagSpecification.java deleted file mode 100644 index 57ffdde84..000000000 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TagSpecification.java +++ /dev/null @@ -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 ofDistributionSet(final Long dsId) { - return (dsRoot, query, criteriaBuilder) -> { - final Join tagJoin = dsRoot - .join(JpaDistributionSetTag_.assignedToDistributionSet); - - query.distinct(true); - - return criteriaBuilder.equal(tagJoin.get(AbstractJpaBaseEntity_.id), dsId); - }; - } -} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetFilterQuerySpecification.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetFilterQuerySpecification.java index 8adb15f1e..1fa8a7962 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetFilterQuerySpecification.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetFilterQuerySpecification.java @@ -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 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()}. diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java index 43fc6cbfb..5a976433f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java @@ -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 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 hasTargetUpdateStatus(final Collection 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 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:
- * poll_itvl + overdue_itvl - * - * @param overdueTimestamp the calculated timestamp to compare with the last response of a target (lastTargetQuery).
- * The overdueTimestamp has to be calculated with the following expression:
- * overdueTimestamp = nowTimestamp - poll_itvl - overdue_itvl - * @return the {@link Target} {@link Specification} - */ - public static Specification 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 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 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 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 hasControllerIdAndAssignedDistributionSetIdNot(final List tIDs, - @NotNull final Long distributionId) { + public static Specification hasControllerIdAndAssignedDistributionSetIdNot( + final List 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 hasTagName(final String tagName) { - return (targetRoot, query, cb) -> { - final SetJoin 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 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 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 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 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 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 failedActionsForRollout(final String rolloutId) { return (targetRoot, query, cb) -> { final Join targetActions = targetRoot.join("actions"); @@ -461,35 +330,6 @@ public final class TargetSpecifications { }; } - private static Predicate getHasTagsPredicate(final Root targetRoot, final CriteriaBuilder cb, - final Boolean selectTargetWithNoTag, final String[] tagNames) { - final SetJoin tags = targetRoot.join(JpaTarget_.tags, JoinType.LEFT); - final Path exp = tags.get(AbstractJpaNamedEntity_.name); - - final List 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 targetRoot) { - return targetRoot.get(JpaTarget_.targetType).isNull(); - } - private static Path getDsTypeIdPath(final Root root) { final Join targetTypeJoin = root.join(JpaTarget_.targetType, JoinType.LEFT); return targetTypeJoin.join(JpaTargetType_.distributionSetTypes, JoinType.LEFT).get(AbstractJpaBaseEntity_.id); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetTypeSpecification.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetTypeSpecification.java index 3bddf38bc..9d45caf6d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetTypeSpecification.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetTypeSpecification.java @@ -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 hasIdIn(final Collection 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 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 likeName(final String name) { - return (targetRoot, query, cb) -> cb.like(cb.lower(targetRoot.get(AbstractJpaNamedEntity_.name)), name.toLowerCase()); - } } \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java index 4e02969e8..8a5e4be20 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java @@ -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 targets, final TargetType type) { - return targetManagement.assignType( - targets.stream().map(Target::getControllerId).toList(), type.getId()); + protected void initiateTypeAssignment(final Collection 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 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(); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetAccessControllerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetAccessControllerTest.java index ac7d63751..9aab00788 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetAccessControllerTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetAccessControllerTest.java @@ -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()); }); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerIntTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerIntTest.java index aae12974d..9040a4753 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerIntTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerIntTest.java @@ -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 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 findActionsByDistributionSet(final Pageable pageable, final long distributionSetId) { return actionRepository - .findAll(ActionSpecifications.byDistributionSetId(distributionSetId), pageable) + .findAll(byDistributionSetId(distributionSetId), pageable) .map(Action.class::cast); } } \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/event/RepositoryEntityEventTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/event/RepositoryEntityEventTest.java index a823980d3..eff258346 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/event/RepositoryEntityEventTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/event/RepositoryEntityEventTest.java @@ -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(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DeploymentManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DeploymentManagementTest.java index 713d2b2e9..7f814ec31 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DeploymentManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DeploymentManagementTest.java @@ -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); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/RolloutManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/RolloutManagementTest.java index 3ea7eb150..60092d166 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/RolloutManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/RolloutManagementTest.java @@ -958,11 +958,12 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { expectedTargetCountStatus.put(TotalTargetCountStatus.Status.FINISHED, 9L); validateRolloutActionStatus(rolloutTwo.getId(), expectedTargetCountStatus); changeStatusForAllRunningActions(rolloutTwo, Status.FINISHED); - final Page targetPage = targetManagement.findByUpdateStatus(TargetUpdateStatus.IN_SYNC, PAGE); - final List targetList = targetPage.getContent(); + final List 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)); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementSearchTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementSearchTest.java index 39a58db9a..6c7f79895 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementSearchTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementSearchTest.java @@ -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 unassigned = testdataFactory.createTargets(9, "unassigned"); - final List 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 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 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 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 targDs = testdataFactory.createTargets(100, targetDsDIdPref, - targetDsDIdPref.concat(SPACE_AND_DESCRIPTION), null); - - final String targetDsEIdPref = "targ-E"; - final List 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 unknown = List.of(TargetUpdateStatus.UNKNOWN); - final List pending = List.of(TargetUpdateStatus.PENDING); - final List 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 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 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 both, final List 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 pending, final List 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 pending, final List 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 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 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 pending, final List 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 pending, - final List 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 unknown, final List 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 unknown, - final List 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 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 unknown, final List 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 unknown, - final List 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 unknown, - final List 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 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 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 concat(final List... targets) { - final List result = new ArrayList<>(); - Arrays.asList(targets).forEach(result::addAll); - return result; - } - - private void verifyThat200TargetsHaveTagD(final TargetTag targTagD, final List 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 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 expected) { - final FilterParams filterParams = new FilterParams("%targ-E%", null, Boolean.FALSE, targetType.getId()); - final List 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 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); - } } \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementSecurityTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementSecurityTest.java index 27411918a..d0bfe9ffc 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementSecurityTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementSecurityTest.java @@ -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. */ diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementTest.java index 448bcf3fb..2b29a88ee 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementTest.java @@ -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
@@ -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 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 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.assertThat(targetsLeft).as("Not all undeleted found").doesNotContain(deletedTargets); + Assertions. 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 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 typeATargets = testdataFactory.createTargets(10, "typeATargets", "first description"); final List 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 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 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> specList = buildSpecificationList(filterParams); + return JpaManagementHelper.countBySpec(targetRepository, specList); + } + + private List> buildSpecificationList(final FilterParams filterParams) { + final List> 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 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 targetRoot, final CriteriaBuilder cb, final Boolean selectTargetWithNoTag, final String[] tagNames) { + final SetJoin tags = targetRoot.join(JpaTarget_.tags, JoinType.LEFT); + final Path exp = tags.get(AbstractJpaNamedEntity_.name); + + final List 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) {} +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java index 20f32a13c..06466d21a 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java @@ -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 findFirstModuleByType(final DistributionSetManagement.Create dsCreate, final SoftwareModuleType type) { + protected Optional 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 findByUpdateStatus(final TargetUpdateStatus status, final Pageable pageable) { + return targetManagement.findAll(pageable).stream().filter(target -> status.equals(target.getUpdateStatus())).toList(); + } } \ No newline at end of file diff --git a/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/AbstractRestIntegrationTest.java b/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/AbstractRestIntegrationTest.java index 624a6517b..c5a63a329 100644 --- a/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/AbstractRestIntegrationTest.java +++ b/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/AbstractRestIntegrationTest.java @@ -12,12 +12,16 @@ package org.eclipse.hawkbit.rest; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.eclipse.hawkbit.repository.jpa.JpaRepositoryConfiguration; +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.test.TestConfiguration; import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.jpa.domain.Specification; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; @@ -64,4 +68,8 @@ public abstract class AbstractRestIntegrationTest extends AbstractIntegrationTes protected static String toJson(final Object obj) throws JsonProcessingException { return OBJECT_MAPPER.writeValueAsString(obj); } + + protected static Specification byDistributionSetId(final Long distributionSetId) { + return (root, query, cb) -> cb.equal(root.get(JpaAction_.distributionSet).get(AbstractJpaBaseEntity_.id), distributionSetId); + } }