Soft Module metadata as complex map value (#2568)

---------

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-07-30 17:29:02 +03:00
committed by GitHub
parent 4a8e60764f
commit 08cacf9034
38 changed files with 392 additions and 962 deletions

View File

@@ -33,7 +33,6 @@ import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TenantMetaData; import org.eclipse.hawkbit.repository.model.TenantMetaData;
import org.eclipse.hawkbit.rest.json.model.ResponseList; import org.eclipse.hawkbit.rest.json.model.ResponseList;
@@ -133,7 +132,7 @@ public final class DataConversionHelper {
final Target target, final Action uAction, final Target target, final Action uAction,
final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement, final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement,
final HttpRequest request, final ControllerManagement controllerManagement) { final HttpRequest request, final ControllerManagement controllerManagement) {
final Map<Long, List<SoftwareModuleMetadata>> metadata = controllerManagement final Map<Long, Map<String, String>> metadata = controllerManagement
.findTargetVisibleMetaDataBySoftwareModuleId(uAction.getDistributionSet().getModules().stream() .findTargetVisibleMetaDataBySoftwareModuleId(uAction.getDistributionSet().getModules().stream()
.map(SoftwareModule::getId).toList()); .map(SoftwareModule::getId).toList());
@@ -155,10 +154,10 @@ public final class DataConversionHelper {
.toList()); .toList());
} }
private static List<DdiMetadata> mapMetadata(final List<SoftwareModuleMetadata> metadata) { private static List<DdiMetadata> mapMetadata(final Map<String, String> metadata) {
return CollectionUtils.isEmpty(metadata) return CollectionUtils.isEmpty(metadata)
? null ? null
: metadata.stream().map(md -> new DdiMetadata(md.getKey(), md.getValue())).toList(); : metadata.entrySet().stream().map(md -> new DdiMetadata(md.getKey(), md.getValue())).toList();
} }
private static String mapChunkLegacyKeys(final String key) { private static String mapChunkLegacyKeys(final String key) {

View File

@@ -48,6 +48,7 @@ import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionStatus;
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.MetadataValueCreate;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.matcher.Expect; import org.eclipse.hawkbit.repository.test.matcher.Expect;
@@ -253,10 +254,8 @@ class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final Artifact artifactSignature = testdataFactory.createArtifact(nextBytes(ARTIFACT_SIZE), final Artifact artifactSignature = testdataFactory.createArtifact(nextBytes(ARTIFACT_SIZE),
getOsModule(ds), "test1.signature", ARTIFACT_SIZE); getOsModule(ds), "test1.signature", ARTIFACT_SIZE);
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(getOsModule(ds)) softwareModuleManagement.createMetadata(getOsModule(ds), visibleMetadataOsKey, new MetadataValueCreate(visibleMetadataOsValue, true));
.key(visibleMetadataOsKey).value(visibleMetadataOsValue).targetVisible(true)); softwareModuleManagement.createMetadata(getOsModule(ds), "metaDataNotVisible", new MetadataValueCreate("withValue", false));
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(getOsModule(ds))
.key("metaDataNotVisible").value("withValue").targetVisible(false));
final Target savedTarget = createTargetAndAssertNoActiveActions(); final Target savedTarget = createTargetAndAssertNoActiveActions();
@@ -374,10 +373,8 @@ class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final Artifact artifactSignature = testdataFactory.createArtifact( final Artifact artifactSignature = testdataFactory.createArtifact(
nextBytes(ARTIFACT_SIZE), getOsModule(ds), "test1.signature", ARTIFACT_SIZE); nextBytes(ARTIFACT_SIZE), getOsModule(ds), "test1.signature", ARTIFACT_SIZE);
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(getOsModule(ds)) softwareModuleManagement.createMetadata(getOsModule(ds), "metaDataVisible", new MetadataValueCreate("withValue", true));
.key("metaDataVisible").value("withValue").targetVisible(true)); softwareModuleManagement.createMetadata(getOsModule(ds), "metaDataNotVisible", new MetadataValueCreate("withValue", false));
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(getOsModule(ds))
.key("metaDataNotVisible").value("withValue").targetVisible(false));
final Target savedTarget = createTargetAndAssertNoActiveActions(); final Target savedTarget = createTargetAndAssertNoActiveActions();

View File

