diff --git a/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java b/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java index 1816e820d..19c194dfa 100644 --- a/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java +++ b/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java @@ -286,7 +286,7 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi { @Override public void updateMetadata(final Long distributionSetId, final String metadataKey, final MgmtMetadataBodyPut metadata) { - distributionSetManagement.updateMetadata(distributionSetId, metadataKey, metadata.getValue()); + distributionSetManagement.createMetadata(distributionSetId, metadataKey, metadata.getValue()); } @Override 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 3040f589b..4ac74b59b 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 @@ -438,7 +438,7 @@ public class MgmtTargetResource implements MgmtTargetRestApi { @Override public void updateMetadata(final String targetId, final String metadataKey, final MgmtMetadataBodyPut metadata) { - targetManagement.updateMetadata(targetId, metadataKey, metadata.getValue()); + targetManagement.createMetadata(targetId, metadataKey, metadata.getValue()); } @Override diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java index 98de3bde6..170a830ee 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java @@ -54,14 +54,13 @@ import org.springframework.security.access.prepost.PreAuthorize; * Management service for {@link DistributionSet}s. */ public interface DistributionSetManagement - extends RepositoryManagement { + extends RepositoryManagement, MetadataSupport { @Override default String permissionGroup() { return "DISTRIBUTION_SET"; } - /** * Find {@link DistributionSet} based on given ID including (lazy loaded) details, e.g. {@link DistributionSet#getModules()}.
* For performance reasons it is recommended to use {@link #get(long)} if the details are not required. @@ -140,49 +139,6 @@ public interface DistributionSetManagement @PreAuthorize(HAS_UPDATE_REPOSITORY) List unassignTag(@NotEmpty Collection ids, long tagId); - /** - * Creates a map of distribution set meta-data entries. - * - * @param id if the {@link DistributionSet} the meta-data has to be created for - * @param metadata the meta-data entries to create or update - * @throws EntityNotFoundException if given set does not exist - * @throws EntityAlreadyExistsException in case one of the meta-data entry already exists for the specific key - * @throws AssignmentQuotaExceededException if the maximum number of meta-data entries is exceeded for the addressed {@link DistributionSet} - */ - @PreAuthorize(HAS_UPDATE_REPOSITORY) - void createMetadata(long id, @NotEmpty Map metadata); - - /** - * Finds all meta-data by the given distribution set id. - * - * @param id the distribution set id to retrieve the meta-data from - * @return a paged result of all meta-data entries for a given distribution set id - * @throws EntityNotFoundException if distribution set with given ID does not exist - */ - @PreAuthorize(HAS_READ_REPOSITORY) - Map getMetadata(long id); - - /** - * Updates a distribution set meta-data values by adding them. - * - * @param id {@link DistributionSet} of the meta-data entry to be updated - * @param key meta data-entry key to be updated - * @param value meta data-entry to be new value - * @throws EntityNotFoundException in case the meta-data entry does not exist and cannot be updated - */ - @PreAuthorize(HAS_UPDATE_REPOSITORY) - void updateMetadata(long id, @NotNull String key, @NotNull String value); - - /** - * Deletes a distribution set meta-data entry. - * - * @param id where meta-data has to be deleted - * @param key of the meta-data element - * @throws EntityNotFoundException if given set does not exist - */ - @PreAuthorize(HAS_UPDATE_REPOSITORY) - void deleteMetadata(long id, @NotEmpty String key); - /** * Locks a distribution set. From then on its functional properties could not be changed, and it could be assigned to targets * diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/MetadataSupport.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/MetadataSupport.java new file mode 100644 index 000000000..4145a9754 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/MetadataSupport.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * 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.Map; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions; +import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; +import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; +import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.springframework.security.access.prepost.PreAuthorize; + +/** + * Represents interface for managing meta-data of entities. The keys are always Strings, while the values are generic + * + * @param the type of the meta-data value + */ +@SuppressWarnings("java:S119") // java:S119 - better self explainable +public interface MetadataSupport { + + /** + * Creates or updates a meta-data value. + * + * @param id the entity id which meta-data has to be updated + * @param key the key of the meta-data entry to be updated + * @param value the meta-data value to be updated + * @throws EntityNotFoundException in case the meta-data entry does not exist and cannot be updated + */ + @PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY) + void createMetadata(@NotNull Long id, @NotEmpty String key, @NotNull @Valid MV value); + + /** + * Creates a list of entity meta-data entries. + * + * @param id the entity id which meta-data has to be created + * @param metadata the meta-data entries to create + * @throws EntityAlreadyExistsException in case one of the meta-data entry already exists for the specific key + * @throws EntityNotFoundException if entity with given ID does not exist + * @throws AssignmentQuotaExceededException if the maximum number of meta-data entries is exceeded for the addressed entity + */ + @PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY) + void createMetadata(@NotNull Long id, @NotEmpty @Valid Map metadata); + + /** + * Finds all meta-data by the given entity id and key. + * + * @param id the entity id to retrieve the meta-data from + * @param key the meta-data key to retrieve + * @return a paged result of all meta-data entries for a given entity id + * @throws EntityNotFoundException if entity with given ID does not exist ot the + */ + @PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY) + MV getMetadata(@NotNull Long id, @NotEmpty String key); + + /** + * Finds all meta-data by the given entity id. + * + * @param id the entity id to retrieve the meta-data from + * @return a paged result of all meta-data entries for a given entity id + * @throws EntityNotFoundException if entity with given ID does not exist + */ + @PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY) + Map getMetadata(@NotNull Long id); + + /** + * Deletes a entity meta-data entry. + * + * @param id where meta-data has to be deleted + * @param key of the meta-data element + * @throws EntityNotFoundException if entity with given ID does not exist or the key is not found + */ + @PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY) + void deleteMetadata(@NotNull Long id, @NotEmpty String key); +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/PermissionSupport.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/PermissionSupport.java new file mode 100644 index 000000000..39ef88280 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/PermissionSupport.java @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * 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; + +public interface PermissionSupport { + + String permissionGroup(); +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryManagement.java index 7e02a6bcb..2653ce85f 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryManagement.java @@ -36,7 +36,7 @@ import org.springframework.security.access.prepost.PreAuthorize; * @param type of the create request * @param type of the update request */ -public interface RepositoryManagement> { +public interface RepositoryManagement> extends PermissionSupport { /** * Creates new {@link BaseEntity}. @@ -153,6 +153,7 @@ public interface RepositoryManagement ids); + @Override default String permissionGroup() { return "REPOSITORY"; } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java index a08f9ad71..9165af435 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java @@ -42,66 +42,13 @@ import org.springframework.security.access.prepost.PreAuthorize; * Service for managing {@link SoftwareModule}s. */ public interface SoftwareModuleManagement - extends RepositoryManagement { + extends RepositoryManagement, MetadataSupport { @Override default String permissionGroup() { return "SOFTWARE_MODULE"; } - /** - * Creates or updates a distribution set meta-data value. - * - * @param id the software module id which meta-data has to be updated - * @param key the key of the meta-data entry to be updated - * @param value the meta-data value to be updated - * @throws EntityNotFoundException in case the meta-data entry does not exist and cannot be updated - */ - @PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY) - void createMetadata(@NotNull Long id, @NotEmpty String key, @NotNull @Valid MetadataValue value); - - /** - * Creates a list of software module meta-data entries. - * - * @param metadata the meta-data entries to create - * @throws EntityAlreadyExistsException in case one of the meta-data entry already exists for the specific key - * @throws EntityNotFoundException if software module with given ID does not exist - * @throws AssignmentQuotaExceededException if the maximum number of meta-data entries is exceeded for the addressed {@link SoftwareModule} - */ - @PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY) - void createMetadata(@NotNull Long id, @NotEmpty @Valid Map metadata); - - /** - * Finds all meta-data by the given software module id and key. - * - * @param id the software module id to retrieve the meta-data from - * @param key the meta-data key to retrieve - * @return a paged result of all meta-data entries for a given software module id - * @throws EntityNotFoundException if software module with given ID does not exist ot the - */ - @PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY) - MetadataValue getMetadata(@NotNull Long id, @NotEmpty String key); - - /** - * Finds all meta-data by the given software module id. - * - * @param id the software module id to retrieve the meta-data from - * @return a paged result of all meta-data entries for a given software module id - * @throws EntityNotFoundException if software module with given ID does not exist - */ - @PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY) - Map getMetadata(@NotNull Long id); - - /** - * Deletes a software module meta-data entry. - * - * @param id where meta-data has to be deleted - * @param key of the meta-data element - * @throws EntityNotFoundException if software module with given ID does not exist or the key is not found - */ - @PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY) - void deleteMetadata(@NotNull Long id, @NotEmpty String key); - @PreAuthorize(SpringEvalExpressions.IS_SYSTEM_CODE) Map> findMetaDataBySoftwareModuleIdsAndTargetVisible(Collection ids); 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 dd6d1767f..6a8d6de4e 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 @@ -15,12 +15,10 @@ import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_AU import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_AUTH_CREATE_TARGET; import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_AUTH_DELETE_TARGET; import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_AUTH_PREFIX; -import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_READ_REPOSITORY; import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET; import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_AUTH_READ_TARGET; import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ; import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_AUTH_SUFFIX; -import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_UPDATE_REPOSITORY; import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET; import java.util.Collection; @@ -36,6 +34,7 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import org.eclipse.hawkbit.im.authentication.SpPermission; +import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions; import org.eclipse.hawkbit.repository.builder.TargetCreate; import org.eclipse.hawkbit.repository.builder.TargetUpdate; import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; @@ -60,7 +59,7 @@ import org.springframework.security.access.prepost.PreAuthorize; /** * Management service for {@link Target}s. */ -public interface TargetManagement { +public interface TargetManagement extends PermissionSupport { String DETAILS_BASE = "base"; String DETAILS_AUTO_CONFIRMATION_STATUS = "autoConfirmationStatus"; @@ -72,6 +71,12 @@ public interface TargetManagement { HAS_AUTH_AND + HAS_AUTH_PREFIX + SpPermission.READ_TARGET + HAS_AUTH_SUFFIX + BRACKET_CLOSE; + + @Override + default String permissionGroup() { + return "TARGET"; + } + /** * Counts number of targets with the given distribution set assigned. * @@ -744,45 +749,56 @@ public interface TargetManagement { Page findByControllerAttributesRequested(@NotNull Pageable pageable); /** - * Creates a list of target meta-data entries. + * Creates or updates a meta-data value. * - * @param controllerId {@link Target} controller id the meta-data has to be created for - * @param metadata the meta-data entries to create or update - * @throws EntityNotFoundException if given target does not exist - * @throws EntityAlreadyExistsException in case one of the metad-ata entry already exists for the specific key - * @throws AssignmentQuotaExceededException if the maximum number of meta-data entries is exceeded for the addressed {@link Target} - */ - @PreAuthorize(HAS_UPDATE_REPOSITORY) - void createMetadata(@NotEmpty String controllerId, @NotEmpty Map metadata); - - /** - * Finds a single target meta-data by its id. - * - * @param controllerId of the {@link Target} - * @return the found target meta-data - * @throws EntityNotFoundException if target with given ID does not exist - */ - @PreAuthorize(HAS_READ_REPOSITORY) - Map getMetadata(@NotEmpty String controllerId); - - /** - * Updates a target meta-data value if corresponding entry exists. - * - * @param controllerId {@link Target} controller id of the meta-data entry to be updated - * @param key meta data-entry key to be updated - * @param value meta data-entry to be new value + * @param controllerId the entity id which meta-data has to be updated + * @param key the key of the meta-data entry to be updated + * @param value the meta-data value to be updated * @throws EntityNotFoundException in case the meta-data entry does not exist and cannot be updated */ - @PreAuthorize(HAS_UPDATE_REPOSITORY) - void updateMetadata(@NotEmpty String controllerId, @NotNull String key, @NotNull String value); + @PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY) + void createMetadata(@NotNull String controllerId, @NotEmpty String key, @NotNull @Valid String value); /** - * Deletes a target meta data entry. + * Creates a list of entity meta-data entries. + * + * @param controllerId the entity id which meta-data has to be created + * @param metadata the meta-data entries to create + * @throws EntityAlreadyExistsException in case one of the meta-data entry already exists for the specific key + * @throws EntityNotFoundException if entity with given ID does not exist + * @throws AssignmentQuotaExceededException if the maximum number of meta-data entries is exceeded for the addressed entity + */ + @PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY) + void createMetadata(@NotNull String controllerId, @NotEmpty @Valid Map metadata); + + /** + * Finds all meta-data by the given entity id and key. + * + * @param controllerId the entity id to retrieve the meta-data from + * @param key the meta-data key to retrieve + * @return a paged result of all meta-data entries for a given entity id + * @throws EntityNotFoundException if entity with given ID does not exist ot the + */ + @PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY) + String getMetadata(@NotNull String controllerId, @NotEmpty String key); + + /** + * Finds all meta-data by the given entity id. + * + * @param controllerId the entity id to retrieve the meta-data from + * @return a paged result of all meta-data entries for a given entity id + * @throws EntityNotFoundException if entity with given ID does not exist + */ + @PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY) + Map getMetadata(@NotNull String controllerId); + + /** + * Deletes a entity meta-data entry. * * @param controllerId where meta-data has to be deleted - * @param key of the meta data element - * @throws EntityNotFoundException if given target does not exist + * @param key of the meta-data element + * @throws EntityNotFoundException if entity with given ID does not exist or the key is not found */ - @PreAuthorize(HAS_UPDATE_REPOSITORY) - void deleteMetadata(@NotEmpty String controllerId, @NotEmpty String key); + @PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY) + void deleteMetadata(@NotNull String controllerId, @NotEmpty String key); } \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/RepositoryConfiguration.java b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/RepositoryConfiguration.java index d9eb5a6f6..e08d4a81e 100644 --- a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/RepositoryConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/RepositoryConfiguration.java @@ -64,9 +64,9 @@ public class RepositoryConfiguration { public boolean hasPermission(final Authentication authentication, final Object targetDomainObject, final Object permission) { if (targetDomainObject instanceof MethodSecurityExpressionOperations root) { final String neededPermission = - permission + "_" + (root.getThis() instanceof RepositoryManagement repositoryManagement - ? repositoryManagement.permissionGroup() - : "REPOSITORY"); // TODO - should not fall back here - all using parmissions should extend repository management interface + permission + "_" + (root.getThis() instanceof PermissionSupport permissionSupport + ? permissionSupport.permissionGroup() + : "REPOSITORY"); // TODO - should not fall back here - all using permissions should extend repository management interface final boolean hasPermission = roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities()).stream() .map(GrantedAuthority::getAuthority) .anyMatch(authority -> authority.equals(neededPermission)); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/AbstractJpaRepositoryManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/AbstractJpaRepositoryManagement.java index 3c42ea421..6a70db0ec 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/AbstractJpaRepositoryManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/AbstractJpaRepositoryManagement.java @@ -80,11 +80,12 @@ abstract class AbstractJpaRepositoryManagement entityConstructor; + private final Supplier jpaEntityCreator; protected AbstractJpaRepositoryManagement(final R jpaRepository, final EntityManager entityManager) { this.jpaRepository = jpaRepository; this.entityManager = entityManager; + final Constructor entityConstructor; try { entityConstructor = jpaRepository.getDomainClass().getConstructor(); // test if method works, if fine - it shall never fail when called later @@ -92,6 +93,13 @@ abstract class AbstractJpaRepositoryManagement { + try { + return entityConstructor.newInstance(); + } catch (final InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new IllegalStateException("Must NEVER happen!", e); + } + }; } @Override @@ -156,7 +164,7 @@ abstract class AbstractJpaRepositoryManagement update, final T entity) { - // update getId has not setter in target JPA entity but shall have getter and the value shall be the same + // update getId has no setter in target JPA entity but shall have getter and the value shall be the same // otherwise the Utils will throw an exception that there is no counterpart setter for getId if (ObjectCopyUtil.copy(update, entity, false, this::attach)) { return jpaRepository.save(entity); @@ -289,12 +297,7 @@ abstract class AbstractJpaRepositoryManagement, C, U extends Identifiable, R extends BaseEntityRepository, A extends Enum & RsqlQueryField, MV, MVI extends MV> + extends AbstractJpaRepositoryManagement implements MetadataSupport { + + private final Supplier metadataValueCreator; + private final boolean useCopy; + + // java:S3011 - intentionally to provide option to forbid constructors + // java:S1141 - better visible this way + @SuppressWarnings({"unchecked", "java:S3011", "java:S1141"}) + protected AbstractJpaRepositoryWithMetadataManagement(final R repository, final EntityManager entityManager) { + super(repository, entityManager); + try { + final Class metadataValueType = (Class) ((ParameterizedType) jpaRepository.getDomainClass().getMethod("getMetadata") + .getGenericReturnType()) + .getActualTypeArguments()[1]; + final Constructor constructor; + try { + constructor = metadataValueType.getDeclaredConstructor(); + constructor.setAccessible(true); + constructor.newInstance(); + } catch (final NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new IllegalStateException("Metadata class " + jpaRepository.getDomainClass() + " shall have no-args constructor", e); + } + metadataValueCreator = () -> { + try { + return constructor.newInstance(); + } catch (final InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new IllegalStateException("Must NEVER happen!", e); + } + }; + useCopy = !String.class.equals(metadataValueType) && !metadataValueType.isPrimitive(); + } catch (final NoSuchMethodException | SecurityException e) { + throw new IllegalStateException(e); + } + } + + @Override + @Transactional + @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY)) + public void createMetadata(final Long id, final String key, final MV value) { + final T jpaEntity = getValid(id); + final Map metadataValueMap = jpaEntity.getMetadata(); + final MVI existingValue = metadataValueMap.get(key); + if (existingValue == null) { + assertMetadataQuota(metadataValueMap.size() + 1L); + } + if (setMetadataValue(key, value, existingValue, metadataValueMap)) { + jpaRepository.save(jpaEntity); + } + } + + @Override + @Transactional + @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY)) + public void createMetadata(final Long id, final Map metadata) { + final T jpaEntity = getValid(id); + final Map metadataValueMap = jpaEntity.getMetadata(); + assertMetadataQuota(metadata.keySet().stream().filter(key -> !metadataValueMap.containsKey(key)).count() + metadataValueMap.size()); + final AtomicBoolean changed = new AtomicBoolean(false); + metadata.forEach((key, value) -> { + if (setMetadataValue(key, value, metadataValueMap.get(key), metadataValueMap)) { + changed.set(true); + } + }); + if (changed.get()) { + jpaRepository.save(jpaEntity); + } + } + + @Override + public MV getMetadata(final Long id, final String key) { + final T jpaEntity = getValid(id); + final MV metadataValue = jpaEntity.getMetadata().get(key); + if (metadataValue == null) { + throw new EntityNotFoundException("Metadata", jpaRepository.getManagementClass().getSimpleName() + ":" + id + ":" + key); + } else { + return metadataValue; + } + } + + @Override + public Map getMetadata(final Long id) { + return jpaRepository + .findById(id) + .map(T::getMetadata) + .map(metadata -> metadata.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + e -> (MV) e.getValue()))) + .orElseThrow(() -> new EntityNotFoundException(jpaRepository.getManagementClass(), id)); + } + + @Override + @Transactional + @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY)) + public void deleteMetadata(final Long id, final String key) { + final T jpaEntity = getValid(id); + final Map metadataValueMap = jpaEntity.getMetadata(); + if (!metadataValueMap.containsKey(key)) { + throw new EntityNotFoundException("Metadata", jpaRepository.getManagementClass().getSimpleName() + ":" + id + ":" + key); + } + metadataValueMap.remove(key); + jpaRepository.save(jpaEntity); + } + + protected abstract void assertMetadataQuota(final long requested); + + private T getValid(final Long id) { + final T jpaEntity = jpaRepository + .findById(id) + .orElseThrow(() -> new EntityNotFoundException(jpaRepository.getManagementClass(), id)); + if (!jpaEntity.isValid()) { + throw new InvalidDistributionSetException(jpaRepository.getManagementClass().getSimpleName() + " " + id + " is invalid"); + } + return jpaEntity; + } + + @SuppressWarnings("unchecked") + private boolean setMetadataValue(final String key, final MV newValue, final MVI existingValue, final Map metadataValueMap) { + if (useCopy) { + final MVI jpaMetadataValue = existingValue == null ? metadataValueCreator.get() : existingValue; + if (ObjectCopyUtil.copy(newValue, jpaMetadataValue, true, UnaryOperator.identity())) { + metadataValueMap.put(key, jpaMetadataValue); + return true; + } else { + return false; + } + } else { + if (Objects.equals(newValue, existingValue)) { + return false; + } else { + metadataValueMap.put(key, (MVI)newValue); + return true; + } + } + } +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java index 65703b701..22c98379e 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java @@ -90,7 +90,7 @@ import org.springframework.util.ObjectUtils; @Service @ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "distribution-set-management" }, matchIfMissing = true) public class JpaDistributionSetManagement - extends AbstractJpaRepositoryManagement + extends AbstractJpaRepositoryWithMetadataManagement implements DistributionSetManagement { private final DistributionSetTagManagement distributionSetTagManagement; @@ -253,63 +253,6 @@ public class JpaDistributionSetManagement }); } - @Override - @Transactional - @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY)) - public void createMetadata(final long id, final Map md) { - final JpaDistributionSet distributionSet = getValid0(id); - - // get the modifiable metadata map - final Map metadata = distributionSet.getMetadata(); - md.keySet().forEach(key -> { - if (metadata.containsKey(key)) { - throw new EntityAlreadyExistsException("Metadata entry with key '" + key + "' already exists"); - } - }); - metadata.putAll(md); - - assertMetaDataQuota(id, metadata.size()); - - jpaRepository.save(distributionSet); - } - - @Override - public Map getMetadata(final long id) { - assertDistributionSetExists(id); - return getMap(id, JpaDistributionSet_.metadata); - } - - @Override - @Transactional - @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY)) - public void updateMetadata(final long id, final String key, final String value) { - final JpaDistributionSet distributionSet = getValid0(id); - - // get the modifiable metadata map - final Map metadata = distributionSet.getMetadata(); - if (!metadata.containsKey(key)) { - throw new EntityNotFoundException("DistributionSet metadata", id + ":" + key); - } - metadata.put(key, value); - - jpaRepository.save(distributionSet); - } - - @Override - @Transactional - @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY)) - public void deleteMetadata(final long id, final String key) { - final JpaDistributionSet distributionSet = getValid0(id); - - // get the modifiable metadata map - final Map metadata = distributionSet.getMetadata(); - if (metadata.remove(key) == null) { - throw new EntityNotFoundException("DistributionSet metadata", id + ":" + key); - } - - jpaRepository.save(distributionSet); - } - @Override @Transactional @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY)) @@ -450,6 +393,17 @@ public class JpaDistributionSetManagement return jpaRepository.countAutoAssignmentsForDistributionSet(id); } + /** + * Asserts the meta-data quota for the software module with the given ID. + * + * @param requested Number of meta-data entries to be created. + */ + @Override + protected void assertMetadataQuota(final long requested) { + final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerDistributionSet(); + QuotaHelper.assertAssignmentQuota(requested, maxMetaData, String.class, DistributionSet.class); + } + // check if it shall implicitly lock a distribution set boolean isImplicitLockApplicable(final DistributionSet distributionSet) { final JpaDistributionSet jpaDistributionSet = (JpaDistributionSet) distributionSet; @@ -584,11 +538,6 @@ public class JpaDistributionSetManagement return specifications; } - private void assertMetaDataQuota(final Long dsId, final int requested) { - final int limit = quotaManagement.getMaxMetaDataEntriesPerDistributionSet(); - QuotaHelper.assertAssignmentQuota(dsId, requested, limit, "Metadata", DistributionSet.class.getSimpleName(), null); - } - private void assertSoftwareModuleQuota(final Long id, final int requested) { QuotaHelper.assertAssignmentQuota(id, requested, quotaManagement.getMaxSoftwareModulesPerDistributionSet(), SoftwareModule.class, DistributionSet.class, softwareModuleRepository::countByAssignedToId); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java index be36dca94..a48fe228e 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java @@ -20,8 +20,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.UnaryOperator; import java.util.stream.Collectors; import jakarta.persistence.EntityManager; @@ -48,7 +46,6 @@ import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValue; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; -import org.eclipse.hawkbit.utils.ObjectCopyUtil; import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty; import org.springframework.dao.ConcurrencyFailureException; import org.springframework.data.domain.Page; @@ -64,12 +61,9 @@ import org.springframework.util.ObjectUtils; @Service @ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "software-module-management" }, matchIfMissing = true) public class JpaSoftwareModuleManagement - extends - AbstractJpaRepositoryManagement + extends AbstractJpaRepositoryWithMetadataManagement implements SoftwareModuleManagement { - protected static final String SOFTWARE_MODULE_METADATA = "SoftwareModuleMetadata"; - private final DistributionSetRepository distributionSetRepository; private final SoftwareModuleTypeRepository softwareModuleTypeRepository; private final ArtifactManagement artifactManagement; @@ -164,88 +158,6 @@ public class JpaSoftwareModuleManagement .toList(); } - @Override - @Transactional - @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY)) - public void createMetadata(final Long id, final String key, final MetadataValue value) { - final JpaSoftwareModule softwareModule = jpaRepository - .findById(id) - .orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)); - final Map metadataValueMap = softwareModule.getMetadata(); - final JpaMetadataValue existingValue = metadataValueMap.get(key); - if (existingValue == null) { - assertMetadataQuota(metadataValueMap.size() + 1L); - } - final JpaMetadataValue jpaMetadataValue = existingValue == null ? - new JpaMetadataValue() : existingValue; - if (ObjectCopyUtil.copy(value, jpaMetadataValue, true, UnaryOperator.identity())) { - metadataValueMap.put(key, jpaMetadataValue); - jpaRepository.save(softwareModule); - } - } - - @Override - @Transactional - @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY)) - public void createMetadata(final Long id, final Map metadata) { - final JpaSoftwareModule softwareModule = jpaRepository - .findById(id) - .orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)); - final Map metadataValueMap = softwareModule.getMetadata(); - assertMetadataQuota(metadata.keySet().stream().filter(key -> !metadataValueMap.containsKey(key)).count() + metadataValueMap.size()); - final AtomicBoolean changed = new AtomicBoolean(false); - metadata.forEach((key, value) -> { - final JpaMetadataValue jpaMetadataValue = metadataValueMap.getOrDefault(key, new JpaMetadataValue()); - if (ObjectCopyUtil.copy(value, jpaMetadataValue, true, UnaryOperator.identity())) { - metadataValueMap.put(key, jpaMetadataValue); - changed.set(true); - } - }); - if (changed.get()) { - jpaRepository.save(softwareModule); - } - } - - @Override - public MetadataValue getMetadata(final Long id, final String key) { - final JpaSoftwareModule softwareModule = jpaRepository - .findById(id) - .orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)); - final MetadataValue metadataValue = softwareModule.getMetadata().get(key); - if (metadataValue == null) { - throw new EntityNotFoundException(SOFTWARE_MODULE_METADATA, id + ":" + key); - } else { - return metadataValue; - } - } - - @Override - public Map getMetadata(final Long id) { - return jpaRepository - .findById(id) - .map(JpaSoftwareModule::getMetadata) - .map(metadata -> metadata.entrySet().stream() - .collect(Collectors.toMap( - Map.Entry::getKey, - e -> (MetadataValue)e.getValue()))) - .orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)); - } - - @Override - @Transactional - @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY)) - public void deleteMetadata(final Long id, final String key) { - final JpaSoftwareModule softwareModule = jpaRepository - .findById(id) - .orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)); - final Map metadataValueMap = softwareModule.getMetadata(); - if (!metadataValueMap.containsKey(key)) { - throw new EntityNotFoundException(SOFTWARE_MODULE_METADATA, id + ":" + key); - } - metadataValueMap.remove(key); - jpaRepository.save(softwareModule); - } - // called only with 'system code' access, so no need to check access control @Override public Map> findMetaDataBySoftwareModuleIdsAndTargetVisible(final Collection ids) { @@ -349,6 +261,17 @@ public class JpaSoftwareModuleManagement return jpaRepository.count(SoftwareModuleSpecification.byAssignedToDs(distributionSetId)); } + /** + * Asserts the meta-data quota for the software module with the given ID. + * + * @param requested Number of meta-data entries to be created. + */ + @Override + protected void assertMetadataQuota(final long requested) { + final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule(); + QuotaHelper.assertAssignmentQuota(requested, maxMetaData, SoftwareModule.MetadataValueCreate.class, SoftwareModule.class); + } + private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) { jpaRepository.getAccessController().ifPresent(accessController -> accessController.assertOperationAllowed(AccessController.Operation.DELETE, swModule)); @@ -364,16 +287,6 @@ public class JpaSoftwareModuleManagement smFilterNameAndVersionEntries[1]); } - /** - * Asserts the meta-data quota for the software module with the given ID. - * - * @param requested Number of meta-data entries to be created. - */ - private void assertMetadataQuota(final long requested) { - final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule(); - QuotaHelper.assertAssignmentQuota(requested, maxMetaData, SoftwareModule.MetadataValueCreate.class, SoftwareModule.class); - } - private void assertSoftwareModuleTypeExists(final Long typeId) { if (!softwareModuleTypeRepository.existsById(typeId)) { throw new EntityNotFoundException(SoftwareModuleType.class, typeId); 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 30a359a87..a70b80675 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 @@ -711,6 +711,23 @@ public class JpaTargetManagement implements TargetManagement { targetRepository, List.of(TargetSpecifications.hasRequestControllerAttributesTrue()), pageable); } + @Override + @Transactional + @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, + backoff = @Backoff(delay = Constants.TX_RT_DELAY)) + public void createMetadata(final String controllerId, final String key, final String value) { + final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId); + + // get the modifiable metadata map + final Map metadata = target.getMetadata(); + if (!metadata.containsKey(key)) { + throw new EntityNotFoundException("Target metadata", controllerId + ":" + key); + } + metadata.put(key, value); + + targetRepository.save(target); + } + @Override @Transactional @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, @@ -737,20 +754,8 @@ public class JpaTargetManagement implements TargetManagement { } @Override - @Transactional - @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, - backoff = @Backoff(delay = Constants.TX_RT_DELAY)) - public void updateMetadata(final String controllerId, final String key, final String value) { - final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId); - - // get the modifiable metadata map - final Map metadata = target.getMetadata(); - if (!metadata.containsKey(key)) { - throw new EntityNotFoundException("Target metadata", controllerId + ":" + key); - } - metadata.put(key, value); - - targetRepository.save(target); + public String getMetadata(final String controllerId, final String key) { + return getMap(controllerId, JpaTarget_.metadata).get(key); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java index d5a6d1b12..9bd38dec9 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java @@ -70,7 +70,9 @@ import org.springframework.core.annotation.Order; attributeNodes = { @NamedAttributeNode("modules"), @NamedAttributeNode("tags"), @NamedAttributeNode("type") }) // exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities @SuppressWarnings("squid:S2160") -public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implements DistributionSet, EventAwareEntity { +public class JpaDistributionSet + extends AbstractJpaNamedVersionedEntity + implements DistributionSet, WithMetadata, EventAwareEntity { @Serial private static final long serialVersionUID = 1L; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java index 8658fefcc..5f86ab1c9 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java @@ -51,6 +51,7 @@ import org.eclipse.hawkbit.repository.exception.LockedException; import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.SoftwareModule; +import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValue; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; /** @@ -70,7 +71,9 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType; @NamedEntityGraph(name = "SoftwareModule.artifacts", attributeNodes = { @NamedAttributeNode("artifacts") }) // exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities @SuppressWarnings("squid:S2160") -public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implements SoftwareModule, EventAwareEntity { +public class JpaSoftwareModule + extends AbstractJpaNamedVersionedEntity + implements SoftwareModule, WithMetadata, EventAwareEntity { @Serial private static final long serialVersionUID = 1L; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/WithMetadata.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/WithMetadata.java new file mode 100644 index 000000000..0e149f5db --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/WithMetadata.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * 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.model; + +import java.util.Map; + +/** + * Interface for entities that support metadata. + * @param metadata value type + * @param metadata value implementation type + */ +@SuppressWarnings("java:S119") // java:S119 - better self explainable +public interface WithMetadata { + + Map getMetadata(); + + // return if the entity is valid for update metadata + default boolean isValid() { + return true; + } +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetAccessControllerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetAccessControllerTest.java index 95c67d447..894b0113a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetAccessControllerTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetAccessControllerTest.java @@ -160,11 +160,11 @@ class DistributionSetAccessControllerTest extends AbstractJpaIntegrationTest { // verify distributionSetManagement#updateMetaData final String newValue = "newValue"; - distributionSetManagement.updateMetadata(permitted.getId(), mdPresetKey, newValue); - assertThatThrownBy(() -> distributionSetManagement.updateMetadata(readOnlyId, mdPresetKey, newValue)) + distributionSetManagement.createMetadata(permitted.getId(), mdPresetKey, newValue); + assertThatThrownBy(() -> distributionSetManagement.createMetadata(readOnlyId, mdPresetKey, newValue)) .as("Distribution set not allowed to me modified.") .isInstanceOf(InsufficientPermissionException.class); - assertThatThrownBy(() -> distributionSetManagement.updateMetadata(hiddenId, mdPresetKey, newValue)) + assertThatThrownBy(() -> distributionSetManagement.createMetadata(hiddenId, mdPresetKey, newValue)) .as("Distribution set should not be visible.") .isInstanceOf(EntityNotFoundException.class); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetManagementSecurityTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetManagementSecurityTest.java index c5aaa66be..986a05f8d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetManagementSecurityTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetManagementSecurityTest.java @@ -74,7 +74,7 @@ class DistributionSetManagementSecurityTest * Tests ManagementAPI PreAuthorized method with correct and insufficient permissions. */ @Test - void createMetadataPermissionsCheck() { + void createMetadataMapPermissionsCheck() { assertPermissions( () -> { distributionSetManagement.createMetadata(1L, Map.of("key", "value")); @@ -95,9 +95,9 @@ class DistributionSetManagementSecurityTest * Tests ManagementAPI PreAuthorized method with correct and insufficient permissions. */ @Test - void updateMetadataPermissionsCheck() { + void createMetadataPermissionsCheck() { assertPermissions(() -> { - distributionSetManagement.updateMetadata(1L, "key", "value"); + distributionSetManagement.createMetadata(1L, "key", "value"); return null; }, List.of(SpPermission.UPDATE_REPOSITORY)); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetManagementTest.java index bf020cf66..27696a086 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetManagementTest.java @@ -156,8 +156,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest { () -> distributionSetManagement.update(DistributionSetManagement.Update.builder().id(NOT_EXIST_IDL).build()), "DistributionSet"); - verifyThrownExceptionBy(() -> distributionSetManagement.updateMetadata(NOT_EXIST_IDL, "xxx", "xxx"), "DistributionSet"); - verifyThrownExceptionBy(() -> distributionSetManagement.updateMetadata(set.getId(), NOT_EXIST_ID, "xxx"), "DistributionSet"); + verifyThrownExceptionBy(() -> distributionSetManagement.createMetadata(NOT_EXIST_IDL, "xxx", "xxx"), "DistributionSet"); verifyThrownExceptionBy(() -> distributionSetManagement.getOrElseThrowException(NOT_EXIST_IDL), "DistributionSet"); @@ -240,19 +239,6 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest { }); } - /** - * Checks that metadata for a distribution set can be created. - */ - @Test - void createMetadata() { - final String knownKey = "dsMetaKnownKey"; - final String knownValue = "dsMetaKnownValue"; - - final DistributionSet ds = testdataFactory.createDistributionSet("testDs"); - - insertMetadata(knownKey, knownValue, ds); // and validate - } - /** * Verifies the enforcement of the metadata quota per distribution set. */ @@ -520,7 +506,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest { */ @Test @WithUser(allSpPermissions = true) - void updateMetadata() { + void createMetadata() { final String knownKey = "myKnownKey"; final String knownValue = "myKnownValue"; final String knownUpdateValue = "myNewUpdatedValue"; @@ -539,7 +525,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest { waitNextMillis(); // update the DS metadata - distributionSetManagement.updateMetadata(ds.getId(), knownKey, knownUpdateValue); + distributionSetManagement.createMetadata(ds.getId(), knownKey, knownUpdateValue); // we are updating the sw metadata so also modifying the base software // module so opt lock revision must be three final DistributionSet reloadedDS = getOrThrow(distributionSetManagement.get(ds.getId())); @@ -944,7 +930,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest { // assert that an existing metadata can not be updated assertThatExceptionOfType(InvalidDistributionSetException.class) .as("Invalid distributionSet should throw an exception") - .isThrownBy(() -> distributionSetManagement.updateMetadata(dsId, knownKey1, knownUpdateValue)); + .isThrownBy(() -> distributionSetManagement.createMetadata(dsId, knownKey1, knownUpdateValue)); } /** 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 2c5c8887a..63c3b51ad 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 @@ -500,13 +500,13 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest { * Tests ManagementAPI PreAuthorized method with correct and insufficient permissions. */ @Test - void createMetadataPermissionsCheck() { + void createMetadataMapPermissionsCheck() { assertPermissions( () -> { targetManagement.createMetadata("controllerId", Map.of("key", "value")); return null; }, - List.of(SpPermission.UPDATE_REPOSITORY)); + List.of(SpPermission.UPDATE_TARGET)); } /** @@ -514,20 +514,20 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest { */ @Test void getMetadataPermissionsCheck() { - assertPermissions(() -> targetManagement.getMetadata("controllerId"), List.of(SpPermission.READ_REPOSITORY)); + assertPermissions(() -> targetManagement.getMetadata("controllerId"), List.of(SpPermission.READ_TARGET)); } /** * Tests ManagementAPI PreAuthorized method with correct and insufficient permissions. */ @Test - @WithUser(principal = "user", authorities = { SpPermission.UPDATE_REPOSITORY }) - void updateMetadataPermissionsCheck() { + @WithUser(principal = "user", authorities = { SpPermission.UPDATE_TARGET }) + void createMetadataPermissionsCheck() { assertPermissions(() -> { - targetManagement.updateMetadata("controllerId", "key", "value"); + targetManagement.createMetadata("controllerId", "key", "value"); return null; }, - List.of(SpPermission.UPDATE_REPOSITORY)); + List.of(SpPermission.UPDATE_TARGET)); } /** @@ -538,6 +538,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest { assertPermissions(() -> { targetManagement.deleteMetadata("controllerId", "key"); return null; - }, List.of(SpPermission.UPDATE_REPOSITORY)); + }, List.of(SpPermission.UPDATE_TARGET)); } } \ No newline at end of file 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 754312b5b..836f2f595 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 @@ -154,7 +154,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { verifyThrownExceptionBy(() -> targetManagement.deleteMetadata(NOT_EXIST_ID, "xxx"), "Target"); verifyThrownExceptionBy(() -> targetManagement.deleteMetadata(target.getControllerId(), NOT_EXIST_ID), "Target"); verifyThrownExceptionBy(() -> targetManagement.getMetadata(NOT_EXIST_ID).get("xxx"), "Target"); - verifyThrownExceptionBy(() -> targetManagement.updateMetadata(NOT_EXIST_ID, "xxx", "xxx"), "Target"); + verifyThrownExceptionBy(() -> targetManagement.createMetadata(NOT_EXIST_ID, "xxx", "xxx"), "Target"); } /** @@ -773,17 +773,6 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { assertThat(target.isRequestControllerAttributes()).isFalse(); assertThat(targetManagement.findByControllerAttributesRequested(PAGE).getContent()).contains(updated); assertThat(targetManagement.isControllerAttributesRequested(knownControllerId)).isTrue(); - - } - - /** - * Checks that metadata for a target can be created. - */ - @Test - void createMetadata() { - insertMetadata( - "targetMetaKnownKey", "targetMetaKnownValue", - testdataFactory.createTarget("targetIdWithMetadata")); // and validate } /** @@ -847,7 +836,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { */ @Test @WithUser(allSpPermissions = true) - void updateMetadata() { + void createMetadata() { final String knownKey = "myKnownKey"; final String knownValue = "myKnownValue"; final String knownUpdateValue = "myNewUpdatedValue"; @@ -864,7 +853,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { assertThat(changedLockRevisionTarget.getOptLockRevision()).isEqualTo(2); // update the target metadata - targetManagement.updateMetadata(target.getControllerId(), knownKey, knownUpdateValue); + targetManagement.createMetadata(target.getControllerId(), knownKey, knownUpdateValue); // we are updating the target meta-data so also modifying the target so opt lock revision must be three final Target changedLockRevisionTarget2 = targetManagement.get(target.getId()).orElseThrow(NoSuchElementException::new); assertThat(changedLockRevisionTarget2.getOptLockRevision()).isEqualTo(3);