diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java index ae155ddb8..6d711c19e 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java @@ -114,11 +114,11 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme // enforce the 'max targets per auto assign' quota right here even // if the result of the filter query can vary over time - if (create.getAutoAssignDistributionSetId().isPresent()) { + create.getAutoAssignDistributionSetId().ifPresent(dsId -> { WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement) .validate(create); - assertMaxTargetsQuota(query); - } + assertMaxTargetsQuota(query, create.getName().orElse(null), dsId); + }); }); return targetFilterQueryRepository.save(create.build()); @@ -244,7 +244,8 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme // query is going to change if (targetFilterQuery.getAutoAssignDistributionSet() != null && !query.equals(targetFilterQuery.getQuery())) { - assertMaxTargetsQuota(query); + assertMaxTargetsQuota(query, targetFilterQuery.getName(), + targetFilterQuery.getAutoAssignDistributionSet().getId()); } // set the new query @@ -268,11 +269,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme targetFilterQuery.setConfirmationRequired(false); } else { WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(update); - // we cannot be sure that the quota was enforced at creation time - // because the Target Filter Query REST API does not allow to - // specify an - // auto-assign distribution set when creating a target filter query - assertMaxTargetsQuota(targetFilterQuery.getQuery()); + assertMaxTargetsQuota(targetFilterQuery.getQuery(), targetFilterQuery.getName(), update.getDsId()); final JpaDistributionSet ds = (JpaDistributionSet) distributionSetManagement .getValidAndComplete(update.getDsId()); verifyDistributionSetAndThrowExceptionIfDeleted(ds); @@ -320,15 +317,15 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme try { RSQLUtility.validateRsqlFor(query, TargetFields.class); return true; - } catch (RSQLParserException | RSQLParameterUnsupportedFieldException e) { + } catch (final RSQLParserException | RSQLParameterUnsupportedFieldException e) { LOGGER.debug("The RSQL query '" + query + "' is invalid.", e); return false; } } - private void assertMaxTargetsQuota(final String query) { - QuotaHelper.assertAssignmentQuota(targetManagement.countByRsql(query), - quotaManagement.getMaxTargetsPerAutoAssignment(), Target.class, TargetFilterQuery.class); + private void assertMaxTargetsQuota(final String query, final String filterName, final long dsId) { + QuotaHelper.assertAssignmentQuota(filterName, targetManagement.countByRsqlAndNonDSAndCompatible(dsId, query), + quotaManagement.getMaxTargetsPerAutoAssignment(), Target.class, TargetFilterQuery.class, null); } @Override diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/MgmtUiConfiguration.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/MgmtUiConfiguration.java index 7f60b3df3..777f0dbec 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/MgmtUiConfiguration.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/MgmtUiConfiguration.java @@ -24,6 +24,7 @@ import org.eclipse.hawkbit.ui.error.extractors.EntityNotFoundErrorExtractor; import org.eclipse.hawkbit.ui.error.extractors.IncompatibleTargetTypeErrorExtractor; import org.eclipse.hawkbit.ui.error.extractors.InsufficientPermissionErrorExtractor; import org.eclipse.hawkbit.ui.error.extractors.InvalidDistributionSetErrorExtractor; +import org.eclipse.hawkbit.ui.error.extractors.AssignmentQuotaExceededErrorExtractor; import org.eclipse.hawkbit.ui.error.extractors.UiErrorDetailsExtractor; import org.eclipse.hawkbit.ui.error.extractors.UploadErrorExtractor; import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; @@ -195,6 +196,18 @@ public class MgmtUiConfiguration { return new ArtifactEncryptionErrorExtractor(i18n); } + /** + * UI Assignment Quota exceeded Error details extractor bean. + * + * @param i18n + * VaadinMessageSource + * @return UI Assignment Quota exceeded Error details extractor + */ + @Bean + UiErrorDetailsExtractor assignmentQuotaExceededErrorExtractor(final VaadinMessageSource i18n) { + return new AssignmentQuotaExceededErrorExtractor(i18n); + } + /** * Vaadin4Spring servlet bean. * diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/error/extractors/AssignmentQuotaExceededErrorExtractor.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/error/extractors/AssignmentQuotaExceededErrorExtractor.java new file mode 100644 index 000000000..7e635dba2 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/error/extractors/AssignmentQuotaExceededErrorExtractor.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2022 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.ui.error.extractors; + +import java.util.Optional; + +import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; +import org.eclipse.hawkbit.ui.error.UiErrorDetails; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; + +/** + * UI error details extractor for {@link AssignmentQuotaExceededException}. + */ +public class AssignmentQuotaExceededErrorExtractor extends AbstractSingleUiErrorDetailsExtractor { + private final VaadinMessageSource i18n; + + /** + * Constructor for AssignmentQuotaExceededErrorExtractor. + * + * @param i18n + * Message source used for localization + */ + public AssignmentQuotaExceededErrorExtractor(final VaadinMessageSource i18n) { + this.i18n = i18n; + } + + @Override + protected Optional findDetails(final Throwable error) { + return findExceptionOf(error, AssignmentQuotaExceededException.class).map( + ex -> UiErrorDetails.create(i18n.getMessage("caption.quota.assignment.exceeded"), ex.getMessage())); + } +} diff --git a/hawkbit-ui/src/main/resources/messages.properties b/hawkbit-ui/src/main/resources/messages.properties index 8d563d764..5c0c82c8b 100644 --- a/hawkbit-ui/src/main/resources/messages.properties +++ b/hawkbit-ui/src/main/resources/messages.properties @@ -113,6 +113,7 @@ caption.action.messages = Messages caption.error = Error caption.no.permission = Insufficient Permission caption.entity.missing.error = Entity is missing +caption.quota.assignment.exceeded = Assignment quota exceeded caption.new.softwaremodule.application = Configure New Application caption.new.softwaremodule.os = Configure New OS caption.filter.simple = Simple Filter