@@ -29,11 +29,6 @@ import java.util.stream.StreamSupport;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.IterableUtils; import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.ListUtils; import org.apache.commons.collections4.ListUtils;
import org.eclipse.hawkbit.repository.artifact.urlhandler.ApiType;
import org.eclipse.hawkbit.repository.artifact.urlhandler.ArtifactUrl;
import org.eclipse.hawkbit.repository.artifact.urlhandler.ArtifactUrlHandler;
import org.eclipse.hawkbit.repository.artifact.urlhandler.URLPlaceholder;
import org.eclipse.hawkbit.repository.artifact.urlhandler.URLPlaceholder.SoftwareData;
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic; import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey; import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.amqp.api.MessageType; import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
@@ -49,11 +44,15 @@ import org.eclipse.hawkbit.dmf.json.model.DmfSoftwareModule;
import org.eclipse.hawkbit.dmf.json.model.DmfTarget; import org.eclipse.hawkbit.dmf.json.model.DmfTarget;
import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement; import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.artifact.urlhandler.ApiType;
import org.eclipse.hawkbit.repository.artifact.urlhandler.ArtifactUrl;
import org.eclipse.hawkbit.repository.artifact.urlhandler.ArtifactUrlHandler;
import org.eclipse.hawkbit.repository.artifact.urlhandler.URLPlaceholder;
import org.eclipse.hawkbit.repository.artifact.urlhandler.URLPlaceholder.SoftwareData;
import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent; import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent; import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent;
import org.eclipse.hawkbit.repository.event.remote.service.CancelTargetAssignmentServiceEvent; import org.eclipse.hawkbit.repository.event.remote.service.CancelTargetAssignmentServiceEvent;
@@ -71,7 +70,6 @@ import org.eclipse.hawkbit.repository.model.ActionProperties;
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.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TenantMetaData; import org.eclipse.hawkbit.repository.model.TenantMetaData;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
@@ -81,7 +79,6 @@ import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.data.domain.PageRequest;
import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@@ -124,7 +121,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
final AmqpMessageSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler, final AmqpMessageSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler,
final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement, final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement,
final TargetManagement targetManagement, final TargetManagement targetManagement,
final SoftwareModuleManagement<? extends SoftwareModule> softwareModuleManagement, final DistributionSetManagement<? extends DistributionSet> distributionSetManagement, final SoftwareModuleManagement<? extends SoftwareModule> softwareModuleManagement,
final DistributionSetManagement<? extends DistributionSet> distributionSetManagement,
final DeploymentManagement deploymentManagement, final DeploymentManagement deploymentManagement,
final TenantConfigurationManagement tenantConfigurationManagement) { final TenantConfigurationManagement tenantConfigurationManagement) {
super(rabbitTemplate); super(rabbitTemplate);
@@ -187,15 +185,14 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
protected void sendUpdateMessageToTarget( protected void sendUpdateMessageToTarget(
final ActionProperties actionsProps, final Target target, final ActionProperties actionsProps, final Target target,
final Map<SoftwareModule, List<SoftwareModuleMetadata>> softwareModules) { final Map<SoftwareModule, Map<String, String>> softwareModules) {
final Map<String, ActionProperties> actionProp = new HashMap<>(); final Map<String, ActionProperties> actionProp = new HashMap<>();
actionProp.put(target.getControllerId(), actionsProps); actionProp.put(target.getControllerId(), actionsProps);
sendUpdateMessageToTargets(actionProp, Collections.singletonList(target), softwareModules); sendUpdateMessageToTargets(actionProp, Collections.singletonList(target), softwareModules);
} }
protected DmfDownloadAndUpdateRequest createDownloadAndUpdateRequest( protected DmfDownloadAndUpdateRequest createDownloadAndUpdateRequest(
final Target target, final Long actionId, final Target target, final Long actionId, final Map<SoftwareModule, Map<String, String>> softwareModules) {
final Map<SoftwareModule, List<SoftwareModuleMetadata>> softwareModules) {
return new DmfDownloadAndUpdateRequest( return new DmfDownloadAndUpdateRequest(
actionId, actionId,
systemSecurityContext.runAsSystem(target::getSecurityToken), systemSecurityContext.runAsSystem(target::getSecurityToken),
@@ -273,7 +270,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
} }
protected DmfConfirmRequest createConfirmRequest( protected DmfConfirmRequest createConfirmRequest(
final Target target, final Long actionId, final Map<SoftwareModule, List<SoftwareModuleMetadata>> softwareModules) { final Target target, final Long actionId, final Map<SoftwareModule, Map<String, String>> softwareModules) {
return new DmfConfirmRequest( return new DmfConfirmRequest(
actionId, actionId,
systemSecurityContext.runAsSystem(target::getSecurityToken), systemSecurityContext.runAsSystem(target::getSecurityToken),
@@ -282,7 +279,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
void sendMultiActionRequestToTarget( void sendMultiActionRequestToTarget(
final Target target, final List<Action> actions, final Target target, final List<Action> actions,
final Function<SoftwareModule, List<SoftwareModuleMetadata>> getSoftwareModuleMetaData) { final Function<SoftwareModule, Map<String, String>> getSoftwareModuleMetaData) {
final URI targetAddress = target.getAddress(); final URI targetAddress = target.getAddress();
if (!IpUtil.isAmqpUri(targetAddress) || CollectionUtils.isEmpty(actions)) { if (!IpUtil.isAmqpUri(targetAddress) || CollectionUtils.isEmpty(actions)) {
return; return;
@@ -295,9 +292,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
target, action, target, action,
action.getDistributionSet().getModules().stream() action.getDistributionSet().getModules().stream()
.collect(Collectors.toMap(Function.identity(), module -> { .collect(Collectors.toMap(Function.identity(), module -> {
final List<SoftwareModuleMetadata> softwareModuleMetadata = getSoftwareModuleMetaData.apply( final Map<String, String> softwareModuleMetadata = getSoftwareModuleMetaData.apply(module);
module); return softwareModuleMetadata == null ? Collections.emptyMap() : softwareModuleMetadata;
return softwareModuleMetadata == null ? Collections.emptyList() : softwareModuleMetadata;
}))); })));
final int weight = deploymentManagement.getWeightConsideringDefault(action); final int weight = deploymentManagement.getWeightConsideringDefault(action);
return new DmfMultiActionRequest.DmfMultiActionElement(getEventTypeForAction(action), actionRequest, weight); return new DmfMultiActionRequest.DmfMultiActionElement(getEventTypeForAction(action), actionRequest, weight);
@@ -424,14 +420,14 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
private void sendUpdateMessageToTargets( private void sendUpdateMessageToTargets(
final Long dsId, final Map<String, ActionProperties> actionsPropsByTargetId, final List<Target> targets) { final Long dsId, final Map<String, ActionProperties> actionsPropsByTargetId, final List<Target> targets) {
distributionSetManagement.get(dsId).ifPresent(ds -> { distributionSetManagement.get(dsId).ifPresent(ds -> {
final Map<SoftwareModule, List<SoftwareModuleMetadata>> softwareModules = getSoftwareModulesWithMetadata(ds); final Map<SoftwareModule, Map<String, String>> softwareModules = getSoftwareModulesWithMetadata(ds);
sendUpdateMessageToTargets(actionsPropsByTargetId, targets, softwareModules); sendUpdateMessageToTargets(actionsPropsByTargetId, targets, softwareModules);
}); });
} }
private void sendUpdateMessageToTargets( private void sendUpdateMessageToTargets(
final Map<String, ActionProperties> actionsPropsByTargetId, final Map<String, ActionProperties> actionsPropsByTargetId,
final List<Target> targets, final Map<SoftwareModule, List<SoftwareModuleMetadata>> softwareModules) { final List<Target> targets, final Map<SoftwareModule, Map<String, String>> softwareModules) {
if (!targets.isEmpty() && isBatchAssignmentsEnabled()) { if (!targets.isEmpty() && isBatchAssignmentsEnabled()) {
sendBatchUpdateMessage(actionsPropsByTargetId, targets, softwareModules); sendBatchUpdateMessage(actionsPropsByTargetId, targets, softwareModules);
} else { } else {
@@ -455,7 +451,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
.flatMap(ds -> ds.getModules().stream()) .flatMap(ds -> ds.getModules().stream())
.map(SoftwareModule::getId)) .map(SoftwareModule::getId))
.collect(Collectors.toSet()); .collect(Collectors.toSet());
final Map<Long, List<SoftwareModuleMetadata>> getSoftwareModuleMetadata = final Map<Long, ? extends Map<String, String>> getSoftwareModuleMetadata =
allSmIds.isEmpty() allSmIds.isEmpty()
? Collections.emptyMap() ? Collections.emptyMap()
: softwareModuleManagement.findMetaDataBySoftwareModuleIdsAndTargetVisible(allSmIds); : softwareModuleManagement.findMetaDataBySoftwareModuleIdsAndTargetVisible(allSmIds);
@@ -467,7 +463,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
private DmfActionRequest createDmfActionRequest( private DmfActionRequest createDmfActionRequest(
final Target target, final Action action, final Target target, final Action action,
final Map<SoftwareModule, List<SoftwareModuleMetadata>> softwareModules) { final Map<SoftwareModule, Map<String, String>> softwareModules) {
if (action.isCancelingOrCanceled()) { if (action.isCancelingOrCanceled()) {
return new DmfActionRequest(action.getId()); return new DmfActionRequest(action.getId());
} else if (action.isWaitingConfirmation()) { } else if (action.isWaitingConfirmation()) {
@@ -477,8 +473,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
} }
private void sendSingleUpdateMessage( private void sendSingleUpdateMessage(
final ActionProperties action, final Target target, final ActionProperties action, final Target target, final Map<SoftwareModule, Map<String, String>> modules) {
final Map<SoftwareModule, List<SoftwareModuleMetadata>> modules) {
final String tenant = action.getTenant(); final String tenant = action.getTenant();
final URI targetAddress = target.getAddress(); final URI targetAddress = target.getAddress();
@@ -531,7 +526,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
} }
private List<DmfSoftwareModule> convertToAmqpSoftwareModules( private List<DmfSoftwareModule> convertToAmqpSoftwareModules(
final Target target, final Map<SoftwareModule, List<SoftwareModuleMetadata>> softwareModules) { final Target target, final Map<SoftwareModule, Map<String, String>> softwareModules) {
return Optional.ofNullable(softwareModules) return Optional.ofNullable(softwareModules)
.map(Map::entrySet) .map(Map::entrySet)
.map(Set::stream) .map(Set::stream)
@@ -539,19 +534,18 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
.orElse(null); .orElse(null);
} }
private DmfSoftwareModule convertToAmqpSoftwareModule( private DmfSoftwareModule convertToAmqpSoftwareModule(final Target target, final Entry<SoftwareModule, Map<String, String>> entry) {
final Target target, final Entry<SoftwareModule, List<SoftwareModuleMetadata>> entry) {
return new DmfSoftwareModule( return new DmfSoftwareModule(
entry.getKey().getId(), entry.getKey().getId(),
entry.getKey().getType().getKey(), entry.getKey().getType().getKey(),
entry.getKey().getVersion(), entry.getKey().getVersion(),
entry.getKey().isEncrypted() ? Boolean.TRUE : null, entry.getKey().isEncrypted() ? Boolean.TRUE : null,
convertArtifacts(target, entry.getKey().getArtifacts()), convertArtifacts(target, entry.getKey().getArtifacts()),
CollectionUtils.isEmpty(entry.getValue()) ? null :convertMetadata(entry.getValue())); CollectionUtils.isEmpty(entry.getValue()) ? null : convertMetadata(entry.getValue()));
} }
private List<DmfMetadata> convertMetadata(final List<SoftwareModuleMetadata> metadata) { private List<DmfMetadata> convertMetadata(final Map<String, String> metadata) {
return metadata.stream().map(md -> new DmfMetadata(md.getKey(), md.getValue())).toList(); return metadata.entrySet().stream().map(md -> new DmfMetadata(md.getKey(), md.getValue())).toList();
} }
private List<DmfArtifact> convertArtifacts(final Target target, final List<Artifact> localArtifacts) { private List<DmfArtifact> convertArtifacts(final Target target, final List<Artifact> localArtifacts) {
@@ -559,8 +553,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
return Collections.emptyList(); return Collections.emptyList();
} }
return localArtifacts.stream().map(localArtifact -> convertArtifact(target, localArtifact)) return localArtifacts.stream().map(localArtifact -> convertArtifact(target, localArtifact)).toList();
.toList();
} }
private DmfArtifact convertArtifact(final Target target, final Artifact localArtifact) { private DmfArtifact convertArtifact(final Target target, final Artifact localArtifact) {
@@ -582,19 +575,22 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
); );
} }
private Map<SoftwareModule, List<SoftwareModuleMetadata>> getSoftwareModulesWithMetadata(final DistributionSet distributionSet) { @SuppressWarnings("unchecked")
return distributionSet.getModules().stream().collect(Collectors.toMap(Function.identity(), this::getSoftwareModuleMetadata)); private Map<SoftwareModule, Map<String, String>> getSoftwareModulesWithMetadata(final DistributionSet distributionSet) {
} final Map<Long, Map<String, String>> softwareModuleMetadata =
softwareModuleManagement.findMetaDataBySoftwareModuleIdsAndTargetVisible(
private List<SoftwareModuleMetadata> getSoftwareModuleMetadata(final SoftwareModule module) { distributionSet.getModules().stream()
return softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible( .map(SoftwareModule::getId)
module.getId(), PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT)).getContent(); .toList());
return distributionSet.getModules().stream()
.collect(Collectors.toMap(
Function.identity(),
module -> softwareModuleMetadata.getOrDefault(module.getId(), Collections.emptyMap())));
} }
private void sendBatchUpdateMessage( private void sendBatchUpdateMessage(
final Map<String, ActionProperties> actions, final List<Target> targets, final Map<String, ActionProperties> actions, final List<Target> targets,
final Map<SoftwareModule, List<SoftwareModuleMetadata>> modules) { final Map<SoftwareModule, Map<String, String>> modules) {
final List<DmfTarget> dmfTargets = targets.stream() final List<DmfTarget> dmfTargets = targets.stream()
.filter(target -> IpUtil.isAmqpUri(target.getAddress())) .filter(target -> IpUtil.isAmqpUri(target.getAddress()))
.map(t -> convertToDmfTarget(t, actions.get(t.getControllerId()).getId())) .map(t -> convertToDmfTarget(t, actions.get(t.getControllerId()).getId()))

View File

@@ -51,7 +51,6 @@ import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionProperties; import org.eclipse.hawkbit.repository.model.ActionProperties;
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.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails; import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails;
@@ -351,7 +350,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
.flatMap(ds -> ds.getModules().stream()) .flatMap(ds -> ds.getModules().stream())
.map(SoftwareModule::getId) .map(SoftwareModule::getId)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
final Map<Long, List<SoftwareModuleMetadata>> getSoftwareModuleMetadata = final Map<Long, Map<String, String>> getSoftwareModuleMetadata =
allSmIds.isEmpty() ? Collections.emptyMap() : controllerManagement.findTargetVisibleMetaDataBySoftwareModuleId(allSmIds); allSmIds.isEmpty() ? Collections.emptyMap() : controllerManagement.findTargetVisibleMetaDataBySoftwareModuleId(allSmIds);
amqpMessageDispatcherService.sendMultiActionRequestToTarget(target, actions, module -> getSoftwareModuleMetadata.get(module.getId())); amqpMessageDispatcherService.sendMultiActionRequestToTarget(target, actions, module -> getSoftwareModuleMetadata.get(module.getId()));
@@ -373,11 +372,11 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
} }
} }
private Map<SoftwareModule, List<SoftwareModuleMetadata>> getSoftwareModulesWithMetadata(final DistributionSet distributionSet) { private Map<SoftwareModule, Map<String, String>> getSoftwareModulesWithMetadata(final DistributionSet distributionSet) {
final List<Long> smIds = distributionSet.getModules().stream().map(SoftwareModule::getId).toList(); final List<Long> smIds = distributionSet.getModules().stream().map(SoftwareModule::getId).toList();
final Map<Long, List<SoftwareModuleMetadata>> metadata = controllerManagement.findTargetVisibleMetaDataBySoftwareModuleId(smIds); final Map<Long, Map<String, String>> metadata = controllerManagement.findTargetVisibleMetaDataBySoftwareModuleId(smIds);
return distributionSet.getModules().stream().collect(Collectors.toMap( return distributionSet.getModules().stream().collect(Collectors.toMap(
Function.identity(), sm -> metadata.getOrDefault(sm.getId(), Collections.emptyList()))); Function.identity(), sm -> metadata.getOrDefault(sm.getId(), Collections.emptyMap())));
} }

View File

@@ -16,6 +16,7 @@ import java.io.InputStream;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import jakarta.validation.ValidationException; import jakarta.validation.ValidationException;
@@ -35,7 +36,6 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftwareModuleRestApi;
import org.eclipse.hawkbit.mgmt.rest.resource.mapper.MgmtSoftwareModuleMapper; import org.eclipse.hawkbit.mgmt.rest.resource.mapper.MgmtSoftwareModuleMapper;
import org.eclipse.hawkbit.mgmt.rest.resource.util.PagingUtility; import org.eclipse.hawkbit.mgmt.rest.resource.util.PagingUtility;
import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement; import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.SystemManagement;
@@ -43,7 +43,8 @@ import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.ArtifactUpload; import org.eclipse.hawkbit.repository.model.ArtifactUpload;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValue;
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValueCreate;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.rest.json.model.ResponseList; import org.eclipse.hawkbit.rest.json.model.ResponseList;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
@@ -67,7 +68,6 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
private final ArtifactUrlHandler artifactUrlHandler; private final ArtifactUrlHandler artifactUrlHandler;
private final MgmtSoftwareModuleMapper mgmtSoftwareModuleMapper; private final MgmtSoftwareModuleMapper mgmtSoftwareModuleMapper;
private final SystemManagement systemManagement; private final SystemManagement systemManagement;
private final EntityFactory entityFactory;
MgmtSoftwareModuleResource( MgmtSoftwareModuleResource(
final ArtifactManagement artifactManagement, final ArtifactManagement artifactManagement,
@@ -75,14 +75,13 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
final SoftwareModuleTypeManagement<? extends SoftwareModuleType> softwareModuleTypeManagement, final SoftwareModuleTypeManagement<? extends SoftwareModuleType> softwareModuleTypeManagement,
final ArtifactUrlHandler artifactUrlHandler, final ArtifactUrlHandler artifactUrlHandler,
final MgmtSoftwareModuleMapper mgmtSoftwareModuleMapper, final MgmtSoftwareModuleMapper mgmtSoftwareModuleMapper,
final SystemManagement systemManagement, final EntityFactory entityFactory) { final SystemManagement systemManagement) {
this.artifactManagement = artifactManagement; this.artifactManagement = artifactManagement;
this.softwareModuleManagement = softwareModuleManagement; this.softwareModuleManagement = softwareModuleManagement;
this.softwareModuleTypeManagement = softwareModuleTypeManagement; this.softwareModuleTypeManagement = softwareModuleTypeManagement;
this.artifactUrlHandler = artifactUrlHandler; this.artifactUrlHandler = artifactUrlHandler;
this.mgmtSoftwareModuleMapper = mgmtSoftwareModuleMapper; this.mgmtSoftwareModuleMapper = mgmtSoftwareModuleMapper;
this.systemManagement = systemManagement; this.systemManagement = systemManagement;
this.entityFactory = entityFactory;
} }
@Override @Override
@@ -138,12 +137,12 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
public ResponseEntity<MgmtArtifact> getArtifact(final Long softwareModuleId, final Long artifactId, final Boolean useArtifactUrlHandler) { public ResponseEntity<MgmtArtifact> getArtifact(final Long softwareModuleId, final Long artifactId, final Boolean useArtifactUrlHandler) {
final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, artifactId); final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, artifactId);
final MgmtArtifact response = MgmtSoftwareModuleMapper.toResponse(module.getArtifact(artifactId).get()); final MgmtArtifact response = MgmtSoftwareModuleMapper.toResponse(module.getArtifact(artifactId).orElseThrow());
if (!module.isDeleted()) { if (!module.isDeleted()) {
if (Boolean.TRUE.equals(useArtifactUrlHandler)) { if (Boolean.TRUE.equals(useArtifactUrlHandler)) {
MgmtSoftwareModuleMapper.addLinks(module.getArtifact(artifactId).get(), response, artifactUrlHandler, systemManagement); MgmtSoftwareModuleMapper.addLinks(module.getArtifact(artifactId).orElseThrow(), response, artifactUrlHandler, systemManagement);
} else { } else {
MgmtSoftwareModuleMapper.addLinks(module.getArtifact(artifactId).get(), response); MgmtSoftwareModuleMapper.addLinks(module.getArtifact(artifactId).orElseThrow(), response);
} }
} }
@@ -236,7 +235,7 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
@Override @Override
public void createMetadata(final Long softwareModuleId, final List<MgmtSoftwareModuleMetadata> metadataRest) { public void createMetadata(final Long softwareModuleId, final List<MgmtSoftwareModuleMetadata> metadataRest) {
softwareModuleManagement.createMetadata(MgmtSoftwareModuleMapper.fromRequestSwMetadata(entityFactory, softwareModuleId, metadataRest)); softwareModuleManagement.createMetadata(softwareModuleId, MgmtSoftwareModuleMapper.fromRequestSwMetadata(metadataRest));
} }
@Override @Override
@@ -244,27 +243,24 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
// check if software module exists otherwise throw exception immediately // check if software module exists otherwise throw exception immediately
findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null);
final List<SoftwareModuleMetadata> metadata = softwareModuleManagement.getMetadata(softwareModuleId); final Map<String, MetadataValue> metadata = softwareModuleManagement.getMetadata(softwareModuleId);
return ResponseEntity.ok(new PagedList<>(MgmtSoftwareModuleMapper.toResponseSwMetadata(metadata), metadata.size())); return ResponseEntity.ok(new PagedList<>(MgmtSoftwareModuleMapper.toResponseSwMetadata(metadata), metadata.size()));
} }
@Override @Override
public ResponseEntity<MgmtSoftwareModuleMetadata> getMetadataValue(final Long softwareModuleId, final String metadataKey) { public ResponseEntity<MgmtSoftwareModuleMetadata> getMetadataValue(final Long softwareModuleId, final String metadataKey) {
final SoftwareModuleMetadata findOne = softwareModuleManagement.getMetadata(softwareModuleId).stream() final MetadataValue metadataValue = softwareModuleManagement.getMetadata(softwareModuleId).get(metadataKey);
.filter(entry -> entry.getKey().equals(metadataKey)) if (metadataValue == null) {
.findFirst() throw new EntityNotFoundException("SoftwareModule metadata", softwareModuleId + ":" + metadataKey);
.orElseThrow(() -> new EntityNotFoundException("SoftwareModule metadata", softwareModuleId + ":" + metadataKey)); }
return ResponseEntity.ok(MgmtSoftwareModuleMapper.toResponseSwMetadata(findOne)); return ResponseEntity.ok(MgmtSoftwareModuleMapper.toResponseSwMetadata(metadataKey, metadataValue));
} }
@Override @Override
public void updateMetadata(final Long softwareModuleId, final String metadataKey, final MgmtSoftwareModuleMetadataBodyPut metadata) { public void updateMetadata(final Long softwareModuleId, final String metadataKey, final MgmtSoftwareModuleMetadataBodyPut metadata) {
softwareModuleManagement.updateMetadata( softwareModuleManagement.createMetadata(
entityFactory.softwareModuleMetadata() softwareModuleId, metadataKey, new MetadataValueCreate(metadata.getValue(), metadata.getTargetVisible()));
.update(softwareModuleId, metadataKey)
.value(metadata.getValue())
.targetVisible(metadata.getTargetVisible()));
} }
@Override @Override

View File

@@ -15,6 +15,8 @@ import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import jakarta.validation.ValidationException; import jakarta.validation.ValidationException;
@@ -28,7 +30,6 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftwareModuleRestApi;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftwareModuleTypeRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftwareModuleTypeRestApi;
import org.eclipse.hawkbit.mgmt.rest.resource.MgmtDownloadArtifactResource; import org.eclipse.hawkbit.mgmt.rest.resource.MgmtDownloadArtifactResource;
import org.eclipse.hawkbit.mgmt.rest.resource.MgmtSoftwareModuleResource; import org.eclipse.hawkbit.mgmt.rest.resource.MgmtSoftwareModuleResource;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement; import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.SystemManagement;
@@ -36,11 +37,11 @@ import org.eclipse.hawkbit.repository.artifact.urlhandler.ApiType;
import org.eclipse.hawkbit.repository.artifact.urlhandler.ArtifactUrl; import org.eclipse.hawkbit.repository.artifact.urlhandler.ArtifactUrl;
import org.eclipse.hawkbit.repository.artifact.urlhandler.ArtifactUrlHandler; import org.eclipse.hawkbit.repository.artifact.urlhandler.ArtifactUrlHandler;
import org.eclipse.hawkbit.repository.artifact.urlhandler.URLPlaceholder; import org.eclipse.hawkbit.repository.artifact.urlhandler.URLPlaceholder;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValue;
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValueCreate;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.rest.json.model.ResponseList; import org.eclipse.hawkbit.rest.json.model.ResponseList;
import org.springframework.hateoas.Link; import org.springframework.hateoas.Link;
@@ -59,17 +60,14 @@ public final class MgmtSoftwareModuleMapper {
this.softwareModuleTypeManagement = softwareModuleTypeManagement; this.softwareModuleTypeManagement = softwareModuleTypeManagement;
} }
public static List<SoftwareModuleMetadataCreate> fromRequestSwMetadata( public static Map<String, MetadataValueCreate> fromRequestSwMetadata(final Collection<MgmtSoftwareModuleMetadata> metadata) {
final EntityFactory entityFactory, final Long softwareModuleId, final Collection<MgmtSoftwareModuleMetadata> metadata) {
if (metadata == null) { if (metadata == null) {
return Collections.emptyList(); return Collections.emptyMap();
} }
return metadata.stream() return metadata.stream().collect(Collectors.toMap(
.map(metadataRest -> entityFactory.softwareModuleMetadata().create(softwareModuleId) MgmtSoftwareModuleMetadata::getKey,
.key(metadataRest.getKey()).value(metadataRest.getValue()) metadataRest -> new MetadataValueCreate(metadataRest.getValue(), metadataRest.isTargetVisible())));
.targetVisible(metadataRest.isTargetVisible()))
.toList();
} }
public List<SoftwareModuleManagement.Create> smFromRequest(final Collection<MgmtSoftwareModuleRequestBodyPost> smsRest) { public List<SoftwareModuleManagement.Create> smFromRequest(final Collection<MgmtSoftwareModuleRequestBodyPost> smsRest) {
@@ -88,17 +86,17 @@ public final class MgmtSoftwareModuleMapper {
return new ResponseList<>(softwareModules.stream().map(MgmtSoftwareModuleMapper::toResponse).toList()); return new ResponseList<>(softwareModules.stream().map(MgmtSoftwareModuleMapper::toResponse).toList());
} }
public static List<MgmtSoftwareModuleMetadata> toResponseSwMetadata(final Collection<SoftwareModuleMetadata> metadata) { public static List<MgmtSoftwareModuleMetadata> toResponseSwMetadata(final Map<String, ? extends MetadataValue> metadata) {
if (metadata == null) { if (metadata == null) {
return Collections.emptyList(); return Collections.emptyList();
} }
return metadata.stream().map(MgmtSoftwareModuleMapper::toResponseSwMetadata).toList(); return metadata.entrySet().stream().map(e -> toResponseSwMetadata(e.getKey(), e.getValue())).toList();
} }
public static MgmtSoftwareModuleMetadata toResponseSwMetadata(final SoftwareModuleMetadata metadata) { public static MgmtSoftwareModuleMetadata toResponseSwMetadata(final String key, final MetadataValue metadata) {
final MgmtSoftwareModuleMetadata metadataRest = new MgmtSoftwareModuleMetadata(); final MgmtSoftwareModuleMetadata metadataRest = new MgmtSoftwareModuleMetadata();
metadataRest.setKey(metadata.getKey()); metadataRest.setKey(key);
metadataRest.setValue(metadata.getValue()); metadataRest.setValue(metadata.getValue());
metadataRest.setTargetVisible(metadata.isTargetVisible()); metadataRest.setTargetVisible(metadata.isTargetVisible());
return metadataRest; return metadataRest;

View File

@@ -55,7 +55,8 @@ import org.eclipse.hawkbit.repository.model.ArtifactUpload;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValue;
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValueCreate;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.test.util.HashGeneratorUtils; import org.eclipse.hawkbit.repository.test.util.HashGeneratorUtils;
@@ -162,8 +163,7 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
final SoftwareModule module = findFirstModuleByType(testdataFactory.createDistributionSet("one"), osType).get(); final SoftwareModule module = findFirstModuleByType(testdataFactory.createDistributionSet("one"), osType).get();
for (int index = 0; index < totalMetadata; index++) { for (int index = 0; index < totalMetadata; index++) {
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(module.getId()) softwareModuleManagement.createMetadata(module.getId(), knownKeyPrefix + index, new MetadataValueCreate(knownValuePrefix + index));
.key(knownKeyPrefix + index).value(knownValuePrefix + index));
} }
mvc.perform(get(MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/metadata", mvc.perform(get(MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/metadata",
@@ -184,8 +184,7 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
final SoftwareModule module = findFirstModuleByType(testdataFactory.createDistributionSet("one"), osType).orElseThrow(); final SoftwareModule module = findFirstModuleByType(testdataFactory.createDistributionSet("one"), osType).orElseThrow();
for (int index = 0; index < totalMetadata; index++) { for (int index = 0; index < totalMetadata; index++) {
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(module.getId()) softwareModuleManagement.createMetadata(module.getId(), knownKeyPrefix + index, new MetadataValueCreate(knownValuePrefix + index));
.key(knownKeyPrefix + index).value(knownValuePrefix + index));
} }
mvc.perform(get(MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/metadata", mvc.perform(get(MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/metadata",
@@ -206,8 +205,7 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
final String knownKey = "knownKey"; final String knownKey = "knownKey";
final String knownValue = "knownValue"; final String knownValue = "knownValue";
final SoftwareModule module = findFirstModuleByType(testdataFactory.createDistributionSet("one"), osType).orElseThrow(); final SoftwareModule module = findFirstModuleByType(testdataFactory.createDistributionSet("one"), osType).orElseThrow();
softwareModuleManagement.updateMetadata( softwareModuleManagement.createMetadata(module.getId(), knownKey, new MetadataValueCreate(knownValue));
entityFactory.softwareModuleMetadata().create(module.getId()).key(knownKey).value(knownValue));
mvc.perform(get(MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/metadata/{metadataKey}", mvc.perform(get(MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/metadata/{metadataKey}",
module.getId(), knownKey)) module.getId(), knownKey))
@@ -1424,9 +1422,9 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
.andExpect(status().isCreated()); .andExpect(status().isCreated());
assertThat(softwareModuleManagement.getMetadata(sm.getId(), knownKey1)) assertThat(softwareModuleManagement.getMetadata(sm.getId(), knownKey1))
.as("Metadata key is wrong").extracting(SoftwareModuleMetadata::getValue).isEqualTo(knownValue1); .as("Metadata key is wrong").extracting(MetadataValue::getValue).isEqualTo(knownValue1);
assertThat(softwareModuleManagement.getMetadata(sm.getId(), knownKey2)) assertThat(softwareModuleManagement.getMetadata(sm.getId(), knownKey2))
.as("Metadata key is wrong").extracting(SoftwareModuleMetadata::getValue).isEqualTo(knownValue2); .as("Metadata key is wrong").extracting(MetadataValue::getValue).isEqualTo(knownValue2);
// verify quota enforcement // verify quota enforcement
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule(); final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule();
@@ -1443,7 +1441,6 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
// verify that the number of meta data entries has not changed (we cannot use the PAGE constant here as it tries to sort by ID) // verify that the number of meta data entries has not changed (we cannot use the PAGE constant here as it tries to sort by ID)
assertThat(softwareModuleManagement.getMetadata(sm.getId()).size()).isEqualTo(metaData1.length()); assertThat(softwareModuleManagement.getMetadata(sm.getId()).size()).isEqualTo(metaData1.length());
} }
/** /**
@@ -1457,19 +1454,18 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
final String updateValue = "valueForUpdate"; final String updateValue = "valueForUpdate";
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
softwareModuleManagement.updateMetadata( softwareModuleManagement.createMetadata(sm.getId(), knownKey, new MetadataValueCreate(knownValue));
entityFactory.softwareModuleMetadata().create(sm.getId()).key(knownKey).value(knownValue));
final JSONObject jsonObject = new JSONObject().put("key", knownKey) final JSONObject jsonObject = new JSONObject().put("key", knownKey)
.put("value", updateValue) .put("value", updateValue)
.put("targetVisible", true); .put("targetVisible", true);
mvc.perform(put("/rest/v1/softwaremodules/{swId}/metadata/{key}", sm.getId(), knownKey).accept( mvc.perform(put("/rest/v1/softwaremodules/{swId}/metadata/{key}", sm.getId(), knownKey)
MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON).content(jsonObject.toString())) .accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON).content(jsonObject.toString()))
.andDo(MockMvcResultPrinter.print()) .andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()); .andExpect(status().isOk());
final SoftwareModuleMetadata assertDS = softwareModuleManagement.getMetadata(sm.getId(), knownKey); final MetadataValue assertDS = softwareModuleManagement.getMetadata(sm.getId(), knownKey);
assertThat(assertDS.getValue()).as("Metadata is wrong").isEqualTo(updateValue); assertThat(assertDS.getValue()).as("Metadata is wrong").isEqualTo(updateValue);
assertThat(assertDS.isTargetVisible()).as("target visible is wrong").isTrue(); assertThat(assertDS.isTargetVisible()).as("target visible is wrong").isTrue();
} }
@@ -1484,8 +1480,7 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
final String knownValue = "knownValue"; final String knownValue = "knownValue";
final long smId = testdataFactory.createSoftwareModuleOs().getId(); final long smId = testdataFactory.createSoftwareModuleOs().getId();
softwareModuleManagement.updateMetadata( softwareModuleManagement.createMetadata(smId, knownKey, new MetadataValueCreate(knownValue));
entityFactory.softwareModuleMetadata().create(smId).key(knownKey).value(knownValue));
mvc.perform(delete("/rest/v1/softwaremodules/{swId}/metadata/{key}", smId, knownKey)) mvc.perform(delete("/rest/v1/softwaremodules/{swId}/metadata/{key}", smId, knownKey))
.andDo(MockMvcResultPrinter.print()) .andDo(MockMvcResultPrinter.print())
@@ -1505,8 +1500,7 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
final String knownValue = "knownValue"; final String knownValue = "knownValue";
final long smId = testdataFactory.createSoftwareModuleOs().getId(); final long smId = testdataFactory.createSoftwareModuleOs().getId();
softwareModuleManagement.updateMetadata( softwareModuleManagement.createMetadata(smId, knownKey, new MetadataValueCreate(knownValue));
entityFactory.softwareModuleMetadata().create(smId).key(knownKey).value(knownValue));
mvc.perform(delete("/rest/v1/softwaremodules/{swId}/metadata/XXX", smId, knownKey)) mvc.perform(delete("/rest/v1/softwaremodules/{swId}/metadata/XXX", smId, knownKey))
.andDo(MockMvcResultPrinter.print()) .andDo(MockMvcResultPrinter.print())

View File

@@ -32,7 +32,6 @@ import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.AutoConfirmationStatus; import org.eclipse.hawkbit.repository.model.AutoConfirmationStatus;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties; import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties;
@@ -69,14 +68,13 @@ public interface ControllerManagement {
Optional<SoftwareModule> getSoftwareModule(long moduleId); Optional<SoftwareModule> getSoftwareModule(long moduleId);
/** /**
* Retrieves {@link SoftwareModuleMetadata} where {@link SoftwareModuleMetadata#isTargetVisible()}. * Retrieves software module metadata where isTargetVisible.
* *
* @param moduleId of the {@link SoftwareModule} * @param moduleId of the {@link SoftwareModule}
* @return the map of software module id to {@link SoftwareModuleMetadata} with maximum size of * @return the map of software module id to software module metadata with maximum size of {@link RepositoryConstants#MAX_META_DATA_COUNT}
* {@link RepositoryConstants#MAX_META_DATA_COUNT}
*/ */
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Map<Long, List<SoftwareModuleMetadata>> findTargetVisibleMetaDataBySoftwareModuleId(@NotNull Collection<Long> moduleId); Map<Long, Map<String, String>> findTargetVisibleMetaDataBySoftwareModuleId(@NotNull Collection<Long> moduleId);
/** /**
* Simple addition of a new {@link ActionStatus} entry to the {@link Action}. No state changes. * Simple addition of a new {@link ActionStatus} entry to the {@link Action}. No state changes.

View File

@@ -12,7 +12,6 @@ package org.eclipse.hawkbit.repository;
import org.eclipse.hawkbit.repository.builder.ActionStatusBuilder; import org.eclipse.hawkbit.repository.builder.ActionStatusBuilder;
import org.eclipse.hawkbit.repository.builder.RolloutBuilder; import org.eclipse.hawkbit.repository.builder.RolloutBuilder;
import org.eclipse.hawkbit.repository.builder.RolloutGroupBuilder; import org.eclipse.hawkbit.repository.builder.RolloutGroupBuilder;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataBuilder;
import org.eclipse.hawkbit.repository.builder.TagBuilder; import org.eclipse.hawkbit.repository.builder.TagBuilder;
import org.eclipse.hawkbit.repository.builder.TargetBuilder; import org.eclipse.hawkbit.repository.builder.TargetBuilder;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder; import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
@@ -31,11 +30,6 @@ public interface EntityFactory {
*/ */
ActionStatusBuilder actionStatus(); ActionStatusBuilder actionStatus();
/**
* @return {@link SoftwareModuleMetadataBuilder} object
*/
SoftwareModuleMetadataBuilder softwareModuleMetadata();
/** /**
* @return {@link TagBuilder} object * @return {@link TagBuilder} object
*/ */

View File

@@ -9,14 +9,17 @@
*/ */
package org.eclipse.hawkbit.repository; package org.eclipse.hawkbit.repository;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.SoftwareModule;
/** /**
* Repository constants. * Repository constants.
*/ */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class RepositoryConstants { public final class RepositoryConstants {
/** /**
@@ -46,12 +49,7 @@ public final class RepositoryConstants {
/** /**
* Maximum number of metadata entries provided to controllers. * Maximum number of metadata entries provided to controllers.
* *
* @see SoftwareModuleMetadata#isTargetVisible() * @see SoftwareModule.MetadataValueCreate#isTargetVisible()
*/ */
public static final int MAX_META_DATA_COUNT = 50; public static final int MAX_META_DATA_COUNT = 50;
private RepositoryConstants() {
// Utility class.
}
} }

View File

@@ -10,7 +10,6 @@
package org.eclipse.hawkbit.repository; package org.eclipse.hawkbit.repository;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@@ -25,8 +24,6 @@ import lombok.Getter;
import lombok.ToString; import lombok.ToString;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions; import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataUpdate;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
@@ -34,9 +31,8 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.NamedEntity; import org.eclipse.hawkbit.repository.model.NamedEntity;
import org.eclipse.hawkbit.repository.model.NamedVersionedEntity; import org.eclipse.hawkbit.repository.model.NamedVersionedEntity;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; 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.repository.model.Type;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice; import org.springframework.data.domain.Slice;
@@ -53,27 +49,27 @@ public interface SoftwareModuleManagement<T extends SoftwareModule>
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. * Creates a list of software module meta-data entries.
* *
* @param metadata the meta-data entries to create * @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 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 EntityNotFoundException if software module with given ID does not exist
* @throws AssignmentQuotaExceededException if the maximum number of {@link SoftwareModuleMetadata} entries is exceeded for the addressed * @throws AssignmentQuotaExceededException if the maximum number of meta-data entries is exceeded for the addressed {@link SoftwareModule}
* {@link SoftwareModule}
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
void createMetadata(@NotNull @Valid Collection<SoftwareModuleMetadataCreate> metadata); void createMetadata(@NotNull Long id, @NotEmpty @Valid Map<String, ? extends MetadataValue> metadata);
/**
* 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)
List<SoftwareModuleMetadata> getMetadata(long id);
/** /**
* Finds all meta-data by the given software module id and key. * Finds all meta-data by the given software module id and key.
@@ -84,52 +80,30 @@ public interface SoftwareModuleManagement<T extends SoftwareModule>
* @throws EntityNotFoundException if software module with given ID does not exist ot the * @throws EntityNotFoundException if software module with given ID does not exist ot the
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
SoftwareModuleMetadata getMetadata(long id, String key); MetadataValue getMetadata(@NotNull Long id, @NotEmpty String key);
/** /**
* Finds all meta-data by the given software module id where {@link SoftwareModuleMetadata#isTargetVisible()}. * Finds all meta-data by the given software module id.
* *
* @param id the software module id to retrieve the meta-data from * @param id the software module id to retrieve the meta-data from
* @param pageable the page request to page the result
* @return a paged result of all meta-data entries for a given software module id * @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 * @throws EntityNotFoundException if software module with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleIdAndTargetVisible(long id, @NotNull Pageable pageable); Map<String, MetadataValue> getMetadata(@NotNull Long id);
/**
* Creates or updates a single software module meta-data entry.
*
* @param metadata the meta-data entry to create
* @return the updated or created software module meta-data entry
* @throws EntityAlreadyExistsException in case 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 {@link SoftwareModuleMetadata}
* entries is exceeded for the addressed {@link SoftwareModule}
*/
@PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
SoftwareModuleMetadata updateMetadata(@NotNull @Valid SoftwareModuleMetadataCreate metadata);
/**
* Updates a distribution set meta-data value if corresponding entry exists.
*
* @param update the meta-data entry to be updated
* @return the updated meta-data entry
* @throws EntityNotFoundException in case the meta-data entry does not exist and cannot be updated
*/
@PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
SoftwareModuleMetadata updateMetadata(@NotNull @Valid SoftwareModuleMetadataUpdate update);
/** /**
* Deletes a software module meta-data entry. * Deletes a software module meta-data entry.
* *
* @param id where meta-data has to be deleted * @param id where meta-data has to be deleted
* @param key of the meta-data element * @param key of the meta-data element
* @return true if really deleted, false if not
* @throws EntityNotFoundException if software module with given ID does not exist or the key is not found * @throws EntityNotFoundException if software module with given ID does not exist or the key is not found
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
void deleteMetadata(long id, @NotEmpty String key); void deleteMetadata(@NotNull Long id, @NotEmpty String key);
@PreAuthorize(SpringEvalExpressions.IS_SYSTEM_CODE)
Map<Long, Map<String, String>> findMetaDataBySoftwareModuleIdsAndTargetVisible(Collection<Long> ids);
/** /**
* Locks a software module. * Locks a software module.
@@ -198,9 +172,6 @@ public interface SoftwareModuleManagement<T extends SoftwareModule>
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
Slice<T> findByType(long typeId, @NotNull Pageable pageable); Slice<T> findByType(long typeId, @NotNull Pageable pageable);
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
Map<Long, List<SoftwareModuleMetadata>> findMetaDataBySoftwareModuleIdsAndTargetVisible(Collection<Long> moduleIds);
/** /**
* Returns count of all modules assigned to given {@link DistributionSet}. * Returns count of all modules assigned to given {@link DistributionSet}.
* *

View File

@@ -1,32 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.builder;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
/**
* Builder for {@link SoftwareModuleMetadata}.
*/
public interface SoftwareModuleMetadataBuilder {
/**
* @param softwareModuleId of the {@link SoftwareModule} the {@link SoftwareModuleMetadata} belongs to
* @param key of {@link SoftwareModuleMetadata#getKey()}
* @return builder instance
*/
SoftwareModuleMetadataUpdate update(long softwareModuleId, String key);
/**
* @param softwareModuleId of the {@link SoftwareModule} the {@link SoftwareModuleMetadata} belongs to
* @return builder instance
*/
SoftwareModuleMetadataCreate create(long softwareModuleId);
}

View File

@@ -1,49 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.builder;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
/**
* Builder to create a new {@link SoftwareModuleMetadata} entry. Defines all
* fields that can be set at creation time. Other fields are set by the
* repository automatically, e.g. {@link BaseEntity#getCreatedAt()}.
*/
public interface SoftwareModuleMetadataCreate {
/**
* @param key for {@link SoftwareModuleMetadata#getKey()}
* @return updated builder instance
*/
SoftwareModuleMetadataCreate key(@Size(min = 1, max =SoftwareModule.METADATA_KEY_MAX_SIZE) @NotNull String key);
/**
* @param value for {@link SoftwareModuleMetadata#getValue()}
* @return updated builder instance
*/
SoftwareModuleMetadataCreate value(@Size(max = SoftwareModule.METADATA_VALUE_MAX_SIZE) String value);
/**
* @param visible for {@link SoftwareModuleMetadata#isTargetVisible()}
* @return updated builder instance
*/
SoftwareModuleMetadataCreate targetVisible(Boolean visible);
/**
* @return peek on current state of {@link SoftwareModuleMetadata} in the
* builder
*/
SoftwareModuleMetadata build();
}

View File

@@ -1,35 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.builder;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
/**
* Builder to update an existing {@link SoftwareModuleMetadata} entry. Defines
* all fields that can be updated.
*/
public interface SoftwareModuleMetadataUpdate {
/**
* @param value for {@link SoftwareModuleMetadata#getValue()}
* @return updated builder instance
*/
SoftwareModuleMetadataUpdate value(@Size(min = 1, max = SoftwareModule.METADATA_VALUE_MAX_SIZE) @NotNull String value);
/**
* @param visible for {@link SoftwareModuleMetadata#isTargetVisible()}
* @return updated builder instance
*/
SoftwareModuleMetadataUpdate targetVisible(Boolean visible);
}

View File

@@ -12,6 +12,11 @@ package org.eclipse.hawkbit.repository.model;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.NoArgsConstructor;
/** /**
* Software package as sub element of a {@link DistributionSet}. * Software package as sub element of a {@link DistributionSet}.
*/ */
@@ -81,4 +86,28 @@ public interface SoftwareModule extends NamedVersionedEntity {
return getArtifacts().stream().filter(artifact -> artifact.getFilename().equalsIgnoreCase(fileName.trim())) return getArtifacts().stream().filter(artifact -> artifact.getFilename().equalsIgnoreCase(fileName.trim()))
.findAny(); .findAny();
} }
interface MetadataValue {
String getValue();
boolean isTargetVisible();
}
@NoArgsConstructor
@Data
class MetadataValueCreate implements MetadataValue {
@Size(max = METADATA_VALUE_MAX_SIZE)
private String value;
private boolean targetVisible;
public MetadataValueCreate(final String value) {
this(value, false);
}
public MetadataValueCreate(final String value, final boolean targetVisible) {
this.value = value;
this.targetVisible = targetVisible;
}
}
} }

