diff --git a/hawkbit-ddi/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java b/hawkbit-ddi/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java index f05874a1d..ab2d971ee 100644 --- a/hawkbit-ddi/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java +++ b/hawkbit-ddi/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java @@ -33,7 +33,6 @@ import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Artifact; 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.TenantMetaData; import org.eclipse.hawkbit.rest.json.model.ResponseList; @@ -133,7 +132,7 @@ public final class DataConversionHelper { final Target target, final Action uAction, final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement, final HttpRequest request, final ControllerManagement controllerManagement) { - final Map> metadata = controllerManagement + final Map> metadata = controllerManagement .findTargetVisibleMetaDataBySoftwareModuleId(uAction.getDistributionSet().getModules().stream() .map(SoftwareModule::getId).toList()); @@ -155,10 +154,10 @@ public final class DataConversionHelper { .toList()); } - private static List mapMetadata(final List metadata) { + private static List mapMetadata(final Map metadata) { return CollectionUtils.isEmpty(metadata) ? 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) { diff --git a/hawkbit-ddi/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java b/hawkbit-ddi/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java index fb163f9e8..8966b2df8 100644 --- a/hawkbit-ddi/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java +++ b/hawkbit-ddi/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java @@ -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.Artifact; 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.TargetUpdateStatus; import org.eclipse.hawkbit.repository.test.matcher.Expect; @@ -253,10 +254,8 @@ class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { final Artifact artifactSignature = testdataFactory.createArtifact(nextBytes(ARTIFACT_SIZE), getOsModule(ds), "test1.signature", ARTIFACT_SIZE); - softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(getOsModule(ds)) - .key(visibleMetadataOsKey).value(visibleMetadataOsValue).targetVisible(true)); - softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(getOsModule(ds)) - .key("metaDataNotVisible").value("withValue").targetVisible(false)); + softwareModuleManagement.createMetadata(getOsModule(ds), visibleMetadataOsKey, new MetadataValueCreate(visibleMetadataOsValue, true)); + softwareModuleManagement.createMetadata(getOsModule(ds), "metaDataNotVisible", new MetadataValueCreate("withValue", false)); final Target savedTarget = createTargetAndAssertNoActiveActions(); @@ -374,10 +373,8 @@ class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { final Artifact artifactSignature = testdataFactory.createArtifact( nextBytes(ARTIFACT_SIZE), getOsModule(ds), "test1.signature", ARTIFACT_SIZE); - softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(getOsModule(ds)) - .key("metaDataVisible").value("withValue").targetVisible(true)); - softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(getOsModule(ds)) - .key("metaDataNotVisible").value("withValue").targetVisible(false)); + softwareModuleManagement.createMetadata(getOsModule(ds), "metaDataVisible", new MetadataValueCreate("withValue", true)); + softwareModuleManagement.createMetadata(getOsModule(ds), "metaDataNotVisible", new MetadataValueCreate("withValue", false)); final Target savedTarget = createTargetAndAssertNoActiveActions(); diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java index b47f3df6a..e880000cb 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java @@ -29,11 +29,6 @@ import java.util.stream.StreamSupport; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.IterableUtils; 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.MessageHeaderKey; 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.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.DistributionSetManagement; -import org.eclipse.hawkbit.repository.RepositoryConstants; import org.eclipse.hawkbit.repository.SoftwareModuleManagement; import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.TargetManagement; 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.MultiActionCancelEvent; 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.DistributionSet; 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.TenantMetaData; 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.rabbit.core.RabbitTemplate; 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.SecurityContextHolder; import org.springframework.util.CollectionUtils; @@ -124,7 +121,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { final AmqpMessageSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler, final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement, final TargetManagement targetManagement, - final SoftwareModuleManagement softwareModuleManagement, final DistributionSetManagement distributionSetManagement, + final SoftwareModuleManagement softwareModuleManagement, + final DistributionSetManagement distributionSetManagement, final DeploymentManagement deploymentManagement, final TenantConfigurationManagement tenantConfigurationManagement) { super(rabbitTemplate); @@ -187,15 +185,14 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { protected void sendUpdateMessageToTarget( final ActionProperties actionsProps, final Target target, - final Map> softwareModules) { + final Map> softwareModules) { final Map actionProp = new HashMap<>(); actionProp.put(target.getControllerId(), actionsProps); sendUpdateMessageToTargets(actionProp, Collections.singletonList(target), softwareModules); } protected DmfDownloadAndUpdateRequest createDownloadAndUpdateRequest( - final Target target, final Long actionId, - final Map> softwareModules) { + final Target target, final Long actionId, final Map> softwareModules) { return new DmfDownloadAndUpdateRequest( actionId, systemSecurityContext.runAsSystem(target::getSecurityToken), @@ -273,7 +270,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { } protected DmfConfirmRequest createConfirmRequest( - final Target target, final Long actionId, final Map> softwareModules) { + final Target target, final Long actionId, final Map> softwareModules) { return new DmfConfirmRequest( actionId, systemSecurityContext.runAsSystem(target::getSecurityToken), @@ -282,7 +279,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { void sendMultiActionRequestToTarget( final Target target, final List actions, - final Function> getSoftwareModuleMetaData) { + final Function> getSoftwareModuleMetaData) { final URI targetAddress = target.getAddress(); if (!IpUtil.isAmqpUri(targetAddress) || CollectionUtils.isEmpty(actions)) { return; @@ -295,9 +292,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { target, action, action.getDistributionSet().getModules().stream() .collect(Collectors.toMap(Function.identity(), module -> { - final List softwareModuleMetadata = getSoftwareModuleMetaData.apply( - module); - return softwareModuleMetadata == null ? Collections.emptyList() : softwareModuleMetadata; + final Map softwareModuleMetadata = getSoftwareModuleMetaData.apply(module); + return softwareModuleMetadata == null ? Collections.emptyMap() : softwareModuleMetadata; }))); final int weight = deploymentManagement.getWeightConsideringDefault(action); return new DmfMultiActionRequest.DmfMultiActionElement(getEventTypeForAction(action), actionRequest, weight); @@ -424,14 +420,14 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { private void sendUpdateMessageToTargets( final Long dsId, final Map actionsPropsByTargetId, final List targets) { distributionSetManagement.get(dsId).ifPresent(ds -> { - final Map> softwareModules = getSoftwareModulesWithMetadata(ds); + final Map> softwareModules = getSoftwareModulesWithMetadata(ds); sendUpdateMessageToTargets(actionsPropsByTargetId, targets, softwareModules); }); } private void sendUpdateMessageToTargets( final Map actionsPropsByTargetId, - final List targets, final Map> softwareModules) { + final List targets, final Map> softwareModules) { if (!targets.isEmpty() && isBatchAssignmentsEnabled()) { sendBatchUpdateMessage(actionsPropsByTargetId, targets, softwareModules); } else { @@ -455,7 +451,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { .flatMap(ds -> ds.getModules().stream()) .map(SoftwareModule::getId)) .collect(Collectors.toSet()); - final Map> getSoftwareModuleMetadata = + final Map> getSoftwareModuleMetadata = allSmIds.isEmpty() ? Collections.emptyMap() : softwareModuleManagement.findMetaDataBySoftwareModuleIdsAndTargetVisible(allSmIds); @@ -467,7 +463,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { private DmfActionRequest createDmfActionRequest( final Target target, final Action action, - final Map> softwareModules) { + final Map> softwareModules) { if (action.isCancelingOrCanceled()) { return new DmfActionRequest(action.getId()); } else if (action.isWaitingConfirmation()) { @@ -477,8 +473,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { } private void sendSingleUpdateMessage( - final ActionProperties action, final Target target, - final Map> modules) { + final ActionProperties action, final Target target, final Map> modules) { final String tenant = action.getTenant(); final URI targetAddress = target.getAddress(); @@ -531,7 +526,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { } private List convertToAmqpSoftwareModules( - final Target target, final Map> softwareModules) { + final Target target, final Map> softwareModules) { return Optional.ofNullable(softwareModules) .map(Map::entrySet) .map(Set::stream) @@ -539,19 +534,18 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { .orElse(null); } - private DmfSoftwareModule convertToAmqpSoftwareModule( - final Target target, final Entry> entry) { + private DmfSoftwareModule convertToAmqpSoftwareModule(final Target target, final Entry> entry) { return new DmfSoftwareModule( entry.getKey().getId(), entry.getKey().getType().getKey(), entry.getKey().getVersion(), entry.getKey().isEncrypted() ? Boolean.TRUE : null, convertArtifacts(target, entry.getKey().getArtifacts()), - CollectionUtils.isEmpty(entry.getValue()) ? null :convertMetadata(entry.getValue())); + CollectionUtils.isEmpty(entry.getValue()) ? null : convertMetadata(entry.getValue())); } - private List convertMetadata(final List metadata) { - return metadata.stream().map(md -> new DmfMetadata(md.getKey(), md.getValue())).toList(); + private List convertMetadata(final Map metadata) { + return metadata.entrySet().stream().map(md -> new DmfMetadata(md.getKey(), md.getValue())).toList(); } private List convertArtifacts(final Target target, final List localArtifacts) { @@ -559,8 +553,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { return Collections.emptyList(); } - return localArtifacts.stream().map(localArtifact -> convertArtifact(target, localArtifact)) - .toList(); + return localArtifacts.stream().map(localArtifact -> convertArtifact(target, localArtifact)).toList(); } private DmfArtifact convertArtifact(final Target target, final Artifact localArtifact) { @@ -582,19 +575,22 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { ); } - private Map> getSoftwareModulesWithMetadata(final DistributionSet distributionSet) { - return distributionSet.getModules().stream().collect(Collectors.toMap(Function.identity(), this::getSoftwareModuleMetadata)); - } - - private List getSoftwareModuleMetadata(final SoftwareModule module) { - return softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible( - module.getId(), PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT)).getContent(); + @SuppressWarnings("unchecked") + private Map> getSoftwareModulesWithMetadata(final DistributionSet distributionSet) { + final Map> softwareModuleMetadata = + softwareModuleManagement.findMetaDataBySoftwareModuleIdsAndTargetVisible( + distributionSet.getModules().stream() + .map(SoftwareModule::getId) + .toList()); + return distributionSet.getModules().stream() + .collect(Collectors.toMap( + Function.identity(), + module -> softwareModuleMetadata.getOrDefault(module.getId(), Collections.emptyMap()))); } private void sendBatchUpdateMessage( final Map actions, final List targets, - final Map> modules) { - + final Map> modules) { final List dmfTargets = targets.stream() .filter(target -> IpUtil.isAmqpUri(target.getAddress())) .map(t -> convertToDmfTarget(t, actions.get(t.getControllerId()).getId())) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java index 208523036..8f25c286b 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java @@ -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.DistributionSet; 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.security.SystemSecurityContext; import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails; @@ -351,7 +350,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService { .flatMap(ds -> ds.getModules().stream()) .map(SoftwareModule::getId) .collect(Collectors.toSet()); - final Map> getSoftwareModuleMetadata = + final Map> getSoftwareModuleMetadata = allSmIds.isEmpty() ? Collections.emptyMap() : controllerManagement.findTargetVisibleMetaDataBySoftwareModuleId(allSmIds); amqpMessageDispatcherService.sendMultiActionRequestToTarget(target, actions, module -> getSoftwareModuleMetadata.get(module.getId())); @@ -373,11 +372,11 @@ public class AmqpMessageHandlerService extends BaseAmqpService { } } - private Map> getSoftwareModulesWithMetadata(final DistributionSet distributionSet) { + private Map> getSoftwareModulesWithMetadata(final DistributionSet distributionSet) { final List smIds = distributionSet.getModules().stream().map(SoftwareModule::getId).toList(); - final Map> metadata = controllerManagement.findTargetVisibleMetaDataBySoftwareModuleId(smIds); + final Map> metadata = controllerManagement.findTargetVisibleMetaDataBySoftwareModuleId(smIds); 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()))); } diff --git a/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java b/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java index c4152e708..340b0ffa1 100644 --- a/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java +++ b/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java @@ -16,6 +16,7 @@ import java.io.InputStream; import java.text.MessageFormat; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Optional; 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.util.PagingUtility; import org.eclipse.hawkbit.repository.ArtifactManagement; -import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.SoftwareModuleManagement; import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; 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.ArtifactUpload; 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.rest.json.model.ResponseList; import org.springframework.data.domain.Page; @@ -67,7 +68,6 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { private final ArtifactUrlHandler artifactUrlHandler; private final MgmtSoftwareModuleMapper mgmtSoftwareModuleMapper; private final SystemManagement systemManagement; - private final EntityFactory entityFactory; MgmtSoftwareModuleResource( final ArtifactManagement artifactManagement, @@ -75,14 +75,13 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { final SoftwareModuleTypeManagement softwareModuleTypeManagement, final ArtifactUrlHandler artifactUrlHandler, final MgmtSoftwareModuleMapper mgmtSoftwareModuleMapper, - final SystemManagement systemManagement, final EntityFactory entityFactory) { + final SystemManagement systemManagement) { this.artifactManagement = artifactManagement; this.softwareModuleManagement = softwareModuleManagement; this.softwareModuleTypeManagement = softwareModuleTypeManagement; this.artifactUrlHandler = artifactUrlHandler; this.mgmtSoftwareModuleMapper = mgmtSoftwareModuleMapper; this.systemManagement = systemManagement; - this.entityFactory = entityFactory; } @Override @@ -138,12 +137,12 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { public ResponseEntity getArtifact(final Long softwareModuleId, final Long artifactId, final Boolean useArtifactUrlHandler) { 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 (Boolean.TRUE.equals(useArtifactUrlHandler)) { - MgmtSoftwareModuleMapper.addLinks(module.getArtifact(artifactId).get(), response, artifactUrlHandler, systemManagement); + MgmtSoftwareModuleMapper.addLinks(module.getArtifact(artifactId).orElseThrow(), response, artifactUrlHandler, systemManagement); } 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 public void createMetadata(final Long softwareModuleId, final List metadataRest) { - softwareModuleManagement.createMetadata(MgmtSoftwareModuleMapper.fromRequestSwMetadata(entityFactory, softwareModuleId, metadataRest)); + softwareModuleManagement.createMetadata(softwareModuleId, MgmtSoftwareModuleMapper.fromRequestSwMetadata(metadataRest)); } @Override @@ -244,27 +243,24 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { // check if software module exists otherwise throw exception immediately findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); - final List metadata = softwareModuleManagement.getMetadata(softwareModuleId); + final Map metadata = softwareModuleManagement.getMetadata(softwareModuleId); return ResponseEntity.ok(new PagedList<>(MgmtSoftwareModuleMapper.toResponseSwMetadata(metadata), metadata.size())); } @Override public ResponseEntity getMetadataValue(final Long softwareModuleId, final String metadataKey) { - final SoftwareModuleMetadata findOne = softwareModuleManagement.getMetadata(softwareModuleId).stream() - .filter(entry -> entry.getKey().equals(metadataKey)) - .findFirst() - .orElseThrow(() -> new EntityNotFoundException("SoftwareModule metadata", softwareModuleId + ":" + metadataKey)); + final MetadataValue metadataValue = softwareModuleManagement.getMetadata(softwareModuleId).get(metadataKey); + if (metadataValue == null) { + throw new EntityNotFoundException("SoftwareModule metadata", softwareModuleId + ":" + metadataKey); + } - return ResponseEntity.ok(MgmtSoftwareModuleMapper.toResponseSwMetadata(findOne)); + return ResponseEntity.ok(MgmtSoftwareModuleMapper.toResponseSwMetadata(metadataKey, metadataValue)); } @Override public void updateMetadata(final Long softwareModuleId, final String metadataKey, final MgmtSoftwareModuleMetadataBodyPut metadata) { - softwareModuleManagement.updateMetadata( - entityFactory.softwareModuleMetadata() - .update(softwareModuleId, metadataKey) - .value(metadata.getValue()) - .targetVisible(metadata.getTargetVisible())); + softwareModuleManagement.createMetadata( + softwareModuleId, metadataKey, new MetadataValueCreate(metadata.getValue(), metadata.getTargetVisible())); } @Override diff --git a/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/mapper/MgmtSoftwareModuleMapper.java b/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/mapper/MgmtSoftwareModuleMapper.java index 16e59339a..36a5851c4 100644 --- a/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/mapper/MgmtSoftwareModuleMapper.java +++ b/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/mapper/MgmtSoftwareModuleMapper.java @@ -15,6 +15,8 @@ import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; 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.resource.MgmtDownloadArtifactResource; 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.SoftwareModuleTypeManagement; 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.ArtifactUrlHandler; 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.model.Artifact; 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.rest.json.model.ResponseList; import org.springframework.hateoas.Link; @@ -59,17 +60,14 @@ public final class MgmtSoftwareModuleMapper { this.softwareModuleTypeManagement = softwareModuleTypeManagement; } - public static List fromRequestSwMetadata( - final EntityFactory entityFactory, final Long softwareModuleId, final Collection metadata) { + public static Map fromRequestSwMetadata(final Collection metadata) { if (metadata == null) { - return Collections.emptyList(); + return Collections.emptyMap(); } - return metadata.stream() - .map(metadataRest -> entityFactory.softwareModuleMetadata().create(softwareModuleId) - .key(metadataRest.getKey()).value(metadataRest.getValue()) - .targetVisible(metadataRest.isTargetVisible())) - .toList(); + return metadata.stream().collect(Collectors.toMap( + MgmtSoftwareModuleMetadata::getKey, + metadataRest -> new MetadataValueCreate(metadataRest.getValue(), metadataRest.isTargetVisible()))); } public List smFromRequest(final Collection smsRest) { @@ -88,17 +86,17 @@ public final class MgmtSoftwareModuleMapper { return new ResponseList<>(softwareModules.stream().map(MgmtSoftwareModuleMapper::toResponse).toList()); } - public static List toResponseSwMetadata(final Collection metadata) { + public static List toResponseSwMetadata(final Map metadata) { if (metadata == null) { 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(); - metadataRest.setKey(metadata.getKey()); + metadataRest.setKey(key); metadataRest.setValue(metadata.getValue()); metadataRest.setTargetVisible(metadata.isTargetVisible()); return metadataRest; diff --git a/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java b/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java index 55118edb7..477cc3775 100644 --- a/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java +++ b/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java @@ -55,7 +55,8 @@ import org.eclipse.hawkbit.repository.model.ArtifactUpload; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetType; 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.Target; 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(); for (int index = 0; index < totalMetadata; index++) { - softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(module.getId()) - .key(knownKeyPrefix + index).value(knownValuePrefix + index)); + softwareModuleManagement.createMetadata(module.getId(), knownKeyPrefix + index, new MetadataValueCreate(knownValuePrefix + index)); } 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(); for (int index = 0; index < totalMetadata; index++) { - softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(module.getId()) - .key(knownKeyPrefix + index).value(knownValuePrefix + index)); + softwareModuleManagement.createMetadata(module.getId(), knownKeyPrefix + index, new MetadataValueCreate(knownValuePrefix + index)); } mvc.perform(get(MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/metadata", @@ -206,8 +205,7 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes final String knownKey = "knownKey"; final String knownValue = "knownValue"; final SoftwareModule module = findFirstModuleByType(testdataFactory.createDistributionSet("one"), osType).orElseThrow(); - softwareModuleManagement.updateMetadata( - entityFactory.softwareModuleMetadata().create(module.getId()).key(knownKey).value(knownValue)); + softwareModuleManagement.createMetadata(module.getId(), knownKey, new MetadataValueCreate(knownValue)); mvc.perform(get(MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/metadata/{metadataKey}", module.getId(), knownKey)) @@ -1424,9 +1422,9 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes .andExpect(status().isCreated()); 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)) - .as("Metadata key is wrong").extracting(SoftwareModuleMetadata::getValue).isEqualTo(knownValue2); + .as("Metadata key is wrong").extracting(MetadataValue::getValue).isEqualTo(knownValue2); // verify quota enforcement 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) assertThat(softwareModuleManagement.getMetadata(sm.getId()).size()).isEqualTo(metaData1.length()); - } /** @@ -1457,19 +1454,18 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes final String updateValue = "valueForUpdate"; final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); - softwareModuleManagement.updateMetadata( - entityFactory.softwareModuleMetadata().create(sm.getId()).key(knownKey).value(knownValue)); + softwareModuleManagement.createMetadata(sm.getId(), knownKey, new MetadataValueCreate(knownValue)); final JSONObject jsonObject = new JSONObject().put("key", knownKey) .put("value", updateValue) .put("targetVisible", true); - mvc.perform(put("/rest/v1/softwaremodules/{swId}/metadata/{key}", sm.getId(), knownKey).accept( - MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON).content(jsonObject.toString())) + mvc.perform(put("/rest/v1/softwaremodules/{swId}/metadata/{key}", sm.getId(), knownKey) + .accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON).content(jsonObject.toString())) .andDo(MockMvcResultPrinter.print()) .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.isTargetVisible()).as("target visible is wrong").isTrue(); } @@ -1484,8 +1480,7 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes final String knownValue = "knownValue"; final long smId = testdataFactory.createSoftwareModuleOs().getId(); - softwareModuleManagement.updateMetadata( - entityFactory.softwareModuleMetadata().create(smId).key(knownKey).value(knownValue)); + softwareModuleManagement.createMetadata(smId, knownKey, new MetadataValueCreate(knownValue)); mvc.perform(delete("/rest/v1/softwaremodules/{swId}/metadata/{key}", smId, knownKey)) .andDo(MockMvcResultPrinter.print()) @@ -1505,8 +1500,7 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes final String knownValue = "knownValue"; final long smId = testdataFactory.createSoftwareModuleOs().getId(); - softwareModuleManagement.updateMetadata( - entityFactory.softwareModuleMetadata().create(smId).key(knownKey).value(knownValue)); + softwareModuleManagement.createMetadata(smId, knownKey, new MetadataValueCreate(knownValue)); mvc.perform(delete("/rest/v1/softwaremodules/{swId}/metadata/XXX", smId, knownKey)) .andDo(MockMvcResultPrinter.print()) diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java index ad48e328c..d1d1ba501 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java @@ -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.AutoConfirmationStatus; 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.TargetUpdateStatus; import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties; @@ -69,14 +68,13 @@ public interface ControllerManagement { Optional getSoftwareModule(long moduleId); /** - * Retrieves {@link SoftwareModuleMetadata} where {@link SoftwareModuleMetadata#isTargetVisible()}. + * Retrieves software module metadata where isTargetVisible. * * @param moduleId of the {@link SoftwareModule} - * @return the map of software module id to {@link SoftwareModuleMetadata} with maximum size of - * {@link RepositoryConstants#MAX_META_DATA_COUNT} + * @return the map of software module id to software module metadata with maximum size of {@link RepositoryConstants#MAX_META_DATA_COUNT} */ @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) - Map> findTargetVisibleMetaDataBySoftwareModuleId(@NotNull Collection moduleId); + Map> findTargetVisibleMetaDataBySoftwareModuleId(@NotNull Collection moduleId); /** * Simple addition of a new {@link ActionStatus} entry to the {@link Action}. No state changes. diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/EntityFactory.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/EntityFactory.java index 8b1340b3e..03b651b37 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/EntityFactory.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/EntityFactory.java @@ -12,7 +12,6 @@ package org.eclipse.hawkbit.repository; import org.eclipse.hawkbit.repository.builder.ActionStatusBuilder; import org.eclipse.hawkbit.repository.builder.RolloutBuilder; 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.TargetBuilder; import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder; @@ -31,11 +30,6 @@ public interface EntityFactory { */ ActionStatusBuilder actionStatus(); - /** - * @return {@link SoftwareModuleMetadataBuilder} object - */ - SoftwareModuleMetadataBuilder softwareModuleMetadata(); - /** * @return {@link TagBuilder} object */ diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryConstants.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryConstants.java index 9b188f982..31101d07f 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryConstants.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryConstants.java @@ -9,14 +9,17 @@ */ package org.eclipse.hawkbit.repository; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.DistributionSetType; -import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; +import org.eclipse.hawkbit.repository.model.SoftwareModule; /** * Repository constants. */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) public final class RepositoryConstants { /** @@ -46,12 +49,7 @@ public final class RepositoryConstants { /** * Maximum number of metadata entries provided to controllers. * - * @see SoftwareModuleMetadata#isTargetVisible() + * @see SoftwareModule.MetadataValueCreate#isTargetVisible() */ public static final int MAX_META_DATA_COUNT = 50; - - private RepositoryConstants() { - // Utility class. - } - -} +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java index a3ee059a9..a08f9ad71 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java @@ -10,7 +10,6 @@ package org.eclipse.hawkbit.repository; import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.Optional; @@ -25,8 +24,6 @@ import lombok.Getter; import lombok.ToString; import lombok.experimental.SuperBuilder; 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.EntityAlreadyExistsException; 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.NamedVersionedEntity; 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.Type; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -53,27 +49,27 @@ public interface SoftwareModuleManagement return "SOFTWARE_MODULE"; } + /** + * Creates or updates a distribution set meta-data value. + * + * @param id the software module id which meta-data has to be updated + * @param key the key of the meta-data entry to be updated + * @param value the meta-data value to be updated + * @throws EntityNotFoundException in case the meta-data entry does not exist and cannot be updated + */ + @PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY) + void createMetadata(@NotNull Long id, @NotEmpty String key, @NotNull @Valid MetadataValue value); + /** * Creates a list of software module meta-data entries. * * @param metadata the meta-data entries to create * @throws EntityAlreadyExistsException in case one of the meta-data entry already exists for the specific key * @throws EntityNotFoundException if software module with given ID does not exist - * @throws AssignmentQuotaExceededException if the maximum number of {@link SoftwareModuleMetadata} entries is exceeded for the addressed - * {@link SoftwareModule} + * @throws AssignmentQuotaExceededException if the maximum number of meta-data entries is exceeded for the addressed {@link SoftwareModule} */ @PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY) - void createMetadata(@NotNull @Valid Collection 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 getMetadata(long id); + void createMetadata(@NotNull Long id, @NotEmpty @Valid Map metadata); /** * Finds all meta-data by the given software module id and key. @@ -84,52 +80,30 @@ public interface SoftwareModuleManagement * @throws EntityNotFoundException if software module with given ID does not exist ot the */ @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 pageable the page request to page the result * @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) - Page findMetaDataBySoftwareModuleIdAndTargetVisible(long id, @NotNull Pageable pageable); - - /** - * 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); + Map getMetadata(@NotNull Long id); /** * Deletes a software module meta-data entry. * * @param id where meta-data has to be deleted * @param key of the meta-data element - * @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 */ @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> findMetaDataBySoftwareModuleIdsAndTargetVisible(Collection ids); /** * Locks a software module. @@ -198,9 +172,6 @@ public interface SoftwareModuleManagement @PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY) Slice findByType(long typeId, @NotNull Pageable pageable); - @PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY) - Map> findMetaDataBySoftwareModuleIdsAndTargetVisible(Collection moduleIds); - /** * Returns count of all modules assigned to given {@link DistributionSet}. * diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/SoftwareModuleMetadataBuilder.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/SoftwareModuleMetadataBuilder.java deleted file mode 100644 index 2c8e40d63..000000000 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/SoftwareModuleMetadataBuilder.java +++ /dev/null @@ -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); -} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/SoftwareModuleMetadataCreate.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/SoftwareModuleMetadataCreate.java deleted file mode 100644 index 3e77be031..000000000 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/SoftwareModuleMetadataCreate.java +++ /dev/null @@ -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(); -} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/SoftwareModuleMetadataUpdate.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/SoftwareModuleMetadataUpdate.java deleted file mode 100644 index 05cb7212e..000000000 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/SoftwareModuleMetadataUpdate.java +++ /dev/null @@ -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); -} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java index b247b417f..c008a5bf8 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java @@ -12,6 +12,11 @@ package org.eclipse.hawkbit.repository.model; import java.util.List; import java.util.Optional; +import jakarta.validation.constraints.Size; + +import lombok.Data; +import lombok.NoArgsConstructor; + /** * 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())) .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; + } + } } \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModuleMetadata.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModuleMetadata.java deleted file mode 100644 index 91ee113af..000000000 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModuleMetadata.java +++ /dev/null @@ -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 true if element is visible for targets as part of {@link Action}. - */ - boolean isTargetVisible(); -} diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/GenericSoftwareModuleMetadataUpdate.java b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/GenericSoftwareModuleMetadataUpdate.java deleted file mode 100644 index bd26860e5..000000000 --- a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/GenericSoftwareModuleMetadataUpdate.java +++ /dev/null @@ -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 - implements SoftwareModuleMetadataUpdate { - - public GenericSoftwareModuleMetadataUpdate(final long softwareModuleId, final String key) { - super.softwareModuleId = softwareModuleId; - this.key = key; - } - -} diff --git a/hawkbit-repository/hawkbit-repository-jpa-ql/src/main/java/org/eclipse/hawkbit/repository/jpa/ql/SpecificationBuilder.java b/hawkbit-repository/hawkbit-repository-jpa-ql/src/main/java/org/eclipse/hawkbit/repository/jpa/ql/SpecificationBuilder.java index 976be4141..914daa00b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa-ql/src/main/java/org/eclipse/hawkbit/repository/jpa/ql/SpecificationBuilder.java +++ b/hawkbit-repository/hawkbit-repository-jpa-ql/src/main/java/org/eclipse/hawkbit/repository/jpa/ql/SpecificationBuilder.java @@ -149,17 +149,16 @@ public class SpecificationBuilder { if (comparison.getValue() == null) { // map entry with key is null (doesn't exist) / is not null (exists) - use left join with on return switch (op) { - case EQ -> cb.isNull(pathResolver.getJoinOn(attribute, split[1])); - case NE -> cb.isNotNull(pathResolver.getJoinOn(attribute, split[1])); + case EQ -> cb.isNull(toMapValuePath(pathResolver.getJoinOn(attribute, split[1]))); + case NE -> cb.isNotNull(toMapValuePath(pathResolver.getJoinOn(attribute, split[1]))); default -> throw new RSQLParameterSyntaxException( String.format("Operator %s is not supported for map fields with value null", op)); }; } else { final MapJoin mapPath = (MapJoin) pathResolver.getPath(attribute); - final Path valuePath = (Path) mapPath.value(); return isNot(op) - ? compare(comparison, pathResolver.getJoinOnInner(attribute, split[1])) - : cb.and(equal(mapPath.key(), split[1]), compare(comparison, valuePath)); + ? compare(comparison, toMapValuePath(pathResolver.getJoinOnInner(attribute, split[1]))) + : cb.and(equal(mapPath.key(), split[1]), compare(comparison, toMapValuePath(mapPath))); } } else if (attribute instanceof SetAttribute setAttribute) { if (split.length < 2 || ObjectUtils.isEmpty(split[1])) { @@ -185,6 +184,12 @@ public class SpecificationBuilder { } } + @SuppressWarnings("unchecked") + private static Path toMapValuePath(final Path mapJoin) { + final Path valuePath = ((MapJoin) mapJoin).value(); + return valuePath.getJavaType() == String.class ? (Path) valuePath : valuePath.get("value"); + } + private Predicate compare(final Comparison comparison, final Path fieldPath) { final List values = getValues(comparison, fieldPath.getJavaType()); final Object firstValue = values.get(0); diff --git a/hawkbit-repository/hawkbit-repository-jpa-ql/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/legacy/JpaQueryRsqlVisitor.java b/hawkbit-repository/hawkbit-repository-jpa-ql/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/legacy/JpaQueryRsqlVisitor.java index 7b270febe..ecbba586e 100644 --- a/hawkbit-repository/hawkbit-repository-jpa-ql/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/legacy/JpaQueryRsqlVisitor.java +++ b/hawkbit-repository/hawkbit-repository-jpa-ql/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/legacy/JpaQueryRsqlVisitor.java @@ -343,8 +343,7 @@ public class JpaQueryRsqlVisitor & RsqlQueryField, T> extends final Predicate mapPredicate = mapToMapPredicate(fieldPath, queryPath); - - final Predicate valuePredicate = addOperatorPredicate(node, fieldPath, transformedValues, value, queryPath); + final Predicate valuePredicate = addOperatorPredicate(node, queryPath.getEnumValue().isMap() ? (Path)toMapValuePath(fieldPath) : fieldPath, transformedValues, value, queryPath); return toSingleList(mapPredicate != null ? cb.and(mapPredicate, valuePredicate) : valuePredicate); } @@ -380,6 +379,16 @@ public class JpaQueryRsqlVisitor & RsqlQueryField, T> extends } } + @SuppressWarnings("unchecked") + private static Path toMapValuePath(final Path fieldPath) { + final Path mapValuePath = ((MapJoin) pathOfString(fieldPath)).value(); + if (mapValuePath.getJavaType() == String.class) { + return (Path) mapValuePath; + } else { + return mapValuePath.get("value"); + } + } + private Predicate getOutPredicate( final List transformedValues, final QueryPath queryPath, final Path fieldPath) { @@ -496,8 +505,7 @@ public class JpaQueryRsqlVisitor & RsqlQueryField, T> extends @SuppressWarnings({ "rawtypes", "unchecked" }) private Expression getExpressionToCompare(final Path innerFieldPath, final A enumField) { if (enumField.isMap()){ - // Currently we support only string key. So below cast is safe. - return (Expression) (((MapJoin) pathOfString(innerFieldPath)).value()); + return toMapValuePath(innerFieldPath); } else { return pathOfString(innerFieldPath); } diff --git a/hawkbit-repository/hawkbit-repository-jpa-ql/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/legacy/JpaQueryRsqlVisitorG2.java b/hawkbit-repository/hawkbit-repository-jpa-ql/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/legacy/JpaQueryRsqlVisitorG2.java index f261feac9..8fec04fc0 100644 --- a/hawkbit-repository/hawkbit-repository-jpa-ql/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/legacy/JpaQueryRsqlVisitorG2.java +++ b/hawkbit-repository/hawkbit-repository-jpa-ql/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/legacy/JpaQueryRsqlVisitorG2.java @@ -129,7 +129,7 @@ public class JpaQueryRsqlVisitorG2 & RsqlQueryField, T> if (values.get(0) == null) { // IS operator for maps and null value is treated as doesn't exist correspondingly ((PluralJoin) fieldPath).on(toMapEntryKeyPredicate(queryPath, fieldPath)); - return cb.isNull(fieldPath); + return cb.isNull(toMapValuePath(fieldPath)); } } else if (node.getOperator() == NOT) { if (values.size() != 1) { @@ -139,10 +139,10 @@ public class JpaQueryRsqlVisitorG2 & RsqlQueryField, T> ((PluralJoin) fieldPath).on(toMapEntryKeyPredicate(queryPath, fieldPath)); if (values.get(0) == null) { // special handling of "exists" - return cb.isNotNull(fieldPath); + return cb.isNotNull(toMapValuePath(fieldPath)); } else { // 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); @@ -150,11 +150,21 @@ public class JpaQueryRsqlVisitorG2 & RsqlQueryField, T> 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); } + @SuppressWarnings("unchecked") + private static Path toMapValuePath(final Path fieldPath) { + final Path mapValuePath = ((MapJoin) pathOfString(fieldPath)).value(); + if (mapValuePath.getJavaType() == String.class) { + return (Path) mapValuePath; + } else { + return mapValuePath.get("value"); + } + } + @SuppressWarnings("unchecked") private Predicate toMapEntryKeyPredicate(final QueryPath queryPath, final Path fieldPath) { final String[] graph = queryPath.getJpaPath(); @@ -341,7 +351,7 @@ public class JpaQueryRsqlVisitorG2 & RsqlQueryField, T> private Path getExpressionToCompare(final A enumField, final Path fieldPath) { if (enumField.isMap()) { // Currently we support only string key. So below cast is safe. - return (Path) (((MapJoin) fieldPath).value()); + return toMapValuePath(fieldPath); } else { return pathOfString(fieldPath); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java index 624880002..80f1e265a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java @@ -13,7 +13,6 @@ import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.builder.ActionStatusBuilder; import org.eclipse.hawkbit.repository.builder.RolloutBuilder; 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.TargetBuilder; import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder; @@ -33,19 +32,16 @@ public class JpaEntityFactory implements EntityFactory { private final TargetBuilder targetBuilder; private final TargetTypeBuilder targetTypeBuilder; private final TargetFilterQueryBuilder targetFilterQueryBuilder; - private final SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder; private final RolloutBuilder rolloutBuilder; @SuppressWarnings("java:S107") public JpaEntityFactory( final TargetBuilder targetBuilder, final TargetTypeBuilder targetTypeBuilder, final TargetFilterQueryBuilder targetFilterQueryBuilder, - final SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder, final RolloutBuilder rolloutBuilder) { this.targetBuilder = targetBuilder; this.targetTypeBuilder = targetTypeBuilder; this.targetFilterQueryBuilder = targetFilterQueryBuilder; - this.softwareModuleMetadataBuilder = softwareModuleMetadataBuilder; this.rolloutBuilder = rolloutBuilder; } @Override @@ -53,11 +49,6 @@ public class JpaEntityFactory implements EntityFactory { return new JpaActionStatusBuilder(); } - @Override - public SoftwareModuleMetadataBuilder softwareModuleMetadata() { - return softwareModuleMetadataBuilder; - } - @Override public TagBuilder tag() { return (TagBuilder)new JpaTagBuilder(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRepositoryConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRepositoryConfiguration.java index 299d5c2b9..82fd45c92 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRepositoryConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRepositoryConfiguration.java @@ -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.autoassign.AutoAssignExecutor; 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.TargetFilterQueryBuilder; 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.CleanupTask; 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.JpaTargetFilterQueryBuilder; 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.AfterTransactionCommitExecutor; 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.JpaArtifact; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; @@ -323,12 +320,6 @@ public class JpaRepositoryConfiguration { return new JpaTargetTypeBuilder(dsTypeManagement); } - @Bean - SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder( - final JpaSoftwareModuleManagement softwareModuleManagement) { - return new JpaSoftwareModuleMetadataBuilder(softwareModuleManagement); - } - /** * @param distributionSetManagement for loading {@link Rollout#getDistributionSet()} * @return RolloutBuilder bean @@ -461,11 +452,9 @@ public class JpaRepositoryConfiguration { EntityFactory entityFactory( final TargetBuilder targetBuilder, final TargetTypeBuilder targetTypeBuilder, final TargetFilterQueryBuilder targetFilterQueryBuilder, - final SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder, final RolloutBuilder rolloutBuilder) { return new JpaEntityFactory( - targetBuilder, targetTypeBuilder, targetFilterQueryBuilder, - softwareModuleMetadataBuilder, rolloutBuilder); + targetBuilder, targetTypeBuilder, targetFilterQueryBuilder, rolloutBuilder); } /** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleMetadataBuilder.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleMetadataBuilder.java deleted file mode 100644 index 81ccf74d3..000000000 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleMetadataBuilder.java +++ /dev/null @@ -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); - } -} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleMetadataCreate.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleMetadataCreate.java deleted file mode 100644 index 437b2e82e..000000000 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleMetadataCreate.java +++ /dev/null @@ -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 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)); - } -} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java index 364b82fb2..2e790cef8 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java @@ -54,6 +54,7 @@ import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper; import org.eclipse.hawkbit.repository.QuotaManagement; import org.eclipse.hawkbit.repository.RepositoryConstants; import org.eclipse.hawkbit.repository.RepositoryProperties; +import org.eclipse.hawkbit.repository.SoftwareModuleManagement; import org.eclipse.hawkbit.repository.TargetTypeManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; 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.repository.ActionRepository; 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.TargetRepository; 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.DistributionSetAssignmentResult; 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.TargetType; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; @@ -143,7 +142,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont private final DeploymentManagement deploymentManagement; private final ConfirmationManagement confirmationManagement; private final SoftwareModuleRepository softwareModuleRepository; - private final SoftwareModuleMetadataRepository softwareModuleMetadataRepository; + private final SoftwareModuleManagement softwareModuleManagement; private final DistributionSetManagement distributionSetManagement; private final TenantConfigurationManagement tenantConfigurationManagement; private final ControllerPollProperties controllerPollProperties; @@ -162,7 +161,8 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont final RepositoryProperties repositoryProperties, final TargetRepository targetRepository, final TargetTypeManagement targetTypeManagement, final DeploymentManagement deploymentManagement, final ConfirmationManagement confirmationManagement, - final SoftwareModuleRepository softwareModuleRepository, final SoftwareModuleMetadataRepository softwareModuleMetadataRepository, + final SoftwareModuleRepository softwareModuleRepository, + final SoftwareModuleManagement softwareModuleManagement, final DistributionSetManagement distributionSetManagement, final TenantConfigurationManagement tenantConfigurationManagement, final ControllerPollProperties controllerPollProperties, final PlatformTransactionManager txManager, final EntityFactory entityFactory, final EntityManager entityManager, @@ -176,7 +176,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont this.deploymentManagement = deploymentManagement; this.confirmationManagement = confirmationManagement; this.softwareModuleRepository = softwareModuleRepository; - this.softwareModuleMetadataRepository = softwareModuleMetadataRepository; + this.softwareModuleManagement = softwareModuleManagement; this.distributionSetManagement = distributionSetManagement; this.tenantConfigurationManagement = tenantConfigurationManagement; this.controllerPollProperties = controllerPollProperties; @@ -281,11 +281,8 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont } @Override - public Map> findTargetVisibleMetaDataBySoftwareModuleId(final Collection moduleId) { - return softwareModuleMetadataRepository - .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()))); + public Map> findTargetVisibleMetaDataBySoftwareModuleId(final Collection moduleId) { + return systemSecurityContext.runAsSystem(() -> softwareModuleManagement.findMetaDataBySoftwareModuleIdsAndTargetVisible(moduleId)); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java index 9120ca6b7..be36dca94 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java @@ -15,37 +15,30 @@ import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_M import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.UnaryOperator; import java.util.stream.Collectors; import jakarta.persistence.EntityManager; import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.QuotaManagement; -import org.eclipse.hawkbit.repository.RepositoryConstants; import org.eclipse.hawkbit.repository.SoftwareModuleFields; import org.eclipse.hawkbit.repository.SoftwareModuleManagement; 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.LockedException; import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper; 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.JpaSoftwareModuleMetadata; -import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata_; -import org.eclipse.hawkbit.repository.jpa.model.SwMetadataCompositeKey; +import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule.JpaMetadataValue; import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder; 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.SoftwareModuleTypeRepository; 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.DistributionSet; 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.utils.ObjectCopyUtil; import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty; import org.springframework.dao.ConcurrencyFailureException; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.jpa.domain.Specification; @@ -71,13 +64,13 @@ import org.springframework.util.ObjectUtils; @Service @ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "software-module-management" }, matchIfMissing = true) public class JpaSoftwareModuleManagement - extends AbstractJpaRepositoryManagement + extends + AbstractJpaRepositoryManagement implements SoftwareModuleManagement { protected static final String SOFTWARE_MODULE_METADATA = "SoftwareModuleMetadata"; private final DistributionSetRepository distributionSetRepository; - private final SoftwareModuleMetadataRepository softwareModuleMetadataRepository; private final SoftwareModuleTypeRepository softwareModuleTypeRepository; private final ArtifactManagement artifactManagement; private final QuotaManagement quotaManagement; @@ -86,12 +79,10 @@ public class JpaSoftwareModuleManagement final SoftwareModuleRepository softwareModuleRepository, final EntityManager entityManager, final DistributionSetRepository distributionSetRepository, - final SoftwareModuleMetadataRepository softwareModuleMetadataRepository, final SoftwareModuleTypeRepository softwareModuleTypeRepository, final ArtifactManagement artifactManagement, final QuotaManagement quotaManagement) { super(softwareModuleRepository, entityManager); this.distributionSetRepository = distributionSetRepository; - this.softwareModuleMetadataRepository = softwareModuleMetadataRepository; this.softwareModuleTypeRepository = softwareModuleTypeRepository; this.artifactManagement = artifactManagement; this.quotaManagement = quotaManagement; @@ -176,86 +167,97 @@ public class JpaSoftwareModuleManagement @Override @Transactional @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY)) - public void createMetadata(final Collection create) { - // group by software module id to minimize database access - create.stream() - .map(JpaSoftwareModuleMetadataCreate.class::cast) - .collect(Collectors.groupingBy(JpaSoftwareModuleMetadataCreate::getSoftwareModuleId)) - .forEach((id, createsForSoftwareModule) -> { - assertSoftwareModuleExists(id); - assertMetadataQuota(id, createsForSoftwareModule.size()); - - // touch to update revision and last modified timestamp - JpaManagementHelper.touch( - entityManager, jpaRepository, jpaRepository.findById(id).orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id))); - createsForSoftwareModule.forEach(this::saveMetadata); - }); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public List 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 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); + public void createMetadata(final Long id, final String key, final MetadataValue value) { + final JpaSoftwareModule softwareModule = jpaRepository + .findById(id) + .orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)); + final Map metadataValueMap = softwareModule.getMetadata(); + final JpaMetadataValue existingValue = metadataValueMap.get(key); + if (existingValue == null) { + assertMetadataQuota(metadataValueMap.size() + 1L); + } + final JpaMetadataValue jpaMetadataValue = existingValue == null ? + new JpaMetadataValue() : existingValue; + if (ObjectCopyUtil.copy(value, jpaMetadataValue, true, UnaryOperator.identity())) { + metadataValueMap.put(key, jpaMetadataValue); + jpaRepository.save(softwareModule); + } } @Override @Transactional @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY)) - public SoftwareModuleMetadata updateMetadata(final SoftwareModuleMetadataCreate c) { - final JpaSoftwareModuleMetadataCreate create = (JpaSoftwareModuleMetadataCreate) c; - final Long id = create.getSoftwareModuleId(); + public void createMetadata(final Long id, final Map metadata) { + final JpaSoftwareModule softwareModule = jpaRepository + .findById(id) + .orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)); + final Map metadataValueMap = softwareModule.getMetadata(); + assertMetadataQuota(metadata.keySet().stream().filter(key -> !metadataValueMap.containsKey(key)).count() + metadataValueMap.size()); + final AtomicBoolean changed = new AtomicBoolean(false); + metadata.forEach((key, value) -> { + final JpaMetadataValue jpaMetadataValue = metadataValueMap.getOrDefault(key, new JpaMetadataValue()); + if (ObjectCopyUtil.copy(value, jpaMetadataValue, true, UnaryOperator.identity())) { + metadataValueMap.put(key, jpaMetadataValue); + changed.set(true); + } + }); + if (changed.get()) { + jpaRepository.save(softwareModule); + } + } - assertSoftwareModuleExists(id); - assertMetadataQuota(id, 1); + @Override + public MetadataValue getMetadata(final Long id, final String key) { + final JpaSoftwareModule softwareModule = jpaRepository + .findById(id) + .orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)); + final MetadataValue metadataValue = softwareModule.getMetadata().get(key); + if (metadataValue == null) { + throw new EntityNotFoundException(SOFTWARE_MODULE_METADATA, id + ":" + key); + } else { + return metadataValue; + } + } - // touch to update revision and last modified timestamp - JpaManagementHelper.touch( - entityManager, jpaRepository, jpaRepository.findById(id).orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id))); - return saveMetadata(create); + @Override + public Map getMetadata(final Long id) { + return jpaRepository + .findById(id) + .map(JpaSoftwareModule::getMetadata) + .map(metadata -> metadata.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + e -> (MetadataValue)e.getValue()))) + .orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)); } @Override @Transactional @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY)) - public SoftwareModuleMetadata updateMetadata(final SoftwareModuleMetadataUpdate u) { - final GenericSoftwareModuleMetadataUpdate update = (GenericSoftwareModuleMetadataUpdate) u; - - // check if exists otherwise throw entity not found exception - final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) findMetadata( - update.getSoftwareModuleId(), update.getKey()) - .orElseThrow(() -> new EntityNotFoundException(SOFTWARE_MODULE_METADATA, update.getSoftwareModuleId() + ":" + update.getKey())); - - update.getValue().ifPresent(metadata::setValue); - update.isTargetVisible().ifPresent(metadata::setTargetVisible); - - JpaManagementHelper.touch(entityManager, jpaRepository, metadata.getSoftwareModule()); - return softwareModuleMetadataRepository.save(metadata); + public void deleteMetadata(final Long id, final String key) { + final JpaSoftwareModule softwareModule = jpaRepository + .findById(id) + .orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)); + final Map metadataValueMap = softwareModule.getMetadata(); + if (!metadataValueMap.containsKey(key)) { + throw new EntityNotFoundException(SOFTWARE_MODULE_METADATA, id + ":" + key); + } + metadataValueMap.remove(key); + jpaRepository.save(softwareModule); } + // called only with 'system code' access, so no need to check access control @Override - @Transactional - @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY)) - public void deleteMetadata(final long id, final String key) { - final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) findMetadata(id, key) - .orElseThrow(() -> new EntityNotFoundException(SOFTWARE_MODULE_METADATA, id + ":" + key)); - - JpaManagementHelper.touch(entityManager, jpaRepository, metadata.getSoftwareModule()); - softwareModuleMetadataRepository.deleteById(metadata.getId()); + public Map> findMetaDataBySoftwareModuleIdsAndTargetVisible(final Collection ids) { + return jpaRepository.findVisibleMetadataByModuleIds(ids) + .stream() + .collect(Collectors.groupingBy( + entry -> (Long) entry[0], + Collectors.toMap( + entry -> (String) entry[1], + entry -> (String) entry[2], + (existing, replacement) -> existing, // in case of duplicates, keep the first one + HashMap::new))); } @Override @@ -340,14 +342,6 @@ public class JpaSoftwareModuleManagement ); } - @Override - public Map> findMetaDataBySoftwareModuleIdsAndTargetVisible(final Collection 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 public long countByAssignedTo(final long distributionSetId) { assertDistributionSetExists(distributionSetId); @@ -355,10 +349,6 @@ public class JpaSoftwareModuleManagement return jpaRepository.count(SoftwareModuleSpecification.byAssignedToDs(distributionSetId)); } - private static Specification metadataBySoftwareModuleIdSpec(final long id) { - return (root, query, cb) -> cb.equal(root.get(JpaSoftwareModuleMetadata_.softwareModule).get(AbstractJpaBaseEntity_.id), id); - } - private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) { jpaRepository.getAccessController().ifPresent(accessController -> accessController.assertOperationAllowed(AccessController.Operation.DELETE, swModule)); @@ -374,41 +364,14 @@ public class JpaSoftwareModuleManagement smFilterNameAndVersionEntries[1]); } - private SoftwareModuleMetadata saveMetadata(final JpaSoftwareModuleMetadataCreate create) { - assertSoftwareModuleMetadataDoesNotExist(create.getSoftwareModuleId(), create); - return softwareModuleMetadataRepository.save(create.build()); - } - - private Optional 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. * - * @param id The software module ID. * @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(); - QuotaHelper.assertAssignmentQuota( - 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); - } + QuotaHelper.assertAssignmentQuota(requested, maxMetaData, SoftwareModule.MetadataValueCreate.class, SoftwareModule.class); } private void assertSoftwareModuleTypeExists(final Long typeId) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java index 0043d7514..8658fefcc 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java @@ -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 * available under the terms of the Eclipse Public License 2.0 @@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa.model; import java.io.Serial; +import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -20,6 +21,7 @@ import jakarta.persistence.CollectionTable; import jakarta.persistence.Column; import jakarta.persistence.ConstraintMode; import jakarta.persistence.ElementCollection; +import jakarta.persistence.Embeddable; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.ForeignKey; @@ -36,7 +38,7 @@ import jakarta.persistence.UniqueConstraint; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; -import lombok.AccessLevel; +import lombok.Data; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -102,8 +104,7 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement joinColumns = { @JoinColumn(name = "sm", nullable = false) }, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_sm_metadata_sm")) @MapKeyColumn(name = "meta_key", length = SoftwareModule.METADATA_KEY_MAX_SIZE) - @Column(name = "meta_value", length = SoftwareModule.METADATA_VALUE_MAX_SIZE) - private Map metadata; + private Map metadata; @Column(name = "locked") private boolean locked; @@ -192,4 +193,18 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement public void fireDeleteEvent() { 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; + } } \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModuleMetadata.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModuleMetadata.java deleted file mode 100644 index b2cb566eb..000000000 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModuleMetadata.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.repository.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()); - } -} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/SwMetadataCompositeKey.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/SwMetadataCompositeKey.java deleted file mode 100644 index 46df6fd26..000000000 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/SwMetadataCompositeKey.java +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/SoftwareModuleMetadataRepository.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/SoftwareModuleMetadataRepository.java deleted file mode 100644 index 9abcff017..000000000 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/SoftwareModuleMetadataRepository.java +++ /dev/null @@ -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, - CrudRepository, - JpaSpecificationExecutor { - - /** - * 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 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. - *

- * 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 findBySoftwareModuleIdInAndTargetVisible( - @Param("moduleId") Collection moduleId, @Param("targetVisible") boolean targetVisible, Pageable page); - - /** - * Counts the meta-data entries that are associated with the addressed software module. - *

- * 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); -} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/SoftwareModuleRepository.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/SoftwareModuleRepository.java index eefcc7fb2..60e4ebb5c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/SoftwareModuleRepository.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/SoftwareModuleRepository.java @@ -9,6 +9,9 @@ */ package org.eclipse.hawkbit.repository.jpa.repository; +import java.util.Collection; +import java.util.List; + import jakarta.persistence.EntityManager; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; @@ -47,6 +50,9 @@ public interface SoftwareModuleRepository extends BaseEntityRepository findVisibleMetadataByModuleIds(@Param("ids") Collection ids); + /** * Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety * reasons (this is a "delete everything" query after all) we add the tenant diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java index 95a0d08d7..eb1b0ec3b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java @@ -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.RolloutRepository; 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.SoftwareModuleTypeRepository; import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository; @@ -111,8 +110,6 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest @Autowired protected DistributionSetTagRepository distributionSetTagRepository; @Autowired - protected SoftwareModuleMetadataRepository softwareModuleMetadataRepository; - @Autowired protected ActionStatusRepository actionStatusRepository; @Autowired protected LocalArtifactRepository artifactRepository; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/ControllerManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/ControllerManagementTest.java index b3c16cc8a..b4a0893a9 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/ControllerManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/ControllerManagementTest.java @@ -73,7 +73,6 @@ import org.eclipse.hawkbit.repository.model.ArtifactUpload; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; 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.TargetUpdateStatus; import org.eclipse.hawkbit.repository.test.matcher.Expect; @@ -989,7 +988,7 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { final DistributionSet set = testdataFactory.createDistributionSet(); testdataFactory.addSoftwareModuleMetadata(set); - final Map> result = controllerManagement + final Map> result = controllerManagement .findTargetVisibleMetaDataBySoftwareModuleId(set.getModules().stream().map(SoftwareModule::getId).toList()); assertThat(result).hasSize(3); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/SoftwareManagementSecurityTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/SoftwareManagementSecurityTest.java index f270244ca..fd4236fc1 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/SoftwareManagementSecurityTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/SoftwareManagementSecurityTest.java @@ -10,12 +10,15 @@ package org.eclipse.hawkbit.repository.jpa.management; import java.util.List; +import java.util.Map; 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.SoftwareModuleManagement; import org.eclipse.hawkbit.repository.jpa.AbstractRepositoryManagementSecurityTest; import org.eclipse.hawkbit.repository.model.SoftwareModule; +import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValueCreate; import org.junit.jupiter.api.Test; /** @@ -46,11 +49,15 @@ class SoftwareManagementSecurityTest @Test void createMetaDataPermissionsCheck() { 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)); assertPermissions(() -> { softwareModuleManagement.createMetadata( - List.of(entityFactory.softwareModuleMetadata().create(1L).key("key").value("value"))); + 1L, + Map.of("key", new MetadataValueCreate("value"))); return null; }, List.of(SpPermission.UPDATE_REPOSITORY)); } @@ -112,15 +119,6 @@ class SoftwareManagementSecurityTest 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. */ @@ -157,7 +155,10 @@ class SoftwareManagementSecurityTest @Test void updateMetaDataPermissionsCheck() { assertPermissions( - () -> softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().update(1L, "key").value("value")), + () -> { + softwareModuleManagement.createMetadata(1L, "key", new MetadataValueCreate("value")); + return null; + }, List.of(SpPermission.UPDATE_REPOSITORY)); } @@ -167,6 +168,6 @@ class SoftwareManagementSecurityTest @Test void findMetaDataBySoftwareModuleIdsAndTargetVisiblePermissionsCheck() { assertPermissions(() -> softwareModuleManagement.findMetaDataBySoftwareModuleIdsAndTargetVisible(List.of(1L)), - List.of(SpPermission.READ_REPOSITORY)); + List.of(SpRole.SYSTEM_ROLE)); } } \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/SoftwareModuleManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/SoftwareModuleManagementTest.java index 3133e460d..d7b22efc5 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/SoftwareModuleManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/SoftwareModuleManagementTest.java @@ -14,11 +14,11 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayInputStream; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; @@ -26,7 +26,6 @@ import jakarta.validation.ConstraintViolationException; import org.eclipse.hawkbit.repository.DistributionSetManagement; 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.exception.AssignmentQuotaExceededException; 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.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.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.DistributionSet; 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.Target; 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.util.WithUser; import org.junit.jupiter.api.Test; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; /** * Feature: Component Tests - Repository
@@ -62,8 +59,6 @@ import org.springframework.data.domain.PageRequest; */ 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 * of Optional not present. @@ -72,14 +67,10 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { @ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) }) void nonExistingEntityAccessReturnsNotPresent() { final SoftwareModule module = testdataFactory.createSoftwareModuleApp(); - assertThat(softwareModuleManagement.get(1234L)).isNotPresent(); - - assertThat(softwareModuleManagement.findByNameAndVersionAndType(NOT_EXIST_ID, NOT_EXIST_ID, osType.getId())) - .isNotPresent(); - - assertThatExceptionOfType(EntityNotFoundException.class) - .isThrownBy(() -> softwareModuleManagement.getMetadata(module.getId(), NOT_EXIST_ID)); + assertThat(softwareModuleManagement.findByNameAndVersionAndType(NOT_EXIST_ID, NOT_EXIST_ID, osType.getId())).isNotPresent(); + final Long moduleId = module.getId(); + assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> softwareModuleManagement.getMetadata(moduleId, NOT_EXIST_ID)); } /** @@ -89,7 +80,7 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { @Test @ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) }) void entityQueriesReferringToNotExistingEntitiesThrowsException() { - final SoftwareModule module = testdataFactory.createSoftwareModuleApp(); + testdataFactory.createSoftwareModuleApp(); final SoftwareModuleManagement.Create noType = SoftwareModuleManagement.Create.builder().name("xxx").type(null).build(); final List noTypeList = List.of(noType); @@ -98,22 +89,20 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> softwareModuleManagement.create(noType)); verifyThrownExceptionBy( - () -> softwareModuleManagement.updateMetadata( - entityFactory.softwareModuleMetadata().create(NOT_EXIST_IDL).key("xxx").value("xxx")), "SoftwareModule"); + () -> softwareModuleManagement.createMetadata( + NOT_EXIST_IDL, "xxx", new MetadataValueCreate("xxx")), "SoftwareModule"); verifyThrownExceptionBy( - () -> softwareModuleManagement.createMetadata(Collections.singletonList( - entityFactory.softwareModuleMetadata().create(NOT_EXIST_IDL).key("xxx").value("xxx"))), "SoftwareModule"); + () -> softwareModuleManagement.createMetadata( + NOT_EXIST_IDL, + Map.of("xxx", new MetadataValueCreate("xxx"))), "SoftwareModule"); verifyThrownExceptionBy(() -> softwareModuleManagement.delete(NOT_EXIST_IDL), "SoftwareModule"); verifyThrownExceptionBy(() -> softwareModuleManagement.delete(Collections.singletonList(NOT_EXIST_IDL)), "SoftwareModule"); verifyThrownExceptionBy(() -> softwareModuleManagement.deleteMetadata(NOT_EXIST_IDL, "xxx"), "SoftwareModule"); verifyThrownExceptionBy( - () -> softwareModuleManagement.updateMetadata( - entityFactory.softwareModuleMetadata().update(NOT_EXIST_IDL, "xxx").value("xxx")), "SoftwareModule"); - verifyThrownExceptionBy( - () -> softwareModuleManagement.updateMetadata( - entityFactory.softwareModuleMetadata().update(module.getId(), NOT_EXIST_ID).value("xxx")), "SoftwareModuleMetadata"); + () -> softwareModuleManagement.createMetadata( + NOT_EXIST_IDL, "xxx", new MetadataValueCreate("xxx")), "SoftwareModule"); verifyThrownExceptionBy(() -> softwareModuleManagement.findByAssignedTo(NOT_EXIST_IDL, PAGE), "DistributionSet"); @@ -241,8 +230,8 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { testdataFactory.createSoftwareModuleApp(); assertThat((List) softwareModuleManagement.findByType(osType.getId(), PAGE).getContent()) - .as("Expected to find the following number of modules:").hasSize(2).as("with the following elements") - .contains(two, one); + .as("Expected to find the following number of modules:").hasSize(2) + .as("with the following elements").contains(two, one); } /** @@ -525,30 +514,21 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { assertThat(softwareModule.getOptLockRevision()).isEqualTo(1); - final SoftwareModuleMetadataCreate swMetadata1 = entityFactory.softwareModuleMetadata() - .create(softwareModule.getId()) - .key(knownKey1) - .value(knownValue1) - .targetVisible(true); - - final SoftwareModuleMetadataCreate swMetadata2 = entityFactory.softwareModuleMetadata() - .create(softwareModule.getId()) - .key(knownKey2) - .value(knownValue2); - - softwareModuleManagement.createMetadata(Arrays.asList(swMetadata1, swMetadata2)); + softwareModuleManagement.createMetadata( + softwareModule.getId(), + Map.of( + knownKey1, new MetadataValueCreate(knownValue1, true), + knownKey2, new MetadataValueCreate(knownValue2))); final SoftwareModule changedLockRevisionModule = softwareModuleManagement.get(softwareModule.getId()).get(); assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2); assertThat(softwareModuleManagement.getMetadata(softwareModule.getId(), knownKey1)).satisfies(metadata -> { - assertThat(metadata.getKey()).isEqualTo(knownKey1); assertThat(metadata.getValue()).isEqualTo(knownValue1); assertThat(metadata.isTargetVisible()).isTrue(); }); assertThat(softwareModuleManagement.getMetadata(softwareModule.getId(), knownKey2)).satisfies(metadata -> { - assertThat(metadata.getKey()).isEqualTo(knownKey2); assertThat(metadata.getValue()).isEqualTo(knownValue2); assertThat(metadata.isTargetVisible()).isFalse(); }); @@ -559,80 +539,49 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { */ @Test void createSoftwareModuleMetadataUntilQuotaIsExceeded() { - - // add meta data one by one + // add meta-data one by one final SoftwareModule module = testdataFactory.createSoftwareModuleApp("m1"); final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule(); for (int i = 0; i < maxMetaData; ++i) { - softwareModuleManagement.updateMetadata( - entityFactory.softwareModuleMetadata().create(module.getId()).key("k" + i).value("v" + i)); + softwareModuleManagement.createMetadata(module.getId(), "k" + i, new MetadataValueCreate("v" + i)); } // quota exceeded - final SoftwareModuleMetadataCreate metadata = entityFactory.softwareModuleMetadata().create(module.getId()) - .key("k" + maxMetaData).value("v" + maxMetaData); + final Long moduleId = module.getId(); + final MetadataValueCreate metadata = new MetadataValueCreate("v" + maxMetaData); assertThatExceptionOfType(AssignmentQuotaExceededException.class) - .isThrownBy(() -> softwareModuleManagement.updateMetadata(metadata)); + .isThrownBy(() -> softwareModuleManagement.createMetadata(moduleId, "k" + maxMetaData, metadata)); // add multiple meta data entries at once final SoftwareModule module2 = testdataFactory.createSoftwareModuleApp("m2"); - final List create = new ArrayList<>(); + final Long moduleId2 = module2.getId(); + final Map create = new HashMap<>(); 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 assertThatExceptionOfType(AssignmentQuotaExceededException.class) - .isThrownBy(() -> softwareModuleManagement.createMetadata(create)); + .isThrownBy(() -> softwareModuleManagement.createMetadata(moduleId2, create)); // add some meta data entries final SoftwareModule module3 = testdataFactory.createSoftwareModuleApp("m3"); final int firstHalf = Math.round((maxMetaData) / 2.f); for (int i = 0; i < firstHalf; ++i) { - softwareModuleManagement.updateMetadata( - entityFactory.softwareModuleMetadata().create(module3.getId()).key("k" + i).value("v" + i)); + softwareModuleManagement.createMetadata(module3.getId(), "k" + i, new MetadataValueCreate("v" + i)); } // add too many data entries + final Long moduleId3 = module3.getId(); final int secondHalf = maxMetaData - firstHalf; - final List create2 = new ArrayList<>(); + final Map create2 = new HashMap<>(); 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 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. */ @@ -649,8 +598,8 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { assertThat(ah.getOptLockRevision()).isEqualTo(1); // create an software module meta data entry - final SoftwareModuleMetadata softwareModuleMetadata = softwareModuleManagement.updateMetadata( - entityFactory.softwareModuleMetadata().create(ah.getId()).key(knownKey).value(knownValue)); + softwareModuleManagement.createMetadata(ah.getId(), knownKey, new MetadataValueCreate(knownValue)); + final MetadataValue softwareModuleMetadata = softwareModuleManagement.getMetadata(ah.getId(), knownKey); assertThat(softwareModuleMetadata.isTargetVisible()).isFalse(); assertThat(softwareModuleMetadata.getValue()).isEqualTo(knownValue); @@ -660,8 +609,8 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2); // update the software module metadata - final SoftwareModuleMetadata updated = softwareModuleManagement.updateMetadata(entityFactory - .softwareModuleMetadata().update(ah.getId(), knownKey).value(knownUpdateValue).targetVisible(true)); + softwareModuleManagement.createMetadata(ah.getId(), knownKey, new MetadataValueCreate(knownUpdateValue, true)); + final MetadataValue updated = softwareModuleManagement.getMetadata(ah.getId(), knownKey); // we are updating the sw metadata so also modifying the base software // module so opt lock revision must be two @@ -672,8 +621,6 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { assertThat(updated).isNotNull(); assertThat(updated.getValue()).isEqualTo(knownUpdateValue); 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(); - softwareModuleManagement.updateMetadata( - entityFactory.softwareModuleMetadata().create(swModule.getId()).key(knownKey1).value(knownValue1)); + softwareModuleManagement.createMetadata(swModule.getId(), knownKey1, new MetadataValueCreate(knownValue1)); - assertThat(softwareModuleManagement.getMetadata(swModule.getId())) + assertThat(softwareModuleManagement.getMetadata(swModule.getId()).entrySet()) .as("Contains the created metadata element").allSatisfy(metadata -> { - assertThat(((JpaSoftwareModuleMetadata) metadata).getSoftwareModule().getId()).isEqualTo(swModule.getId()); assertThat(metadata.getKey()).isEqualTo(knownKey1); - assertThat(metadata.getValue()).isEqualTo(knownValue1); + assertThat(metadata.getValue().getValue()).isEqualTo(knownValue1); }); softwareModuleManagement.deleteMetadata(swModule.getId(), knownKey1); 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. */ @@ -729,28 +657,24 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { final int metadataCountSw2 = 10; for (int index = 0; index < metadataCountSw1; index++) { - softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(sw1.getId()) - .key("key" + index).value("value" + index).targetVisible(true)); + softwareModuleManagement.createMetadata(sw1.getId(), "key" + index, new MetadataValueCreate("value" + index, true)); } for (int index = 0; index < metadataCountSw2; index++) { - softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(sw2.getId()) - .key("key" + index).value("value" + index).targetVisible(false)); + softwareModuleManagement.createMetadata(sw2.getId(), "key" + index, new MetadataValueCreate("value" + index, false)); } - final List metadataSw1 = softwareModuleManagement.getMetadata(sw1.getId()); - final List metadataSw2 = softwareModuleManagement.getMetadata(sw2.getId()); + final Map metadataSw1 = softwareModuleManagement.getMetadata(sw1.getId()); + final Map metadataSw2 = softwareModuleManagement.getMetadata(sw2.getId()); assertThat(metadataSw1).hasSize(metadataCountSw1); assertThat(metadataSw2).hasSize(metadataCountSw2); - final Page metadataSw1V = softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible( - sw1.getId(), PAGE_REQUEST_100); - final Page metadataSw2V = softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible( - sw2.getId(), PAGE_REQUEST_100); - assertThat(metadataSw1V.getNumberOfElements()).isEqualTo(metadataCountSw1); - assertThat(metadataSw1V.getTotalElements()).isEqualTo(metadataCountSw1); - assertThat(metadataSw2V.getNumberOfElements()).isZero(); - assertThat(metadataSw2V.getTotalElements()).isZero(); + final Map metadataSw1V = softwareModuleManagement + .findMetaDataBySoftwareModuleIdsAndTargetVisible(List.of(sw1.getId())).get(sw1.getId()); + final Map metadataSw2V = softwareModuleManagement + .findMetaDataBySoftwareModuleIdsAndTargetVisible(List.of(sw2.getId())).get(sw2.getId()); + assertThat(metadataSw1V).hasSize(metadataCountSw1); + assertThat(metadataSw2V).isNull(); } /** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlSoftwareModuleFieldTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlSoftwareModuleFieldTest.java index 399645e5a..78b0b8ccd 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlSoftwareModuleFieldTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlSoftwareModuleFieldTest.java @@ -13,10 +13,10 @@ import static org.assertj.core.api.Assertions.assertThat; import org.eclipse.hawkbit.repository.SoftwareModuleFields; 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.model.JpaSoftwareModule; 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.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -49,13 +49,8 @@ class RsqlSoftwareModuleFieldTest extends AbstractJpaIntegrationTest { final JpaSoftwareModule ah2 = (JpaSoftwareModule) softwareModuleManagement.create( SoftwareModuleManagement.Create.builder().type(appType).name("agent-hub2").version("1.0.1").description("agent-hub2").build()); - final SoftwareModuleMetadataCreate softwareModuleMetadata = entityFactory.softwareModuleMetadata() - .create(ah.getId()).key("metaKey").value("metaValue"); - softwareModuleManagement.updateMetadata(softwareModuleMetadata); - - final SoftwareModuleMetadataCreate softwareModuleMetadata2 = entityFactory.softwareModuleMetadata() - .create(ah2.getId()).key("metaKey").value("value"); - softwareModuleManagement.updateMetadata(softwareModuleMetadata2); + softwareModuleManagement.createMetadata(ah.getId(), "metaKey", new MetadataValueCreate("metaValue")); + softwareModuleManagement.createMetadata(ah2.getId(), "metaKey", new MetadataValueCreate("value")); } /** @@ -160,7 +155,6 @@ class RsqlSoftwareModuleFieldTest extends AbstractJpaIntegrationTest { assertRSQLQuery(SoftwareModuleFields.METADATA.name() + ".metaKey=in=(metaValue,value)", 2); assertRSQLQuery(SoftwareModuleFields.METADATA.name() + ".metaKey=out=(metaValue,notexist)", 1); assertRSQLQuery(SoftwareModuleFields.METADATA.name() + ".notExist==metaValue", 0); - } private void assertRSQLQuery(final String rsql, final long expectedEntity) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlToSqlTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlToSqlTest.java index 24e91af15..47c8f1677 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlToSqlTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RsqlToSqlTest.java @@ -18,8 +18,10 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import org.eclipse.hawkbit.repository.RsqlQueryField; +import org.eclipse.hawkbit.repository.SoftwareModuleFields; import org.eclipse.hawkbit.repository.TargetFields; 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.ql.utils.HawkbitQlToSql; import org.eclipse.hawkbit.repository.test.TestConfiguration; @@ -45,6 +47,8 @@ class RsqlToSqlTest { @Test 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, "tag!=TAG1 and tag==TAG2"); printFrom(JpaTarget.class, TargetFields.class, "tag==TAG1 and tag!=TAG2"); diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java index 94f5d6c62..4e98dcd41 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java @@ -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.RolloutGroupConditions; 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.Tag; 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}. *

* {@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 SoftwareModuleMetadata#isTargetVisible()} + * {@link MetadataValueCreate#isTargetVisible()} * * @param set to add metadata to */ @@ -1256,10 +1256,14 @@ public class TestdataFactory { } private void addTestModuleMetadata(final SoftwareModule module) { - softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(module.getId()) - .key(VISIBLE_SM_MD_KEY).value(VISIBLE_SM_MD_VALUE).targetVisible(true)); - softwareModuleManagement.updateMetadata(entityFactory.softwareModuleMetadata().create(module.getId()) - .key(INVISIBLE_SM_MD_KEY).value(INVISIBLE_SM_MD_VALUE).targetVisible(false)); + softwareModuleManagement.createMetadata( + module.getId(), + VISIBLE_SM_MD_KEY, + 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) {