Absract metatype impl (#2575)
* Add common "interface" for metadata supporting entities * Add common metadata implementation for distribution set and software module * Extract PermissionSupport + extend by TargetManagement * TargetManagement tags are now protected by Target permissions - as it should be --------- Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -286,7 +286,7 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateMetadata(final Long distributionSetId, final String metadataKey, final MgmtMetadataBodyPut metadata) {
|
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
|
@Override
|
||||||
|
|||||||
@@ -438,7 +438,7 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateMetadata(final String targetId, final String metadataKey, final MgmtMetadataBodyPut metadata) {
|
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
|
@Override
|
||||||
|
|||||||
@@ -54,14 +54,13 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
|||||||
* Management service for {@link DistributionSet}s.
|
* Management service for {@link DistributionSet}s.
|
||||||
*/
|
*/
|
||||||
public interface DistributionSetManagement<T extends DistributionSet>
|
public interface DistributionSetManagement<T extends DistributionSet>
|
||||||
extends RepositoryManagement<T, DistributionSetManagement.Create, DistributionSetManagement.Update> {
|
extends RepositoryManagement<T, DistributionSetManagement.Create, DistributionSetManagement.Update>, MetadataSupport<String> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default String permissionGroup() {
|
default String permissionGroup() {
|
||||||
return "DISTRIBUTION_SET";
|
return "DISTRIBUTION_SET";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find {@link DistributionSet} based on given ID including (lazy loaded) details, e.g. {@link DistributionSet#getModules()}. <br/>
|
* Find {@link DistributionSet} based on given ID including (lazy loaded) details, e.g. {@link DistributionSet#getModules()}. <br/>
|
||||||
* For performance reasons it is recommended to use {@link #get(long)} if the details are not required.
|
* For performance reasons it is recommended to use {@link #get(long)} if the details are not required.
|
||||||
@@ -140,49 +139,6 @@ public interface DistributionSetManagement<T extends DistributionSet>
|
|||||||
@PreAuthorize(HAS_UPDATE_REPOSITORY)
|
@PreAuthorize(HAS_UPDATE_REPOSITORY)
|
||||||
List<T> unassignTag(@NotEmpty Collection<Long> ids, long tagId);
|
List<T> unassignTag(@NotEmpty Collection<Long> 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<String, String> 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<String, String> 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
|
* Locks a distribution set. From then on its functional properties could not be changed, and it could be assigned to targets
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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 <MV> the type of the meta-data value
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("java:S119") // java:S119 - better self explainable
|
||||||
|
public interface MetadataSupport<MV> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<String, ? extends MV> 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<String, MV> 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);
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -36,7 +36,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
|||||||
* @param <C> type of the create request
|
* @param <C> type of the create request
|
||||||
* @param <U> type of the update request
|
* @param <U> type of the update request
|
||||||
*/
|
*/
|
||||||
public interface RepositoryManagement<T extends BaseEntity, C, U extends Identifiable<Long>> {
|
public interface RepositoryManagement<T extends BaseEntity, C, U extends Identifiable<Long>> extends PermissionSupport {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new {@link BaseEntity}.
|
* Creates new {@link BaseEntity}.
|
||||||
@@ -153,6 +153,7 @@ public interface RepositoryManagement<T extends BaseEntity, C, U extends Identif
|
|||||||
@PreAuthorize(SpringEvalExpressions.HAS_DELETE_REPOSITORY)
|
@PreAuthorize(SpringEvalExpressions.HAS_DELETE_REPOSITORY)
|
||||||
void delete(@NotEmpty Collection<Long> ids);
|
void delete(@NotEmpty Collection<Long> ids);
|
||||||
|
|
||||||
|
@Override
|
||||||
default String permissionGroup() {
|
default String permissionGroup() {
|
||||||
return "REPOSITORY";
|
return "REPOSITORY";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,66 +42,13 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
|||||||
* Service for managing {@link SoftwareModule}s.
|
* Service for managing {@link SoftwareModule}s.
|
||||||
*/
|
*/
|
||||||
public interface SoftwareModuleManagement<T extends SoftwareModule>
|
public interface SoftwareModuleManagement<T extends SoftwareModule>
|
||||||
extends RepositoryManagement<T, SoftwareModuleManagement.Create, SoftwareModuleManagement.Update> {
|
extends RepositoryManagement<T, SoftwareModuleManagement.Create, SoftwareModuleManagement.Update>, MetadataSupport<MetadataValue> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default String permissionGroup() {
|
default String permissionGroup() {
|
||||||
return "SOFTWARE_MODULE";
|
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<String, ? extends MetadataValue> 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<String, MetadataValue> 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)
|
@PreAuthorize(SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||||
Map<Long, Map<String, String>> findMetaDataBySoftwareModuleIdsAndTargetVisible(Collection<Long> ids);
|
Map<Long, Map<String, String>> findMetaDataBySoftwareModuleIdsAndTargetVisible(Collection<Long> ids);
|
||||||
|
|
||||||
|
|||||||
@@ -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_CREATE_TARGET;
|
||||||
import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_AUTH_DELETE_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_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_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_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_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_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 static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -36,6 +34,7 @@ import jakarta.validation.constraints.NotEmpty;
|
|||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
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.TargetCreate;
|
||||||
import org.eclipse.hawkbit.repository.builder.TargetUpdate;
|
import org.eclipse.hawkbit.repository.builder.TargetUpdate;
|
||||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||||
@@ -60,7 +59,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
|||||||
/**
|
/**
|
||||||
* Management service for {@link Target}s.
|
* Management service for {@link Target}s.
|
||||||
*/
|
*/
|
||||||
public interface TargetManagement {
|
public interface TargetManagement extends PermissionSupport {
|
||||||
|
|
||||||
String DETAILS_BASE = "base";
|
String DETAILS_BASE = "base";
|
||||||
String DETAILS_AUTO_CONFIRMATION_STATUS = "autoConfirmationStatus";
|
String DETAILS_AUTO_CONFIRMATION_STATUS = "autoConfirmationStatus";
|
||||||
@@ -72,6 +71,12 @@ public interface TargetManagement {
|
|||||||
HAS_AUTH_AND +
|
HAS_AUTH_AND +
|
||||||
HAS_AUTH_PREFIX + SpPermission.READ_TARGET + HAS_AUTH_SUFFIX +
|
HAS_AUTH_PREFIX + SpPermission.READ_TARGET + HAS_AUTH_SUFFIX +
|
||||||
BRACKET_CLOSE;
|
BRACKET_CLOSE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default String permissionGroup() {
|
||||||
|
return "TARGET";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counts number of targets with the given distribution set assigned.
|
* Counts number of targets with the given distribution set assigned.
|
||||||
*
|
*
|
||||||
@@ -744,45 +749,56 @@ public interface TargetManagement {
|
|||||||
Page<Target> findByControllerAttributesRequested(@NotNull Pageable pageable);
|
Page<Target> 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 controllerId the entity id which meta-data has to be updated
|
||||||
* @param metadata the meta-data entries to create or update
|
* @param key the key of the meta-data entry to be updated
|
||||||
* @throws EntityNotFoundException if given target does not exist
|
* @param value the meta-data value to be updated
|
||||||
* @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<String, String> 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<String, String> 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
|
|
||||||
* @throws EntityNotFoundException in case the meta-data entry does not exist and cannot be updated
|
* @throws EntityNotFoundException in case the meta-data entry does not exist and cannot be updated
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(HAS_UPDATE_REPOSITORY)
|
@PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
|
||||||
void updateMetadata(@NotEmpty String controllerId, @NotNull String key, @NotNull String value);
|
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<String, String> 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<String, String> getMetadata(@NotNull String controllerId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a entity meta-data entry.
|
||||||
*
|
*
|
||||||
* @param controllerId where meta-data has to be deleted
|
* @param controllerId where meta-data has to be deleted
|
||||||
* @param key of the meta data element
|
* @param key of the meta-data element
|
||||||
* @throws EntityNotFoundException if given target does not exist
|
* @throws EntityNotFoundException if entity with given ID does not exist or the key is not found
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(HAS_UPDATE_REPOSITORY)
|
@PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
|
||||||
void deleteMetadata(@NotEmpty String controllerId, @NotEmpty String key);
|
void deleteMetadata(@NotNull String controllerId, @NotEmpty String key);
|
||||||
}
|
}
|
||||||
@@ -64,9 +64,9 @@ public class RepositoryConfiguration {
|
|||||||
public boolean hasPermission(final Authentication authentication, final Object targetDomainObject, final Object permission) {
|
public boolean hasPermission(final Authentication authentication, final Object targetDomainObject, final Object permission) {
|
||||||
if (targetDomainObject instanceof MethodSecurityExpressionOperations root) {
|
if (targetDomainObject instanceof MethodSecurityExpressionOperations root) {
|
||||||
final String neededPermission =
|
final String neededPermission =
|
||||||
permission + "_" + (root.getThis() instanceof RepositoryManagement<?, ?, ?> repositoryManagement
|
permission + "_" + (root.getThis() instanceof PermissionSupport permissionSupport
|
||||||
? repositoryManagement.permissionGroup()
|
? permissionSupport.permissionGroup()
|
||||||
: "REPOSITORY"); // TODO - should not fall back here - all using parmissions should extend repository management interface
|
: "REPOSITORY"); // TODO - should not fall back here - all using permissions should extend repository management interface
|
||||||
final boolean hasPermission = roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities()).stream()
|
final boolean hasPermission = roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities()).stream()
|
||||||
.map(GrantedAuthority::getAuthority)
|
.map(GrantedAuthority::getAuthority)
|
||||||
.anyMatch(authority -> authority.equals(neededPermission));
|
.anyMatch(authority -> authority.equals(neededPermission));
|
||||||
|
|||||||
@@ -80,11 +80,12 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
|||||||
|
|
||||||
protected final R jpaRepository;
|
protected final R jpaRepository;
|
||||||
protected final EntityManager entityManager;
|
protected final EntityManager entityManager;
|
||||||
private final Constructor<T> entityConstructor;
|
private final Supplier<T> jpaEntityCreator;
|
||||||
|
|
||||||
protected AbstractJpaRepositoryManagement(final R jpaRepository, final EntityManager entityManager) {
|
protected AbstractJpaRepositoryManagement(final R jpaRepository, final EntityManager entityManager) {
|
||||||
this.jpaRepository = jpaRepository;
|
this.jpaRepository = jpaRepository;
|
||||||
this.entityManager = entityManager;
|
this.entityManager = entityManager;
|
||||||
|
final Constructor<T> entityConstructor;
|
||||||
try {
|
try {
|
||||||
entityConstructor = jpaRepository.getDomainClass().getConstructor();
|
entityConstructor = jpaRepository.getDomainClass().getConstructor();
|
||||||
// test if method works, if fine - it shall never fail when called later
|
// test if method works, if fine - it shall never fail when called later
|
||||||
@@ -92,6 +93,13 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
|||||||
} catch (final NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
} catch (final NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||||
throw new IllegalStateException("JPA domain class " + jpaRepository.getDomainClass() + " shall have public no-args constructor", e);
|
throw new IllegalStateException("JPA domain class " + jpaRepository.getDomainClass() + " shall have public no-args constructor", e);
|
||||||
}
|
}
|
||||||
|
jpaEntityCreator = () -> {
|
||||||
|
try {
|
||||||
|
return entityConstructor.newInstance();
|
||||||
|
} catch (final InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new IllegalStateException("Must NEVER happen!", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -156,7 +164,7 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected T update(final Identifiable<Long> update, final T entity) {
|
protected T update(final Identifiable<Long> 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
|
// otherwise the Utils will throw an exception that there is no counterpart setter for getId
|
||||||
if (ObjectCopyUtil.copy(update, entity, false, this::attach)) {
|
if (ObjectCopyUtil.copy(update, entity, false, this::attach)) {
|
||||||
return jpaRepository.save(entity);
|
return jpaRepository.save(entity);
|
||||||
@@ -289,12 +297,7 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private T jpaEntity(final Object create) {
|
private T jpaEntity(final Object create) {
|
||||||
final T jpaEntity;
|
final T jpaEntity = jpaEntityCreator.get();
|
||||||
try {
|
|
||||||
jpaEntity = entityConstructor.newInstance();
|
|
||||||
} catch (final InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
|
||||||
throw new IllegalStateException("Must NEVER happen!", e);
|
|
||||||
}
|
|
||||||
ObjectCopyUtil.copy(create, jpaEntity, false, this::attach);
|
ObjectCopyUtil.copy(create, jpaEntity, false, this::attach);
|
||||||
return jpaEntity;
|
return jpaEntity;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
/**
|
||||||
|
* 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.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.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import jakarta.persistence.EntityManager;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.Identifiable;
|
||||||
|
import org.eclipse.hawkbit.repository.MetadataSupport;
|
||||||
|
import org.eclipse.hawkbit.repository.RsqlQueryField;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.model.WithMetadata;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.repository.BaseEntityRepository;
|
||||||
|
import org.eclipse.hawkbit.utils.ObjectCopyUtil;
|
||||||
|
import org.springframework.dao.ConcurrencyFailureException;
|
||||||
|
import org.springframework.retry.annotation.Backoff;
|
||||||
|
import org.springframework.retry.annotation.Retryable;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@SuppressWarnings("java:S119") // java:S119 - better self explainable
|
||||||
|
abstract class AbstractJpaRepositoryWithMetadataManagement<T extends AbstractJpaBaseEntity & WithMetadata<MV, MVI>, C, U extends Identifiable<Long>, R extends BaseEntityRepository<T>, A extends Enum<A> & RsqlQueryField, MV, MVI extends MV>
|
||||||
|
extends AbstractJpaRepositoryManagement<T, C, U, R, A> implements MetadataSupport<MV> {
|
||||||
|
|
||||||
|
private final Supplier<MVI> 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<MVI> metadataValueType = (Class<MVI>) ((ParameterizedType) jpaRepository.getDomainClass().getMethod("getMetadata")
|
||||||
|
.getGenericReturnType())
|
||||||
|
.getActualTypeArguments()[1];
|
||||||
|
final Constructor<MVI> 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<String, MVI> 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<String, ? extends MV> metadata) {
|
||||||
|
final T jpaEntity = getValid(id);
|
||||||
|
final Map<String, MVI> 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<String, MV> 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<String, MVI> 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<String, MVI> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -90,7 +90,7 @@ import org.springframework.util.ObjectUtils;
|
|||||||
@Service
|
@Service
|
||||||
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "distribution-set-management" }, matchIfMissing = true)
|
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "distribution-set-management" }, matchIfMissing = true)
|
||||||
public class JpaDistributionSetManagement
|
public class JpaDistributionSetManagement
|
||||||
extends AbstractJpaRepositoryManagement<JpaDistributionSet, DistributionSetManagement.Create, DistributionSetManagement.Update, DistributionSetRepository, DistributionSetFields>
|
extends AbstractJpaRepositoryWithMetadataManagement<JpaDistributionSet, DistributionSetManagement.Create, DistributionSetManagement.Update, DistributionSetRepository, DistributionSetFields, String, String>
|
||||||
implements DistributionSetManagement<JpaDistributionSet> {
|
implements DistributionSetManagement<JpaDistributionSet> {
|
||||||
|
|
||||||
private final DistributionSetTagManagement<JpaDistributionSetTag> distributionSetTagManagement;
|
private final DistributionSetTagManagement<JpaDistributionSetTag> 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<String, String> md) {
|
|
||||||
final JpaDistributionSet distributionSet = getValid0(id);
|
|
||||||
|
|
||||||
// get the modifiable metadata map
|
|
||||||
final Map<String, String> 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<String, String> 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<String, String> 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<String, String> metadata = distributionSet.getMetadata();
|
|
||||||
if (metadata.remove(key) == null) {
|
|
||||||
throw new EntityNotFoundException("DistributionSet metadata", id + ":" + key);
|
|
||||||
}
|
|
||||||
|
|
||||||
jpaRepository.save(distributionSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
@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);
|
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
|
// check if it shall implicitly lock a distribution set
|
||||||
boolean isImplicitLockApplicable(final DistributionSet distributionSet) {
|
boolean isImplicitLockApplicable(final DistributionSet distributionSet) {
|
||||||
final JpaDistributionSet jpaDistributionSet = (JpaDistributionSet) distributionSet;
|
final JpaDistributionSet jpaDistributionSet = (JpaDistributionSet) distributionSet;
|
||||||
@@ -584,11 +538,6 @@ public class JpaDistributionSetManagement
|
|||||||
return specifications;
|
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) {
|
private void assertSoftwareModuleQuota(final Long id, final int requested) {
|
||||||
QuotaHelper.assertAssignmentQuota(id, requested, quotaManagement.getMaxSoftwareModulesPerDistributionSet(),
|
QuotaHelper.assertAssignmentQuota(id, requested, quotaManagement.getMaxSoftwareModulesPerDistributionSet(),
|
||||||
SoftwareModule.class, DistributionSet.class, softwareModuleRepository::countByAssignedToId);
|
SoftwareModule.class, DistributionSet.class, softwareModuleRepository::countByAssignedToId);
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.function.UnaryOperator;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import jakarta.persistence.EntityManager;
|
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;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValue;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValue;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||||
import org.eclipse.hawkbit.utils.ObjectCopyUtil;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||||
import org.springframework.dao.ConcurrencyFailureException;
|
import org.springframework.dao.ConcurrencyFailureException;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
@@ -64,12 +61,9 @@ import org.springframework.util.ObjectUtils;
|
|||||||
@Service
|
@Service
|
||||||
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "software-module-management" }, matchIfMissing = true)
|
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "software-module-management" }, matchIfMissing = true)
|
||||||
public class JpaSoftwareModuleManagement
|
public class JpaSoftwareModuleManagement
|
||||||
extends
|
extends AbstractJpaRepositoryWithMetadataManagement<JpaSoftwareModule, SoftwareModuleManagement.Create, SoftwareModuleManagement.Update, SoftwareModuleRepository, SoftwareModuleFields, MetadataValue, JpaSoftwareModule.JpaMetadataValue>
|
||||||
AbstractJpaRepositoryManagement<JpaSoftwareModule, SoftwareModuleManagement.Create, SoftwareModuleManagement.Update, SoftwareModuleRepository, SoftwareModuleFields>
|
|
||||||
implements SoftwareModuleManagement<JpaSoftwareModule> {
|
implements SoftwareModuleManagement<JpaSoftwareModule> {
|
||||||
|
|
||||||
protected static final String SOFTWARE_MODULE_METADATA = "SoftwareModuleMetadata";
|
|
||||||
|
|
||||||
private final DistributionSetRepository distributionSetRepository;
|
private final DistributionSetRepository distributionSetRepository;
|
||||||
private final SoftwareModuleTypeRepository softwareModuleTypeRepository;
|
private final SoftwareModuleTypeRepository softwareModuleTypeRepository;
|
||||||
private final ArtifactManagement artifactManagement;
|
private final ArtifactManagement artifactManagement;
|
||||||
@@ -164,88 +158,6 @@ public class JpaSoftwareModuleManagement
|
|||||||
.toList();
|
.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<String, JpaMetadataValue> 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<String, ? extends MetadataValue> metadata) {
|
|
||||||
final JpaSoftwareModule softwareModule = jpaRepository
|
|
||||||
.findById(id)
|
|
||||||
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id));
|
|
||||||
final Map<String, JpaMetadataValue> 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<String, MetadataValue> 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<String, JpaMetadataValue> 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
|
// called only with 'system code' access, so no need to check access control
|
||||||
@Override
|
@Override
|
||||||
public Map<Long, Map<String, String>> findMetaDataBySoftwareModuleIdsAndTargetVisible(final Collection<Long> ids) {
|
public Map<Long, Map<String, String>> findMetaDataBySoftwareModuleIdsAndTargetVisible(final Collection<Long> ids) {
|
||||||
@@ -349,6 +261,17 @@ public class JpaSoftwareModuleManagement
|
|||||||
return jpaRepository.count(SoftwareModuleSpecification.byAssignedToDs(distributionSetId));
|
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) {
|
private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) {
|
||||||
jpaRepository.getAccessController().ifPresent(accessController ->
|
jpaRepository.getAccessController().ifPresent(accessController ->
|
||||||
accessController.assertOperationAllowed(AccessController.Operation.DELETE, swModule));
|
accessController.assertOperationAllowed(AccessController.Operation.DELETE, swModule));
|
||||||
@@ -364,16 +287,6 @@ public class JpaSoftwareModuleManagement
|
|||||||
smFilterNameAndVersionEntries[1]);
|
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) {
|
private void assertSoftwareModuleTypeExists(final Long typeId) {
|
||||||
if (!softwareModuleTypeRepository.existsById(typeId)) {
|
if (!softwareModuleTypeRepository.existsById(typeId)) {
|
||||||
throw new EntityNotFoundException(SoftwareModuleType.class, typeId);
|
throw new EntityNotFoundException(SoftwareModuleType.class, typeId);
|
||||||
|
|||||||
@@ -711,6 +711,23 @@ public class JpaTargetManagement implements TargetManagement {
|
|||||||
targetRepository, List.of(TargetSpecifications.hasRequestControllerAttributesTrue()), pageable);
|
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<String, String> metadata = target.getMetadata();
|
||||||
|
if (!metadata.containsKey(key)) {
|
||||||
|
throw new EntityNotFoundException("Target metadata", controllerId + ":" + key);
|
||||||
|
}
|
||||||
|
metadata.put(key, value);
|
||||||
|
|
||||||
|
targetRepository.save(target);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||||
@@ -737,20 +754,8 @@ public class JpaTargetManagement implements TargetManagement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
public String getMetadata(final String controllerId, final String key) {
|
||||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
return getMap(controllerId, JpaTarget_.metadata).get(key);
|
||||||
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<String, String> metadata = target.getMetadata();
|
|
||||||
if (!metadata.containsKey(key)) {
|
|
||||||
throw new EntityNotFoundException("Target metadata", controllerId + ":" + key);
|
|
||||||
}
|
|
||||||
metadata.put(key, value);
|
|
||||||
|
|
||||||
targetRepository.save(target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -70,7 +70,9 @@ import org.springframework.core.annotation.Order;
|
|||||||
attributeNodes = { @NamedAttributeNode("modules"), @NamedAttributeNode("tags"), @NamedAttributeNode("type") })
|
attributeNodes = { @NamedAttributeNode("modules"), @NamedAttributeNode("tags"), @NamedAttributeNode("type") })
|
||||||
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities
|
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities
|
||||||
@SuppressWarnings("squid:S2160")
|
@SuppressWarnings("squid:S2160")
|
||||||
public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implements DistributionSet, EventAwareEntity {
|
public class JpaDistributionSet
|
||||||
|
extends AbstractJpaNamedVersionedEntity
|
||||||
|
implements DistributionSet, WithMetadata<String, String>, EventAwareEntity {
|
||||||
|
|
||||||
@Serial
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ import org.eclipse.hawkbit.repository.exception.LockedException;
|
|||||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
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.repository.model.SoftwareModuleType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,7 +71,9 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
|||||||
@NamedEntityGraph(name = "SoftwareModule.artifacts", attributeNodes = { @NamedAttributeNode("artifacts") })
|
@NamedEntityGraph(name = "SoftwareModule.artifacts", attributeNodes = { @NamedAttributeNode("artifacts") })
|
||||||
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities
|
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities
|
||||||
@SuppressWarnings("squid:S2160")
|
@SuppressWarnings("squid:S2160")
|
||||||
public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implements SoftwareModule, EventAwareEntity {
|
public class JpaSoftwareModule
|
||||||
|
extends AbstractJpaNamedVersionedEntity
|
||||||
|
implements SoftwareModule, WithMetadata<MetadataValue, JpaSoftwareModule.JpaMetadataValue>, EventAwareEntity {
|
||||||
|
|
||||||
@Serial
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|||||||
@@ -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 <MV> metadata value type
|
||||||
|
* @param <MVI> metadata value implementation type
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("java:S119") // java:S119 - better self explainable
|
||||||
|
public interface WithMetadata<MV, MVI extends MV> {
|
||||||
|
|
||||||
|
Map<String, MVI> getMetadata();
|
||||||
|
|
||||||
|
// return if the entity is valid for update metadata
|
||||||
|
default boolean isValid() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -160,11 +160,11 @@ class DistributionSetAccessControllerTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
// verify distributionSetManagement#updateMetaData
|
// verify distributionSetManagement#updateMetaData
|
||||||
final String newValue = "newValue";
|
final String newValue = "newValue";
|
||||||
distributionSetManagement.updateMetadata(permitted.getId(), mdPresetKey, newValue);
|
distributionSetManagement.createMetadata(permitted.getId(), mdPresetKey, newValue);
|
||||||
assertThatThrownBy(() -> distributionSetManagement.updateMetadata(readOnlyId, mdPresetKey, newValue))
|
assertThatThrownBy(() -> distributionSetManagement.createMetadata(readOnlyId, mdPresetKey, newValue))
|
||||||
.as("Distribution set not allowed to me modified.")
|
.as("Distribution set not allowed to me modified.")
|
||||||
.isInstanceOf(InsufficientPermissionException.class);
|
.isInstanceOf(InsufficientPermissionException.class);
|
||||||
assertThatThrownBy(() -> distributionSetManagement.updateMetadata(hiddenId, mdPresetKey, newValue))
|
assertThatThrownBy(() -> distributionSetManagement.createMetadata(hiddenId, mdPresetKey, newValue))
|
||||||
.as("Distribution set should not be visible.")
|
.as("Distribution set should not be visible.")
|
||||||
.isInstanceOf(EntityNotFoundException.class);
|
.isInstanceOf(EntityNotFoundException.class);
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ class DistributionSetManagementSecurityTest
|
|||||||
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
|
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void createMetadataPermissionsCheck() {
|
void createMetadataMapPermissionsCheck() {
|
||||||
assertPermissions(
|
assertPermissions(
|
||||||
() -> {
|
() -> {
|
||||||
distributionSetManagement.createMetadata(1L, Map.of("key", "value"));
|
distributionSetManagement.createMetadata(1L, Map.of("key", "value"));
|
||||||
@@ -95,9 +95,9 @@ class DistributionSetManagementSecurityTest
|
|||||||
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
|
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void updateMetadataPermissionsCheck() {
|
void createMetadataPermissionsCheck() {
|
||||||
assertPermissions(() -> {
|
assertPermissions(() -> {
|
||||||
distributionSetManagement.updateMetadata(1L, "key", "value");
|
distributionSetManagement.createMetadata(1L, "key", "value");
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
List.of(SpPermission.UPDATE_REPOSITORY));
|
List.of(SpPermission.UPDATE_REPOSITORY));
|
||||||
|
|||||||
@@ -156,8 +156,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
() -> distributionSetManagement.update(DistributionSetManagement.Update.builder().id(NOT_EXIST_IDL).build()),
|
() -> distributionSetManagement.update(DistributionSetManagement.Update.builder().id(NOT_EXIST_IDL).build()),
|
||||||
"DistributionSet");
|
"DistributionSet");
|
||||||
|
|
||||||
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetadata(NOT_EXIST_IDL, "xxx", "xxx"), "DistributionSet");
|
verifyThrownExceptionBy(() -> distributionSetManagement.createMetadata(NOT_EXIST_IDL, "xxx", "xxx"), "DistributionSet");
|
||||||
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetadata(set.getId(), NOT_EXIST_ID, "xxx"), "DistributionSet");
|
|
||||||
|
|
||||||
verifyThrownExceptionBy(() -> distributionSetManagement.getOrElseThrowException(NOT_EXIST_IDL), "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.
|
* Verifies the enforcement of the metadata quota per distribution set.
|
||||||
*/
|
*/
|
||||||
@@ -520,7 +506,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@WithUser(allSpPermissions = true)
|
@WithUser(allSpPermissions = true)
|
||||||
void updateMetadata() {
|
void createMetadata() {
|
||||||
final String knownKey = "myKnownKey";
|
final String knownKey = "myKnownKey";
|
||||||
final String knownValue = "myKnownValue";
|
final String knownValue = "myKnownValue";
|
||||||
final String knownUpdateValue = "myNewUpdatedValue";
|
final String knownUpdateValue = "myNewUpdatedValue";
|
||||||
@@ -539,7 +525,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
waitNextMillis();
|
waitNextMillis();
|
||||||
// update the DS metadata
|
// 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
|
// we are updating the sw metadata so also modifying the base software
|
||||||
// module so opt lock revision must be three
|
// module so opt lock revision must be three
|
||||||
final DistributionSet reloadedDS = getOrThrow(distributionSetManagement.get(ds.getId()));
|
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
|
// assert that an existing metadata can not be updated
|
||||||
assertThatExceptionOfType(InvalidDistributionSetException.class)
|
assertThatExceptionOfType(InvalidDistributionSetException.class)
|
||||||
.as("Invalid distributionSet should throw an exception")
|
.as("Invalid distributionSet should throw an exception")
|
||||||
.isThrownBy(() -> distributionSetManagement.updateMetadata(dsId, knownKey1, knownUpdateValue));
|
.isThrownBy(() -> distributionSetManagement.createMetadata(dsId, knownKey1, knownUpdateValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -500,13 +500,13 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
|
|||||||
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
|
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void createMetadataPermissionsCheck() {
|
void createMetadataMapPermissionsCheck() {
|
||||||
assertPermissions(
|
assertPermissions(
|
||||||
() -> {
|
() -> {
|
||||||
targetManagement.createMetadata("controllerId", Map.of("key", "value"));
|
targetManagement.createMetadata("controllerId", Map.of("key", "value"));
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
List.of(SpPermission.UPDATE_REPOSITORY));
|
List.of(SpPermission.UPDATE_TARGET));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -514,20 +514,20 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void getMetadataPermissionsCheck() {
|
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.
|
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@WithUser(principal = "user", authorities = { SpPermission.UPDATE_REPOSITORY })
|
@WithUser(principal = "user", authorities = { SpPermission.UPDATE_TARGET })
|
||||||
void updateMetadataPermissionsCheck() {
|
void createMetadataPermissionsCheck() {
|
||||||
assertPermissions(() -> {
|
assertPermissions(() -> {
|
||||||
targetManagement.updateMetadata("controllerId", "key", "value");
|
targetManagement.createMetadata("controllerId", "key", "value");
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
List.of(SpPermission.UPDATE_REPOSITORY));
|
List.of(SpPermission.UPDATE_TARGET));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -538,6 +538,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
|
|||||||
assertPermissions(() -> {
|
assertPermissions(() -> {
|
||||||
targetManagement.deleteMetadata("controllerId", "key");
|
targetManagement.deleteMetadata("controllerId", "key");
|
||||||
return null;
|
return null;
|
||||||
}, List.of(SpPermission.UPDATE_REPOSITORY));
|
}, List.of(SpPermission.UPDATE_TARGET));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
verifyThrownExceptionBy(() -> targetManagement.deleteMetadata(NOT_EXIST_ID, "xxx"), "Target");
|
verifyThrownExceptionBy(() -> targetManagement.deleteMetadata(NOT_EXIST_ID, "xxx"), "Target");
|
||||||
verifyThrownExceptionBy(() -> targetManagement.deleteMetadata(target.getControllerId(), NOT_EXIST_ID), "Target");
|
verifyThrownExceptionBy(() -> targetManagement.deleteMetadata(target.getControllerId(), NOT_EXIST_ID), "Target");
|
||||||
verifyThrownExceptionBy(() -> targetManagement.getMetadata(NOT_EXIST_ID).get("xxx"), "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(target.isRequestControllerAttributes()).isFalse();
|
||||||
assertThat(targetManagement.findByControllerAttributesRequested(PAGE).getContent()).contains(updated);
|
assertThat(targetManagement.findByControllerAttributesRequested(PAGE).getContent()).contains(updated);
|
||||||
assertThat(targetManagement.isControllerAttributesRequested(knownControllerId)).isTrue();
|
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
|
@Test
|
||||||
@WithUser(allSpPermissions = true)
|
@WithUser(allSpPermissions = true)
|
||||||
void updateMetadata() {
|
void createMetadata() {
|
||||||
final String knownKey = "myKnownKey";
|
final String knownKey = "myKnownKey";
|
||||||
final String knownValue = "myKnownValue";
|
final String knownValue = "myKnownValue";
|
||||||
final String knownUpdateValue = "myNewUpdatedValue";
|
final String knownUpdateValue = "myNewUpdatedValue";
|
||||||
@@ -864,7 +853,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
assertThat(changedLockRevisionTarget.getOptLockRevision()).isEqualTo(2);
|
assertThat(changedLockRevisionTarget.getOptLockRevision()).isEqualTo(2);
|
||||||
|
|
||||||
// update the target metadata
|
// 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
|
// 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);
|
final Target changedLockRevisionTarget2 = targetManagement.get(target.getId()).orElseThrow(NoSuchElementException::new);
|
||||||
assertThat(changedLockRevisionTarget2.getOptLockRevision()).isEqualTo(3);
|
assertThat(changedLockRevisionTarget2.getOptLockRevision()).isEqualTo(3);
|
||||||
|
|||||||
Reference in New Issue
Block a user