View File

@@ -1,34 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.model;
import java.io.Serializable;
/**
* Metadata element of a {@link SoftwareModule}. The software module metadata is not only (key, value) pair (like the metadata of
* targets and distribution sets), but also contains the information if the metadata is visible for targets as part of {@link Action}.
*/
public interface SoftwareModuleMetadata extends Serializable {
/**
* @return the key
*/
String getKey();
/**
* @return the value
*/
String getValue();
/**
* @return <code>true</code> if element is visible for targets as part of {@link Action}.
*/
boolean isTargetVisible();
}

View File

@@ -1,24 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.builder;
/**
* Update implementation.
*/
public class GenericSoftwareModuleMetadataUpdate
extends AbstractSoftwareModuleMetadataUpdateCreate<SoftwareModuleMetadataUpdate>
implements SoftwareModuleMetadataUpdate {
public GenericSoftwareModuleMetadataUpdate(final long softwareModuleId, final String key) {
super.softwareModuleId = softwareModuleId;
this.key = key;
}
}

View File

@@ -149,17 +149,16 @@ public class SpecificationBuilder<T> {
if (comparison.getValue() == null) { if (comparison.getValue() == null) {
// map entry with key is null (doesn't exist) / is not null (exists) - use left join with on // map entry with key is null (doesn't exist) / is not null (exists) - use left join with on
return switch (op) { return switch (op) {
case EQ -> cb.isNull(pathResolver.getJoinOn(attribute, split[1])); case EQ -> cb.isNull(toMapValuePath(pathResolver.getJoinOn(attribute, split[1])));
case NE -> cb.isNotNull(pathResolver.getJoinOn(attribute, split[1])); case NE -> cb.isNotNull(toMapValuePath(pathResolver.getJoinOn(attribute, split[1])));
default -> throw new RSQLParameterSyntaxException( default -> throw new RSQLParameterSyntaxException(
String.format("Operator %s is not supported for map fields with value null", op)); String.format("Operator %s is not supported for map fields with value null", op));
}; };
} else { } else {
final MapJoin<?, ?, ?> mapPath = (MapJoin<?, ?, ?>) pathResolver.getPath(attribute); final MapJoin<?, ?, ?> mapPath = (MapJoin<?, ?, ?>) pathResolver.getPath(attribute);
final Path<String> valuePath = (Path<String>) mapPath.value();
return isNot(op) return isNot(op)
? compare(comparison, pathResolver.getJoinOnInner(attribute, split[1])) ? compare(comparison, toMapValuePath(pathResolver.getJoinOnInner(attribute, split[1])))
: cb.and(equal(mapPath.key(), split[1]), compare(comparison, valuePath)); : cb.and(equal(mapPath.key(), split[1]), compare(comparison, toMapValuePath(mapPath)));
} }
} else if (attribute instanceof SetAttribute<?, ?> setAttribute) { } else if (attribute instanceof SetAttribute<?, ?> setAttribute) {
if (split.length < 2 || ObjectUtils.isEmpty(split[1])) { if (split.length < 2 || ObjectUtils.isEmpty(split[1])) {
@@ -185,6 +184,12 @@ public class SpecificationBuilder<T> {
} }
} }
@SuppressWarnings("unchecked")
private static Path<String> toMapValuePath(final Path<?> mapJoin) {
final Path<?> valuePath = ((MapJoin<?, ?, ?>) mapJoin).value();
return valuePath.getJavaType() == String.class ? (Path<String>) valuePath : valuePath.get("value");
}
private Predicate compare(final Comparison comparison, final Path<?> fieldPath) { private Predicate compare(final Comparison comparison, final Path<?> fieldPath) {
final List<Object> values = getValues(comparison, fieldPath.getJavaType()); final List<Object> values = getValues(comparison, fieldPath.getJavaType());
final Object firstValue = values.get(0); final Object firstValue = values.get(0);

View File

@@ -343,8 +343,7 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
final Predicate mapPredicate = mapToMapPredicate(fieldPath, queryPath); final Predicate mapPredicate = mapToMapPredicate(fieldPath, queryPath);
final Predicate valuePredicate = addOperatorPredicate(node, queryPath.getEnumValue().isMap() ? (Path)toMapValuePath(fieldPath) : fieldPath, transformedValues, value, queryPath);
final Predicate valuePredicate = addOperatorPredicate(node, fieldPath, transformedValues, value, queryPath);
return toSingleList(mapPredicate != null ? cb.and(mapPredicate, valuePredicate) : valuePredicate); return toSingleList(mapPredicate != null ? cb.and(mapPredicate, valuePredicate) : valuePredicate);
} }
@@ -380,6 +379,16 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
} }
} }
@SuppressWarnings("unchecked")
private static Path<String> toMapValuePath(final Path<?> fieldPath) {
final Path<?> mapValuePath = ((MapJoin<?, ?, ?>) pathOfString(fieldPath)).value();
if (mapValuePath.getJavaType() == String.class) {
return (Path<String>) mapValuePath;
} else {
return mapValuePath.get("value");
}
}
private Predicate getOutPredicate( private Predicate getOutPredicate(
final List<Object> transformedValues, final List<Object> transformedValues,
final QueryPath queryPath, final Path<Object> fieldPath) { final QueryPath queryPath, final Path<Object> fieldPath) {
@@ -496,8 +505,7 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
private Expression<String> getExpressionToCompare(final Path innerFieldPath, final A enumField) { private Expression<String> getExpressionToCompare(final Path innerFieldPath, final A enumField) {
if (enumField.isMap()){ if (enumField.isMap()){
// Currently we support only string key. So below cast is safe. return toMapValuePath(innerFieldPath);
return (Expression<String>) (((MapJoin<?, ?, ?>) pathOfString(innerFieldPath)).value());
} else { } else {
return pathOfString(innerFieldPath); return pathOfString(innerFieldPath);
} }

View File

@@ -129,7 +129,7 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
if (values.get(0) == null) { if (values.get(0) == null) {
// IS operator for maps and null value is treated as doesn't exist correspondingly // IS operator for maps and null value is treated as doesn't exist correspondingly
((PluralJoin<?, ?, ?>) fieldPath).on(toMapEntryKeyPredicate(queryPath, fieldPath)); ((PluralJoin<?, ?, ?>) fieldPath).on(toMapEntryKeyPredicate(queryPath, fieldPath));
return cb.isNull(fieldPath); return cb.isNull(toMapValuePath(fieldPath));
} }
} else if (node.getOperator() == NOT) { } else if (node.getOperator() == NOT) {
if (values.size() != 1) { if (values.size() != 1) {
@@ -139,10 +139,10 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
((PluralJoin<?, ?, ?>) fieldPath).on(toMapEntryKeyPredicate(queryPath, fieldPath)); ((PluralJoin<?, ?, ?>) fieldPath).on(toMapEntryKeyPredicate(queryPath, fieldPath));
if (values.get(0) == null) { if (values.get(0) == null) {
// special handling of "exists" // special handling of "exists"
return cb.isNotNull(fieldPath); return cb.isNotNull(toMapValuePath(fieldPath));
} else { } else {
// special handling or "not equal" or null (same as != but with possible optimized join - no subquery) // special handling or "not equal" or null (same as != but with possible optimized join - no subquery)
return toNotEqualToPredicate(queryPath, fieldPath, values.get(0)); return toNotEqualToPredicate(queryPath, toMapValuePath(fieldPath), values.get(0));
} }
} }
mapEntryKeyPredicate = toMapEntryKeyPredicate(queryPath, fieldPath); mapEntryKeyPredicate = toMapEntryKeyPredicate(queryPath, fieldPath);
@@ -150,11 +150,21 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
mapEntryKeyPredicate = null; mapEntryKeyPredicate = null;
} }
final Predicate valuePredicate = toOperatorAndValuePredicate(node, queryPath, fieldPath, values); final Predicate valuePredicate = toOperatorAndValuePredicate(node, queryPath, queryPath.getEnumValue().isMap() ? toMapValuePath(fieldPath) : fieldPath, values);
return mapEntryKeyPredicate == null ? valuePredicate : cb.and(mapEntryKeyPredicate, valuePredicate); return mapEntryKeyPredicate == null ? valuePredicate : cb.and(mapEntryKeyPredicate, valuePredicate);
} }
@SuppressWarnings("unchecked")
private static Path<String> toMapValuePath(final Path<?> fieldPath) {
final Path<?> mapValuePath = ((MapJoin<?, ?, ?>) pathOfString(fieldPath)).value();
if (mapValuePath.getJavaType() == String.class) {
return (Path<String>) mapValuePath;
} else {
return mapValuePath.get("value");
}
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Predicate toMapEntryKeyPredicate(final QueryPath queryPath, final Path<?> fieldPath) { private Predicate toMapEntryKeyPredicate(final QueryPath queryPath, final Path<?> fieldPath) {
final String[] graph = queryPath.getJpaPath(); final String[] graph = queryPath.getJpaPath();
@@ -341,7 +351,7 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
private Path<String> getExpressionToCompare(final A enumField, final Path fieldPath) { private Path<String> getExpressionToCompare(final A enumField, final Path fieldPath) {
if (enumField.isMap()) { if (enumField.isMap()) {
// Currently we support only string key. So below cast is safe. // Currently we support only string key. So below cast is safe.
return (Path<String>) (((MapJoin<?, ?, ?>) fieldPath).value()); return toMapValuePath(fieldPath);
} else { } else {
return pathOfString(fieldPath); return pathOfString(fieldPath);
} }

View File

@@ -13,7 +13,6 @@ import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.builder.ActionStatusBuilder; import org.eclipse.hawkbit.repository.builder.ActionStatusBuilder;
import org.eclipse.hawkbit.repository.builder.RolloutBuilder; import org.eclipse.hawkbit.repository.builder.RolloutBuilder;
import org.eclipse.hawkbit.repository.builder.RolloutGroupBuilder; import org.eclipse.hawkbit.repository.builder.RolloutGroupBuilder;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataBuilder;
import org.eclipse.hawkbit.repository.builder.TagBuilder; import org.eclipse.hawkbit.repository.builder.TagBuilder;
import org.eclipse.hawkbit.repository.builder.TargetBuilder; import org.eclipse.hawkbit.repository.builder.TargetBuilder;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder; import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
@@ -33,19 +32,16 @@ public class JpaEntityFactory implements EntityFactory {
private final TargetBuilder targetBuilder; private final TargetBuilder targetBuilder;
private final TargetTypeBuilder targetTypeBuilder; private final TargetTypeBuilder targetTypeBuilder;
private final TargetFilterQueryBuilder targetFilterQueryBuilder; private final TargetFilterQueryBuilder targetFilterQueryBuilder;
private final SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder;
private final RolloutBuilder rolloutBuilder; private final RolloutBuilder rolloutBuilder;
@SuppressWarnings("java:S107") @SuppressWarnings("java:S107")
public JpaEntityFactory( public JpaEntityFactory(
final TargetBuilder targetBuilder, final TargetTypeBuilder targetTypeBuilder, final TargetBuilder targetBuilder, final TargetTypeBuilder targetTypeBuilder,
final TargetFilterQueryBuilder targetFilterQueryBuilder, final TargetFilterQueryBuilder targetFilterQueryBuilder,
final SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder,
final RolloutBuilder rolloutBuilder) { final RolloutBuilder rolloutBuilder) {
this.targetBuilder = targetBuilder; this.targetBuilder = targetBuilder;
this.targetTypeBuilder = targetTypeBuilder; this.targetTypeBuilder = targetTypeBuilder;
this.targetFilterQueryBuilder = targetFilterQueryBuilder; this.targetFilterQueryBuilder = targetFilterQueryBuilder;
this.softwareModuleMetadataBuilder = softwareModuleMetadataBuilder;
this.rolloutBuilder = rolloutBuilder; this.rolloutBuilder = rolloutBuilder;
} }
@Override @Override
@@ -53,11 +49,6 @@ public class JpaEntityFactory implements EntityFactory {
return new JpaActionStatusBuilder(); return new JpaActionStatusBuilder();
} }
@Override
public SoftwareModuleMetadataBuilder softwareModuleMetadata() {
return softwareModuleMetadataBuilder;
}
@Override @Override
public TagBuilder<Tag> tag() { public TagBuilder<Tag> tag() {
return (TagBuilder)new JpaTagBuilder(); return (TagBuilder)new JpaTagBuilder();

View File

@@ -44,7 +44,6 @@ import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryptionSecr
import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryptionService; import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryptionService;
import org.eclipse.hawkbit.repository.autoassign.AutoAssignExecutor; import org.eclipse.hawkbit.repository.autoassign.AutoAssignExecutor;
import org.eclipse.hawkbit.repository.builder.RolloutBuilder; import org.eclipse.hawkbit.repository.builder.RolloutBuilder;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataBuilder;
import org.eclipse.hawkbit.repository.builder.TargetBuilder; import org.eclipse.hawkbit.repository.builder.TargetBuilder;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder; import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
import org.eclipse.hawkbit.repository.builder.TargetTypeBuilder; import org.eclipse.hawkbit.repository.builder.TargetTypeBuilder;
@@ -60,7 +59,6 @@ import org.eclipse.hawkbit.repository.jpa.autocleanup.AutoActionCleanup;
import org.eclipse.hawkbit.repository.jpa.autocleanup.AutoCleanupScheduler; import org.eclipse.hawkbit.repository.jpa.autocleanup.AutoCleanupScheduler;
import org.eclipse.hawkbit.repository.jpa.autocleanup.CleanupTask; import org.eclipse.hawkbit.repository.jpa.autocleanup.CleanupTask;
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutBuilder; import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleMetadataBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetBuilder; import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetFilterQueryBuilder; import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetFilterQueryBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetTypeBuilder; import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetTypeBuilder;
@@ -70,7 +68,6 @@ import org.eclipse.hawkbit.repository.jpa.event.JpaEventEntityManager;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitDefaultServiceExecutor; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitDefaultServiceExecutor;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper; import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper;
import org.eclipse.hawkbit.repository.jpa.management.JpaSoftwareModuleManagement;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact; import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
@@ -323,12 +320,6 @@ public class JpaRepositoryConfiguration {
return new JpaTargetTypeBuilder(dsTypeManagement); return new JpaTargetTypeBuilder(dsTypeManagement);
} }
@Bean
SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder(
final JpaSoftwareModuleManagement softwareModuleManagement) {
return new JpaSoftwareModuleMetadataBuilder(softwareModuleManagement);
}
/** /**
* @param distributionSetManagement for loading {@link Rollout#getDistributionSet()} * @param distributionSetManagement for loading {@link Rollout#getDistributionSet()}
* @return RolloutBuilder bean * @return RolloutBuilder bean
@@ -461,11 +452,9 @@ public class JpaRepositoryConfiguration {
EntityFactory entityFactory( EntityFactory entityFactory(
final TargetBuilder targetBuilder, final TargetTypeBuilder targetTypeBuilder, final TargetBuilder targetBuilder, final TargetTypeBuilder targetTypeBuilder,
final TargetFilterQueryBuilder targetFilterQueryBuilder, final TargetFilterQueryBuilder targetFilterQueryBuilder,
final SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder,
final RolloutBuilder rolloutBuilder) { final RolloutBuilder rolloutBuilder) {
return new JpaEntityFactory( return new JpaEntityFactory(
targetBuilder, targetTypeBuilder, targetFilterQueryBuilder, targetBuilder, targetTypeBuilder, targetFilterQueryBuilder, rolloutBuilder);
softwareModuleMetadataBuilder, rolloutBuilder);
} }
/** /**

View File

@@ -1,39 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.builder.GenericSoftwareModuleMetadataUpdate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataBuilder;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataUpdate;
import org.eclipse.hawkbit.repository.jpa.management.JpaSoftwareModuleManagement;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
/**
* Builder implementation for {@link SoftwareModuleMetadata}.
*/
public class JpaSoftwareModuleMetadataBuilder implements SoftwareModuleMetadataBuilder {
private final JpaSoftwareModuleManagement softwareModuleManagement;
public JpaSoftwareModuleMetadataBuilder(final JpaSoftwareModuleManagement softwareModuleManagement) {
this.softwareModuleManagement = softwareModuleManagement;
}
@Override
public SoftwareModuleMetadataUpdate update(final long softwareModuleId, final String key) {
return new GenericSoftwareModuleMetadataUpdate(softwareModuleId, key);
}
@Override
public SoftwareModuleMetadataCreate create(final long softwareModuleId) {
return new JpaSoftwareModuleMetadataCreate(softwareModuleId, softwareModuleManagement);
}
}

View File

@@ -1,44 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.builder.AbstractSoftwareModuleMetadataUpdateCreate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.management.JpaSoftwareModuleManagement;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
/**
* Create/build implementation.
*/
public class JpaSoftwareModuleMetadataCreate
extends AbstractSoftwareModuleMetadataUpdateCreate<SoftwareModuleMetadataCreate> implements SoftwareModuleMetadataCreate {
private final JpaSoftwareModuleManagement softwareModuleManagement;
JpaSoftwareModuleMetadataCreate(final long softwareModuleId, final JpaSoftwareModuleManagement softwareModuleManagement) {
this.softwareModuleManagement = softwareModuleManagement;
this.softwareModuleId = softwareModuleId;
}
@Override
public JpaSoftwareModuleMetadata build() {
final JpaSoftwareModule module = softwareModuleManagement.get(softwareModuleId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, softwareModuleId));
if (key == null) {
new JpaSoftwareModuleMetadata(key, module, value, isTargetVisible().orElse(false));
}
return new JpaSoftwareModuleMetadata(key, module, value, isTargetVisible().orElse(false));
}
}

View File

@@ -54,6 +54,7 @@ import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
import org.eclipse.hawkbit.repository.QuotaManagement; import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryConstants; import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.RepositoryProperties; import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.TargetTypeManagement; import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.UpdateMode; import org.eclipse.hawkbit.repository.UpdateMode;
@@ -82,7 +83,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.ql.EntityMatcher; import org.eclipse.hawkbit.repository.jpa.ql.EntityMatcher;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository; import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository; import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleMetadataRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository; import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository; import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications; import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
@@ -96,7 +96,6 @@ import org.eclipse.hawkbit.repository.model.AutoConfirmationStatus;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetType; import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
@@ -143,7 +142,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
private final DeploymentManagement deploymentManagement; private final DeploymentManagement deploymentManagement;
private final ConfirmationManagement confirmationManagement; private final ConfirmationManagement confirmationManagement;
private final SoftwareModuleRepository softwareModuleRepository; private final SoftwareModuleRepository softwareModuleRepository;
private final SoftwareModuleMetadataRepository softwareModuleMetadataRepository; private final SoftwareModuleManagement<? extends SoftwareModule> softwareModuleManagement;
private final DistributionSetManagement<? extends DistributionSet> distributionSetManagement; private final DistributionSetManagement<? extends DistributionSet> distributionSetManagement;
private final TenantConfigurationManagement tenantConfigurationManagement; private final TenantConfigurationManagement tenantConfigurationManagement;
private final ControllerPollProperties controllerPollProperties; private final ControllerPollProperties controllerPollProperties;
@@ -162,7 +161,8 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
final RepositoryProperties repositoryProperties, final RepositoryProperties repositoryProperties,
final TargetRepository targetRepository, final TargetTypeManagement targetTypeManagement, final TargetRepository targetRepository, final TargetTypeManagement targetTypeManagement,
final DeploymentManagement deploymentManagement, final ConfirmationManagement confirmationManagement, final DeploymentManagement deploymentManagement, final ConfirmationManagement confirmationManagement,
final SoftwareModuleRepository softwareModuleRepository, final SoftwareModuleMetadataRepository softwareModuleMetadataRepository, final SoftwareModuleRepository softwareModuleRepository,
final SoftwareModuleManagement<? extends SoftwareModule> softwareModuleManagement,
final DistributionSetManagement<? extends DistributionSet> distributionSetManagement, final DistributionSetManagement<? extends DistributionSet> distributionSetManagement,
final TenantConfigurationManagement tenantConfigurationManagement, final ControllerPollProperties controllerPollProperties, final TenantConfigurationManagement tenantConfigurationManagement, final ControllerPollProperties controllerPollProperties,
final PlatformTransactionManager txManager, final EntityFactory entityFactory, final EntityManager entityManager, final PlatformTransactionManager txManager, final EntityFactory entityFactory, final EntityManager entityManager,
@@ -176,7 +176,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
this.deploymentManagement = deploymentManagement; this.deploymentManagement = deploymentManagement;
this.confirmationManagement = confirmationManagement; this.confirmationManagement = confirmationManagement;
this.softwareModuleRepository = softwareModuleRepository; this.softwareModuleRepository = softwareModuleRepository;
this.softwareModuleMetadataRepository = softwareModuleMetadataRepository; this.softwareModuleManagement = softwareModuleManagement;
this.distributionSetManagement = distributionSetManagement; this.distributionSetManagement = distributionSetManagement;
this.tenantConfigurationManagement = tenantConfigurationManagement; this.tenantConfigurationManagement = tenantConfigurationManagement;
this.controllerPollProperties = controllerPollProperties; this.controllerPollProperties = controllerPollProperties;
@@ -281,11 +281,8 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
} }
@Override @Override
public Map<Long, List<SoftwareModuleMetadata>> findTargetVisibleMetaDataBySoftwareModuleId(final Collection<Long> moduleId) { public Map<Long, Map<String, String>> findTargetVisibleMetaDataBySoftwareModuleId(final Collection<Long> moduleId) {
return softwareModuleMetadataRepository return systemSecurityContext.runAsSystem(() -> softwareModuleManagement.findMetaDataBySoftwareModuleIdsAndTargetVisible(moduleId));
.findBySoftwareModuleIdInAndTargetVisible(moduleId, true, PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT))
.getContent().stream()
.collect(Collectors.groupingBy(o -> (Long) o[0], Collectors.mapping(o -> (SoftwareModuleMetadata) o[1], Collectors.toList())));
} }
@Override @Override

View File

@@ -15,37 +15,30 @@ import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_M
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; 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;
import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.QuotaManagement; import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.SoftwareModuleFields; import org.eclipse.hawkbit.repository.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement; import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryptionService; import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryptionService;
import org.eclipse.hawkbit.repository.builder.GenericSoftwareModuleMetadataUpdate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataUpdate;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.LockedException; import org.eclipse.hawkbit.repository.exception.LockedException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper; import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController; import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleMetadataCreate;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule.JpaMetadataValue;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata_;
import org.eclipse.hawkbit.repository.jpa.model.SwMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder; import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository; import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleMetadataRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository; import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository; import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleSpecification; import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleSpecification;
@@ -53,12 +46,12 @@ import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
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.SoftwareModuleMetadata; 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;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice; import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
@@ -71,13 +64,13 @@ 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 AbstractJpaRepositoryManagement<JpaSoftwareModule, SoftwareModuleManagement.Create, SoftwareModuleManagement.Update, SoftwareModuleRepository, SoftwareModuleFields> extends
AbstractJpaRepositoryManagement<JpaSoftwareModule, SoftwareModuleManagement.Create, SoftwareModuleManagement.Update, SoftwareModuleRepository, SoftwareModuleFields>
implements SoftwareModuleManagement<JpaSoftwareModule> { implements SoftwareModuleManagement<JpaSoftwareModule> {
protected static final String SOFTWARE_MODULE_METADATA = "SoftwareModuleMetadata"; protected static final String SOFTWARE_MODULE_METADATA = "SoftwareModuleMetadata";
private final DistributionSetRepository distributionSetRepository; private final DistributionSetRepository distributionSetRepository;
private final SoftwareModuleMetadataRepository softwareModuleMetadataRepository;
private final SoftwareModuleTypeRepository softwareModuleTypeRepository; private final SoftwareModuleTypeRepository softwareModuleTypeRepository;
private final ArtifactManagement artifactManagement; private final ArtifactManagement artifactManagement;
private final QuotaManagement quotaManagement; private final QuotaManagement quotaManagement;
@@ -86,12 +79,10 @@ public class JpaSoftwareModuleManagement
final SoftwareModuleRepository softwareModuleRepository, final SoftwareModuleRepository softwareModuleRepository,
final EntityManager entityManager, final EntityManager entityManager,
final DistributionSetRepository distributionSetRepository, final DistributionSetRepository distributionSetRepository,
final SoftwareModuleMetadataRepository softwareModuleMetadataRepository,
final SoftwareModuleTypeRepository softwareModuleTypeRepository, final SoftwareModuleTypeRepository softwareModuleTypeRepository,
final ArtifactManagement artifactManagement, final QuotaManagement quotaManagement) { final ArtifactManagement artifactManagement, final QuotaManagement quotaManagement) {
super(softwareModuleRepository, entityManager); super(softwareModuleRepository, entityManager);
this.distributionSetRepository = distributionSetRepository; this.distributionSetRepository = distributionSetRepository;
this.softwareModuleMetadataRepository = softwareModuleMetadataRepository;
this.softwareModuleTypeRepository = softwareModuleTypeRepository; this.softwareModuleTypeRepository = softwareModuleTypeRepository;
this.artifactManagement = artifactManagement; this.artifactManagement = artifactManagement;
this.quotaManagement = quotaManagement; this.quotaManagement = quotaManagement;
@@ -176,86 +167,97 @@ public class JpaSoftwareModuleManagement
@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))
public void createMetadata(final Collection<SoftwareModuleMetadataCreate> create) { public void createMetadata(final Long id, final String key, final MetadataValue value) {
// group by software module id to minimize database access final JpaSoftwareModule softwareModule = jpaRepository
create.stream() .findById(id)
.map(JpaSoftwareModuleMetadataCreate.class::cast) .orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id));
.collect(Collectors.groupingBy(JpaSoftwareModuleMetadataCreate::getSoftwareModuleId)) final Map<String, JpaMetadataValue> metadataValueMap = softwareModule.getMetadata();
.forEach((id, createsForSoftwareModule) -> { final JpaMetadataValue existingValue = metadataValueMap.get(key);
assertSoftwareModuleExists(id); if (existingValue == null) {
assertMetadataQuota(id, createsForSoftwareModule.size()); assertMetadataQuota(metadataValueMap.size() + 1L);
}
// touch to update revision and last modified timestamp final JpaMetadataValue jpaMetadataValue = existingValue == null ?
JpaManagementHelper.touch( new JpaMetadataValue() : existingValue;
entityManager, jpaRepository, jpaRepository.findById(id).orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id))); if (ObjectCopyUtil.copy(value, jpaMetadataValue, true, UnaryOperator.identity())) {
createsForSoftwareModule.forEach(this::saveMetadata); metadataValueMap.put(key, jpaMetadataValue);
}); jpaRepository.save(softwareModule);
} }
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public List<SoftwareModuleMetadata> getMetadata(final long id) {
assertSoftwareModuleExists(id);
return (List) softwareModuleMetadataRepository.findAll(metadataBySoftwareModuleIdSpec(id));
}
@Override
public SoftwareModuleMetadata getMetadata(final long id, final String key) {
assertSoftwareModuleExists(id);
return findMetadata(id, key).orElseThrow(() -> new EntityNotFoundException(SOFTWARE_MODULE_METADATA, id + ":" + key));
}
@Override
public Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleIdAndTargetVisible(final long id, final Pageable pageable) {
assertSoftwareModuleExists(id);
return JpaManagementHelper.convertPage(softwareModuleMetadataRepository.findBySoftwareModuleIdAndTargetVisible(
id, true, PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT)), pageable);
} }
@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))
public SoftwareModuleMetadata updateMetadata(final SoftwareModuleMetadataCreate c) { public void createMetadata(final Long id, final Map<String, ? extends MetadataValue> metadata) {
final JpaSoftwareModuleMetadataCreate create = (JpaSoftwareModuleMetadataCreate) c; final JpaSoftwareModule softwareModule = jpaRepository
final Long id = create.getSoftwareModuleId(); .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);
}
}
assertSoftwareModuleExists(id); @Override
assertMetadataQuota(id, 1); 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;
}
}
// touch to update revision and last modified timestamp @Override
JpaManagementHelper.touch( public Map<String, MetadataValue> getMetadata(final Long id) {
entityManager, jpaRepository, jpaRepository.findById(id).orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id))); return jpaRepository
return saveMetadata(create); .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 @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))
public SoftwareModuleMetadata updateMetadata(final SoftwareModuleMetadataUpdate u) { public void deleteMetadata(final Long id, final String key) {
final GenericSoftwareModuleMetadataUpdate update = (GenericSoftwareModuleMetadataUpdate) u; final JpaSoftwareModule softwareModule = jpaRepository
.findById(id)
// check if exists otherwise throw entity not found exception .orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id));
final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) findMetadata( final Map<String, JpaMetadataValue> metadataValueMap = softwareModule.getMetadata();
update.getSoftwareModuleId(), update.getKey()) if (!metadataValueMap.containsKey(key)) {
.orElseThrow(() -> new EntityNotFoundException(SOFTWARE_MODULE_METADATA, update.getSoftwareModuleId() + ":" + update.getKey())); throw new EntityNotFoundException(SOFTWARE_MODULE_METADATA, id + ":" + key);
}
update.getValue().ifPresent(metadata::setValue); metadataValueMap.remove(key);
update.isTargetVisible().ifPresent(metadata::setTargetVisible); jpaRepository.save(softwareModule);
JpaManagementHelper.touch(entityManager, jpaRepository, metadata.getSoftwareModule());
return softwareModuleMetadataRepository.save(metadata);
} }
// called only with 'system code' access, so no need to check access control
@Override @Override
@Transactional public Map<Long, Map<String, String>> findMetaDataBySoftwareModuleIdsAndTargetVisible(final Collection<Long> ids) {
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY)) return jpaRepository.findVisibleMetadataByModuleIds(ids)
public void deleteMetadata(final long id, final String key) { .stream()
final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) findMetadata(id, key) .collect(Collectors.groupingBy(
.orElseThrow(() -> new EntityNotFoundException(SOFTWARE_MODULE_METADATA, id + ":" + key)); entry -> (Long) entry[0],
Collectors.toMap(
JpaManagementHelper.touch(entityManager, jpaRepository, metadata.getSoftwareModule()); entry -> (String) entry[1],
softwareModuleMetadataRepository.deleteById(metadata.getId()); entry -> (String) entry[2],
(existing, replacement) -> existing, // in case of duplicates, keep the first one
HashMap::new)));
} }
@Override @Override
@@ -340,14 +342,6 @@ public class JpaSoftwareModuleManagement
); );
} }
@Override
public Map<Long, List<SoftwareModuleMetadata>> findMetaDataBySoftwareModuleIdsAndTargetVisible(final Collection<Long> moduleIds) {
return softwareModuleMetadataRepository
.findBySoftwareModuleIdInAndTargetVisible(moduleIds, true, PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT))
.getContent().stream()
.collect(Collectors.groupingBy(o -> (Long) o[0], Collectors.mapping(o -> (SoftwareModuleMetadata) o[1], Collectors.toList())));
}
@Override @Override
public long countByAssignedTo(final long distributionSetId) { public long countByAssignedTo(final long distributionSetId) {
assertDistributionSetExists(distributionSetId); assertDistributionSetExists(distributionSetId);
@@ -355,10 +349,6 @@ public class JpaSoftwareModuleManagement
return jpaRepository.count(SoftwareModuleSpecification.byAssignedToDs(distributionSetId)); return jpaRepository.count(SoftwareModuleSpecification.byAssignedToDs(distributionSetId));
} }
private static Specification<JpaSoftwareModuleMetadata> metadataBySoftwareModuleIdSpec(final long id) {
return (root, query, cb) -> cb.equal(root.get(JpaSoftwareModuleMetadata_.softwareModule).get(AbstractJpaBaseEntity_.id), id);
}
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));
@@ -374,41 +364,14 @@ public class JpaSoftwareModuleManagement
smFilterNameAndVersionEntries[1]); smFilterNameAndVersionEntries[1]);
} }
private SoftwareModuleMetadata saveMetadata(final JpaSoftwareModuleMetadataCreate create) {
assertSoftwareModuleMetadataDoesNotExist(create.getSoftwareModuleId(), create);
return softwareModuleMetadataRepository.save(create.build());
}
private Optional<SoftwareModuleMetadata> findMetadata(final long id, final String key) {
assertSoftwareModuleExists(id);
return softwareModuleMetadataRepository.findById(new SwMetadataCompositeKey(id, key))
.map(SoftwareModuleMetadata.class::cast);
}
private void assertSoftwareModuleMetadataDoesNotExist(final Long id, final JpaSoftwareModuleMetadataCreate md) {
if (softwareModuleMetadataRepository.existsById(new SwMetadataCompositeKey(id, md.getKey()))) {
throw new EntityAlreadyExistsException("Metadata entry with key '" + md.getKey() + "' already exists!");
}
}
/** /**
* Asserts the meta-data quota for the software module with the given ID. * Asserts the meta-data quota for the software module with the given ID.
* *
* @param id The software module ID.
* @param requested Number of meta-data entries to be created. * @param requested Number of meta-data entries to be created.
*/ */
private void assertMetadataQuota(final Long id, final int requested) { private void assertMetadataQuota(final long requested) {
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule(); final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule();
QuotaHelper.assertAssignmentQuota( QuotaHelper.assertAssignmentQuota(requested, maxMetaData, SoftwareModule.MetadataValueCreate.class, SoftwareModule.class);
id, requested, maxMetaData, SoftwareModuleMetadata.class, SoftwareModule.class,
softwareModuleMetadataRepository::countBySoftwareModuleId);
}
private void assertSoftwareModuleExists(final Long id) {
if (!jpaRepository.existsById(id)) {
throw new EntityNotFoundException(SoftwareModule.class, id);
}
} }
private void assertSoftwareModuleTypeExists(final Long typeId) { private void assertSoftwareModuleTypeExists(final Long typeId) {

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others * Copyright (c) 2025 Contributors to the Eclipse Foundation
* *
* This program and the accompanying materials are made * This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0 * available under the terms of the Eclipse Public License 2.0
@@ -10,6 +10,7 @@
package org.eclipse.hawkbit.repository.jpa.model; package org.eclipse.hawkbit.repository.jpa.model;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -20,6 +21,7 @@ import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.ConstraintMode; import jakarta.persistence.ConstraintMode;
import jakarta.persistence.ElementCollection; import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.FetchType; import jakarta.persistence.FetchType;
import jakarta.persistence.ForeignKey; import jakarta.persistence.ForeignKey;
@@ -36,7 +38,7 @@ import jakarta.persistence.UniqueConstraint;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import lombok.AccessLevel; import lombok.Data;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
@@ -102,8 +104,7 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
joinColumns = { @JoinColumn(name = "sm", nullable = false) }, joinColumns = { @JoinColumn(name = "sm", nullable = false) },
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_sm_metadata_sm")) foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_sm_metadata_sm"))
@MapKeyColumn(name = "meta_key", length = SoftwareModule.METADATA_KEY_MAX_SIZE) @MapKeyColumn(name = "meta_key", length = SoftwareModule.METADATA_KEY_MAX_SIZE)
@Column(name = "meta_value", length = SoftwareModule.METADATA_VALUE_MAX_SIZE) private Map<String, JpaMetadataValue> metadata;
private Map<String, String> metadata;
@Column(name = "locked") @Column(name = "locked")
private boolean locked; private boolean locked;
@@ -192,4 +193,18 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
public void fireDeleteEvent() { public void fireDeleteEvent() {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new SoftwareModuleDeletedEvent(getTenant(), getId(), getClass())); EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new SoftwareModuleDeletedEvent(getTenant(), getId(), getClass()));
} }
@Data
@Embeddable
public static class JpaMetadataValue implements MetadataValue, Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Column(name = "meta_value", length = SoftwareModule.METADATA_VALUE_MAX_SIZE)
@Size(max = METADATA_VALUE_MAX_SIZE)
private String value;
@Column(name = "target_visible")
private boolean targetVisible;
}
} }

View File

@@ -1,81 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.model;
import java.io.Serial;
import jakarta.persistence.Basic;
import jakarta.persistence.Column;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.ForeignKey;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
/**
* Metadata for {@link SoftwareModule}.
*/
@NoArgsConstructor // Default constructor for JPA
@Data
@IdClass(SwMetadataCompositeKey.class)
@Entity
@Table(name = "sp_sm_metadata")
public class JpaSoftwareModuleMetadata implements SoftwareModuleMetadata {
@Serial
private static final long serialVersionUID = 1L;
@Id
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(
name = "sm", nullable = false, updatable = false,
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_sm_metadata_sm"))
private JpaSoftwareModule softwareModule;
@Id
@Column(name = "meta_key", nullable = false, length = SoftwareModule.METADATA_KEY_MAX_SIZE, updatable = false)
@Size(min = 1, max = SoftwareModule.METADATA_KEY_MAX_SIZE)
@NotNull
private String key;
@Column(name = "meta_value", length = SoftwareModule.METADATA_VALUE_MAX_SIZE)
@Size(max = SoftwareModule.METADATA_VALUE_MAX_SIZE)
@Basic
private String value;
@Column(name = "target_visible")
private boolean targetVisible;
public JpaSoftwareModuleMetadata(final String key, final SoftwareModule softwareModule, final String value) {
this(key, softwareModule, value, false);
}
public JpaSoftwareModuleMetadata(final String key, final SoftwareModule softwareModule, final String value, final boolean targetVisible) {
this.key = key;
this.value = value;
this.softwareModule = (JpaSoftwareModule) softwareModule;
this.targetVisible = targetVisible;
}
public SwMetadataCompositeKey getId() {
return new SwMetadataCompositeKey(softwareModule.getId(), getKey());
}
}

View File

@@ -1,39 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.model;
import java.io.Serial;
import java.io.Serializable;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* The Software Module meta-data composite key which contains the meta-data key and the ID of the software module itself.
*/
@NoArgsConstructor // Default constructor for JPA
@Data
public final class SwMetadataCompositeKey implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String key;
private Long softwareModule;
/**
* @param moduleId the software module for this meta-data
* @param key the key of the meta-data
*/
public SwMetadataCompositeKey(final Long moduleId, final String key) {
this.softwareModule = moduleId;
this.key = key;
}
}

View File

@@ -1,69 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.repository;
import java.util.Collection;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.jpa.model.SwMetadataCompositeKey;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link SoftwareModuleMetadata} repository.
*/
@Transactional(readOnly = true)
public interface SoftwareModuleMetadataRepository
extends PagingAndSortingRepository<JpaSoftwareModuleMetadata, SwMetadataCompositeKey>,
CrudRepository<JpaSoftwareModuleMetadata, SwMetadataCompositeKey>,
JpaSpecificationExecutor<JpaSoftwareModuleMetadata> {
/**
* Locates the meta-data entries that match the given software module ID and target visibility flag.
*
* @param moduleId The ID of the software module.
* @param targetVisible The target visibility flag.
* @param page The pagination parameters.
* @return A {@link Page} with the matching meta-data entries.
*/
Page<JpaSoftwareModuleMetadata> findBySoftwareModuleIdAndTargetVisible(
@Param("moduleId") Long moduleId, @Param("targetVisible") boolean targetVisible, Pageable page);
/**
* Locates the meta-data entries that match the given software module IDs and target visibility flag.
* <p/>
* No access control applied
*
* @param moduleId List of software module IDs.
* @param targetVisible The target visibility flag.
* @param page The pagination parameters.
* @return A {@link Page} with the matching meta-data entries.
*/
@Query("SELECT smd.softwareModule.id, smd FROM JpaSoftwareModuleMetadata smd WHERE smd.softwareModule.id IN :moduleId AND smd.targetVisible = :targetVisible")
Page<Object[]> findBySoftwareModuleIdInAndTargetVisible(
@Param("moduleId") Collection<Long> moduleId, @Param("targetVisible") boolean targetVisible, Pageable page);
/**
* Counts the meta-data entries that are associated with the addressed software module.
* <p/>
* No access control applied
*
* @param moduleId The ID of the software module.
* @return The number of meta-data entries associated with the software module.
*/
long countBySoftwareModuleId(@Param("moduleId") Long moduleId);
}

View File

@@ -9,6 +9,9 @@
*/ */
package org.eclipse.hawkbit.repository.jpa.repository; package org.eclipse.hawkbit.repository.jpa.repository;
import java.util.Collection;
import java.util.List;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
@@ -47,6 +50,9 @@ public interface SoftwareModuleRepository extends BaseEntityRepository<JpaSoftwa
*/ */
long countByAssignedToId(Long distributionSetId); long countByAssignedToId(Long distributionSetId);
@Query("SELECT sm.id, KEY(m), m.value FROM JpaSoftwareModule sm JOIN sm.metadata m WHERE sm.id IN :ids AND m.targetVisible = true")
List<Object[]> findVisibleMetadataByModuleIds(@Param("ids") Collection<Long> ids);
/** /**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety * Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant * reasons (this is a "delete everything" query after all) we add the tenant

View File

@@ -44,7 +44,6 @@ import org.eclipse.hawkbit.repository.jpa.repository.LocalArtifactRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository; import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository; import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutTargetGroupRepository; import org.eclipse.hawkbit.repository.jpa.repository.RolloutTargetGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleMetadataRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository; import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository; import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository; import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
@@ -111,8 +110,6 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
@Autowired @Autowired
protected DistributionSetTagRepository distributionSetTagRepository; protected DistributionSetTagRepository distributionSetTagRepository;
@Autowired @Autowired
protected SoftwareModuleMetadataRepository softwareModuleMetadataRepository;
@Autowired
protected ActionStatusRepository actionStatusRepository; protected ActionStatusRepository actionStatusRepository;
@Autowired @Autowired
protected LocalArtifactRepository artifactRepository; protected LocalArtifactRepository artifactRepository;

View File

@@ -73,7 +73,6 @@ import org.eclipse.hawkbit.repository.model.ArtifactUpload;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.matcher.Expect; import org.eclipse.hawkbit.repository.test.matcher.Expect;
@@ -989,7 +988,7 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
final DistributionSet set = testdataFactory.createDistributionSet(); final DistributionSet set = testdataFactory.createDistributionSet();
testdataFactory.addSoftwareModuleMetadata(set); testdataFactory.addSoftwareModuleMetadata(set);
final Map<Long, List<SoftwareModuleMetadata>> result = controllerManagement final Map<Long, Map<String, String>> result = controllerManagement
.findTargetVisibleMetaDataBySoftwareModuleId(set.getModules().stream().map(SoftwareModule::getId).toList()); .findTargetVisibleMetaDataBySoftwareModuleId(set.getModules().stream().map(SoftwareModule::getId).toList());
assertThat(result).hasSize(3); assertThat(result).hasSize(3);

View File

@@ -10,12 +10,15 @@
package org.eclipse.hawkbit.repository.jpa.management; package org.eclipse.hawkbit.repository.jpa.management;
import java.util.List; import java.util.List;
import java.util.Map;
import org.eclipse.hawkbit.im.authentication.SpPermission; import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.im.authentication.SpRole;
import org.eclipse.hawkbit.repository.RepositoryManagement; import org.eclipse.hawkbit.repository.RepositoryManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement; import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.jpa.AbstractRepositoryManagementSecurityTest; import org.eclipse.hawkbit.repository.jpa.AbstractRepositoryManagementSecurityTest;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValueCreate;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
/** /**
@@ -46,11 +49,15 @@ class SoftwareManagementSecurityTest
@Test @Test
void createMetaDataPermissionsCheck() { void createMetaDataPermissionsCheck() {
assertPermissions( assertPermissions(
() -> softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(1L).key("key").value("value")), () -> {
softwareModuleManagement.createMetadata(1L, "key", new MetadataValueCreate("value"));
return null;
},
List.of(SpPermission.UPDATE_REPOSITORY)); List.of(SpPermission.UPDATE_REPOSITORY));
assertPermissions(() -> { assertPermissions(() -> {
softwareModuleManagement.createMetadata( softwareModuleManagement.createMetadata(
List.of(entityFactory.softwareModuleMetadata().create(1L).key("key").value("value"))); 1L,
Map.of("key", new MetadataValueCreate("value")));
return null; return null;
}, List.of(SpPermission.UPDATE_REPOSITORY)); }, List.of(SpPermission.UPDATE_REPOSITORY));
} }
@@ -112,15 +119,6 @@ class SoftwareManagementSecurityTest
assertPermissions(() -> softwareModuleManagement.getMetadata(1L), List.of(SpPermission.READ_REPOSITORY)); assertPermissions(() -> softwareModuleManagement.getMetadata(1L), List.of(SpPermission.READ_REPOSITORY));
} }
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void findMetaDataBySoftwareModuleIdAndTargetVisiblePermissionsCheck() {
assertPermissions(() -> softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible(1L, PAGE),
List.of(SpPermission.READ_REPOSITORY));
}
/** /**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions. * Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/ */
@@ -157,7 +155,10 @@ class SoftwareManagementSecurityTest
@Test @Test
void updateMetaDataPermissionsCheck() { void updateMetaDataPermissionsCheck() {
assertPermissions( assertPermissions(
() -> softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().update(1L, "key").value("value")), () -> {
softwareModuleManagement.createMetadata(1L, "key", new MetadataValueCreate("value"));
return null;
},
List.of(SpPermission.UPDATE_REPOSITORY)); List.of(SpPermission.UPDATE_REPOSITORY));
} }
@@ -167,6 +168,6 @@ class SoftwareManagementSecurityTest
@Test @Test
void findMetaDataBySoftwareModuleIdsAndTargetVisiblePermissionsCheck() { void findMetaDataBySoftwareModuleIdsAndTargetVisiblePermissionsCheck() {
assertPermissions(() -> softwareModuleManagement.findMetaDataBySoftwareModuleIdsAndTargetVisible(List.of(1L)), assertPermissions(() -> softwareModuleManagement.findMetaDataBySoftwareModuleIdsAndTargetVisible(List.of(1L)),
List.of(SpPermission.READ_REPOSITORY)); List.of(SpRole.SYSTEM_ROLE));
} }
} }

View File

@@ -14,11 +14,11 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@@ -26,7 +26,6 @@ import jakarta.validation.ConstraintViolationException;
import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement; import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
@@ -37,7 +36,6 @@ import org.eclipse.hawkbit.repository.jpa.RandomGeneratedInputStream;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_; import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
@@ -45,7 +43,8 @@ import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.ArtifactUpload; import org.eclipse.hawkbit.repository.model.ArtifactUpload;
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.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValue;
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValueCreate;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
@@ -53,8 +52,6 @@ import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents; import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.repository.test.util.WithUser; import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
/** /**
* Feature: Component Tests - Repository<br/> * Feature: Component Tests - Repository<br/>
@@ -62,8 +59,6 @@ import org.springframework.data.domain.PageRequest;
*/ */
class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
private static final PageRequest PAGE_REQUEST_100 = PageRequest.of(0, 100);
/** /**
* Verifies that management get access reacts as specified on calls for non existing entities by means * Verifies that management get access reacts as specified on calls for non existing entities by means
* of Optional not present. * of Optional not present.
@@ -72,14 +67,10 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) }) @ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) })
void nonExistingEntityAccessReturnsNotPresent() { void nonExistingEntityAccessReturnsNotPresent() {
final SoftwareModule module = testdataFactory.createSoftwareModuleApp(); final SoftwareModule module = testdataFactory.createSoftwareModuleApp();
assertThat(softwareModuleManagement.get(1234L)).isNotPresent(); assertThat(softwareModuleManagement.get(1234L)).isNotPresent();
assertThat(softwareModuleManagement.findByNameAndVersionAndType(NOT_EXIST_ID, NOT_EXIST_ID, osType.getId())).isNotPresent();
assertThat(softwareModuleManagement.findByNameAndVersionAndType(NOT_EXIST_ID, NOT_EXIST_ID, osType.getId())) final Long moduleId = module.getId();
.isNotPresent(); assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> softwareModuleManagement.getMetadata(moduleId, NOT_EXIST_ID));
assertThatExceptionOfType(EntityNotFoundException.class)
.isThrownBy(() -> softwareModuleManagement.getMetadata(module.getId(), NOT_EXIST_ID));
} }
/** /**
@@ -89,7 +80,7 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) }) @ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) })
void entityQueriesReferringToNotExistingEntitiesThrowsException() { void entityQueriesReferringToNotExistingEntitiesThrowsException() {
final SoftwareModule module = testdataFactory.createSoftwareModuleApp(); testdataFactory.createSoftwareModuleApp();
final SoftwareModuleManagement.Create noType = SoftwareModuleManagement.Create.builder().name("xxx").type(null).build(); final SoftwareModuleManagement.Create noType = SoftwareModuleManagement.Create.builder().name("xxx").type(null).build();
final List<SoftwareModuleManagement.Create> noTypeList = List.of(noType); final List<SoftwareModuleManagement.Create> noTypeList = List.of(noType);
@@ -98,22 +89,20 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> softwareModuleManagement.create(noType)); assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> softwareModuleManagement.create(noType));
verifyThrownExceptionBy( verifyThrownExceptionBy(
() -> softwareModuleManagement.updateMetadata( () -> softwareModuleManagement.createMetadata(
entityFactory.softwareModuleMetadata().create(NOT_EXIST_IDL).key("xxx").value("xxx")), "SoftwareModule"); NOT_EXIST_IDL, "xxx", new MetadataValueCreate("xxx")), "SoftwareModule");
verifyThrownExceptionBy( verifyThrownExceptionBy(
() -> softwareModuleManagement.createMetadata(Collections.singletonList( () -> softwareModuleManagement.createMetadata(
entityFactory.softwareModuleMetadata().create(NOT_EXIST_IDL).key("xxx").value("xxx"))), "SoftwareModule"); NOT_EXIST_IDL,
Map.of("xxx", new MetadataValueCreate("xxx"))), "SoftwareModule");
verifyThrownExceptionBy(() -> softwareModuleManagement.delete(NOT_EXIST_IDL), "SoftwareModule"); verifyThrownExceptionBy(() -> softwareModuleManagement.delete(NOT_EXIST_IDL), "SoftwareModule");
verifyThrownExceptionBy(() -> softwareModuleManagement.delete(Collections.singletonList(NOT_EXIST_IDL)), "SoftwareModule"); verifyThrownExceptionBy(() -> softwareModuleManagement.delete(Collections.singletonList(NOT_EXIST_IDL)), "SoftwareModule");
verifyThrownExceptionBy(() -> softwareModuleManagement.deleteMetadata(NOT_EXIST_IDL, "xxx"), "SoftwareModule"); verifyThrownExceptionBy(() -> softwareModuleManagement.deleteMetadata(NOT_EXIST_IDL, "xxx"), "SoftwareModule");
verifyThrownExceptionBy( verifyThrownExceptionBy(
() -> softwareModuleManagement.updateMetadata( () -> softwareModuleManagement.createMetadata(
entityFactory.softwareModuleMetadata().update(NOT_EXIST_IDL, "xxx").value("xxx")), "SoftwareModule"); NOT_EXIST_IDL, "xxx", new MetadataValueCreate("xxx")), "SoftwareModule");
verifyThrownExceptionBy(
() -> softwareModuleManagement.updateMetadata(
entityFactory.softwareModuleMetadata().update(module.getId(), NOT_EXIST_ID).value("xxx")), "SoftwareModuleMetadata");
verifyThrownExceptionBy(() -> softwareModuleManagement.findByAssignedTo(NOT_EXIST_IDL, PAGE), "DistributionSet"); verifyThrownExceptionBy(() -> softwareModuleManagement.findByAssignedTo(NOT_EXIST_IDL, PAGE), "DistributionSet");
@@ -241,8 +230,8 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
testdataFactory.createSoftwareModuleApp(); testdataFactory.createSoftwareModuleApp();
assertThat((List) softwareModuleManagement.findByType(osType.getId(), PAGE).getContent()) assertThat((List) softwareModuleManagement.findByType(osType.getId(), PAGE).getContent())
.as("Expected to find the following number of modules:").hasSize(2).as("with the following elements") .as("Expected to find the following number of modules:").hasSize(2)
.contains(two, one); .as("with the following elements").contains(two, one);
} }
/** /**
@@ -525,30 +514,21 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
assertThat(softwareModule.getOptLockRevision()).isEqualTo(1); assertThat(softwareModule.getOptLockRevision()).isEqualTo(1);
final SoftwareModuleMetadataCreate swMetadata1 = entityFactory.softwareModuleMetadata() softwareModuleManagement.createMetadata(
.create(softwareModule.getId()) softwareModule.getId(),
.key(knownKey1) Map.of(
.value(knownValue1) knownKey1, new MetadataValueCreate(knownValue1, true),
.targetVisible(true); knownKey2, new MetadataValueCreate(knownValue2)));
final SoftwareModuleMetadataCreate swMetadata2 = entityFactory.softwareModuleMetadata()
.create(softwareModule.getId())
.key(knownKey2)
.value(knownValue2);
softwareModuleManagement.createMetadata(Arrays.asList(swMetadata1, swMetadata2));
final SoftwareModule changedLockRevisionModule = softwareModuleManagement.get(softwareModule.getId()).get(); final SoftwareModule changedLockRevisionModule = softwareModuleManagement.get(softwareModule.getId()).get();
assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2); assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2);
assertThat(softwareModuleManagement.getMetadata(softwareModule.getId(), knownKey1)).satisfies(metadata -> { assertThat(softwareModuleManagement.getMetadata(softwareModule.getId(), knownKey1)).satisfies(metadata -> {
assertThat(metadata.getKey()).isEqualTo(knownKey1);
assertThat(metadata.getValue()).isEqualTo(knownValue1); assertThat(metadata.getValue()).isEqualTo(knownValue1);
assertThat(metadata.isTargetVisible()).isTrue(); assertThat(metadata.isTargetVisible()).isTrue();
}); });
assertThat(softwareModuleManagement.getMetadata(softwareModule.getId(), knownKey2)).satisfies(metadata -> { assertThat(softwareModuleManagement.getMetadata(softwareModule.getId(), knownKey2)).satisfies(metadata -> {
assertThat(metadata.getKey()).isEqualTo(knownKey2);
assertThat(metadata.getValue()).isEqualTo(knownValue2); assertThat(metadata.getValue()).isEqualTo(knownValue2);
assertThat(metadata.isTargetVisible()).isFalse(); assertThat(metadata.isTargetVisible()).isFalse();
}); });
@@ -559,80 +539,49 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
*/ */
@Test @Test
void createSoftwareModuleMetadataUntilQuotaIsExceeded() { void createSoftwareModuleMetadataUntilQuotaIsExceeded() {
// add meta-data one by one
// add meta data one by one
final SoftwareModule module = testdataFactory.createSoftwareModuleApp("m1"); final SoftwareModule module = testdataFactory.createSoftwareModuleApp("m1");
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule(); final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule();
for (int i = 0; i < maxMetaData; ++i) { for (int i = 0; i < maxMetaData; ++i) {
softwareModuleManagement.updateMetadata( softwareModuleManagement.createMetadata(module.getId(), "k" + i, new MetadataValueCreate("v" + i));
entityFactory.softwareModuleMetadata().create(module.getId()).key("k" + i).value("v" + i));
} }
// quota exceeded // quota exceeded
final SoftwareModuleMetadataCreate metadata = entityFactory.softwareModuleMetadata().create(module.getId()) final Long moduleId = module.getId();
.key("k" + maxMetaData).value("v" + maxMetaData); final MetadataValueCreate metadata = new MetadataValueCreate("v" + maxMetaData);
assertThatExceptionOfType(AssignmentQuotaExceededException.class) assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> softwareModuleManagement.updateMetadata(metadata)); .isThrownBy(() -> softwareModuleManagement.createMetadata(moduleId, "k" + maxMetaData, metadata));
// add multiple meta data entries at once // add multiple meta data entries at once
final SoftwareModule module2 = testdataFactory.createSoftwareModuleApp("m2"); final SoftwareModule module2 = testdataFactory.createSoftwareModuleApp("m2");
final List<SoftwareModuleMetadataCreate> create = new ArrayList<>(); final Long moduleId2 = module2.getId();
final Map<String, MetadataValueCreate> create = new HashMap<>();
for (int i = 0; i < maxMetaData + 1; ++i) { for (int i = 0; i < maxMetaData + 1; ++i) {
create.add(entityFactory.softwareModuleMetadata().create(module2.getId()).key("k" + i).value("v" + i)); create.put("k" + i, new MetadataValueCreate("v" + i));
} }
// quota exceeded // quota exceeded
assertThatExceptionOfType(AssignmentQuotaExceededException.class) assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> softwareModuleManagement.createMetadata(create)); .isThrownBy(() -> softwareModuleManagement.createMetadata(moduleId2, create));
// add some meta data entries // add some meta data entries
final SoftwareModule module3 = testdataFactory.createSoftwareModuleApp("m3"); final SoftwareModule module3 = testdataFactory.createSoftwareModuleApp("m3");
final int firstHalf = Math.round((maxMetaData) / 2.f); final int firstHalf = Math.round((maxMetaData) / 2.f);
for (int i = 0; i < firstHalf; ++i) { for (int i = 0; i < firstHalf; ++i) {
softwareModuleManagement.updateMetadata( softwareModuleManagement.createMetadata(module3.getId(), "k" + i, new MetadataValueCreate("v" + i));
entityFactory.softwareModuleMetadata().create(module3.getId()).key("k" + i).value("v" + i));
} }
// add too many data entries // add too many data entries
final Long moduleId3 = module3.getId();
final int secondHalf = maxMetaData - firstHalf; final int secondHalf = maxMetaData - firstHalf;
final List<SoftwareModuleMetadataCreate> create2 = new ArrayList<>(); final Map<String, MetadataValueCreate> create2 = new HashMap<>();
for (int i = 0; i < secondHalf + 1; ++i) { for (int i = 0; i < secondHalf + 1; ++i) {
create2.add(entityFactory.softwareModuleMetadata().create(module3.getId()).key("kk" + i).value("vv" + i)); create2.put("kk" + i, new MetadataValueCreate("vv" + i));
} }
// quota exceeded // quota exceeded
assertThatExceptionOfType(AssignmentQuotaExceededException.class) assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> softwareModuleManagement.createMetadata(create2)); .isThrownBy(() -> softwareModuleManagement.createMetadata(moduleId3, create2));
} }
/**
* Checks that metadata for a software module cannot be created for an existing key.
*/
@Test
void createSoftwareModuleMetadataFailsIfKeyExists() {
final String knownKey1 = "myKnownKey1";
final String knownValue1 = "myKnownValue1";
final SoftwareModule ah = testdataFactory.createSoftwareModuleApp();
final SoftwareModuleMetadataCreate metadata = entityFactory.softwareModuleMetadata()
.create(ah.getId()).key(knownKey1).value(knownValue1).targetVisible(true);
softwareModuleManagement.updateMetadata(metadata);
assertThatExceptionOfType(EntityAlreadyExistsException.class)
.isThrownBy(() -> softwareModuleManagement.updateMetadata(metadata))
.withMessageContaining("Metadata").withMessageContaining(knownKey1);
final String knownKey2 = "myKnownKey2";
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(ah.getId()).key(knownKey2)
.value(knownValue1).targetVisible(false));
final SoftwareModuleMetadataCreate metadata2 = entityFactory.softwareModuleMetadata().create(ah.getId())
.key(knownKey2).value(knownValue1).targetVisible(true);
assertThatExceptionOfType(EntityAlreadyExistsException.class)
.isThrownBy(() -> softwareModuleManagement.updateMetadata(metadata2))
.withMessageContaining("Metadata").withMessageContaining(knownKey2);
}
/** /**
* Checks that metadata for a software module can be updated. * Checks that metadata for a software module can be updated.
*/ */
@@ -649,8 +598,8 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
assertThat(ah.getOptLockRevision()).isEqualTo(1); assertThat(ah.getOptLockRevision()).isEqualTo(1);
// create an software module meta data entry // create an software module meta data entry
final SoftwareModuleMetadata softwareModuleMetadata = softwareModuleManagement.updateMetadata( softwareModuleManagement.createMetadata(ah.getId(), knownKey, new MetadataValueCreate(knownValue));
entityFactory.softwareModuleMetadata().create(ah.getId()).key(knownKey).value(knownValue)); final MetadataValue softwareModuleMetadata = softwareModuleManagement.getMetadata(ah.getId(), knownKey);
assertThat(softwareModuleMetadata.isTargetVisible()).isFalse(); assertThat(softwareModuleMetadata.isTargetVisible()).isFalse();
assertThat(softwareModuleMetadata.getValue()).isEqualTo(knownValue); assertThat(softwareModuleMetadata.getValue()).isEqualTo(knownValue);
@@ -660,8 +609,8 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2); assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2);
// update the software module metadata // update the software module metadata
final SoftwareModuleMetadata updated = softwareModuleManagement.updateMetadata(entityFactory softwareModuleManagement.createMetadata(ah.getId(), knownKey, new MetadataValueCreate(knownUpdateValue, true));
.softwareModuleMetadata().update(ah.getId(), knownKey).value(knownUpdateValue).targetVisible(true)); final MetadataValue updated = softwareModuleManagement.getMetadata(ah.getId(), knownKey);
// 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 two // module so opt lock revision must be two
@@ -672,8 +621,6 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
assertThat(updated).isNotNull(); assertThat(updated).isNotNull();
assertThat(updated.getValue()).isEqualTo(knownUpdateValue); assertThat(updated.getValue()).isEqualTo(knownUpdateValue);
assertThat(updated.isTargetVisible()).isTrue(); assertThat(updated.isTargetVisible()).isTrue();
assertThat(((JpaSoftwareModuleMetadata) updated).getId().getKey()).isEqualTo(knownKey);
assertThat(((JpaSoftwareModuleMetadata) updated).getSoftwareModule().getId()).isEqualTo(ah.getId());
} }
/** /**
@@ -686,37 +633,18 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
final SoftwareModule swModule = testdataFactory.createSoftwareModuleApp(); final SoftwareModule swModule = testdataFactory.createSoftwareModuleApp();
softwareModuleManagement.updateMetadata( softwareModuleManagement.createMetadata(swModule.getId(), knownKey1, new MetadataValueCreate(knownValue1));
entityFactory.softwareModuleMetadata().create(swModule.getId()).key(knownKey1).value(knownValue1));
assertThat(softwareModuleManagement.getMetadata(swModule.getId())) assertThat(softwareModuleManagement.getMetadata(swModule.getId()).entrySet())
.as("Contains the created metadata element").allSatisfy(metadata -> { .as("Contains the created metadata element").allSatisfy(metadata -> {
assertThat(((JpaSoftwareModuleMetadata) metadata).getSoftwareModule().getId()).isEqualTo(swModule.getId());
assertThat(metadata.getKey()).isEqualTo(knownKey1); assertThat(metadata.getKey()).isEqualTo(knownKey1);
assertThat(metadata.getValue()).isEqualTo(knownValue1); assertThat(metadata.getValue().getValue()).isEqualTo(knownValue1);
}); });
softwareModuleManagement.deleteMetadata(swModule.getId(), knownKey1); softwareModuleManagement.deleteMetadata(swModule.getId(), knownKey1);
assertThat(softwareModuleManagement.getMetadata(swModule.getId())).as("Metadata elements are").isEmpty(); assertThat(softwareModuleManagement.getMetadata(swModule.getId())).as("Metadata elements are").isEmpty();
} }
/**
* Verifies that non existing metadata find results in exception.
*/
@Test
void findSoftwareModuleMetadataFailsIfEntryDoesNotExist() {
final String knownKey1 = "myKnownKey1";
final String knownValue1 = "myKnownValue1";
final SoftwareModule ah = testdataFactory.createSoftwareModuleApp();
softwareModuleManagement.updateMetadata(
entityFactory.softwareModuleMetadata().create(ah.getId()).key(knownKey1).value(knownValue1));
assertThatExceptionOfType(EntityNotFoundException.class)
.isThrownBy(() -> softwareModuleManagement.getMetadata(ah.getId(), "doesnotexist"));
}
/** /**
* Queries and loads the metadata related to a given software module. * Queries and loads the metadata related to a given software module.
*/ */
@@ -729,28 +657,24 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
final int metadataCountSw2 = 10; final int metadataCountSw2 = 10;
for (int index = 0; index < metadataCountSw1; index++) { for (int index = 0; index < metadataCountSw1; index++) {
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(sw1.getId()) softwareModuleManagement.createMetadata(sw1.getId(), "key" + index, new MetadataValueCreate("value" + index, true));
.key("key" + index).value("value" + index).targetVisible(true));
} }
for (int index = 0; index < metadataCountSw2; index++) { for (int index = 0; index < metadataCountSw2; index++) {
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(sw2.getId()) softwareModuleManagement.createMetadata(sw2.getId(), "key" + index, new MetadataValueCreate("value" + index, false));
.key("key" + index).value("value" + index).targetVisible(false));
} }
final List<SoftwareModuleMetadata> metadataSw1 = softwareModuleManagement.getMetadata(sw1.getId()); final Map<String, MetadataValue> metadataSw1 = softwareModuleManagement.getMetadata(sw1.getId());
final List<SoftwareModuleMetadata> metadataSw2 = softwareModuleManagement.getMetadata(sw2.getId()); final Map<String, MetadataValue> metadataSw2 = softwareModuleManagement.getMetadata(sw2.getId());
assertThat(metadataSw1).hasSize(metadataCountSw1); assertThat(metadataSw1).hasSize(metadataCountSw1);
assertThat(metadataSw2).hasSize(metadataCountSw2); assertThat(metadataSw2).hasSize(metadataCountSw2);
final Page<SoftwareModuleMetadata> metadataSw1V = softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible( final Map<String, String> metadataSw1V = softwareModuleManagement
sw1.getId(), PAGE_REQUEST_100); .findMetaDataBySoftwareModuleIdsAndTargetVisible(List.of(sw1.getId())).get(sw1.getId());
final Page<SoftwareModuleMetadata> metadataSw2V = softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible( final Map<String, String> metadataSw2V = softwareModuleManagement
sw2.getId(), PAGE_REQUEST_100); .findMetaDataBySoftwareModuleIdsAndTargetVisible(List.of(sw2.getId())).get(sw2.getId());
assertThat(metadataSw1V.getNumberOfElements()).isEqualTo(metadataCountSw1); assertThat(metadataSw1V).hasSize(metadataCountSw1);
assertThat(metadataSw1V.getTotalElements()).isEqualTo(metadataCountSw1); assertThat(metadataSw2V).isNull();
assertThat(metadataSw2V.getNumberOfElements()).isZero();
assertThat(metadataSw2V.getTotalElements()).isZero();
} }
/** /**

View File

@@ -13,10 +13,10 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.eclipse.hawkbit.repository.SoftwareModuleFields; import org.eclipse.hawkbit.repository.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement; import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest; import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValueCreate;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory; import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -49,13 +49,8 @@ class RsqlSoftwareModuleFieldTest extends AbstractJpaIntegrationTest {
final JpaSoftwareModule ah2 = (JpaSoftwareModule) softwareModuleManagement.create( final JpaSoftwareModule ah2 = (JpaSoftwareModule) softwareModuleManagement.create(
SoftwareModuleManagement.Create.builder().type(appType).name("agent-hub2").version("1.0.1").description("agent-hub2").build()); SoftwareModuleManagement.Create.builder().type(appType).name("agent-hub2").version("1.0.1").description("agent-hub2").build());
final SoftwareModuleMetadataCreate softwareModuleMetadata = entityFactory.softwareModuleMetadata() softwareModuleManagement.createMetadata(ah.getId(), "metaKey", new MetadataValueCreate("metaValue"));
.create(ah.getId()).key("metaKey").value("metaValue"); softwareModuleManagement.createMetadata(ah2.getId(), "metaKey", new MetadataValueCreate("value"));
softwareModuleManagement.updateMetadata(softwareModuleMetadata);
final SoftwareModuleMetadataCreate softwareModuleMetadata2 = entityFactory.softwareModuleMetadata()
.create(ah2.getId()).key("metaKey").value("value");
softwareModuleManagement.updateMetadata(softwareModuleMetadata2);
} }
/** /**
@@ -160,7 +155,6 @@ class RsqlSoftwareModuleFieldTest extends AbstractJpaIntegrationTest {
assertRSQLQuery(SoftwareModuleFields.METADATA.name() + ".metaKey=in=(metaValue,value)", 2); assertRSQLQuery(SoftwareModuleFields.METADATA.name() + ".metaKey=in=(metaValue,value)", 2);
assertRSQLQuery(SoftwareModuleFields.METADATA.name() + ".metaKey=out=(metaValue,notexist)", 1); assertRSQLQuery(SoftwareModuleFields.METADATA.name() + ".metaKey=out=(metaValue,notexist)", 1);
assertRSQLQuery(SoftwareModuleFields.METADATA.name() + ".notExist==metaValue", 0); assertRSQLQuery(SoftwareModuleFields.METADATA.name() + ".notExist==metaValue", 0);
} }
private void assertRSQLQuery(final String rsql, final long expectedEntity) { private void assertRSQLQuery(final String rsql, final long expectedEntity) {

View File

@@ -18,8 +18,10 @@ import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceContext;
import org.eclipse.hawkbit.repository.RsqlQueryField; import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.TargetFields; import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.jpa.JpaRepositoryConfiguration; import org.eclipse.hawkbit.repository.jpa.JpaRepositoryConfiguration;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.ql.utils.HawkbitQlToSql; import org.eclipse.hawkbit.repository.jpa.ql.utils.HawkbitQlToSql;
import org.eclipse.hawkbit.repository.test.TestConfiguration; import org.eclipse.hawkbit.repository.test.TestConfiguration;
@@ -45,6 +47,8 @@ class RsqlToSqlTest {
@Test @Test
void printPG() { void printPG() {
printFrom(JpaSoftwareModule.class, SoftwareModuleFields.class, "metadata.x!=y");
printFrom(JpaSoftwareModule.class, SoftwareModuleFields.class, "metadata.x==y");
printFrom(JpaTarget.class, TargetFields.class, "metadata.x==y"); printFrom(JpaTarget.class, TargetFields.class, "metadata.x==y");
printFrom(JpaTarget.class, TargetFields.class, "tag!=TAG1 and tag==TAG2"); printFrom(JpaTarget.class, TargetFields.class, "tag!=TAG1 and tag==TAG2");
printFrom(JpaTarget.class, TargetFields.class, "tag==TAG1 and tag!=TAG2"); printFrom(JpaTarget.class, TargetFields.class, "tag==TAG1 and tag!=TAG2");

View File

@@ -74,7 +74,7 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCond
import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder; import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions; import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValueCreate;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.Tag; import org.eclipse.hawkbit.repository.model.Tag;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
@@ -341,12 +341,12 @@ public class TestdataFactory {
} }
/** /**
* Adds {@link SoftwareModuleMetadata} to every module of given {@link DistributionSet}. * Adds software module metadata to every module of given {@link DistributionSet}.
* <p/> * <p/>
* {@link #VISIBLE_SM_MD_VALUE}, {@link #VISIBLE_SM_MD_KEY} with * {@link #VISIBLE_SM_MD_VALUE}, {@link #VISIBLE_SM_MD_KEY} with
* {@link SoftwareModuleMetadata#isTargetVisible()} and * {@link MetadataValueCreate#isTargetVisible()} and
* {@link #INVISIBLE_SM_MD_KEY}, {@link #INVISIBLE_SM_MD_VALUE} without * {@link #INVISIBLE_SM_MD_KEY}, {@link #INVISIBLE_SM_MD_VALUE} without
* {@link SoftwareModuleMetadata#isTargetVisible()} * {@link MetadataValueCreate#isTargetVisible()}
* *
* @param set to add metadata to * @param set to add metadata to
*/ */
@@ -1256,10 +1256,14 @@ public class TestdataFactory {
} }
private void addTestModuleMetadata(final SoftwareModule module) { private void addTestModuleMetadata(final SoftwareModule module) {
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(module.getId()) softwareModuleManagement.createMetadata(
.key(VISIBLE_SM_MD_KEY).value(VISIBLE_SM_MD_VALUE).targetVisible(true)); module.getId(),
softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(module.getId()) VISIBLE_SM_MD_KEY,
.key(INVISIBLE_SM_MD_KEY).value(INVISIBLE_SM_MD_VALUE).targetVisible(false)); new MetadataValueCreate(VISIBLE_SM_MD_VALUE, true));
softwareModuleManagement.createMetadata(
module.getId(),
INVISIBLE_SM_MD_KEY,
new MetadataValueCreate(INVISIBLE_SM_MD_VALUE, false));
} }
private void assertTargetProperlyCreated(final Target target) { private void assertTargetProperlyCreated(final Target target) {