Fine grained repository permissions (#2562)

1. Introduce @PrreAuthorize check based on hasPermission - allowing custom processing (compared with non-modifiable hasAuthority/Role processing)
2. Dedicated permissions could be implemented on management api level. Check is made by plugged in PermissionEvaluator
3. Thus common XXX_REPOSITORY permissions could differ for extending services
4. Change create/update entity builder pattern - not via EntityFactory but via clean static lombok based builders (with fine fluent api).
5. Implement abstract repository management jpa class that handles the boilerplate code from extending classes in single place consistently -> AbsreactJpaRepositoryManagement
6. Register management api-s as **Sevice**-s instead of **Bean**-s in order to make easier maintainable and get away from heavy argument forwading
7. Simplify custom hawkbit repository registration + adding proxy to handle exception mapping at lower level - thus not depending on Aspects for converting exceptions
8. Implemented general purpose 'copy' utility (ObjectCopyUtil) that using getter/setter patterns is able to copy (e.g. Create/Update) objects to other objects (e.g. JPA entity objects)
This commit is contained in:
Avgustin Marinov
2025-07-28 14:57:33 +03:00
committed by GitHub
parent 8cdbe54cbe
commit 2b66449ff1
214 changed files with 3456 additions and 4416 deletions

View File

@@ -1,50 +0,0 @@
/**
* Copyright (c) 2021 Bosch.IO 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;
import jakarta.persistence.EntityManager;
import org.eclipse.hawkbit.repository.BaseRepositoryTypeProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.lang.NonNull;
/**
* A {@link JpaRepositoryFactoryBean} extension that allow injection of custom repository factories by using a
* {@link BaseRepositoryTypeProvider} implementation, allows injecting different base repository implementations based on repository type
*/
@SuppressWarnings("java:S119") // java:S119 - ID is inherited from JpaRepositoryFactoryBean
public class CustomBaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID> extends JpaRepositoryFactoryBean<T, S, ID> {
private BaseRepositoryTypeProvider baseRepoProvider;
/**
* Creates a new {@link JpaRepositoryFactoryBean} for the given repository interface.
*
* @param repositoryInterface must not be {@literal null}.
*/
public CustomBaseRepositoryFactoryBean(final Class<? extends T> repositoryInterface) {
super(repositoryInterface);
}
@Autowired // if it is a constructor injection sometimes doesn't work - base repo provider is not available at construct time
public void setBaseRepoProvider(final BaseRepositoryTypeProvider baseRepoProvider) {
this.baseRepoProvider = baseRepoProvider;
}
@Override
protected RepositoryFactorySupport createRepositoryFactory(@NonNull final EntityManager entityManager) {
final RepositoryFactorySupport rfs = super.createRepositoryFactory(entityManager);
rfs.setRepositoryBaseClass(baseRepoProvider.getBaseRepositoryType(getObjectType()));
return rfs;
}
}

View File

@@ -0,0 +1,140 @@
/**
* Copyright (c) 2021 Bosch.IO 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;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.eclipse.hawkbit.repository.jpa.repository.HawkbitBaseRepository;
import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.query.EscapeCharacter;
import org.springframework.data.jpa.repository.query.JpaQueryMethodFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.jpa.repository.support.JpaRepositoryImplementation;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.querydsl.EntityPathResolver;
import org.springframework.data.querydsl.SimpleEntityPathResolver;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport;
import org.springframework.lang.Nullable;
/**
* A {@link TransactionalRepositoryFactoryBeanSupport} extension that uses {@link HawkbitBaseRepository} as base repository and
* proxied repositories in order to convert exceptions to management exceptions.
*/
@SuppressWarnings("java:S119") // java:S119 - ID is inherited from TransactionalRepositoryFactoryBeanSupport
public class HawkbitBaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID> extends TransactionalRepositoryFactoryBeanSupport<T, S, ID> {
private EntityPathResolver entityPathResolver;
private EscapeCharacter escapeCharacter = EscapeCharacter.DEFAULT;
private JpaQueryMethodFactory queryMethodFactory;
@Nullable
private EntityManager entityManager;
/**
* Creates a new {@link JpaRepositoryFactoryBean} for the given repository interface.
*
* @param repositoryInterface must not be {@literal null}.
*/
public HawkbitBaseRepositoryFactoryBean(final Class<? extends T> repositoryInterface) {
super(repositoryInterface);
}
@Autowired
public void setEntityPathResolver(final ObjectProvider<EntityPathResolver> resolver) {
this.entityPathResolver = resolver.getIfAvailable(() -> SimpleEntityPathResolver.INSTANCE);
}
@Autowired
public void setEscapeCharacter(final char escapeCharacter) {
this.escapeCharacter = EscapeCharacter.of(escapeCharacter);
}
@Autowired
public void setQueryMethodFactory(@Nullable final JpaQueryMethodFactory factory) {
if (factory != null) {
this.queryMethodFactory = factory;
}
}
@PersistenceContext
public void setEntityManager(final EntityManager entityManager) {
this.entityManager = entityManager;
}
@Override
public void afterPropertiesSet() {
Objects.requireNonNull(entityManager, "EntityManager must not be null");
super.afterPropertiesSet();
}
@Override
public void setMappingContext(final MappingContext<?, ?> mappingContext) {
super.setMappingContext(mappingContext);
}
@Override
protected RepositoryFactorySupport doCreateRepositoryFactory() {
Objects.requireNonNull(entityManager, "EntityManager must not be null");
final JpaRepositoryFactory jpaRepositoryFactory = new JpaRepositoryFactory(entityManager) {
@Override
protected JpaRepositoryImplementation<?, ?> getTargetRepository(
final RepositoryInformation information, final EntityManager entityManager) {
final JpaRepositoryImplementation<?, ?> jpaRepositoryImplementation = super.getTargetRepository(information, entityManager);
return (JpaRepositoryImplementation<?, ?>) Proxy.newProxyInstance(
jpaRepositoryImplementation.getClass().getClassLoader(),
interfaces(jpaRepositoryImplementation.getClass(), new HashSet<>()).toArray(new Class<?>[0]),
(proxy, method, args) -> {
try {
return method.invoke(jpaRepositoryImplementation, args);
} catch (final InvocationTargetException e) {
final Throwable cause = e.getCause() == null ? e : e.getCause();
throw cause instanceof Exception exc ? ExceptionMapper.map(exc) : e;
} catch (final Exception e) {
throw ExceptionMapper.map(e);
}
});
}
};
jpaRepositoryFactory.setEntityPathResolver(entityPathResolver);
jpaRepositoryFactory.setEscapeCharacter(escapeCharacter);
if (queryMethodFactory != null) {
jpaRepositoryFactory.setQueryMethodFactory(queryMethodFactory);
}
jpaRepositoryFactory.setRepositoryBaseClass(HawkbitBaseRepository.class);
return jpaRepositoryFactory;
}
private static Set<Class<?>> interfaces(final Class<?> type, final Set<Class<?>> interfaces) {
Collections.addAll(interfaces, type.getInterfaces());
final Class<?> superclass = type.getSuperclass();
if (superclass != null && superclass != Object.class) {
return interfaces(superclass, interfaces);
} else {
return interfaces;
}
}
}

View File

@@ -11,21 +11,17 @@ package org.eclipse.hawkbit.repository.jpa;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.builder.ActionStatusBuilder;
import org.eclipse.hawkbit.repository.builder.DistributionSetBuilder;
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeBuilder;
import org.eclipse.hawkbit.repository.builder.RolloutBuilder;
import org.eclipse.hawkbit.repository.builder.RolloutGroupBuilder;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleBuilder;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataBuilder;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeBuilder;
import org.eclipse.hawkbit.repository.builder.TagBuilder;
import org.eclipse.hawkbit.repository.builder.TargetBuilder;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
import org.eclipse.hawkbit.repository.builder.TargetTypeBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleTypeBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTagBuilder;
import org.eclipse.hawkbit.repository.model.Tag;
import org.springframework.validation.annotation.Validated;
/**
@@ -37,26 +33,19 @@ public class JpaEntityFactory implements EntityFactory {
private final TargetBuilder targetBuilder;
private final TargetTypeBuilder targetTypeBuilder;
private final TargetFilterQueryBuilder targetFilterQueryBuilder;
private final SoftwareModuleBuilder softwareModuleBuilder;
private final SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder;
private final DistributionSetBuilder distributionSetBuilder;
private final DistributionSetTypeBuilder distributionSetTypeBuilder;
private final RolloutBuilder rolloutBuilder;
@SuppressWarnings("java:S107")
public JpaEntityFactory(
final TargetBuilder targetBuilder, final TargetTypeBuilder targetTypeBuilder,
final TargetFilterQueryBuilder targetFilterQueryBuilder,
final SoftwareModuleBuilder softwareModuleBuilder, final SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder,
final DistributionSetBuilder distributionSetBuilder, final DistributionSetTypeBuilder distributionSetTypeBuilder,
final SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder,
final RolloutBuilder rolloutBuilder) {
this.targetBuilder = targetBuilder;
this.targetTypeBuilder = targetTypeBuilder;
this.targetFilterQueryBuilder = targetFilterQueryBuilder;
this.softwareModuleBuilder = softwareModuleBuilder;
this.softwareModuleMetadataBuilder = softwareModuleMetadataBuilder;
this.distributionSetBuilder = distributionSetBuilder;
this.distributionSetTypeBuilder = distributionSetTypeBuilder;
this.rolloutBuilder = rolloutBuilder;
}
@Override
@@ -64,46 +53,27 @@ public class JpaEntityFactory implements EntityFactory {
return new JpaActionStatusBuilder();
}
@Override
public DistributionSetBuilder distributionSet() {
return distributionSetBuilder;
}
@Override
public SoftwareModuleMetadataBuilder softwareModuleMetadata() {
return softwareModuleMetadataBuilder;
}
@Override
public TagBuilder tag() {
return new JpaTagBuilder();
public TagBuilder<Tag> tag() {
return (TagBuilder)new JpaTagBuilder();
}
@Override
public RolloutGroupBuilder rolloutGroup() {
return new JpaRolloutGroupBuilder();
}
@Override
public DistributionSetTypeBuilder distributionSetType() {
return distributionSetTypeBuilder;
}
@Override
public RolloutBuilder rollout() {
return rolloutBuilder;
}
@Override
public SoftwareModuleBuilder softwareModule() {
return softwareModuleBuilder;
}
@Override
public SoftwareModuleTypeBuilder softwareModuleType() {
return new JpaSoftwareModuleTypeBuilder();
}
@Override
public TargetBuilder target() {
return targetBuilder;

View File

@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.repository.jpa;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
import javax.sql.DataSource;
@@ -19,19 +18,10 @@ import jakarta.persistence.EntityManager;
import jakarta.validation.Validation;
import io.micrometer.core.instrument.MeterRegistry;
import org.aopalliance.intercept.MethodInvocation;
import org.eclipse.hawkbit.ContextAware;
import org.eclipse.hawkbit.repository.artifact.ArtifactRepository;
import org.eclipse.hawkbit.cache.TenancyCacheManager;
import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryption;
import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryptionSecretsStore;
import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryptionService;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.BaseRepositoryTypeProvider;
import org.eclipse.hawkbit.repository.ConfirmationManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.PropertiesQuotaManagement;
@@ -44,20 +34,16 @@ import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutHandler;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.RolloutStatusCache;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TargetTagManagement;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.TenantStatsManagement;
import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryption;
import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryptionSecretsStore;
import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryptionService;
import org.eclipse.hawkbit.repository.autoassign.AutoAssignExecutor;
import org.eclipse.hawkbit.repository.builder.DistributionSetBuilder;
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeBuilder;
import org.eclipse.hawkbit.repository.builder.RolloutBuilder;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleBuilder;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataBuilder;
import org.eclipse.hawkbit.repository.builder.TargetBuilder;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
@@ -73,38 +59,18 @@ import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignScheduler;
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.JpaDistributionSetBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetTypeBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleBuilder;
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;
import org.eclipse.hawkbit.repository.jpa.cluster.LockProperties;
import org.eclipse.hawkbit.repository.jpa.cluster.DistributedLockRepository;
import org.eclipse.hawkbit.repository.jpa.cluster.LockProperties;
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.management.JpaArtifactManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaConfirmationManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaControllerManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaDeploymentManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaDistributionSetInvalidationManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaDistributionSetManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaDistributionSetTagManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaDistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaRolloutGroupManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaRolloutManagement;
import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper;
import org.eclipse.hawkbit.repository.jpa.management.JpaSoftwareModuleManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaSoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaSystemManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaTargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaTargetManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaTargetTagManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaTargetTypeManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaTenantConfigurationManagement;
import org.eclipse.hawkbit.repository.jpa.management.JpaTenantStatsManagement;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
@@ -118,24 +84,16 @@ import org.eclipse.hawkbit.repository.jpa.model.helper.EntityInterceptorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.HawkbitBaseRepository;
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.TargetFilterQueryRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TenantConfigurationRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TenantMetaDataRepository;
import org.eclipse.hawkbit.repository.jpa.rollout.RolloutScheduler;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.PauseRolloutGroupAction;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupActionEvaluator;
@@ -144,25 +102,19 @@ import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupEvaluati
import org.eclipse.hawkbit.repository.jpa.rollout.condition.StartNextGroupRolloutGroupSuccessAction;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.ThresholdRolloutGroupErrorCondition;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.ThresholdRolloutGroupSuccessCondition;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver;
import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.eclipse.hawkbit.utils.TenantConfigHelper;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -172,16 +124,13 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.cache.CacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.integration.jdbc.lock.DefaultLockRepository;
@@ -192,14 +141,19 @@ import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.lang.NonNull;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.security.authorization.AuthorizationDeniedException;
import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
;
/**
* General configuration for hawkBit's Repository.
*/
@EnableJpaRepositories(value = "org.eclipse.hawkbit.repository.jpa.repository", repositoryFactoryBeanClass = CustomBaseRepositoryFactoryBean.class)
@EnableJpaRepositories(value = "org.eclipse.hawkbit.repository.jpa.repository", repositoryFactoryBeanClass = HawkbitBaseRepositoryFactoryBean.class)
@EnableTransactionManagement
@EnableJpaAuditing
@EnableAspectJAutoProxy
@@ -207,8 +161,10 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
@EnableScheduling
@EnableRetry
@EntityScan("org.eclipse.hawkbit.repository.jpa.model")
@ComponentScan("org.eclipse.hawkbit.repository.jpa.management")
@PropertySource("classpath:/hawkbit-jpa-defaults.properties")
@Import({ JpaConfiguration.class, RepositoryConfiguration.class, LockProperties.class, DataSourceAutoConfiguration.class, SystemManagementCacheKeyGenerator.class })
@Import({ JpaConfiguration.class, RepositoryConfiguration.class, LockProperties.class, DataSourceAutoConfiguration.class,
SystemManagementCacheKeyGenerator.class })
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class JpaRepositoryConfiguration {
@@ -224,7 +180,8 @@ public class JpaRepositoryConfiguration {
// methods shall not be called - we need the validator in future
processor.setValidator(Validation.byDefaultProvider()
.configure()
.addProperty(org.hibernate.validator.BaseHibernateValidatorConfiguration.ALLOW_PARALLEL_METHODS_DEFINE_PARAMETER_CONSTRAINTS, "true")
.addProperty(org.hibernate.validator.BaseHibernateValidatorConfiguration.ALLOW_PARALLEL_METHODS_DEFINE_PARAMETER_CONSTRAINTS,
"true")
.buildValidatorFactory()
.getValidator());
return processor;
@@ -270,7 +227,8 @@ public class JpaRepositoryConfiguration {
@Bean
@ConditionalOnProperty(name = "hawkbit.lock", havingValue = "distributed", matchIfMissing = true)
@ConditionalOnMissingBean
LockRepository lockRepository(final DataSource dataSource, final LockProperties lockProperties, final PlatformTransactionManager txManager) {
LockRepository lockRepository(final DataSource dataSource, final LockProperties lockProperties,
final PlatformTransactionManager txManager) {
final DefaultLockRepository repository = new DistributedLockRepository(dataSource, lockProperties, txManager);
repository.setPrefix("SP_");
return repository;
@@ -279,7 +237,7 @@ public class JpaRepositoryConfiguration {
@Bean
@ConditionalOnMissingBean
public LockRegistry lockRegistry(final Optional<LockRepository> lockRepository) {
return lockRepository.<LockRegistry>map(JdbcLockRegistry::new).orElseGet(DefaultLockRegistry::new);
return lockRepository.<LockRegistry> map(JdbcLockRegistry::new).orElseGet(DefaultLockRegistry::new);
}
@Bean
@@ -351,17 +309,6 @@ public class JpaRepositoryConfiguration {
return e -> e instanceof TargetPollEvent && !repositoryProperties.isPublishTargetPollEvent();
}
/**
* @param distributionSetTypeManagement to loading the {@link DistributionSetType}
* @param softwareManagement for loading {@link DistributionSet#getModules()}
* @return DistributionSetBuilder bean
*/
@Bean
DistributionSetBuilder distributionSetBuilder(final DistributionSetTypeManagement distributionSetTypeManagement,
final SoftwareModuleManagement softwareManagement) {
return new JpaDistributionSetBuilder(distributionSetTypeManagement, softwareManagement);
}
@Bean
TargetBuilder targetBuilder(final TargetTypeManagement targetTypeManagement) {
return new JpaTargetBuilder(targetTypeManagement);
@@ -378,30 +325,10 @@ public class JpaRepositoryConfiguration {
@Bean
SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder(
final SoftwareModuleManagement softwareModuleManagement) {
final JpaSoftwareModuleManagement softwareModuleManagement) {
return new JpaSoftwareModuleMetadataBuilder(softwareModuleManagement);
}
/**
* @param softwareModuleTypeManagement for loading {@link DistributionSetType#getMandatoryModuleTypes()}
* and {@link DistributionSetType#getOptionalModuleTypes()}
* @return DistributionSetTypeBuilder bean
*/
@Bean
DistributionSetTypeBuilder distributionSetTypeBuilder(
final SoftwareModuleTypeManagement softwareModuleTypeManagement) {
return new JpaDistributionSetTypeBuilder(softwareModuleTypeManagement);
}
/**
* @param softwareModuleTypeManagement for loading {@link SoftwareModule#getType()}
* @return SoftwareModuleBuilder bean
*/
@Bean
SoftwareModuleBuilder softwareModuleBuilder(final SoftwareModuleTypeManagement softwareModuleTypeManagement) {
return new JpaSoftwareModuleBuilder(softwareModuleTypeManagement);
}
/**
* @param distributionSetManagement for loading {@link Rollout#getDistributionSet()}
* @return RolloutBuilder bean
@@ -485,221 +412,6 @@ public class JpaRepositoryConfiguration {
return new ExceptionMappingAspectHandler();
}
/**
* Default {@link BaseRepositoryTypeProvider} bean always provides the NoCountBaseRepository
*
* @return a {@link BaseRepositoryTypeProvider} bean
*/
@Bean
@ConditionalOnMissingBean
BaseRepositoryTypeProvider baseRepositoryTypeProvider() {
return new HawkbitBaseRepository.RepositoryTypeProvider();
}
/**
* {@link JpaSystemManagement} bean.
*
* @return a new {@link SystemManagement}
*/
@Bean
@ConditionalOnMissingBean
SystemManagement systemManagement(
final TargetRepository targetRepository, final TargetTypeRepository targetTypeRepository,
final TargetTagRepository targetTagRepository, final TargetFilterQueryRepository targetFilterQueryRepository,
final SoftwareModuleRepository softwareModuleRepository, final SoftwareModuleTypeRepository softwareModuleTypeRepository,
final DistributionSetRepository distributionSetRepository, final DistributionSetTypeRepository distributionSetTypeRepository,
final DistributionSetTagRepository distributionSetTagRepository, final RolloutRepository rolloutRepository,
final TenantConfigurationRepository tenantConfigurationRepository, final TenantMetaDataRepository tenantMetaDataRepository,
final TenantStatsManagement systemStatsManagement, final SystemManagementCacheKeyGenerator currentTenantCacheKeyGenerator,
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware, final PlatformTransactionManager txManager,
final TenancyCacheManager cacheManager, final RolloutStatusCache rolloutStatusCache,
final EntityManager entityManager, final RepositoryProperties repositoryProperties,
final JpaProperties properties) {
return new JpaSystemManagement(targetRepository, targetTypeRepository, targetTagRepository,
targetFilterQueryRepository, softwareModuleRepository, softwareModuleTypeRepository, distributionSetRepository,
distributionSetTypeRepository, distributionSetTagRepository, rolloutRepository, tenantConfigurationRepository,
tenantMetaDataRepository, systemStatsManagement, currentTenantCacheKeyGenerator, systemSecurityContext,
tenantAware, txManager, cacheManager, rolloutStatusCache, entityManager, repositoryProperties, properties);
}
/**
* {@link JpaDistributionSetManagement} bean.
*
* @return a new {@link DistributionSetManagement}
*/
@Bean
@ConditionalOnMissingBean
DistributionSetManagement distributionSetManagement(
final EntityManager entityManager,
final DistributionSetRepository distributionSetRepository,
final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement,
final DistributionSetTypeManagement distributionSetTypeManagement, final QuotaManagement quotaManagement,
final TargetRepository targetRepository,
final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository,
final SystemSecurityContext systemSecurityContext, final TenantConfigurationManagement tenantConfigurationManagement,
final SoftwareModuleRepository softwareModuleRepository,
final DistributionSetTagRepository distributionSetTagRepository, final RepositoryProperties repositoryProperties) {
return new JpaDistributionSetManagement(entityManager, distributionSetRepository, distributionSetTagManagement,
systemManagement, distributionSetTypeManagement, quotaManagement,
targetRepository, targetFilterQueryRepository, actionRepository,
TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement),
softwareModuleRepository, distributionSetTagRepository, repositoryProperties);
}
/**
* {@link JpaDistributionSetManagement} bean.
*
* @return a new {@link DistributionSetManagement}
*/
@Bean
@ConditionalOnMissingBean
DistributionSetTypeManagement distributionSetTypeManagement(
final DistributionSetTypeRepository distributionSetTypeRepository,
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
final DistributionSetRepository distributionSetRepository, final TargetTypeRepository targetTypeRepository,
final QuotaManagement quotaManagement) {
return new JpaDistributionSetTypeManagement(distributionSetTypeRepository, softwareModuleTypeRepository,
distributionSetRepository, targetTypeRepository, quotaManagement);
}
/**
* {@link JpaTargetTypeManagement} bean.
*
* @return a new {@link TargetTypeManagement}
*/
@Bean
@ConditionalOnMissingBean
TargetTypeManagement targetTypeManagement(final TargetTypeRepository targetTypeRepository,
final TargetRepository targetRepository, final DistributionSetTypeRepository distributionSetTypeRepository,
final QuotaManagement quotaManagement) {
return new JpaTargetTypeManagement(targetTypeRepository, targetRepository, distributionSetTypeRepository, quotaManagement);
}
/**
* {@link JpaTenantStatsManagement} bean.
*
* @return a new {@link TenantStatsManagement}
*/
@Bean
@ConditionalOnMissingBean
TenantStatsManagement tenantStatsManagement(
final TargetRepository targetRepository, final LocalArtifactRepository artifactRepository, final ActionRepository actionRepository,
final TenantAware tenantAware) {
return new JpaTenantStatsManagement(targetRepository, artifactRepository, actionRepository, tenantAware);
}
/**
* {@link JpaTenantConfigurationManagement} bean.
*
* @return a new {@link TenantConfigurationManagement}
*/
@Bean
@ConditionalOnMissingBean
TenantConfigurationManagement tenantConfigurationManagement(
final TenantConfigurationRepository tenantConfigurationRepository,
final TenantConfigurationProperties tenantConfigurationProperties,
final CacheManager cacheManager, final AfterTransactionCommitExecutor afterCommitExecutor,
final ApplicationContext applicationContext) {
return new JpaTenantConfigurationManagement(tenantConfigurationRepository, tenantConfigurationProperties,
cacheManager, afterCommitExecutor, applicationContext);
}
/**
* {@link JpaTargetManagement} bean.
*
* @return a new {@link JpaTargetManagement}
*/
@Bean
@ConditionalOnMissingBean
TargetManagement targetManagement(
final EntityManager entityManager, final QuotaManagement quotaManagement,
final TargetRepository targetRepository,
final RolloutGroupRepository rolloutGroupRepository,
final TargetFilterQueryRepository targetFilterQueryRepository,
final TargetTypeRepository targetTypeRepository, final TargetTagRepository targetTagRepository,
final TenantAware tenantAware, final DistributionSetManagement distributionSetManagement) {
return new JpaTargetManagement(entityManager, distributionSetManagement, quotaManagement, targetRepository,
targetTypeRepository, rolloutGroupRepository, targetFilterQueryRepository,
targetTagRepository, tenantAware);
}
/**
* {@link JpaTargetFilterQueryManagement} bean.
*
* @param targetFilterQueryRepository holding {@link TargetFilterQuery} entities
* @param targetManagement managing {@link Target} entities
* @param distributionSetManagement for auto assign DS access
* @param quotaManagement to access quotas
* @return a new {@link TargetFilterQueryManagement}
*/
@Bean
@ConditionalOnMissingBean
TargetFilterQueryManagement targetFilterQueryManagement(
final TargetFilterQueryRepository targetFilterQueryRepository, final TargetManagement targetManagement,
final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
final TenantConfigurationManagement tenantConfigurationManagement, final RepositoryProperties repositoryProperties,
final SystemSecurityContext systemSecurityContext, final ContextAware contextAware, final AuditorAware<String> auditorAware) {
return new JpaTargetFilterQueryManagement(targetFilterQueryRepository, targetManagement,
distributionSetManagement, quotaManagement, tenantConfigurationManagement, repositoryProperties,
systemSecurityContext, contextAware, auditorAware);
}
/**
* {@link JpaTargetTagManagement} bean.
*
* @return a new {@link TargetTagManagement}
*/
@Bean
@ConditionalOnMissingBean
TargetTagManagement targetTagManagement(final TargetTagRepository targetTagRepository) {
return new JpaTargetTagManagement(targetTagRepository);
}
/**
* {@link JpaDistributionSetTagManagement} bean.
*
* @return a new {@link JpaDistributionSetTagManagement}
*/
@Bean
@ConditionalOnMissingBean
DistributionSetTagManagement distributionSetTagManagement(
final DistributionSetTagRepository distributionSetTagRepository,
final DistributionSetRepository distributionSetRepository) {
return new JpaDistributionSetTagManagement(
distributionSetTagRepository, distributionSetRepository);
}
/**
* {@link JpaSoftwareModuleManagement} bean.
*
* @return a new {@link SoftwareModuleManagement}
*/
@Bean
@ConditionalOnMissingBean
SoftwareModuleManagement softwareModuleManagement(final EntityManager entityManager,
final DistributionSetRepository distributionSetRepository,
final SoftwareModuleRepository softwareModuleRepository,
final SoftwareModuleMetadataRepository softwareModuleMetadataRepository,
final SoftwareModuleTypeRepository softwareModuleTypeRepository, final AuditorAware<String> auditorProvider,
final ArtifactManagement artifactManagement, final QuotaManagement quotaManagement) {
return new JpaSoftwareModuleManagement(entityManager, distributionSetRepository, softwareModuleRepository,
softwareModuleMetadataRepository, softwareModuleTypeRepository, auditorProvider, artifactManagement, quotaManagement);
}
/**
* {@link JpaSoftwareModuleTypeManagement} bean.
*
* @return a new {@link SoftwareModuleTypeManagement}
*/
@Bean
@ConditionalOnMissingBean
SoftwareModuleTypeManagement softwareModuleTypeManagement(
final DistributionSetTypeRepository distributionSetTypeRepository,
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
final SoftwareModuleRepository softwareModuleRepository) {
return new JpaSoftwareModuleTypeManagement(distributionSetTypeRepository, softwareModuleTypeRepository, softwareModuleRepository);
}
@Bean
@ConditionalOnMissingBean
RolloutHandler rolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement,
@@ -726,28 +438,6 @@ public class JpaRepositoryConfiguration {
tenantAware, repositoryProperties);
}
@Bean
@ConditionalOnMissingBean
RolloutManagement rolloutManagement(
final RolloutRepository rolloutRepository,
final RolloutGroupRepository rolloutGroupRepository,
final RolloutApprovalStrategy rolloutApprovalStrategy,
final StartNextGroupRolloutGroupSuccessAction startNextRolloutGroupAction,
final RolloutStatusCache rolloutStatusCache,
final ActionRepository actionRepository,
final TargetManagement targetManagement,
final DistributionSetManagement distributionSetManagement,
final TenantConfigurationManagement tenantConfigurationManagement,
final QuotaManagement quotaManagement,
final AfterTransactionCommitExecutor afterCommit,
final SystemSecurityContext systemSecurityContext, final ContextAware contextAware,
final RepositoryProperties repositoryProperties) {
return new JpaRolloutManagement(rolloutRepository, rolloutGroupRepository, rolloutApprovalStrategy,
startNextRolloutGroupAction, rolloutStatusCache, actionRepository, targetManagement,
distributionSetManagement, tenantConfigurationManagement, quotaManagement, afterCommit,
systemSecurityContext, contextAware, repositoryProperties);
}
/**
* {@link DefaultRolloutApprovalStrategy} bean.
*
@@ -758,90 +448,7 @@ public class JpaRepositoryConfiguration {
RolloutApprovalStrategy rolloutApprovalStrategy(final UserAuthoritiesResolver userAuthoritiesResolver,
final TenantConfigurationManagement tenantConfigurationManagement,
final SystemSecurityContext systemSecurityContext) {
return new DefaultRolloutApprovalStrategy(userAuthoritiesResolver, tenantConfigurationManagement,
systemSecurityContext);
}
/**
* {@link JpaRolloutGroupManagement} bean.
*
* @return a new {@link RolloutGroupManagement}
*/
@Bean
@ConditionalOnMissingBean
RolloutGroupManagement rolloutGroupManagement(final RolloutGroupRepository rolloutGroupRepository,
final RolloutRepository rolloutRepository, final ActionRepository actionRepository,
final TargetRepository targetRepository, final EntityManager entityManager, final RolloutStatusCache rolloutStatusCache) {
return new JpaRolloutGroupManagement(rolloutGroupRepository, rolloutRepository, actionRepository,
targetRepository, entityManager, rolloutStatusCache);
}
/**
* {@link JpaDeploymentManagement} bean.
*
* @return a new {@link DeploymentManagement}
*/
@Bean
@ConditionalOnMissingBean
DeploymentManagement deploymentManagement(final EntityManager entityManager,
final ActionRepository actionRepository,
final DistributionSetManagement distributionSetManagement, final TargetRepository targetRepository,
final ActionStatusRepository actionStatusRepository, final AuditorAware<String> auditorProvider,
final AfterTransactionCommitExecutor afterCommit, final PlatformTransactionManager txManager,
final TenantConfigurationManagement tenantConfigurationManagement, final QuotaManagement quotaManagement,
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware, final AuditorAware<String> auditorAware,
final JpaProperties properties, final RepositoryProperties repositoryProperties) {
return new JpaDeploymentManagement(entityManager, actionRepository, distributionSetManagement, targetRepository, actionStatusRepository,
auditorProvider,
afterCommit, txManager, tenantConfigurationManagement,
quotaManagement, systemSecurityContext, tenantAware, auditorAware, properties.getDatabase(), repositoryProperties);
}
@Bean
@ConditionalOnMissingBean
ConfirmationManagement confirmationManagement(final TargetRepository targetRepository,
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
final RepositoryProperties repositoryProperties, final QuotaManagement quotaManagement,
final EntityManager entityManager, final EntityFactory entityFactory) {
return new JpaConfirmationManagement(targetRepository, actionRepository, actionStatusRepository,
repositoryProperties, quotaManagement, entityManager, entityFactory);
}
/**
* {@link JpaControllerManagement} bean.
*
* @return a new {@link ControllerManagement}
*/
@Bean
@ConditionalOnMissingBean
ControllerManagement controllerManagement(
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement,
final RepositoryProperties repositoryProperties,
final TargetRepository targetRepository, final TargetTypeManagement targetTypeManagement,
final DeploymentManagement deploymentManagement, final ConfirmationManagement confirmationManagement,
final SoftwareModuleRepository softwareModuleRepository, final SoftwareModuleMetadataRepository softwareModuleMetadataRepository,
final DistributionSetManagement distributionSetManagement,
final TenantConfigurationManagement tenantConfigurationManagement, final ControllerPollProperties controllerPollProperties,
final PlatformTransactionManager txManager, final EntityFactory entityFactory, final EntityManager entityManager,
final AfterTransactionCommitExecutor afterCommit,
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware,
final ScheduledExecutorService executorService) {
return new JpaControllerManagement(actionRepository, actionStatusRepository, quotaManagement, repositoryProperties,
targetRepository, targetTypeManagement, deploymentManagement, confirmationManagement, softwareModuleRepository,
softwareModuleMetadataRepository, distributionSetManagement, tenantConfigurationManagement, controllerPollProperties,
txManager,entityFactory, entityManager, afterCommit, systemSecurityContext, tenantAware, executorService);
}
@Bean
@ConditionalOnMissingBean
ArtifactManagement artifactManagement(
final EntityManager entityManager, final PlatformTransactionManager txManager,
final LocalArtifactRepository localArtifactRepository, final SoftwareModuleRepository softwareModuleRepository,
final Optional<ArtifactRepository> artifactRepository,
final QuotaManagement quotaManagement, final TenantAware tenantAware) {
return new JpaArtifactManagement(
entityManager, txManager, localArtifactRepository, softwareModuleRepository, artifactRepository.orElse(null),
quotaManagement, tenantAware);
return new DefaultRolloutApprovalStrategy(userAuthoritiesResolver, tenantConfigurationManagement, systemSecurityContext);
}
/**
@@ -854,11 +461,11 @@ public class JpaRepositoryConfiguration {
EntityFactory entityFactory(
final TargetBuilder targetBuilder, final TargetTypeBuilder targetTypeBuilder,
final TargetFilterQueryBuilder targetFilterQueryBuilder,
final SoftwareModuleBuilder softwareModuleBuilder, final SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder,
final DistributionSetBuilder distributionSetBuilder, final DistributionSetTypeBuilder distributionSetTypeBuilder,
final SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder,
final RolloutBuilder rolloutBuilder) {
return new JpaEntityFactory(targetBuilder, targetTypeBuilder, targetFilterQueryBuilder, softwareModuleBuilder,
softwareModuleMetadataBuilder, distributionSetBuilder, distributionSetTypeBuilder, rolloutBuilder);
return new JpaEntityFactory(
targetBuilder, targetTypeBuilder, targetFilterQueryBuilder,
softwareModuleMetadataBuilder, rolloutBuilder);
}
/**
@@ -976,7 +583,7 @@ public class JpaRepositoryConfiguration {
RolloutScheduler rolloutScheduler(
final SystemManagement systemManagement, final RolloutHandler rolloutHandler, final SystemSecurityContext systemSecurityContext,
@Value("${hawkbit.rollout.executor.thread-pool.size:1}") final int threadPoolSize, final Optional<MeterRegistry> meterRegistry) {
return new RolloutScheduler(rolloutHandler, systemManagement, systemSecurityContext, threadPoolSize, meterRegistry);
return new RolloutScheduler(rolloutHandler, systemManagement, systemSecurityContext, threadPoolSize, meterRegistry);
}
/**
@@ -989,25 +596,6 @@ public class JpaRepositoryConfiguration {
return RsqlUtility.getInstance();
}
/**
* {@link JpaDistributionSetInvalidationManagement} bean.
*
* @return a new {@link JpaDistributionSetInvalidationManagement}
*/
@Bean
@ConditionalOnMissingBean
JpaDistributionSetInvalidationManagement distributionSetInvalidationManagement(
final DistributionSetManagement distributionSetManagement, final RolloutManagement rolloutManagement,
final DeploymentManagement deploymentManagement,
final TargetFilterQueryManagement targetFilterQueryManagement, final ActionRepository actionRepository,
final PlatformTransactionManager txManager, final RepositoryProperties repositoryProperties,
final TenantAware tenantAware, final LockRegistry lockRegistry,
final SystemSecurityContext systemSecurityContext) {
return new JpaDistributionSetInvalidationManagement(distributionSetManagement, rolloutManagement,
deploymentManagement, targetFilterQueryManagement, actionRepository, txManager, repositoryProperties,
tenantAware, lockRegistry, systemSecurityContext);
}
/**
* Default artifact encryption service bean that internally uses {@link ArtifactEncryption} and
* {@link ArtifactEncryptionSecretsStore} beans for {@link SoftwareModule} artifacts encryption/decryption
@@ -1019,4 +607,20 @@ public class JpaRepositoryConfiguration {
ArtifactEncryptionService artifactEncryptionService() {
return ArtifactEncryptionService.getInstance();
}
}
@Bean
ManagementExceptionThrowingMethodAuthorizationDeniedHandler managementExceptionThrowingMethodAuthorizationDeniedHandler() {
return new ManagementExceptionThrowingMethodAuthorizationDeniedHandler();
}
public static class ManagementExceptionThrowingMethodAuthorizationDeniedHandler implements MethodAuthorizationDeniedHandler {
@Override
public Object handleDeniedInvocation(final MethodInvocation methodInvocation, final AuthorizationResult authorizationResult) {
throw ExceptionMapper.mapRe(
authorizationResult instanceof AuthorizationDeniedException denied
? denied
: new AuthorizationDeniedException("Access Denied", authorizationResult));
}
}
}

View File

@@ -9,25 +9,13 @@
*/
package org.eclipse.hawkbit.repository.jpa.aspects;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jakarta.transaction.TransactionManager;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.eclipse.hawkbit.exception.GenericSpServerException;
import org.eclipse.hawkbit.repository.exception.ConcurrentModificationException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper;
import org.springframework.core.Ordered;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.transaction.TransactionSystemException;
/**
* {@link Aspect} catches persistence exceptions and wraps them to custom
@@ -38,27 +26,8 @@ import org.springframework.transaction.TransactionSystemException;
@Aspect
public class ExceptionMappingAspectHandler implements Ordered {
private static final Map<String, String> EXCEPTION_MAPPING = new HashMap<>(4);
/**
* this is required to enable a certain order of exception and to select the most specific mappable exception according to the type
* hierarchy of the exception.
*/
private static final List<Class<?>> MAPPED_EXCEPTION_ORDER = new ArrayList<>(4);
static {
MAPPED_EXCEPTION_ORDER.add(DuplicateKeyException.class);
MAPPED_EXCEPTION_ORDER.add(OptimisticLockingFailureException.class);
MAPPED_EXCEPTION_ORDER.add(AccessDeniedException.class);
EXCEPTION_MAPPING.put(DuplicateKeyException.class.getName(), EntityAlreadyExistsException.class.getName());
EXCEPTION_MAPPING.put(OptimisticLockingFailureException.class.getName(), ConcurrentModificationException.class.getName());
EXCEPTION_MAPPING.put(AccessDeniedException.class.getName(), InsufficientPermissionException.class.getName());
}
/**
* catch exceptions of the {@link TransactionManager} and wrap them to custom exceptions.
* Catches exceptions of the {@link TransactionManager} and wrap them to custom exceptions.
*
* @param ex the thrown and catched exception
* @throws Throwable
@@ -68,48 +37,11 @@ public class ExceptionMappingAspectHandler implements Ordered {
// It is a AspectJ proxy which deals with exceptions.
@SuppressWarnings({ "squid:S00112", "squid:S1162" })
public void catchAndWrapJpaExceptionsService(final Exception ex) throws Throwable {
if (log.isTraceEnabled()) {
log.trace("Handling exception {}", ex.getClass().getName(), ex);
} else {
log.debug("Handling exception {}", ex.getClass().getName());
}
// Workaround for EclipseLink merge where it does not throw ConstraintViolationException directly in case of existing entity update
if (ex instanceof TransactionSystemException transactionSystemException) {
throw replaceWithCauseIfConstraintViolationException(transactionSystemException);
}
for (final Class<?> mappedEx : MAPPED_EXCEPTION_ORDER) {
if (!mappedEx.isAssignableFrom(ex.getClass())) {
continue;
}
if (EXCEPTION_MAPPING.containsKey(mappedEx.getName())) {
throw (Exception) Class.forName(EXCEPTION_MAPPING.get(mappedEx.getName())).getConstructor(Throwable.class).newInstance(ex);
}
log.error("there is no mapping configured for exception class {}", mappedEx.getName());
throw new GenericSpServerException(ex);
}
throw ex;
throw ExceptionMapper.map(ex);
}
@Override
public int getOrder() {
return 1;
}
private static Exception replaceWithCauseIfConstraintViolationException(final TransactionSystemException rex) {
Throwable exception = rex;
do {
final Throwable cause = exception.getCause();
if (cause instanceof jakarta.validation.ConstraintViolationException) {
return (Exception) cause;
}
exception = cause;
} while (exception != null);
return rex;
}
}

View File

@@ -1,43 +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.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.builder.DistributionSetBuilder;
import org.eclipse.hawkbit.repository.builder.DistributionSetCreate;
import org.eclipse.hawkbit.repository.builder.DistributionSetUpdate;
import org.eclipse.hawkbit.repository.builder.GenericDistributionSetUpdate;
import org.eclipse.hawkbit.repository.model.DistributionSet;
/**
* Builder implementation for {@link DistributionSet}.
*/
public class JpaDistributionSetBuilder implements DistributionSetBuilder {
private final DistributionSetTypeManagement distributionSetTypeManagement;
private final SoftwareModuleManagement softwareModuleManagement;
public JpaDistributionSetBuilder(final DistributionSetTypeManagement distributionSetTypeManagement,
final SoftwareModuleManagement softwareManagement) {
this.distributionSetTypeManagement = distributionSetTypeManagement;
this.softwareModuleManagement = softwareManagement;
}
@Override
public DistributionSetUpdate update(final long id) {
return new GenericDistributionSetUpdate(id);
}
@Override
public DistributionSetCreate create() {
return new JpaDistributionSetCreate(distributionSetTypeManagement, softwareModuleManagement);
}
}

View File

@@ -1,78 +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 java.util.Collection;
import java.util.Collections;
import java.util.Optional;
import lombok.Getter;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.ValidString;
import org.eclipse.hawkbit.repository.builder.AbstractDistributionSetUpdateCreate;
import org.eclipse.hawkbit.repository.builder.DistributionSetCreate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.springframework.util.CollectionUtils;
/**
* Create/build implementation.
*/
public class JpaDistributionSetCreate extends AbstractDistributionSetUpdateCreate<DistributionSetCreate> implements DistributionSetCreate {
private final DistributionSetTypeManagement distributionSetTypeManagement;
private final SoftwareModuleManagement softwareModuleManagement;
@Getter
@ValidString
private String type;
JpaDistributionSetCreate(
final DistributionSetTypeManagement distributionSetTypeManagement, final SoftwareModuleManagement softwareManagement) {
this.distributionSetTypeManagement = distributionSetTypeManagement;
this.softwareModuleManagement = softwareManagement;
}
@Override
public DistributionSetCreate type(final String type) {
this.type = type.strip();
return this;
}
@Override
public JpaDistributionSet build() {
return new JpaDistributionSet(
name, version, description,
Optional.ofNullable(type).map(this::findDistributionSetTypeWithExceptionIfNotFound).orElse(null),
findSoftwareModuleWithExceptionIfNotFound(modules),
Optional.ofNullable(requiredMigrationStep).orElse(Boolean.FALSE));
}
private DistributionSetType findDistributionSetTypeWithExceptionIfNotFound(final String distributionSetTypekey) {
return distributionSetTypeManagement.findByKey(distributionSetTypekey)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, distributionSetTypekey));
}
private Collection<SoftwareModule> findSoftwareModuleWithExceptionIfNotFound(final Collection<Long> softwareModuleIds) {
if (CollectionUtils.isEmpty(softwareModuleIds)) {
return Collections.emptyList();
}
final Collection<SoftwareModule> modules = softwareModuleManagement.get(softwareModuleIds);
if (modules.size() < softwareModuleIds.size()) {
throw new EntityNotFoundException(SoftwareModule.class, softwareModuleIds);
}
return modules;
}
}

View File

@@ -1,39 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeBuilder;
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeCreate;
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeUpdate;
import org.eclipse.hawkbit.repository.builder.GenericDistributionSetTypeUpdate;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
/**
* Builder implementation for {@link DistributionSetType}.
*/
public class JpaDistributionSetTypeBuilder implements DistributionSetTypeBuilder {
private final SoftwareModuleTypeManagement softwareModuleTypeManagement;
public JpaDistributionSetTypeBuilder(final SoftwareModuleTypeManagement softwareModuleTypeManagement) {
this.softwareModuleTypeManagement = softwareModuleTypeManagement;
}
@Override
public DistributionSetTypeUpdate update(final long id) {
return new GenericDistributionSetTypeUpdate(id);
}
@Override
public DistributionSetTypeCreate create() {
return new JpaDistributionSetTypeCreate(softwareModuleTypeManagement);
}
}

View File

@@ -1,58 +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 java.util.Collection;
import java.util.Collections;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.builder.AbstractDistributionSetTypeUpdateCreate;
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeCreate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.springframework.util.CollectionUtils;
/**
* Create/build implementation.
*/
public class JpaDistributionSetTypeCreate extends AbstractDistributionSetTypeUpdateCreate<DistributionSetTypeCreate>
implements DistributionSetTypeCreate {
private final SoftwareModuleTypeManagement softwareModuleTypeManagement;
JpaDistributionSetTypeCreate(final SoftwareModuleTypeManagement softwareModuleTypeManagement) {
this.softwareModuleTypeManagement = softwareModuleTypeManagement;
}
@Override
public JpaDistributionSetType build() {
final JpaDistributionSetType result = new JpaDistributionSetType(key, name, description, colour);
findSoftwareModuleTypeWithExceptionIfNotFound(mandatory).forEach(result::addMandatoryModuleType);
findSoftwareModuleTypeWithExceptionIfNotFound(optional).forEach(result::addOptionalModuleType);
return result;
}
private Collection<SoftwareModuleType> findSoftwareModuleTypeWithExceptionIfNotFound(
final Collection<Long> softwareModuleTypeId) {
if (CollectionUtils.isEmpty(softwareModuleTypeId)) {
return Collections.emptyList();
}
final Collection<SoftwareModuleType> module = softwareModuleTypeManagement.get(softwareModuleTypeId);
if (module.size() < softwareModuleTypeId.size()) {
throw new EntityNotFoundException(SoftwareModuleType.class, softwareModuleTypeId);
}
return module;
}
}

View File

@@ -1,39 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.builder.GenericSoftwareModuleUpdate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleBuilder;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleCreate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleUpdate;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
/**
* Builder implementation for {@link SoftwareModule}.
*/
public class JpaSoftwareModuleBuilder implements SoftwareModuleBuilder {
private final SoftwareModuleTypeManagement softwareModuleTypeManagement;
public JpaSoftwareModuleBuilder(final SoftwareModuleTypeManagement softwareModuleTypeManagement) {
this.softwareModuleTypeManagement = softwareModuleTypeManagement;
}
@Override
public SoftwareModuleUpdate update(final long id) {
return new GenericSoftwareModuleUpdate(id);
}
@Override
public SoftwareModuleCreate create() {
return new JpaSoftwareModuleCreate(softwareModuleTypeManagement);
}
}

View File

@@ -1,57 +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 jakarta.validation.ValidationException;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.builder.AbstractSoftwareModuleUpdateCreate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleCreate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
/**
* Create/build implementation.
*/
public class JpaSoftwareModuleCreate extends AbstractSoftwareModuleUpdateCreate<SoftwareModuleCreate> implements SoftwareModuleCreate {
private final SoftwareModuleTypeManagement softwareModuleTypeManagement;
private boolean encrypted;
JpaSoftwareModuleCreate(final SoftwareModuleTypeManagement softwareModuleTypeManagement) {
this.softwareModuleTypeManagement = softwareModuleTypeManagement;
}
@Override
public SoftwareModuleCreate encrypted(final boolean encrypted) {
this.encrypted = encrypted;
return this;
}
@Override
public JpaSoftwareModule build() {
return new JpaSoftwareModule(getSoftwareModuleTypeFromKeyString(type), name, version, description, vendor,
encrypted);
}
public boolean isEncrypted() {
return encrypted;
}
private SoftwareModuleType getSoftwareModuleTypeFromKeyString(final String type) {
if (type == null) {
throw new ValidationException("type cannot be null");
}
return softwareModuleTypeManagement.findByKey(type.trim())
.orElseThrow(() -> new EntityNotFoundException(SoftwareModuleType.class, type.trim()));
}
}

View File

@@ -9,11 +9,11 @@
*/
package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
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;
/**
@@ -21,9 +21,9 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
*/
public class JpaSoftwareModuleMetadataBuilder implements SoftwareModuleMetadataBuilder {
private final SoftwareModuleManagement softwareModuleManagement;
private final JpaSoftwareModuleManagement softwareModuleManagement;
public JpaSoftwareModuleMetadataBuilder(final SoftwareModuleManagement softwareModuleManagement) {
public JpaSoftwareModuleMetadataBuilder(final JpaSoftwareModuleManagement softwareModuleManagement) {
this.softwareModuleManagement = softwareModuleManagement;
}

View File

@@ -9,10 +9,11 @@
*/
package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
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;
@@ -23,16 +24,16 @@ public class JpaSoftwareModuleMetadataCreate
extends AbstractSoftwareModuleMetadataUpdateCreate<SoftwareModuleMetadataCreate>
implements SoftwareModuleMetadataCreate {
private final SoftwareModuleManagement softwareModuleManagement;
private final JpaSoftwareModuleManagement softwareModuleManagement;
JpaSoftwareModuleMetadataCreate(final long softwareModuleId, final SoftwareModuleManagement softwareModuleManagement) {
JpaSoftwareModuleMetadataCreate(final long softwareModuleId, final JpaSoftwareModuleManagement softwareModuleManagement) {
this.softwareModuleManagement = softwareModuleManagement;
this.softwareModuleId = softwareModuleId;
}
@Override
public JpaSoftwareModuleMetadata build() {
final SoftwareModule module = softwareModuleManagement.get(softwareModuleId)
final JpaSoftwareModule module = softwareModuleManagement.get(softwareModuleId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, softwareModuleId));
if (key == null) {

View File

@@ -1,32 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.builder.GenericSoftwareModuleTypeUpdate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeBuilder;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeCreate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeUpdate;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
/**
* Builder implementation for {@link SoftwareModuleType}.
*/
public class JpaSoftwareModuleTypeBuilder implements SoftwareModuleTypeBuilder {
@Override
public SoftwareModuleTypeUpdate update(final long id) {
return new GenericSoftwareModuleTypeUpdate(id);
}
@Override
public SoftwareModuleTypeCreate create() {
return new JpaSoftwareModuleTypeCreate();
}
}

View File

@@ -1,29 +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 lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.builder.AbstractSoftwareModuleTypeUpdateCreate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeCreate;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
/**
* Create/build implementation.
*/
@NoArgsConstructor(access = AccessLevel.PACKAGE)
public class JpaSoftwareModuleTypeCreate extends AbstractSoftwareModuleTypeUpdateCreate<SoftwareModuleTypeCreate>
implements SoftwareModuleTypeCreate {
@Override
public JpaSoftwareModuleType build() {
return new JpaSoftwareModuleType(key, name, description, maxAssignments, colour);
}
}

View File

@@ -13,12 +13,13 @@ import org.eclipse.hawkbit.repository.builder.GenericTagUpdate;
import org.eclipse.hawkbit.repository.builder.TagBuilder;
import org.eclipse.hawkbit.repository.builder.TagCreate;
import org.eclipse.hawkbit.repository.builder.TagUpdate;
import org.eclipse.hawkbit.repository.jpa.model.JpaTag;
import org.eclipse.hawkbit.repository.model.Tag;
/**
* Builder implementation for {@link Tag}.
*/
public class JpaTagBuilder implements TagBuilder {
public class JpaTagBuilder implements TagBuilder<JpaTag> {
@Override
public TagUpdate update(final long id) {
@@ -26,7 +27,7 @@ public class JpaTagBuilder implements TagBuilder {
}
@Override
public TagCreate create() {
public TagCreate<JpaTag> create() {
return new JpaTagCreate();
}
}

View File

@@ -13,27 +13,21 @@ import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.builder.AbstractTagUpdateCreate;
import org.eclipse.hawkbit.repository.builder.TagCreate;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.model.Tag;
/**
* Create/build implementation.
*/
@NoArgsConstructor(access = AccessLevel.PACKAGE)
public class JpaTagCreate extends AbstractTagUpdateCreate<TagCreate> implements TagCreate {
public JpaDistributionSetTag buildDistributionSetTag() {
return new JpaDistributionSetTag(name, description, colour);
}
public class JpaTagCreate extends AbstractTagUpdateCreate<TagCreate<JpaTag>> implements TagCreate<JpaTag> {
public JpaTargetTag buildTargetTag() {
return new JpaTargetTag(name, description, colour);
}
@Override
public Tag build() {
public JpaTag build() {
return new JpaTag(name, description, colour);
}
}
}

View File

@@ -0,0 +1,333 @@
/**
* 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.management;
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jakarta.persistence.EntityManager;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaUpdate;
import jakarta.persistence.criteria.Root;
import jakarta.validation.constraints.NotNull;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.RepositoryManagement;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.Jpa;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.JpaRepositoryConfiguration;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.repository.BaseEntityRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.utils.ObjectCopyUtil;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.security.authorization.method.HandleAuthorizationDenied;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.function.SingletonSupplier;
import org.springframework.validation.annotation.Validated;
/**
* JPA implementation of {@link DistributionSetManagement}.
*
* @param <T> the JPA entity type
* @param <C> the type of the create request
* @param <U> the type of the update request
* @param <R> the type of the entity JPA repository
* @param <A> RSQL query field enum
*/
// Spring AOP doesn't support bridge methods and the AspectJ advices as ExceptionMappingAspectHandler could not handle the
// thrown exception (e.g. to convert AuthorizationDeniedException to InsufficientPermissionException).
// That's why we explictly handle the insufficient permission exception with this @HandleAuthorizationDenied annotation.
@HandleAuthorizationDenied(handlerClass = JpaRepositoryConfiguration.ManagementExceptionThrowingMethodAuthorizationDeniedHandler.class)
@Transactional(readOnly = true)
@Validated
abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity, C, U extends Identifiable<Long>, R extends BaseEntityRepository<T>, A extends Enum<A> & RsqlQueryField>
implements RepositoryManagement<T, C, U> {
public static final String DELETED = "deleted";
protected final R jpaRepository;
protected final EntityManager entityManager;
private final Constructor<T> entityConstructor;
protected AbstractJpaRepositoryManagement(final R jpaRepository, final EntityManager entityManager) {
this.jpaRepository = jpaRepository;
this.entityManager = entityManager;
try {
entityConstructor = jpaRepository.getDomainClass().getConstructor();
// test if method works, if fine - it shall never fail when called later
entityConstructor.newInstance();
} catch (final NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException("JPA domain class " + jpaRepository.getDomainClass() + " shall have public no-args constructor", e);
}
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public T create(final C create) {
return jpaRepository.save(AccessController.Operation.CREATE, jpaEntity(create));
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public List<T> create(final Collection<C> create) {
return jpaRepository.saveAll(AccessController.Operation.CREATE, create.stream().map(this::jpaEntity).toList());
}
@Override
public Optional<T> get(final long id) {
return jpaRepository.findById(id);
}
@Override
public List<T> get(final Collection<Long> ids) {
return findAllById(ids);
}
@Override
public boolean exists(final long id) {
return jpaRepository.existsById(id);
}
@Override
public long count() {
return jpaRepository.count(isNotDeleted().orElse(null));
}
@Override
public long countByRsql(String rsql) {
return jpaRepository.count(JpaManagementHelper.combineWithAnd(rsqlSpec(rsql)));
}
@Override
public Slice<T> findAll(final Pageable pageable) {
return JpaManagementHelper.findAllWithoutCountBySpec(
jpaRepository, isNotDeleted().map(List::of).orElseGet(Collections::emptyList), pageable);
}
@Override
public Page<T> findByRsql(final String rsql, final Pageable pageable) {
return JpaManagementHelper.findAllWithCountBySpec(jpaRepository, rsqlSpec(rsql), pageable);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
@SuppressWarnings("java:S1066") // javaS1066 - better readable that way
public T update(final U update) {
final T entity = jpaRepository
.findById(update.getId())
.orElseThrow(() -> new EntityNotFoundException(managementClass(), update.getId()));
return update(update, entity);
}
protected T update(final Identifiable<Long> update, final T entity) {
// update getId has not setter in target JPA entity but shall have getter and the value shall be the same
// otherwise the Utils will throw an exception that there is no counterpart setter for getId
if (ObjectCopyUtil.copy(update, entity, false, this::attach)) {
return jpaRepository.save(entity);
} else { // otherwise it is not changed, so return the same entity
return entity;
}
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public void delete(final long id) {
delete0(List.of(id));
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public void delete(final Collection<Long> ids) {
delete0(ids);
}
@NotNull
private List<Specification<T>> rsqlSpec(final String rsql) {
return isNotDeleted()
.map(isNotDeleted -> List.of(RsqlUtility.getInstance().<A, T> buildRsqlSpecification(rsql, fieldsClass()), isNotDeleted))
.orElseGet(() -> List.of(RsqlUtility.getInstance().buildRsqlSpecification(rsql, fieldsClass())));
}
// return which are for soft deletion
@SuppressWarnings("java:S1172") // java:S1172 - it is intended to be used by subclasses
protected Collection<T> softDelete(final Collection<T> toDelete) {
return Collections.emptyList();
}
protected void delete0(final Collection<Long> ids) {
if (ObjectUtils.isEmpty(ids)) {
return;
}
final List<T> toDelete = findAllById(ids); // throws EntityNotFoundException if any of these does not exist
jpaRepository.getAccessController().ifPresent(ac -> {
for (final T entity : toDelete) {
ac.assertOperationAllowed(AccessController.Operation.DELETE, entity);
}
});
// mark the rest as hard delete
final Collection<Long> toSoftDelete = softDelete(toDelete).stream().map(T::getId).toList();
if (!toSoftDelete.isEmpty()) {
if (!supportSoftDelete()) {
throw new IllegalStateException("Soft delete is not supported for " + jpaRepository.getDomainClass().getSimpleName());
}
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaUpdate<T> update = cb.createCriteriaUpdate(jpaRepository.getDomainClass());
final Root<T> root = update.from(jpaRepository.getDomainClass());
final CriteriaBuilder.In<Long> in = cb.in(root.get(AbstractJpaBaseEntity_.id));
toSoftDelete.forEach(in::value);
update.set(root.get(DELETED), true).where(in);
entityManager.createQuery(update).executeUpdate();
}
final List<Long> toHardDelete = toDelete.stream()
.map(AbstractJpaBaseEntity::getId)
.filter(id -> !toSoftDelete.contains(id))
.toList();
// hard delete the rest if exists
if (!toHardDelete.isEmpty()) {
// don't give the delete statement an empty list, JPA/Oracle cannot
// handle the empty list
jpaRepository.deleteAllById(toHardDelete);
}
}
private List<T> findAllById(final Collection<Long> ids) {
final List<T> foundDs = jpaRepository.findAllById(ids);
if (foundDs.size() != ids.size()) {
throw new EntityNotFoundException(managementClass(), ids, foundDs.stream().map(T::getId).toList());
}
return foundDs;
}
private final Supplier<Class<T>> managementClassSupplier = SingletonSupplier.of(this::managementClass0);
private Class<T> managementClass() {
return managementClassSupplier.get();
}
@SuppressWarnings("unchecked")
private Class<T> managementClass0() {
return (Class<T>) jpaRepository.getManagementClass();
}
private final Supplier<Class<A>> fieldsClassSupplier = SingletonSupplier.of(this::fieldsClass0);
private Class<A> fieldsClass() {
return fieldsClassSupplier.get();
}
@SuppressWarnings("unchecked")
private Class<A> fieldsClass0() {
try {
return (Class<A>) Class.forName("org.eclipse.hawkbit.repository." + managementClass().getSimpleName() + "Fields");
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
}
}
private final Supplier<Boolean> supportSoftDeleteSupplier = SingletonSupplier.of(this::supportSoftDelete0);
private boolean supportSoftDelete() {
return supportSoftDeleteSupplier.get();
}
private boolean supportSoftDelete0() {
return Stream.of(jpaRepository.getDomainClass().getDeclaredFields()).map(Field::getName).anyMatch(DELETED::equals);
}
private final Supplier<Optional<Specification<T>>> isNotDeletedSupplier = SingletonSupplier.of(this::isNotDeleted0);
private Optional<Specification<T>> isNotDeleted() {
return isNotDeletedSupplier.get();
}
private Optional<Specification<T>> isNotDeleted0() {
return supportSoftDelete()
? Optional.of((root, query, cb) -> cb.equal(root.get(DELETED), false))
: Optional.empty();
}
private T jpaEntity(final Object create) {
final T jpaEntity;
try {
jpaEntity = entityConstructor.newInstance();
} catch (final InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException("Must NEVER happen!", e);
}
ObjectCopyUtil.copy(create, jpaEntity, false, this::attach);
return jpaEntity;
}
private Object attach(final Object propertyValue) {
if (Jpa.JPA_VENDOR != Jpa.JpaVendor.HIBERNATE) {
return propertyValue; // no need to attach, only Hibernate supports this
}
if (propertyValue instanceof List<?> list) {
return list.stream().map(this::attach).toList();
} else if (propertyValue instanceof Set<?> set) {
return set.stream().map(this::attach).collect(Collectors.toSet());
} else if (propertyValue instanceof Map<?, ?> map) {
return map.entrySet().stream().collect(Collectors.toMap(
entry -> attach(entry.getKey()),
entry -> attach(entry.getValue())));
} else if (attachable(propertyValue)) {
// hibernate require detached entities to be attached before setting to jpa entity as a sub-property
return entityManager.merge(propertyValue);
} else {
return propertyValue; // no change
}
}
private boolean attachable(final Object propertyValue) {
if (propertyValue == null) {
return false;
}
final Class<?> clazz = propertyValue.getClass();
return !clazz.isPrimitive() && !clazz.isEnum() &&
clazz != String.class && !Number.class.isAssignableFrom(clazz) && !Boolean.class.isAssignableFrom(clazz) &&
!entityManager.contains(propertyValue); // no need to attach
}
}

View File

@@ -47,7 +47,7 @@ public class JpaActionManagement {
protected final QuotaManagement quotaManagement;
protected final RepositoryProperties repositoryProperties;
public JpaActionManagement(
protected JpaActionManagement(
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement,
final RepositoryProperties repositoryProperties) {
this.actionRepository = actionRepository;

View File

@@ -51,11 +51,13 @@ 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.tenancy.TenantAware;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -66,6 +68,8 @@ import org.springframework.validation.annotation.Validated;
@Slf4j
@Transactional(readOnly = true)
@Validated
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "artifact-management" }, matchIfMissing = true)
public class JpaArtifactManagement implements ArtifactManagement {
private final EntityManager entityManager;
@@ -77,7 +81,7 @@ public class JpaArtifactManagement implements ArtifactManagement {
private final TenantAware tenantAware;
private final QuotaManagement quotaManagement;
public JpaArtifactManagement(
protected JpaArtifactManagement(
final EntityManager entityManager,
final PlatformTransactionManager txManager,
final LocalArtifactRepository localArtifactRepository,

View File

@@ -41,10 +41,11 @@ import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.AutoConfirmationStatus;
import org.eclipse.hawkbit.repository.model.Target;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
@@ -56,15 +57,15 @@ import org.springframework.validation.annotation.Validated;
@Slf4j
@Transactional(readOnly = true)
@Validated
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "confirmation-management" }, matchIfMissing = true)
public class JpaConfirmationManagement extends JpaActionManagement implements ConfirmationManagement {
public static final String CONFIRMATION_CODE_MSG_PREFIX = "Confirmation status code: %d";
private final EntityManager entityManager;
private final EntityFactory entityFactory;
private final TargetRepository targetRepository;
public JpaConfirmationManagement(
protected JpaConfirmationManagement(
final TargetRepository targetRepository,
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
final RepositoryProperties repositoryProperties, final QuotaManagement quotaManagement,

View File

@@ -106,6 +106,7 @@ import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties;
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
import org.eclipse.hawkbit.tenancy.configuration.PollingTime;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
@@ -116,6 +117,7 @@ import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
@@ -124,12 +126,11 @@ import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
/**
* JPA based {@link ControllerManagement} implementation.
*/
@Slf4j
@Transactional(readOnly = true)
@Validated
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "controller-management" }, matchIfMissing = true)
public class JpaControllerManagement extends JpaActionManagement implements ControllerManagement {
private static final Pattern PATTERN = Pattern.compile("[a-zA-Z0-9_\\-!@#$%^&*()+=\\[\\]{}|;:'\",.<>/\\\\?\\s]*");
@@ -143,10 +144,11 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
private final ConfirmationManagement confirmationManagement;
private final SoftwareModuleRepository softwareModuleRepository;
private final SoftwareModuleMetadataRepository softwareModuleMetadataRepository;
private final DistributionSetManagement distributionSetManagement;
private final DistributionSetManagement<? extends DistributionSet> distributionSetManagement;
private final TenantConfigurationManagement tenantConfigurationManagement;
private final ControllerPollProperties controllerPollProperties;
private final Duration minPollingTime, maxPollingTime;
private final Duration minPollingTime;
private final Duration maxPollingTime;
private final PlatformTransactionManager txManager;
private final EntityFactory entityFactory;
private final EntityManager entityManager;
@@ -155,13 +157,13 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
private final TenantAware tenantAware;
@SuppressWarnings("squid:S00107")
public JpaControllerManagement(
protected JpaControllerManagement(
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement,
final RepositoryProperties repositoryProperties,
final TargetRepository targetRepository, final TargetTypeManagement targetTypeManagement,
final DeploymentManagement deploymentManagement, final ConfirmationManagement confirmationManagement,
final SoftwareModuleRepository softwareModuleRepository, final SoftwareModuleMetadataRepository softwareModuleMetadataRepository,
final DistributionSetManagement distributionSetManagement,
final DistributionSetManagement<? extends DistributionSet> distributionSetManagement,
final TenantConfigurationManagement tenantConfigurationManagement, final ControllerPollProperties controllerPollProperties,
final PlatformTransactionManager txManager, final EntityFactory entityFactory, final EntityManager entityManager,
final AfterTransactionCommitExecutor afterCommit,

View File

@@ -39,7 +39,6 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.eclipse.hawkbit.repository.ActionFields;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.RepositoryProperties;
@@ -89,6 +88,8 @@ import org.eclipse.hawkbit.repository.model.TargetWithActionType;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.utils.TenantConfigHelper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.domain.Page;
@@ -104,17 +105,17 @@ import org.springframework.retry.annotation.Retryable;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
/**
* JPA implementation for {@link DeploymentManagement}.
*/
@Slf4j
@Transactional(readOnly = true)
@Validated
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "deployment-management" }, matchIfMissing = true)
public class JpaDeploymentManagement extends JpaActionManagement implements DeploymentManagement {
/**
@@ -146,7 +147,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
}
private final EntityManager entityManager;
private final DistributionSetManagement distributionSetManagement;
private final JpaDistributionSetManagement distributionSetManagement;
private final TargetRepository targetRepository;
private final AuditorAware<String> auditorProvider;
private final PlatformTransactionManager txManager;
@@ -160,14 +161,14 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
private final RetryTemplate retryTemplate;
@SuppressWarnings("java:S107")
public JpaDeploymentManagement(
protected JpaDeploymentManagement(
final EntityManager entityManager, final ActionRepository actionRepository,
final DistributionSetManagement distributionSetManagement, final TargetRepository targetRepository,
final JpaDistributionSetManagement distributionSetManagement, final TargetRepository targetRepository,
final ActionStatusRepository actionStatusRepository, final AuditorAware<String> auditorProvider,
final AfterTransactionCommitExecutor afterCommit, final PlatformTransactionManager txManager,
final TenantConfigurationManagement tenantConfigurationManagement, final QuotaManagement quotaManagement,
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware, final AuditorAware<String> auditorAware,
final Database database, final RepositoryProperties repositoryProperties) {
final JpaProperties jpaProperties, final RepositoryProperties repositoryProperties) {
super(actionRepository, actionStatusRepository, quotaManagement, repositoryProperties);
this.entityManager = entityManager;
this.distributionSetManagement = distributionSetManagement;
@@ -184,7 +185,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
this.systemSecurityContext = systemSecurityContext;
this.tenantAware = tenantAware;
this.auditorAware = auditorAware;
this.database = database;
this.database = jpaProperties.getDatabase();
this.retryTemplate = createRetryTemplate();
}

View File

@@ -31,16 +31,20 @@ import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.Cancelat
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidationCount;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
/**
* Jpa implementation for {@link DistributionSetInvalidationManagement}
*/
@Slf4j
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "distribution-set-invalidation-management" }, matchIfMissing = true)
public class JpaDistributionSetInvalidationManagement implements DistributionSetInvalidationManagement {
private final DistributionSetManagement distributionSetManagement;
private final DistributionSetManagement<? extends DistributionSet> distributionSetManagement;
private final RolloutManagement rolloutManagement;
private final DeploymentManagement deploymentManagement;
private final TargetFilterQueryManagement targetFilterQueryManagement;
@@ -52,7 +56,7 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet
private final SystemSecurityContext systemSecurityContext;
@SuppressWarnings("java:S107")
public JpaDistributionSetInvalidationManagement(final DistributionSetManagement distributionSetManagement,
protected JpaDistributionSetInvalidationManagement(final DistributionSetManagement<? extends DistributionSet> distributionSetManagement,
final RolloutManagement rolloutManagement, final DeploymentManagement deploymentManagement,
final TargetFilterQueryManagement targetFilterQueryManagement, final ActionRepository actionRepository,
final PlatformTransactionManager txManager, final RepositoryProperties repositoryProperties,
@@ -134,7 +138,7 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet
throw new IncompleteDistributionSetException("Distribution set of type "
+ distributionSet.getType().getKey() + " is incomplete: " + distributionSet.getId());
}
distributionSetManagement.invalidate(distributionSet);
((DistributionSetManagement)distributionSetManagement).invalidate(distributionSet);
log.debug("Distribution set {} marked as invalid.", setId);
// rollout cancellation should only be permitted with UPDATE_ROLLOUT permission

View File

@@ -9,11 +9,14 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.IMPLICIT_LOCK_ENABLED;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -37,10 +40,7 @@ import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.builder.DistributionSetCreate;
import org.eclipse.hawkbit.repository.builder.DistributionSetUpdate;
import org.eclipse.hawkbit.repository.builder.GenericDistributionSetUpdate;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.exception.DeletedException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
@@ -49,11 +49,10 @@ import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetExcepti
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
@@ -73,7 +72,9 @@ import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Statistic;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.utils.TenantConfigHelper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -81,229 +82,120 @@ import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;
/**
* JPA implementation of {@link DistributionSetManagement}.
*/
@Transactional(readOnly = true)
@Validated
public class JpaDistributionSetManagement implements DistributionSetManagement {
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "distribution-set-management" }, matchIfMissing = true)
public class JpaDistributionSetManagement
extends AbstractJpaRepositoryManagement<JpaDistributionSet, DistributionSetManagement.Create, DistributionSetManagement.Update, DistributionSetRepository, DistributionSetFields>
implements DistributionSetManagement<JpaDistributionSet> {
private final EntityManager entityManager;
private final DistributionSetRepository distributionSetRepository;
private final DistributionSetTagManagement distributionSetTagManagement;
private final SystemManagement systemManagement;
private final DistributionSetTypeManagement distributionSetTypeManagement;
private final QuotaManagement quotaManagement;
private final DistributionSetTagManagement<JpaDistributionSetTag> distributionSetTagManagement;
private final DistributionSetTypeManagement<JpaDistributionSetType> distributionSetTypeManagement;
private final SoftwareModuleRepository softwareModuleRepository;
private final DistributionSetTagRepository distributionSetTagRepository;
private final TargetRepository targetRepository;
private final TargetFilterQueryRepository targetFilterQueryRepository;
private final ActionRepository actionRepository;
private final QuotaManagement quotaManagement;
private final TenantConfigHelper tenantConfigHelper;
private final SoftwareModuleRepository softwareModuleRepository;
private final DistributionSetTagRepository distributionSetTagRepository;
private final RepositoryProperties repositoryProperties;
@SuppressWarnings("java:S107")
public JpaDistributionSetManagement(
protected JpaDistributionSetManagement(
final DistributionSetRepository jpaRepository,
final EntityManager entityManager,
final DistributionSetRepository distributionSetRepository,
final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement,
final DistributionSetTypeManagement distributionSetTypeManagement, final QuotaManagement quotaManagement,
final TargetRepository targetRepository,
final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository,
final TenantConfigHelper tenantConfigHelper,
final DistributionSetTagManagement<JpaDistributionSetTag> distributionSetTagManagement,
final DistributionSetTypeManagement<JpaDistributionSetType> distributionSetTypeManagement,
final SoftwareModuleRepository softwareModuleRepository,
final DistributionSetTagRepository distributionSetTagRepository,
final TargetRepository targetRepository,
final TargetFilterQueryRepository targetFilterQueryRepository,
final ActionRepository actionRepository,
final QuotaManagement quotaManagement,
final SystemSecurityContext systemSecurityContext,
final TenantConfigurationManagement tenantConfigurationManagement,
final RepositoryProperties repositoryProperties) {
this.entityManager = entityManager;
this.distributionSetRepository = distributionSetRepository;
super(jpaRepository, entityManager);
this.distributionSetTagManagement = distributionSetTagManagement;
this.systemManagement = systemManagement;
this.distributionSetTypeManagement = distributionSetTypeManagement;
this.quotaManagement = quotaManagement;
this.softwareModuleRepository = softwareModuleRepository;
this.distributionSetTagRepository = distributionSetTagRepository;
this.targetRepository = targetRepository;
this.targetFilterQueryRepository = targetFilterQueryRepository;
this.actionRepository = actionRepository;
this.tenantConfigHelper = tenantConfigHelper;
this.softwareModuleRepository = softwareModuleRepository;
this.distributionSetTagRepository = distributionSetTagRepository;
this.quotaManagement = quotaManagement;
this.tenantConfigHelper = TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement);
this.repositoryProperties = repositoryProperties;
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSet> create(final Collection<DistributionSetCreate> creates) {
final List<JpaDistributionSet> toCreate = creates.stream().map(JpaDistributionSetCreate.class::cast)
.map(this::setDefaultTypeIfMissing)
.map(JpaDistributionSetCreate::build)
.toList();
return Collections.unmodifiableList(distributionSetRepository.saveAll(AccessController.Operation.CREATE, toCreate));
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSet create(final DistributionSetCreate c) {
final JpaDistributionSetCreate create = (JpaDistributionSetCreate) c;
setDefaultTypeIfMissing(create);
return distributionSetRepository.save(AccessController.Operation.CREATE, create.build());
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
@SuppressWarnings("java:S1066") // javaS1066 - better readable that way
public DistributionSet update(final DistributionSetUpdate u) {
final GenericDistributionSetUpdate update = (GenericDistributionSetUpdate) u;
final JpaDistributionSet set = (JpaDistributionSet) getValid(update.getId());
update.getName().ifPresent(set::setName);
update.getDescription().ifPresent(set::setDescription);
update.getVersion().ifPresent(set::setVersion);
public JpaDistributionSet update(final Update update) {
final JpaDistributionSet distributionSet = getValid0(update.getId());
// lock/unlock ONLY if locked flag is present!
if (Boolean.TRUE.equals(update.locked())) {
if (!set.isLocked()) {
lockSoftwareModules(set);
set.lock();
if (Boolean.TRUE.equals(update.getLocked())) {
if (!distributionSet.isLocked()) {
lockSoftwareModules(distributionSet);
distributionSet.lock();
}
} else if (Boolean.FALSE.equals(update.locked())) {
if (set.isLocked()) {
set.unlock();
} else if (Boolean.FALSE.equals(update.getLocked())) {
if (distributionSet.isLocked()) {
distributionSet.unlock();
}
}
if (update.isRequiredMigrationStep() != null
&& !update.isRequiredMigrationStep().equals(set.isRequiredMigrationStep())) {
if (update.getRequiredMigrationStep() != null && !update.getRequiredMigrationStep().equals(distributionSet.isRequiredMigrationStep())) {
assertDistributionSetIsNotAssignedToTargets(update.getId());
set.setRequiredMigrationStep(update.isRequiredMigrationStep());
}
return distributionSetRepository.save(set);
return super.update(update, distributionSet);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final long id) {
delete0(List.of(id));
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final Collection<Long> distributionSetIDs) {
delete0(distributionSetIDs);
}
private void delete0(final Collection<Long> distributionSetIDs) {
getDistributionSets(distributionSetIDs); // throws EntityNotFoundException if any of these do not exists
final List<JpaDistributionSet> setsFound = distributionSetRepository.findAll(
AccessController.Operation.DELETE, distributionSetRepository.byIdsSpec(distributionSetIDs));
if (setsFound.size() < distributionSetIDs.size()) {
throw new InsufficientPermissionException("No DELETE access to some of distribution sets!");
}
final List<Long> assigned = distributionSetRepository
.findAssignedToTargetDistributionSetsById(distributionSetIDs);
assigned.addAll(distributionSetRepository.findAssignedToRolloutDistributionSetsById(distributionSetIDs));
protected Collection<JpaDistributionSet> softDelete(final Collection<JpaDistributionSet> toDelete) {
// soft delete assigned
if (!assigned.isEmpty()) {
distributionSetRepository.saveAll(
setsFound.stream()
.filter(set -> assigned.contains(set.getId()))
.map(toSoftDelete -> {
// don't use peek since it is by documentation mainly for debugging and could be skipped in some cases
toSoftDelete.setDeleted(true);
return toSoftDelete;
})
.toList());
targetFilterQueryRepository.unsetAutoAssignDistributionSetAndActionTypeAndAccessContext(assigned.toArray(new Long[0]));
final List<Long> ids = toDelete.stream().map(JpaDistributionSet::getId).toList();
final Set<Long> assigned = new HashSet<>(jpaRepository.findAssignedToTargetDistributionSetsById(ids));
assigned.addAll(jpaRepository.findAssignedToRolloutDistributionSetsById(ids));
return toDelete.stream().filter(distributionSet -> assigned.contains(distributionSet.getId())).toList();
}
protected void delete0(final Collection<Long> distributionSetIDs) {
if (ObjectUtils.isEmpty(distributionSetIDs)) {
return; // super checks but if empty we don't want to unassign from target filters
}
// mark the rest as hard delete
final List<Long> toHardDelete = distributionSetIDs.stream().filter(setId -> !assigned.contains(setId)).toList();
// hard delete the rest if exists
if (!toHardDelete.isEmpty()) {
targetFilterQueryRepository.unsetAutoAssignDistributionSetAndActionTypeAndAccessContext(
toHardDelete.toArray(new Long[0]));
// don't give the delete statement an empty list, JPA/Oracle cannot
// handle the empty list
distributionSetRepository.deleteAllById(toHardDelete);
}
// if delete fail (because of permission denied) transaction will be rolled back
targetFilterQueryRepository.unsetAutoAssignDistributionSetAndActionTypeAndAccessContext(distributionSetIDs.toArray(new Long[0]));
super.delete0(distributionSetIDs);
}
@Override
public Optional<DistributionSet> get(final long id) {
return distributionSetRepository.findById(id).map(DistributionSet.class::cast);
}
@Override
public List<DistributionSet> get(final Collection<Long> ids) {
return Collections.unmodifiableList(getDistributionSets(ids));
}
@Override
public boolean exists(final long id) {
return distributionSetRepository.existsById(id);
}
@Override
public long count() {
return distributionSetRepository.count(DistributionSetSpecification.isNotDeleted());
}
@Override
public Slice<DistributionSet> findAll(final Pageable pageable) {
return JpaManagementHelper.findAllWithoutCountBySpec(distributionSetRepository, List.of(
DistributionSetSpecification.isNotDeleted()), pageable);
}
@Override
public Page<DistributionSet> findByRsql(final String rsql, final Pageable pageable) {
return JpaManagementHelper.findAllWithCountBySpec(distributionSetRepository, List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, DistributionSetFields.class),
DistributionSetSpecification.isNotDeleted()), pageable);
}
@Override
public Optional<DistributionSet> getWithDetails(final long id) {
return distributionSetRepository
.findOne(distributionSetRepository.byIdSpec(id), JpaDistributionSet_.GRAPH_DISTRIBUTION_SET_DETAIL)
.map(DistributionSet.class::cast);
public Optional<JpaDistributionSet> getWithDetails(final long id) {
return jpaRepository.findOne(jpaRepository.byIdSpec(id), JpaDistributionSet_.GRAPH_DISTRIBUTION_SET_DETAIL);
}
@Override
@Transactional
public void invalidate(final DistributionSet distributionSet) {
final JpaDistributionSet jpaSet = (JpaDistributionSet) distributionSet;
jpaSet.invalidate();
distributionSetRepository.save(jpaSet);
public void invalidate(final JpaDistributionSet distributionSet) {
distributionSet.invalidate();
jpaRepository.save(distributionSet);
}
@Override
public DistributionSet getOrElseThrowException(final long id) {
public JpaDistributionSet getOrElseThrowException(final long id) {
return getById(id);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSet assignSoftwareModules(final long id, final Collection<Long> softwareModuleId) {
final JpaDistributionSet set = (JpaDistributionSet) getValid(id);
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public JpaDistributionSet assignSoftwareModules(final long id, final Collection<Long> softwareModuleId) {
final JpaDistributionSet set = getValid0(id);
assertDistributionSetIsNotAssignedToTargets(id);
assertSoftwareModuleQuota(id, softwareModuleId.size());
@@ -315,47 +207,44 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
modules.forEach(set::addModule);
return distributionSetRepository.save(set);
return jpaRepository.save(set);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSet unassignSoftwareModule(final long id, final long moduleId) {
final JpaDistributionSet set = (JpaDistributionSet) getValid(id);
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public JpaDistributionSet unassignSoftwareModule(final long id, final long moduleId) {
final JpaDistributionSet set = getValid0(id);
assertDistributionSetIsNotAssignedToTargets(id);
final JpaSoftwareModule module = findSoftwareModuleAndThrowExceptionIfNotFound(moduleId);
set.removeModule(module);
return distributionSetRepository.save(set);
return jpaRepository.save(set);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSet> assignTag(final Collection<Long> ids, final long dsTagId) {
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public List<JpaDistributionSet> assignTag(final Collection<Long> ids, final long dsTagId) {
return updateTag(ids, dsTagId, (tag, distributionSet) -> {
if (distributionSet.getTags().contains(tag)) {
return distributionSet;
} else {
distributionSet.addTag(tag);
return distributionSetRepository.save(distributionSet);
return jpaRepository.save(distributionSet);
}
});
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSet> unassignTag(final Collection<Long> ids, final long dsTagId) {
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public List<JpaDistributionSet> unassignTag(final Collection<Long> ids, final long dsTagId) {
return updateTag(ids, dsTagId, (tag, distributionSet) -> {
if (distributionSet.getTags().contains(tag)) {
distributionSet.removeTag(tag);
return distributionSetRepository.save(distributionSet);
return jpaRepository.save(distributionSet);
} else {
return distributionSet;
}
@@ -364,10 +253,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public void createMetadata(final long id, final Map<String, String> md) {
final JpaDistributionSet distributionSet = (JpaDistributionSet) getValid(id);
final JpaDistributionSet distributionSet = getValid0(id);
// get the modifiable metadata map
final Map<String, String> metadata = distributionSet.getMetadata();
@@ -380,7 +268,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
assertMetaDataQuota(id, metadata.size());
distributionSetRepository.save(distributionSet);
jpaRepository.save(distributionSet);
}
@Override
@@ -391,10 +279,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public void updateMetadata(final long id, final String key, final String value) {
final JpaDistributionSet distributionSet = (JpaDistributionSet) getValid(id);
final JpaDistributionSet distributionSet = getValid0(id);
// get the modifiable metadata map
final Map<String, String> metadata = distributionSet.getMetadata();
@@ -403,15 +290,14 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
}
metadata.put(key, value);
distributionSetRepository.save(distributionSet);
jpaRepository.save(distributionSet);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public void deleteMetadata(final long id, final String key) {
final JpaDistributionSet distributionSet = (JpaDistributionSet) getValid(id);
final JpaDistributionSet distributionSet = getValid0(id);
// get the modifiable metadata map
final Map<String, String> metadata = distributionSet.getMetadata();
@@ -419,68 +305,62 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
throw new EntityNotFoundException("DistributionSet metadata", id + ":" + key);
}
distributionSetRepository.save(distributionSet);
jpaRepository.save(distributionSet);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public void lock(final long id) {
final JpaDistributionSet distributionSet = getById(id);
if (!distributionSet.isLocked()) {
lockSoftwareModules(distributionSet);
distributionSet.lock();
distributionSetRepository.save(distributionSet);
jpaRepository.save(distributionSet);
}
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public void unlock(final long id) {
final JpaDistributionSet distributionSet = getById(id);
if (distributionSet.isLocked()) {
distributionSet.unlock();
distributionSetRepository.save(distributionSet);
jpaRepository.save(distributionSet);
}
}
@Override
public Optional<DistributionSet> findByAction(final long actionId) {
public Optional<JpaDistributionSet> findByAction(final long actionId) {
return actionRepository
.findById(actionId)
.map(action -> {
if (!targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId()))) {
throw new InsufficientPermissionException("Target not accessible (or not found)!");
}
return distributionSetRepository
return jpaRepository
.findOne(DistributionSetSpecification.byIdFetch(action.getDistributionSet().getId()))
.orElseThrow(() ->
new InsufficientPermissionException("DistributionSet not accessible (or not found)!"));
})
.map(DistributionSet.class::cast)
.or(() -> {
throw new EntityNotFoundException(Action.class, actionId);
});
}
@Override
public Optional<DistributionSet> findByNameAndVersion(final String distributionName, final String version) {
return distributionSetRepository
.findOne(DistributionSetSpecification.equalsNameAndVersionIgnoreCase(distributionName, version))
.map(DistributionSet.class::cast);
public Optional<JpaDistributionSet> findByNameAndVersion(final String distributionName, final String version) {
return jpaRepository.findOne(DistributionSetSpecification.equalsNameAndVersionIgnoreCase(distributionName, version));
}
@Override
public DistributionSet getValidAndComplete(final long id) {
final DistributionSet distributionSet = getValid(id);
public JpaDistributionSet getValidAndComplete(final long id) {
final JpaDistributionSet distributionSet = getValid0(id);
if (!distributionSet.isComplete()) {
throw new IncompleteDistributionSetException("Distribution set of type "
+ distributionSet.getType().getKey() + " is incomplete: " + distributionSet.getId());
throw new IncompleteDistributionSetException(
"Distribution set of type " + distributionSet.getType().getKey() + " is incomplete: " + distributionSet.getId());
}
if (distributionSet.isDeleted()) {
@@ -491,60 +371,49 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
}
@Override
public DistributionSet getValid(final long id) {
final DistributionSet distributionSet = getOrElseThrowException(id);
if (!distributionSet.isValid()) {
throw new InvalidDistributionSetException("Distribution set of type " + distributionSet.getType().getKey()
+ " is invalid: " + distributionSet.getId());
}
return distributionSet;
public JpaDistributionSet getValid(final long id) {
return getValid0(id);
}
@Override
public Slice<DistributionSet> findByCompleted(final Boolean complete, final Pageable pageReq) {
public Slice<JpaDistributionSet> findByCompleted(final Boolean complete, final Pageable pageReq) {
final List<Specification<JpaDistributionSet>> specifications = buildSpecsByComplete(complete);
return JpaManagementHelper.findAllWithoutCountBySpec(distributionSetRepository, specifications, pageReq);
return JpaManagementHelper.findAllWithoutCountBySpec(jpaRepository, specifications, pageReq);
}
@Override
public long countByCompleted(final Boolean complete) {
final List<Specification<JpaDistributionSet>> specifications = buildSpecsByComplete(complete);
return JpaManagementHelper.countBySpec(distributionSetRepository, specifications);
return JpaManagementHelper.countBySpec(jpaRepository, buildSpecsByComplete(complete));
}
@Override
public Slice<DistributionSet> findByDistributionSetFilter(final DistributionSetFilter distributionSetFilter, final Pageable pageable) {
final List<Specification<JpaDistributionSet>> specList = buildDistributionSetSpecifications(
distributionSetFilter);
return JpaManagementHelper.findAllWithoutCountBySpec(distributionSetRepository, specList, pageable);
public Slice<JpaDistributionSet> findByDistributionSetFilter(final DistributionSetFilter distributionSetFilter, final Pageable pageable) {
final List<Specification<JpaDistributionSet>> specList = buildDistributionSetSpecifications(distributionSetFilter);
return JpaManagementHelper.findAllWithoutCountBySpec(jpaRepository, specList, pageable);
}
@Override
public long countByDistributionSetFilter(@NotNull final DistributionSetFilter distributionSetFilter) {
final List<Specification<JpaDistributionSet>> specList = buildDistributionSetSpecifications(distributionSetFilter);
return JpaManagementHelper.countBySpec(distributionSetRepository, specList);
return JpaManagementHelper.countBySpec(jpaRepository, specList);
}
@Override
public Page<DistributionSet> findByTag(final long tagId, final Pageable pageable) {
public Page<JpaDistributionSet> findByTag(final long tagId, final Pageable pageable) {
assertDsTagExists(tagId);
return JpaManagementHelper.findAllWithCountBySpec(distributionSetRepository, List.of(
DistributionSetSpecification.hasTag(tagId)), pageable);
return JpaManagementHelper.findAllWithCountBySpec(jpaRepository, List.of(DistributionSetSpecification.hasTag(tagId)), pageable);
}
@Override
public Page<DistributionSet> findByRsqlAndTag(final String rsql, final long tagId, final Pageable pageable) {
public Page<JpaDistributionSet> findByRsqlAndTag(final String rsql, final long tagId, final Pageable pageable) {
assertDsTagExists(tagId);
return JpaManagementHelper.findAllWithCountBySpec(distributionSetRepository, List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, DistributionSetFields.class),
DistributionSetSpecification.hasTag(tagId), DistributionSetSpecification.isNotDeleted()), pageable);
return JpaManagementHelper.findAllWithCountBySpec(
jpaRepository,
List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, DistributionSetFields.class),
DistributionSetSpecification.hasTag(tagId), DistributionSetSpecification.isNotDeleted()),
pageable);
}
@Override
@@ -552,37 +421,31 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
if (!distributionSetTypeManagement.exists(typeId)) {
throw new EntityNotFoundException(DistributionSetType.class, typeId);
}
return distributionSetRepository.count(DistributionSetSpecification.byType(typeId));
return jpaRepository.count(DistributionSetSpecification.byType(typeId));
}
@Override
public boolean isInUse(final long id) {
assertDistributionSetExists(id);
return actionRepository.countByDistributionSetId(id) > 0;
}
@Override
public List<Statistic> countRolloutsByStatusForDistributionSet(final Long id) {
assertDistributionSetExists(id);
return distributionSetRepository.countRolloutsByStatusForDistributionSet(id).stream().map(Statistic.class::cast).toList();
return jpaRepository.countRolloutsByStatusForDistributionSet(id).stream().map(Statistic.class::cast).toList();
}
@Override
public List<Statistic> countActionsByStatusForDistributionSet(final Long id) {
assertDistributionSetExists(id);
return distributionSetRepository.countActionsByStatusForDistributionSet(id).stream()
.map(Statistic.class::cast).toList();
return jpaRepository.countActionsByStatusForDistributionSet(id).stream().map(Statistic.class::cast).toList();
}
@Override
public Long countAutoAssignmentsForDistributionSet(final Long id) {
assertDistributionSetExists(id);
return distributionSetRepository.countAutoAssignmentsForDistributionSet(id);
return jpaRepository.countAutoAssignmentsForDistributionSet(id);
}
// check if it shall implicitly lock a distribution set
@@ -619,40 +482,26 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
final DistributionSetFilter distributionSetFilter) {
final List<Specification<JpaDistributionSet>> specList = new ArrayList<>(10);
Specification<JpaDistributionSet> spec;
if (distributionSetFilter.getIsComplete() != null) {
spec = DistributionSetSpecification.isCompleted(distributionSetFilter.getIsComplete());
specList.add(spec);
specList.add(DistributionSetSpecification.isCompleted(distributionSetFilter.getIsComplete()));
}
if (distributionSetFilter.getIsDeleted() != null) {
spec = DistributionSetSpecification.isDeleted(distributionSetFilter.getIsDeleted());
specList.add(spec);
specList.add(DistributionSetSpecification.isDeleted(distributionSetFilter.getIsDeleted()));
}
if (distributionSetFilter.getIsValid() != null) {
spec = DistributionSetSpecification.isValid(distributionSetFilter.getIsValid());
specList.add(spec);
specList.add(DistributionSetSpecification.isValid(distributionSetFilter.getIsValid()));
}
if (distributionSetFilter.getTypeId() != null) {
spec = DistributionSetSpecification.byType(distributionSetFilter.getTypeId());
specList.add(spec);
specList.add(DistributionSetSpecification.byType(distributionSetFilter.getTypeId()));
}
if (!ObjectUtils.isEmpty(distributionSetFilter.getSearchText())) {
final String[] dsFilterNameAndVersionEntries = JpaManagementHelper
.getFilterNameAndVersionEntries(distributionSetFilter.getSearchText().trim());
spec = DistributionSetSpecification.likeNameAndVersion(dsFilterNameAndVersionEntries[0],
dsFilterNameAndVersionEntries[1]);
specList.add(spec);
specList.add(DistributionSetSpecification.likeNameAndVersion(dsFilterNameAndVersionEntries[0], dsFilterNameAndVersionEntries[1]));
}
if (hasTagsFilterActive(distributionSetFilter)) {
spec = DistributionSetSpecification.hasTags(distributionSetFilter.getTagNames(),
distributionSetFilter.getSelectDSWithNoTag());
specList.add(spec);
specList.add(DistributionSetSpecification.hasTags(
distributionSetFilter.getTagNames(), distributionSetFilter.getSelectDSWithNoTag()));
}
return specList;
}
@@ -660,7 +509,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
private static boolean hasTagsFilterActive(final DistributionSetFilter distributionSetFilter) {
final boolean isNoTagActive = Boolean.TRUE.equals(distributionSetFilter.getSelectDSWithNoTag());
final boolean isAtLeastOneTagActive = !CollectionUtils.isEmpty(distributionSetFilter.getTagNames());
return isNoTagActive || isAtLeastOneTagActive;
}
@@ -670,16 +518,25 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
return distributionSetIds.stream().filter(id -> !foundDistributionSetMap.containsKey(id)).toList();
}
private List<DistributionSet> updateTag(
private JpaDistributionSet getValid0(final long id) {
final JpaDistributionSet distributionSet = getById(id);
if (!distributionSet.isValid()) {
throw new InvalidDistributionSetException(
"Distribution set of type " + distributionSet.getType().getKey() + " is invalid: " + distributionSet.getId());
}
return distributionSet;
}
private List<JpaDistributionSet> updateTag(
final Collection<Long> dsIds, final long dsTagId,
final BiFunction<DistributionSetTag, JpaDistributionSet, DistributionSet> updater) {
final BiFunction<DistributionSetTag, JpaDistributionSet, JpaDistributionSet> updater) {
final DistributionSetTag tag = distributionSetTagManagement.get(dsTagId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, dsTagId));
final List<JpaDistributionSet> allDs = dsIds.size() == 1 ?
distributionSetRepository.findById(dsIds.iterator().next())
jpaRepository.findById(dsIds.iterator().next())
.map(List::of)
.orElseGet(Collections::emptyList) :
distributionSetRepository.findAll(DistributionSetSpecification.byIdsFetch(dsIds));
jpaRepository.findAll(DistributionSetSpecification.byIdsFetch(dsIds));
if (allDs.size() < dsIds.size()) {
throw new EntityNotFoundException(DistributionSet.class, notFound(dsIds, allDs));
}
@@ -716,13 +573,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, softwareModuleId));
}
private JpaDistributionSetCreate setDefaultTypeIfMissing(final JpaDistributionSetCreate create) {
if (create.getType() == null) {
create.type(systemManagement.getTenantMetadata().getDefaultDsType().getKey());
}
return create;
}
private List<Specification<JpaDistributionSet>> buildSpecsByComplete(final Boolean complete) {
final List<Specification<JpaDistributionSet>> specifications = new ArrayList<>();
specifications.add(DistributionSetSpecification.isNotDeleted());
@@ -749,15 +599,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
}
}
private List<JpaDistributionSet> getDistributionSets(final Collection<Long> ids) {
final List<JpaDistributionSet> foundDs = distributionSetRepository.findAllById(ids);
if (foundDs.size() != ids.size()) {
throw new EntityNotFoundException(
DistributionSet.class, ids, foundDs.stream().map(JpaDistributionSet::getId).toList());
}
return foundDs;
}
private void lockSoftwareModules(final JpaDistributionSet distributionSet) {
distributionSet.getModules().forEach(module -> {
if (!module.isLocked()) {
@@ -769,13 +610,13 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
}
private JpaDistributionSet getById(final long id) {
return distributionSetRepository
return jpaRepository
.findById(id)
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, id));
}
private void assertDistributionSetExists(final long id) {
if (!distributionSetRepository.existsById(id)) {
if (!jpaRepository.existsById(id)) {
throw new EntityNotFoundException(DistributionSet.class, id);
}
}

View File

@@ -9,173 +9,73 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
import java.util.Collection;
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import jakarta.persistence.EntityManager;
import org.eclipse.hawkbit.repository.DistributionSetTagFields;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.TargetTagManagement;
import org.eclipse.hawkbit.repository.builder.GenericTagUpdate;
import org.eclipse.hawkbit.repository.builder.TagCreate;
import org.eclipse.hawkbit.repository.builder.TagUpdate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTagCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTagRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetTagSpecifications;
import org.eclipse.hawkbit.repository.jpa.specifications.TagSpecification;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
/**
* JPA implementation of {@link TargetTagManagement}.
*/
@Transactional(readOnly = true)
@Validated
public class JpaDistributionSetTagManagement implements DistributionSetTagManagement {
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "distribution-set-tag-management" }, matchIfMissing = true)
public class JpaDistributionSetTagManagement
extends AbstractJpaRepositoryManagement<JpaDistributionSetTag, DistributionSetTagManagement.Create, DistributionSetTagManagement.Update, DistributionSetTagRepository, DistributionSetTagFields>
implements DistributionSetTagManagement<JpaDistributionSetTag> {
private final DistributionSetTagRepository distributionSetTagRepository;
private final DistributionSetRepository distributionSetRepository;
public JpaDistributionSetTagManagement(
protected JpaDistributionSetTagManagement(
final DistributionSetTagRepository distributionSetTagRepository,
final EntityManager entityManager,
final DistributionSetRepository distributionSetRepository) {
this.distributionSetTagRepository = distributionSetTagRepository;
super(distributionSetTagRepository, entityManager);
this.distributionSetRepository = distributionSetRepository;
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSetTag> create(final Collection<TagCreate> dst) {
final List<JpaDistributionSetTag> toCreate = dst.stream()
.map(JpaTagCreate.class::cast)
.map(JpaTagCreate::buildDistributionSetTag)
.toList();
return Collections.unmodifiableList(distributionSetTagRepository.saveAll(AccessController.Operation.CREATE, toCreate));
public Optional<JpaDistributionSetTag> findByName(final String name) {
return jpaRepository.findByNameEquals(name);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetTag create(final TagCreate c) {
return distributionSetTagRepository.save(AccessController.Operation.CREATE, ((JpaTagCreate) c).buildDistributionSetTag());
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetTag update(final TagUpdate u) {
final GenericTagUpdate update = (GenericTagUpdate) u;
final JpaDistributionSetTag tag = distributionSetTagRepository.findById(update.getId())
.orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, update.getId()));
update.getName().ifPresent(tag::setName);
update.getDescription().ifPresent(tag::setDescription);
update.getColour().ifPresent(tag::setColour);
return distributionSetTagRepository.save(tag);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final long id) {
distributionSetTagRepository.deleteById(id);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final Collection<Long> ids) {
final List<JpaDistributionSetTag> setsFound = distributionSetTagRepository.findAllById(ids);
if (setsFound.size() < ids.size()) {
throw new EntityNotFoundException(
DistributionSetTag.class, ids,
setsFound.stream().map(DistributionSetTag::getId).toList());
}
distributionSetTagRepository.deleteAll(setsFound);
}
@Override
public Optional<DistributionSetTag> get(final long id) {
return distributionSetTagRepository.findById(id).map(DistributionSetTag.class::cast);
}
@Override
public List<DistributionSetTag> get(final Collection<Long> ids) {
return Collections.unmodifiableList(distributionSetTagRepository.findAllById(ids));
}
@Override
public boolean exists(final long id) {
return distributionSetTagRepository.existsById(id);
}
@Override
public long count() {
return distributionSetTagRepository.count();
}
@Override
public Slice<DistributionSetTag> findAll(final Pageable pageable) {
return JpaManagementHelper.findAllWithoutCountBySpec(distributionSetTagRepository, null, pageable);
}
@Override
public Page<DistributionSetTag> findByRsql(final String rsql, final Pageable pageable) {
final Specification<JpaDistributionSetTag> spec = RsqlUtility.getInstance().buildRsqlSpecification(rsql, DistributionSetTagFields.class);
return JpaManagementHelper.findAllWithCountBySpec(distributionSetTagRepository, Collections.singletonList(spec), pageable);
}
@Override
public Optional<DistributionSetTag> findByName(final String name) {
return distributionSetTagRepository.findByNameEquals(name);
}
@Override
public Page<DistributionSetTag> findByDistributionSet(final long distributionSetId, final Pageable pageable) {
public Page<JpaDistributionSetTag> findByDistributionSet(final long distributionSetId, final Pageable pageable) {
if (!distributionSetRepository.existsById(distributionSetId)) {
throw new EntityNotFoundException(DistributionSet.class, distributionSetId);
}
return JpaManagementHelper.findAllWithCountBySpec(distributionSetTagRepository,
return JpaManagementHelper.findAllWithCountBySpec(jpaRepository,
Collections.singletonList(TagSpecification.ofDistributionSet(distributionSetId)), pageable
);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public void delete(final String tagName) {
final JpaDistributionSetTag dsTag = distributionSetTagRepository
final JpaDistributionSetTag dsTag = jpaRepository
.findOne(DistributionSetTagSpecifications.byName(tagName))
.orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, tagName));
distributionSetTagRepository.delete(dsTag);
jpaRepository.delete(dsTag);
}
}
}

View File

@@ -9,29 +9,22 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.LongFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jakarta.persistence.EntityManager;
import org.eclipse.hawkbit.repository.DistributionSetTypeFields;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeCreate;
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeUpdate;
import org.eclipse.hawkbit.repository.builder.GenericDistributionSetTypeUpdate;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetTypeCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
@@ -39,40 +32,35 @@ import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetTypeSpecification;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
/**
* JPA implementation of {@link DistributionSetTypeManagement}.
*/
@Transactional(readOnly = true)
@Validated
public class JpaDistributionSetTypeManagement implements DistributionSetTypeManagement {
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "distribution-set-type-management" }, matchIfMissing = true)
public class JpaDistributionSetTypeManagement
extends AbstractJpaRepositoryManagement<JpaDistributionSetType, DistributionSetTypeManagement.Create, DistributionSetTypeManagement.Update, DistributionSetTypeRepository, DistributionSetTypeFields>
implements DistributionSetTypeManagement<JpaDistributionSetType> {
private final DistributionSetTypeRepository distributionSetTypeRepository;
private final SoftwareModuleTypeRepository softwareModuleTypeRepository;
private final DistributionSetRepository distributionSetRepository;
private final TargetTypeRepository targetTypeRepository;
private final QuotaManagement quotaManagement;
public JpaDistributionSetTypeManagement(
protected JpaDistributionSetTypeManagement(
final DistributionSetTypeRepository distributionSetTypeRepository,
final EntityManager entityManager,
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
final DistributionSetRepository distributionSetRepository, final TargetTypeRepository targetTypeRepository,
final QuotaManagement quotaManagement) {
this.distributionSetTypeRepository = distributionSetTypeRepository;
super(distributionSetTypeRepository, entityManager);
this.softwareModuleTypeRepository = softwareModuleTypeRepository;
this.distributionSetRepository = distributionSetRepository;
this.targetTypeRepository = targetTypeRepository;
@@ -81,181 +69,62 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSetType> create(final Collection<DistributionSetTypeCreate> types) {
final List<JpaDistributionSetType> typesToCreate = types.stream()
.map(JpaDistributionSetTypeCreate.class::cast)
.map(JpaDistributionSetTypeCreate::build).toList();
return Collections.unmodifiableList(
distributionSetTypeRepository.saveAll(AccessController.Operation.CREATE, typesToCreate));
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType create(final DistributionSetTypeCreate c) {
final JpaDistributionSetType distributionSetType = ((JpaDistributionSetTypeCreate) c).build();
return distributionSetTypeRepository.save(AccessController.Operation.CREATE, distributionSetType);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType update(final DistributionSetTypeUpdate u) {
final GenericDistributionSetTypeUpdate update = (GenericDistributionSetTypeUpdate) u;
final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(update.getId());
update.getDescription().ifPresent(type::setDescription);
update.getColour().ifPresent(type::setColour);
if (hasModuleChanges(update)) {
checkDistributionSetTypeNotAssigned(update.getId());
final Collection<Long> currentMandatorySmTypeIds = type.getMandatoryModuleTypes().stream()
.map(SoftwareModuleType::getId).collect(Collectors.toSet());
final Collection<Long> currentOptionalSmTypeIds = type.getOptionalModuleTypes().stream()
.map(SoftwareModuleType::getId).collect(Collectors.toSet());
final Collection<Long> currentSmTypeIds = Stream
.concat(currentMandatorySmTypeIds.stream(), currentOptionalSmTypeIds.stream())
.collect(Collectors.toSet());
final Collection<Long> updatedMandatorySmTypeIds = update.getMandatory().orElse(currentMandatorySmTypeIds);
final Collection<Long> updatedOptionalSmTypeIds = update.getOptional().orElse(currentOptionalSmTypeIds);
final Collection<Long> updatedSmTypeIds = Stream
.concat(updatedMandatorySmTypeIds.stream(), updatedOptionalSmTypeIds.stream())
.collect(Collectors.toSet());
addModuleTypes(currentMandatorySmTypeIds, updatedMandatorySmTypeIds, type::addMandatoryModuleType);
addModuleTypes(currentOptionalSmTypeIds, updatedOptionalSmTypeIds, type::addOptionalModuleType);
removeModuleTypes(currentSmTypeIds, updatedSmTypeIds, type::removeModuleType);
}
return distributionSetTypeRepository.save(type);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public void delete(final long id) {
final JpaDistributionSetType toDelete = distributionSetTypeRepository.findById(id)
final JpaDistributionSetType toDelete = jpaRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, id));
unassignDsTypeFromTargetTypes(id);
if (distributionSetRepository.countByTypeId(id) > 0) {
toDelete.setDeleted(true);
distributionSetTypeRepository.save(AccessController.Operation.DELETE, toDelete);
jpaRepository.save(AccessController.Operation.DELETE, toDelete);
} else {
distributionSetTypeRepository.deleteById(id);
jpaRepository.deleteById(id);
}
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final Collection<Long> ids) {
distributionSetTypeRepository.deleteAllById(ids);
public void delete0(final Collection<Long> ids) {
ids.forEach(this::unassignDsTypeFromTargetTypes);
super.delete0(ids);
}
@Override
public Optional<DistributionSetType> get(final long id) {
return distributionSetTypeRepository.findById(id).map(DistributionSetType.class::cast);
public Optional<JpaDistributionSetType> findByKey(final String key) {
return jpaRepository.findOne(DistributionSetTypeSpecification.byKey(key));
}
@Override
public List<DistributionSetType> get(final Collection<Long> ids) {
return Collections.unmodifiableList(distributionSetTypeRepository.findAllById(ids));
}
@Override
public boolean exists(final long id) {
return distributionSetTypeRepository.existsById(id);
}
@Override
public long count() {
return distributionSetTypeRepository.count(DistributionSetTypeSpecification.isNotDeleted());
}
@Override
public Slice<DistributionSetType> findAll(final Pageable pageable) {
return JpaManagementHelper.findAllWithoutCountBySpec(distributionSetTypeRepository, List.of(
DistributionSetTypeSpecification.isNotDeleted()), pageable);
}
@Override
public Page<DistributionSetType> findByRsql(final String rsql, final Pageable pageable) {
return JpaManagementHelper.findAllWithCountBySpec(distributionSetTypeRepository, List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, DistributionSetTypeFields.class),
DistributionSetTypeSpecification.isNotDeleted()), pageable);
}
@Override
public Optional<DistributionSetType> findByKey(final String key) {
return distributionSetTypeRepository.findOne(DistributionSetTypeSpecification.byKey(key)).map(DistributionSetType.class::cast);
}
@Override
public Optional<DistributionSetType> findByName(final String name) {
return distributionSetTypeRepository.findOne(DistributionSetTypeSpecification.byName(name)).map(DistributionSetType.class::cast);
public Optional<JpaDistributionSetType> findByName(final String name) {
return jpaRepository.findOne(DistributionSetTypeSpecification.byName(name));
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType assignOptionalSoftwareModuleTypes(final long id, final Collection<Long> softwareModulesTypeIds) {
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public JpaDistributionSetType assignOptionalSoftwareModuleTypes(final long id, final Collection<Long> softwareModulesTypeIds) {
return assignSoftwareModuleTypes(id, softwareModulesTypeIds, false);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType assignMandatorySoftwareModuleTypes(final long id, final Collection<Long> softwareModuleTypeIds) {
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public JpaDistributionSetType assignMandatorySoftwareModuleTypes(final long id, final Collection<Long> softwareModuleTypeIds) {
return assignSoftwareModuleTypes(id, softwareModuleTypeIds, true);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType unassignSoftwareModuleType(final long id, final long softwareModuleTypeId) {
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public JpaDistributionSetType unassignSoftwareModuleType(final long id, final long softwareModuleTypeId) {
final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(id);
checkDistributionSetTypeNotAssigned(id);
type.removeModuleType(softwareModuleTypeId);
return distributionSetTypeRepository.save(type);
type.removeModuleType(softwareModuleTypeRepository.getById(softwareModuleTypeId));
return jpaRepository.save(type);
}
private static void removeModuleTypes(
final Collection<Long> currentSmTypeIds, final Collection<Long> updatedSmTypeIds,
final LongFunction<JpaDistributionSetType> removeModuleTypeCallback) {
final Set<Long> smTypeIdsToRemove = currentSmTypeIds.stream().filter(id -> !updatedSmTypeIds.contains(id))
.collect(Collectors.toSet());
if (!CollectionUtils.isEmpty(smTypeIdsToRemove)) {
smTypeIdsToRemove.forEach(removeModuleTypeCallback::apply);
}
}
private static boolean hasModuleChanges(final GenericDistributionSetTypeUpdate update) {
return update.getOptional().isPresent() || update.getMandatory().isPresent();
}
private void addModuleTypes(
final Collection<Long> currentSmTypeIds, final Collection<Long> updatedSmTypeIds,
final Function<SoftwareModuleType, JpaDistributionSetType> addModuleTypeCallback) {
final Set<Long> smTypeIdsToAdd = updatedSmTypeIds.stream().filter(id -> !currentSmTypeIds.contains(id)).collect(Collectors.toSet());
if (!CollectionUtils.isEmpty(smTypeIdsToAdd)) {
softwareModuleTypeRepository.findAllById(smTypeIdsToAdd).forEach(addModuleTypeCallback::apply);
}
}
private DistributionSetType assignSoftwareModuleTypes(
private JpaDistributionSetType assignSoftwareModuleTypes(
final long dsTypeId, final Collection<Long> softwareModulesTypeIds, final boolean mandatory) {
final Collection<JpaSoftwareModuleType> foundModules = softwareModuleTypeRepository.findAllById(softwareModulesTypeIds);
if (foundModules.size() < softwareModulesTypeIds.size()) {
@@ -270,7 +139,7 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
foundModules.forEach(mandatory ? type::addMandatoryModuleType : type::addOptionalModuleType);
return distributionSetTypeRepository.save(type);
return jpaRepository.save(type);
}
/**
@@ -284,7 +153,7 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
private void assertSoftwareModuleTypeQuota(final long id, final int requested) {
QuotaHelper.assertAssignmentQuota(id, requested,
quotaManagement.getMaxSoftwareModuleTypesPerDistributionSetType(), SoftwareModuleType.class,
DistributionSetType.class, distributionSetTypeRepository::countSmTypesById);
DistributionSetType.class, jpaRepository::countSmTypesById);
}
private void unassignDsTypeFromTargetTypes(final long typeId) {
@@ -295,8 +164,8 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
});
}
private JpaDistributionSetType findDistributionSetTypeAndThrowExceptionIfNotFound(final Long setId) {
return (JpaDistributionSetType) get(setId).orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, setId));
private JpaDistributionSetType findDistributionSetTypeAndThrowExceptionIfNotFound(final Long id) {
return jpaRepository.findById(id).orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, id));
}
private void checkDistributionSetTypeNotAssigned(final Long id) {

View File

@@ -48,10 +48,12 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
@@ -61,6 +63,8 @@ import org.springframework.validation.annotation.Validated;
*/
@Validated
@Transactional(readOnly = true)
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "rollout-group-management" }, matchIfMissing = true)
public class JpaRolloutGroupManagement implements RolloutGroupManagement {
private final RolloutGroupRepository rolloutGroupRepository;
@@ -71,7 +75,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
private final RolloutStatusCache rolloutStatusCache;
@SuppressWarnings("java:S107")
public JpaRolloutGroupManagement(final RolloutGroupRepository rolloutGroupRepository,
protected JpaRolloutGroupManagement(final RolloutGroupRepository rolloutGroupRepository,
final RolloutRepository rolloutRepository, final ActionRepository actionRepository,
final TargetRepository targetRepository, final EntityManager entityManager, final RolloutStatusCache rolloutStatusCache) {
this.rolloutGroupRepository = rolloutGroupRepository;

View File

@@ -80,6 +80,7 @@ import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -89,6 +90,7 @@ import org.springframework.data.jpa.domain.Specification;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
@@ -99,6 +101,8 @@ import org.springframework.validation.annotation.Validated;
@Slf4j
@Validated
@Transactional(readOnly = true)
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "rollout-management" }, matchIfMissing = true)
public class JpaRolloutManagement implements RolloutManagement {
private static final List<RolloutStatus> ACTIVE_ROLLOUTS = List.of(
@@ -123,8 +127,7 @@ public class JpaRolloutManagement implements RolloutManagement {
private final ContextAware contextAware;
private final RepositoryProperties repositoryProperties;
@SuppressWarnings("java:S107")
public JpaRolloutManagement(
protected JpaRolloutManagement(
final RolloutRepository rolloutRepository,
final RolloutGroupRepository rolloutGroupRepository,
final RolloutApprovalStrategy rolloutApprovalStrategy,

View File

@@ -9,10 +9,12 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -21,25 +23,22 @@ import java.util.stream.Collectors;
import jakarta.persistence.EntityManager;
import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryptionService;
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.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryptionService;
import org.eclipse.hawkbit.repository.builder.GenericSoftwareModuleMetadataUpdate;
import org.eclipse.hawkbit.repository.builder.GenericSoftwareModuleUpdate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleCreate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataUpdate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleUpdate;
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.JpaSoftwareModuleCreate;
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleMetadataCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata;
@@ -50,7 +49,6 @@ 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.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleSpecification;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.Artifact;
@@ -58,8 +56,8 @@ 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.SoftwareModuleType;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
@@ -67,200 +65,121 @@ import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;
/**
* JPA implementation of {@link SoftwareModuleManagement}.
*/
@Transactional(readOnly = true)
@Validated
public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "software-module-management" }, matchIfMissing = true)
public class JpaSoftwareModuleManagement
extends AbstractJpaRepositoryManagement<JpaSoftwareModule, SoftwareModuleManagement.Create, SoftwareModuleManagement.Update, SoftwareModuleRepository, SoftwareModuleFields>
implements SoftwareModuleManagement<JpaSoftwareModule> {
protected static final String SOFTWARE_MODULE_METADATA = "SoftwareModuleMetadata";
private final EntityManager entityManager;
private final SoftwareModuleTypeManagement<? extends SoftwareModuleType> softwareModuleTypeManagement;
private final DistributionSetRepository distributionSetRepository;
private final SoftwareModuleRepository softwareModuleRepository;
private final SoftwareModuleMetadataRepository softwareModuleMetadataRepository;
private final SoftwareModuleTypeRepository softwareModuleTypeRepository;
private final AuditorAware<String> auditorProvider;
private final ArtifactManagement artifactManagement;
private final QuotaManagement quotaManagement;
@SuppressWarnings("java:S107")
public JpaSoftwareModuleManagement(final EntityManager entityManager,
final DistributionSetRepository distributionSetRepository,
protected JpaSoftwareModuleManagement(
final SoftwareModuleRepository softwareModuleRepository,
final EntityManager entityManager,
final SoftwareModuleTypeManagement<? extends SoftwareModuleType> softwareModuleTypeManagement,
final DistributionSetRepository distributionSetRepository,
final SoftwareModuleMetadataRepository softwareModuleMetadataRepository,
final SoftwareModuleTypeRepository softwareModuleTypeRepository, final AuditorAware<String> auditorProvider,
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
final ArtifactManagement artifactManagement, final QuotaManagement quotaManagement) {
this.entityManager = entityManager;
super(softwareModuleRepository, entityManager);
this.softwareModuleTypeManagement = softwareModuleTypeManagement;
this.distributionSetRepository = distributionSetRepository;
this.softwareModuleRepository = softwareModuleRepository;
this.softwareModuleMetadataRepository = softwareModuleMetadataRepository;
this.softwareModuleTypeRepository = softwareModuleTypeRepository;
this.auditorProvider = auditorProvider;
this.artifactManagement = artifactManagement;
this.quotaManagement = quotaManagement;
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<SoftwareModule> create(final Collection<SoftwareModuleCreate> swModules) {
final List<JpaSoftwareModule> modulesToCreate = swModules.stream().map(JpaSoftwareModuleCreate.class::cast)
.map(JpaSoftwareModuleCreate::build).toList();
final List<SoftwareModule> createdModules = Collections
.unmodifiableList(softwareModuleRepository.saveAll(AccessController.Operation.CREATE, modulesToCreate));
public List<JpaSoftwareModule> create(final Collection<Create> create) {
final List<JpaSoftwareModule> createdModules = super.create(create);
if (createdModules.stream().anyMatch(SoftwareModule::isEncrypted)) {
// flush sm creation in order to get ids
entityManager.flush();
createdModules.stream().filter(SoftwareModule::isEncrypted).map(SoftwareModule::getId)
.forEach(encryptedModuleId -> ArtifactEncryptionService.getInstance()
.addEncryptionSecrets(encryptedModuleId));
createdModules.stream()
.filter(SoftwareModule::isEncrypted)
.map(SoftwareModule::getId)
.forEach(encryptedModuleId -> ArtifactEncryptionService.getInstance().addEncryptionSecrets(encryptedModuleId));
}
return createdModules;
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public SoftwareModule create(final SoftwareModuleCreate c) {
final JpaSoftwareModuleCreate create = (JpaSoftwareModuleCreate) c;
public JpaSoftwareModule create(final Create create) {
final JpaSoftwareModule createdModule = super.create(create);
final JpaSoftwareModule sm = softwareModuleRepository.save(AccessController.Operation.CREATE, create.build());
if (create.isEncrypted()) {
// flush sm creation in order to get an Id
if (createdModule.isEncrypted()) {
// flush sm creation in order to get an id
entityManager.flush();
ArtifactEncryptionService.getInstance().addEncryptionSecrets(sm.getId());
ArtifactEncryptionService.getInstance().addEncryptionSecrets(createdModule.getId());
}
return sm;
return createdModule;
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public SoftwareModule update(final SoftwareModuleUpdate u) {
final GenericSoftwareModuleUpdate update = (GenericSoftwareModuleUpdate) u;
final JpaSoftwareModule module = softwareModuleRepository.findById(update.getId())
public JpaSoftwareModule update(final Update update) {
final JpaSoftwareModule module = jpaRepository.findById(update.getId())
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, update.getId()));
update.getDescription().ifPresent(module::setDescription);
update.getVendor().ifPresent(module::setVendor);
// lock/unlock ONLY if locked flag is present!
if (Boolean.TRUE.equals(update.locked())) {
if (Boolean.TRUE.equals(update.getLocked())) {
module.lock();
} else if (Boolean.FALSE.equals(update.locked())) {
} else if (Boolean.FALSE.equals(update.getLocked())) {
module.unlock();
}
return softwareModuleRepository.save(module);
return super.update(update, module);
}
@Override
protected List<JpaSoftwareModule> softDelete(final Collection<JpaSoftwareModule> toDelete) {
return toDelete.stream()
.filter(swModule -> {
final List<DistributionSet> assignedTo = swModule.getAssignedTo();
if (assignedTo != null) {
final List<DistributionSet> lockedDS = assignedTo.stream()
.filter(DistributionSet::isLocked)
.filter(ds -> !ds.isDeleted())
.toList();
if (!lockedDS.isEmpty()) {
final StringBuilder sb = new StringBuilder("Part of ");
if (lockedDS.size() == 1) {
sb.append("a locked distribution set: ");
} else {
sb.append(lockedDS.size()).append(" locked distribution sets: ");
}
for (final DistributionSet ds : lockedDS) {
sb.append(ds.getName()).append(":").append(ds.getVersion()).append(" (").append(ds.getId()).append("), ");
}
sb.delete(sb.length() - 2, sb.length());
throw new LockedException(JpaSoftwareModule.class, swModule.getId(), "DELETE", sb.toString());
}
}
final boolean isAssigned = !ObjectUtils.isEmpty(assignedTo);
// schedule delete binary data of artifacts for every soft or not soft deleted module
deleteGridFsArtifacts(swModule);
return isAssigned;
})
.toList();
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final long id) {
delete0(List.of(id));
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final Collection<Long> ids) {
delete0(ids);
}
private void delete0(final Collection<Long> ids) {
final List<JpaSoftwareModule> swModulesToDelete = softwareModuleRepository.findAllById(ids);
if (swModulesToDelete.size() < ids.size()) {
throw new EntityNotFoundException(SoftwareModule.class, ids,
swModulesToDelete.stream().map(SoftwareModule::getId).toList());
}
final Set<Long> assignedModuleIds = new HashSet<>();
swModulesToDelete.forEach(swModule -> {
// execute this count operation without access limitations since we have to
// ensure it's not assigned when deleting it.
if (distributionSetRepository.countByModulesId(swModule.getId()) <= 0) {
softwareModuleRepository.deleteById(swModule.getId());
} else {
assignedModuleIds.add(swModule.getId());
}
// schedule delete binary data of artifacts
deleteGridFsArtifacts(swModule);
});
if (!assignedModuleIds.isEmpty()) {
String currentUser = null;
if (auditorProvider != null) {
currentUser = auditorProvider.getCurrentAuditor().orElse(null);
}
/*
TODO AC - could use single update query as before via entity manager directly (considers tenant!):
maybe it will be possible, via specification when migrate to Spring Boot 3
"UPDATE JpaSoftwareModule b SET b.deleted = 1, b.lastModifiedAt = :lastModifiedAt, b.lastModifiedBy = :lastModifiedBy WHERE b.tenant = :tenant AND b.id IN :ids"
*/
final long timestamp = System.currentTimeMillis();
final List<JpaSoftwareModule> toDelete = softwareModuleRepository.findAll(
AccessController.Operation.DELETE, softwareModuleRepository.byIdsSpec(assignedModuleIds));
for (final JpaSoftwareModule softwareModule : toDelete) {
softwareModule.setDeleted(true);
softwareModule.setLastModifiedAt(timestamp);
softwareModule.setLastModifiedBy(currentUser);
}
softwareModuleRepository.saveAll(toDelete);
}
}
@Override
public Optional<SoftwareModule> get(final long id) {
return softwareModuleRepository.findById(id).map(SoftwareModule.class::cast);
}
@Override
public List<SoftwareModule> get(final Collection<Long> ids) {
return Collections.unmodifiableList(softwareModuleRepository.findAllById(ids));
}
@Override
public boolean exists(final long id) {
return softwareModuleRepository.existsById(id);
}
@Override
public long count() {
return softwareModuleRepository.count(SoftwareModuleSpecification.isNotDeleted());
}
@Override
public Slice<SoftwareModule> findAll(final Pageable pageable) {
return JpaManagementHelper.findAllWithoutCountBySpec(softwareModuleRepository, List.of(
SoftwareModuleSpecification.isNotDeleted(),
SoftwareModuleSpecification.fetchType()), pageable);
}
@Override
public Page<SoftwareModule> findByRsql(final String rsql, final Pageable pageable) {
return JpaManagementHelper.findAllWithCountBySpec(softwareModuleRepository, List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, SoftwareModuleFields.class),
SoftwareModuleSpecification.isNotDeleted()), pageable);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public void createMetadata(final Collection<SoftwareModuleMetadataCreate> create) {
// group by software module id to minimize database access
create.stream()
@@ -272,18 +191,16 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
// touch to update revision and last modified timestamp
JpaManagementHelper.touch(
entityManager, softwareModuleRepository,
(JpaSoftwareModule) get(id).orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)));
entityManager, jpaRepository, jpaRepository.findById(id).orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)));
createsForSoftwareModule.forEach(this::saveMetadata);
});
}
@SuppressWarnings({"unchecked", "rawtypes"})
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public List<SoftwareModuleMetadata> getMetadata(final long id) {
assertSoftwareModuleExists(id);
return (List)softwareModuleMetadataRepository.findAll(metadataBySoftwareModuleIdSpec(id));
return (List) softwareModuleMetadataRepository.findAll(metadataBySoftwareModuleIdSpec(id));
}
@Override
@@ -296,15 +213,13 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
@Override
public Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleIdAndTargetVisible(final long id, final Pageable pageable) {
assertSoftwareModuleExists(id);
return JpaManagementHelper.convertPage(softwareModuleMetadataRepository.findBySoftwareModuleIdAndTargetVisible(
id, true, PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT)), pageable);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
@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();
@@ -314,15 +229,13 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
// touch to update revision and last modified timestamp
JpaManagementHelper.touch(
entityManager, softwareModuleRepository,
(JpaSoftwareModule) get(id).orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)));
entityManager, jpaRepository, jpaRepository.findById(id).orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)));
return saveMetadata(create);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
@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;
@@ -334,61 +247,59 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
update.getValue().ifPresent(metadata::setValue);
update.isTargetVisible().ifPresent(metadata::setTargetVisible);
JpaManagementHelper.touch(entityManager, softwareModuleRepository, metadata.getSoftwareModule());
JpaManagementHelper.touch(entityManager, jpaRepository, metadata.getSoftwareModule());
return softwareModuleMetadataRepository.save(metadata);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
@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, softwareModuleRepository, metadata.getSoftwareModule());
JpaManagementHelper.touch(entityManager, jpaRepository, metadata.getSoftwareModule());
softwareModuleMetadataRepository.deleteById(metadata.getId());
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public void lock(final long id) {
final JpaSoftwareModule softwareModule = softwareModuleRepository
final JpaSoftwareModule softwareModule = jpaRepository
.findById(id)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id));
if (!softwareModule.isLocked()) {
softwareModule.lock();
softwareModuleRepository.save(softwareModule);
jpaRepository.save(softwareModule);
}
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public void unlock(final long id) {
final JpaSoftwareModule softwareModule = softwareModuleRepository
final JpaSoftwareModule softwareModule = jpaRepository
.findById(id)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id));
if (softwareModule.isLocked()) {
softwareModule.unlock();
softwareModuleRepository.save(softwareModule);
jpaRepository.save(softwareModule);
}
}
@Override
public Page<SoftwareModule> findByAssignedTo(final long distributionSetId, final Pageable pageable) {
public Page<JpaSoftwareModule> findByAssignedTo(final long distributionSetId, final Pageable pageable) {
assertDistributionSetExists(distributionSetId);
return JpaManagementHelper.findAllWithCountBySpec(softwareModuleRepository,
Collections.singletonList(SoftwareModuleSpecification.byAssignedToDs(distributionSetId)), pageable
);
return JpaManagementHelper.findAllWithCountBySpec(
jpaRepository,
Collections.singletonList(SoftwareModuleSpecification.byAssignedToDs(distributionSetId)),
pageable);
}
@Override
public Slice<SoftwareModule> findByTextAndType(final String searchText, final Long typeId, final Pageable pageable) {
public Slice<JpaSoftwareModule> findByTextAndType(final String searchText, final Long typeId, final Pageable pageable) {
final List<Specification<JpaSoftwareModule>> specList = new ArrayList<>(3);
specList.add(SoftwareModuleSpecification.isNotDeleted());
@@ -403,30 +314,30 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
specList.add(SoftwareModuleSpecification.fetchType());
return JpaManagementHelper.findAllWithoutCountBySpec(softwareModuleRepository, specList, pageable);
return JpaManagementHelper.findAllWithoutCountBySpec(jpaRepository, specList, pageable);
}
@Override
public Optional<SoftwareModule> findByNameAndVersionAndType(final String name, final String version,
final long typeId) {
public Optional<JpaSoftwareModule> findByNameAndVersionAndType(final String name, final String version, final long typeId) {
assertSoftwareModuleTypeExists(typeId);
// TODO AC - Access is restricted. This could have problem with UI when access control is enabled.
// Vaadin UI use this for validation. May need to be called via elevated access
return JpaManagementHelper
.findOneBySpec(softwareModuleRepository, List.of(
SoftwareModuleSpecification.likeNameAndVersion(name, version),
SoftwareModuleSpecification.equalType(typeId),
SoftwareModuleSpecification.fetchType()))
.map(SoftwareModule.class::cast);
.findOneBySpec(
jpaRepository,
List.of(
SoftwareModuleSpecification.likeNameAndVersion(name, version),
SoftwareModuleSpecification.equalType(typeId),
SoftwareModuleSpecification.fetchType()));
}
@Override
public Slice<SoftwareModule> findByType(final long typeId, final Pageable pageable) {
public Slice<JpaSoftwareModule> findByType(final long typeId, final Pageable pageable) {
assertSoftwareModuleTypeExists(typeId);
return JpaManagementHelper.findAllWithoutCountBySpec(
softwareModuleRepository,
jpaRepository,
List.of(
SoftwareModuleSpecification.equalType(typeId),
SoftwareModuleSpecification.isNotDeleted()), pageable
@@ -445,7 +356,7 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
public long countByAssignedTo(final long distributionSetId) {
assertDistributionSetExists(distributionSetId);
return softwareModuleRepository.count(SoftwareModuleSpecification.byAssignedToDs(distributionSetId));
return jpaRepository.count(SoftwareModuleSpecification.byAssignedToDs(distributionSetId));
}
private static Specification<JpaSoftwareModuleMetadata> metadataBySoftwareModuleIdSpec(final long id) {
@@ -453,7 +364,7 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
}
private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) {
softwareModuleRepository.getAccessController().ifPresent(accessController ->
jpaRepository.getAccessController().ifPresent(accessController ->
accessController.assertOperationAllowed(AccessController.Operation.DELETE, swModule));
final Set<String> sha1Hashes = swModule.getArtifacts().stream().map(Artifact::getSha1Hash).collect(Collectors.toSet());
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() ->
@@ -499,7 +410,7 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
}
private void assertSoftwareModuleExists(final Long id) {
if (!softwareModuleRepository.existsById(id)) {
if (!jpaRepository.existsById(id)) {
throw new EntityNotFoundException(SoftwareModule.class, id);
}
}
@@ -515,4 +426,4 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
throw new EntityNotFoundException(DistributionSet.class, distributionSetId);
}
}
}
}

View File

@@ -10,166 +10,52 @@
package org.eclipse.hawkbit.repository.jpa.management;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import jakarta.persistence.EntityManager;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeFields;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.builder.GenericSoftwareModuleTypeUpdate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeCreate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeUpdate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleTypeCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleTypeSpecification;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.stereotype.Service;
/**
* JPA implementation of {@link SoftwareModuleTypeManagement}.
*/
@Transactional(readOnly = true)
@Validated
public class JpaSoftwareModuleTypeManagement implements SoftwareModuleTypeManagement {
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "software-module-type=management" }, matchIfMissing = true)
public class JpaSoftwareModuleTypeManagement
extends AbstractJpaRepositoryManagement<JpaSoftwareModuleType, SoftwareModuleTypeManagement.Create, SoftwareModuleTypeManagement.Update, SoftwareModuleTypeRepository, SoftwareModuleTypeFields>
implements SoftwareModuleTypeManagement<JpaSoftwareModuleType> {
private final DistributionSetTypeRepository distributionSetTypeRepository;
private final SoftwareModuleTypeRepository softwareModuleTypeRepository;
private final SoftwareModuleRepository softwareModuleRepository;
public JpaSoftwareModuleTypeManagement(
final DistributionSetTypeRepository distributionSetTypeRepository,
protected JpaSoftwareModuleTypeManagement(
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
final EntityManager entityManager,
final DistributionSetTypeRepository distributionSetTypeRepository,
final SoftwareModuleRepository softwareModuleRepository) {
super(softwareModuleTypeRepository, entityManager);
this.distributionSetTypeRepository = distributionSetTypeRepository;
this.softwareModuleTypeRepository = softwareModuleTypeRepository;
this.softwareModuleRepository = softwareModuleRepository;
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public SoftwareModuleType create(final SoftwareModuleTypeCreate c) {
final JpaSoftwareModuleTypeCreate create = (JpaSoftwareModuleTypeCreate) c;
return softwareModuleTypeRepository.save(AccessController.Operation.CREATE, create.build());
public Optional<JpaSoftwareModuleType> findByKey(final String key) {
return jpaRepository.findByKey(key);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public SoftwareModuleType update(final SoftwareModuleTypeUpdate u) {
final GenericSoftwareModuleTypeUpdate update = (GenericSoftwareModuleTypeUpdate) u;
final JpaSoftwareModuleType type = (JpaSoftwareModuleType) get(update.getId())
.orElseThrow(() -> new EntityNotFoundException(SoftwareModuleType.class, update.getId()));
update.getDescription().ifPresent(type::setDescription);
update.getColour().ifPresent(type::setColour);
return softwareModuleTypeRepository.save(type);
public Optional<JpaSoftwareModuleType> findByName(final String name) {
return jpaRepository.findByName(name);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final long id) {
final JpaSoftwareModuleType toDelete = softwareModuleTypeRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModuleType.class, id));
delete(toDelete);
protected Collection<JpaSoftwareModuleType> softDelete(final Collection<JpaSoftwareModuleType> toDelete) {
return toDelete.stream().filter(smt ->
softwareModuleRepository.countByType(smt) > 0 || distributionSetTypeRepository.countByElementsSmType(smt) > 0).toList();
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final Collection<Long> ids) {
softwareModuleTypeRepository
.findAll(AccessController.Operation.DELETE, softwareModuleTypeRepository.byIdsSpec(ids))
.forEach(this::delete);
}
@Override
public Optional<SoftwareModuleType> get(final long id) {
return softwareModuleTypeRepository.findById(id).map(SoftwareModuleType.class::cast);
}
@Override
public List<SoftwareModuleType> get(final Collection<Long> ids) {
return Collections.unmodifiableList(softwareModuleTypeRepository.findAllById(ids));
}
@Override
public boolean exists(final long id) {
return softwareModuleTypeRepository.existsById(id);
}
@Override
public long count() {
return softwareModuleTypeRepository.count(SoftwareModuleTypeSpecification.isNotDeleted());
}
@Override
public Slice<SoftwareModuleType> findAll(final Pageable pageable) {
return JpaManagementHelper.findAllWithoutCountBySpec(softwareModuleTypeRepository,
List.of(SoftwareModuleTypeSpecification.isNotDeleted()), pageable
);
}
@Override
public Page<SoftwareModuleType> findByRsql(final String rsql, final Pageable pageable) {
return JpaManagementHelper.findAllWithCountBySpec(softwareModuleTypeRepository, List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, SoftwareModuleTypeFields.class),
SoftwareModuleTypeSpecification.isNotDeleted()), pageable
);
}
@Override
public Optional<SoftwareModuleType> findByKey(final String key) {
return softwareModuleTypeRepository.findByKey(key);
}
@Override
public Optional<SoftwareModuleType> findByName(final String name) {
return softwareModuleTypeRepository.findByName(name);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<SoftwareModuleType> create(final Collection<SoftwareModuleTypeCreate> c) {
final List<JpaSoftwareModuleType> creates = c.stream().map(JpaSoftwareModuleTypeCreate.class::cast)
.map(JpaSoftwareModuleTypeCreate::build).toList();
return Collections.unmodifiableList(
softwareModuleTypeRepository.saveAll(AccessController.Operation.CREATE, creates));
}
private void delete(JpaSoftwareModuleType toDelete) {
if (softwareModuleRepository.countByType(toDelete) > 0
|| distributionSetTypeRepository.countByElementsSmType(toDelete) > 0) {
toDelete.setDeleted(true);
softwareModuleTypeRepository.save(AccessController.Operation.DELETE, toDelete);
} else {
softwareModuleTypeRepository.delete(toDelete);
}
}
}
}

View File

@@ -9,17 +9,18 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
import java.util.Set;
import java.util.function.Consumer;
import jakarta.persistence.EntityManager;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.artifact.ArtifactRepository;
import org.eclipse.hawkbit.cache.TenancyCacheManager;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.RolloutStatusCache;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TenantStatsManagement;
import org.eclipse.hawkbit.repository.artifact.ArtifactRepository;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.CurrentTenantCacheKeyGenerator;
import org.eclipse.hawkbit.repository.jpa.SystemManagementCacheKeyGenerator;
@@ -48,6 +49,7 @@ import org.eclipse.hawkbit.repository.report.model.SystemUsageReportWithTenants;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.dao.ConcurrencyFailureException;
@@ -58,6 +60,7 @@ import org.springframework.lang.Nullable;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@@ -69,6 +72,8 @@ import org.springframework.validation.annotation.Validated;
@Slf4j
@Transactional(readOnly = true)
@Validated
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "system-management" }, matchIfMissing = true)
public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, SystemManagement {
private static final int MAX_TENANTS_QUERY = 1000;
@@ -102,7 +107,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
private ArtifactRepository artifactRepository;
@SuppressWarnings("squid:S00107")
public JpaSystemManagement(
protected JpaSystemManagement(
final TargetRepository targetRepository, final TargetTypeRepository targetTypeRepository,
final TargetTagRepository targetTagRepository, final TargetFilterQueryRepository targetFilterQueryRepository,
final SoftwareModuleRepository softwareModuleRepository, final SoftwareModuleTypeRepository softwareModuleTypeRepository,
@@ -193,6 +198,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
public void forEachTenant(final Consumer<String> consumer) {
forEachTenant0(consumer);
}
private void forEachTenant0(final Consumer<String> consumer) {
Page<String> tenants;
Pageable query = PageRequest.of(0, MAX_TENANTS_QUERY);
@@ -320,18 +326,19 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
distributionSetTypeRepository
.save(new JpaDistributionSetType(org.eclipse.hawkbit.repository.Constants.DST_DEFAULT_OS_ONLY_KEY,
org.eclipse.hawkbit.repository.Constants.DST_DEFAULT_OS_ONLY_NAME,
"Default type with Firmware/OS only.").addMandatoryModuleType(os));
"Default type with Firmware/OS only.").setMandatoryModuleTypes(Set.of(os)));
distributionSetTypeRepository
.save(new JpaDistributionSetType(org.eclipse.hawkbit.repository.Constants.DST_DEFAULT_APP_ONLY_KEY,
org.eclipse.hawkbit.repository.Constants.DST_DEFAULT_APP_ONLY_NAME,
"Default type with app(s) only.").addMandatoryModuleType(app));
"Default type with app(s) only.").setMandatoryModuleTypes(Set.of(app)));
return distributionSetTypeRepository
.save(new JpaDistributionSetType(org.eclipse.hawkbit.repository.Constants.DST_DEFAULT_OS_WITH_APPS_KEY,
org.eclipse.hawkbit.repository.Constants.DST_DEFAULT_OS_WITH_APPS_NAME,
"Default type with Firmware/OS and optional app(s).").addMandatoryModuleType(os)
.addOptionalModuleType(app));
"Default type with Firmware/OS and optional app(s).")
.setMandatoryModuleTypes(Set.of(os))
.setOptionalModuleTypes(Set.of(app)));
}
private TenantMetaData createTenantMetadata0(final String tenant) {

View File

@@ -52,6 +52,7 @@ import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.utils.TenantConfigHelper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.domain.Page;
@@ -60,6 +61,7 @@ import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;
@@ -70,11 +72,13 @@ import org.springframework.validation.annotation.Validated;
@Slf4j
@Transactional(readOnly = true)
@Validated
public class JpaTargetFilterQueryManagement implements TargetFilterQueryManagement {
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "target-filter-management" }, matchIfMissing = true)
class JpaTargetFilterQueryManagement implements TargetFilterQueryManagement {
private final TargetFilterQueryRepository targetFilterQueryRepository;
private final TargetManagement targetManagement;
private final DistributionSetManagement distributionSetManagement;
private final DistributionSetManagement<? extends DistributionSet> distributionSetManagement;
private final QuotaManagement quotaManagement;
private final TenantConfigurationManagement tenantConfigurationManagement;
private final RepositoryProperties repositoryProperties;
@@ -82,10 +86,9 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
private final ContextAware contextAware;
private final AuditorAware<String> auditorAware;
@SuppressWarnings("java:S107")
public JpaTargetFilterQueryManagement(
protected JpaTargetFilterQueryManagement(
final TargetFilterQueryRepository targetFilterQueryRepository,
final TargetManagement targetManagement, final DistributionSetManagement distributionSetManagement,
final TargetManagement targetManagement, final DistributionSetManagement<? extends DistributionSet> distributionSetManagement,
final QuotaManagement quotaManagement, final TenantConfigurationManagement tenantConfigurationManagement,
final RepositoryProperties repositoryProperties,
final SystemSecurityContext systemSecurityContext, final ContextAware contextAware, final AuditorAware<String> auditorAware) {

View File

@@ -77,6 +77,7 @@ import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -84,6 +85,7 @@ import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;
@@ -93,10 +95,12 @@ import org.springframework.validation.annotation.Validated;
*/
@Transactional(readOnly = true)
@Validated
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "target-management" }, matchIfMissing = true)
public class JpaTargetManagement implements TargetManagement {
private final EntityManager entityManager;
private final DistributionSetManagement distributionSetManagement;
private final JpaDistributionSetManagement distributionSetManagement;
private final QuotaManagement quotaManagement;
private final TargetRepository targetRepository;
private final TargetTypeRepository targetTypeRepository;
@@ -106,8 +110,8 @@ public class JpaTargetManagement implements TargetManagement {
private final TenantAware tenantAware;
@SuppressWarnings("java:S107")
public JpaTargetManagement(final EntityManager entityManager,
final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
protected JpaTargetManagement(final EntityManager entityManager,
final JpaDistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
final TargetRepository targetRepository, final TargetTypeRepository targetTypeRepository,
final RolloutGroupRepository rolloutGroupRepository,
final TargetFilterQueryRepository targetFilterQueryRepository,

View File

@@ -28,12 +28,15 @@ import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaNamedEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.model.Tag;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -42,11 +45,13 @@ import org.springframework.validation.annotation.Validated;
*/
@Transactional(readOnly = true)
@Validated
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "target-tag-management" }, matchIfMissing = true)
public class JpaTargetTagManagement implements TargetTagManagement {
private final TargetTagRepository targetTagRepository;
public JpaTargetTagManagement(final TargetTagRepository targetTagRepository) {
protected JpaTargetTagManagement(final TargetTagRepository targetTagRepository) {
this.targetTagRepository = targetTagRepository;;
}
@@ -69,7 +74,7 @@ public class JpaTargetTagManagement implements TargetTagManagement {
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<TargetTag> create(final Collection<TagCreate> tt) {
public List<TargetTag> create(final Collection<TagCreate<Tag>> tt) {
final List<JpaTargetTag> targetTagList = tt.stream().map(JpaTagCreate.class::cast)
.map(JpaTagCreate::buildTargetTag).toList();
return Collections.unmodifiableList(

View File

@@ -38,12 +38,14 @@ import org.eclipse.hawkbit.repository.jpa.specifications.TargetTypeSpecification
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -52,6 +54,8 @@ import org.springframework.validation.annotation.Validated;
*/
@Transactional(readOnly = true)
@Validated
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "target-type-management" }, matchIfMissing = true)
public class JpaTargetTypeManagement implements TargetTypeManagement {
private final TargetTypeRepository targetTypeRepository;
@@ -60,13 +64,7 @@ public class JpaTargetTypeManagement implements TargetTypeManagement {
private final QuotaManagement quotaManagement;
/**
* Constructor
*
* @param targetTypeRepository Target type repository
* @param targetRepository Target repository
*/
public JpaTargetTypeManagement(final TargetTypeRepository targetTypeRepository,
protected JpaTargetTypeManagement(final TargetTypeRepository targetTypeRepository,
final TargetRepository targetRepository, final DistributionSetTypeRepository distributionSetTypeRepository,
final QuotaManagement quotaManagement) {
this.targetTypeRepository = targetTypeRepository;

View File

@@ -53,6 +53,7 @@ import org.eclipse.hawkbit.tenancy.configuration.PollingTime;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
@@ -65,6 +66,7 @@ import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;
@@ -75,6 +77,8 @@ import org.springframework.validation.annotation.Validated;
@Slf4j
@Transactional(readOnly = true)
@Validated
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "tenant-configuration-management" }, matchIfMissing = true)
public class JpaTenantConfigurationManagement implements TenantConfigurationManagement {
private static final ConfigurableConversionService CONVERSION_SERVICE = new DefaultConversionService();
@@ -87,7 +91,7 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
private ServiceMatcher serviceMatcher;
public JpaTenantConfigurationManagement(
protected JpaTenantConfigurationManagement(
final TenantConfigurationRepository tenantConfigurationRepository,
final TenantConfigurationProperties tenantConfigurationProperties,
final CacheManager cacheManager, final AfterTransactionCommitExecutor afterCommitExecutor,

View File

@@ -15,6 +15,8 @@ import org.eclipse.hawkbit.repository.jpa.repository.LocalArtifactRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.report.model.TenantUsage;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -23,6 +25,8 @@ import org.springframework.validation.annotation.Validated;
* Management service for statistics of a single tenant.
*/
@Validated
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "tenant-stats-management" }, matchIfMissing = true)
public class JpaTenantStatsManagement implements TenantStatsManagement {
private final TargetRepository targetRepository;
@@ -30,7 +34,7 @@ public class JpaTenantStatsManagement implements TenantStatsManagement {
private final ActionRepository actionRepository;
private final TenantAware tenantAware;
public JpaTenantStatsManagement(
protected JpaTenantStatsManagement(
final TargetRepository targetRepository, final LocalArtifactRepository artifactRepository, final ActionRepository actionRepository,
final TenantAware tenantAware) {
this.targetRepository = targetRepository;

View File

@@ -40,10 +40,9 @@ import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
/**
* AbstractDsAssignmentStrategy for offline assignments, i.e. not managed by
* hawkBit.
* AbstractDsAssignmentStrategy for offline assignments, i.e. not managed by hawkBit.
*/
public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
OfflineDsAssignmentStrategy(
final TargetRepository targetRepository,

View File

@@ -48,7 +48,7 @@ import org.springframework.util.CollectionUtils;
/**
* AbstractDsAssignmentStrategy for online assignments, i.e. managed by hawkBit.
*/
public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
OnlineDsAssignmentStrategy(final TargetRepository targetRepository,
final AfterTransactionCommitExecutor afterCommit,

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.model;
import java.io.Serial;
import java.io.Serializable;
import java.util.Objects;
import jakarta.persistence.Column;
import jakarta.persistence.ConstraintMode;
@@ -85,28 +86,13 @@ public class DistributionSetTypeElement implements Serializable {
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (key == null ? 0 : key.hashCode());
return result;
return Objects.hash(key, mandatory);
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DistributionSetTypeElement other = (DistributionSetTypeElement) obj;
if (key == null) {
return other.key == null;
} else {
return key.equals(other.key);
}
return obj instanceof DistributionSetTypeElement distributionSetTypeElement &&
Objects.equals(key, distributionSetTypeElement.key) &&
Objects.equals(mandatory, distributionSetTypeElement.mandatory);
}
}

View File

@@ -137,35 +137,22 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
@Column(name = "required_migration_step")
private boolean requiredMigrationStep;
public JpaDistributionSet(
final String name, final String version, final String description,
final DistributionSetType type, final Collection<SoftwareModule> moduleList,
final boolean requiredMigrationStep) {
super(name, version, description);
this.type = type;
// modules shall be set before type.checkComplete call
if (moduleList != null) {
moduleList.forEach(this::addModule);
}
if (this.type != null) {
complete = this.type.checkComplete(this);
}
this.valid = true;
this.requiredMigrationStep = requiredMigrationStep;
}
public JpaDistributionSet(final String name, final String version, final String description,
final DistributionSetType type, final Collection<SoftwareModule> moduleList) {
this(name, version, description, type, moduleList, false);
}
@Override
public Set<SoftwareModule> getModules() {
return Collections.unmodifiableSet(modules);
}
@SuppressWarnings("java:S1144") // used via reflection copy utils
private JpaDistributionSet setModules(final Set<SoftwareModule> modules) {
if (modules == null) {
return this; // do not change
}
modules.forEach(this::addModule); // skip if already present
this.modules.stream().filter(module -> !modules.contains(module)).toList().forEach(this::removeModule);
return this;
}
public void addModule(final SoftwareModule softwareModule) {
if (isLocked()) {
throw new LockedException(JpaDistributionSet.class, getId(), "ADD_SOFTWARE_MODULE");
@@ -175,13 +162,11 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
final Optional<SoftwareModule> found = modules.stream()
.filter(module -> module.getId().equals(softwareModule.getId())).findAny();
if (found.isPresent()) {
return;
}
final long already = modules.stream().filter(module -> module.getType().getKey().equals(softwareModule.getType().getKey())).count();
if (already >= softwareModule.getType().getMaxAssignments()) {
modules.stream().filter(module -> module.getType().getKey().equals(softwareModule.getType().getKey()))
.findAny().ifPresent(modules::remove);

View File

@@ -44,7 +44,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
@Table(name = "sp_distribution_set_type", indexes = {
@Index(name = "sp_idx_distribution_set_type_01", columnList = "tenant,deleted"),
@Index(name = "sp_idx_distribution_set_type_prim", columnList = "tenant,id") }, uniqueConstraints = {
@UniqueConstraint(columnNames = { "tenant", "type_key"}, name = "uk_sp_distribution_set_type_type_key"),
@UniqueConstraint(columnNames = { "tenant", "type_key" }, name = "uk_sp_distribution_set_type_type_key"),
@UniqueConstraint(columnNames = { "tenant", "name" }, name = "uk_sp_distribution_set_type_name") })
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities
@SuppressWarnings("squid:S2160")
@@ -88,6 +88,30 @@ public class JpaDistributionSetType extends AbstractJpaTypeEntity implements Dis
.collect(Collectors.toSet());
}
public JpaDistributionSetType setMandatoryModuleTypes(final Set<SoftwareModuleType> smType) {
return replaceOrAddModuleTypes(smType, true, true);
}
public JpaDistributionSetType setOptionalModuleTypes(final Set<SoftwareModuleType> smType) {
return replaceOrAddModuleTypes(smType, false, true);
}
public JpaDistributionSetType addMandatoryModuleType(final SoftwareModuleType smType) {
return replaceOrAddModuleTypes(Set.of(smType), true, false);
}
public JpaDistributionSetType addOptionalModuleType(final SoftwareModuleType smType) {
return replaceOrAddModuleTypes(Set.of(smType), false, false);
}
public JpaDistributionSetType removeModuleType(final SoftwareModuleType smType) {
elements.stream()
.filter(element -> smType.getId().equals(element.getSmType().getId()))
.toList() // collect to a list to avoid ConcurrentModificationException
.forEach(element -> elements.remove(element));
return this;
}
@Override
public boolean checkComplete(final DistributionSet distributionSet) {
final List<SoftwareModuleType> smTypes = distributionSet.getModules().stream()
@@ -97,23 +121,6 @@ public class JpaDistributionSetType extends AbstractJpaTypeEntity implements Dis
return !smTypes.isEmpty() && new HashSet<>(smTypes).containsAll(getMandatoryModuleTypes());
}
public JpaDistributionSetType addOptionalModuleType(final SoftwareModuleType smType) {
return setModuleType(smType, false);
}
public JpaDistributionSetType addMandatoryModuleType(final SoftwareModuleType smType) {
return setModuleType(smType, true);
}
public JpaDistributionSetType removeModuleType(final Long smTypeId) {
// we search by id (standard equals compares also revision)
elements.stream()
.filter(element -> element.getSmType().getId().equals(smTypeId))
.findAny()
.ifPresent(elements::remove);
return this;
}
@Override
public String toString() {
return "DistributionSetType [key=" + getKey() + ", isDeleted()=" + isDeleted() + ", getId()=" + getId() + "]";
@@ -135,20 +142,30 @@ public class JpaDistributionSetType extends AbstractJpaTypeEntity implements Dis
.publishEvent(new DistributionSetTypeDeletedEvent(getTenant(), getId(), getClass()));
}
private JpaDistributionSetType setModuleType(final SoftwareModuleType smType, final boolean mandatory) {
private JpaDistributionSetType replaceOrAddModuleTypes(final Set<SoftwareModuleType> smTypes, final boolean mandatory, final boolean replace) {
if (smTypes == null) {
return this; // do not change
}
if (elements.isEmpty()) {
elements.add(new DistributionSetTypeElement(this, (JpaSoftwareModuleType) smType, mandatory));
smTypes.forEach(smType -> elements.add(new DistributionSetTypeElement(this, (JpaSoftwareModuleType) smType, mandatory)));
return this;
}
// check if this was in the list before
elements.stream()
.filter(element -> element.getSmType().getKey().equals(smType.getKey()))
.findAny()
.ifPresentOrElse(
element -> element.setMandatory(mandatory),
() -> elements.add(new DistributionSetTypeElement(this, (JpaSoftwareModuleType) smType, mandatory)));
smTypes.stream()
.filter(smType -> !elements.contains(new DistributionSetTypeElement(this, (JpaSoftwareModuleType) smType, mandatory)))
.collect(Collectors.toSet())
.forEach(smType -> elements.add(new DistributionSetTypeElement(this, (JpaSoftwareModuleType) smType, mandatory)));
if (replace) {
final Set<Long> smTypeIds = smTypes.stream()
.map(SoftwareModuleType::getId)
.collect(Collectors.toSet());
elements.stream()
.filter(element -> element.isMandatory() == mandatory && !smTypeIds.contains(element.getSmType().getId()))
.collect(Collectors.toSet())
.forEach(element -> elements.remove(element));
}
return this;
}
}

View File

@@ -112,7 +112,6 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
private boolean deleted;
@ToString.Exclude
@Getter(AccessLevel.NONE)
@ManyToMany(mappedBy = "modules", targetEntity = JpaDistributionSet.class, fetch = FetchType.LAZY)
private List<DistributionSet> assignedTo;
@@ -174,29 +173,6 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
locked = false;
}
public void setDeleted(final boolean deleted) {
if (assignedTo != null) {
final List<DistributionSet> lockedDS = assignedTo.stream()
.filter(DistributionSet::isLocked)
.filter(ds -> !ds.isDeleted())
.toList();
if (!lockedDS.isEmpty()) {
final StringBuilder sb = new StringBuilder("Part of ");
if (lockedDS.size() == 1) {
sb.append("a locked distribution set: ");
} else {
sb.append(lockedDS.size()).append(" locked distribution sets: ");
}
for (final DistributionSet ds : lockedDS) {
sb.append(ds.getName()).append(":").append(ds.getVersion()).append(" (").append(ds.getId()).append("), ");
}
sb.delete(sb.length() - 2, sb.length());
throw new LockedException(JpaSoftwareModule.class, getId(), "DELETE", sb.toString());
}
}
this.deleted = deleted;
}
@Override
public void fireCreateEvent() {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new SoftwareModuleCreatedEvent(this));

View File

@@ -18,8 +18,8 @@ import jakarta.persistence.EntityManager;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaTenantAwareBaseEntity;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.jpa.domain.Specification;
@@ -37,7 +37,7 @@ import org.springframework.transaction.annotation.Transactional;
*/
@NoRepositoryBean
@Transactional(readOnly = true)
public interface BaseEntityRepository<T extends AbstractJpaTenantAwareBaseEntity>
public interface BaseEntityRepository<T extends AbstractJpaBaseEntity>
extends PagingAndSortingRepository<T, Long>, CrudRepository<T, Long>, JpaSpecificationExecutor<T>,
JpaSpecificationEntityGraphExecutor<T>, NoCountSliceRepository<T>, ACMRepository<T> {
@@ -118,11 +118,11 @@ public interface BaseEntityRepository<T extends AbstractJpaTenantAwareBaseEntity
}
}
default <S extends AbstractJpaTenantAwareBaseEntity> Specification<S> byIdSpec(final Long id) {
default <S extends AbstractJpaBaseEntity> Specification<S> byIdSpec(final Long id) {
return (root, query, cb) -> cb.equal(root.get(AbstractJpaBaseEntity_.id), id);
}
default <S extends AbstractJpaTenantAwareBaseEntity> Specification<S> byIdsSpec(final Iterable<Long> ids) {
default <S extends AbstractJpaBaseEntity> Specification<S> byIdsSpec(final Iterable<Long> ids) {
final Collection<Long> collection;
if (ids instanceof Collection<Long> idCollection) {
collection = idCollection;

View File

@@ -25,7 +25,7 @@ import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaTenantAwareBaseEntity;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
@@ -35,7 +35,7 @@ import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
@Slf4j
public class BaseEntityRepositoryACM<T extends AbstractJpaTenantAwareBaseEntity> implements BaseEntityRepository<T> {
public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements BaseEntityRepository<T> {
private static final String SPEC_MUST_NOT_BE_NULL = "Specification must not be null";
private static final String APPENDED_ACCESS_RULES_SPEC_OF_NON_NULL_SPEC_MUST_NOT_BE_NULL =
@@ -337,7 +337,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaTenantAwareBaseEntity>
}
@SuppressWarnings("unchecked")
static <T extends AbstractJpaTenantAwareBaseEntity, R extends BaseEntityRepository<T>> R of(
static <T extends AbstractJpaBaseEntity, R extends BaseEntityRepository<T>> R of(
final R repository, @NonNull final AccessController<T> accessController) {
Objects.requireNonNull(repository);
Objects.requireNonNull(accessController);

View File

@@ -36,7 +36,7 @@ public interface DistributionSetTagRepository
* @param tagName to filter on
* @return the {@link DistributionSetTag} if found, otherwise null
*/
Optional<DistributionSetTag> findByNameEquals(String tagName);
Optional<JpaDistributionSetTag> findByNameEquals(String tagName);
/**
* Returns all instances of the type.

View File

@@ -19,7 +19,6 @@ import jakarta.persistence.NoResultException;
import jakarta.persistence.TypedQuery;
import jakarta.transaction.Transactional;
import org.eclipse.hawkbit.repository.BaseRepositoryTypeProvider;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -175,16 +174,4 @@ public class HawkbitBaseRepository<T, ID extends Serializable> extends SimpleJpa
final List<S> content = query.getResultList();
return new PageImpl<>(content, pageable, content.size());
}
/**
* Simple implementation of {@link BaseRepositoryTypeProvider} leveraging our
* {@link HawkbitBaseRepository} for all current use cases
*/
public static class RepositoryTypeProvider implements BaseRepositoryTypeProvider {
@Override
public Class<?> getBaseRepositoryType(final Class<?> repositoryType) {
return HawkbitBaseRepository.class;
}
}
}

View File

@@ -31,13 +31,13 @@ public interface SoftwareModuleTypeRepository extends BaseEntityRepository<JpaSo
* @param key to search for
* @return all {@link SoftwareModuleType}s in the repository with given {@link SoftwareModuleType#getKey()}
*/
Optional<SoftwareModuleType> findByKey(String key);
Optional<JpaSoftwareModuleType> findByKey(String key);
/**
* @param name to search for
* @return all {@link SoftwareModuleType}s in the repository with given {@link SoftwareModuleType#getName()}
*/
Optional<SoftwareModuleType> findByName(String name);
Optional<JpaSoftwareModuleType> findByName(String name);
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety reasons (this is a "delete everything" query

View File

@@ -9,6 +9,7 @@
*/
package org.eclipse.hawkbit.repository.event.remote.entity;
import org.eclipse.hawkbit.repository.DistributionSetManagement.Create;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.junit.jupiter.api.Test;
@@ -31,6 +32,9 @@ class DistributionSetCreatedEventTest extends AbstractRemoteEntityEventTest<Dist
@Override
protected DistributionSet createEntity() {
return distributionSetManagement.create(
entityFactory.distributionSet().create().name("incomplete").version("2").description("incomplete").type("os"));
Create.builder()
.type(distributionSetTypeManagement.findByKey("os").orElseThrow())
.name("incomplete").version("2").description("incomplete")
.build());
}
}

View File

@@ -9,6 +9,7 @@
*/
package org.eclipse.hawkbit.repository.event.remote.entity;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement.Create;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.junit.jupiter.api.Test;
@@ -38,6 +39,6 @@ class DistributionSetTagEventTest extends AbstractRemoteEntityEventTest<Distribu
@Override
protected DistributionSetTag createEntity() {
return distributionSetTagManagement.create(entityFactory.tag().create().name("tag1"));
return distributionSetTagManagement.create(Create.builder().name("tag1").build());
}
}

View File

@@ -9,6 +9,7 @@
*/
package org.eclipse.hawkbit.repository.event.remote.entity;
import org.eclipse.hawkbit.repository.DistributionSetManagement.Create;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.junit.jupiter.api.Test;
@@ -36,6 +37,9 @@ class DistributionSetUpdatedEventTest extends AbstractRemoteEntityEventTest<Dist
@Override
protected DistributionSet createEntity() {
return distributionSetManagement.create(
entityFactory.distributionSet().create().name("incomplete").version("2").description("incomplete").type("os"));
Create.builder()
.type(distributionSetTypeManagement.findByKey("os").orElseThrow())
.name("incomplete").version("2").description("incomplete")
.build());
}
}

View File

@@ -9,8 +9,10 @@
*/
package org.eclipse.hawkbit.repository.event.remote.entity;
import java.util.Collections;
import java.util.Set;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
@@ -38,10 +40,16 @@ class RolloutEventTest extends AbstractRemoteEntityEventTest<Rollout> {
protected Rollout createEntity() {
testdataFactory.createTarget("12345");
final SoftwareModule module = softwareModuleManagement.create(
entityFactory.softwareModule().create().name("swm").version("2").description("desc").type("os"));
SoftwareModuleManagement.Create.builder()
.type(softwareModuleTypeManagement.findByKey("os").orElseThrow())
.name("swm").version("2").description("desc")
.build());
final DistributionSet ds = distributionSetManagement
.create(entityFactory.distributionSet().create().name("complete").version("2").description("complete")
.type("os").modules(Collections.singletonList(module.getId())));
.create(DistributionSetManagement.Create.builder()
.type(distributionSetTypeManagement.findByKey("os").orElseThrow())
.name("complete").version("2").description("complete")
.modules(Set.of(module))
.build());
return rolloutManagement.create(
entityFactory.rollout().create().name("exampleRollout").targetFilterQuery("controllerId==*").distributionSetId(ds),

View File

@@ -11,9 +11,11 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
@@ -79,12 +81,16 @@ class RolloutGroupEventTest extends AbstractRemoteEntityEventTest<RolloutGroup>
protected RolloutGroup createEntity() {
testdataFactory.createTarget(UUID.randomUUID().toString());
final SoftwareModule module = softwareModuleManagement.create(
entityFactory.softwareModule().create().name("swm").version("2").description("desc").type("os"));
SoftwareModuleManagement.Create.builder()
.type(softwareModuleTypeManagement.findByKey("os").orElseThrow())
.name("swm").version("2").description("desc")
.build());
final DistributionSet ds = distributionSetManagement
.create(entityFactory.distributionSet().create()
.name("complete").version("2")
.description("complete").type("os")
.modules(List.of(module.getId())));
.create(DistributionSetManagement.Create.builder()
.type(distributionSetTypeManagement.findByKey("os").orElseThrow())
.name("complete").version("2").description("complete")
.modules(Set.of(module))
.build());
final Rollout entity = rolloutManagement.create(
entityFactory.rollout().create().name("exampleRollout").targetFilterQuery("controllerId==*").distributionSetId(ds),

View File

@@ -176,16 +176,14 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
targets.stream().map(Target::getControllerId).toList(), tag.getId());
}
protected List<DistributionSet> assignTag(final Collection<DistributionSet> sets,
protected List<? extends DistributionSet> assignTag(final Collection<? extends DistributionSet> sets,
final DistributionSetTag tag) {
return distributionSetManagement.assignTag(
sets.stream().map(DistributionSet::getId).toList(), tag.getId());
return distributionSetManagement.assignTag(sets.stream().map(DistributionSet::getId).toList(), tag.getId());
}
protected List<DistributionSet> unassignTag(final Collection<DistributionSet> sets,
final DistributionSetTag tag) {
return distributionSetManagement.unassignTag(
sets.stream().map(DistributionSet::getId).toList(), tag.getId());
protected List<? extends DistributionSet> unassignTag(
final Collection<DistributionSet> sets, final DistributionSetTag tag) {
return distributionSetManagement.unassignTag(sets.stream().map(DistributionSet::getId).toList(), tag.getId());
}
protected TargetTypeAssignmentResult initiateTypeAssignment(final Collection<Target> targets, final TargetType type) {
@@ -278,7 +276,7 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
/**
* Asserts that the given callable succeeds.
*
* </p>
* Note: This method will assume that EntityNotFoundException is OK, as security tests use dummy (non-existing) IDs.
* It matters to either callable succeeds without any exception or at most EntityNotFoundException.
* All other cases will be considered as an error.
@@ -288,7 +286,7 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
private void assertPermissionWorks(final Callable<?> callable) {
try {
callable.call();
} catch (Throwable th) {
} catch (final Throwable th) {
if (th instanceof EntityNotFoundException) {
log.info("Expected (at most) EntityNotFoundException catch: {}", th.getMessage());
} else {
@@ -298,8 +296,7 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
}
protected void finishAction(final Action action) {
controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(action.getId()).status(Action.Status.FINISHED));
controllerManagement.addUpdateActionStatus(entityFactory.actionStatus().create(action.getId()).status(Action.Status.FINISHED));
}
protected Set<TargetTag> getTargetTags(final String controllerId) {
@@ -313,4 +310,4 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
private JpaRollout refresh(final Rollout rollout) {
return rolloutRepository.findById(rollout.getId()).get();
}
}
}

View File

@@ -12,11 +12,13 @@ package org.eclipse.hawkbit.repository.jpa;
import java.util.List;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.RepositoryManagement;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Pageable;
public abstract class AbstractRepositoryManagementSecurityTest<T, C, U> extends AbstractJpaIntegrationTest {
public abstract class AbstractRepositoryManagementSecurityTest<T extends BaseEntity, C, U extends Identifiable<Long>> extends AbstractJpaIntegrationTest {
/**
* @return the repository management to test with
@@ -127,5 +129,4 @@ public abstract class AbstractRepositoryManagementSecurityTest<T, C, U> extends
void findByRsqlPermissionCheck() {
assertPermissions(() -> getRepositoryManagement().findByRsql("(name==*)", Pageable.ofSize(1)), List.of(SpPermission.READ_REPOSITORY));
}
}
}

View File

@@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
@@ -184,8 +185,10 @@ class DistributionSetAccessControllerTest extends AbstractJpaIntegrationTest {
final DistributionSet permitted = testdataFactory.createDistributionSet();
final DistributionSet readOnly = testdataFactory.createDistributionSet();
final DistributionSet hidden = testdataFactory.createDistributionSet();
final Long dsTagId = distributionSetTagManagement.create(entityFactory.tag().create().name("dsTag")).getId();
final Long dsTag2Id = distributionSetTagManagement.create(entityFactory.tag().create().name("dsTag2")).getId();
final Long dsTagId = distributionSetTagManagement.create(
DistributionSetTagManagement.Create.builder().name("dsTag").build()).getId();
final Long dsTag2Id = distributionSetTagManagement.create(
DistributionSetTagManagement.Create.builder().name("dsTag2").build()).getId();
// perform tag assignment before setting access rules
distributionSetManagement.assignTag(Arrays.asList(permitted.getId(), readOnly.getId(), hidden.getId()), dsTagId);

View File

@@ -21,6 +21,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement.Create;
import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
@@ -251,8 +252,10 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest {
void checkAutoAssignWithFailures() {
// incomplete distribution set that will be assigned
final DistributionSet setF = distributionSetManagement.create(entityFactory.distributionSet().create()
.name("dsA").version("1").type(testdataFactory.findOrCreateDefaultTestDsType()));
final DistributionSet setF = distributionSetManagement.create(Create.builder()
.type(testdataFactory.findOrCreateDefaultTestDsType())
.name("dsA").version("1")
.build());
final DistributionSet setA = testdataFactory.createDistributionSet("dsA");
final DistributionSet setB = testdataFactory.createDistributionSet("dsB");

View File

@@ -16,6 +16,7 @@ import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.assertj.core.api.Assertions;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.event.TenantAwareEvent;
import org.eclipse.hawkbit.repository.event.remote.DistributionSetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent;
@@ -211,7 +212,7 @@ class RepositoryEntityEventTest extends AbstractJpaIntegrationTest {
void softwareModuleUpdateEventIsPublished() throws InterruptedException {
final SoftwareModule softwareModule = testdataFactory.createSoftwareModuleApp();
softwareModuleManagement
.update(entityFactory.softwareModule().update(softwareModule.getId()).description("New"));
.update(SoftwareModuleManagement.Update.builder().id(softwareModule.getId()).description("New").build());
final SoftwareModuleUpdatedEvent softwareModuleUpdatedEvent = eventListener
.waitForEvent(SoftwareModuleUpdatedEvent.class);
@@ -267,5 +268,4 @@ class RepositoryEntityEventTest extends AbstractJpaIntegrationTest {
return null;
}
}
}
}

View File

@@ -660,9 +660,9 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
// create two artifacts with identical SHA1 hash
final Artifact artifact = artifactManagement.create(new ArtifactUpload(new ByteArrayInputStream(random),
ds.findFirstModuleByType(osType).get().getId(), "file1", false, artifactSize));
findFirstModuleByType(ds, osType).orElseThrow().getId(), "file1", false, artifactSize));
final Artifact artifact2 = artifactManagement.create(new ArtifactUpload(new ByteArrayInputStream(random),
ds2.findFirstModuleByType(osType).get().getId(), "file1", false, artifactSize));
findFirstModuleByType(ds2, osType).orElseThrow().getId(), "file1", false, artifactSize));
assertThat(artifact.getSha1Hash()).isEqualTo(artifact2.getSha1Hash());
assertThat(

View File

@@ -15,6 +15,7 @@ import java.util.Set;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.im.authentication.SpRole;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
@@ -291,8 +292,8 @@ class DeploymentManagementSecurityTest extends AbstractJpaIntegrationTest {
@Test
void cancelActionsForDistributionSetPermissionsCheck() {
assertPermissions(() -> {
deploymentManagement.cancelActionsForDistributionSet(DistributionSetInvalidation.CancelationType.FORCE,
entityFactory.distributionSet().create().build());
deploymentManagement.cancelActionsForDistributionSet(
DistributionSetInvalidation.CancelationType.FORCE, new JpaDistributionSet());
return null;
}, List.of(SpPermission.UPDATE_TARGET));
}

View File

@@ -33,6 +33,8 @@ import lombok.Getter;
import org.assertj.core.api.Assertions;
import org.eclipse.hawkbit.repository.ActionStatusFields;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent;
import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent;
@@ -129,7 +131,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
}
/**
* Verifies that management get access react as specified on calls for non existing entities by means
* Verifies that management get access react as specified on calls for non existing entities by means
* of Optional not present.
*/
@Test
@@ -140,8 +142,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
}
/**
* Verifies that management queries react as specified on calls for non existing entities
* by means of throwing EntityNotFoundException.
* Verifies that management queries react as specified on calls for non existing entities
* by means of throwing EntityNotFoundException.
*/
@Test
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) })
@@ -298,7 +300,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
// not exists
assignDS.add(100_000L);
final Long tagId = distributionSetTagManagement.create(entityFactory.tag().create().name("Tag1")).getId();
final Long tagId = distributionSetTagManagement.create(DistributionSetTagManagement.Create.builder().name("Tag1").build()).getId();
assertThatExceptionOfType(EntityNotFoundException.class)
.isThrownBy(() -> distributionSetManagement.assignTag(assignDS, tagId))
@@ -321,7 +323,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6),
@Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 6) })
// implicit lock })
// implicit lock
void multiAssigmentHistoryOverMultiplePagesResultsInTwoActiveAction() {
final DistributionSet cancelDs = testdataFactory.createDistributionSet("Canceled DS", "1.0", Collections.emptyList());
final DistributionSet cancelDs2 = testdataFactory.createDistributionSet("Canceled DS", "1.2", Collections.emptyList());
@@ -336,7 +338,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
}
/**
* Cancels multiple active actions on a target. Expected behaviour is that with two active
* Cancels multiple active actions on a target. Expected behaviour is that with two active
* After canceling the first one also the target goes back to IN_SYNC as no open action is left.
*/
@Test
@@ -384,7 +386,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
}
/**
* Cancels multiple active actions on a target. Expected behaviour is that with two active
* Cancels multiple active actions on a target. Expected behaviour is that with two active
* also the target goes back to IN_SYNC as no open action is left.
*/
@Test
@@ -497,7 +499,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
}
/**
* Simple offline deployment of a distribution set to a list of targets. Verifies that offline assigment
* Simple offline deployment of a distribution set to a list of targets. Verifies that offline assigment
* is correctly executed for targets that do not have a running update already. Those are ignored.
*/
@Test
@@ -848,9 +850,9 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
.contains("Assignment initiated by user 'bumlux'")
.contains("""
Assignment automatically confirmed by initiator 'not_bumlux'.\s
Auto confirmation activated by system user: 'bumlux'\s
Remark: my personal remark""");
} else {
// assignment never required confirmation, auto-confirmation will not be
@@ -1141,8 +1143,12 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
final SoftwareModule ah = testdataFactory.createSoftwareModuleApp();
final SoftwareModule os = testdataFactory.createSoftwareModuleOs();
final DistributionSet incomplete = distributionSetManagement.create(entityFactory.distributionSet().create()
.name("incomplete").version("v1").type(standardDsType).modules(Collections.singletonList(ah.getId())));
final DistributionSet incomplete = distributionSetManagement.create(
DistributionSetManagement.Create.builder()
.type(standardDsType)
.name("incomplete").version("v1")
.modules(Set.of(ah))
.build());
assertThatExceptionOfType(IncompleteDistributionSetException.class)
.as("expected IncompleteDistributionSetException")
@@ -1156,7 +1162,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
}
/**
* Multiple deployments or distribution set to target assignment test. Expected behaviour is that a new deployment
* Multiple deployments or distribution set to target assignment test. Expected behaviour is that a new deployment
* overides unfinished old one which are canceled as part of the operation.
*/
@Test
@@ -1222,7 +1228,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
}
/**
* Multiple deployments or distribution set to target assignment test including finished response
* Multiple deployments or distribution set to target assignment test including finished response
* IN_SYNC status and installed DS is set to the assigned DS entry.
*/
@Test
@@ -1317,12 +1323,11 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
* or {@link Target#getInstalledDistributionSet()}
*/
/**
* Deletes distribution set. Expected behaviour is that a soft delete is performed
* Deletes distribution set. Expected behaviour is that a soft delete is performed
* if the DS is assigned to a target and a hard delete if the DS is not in use at all.
*/
@Test
void deleteDistributionSet() {
final PageRequest pageRequest = PageRequest.of(0, 100, Direction.ASC, "id");
final String undeployedTargetPrefix = "undep-T";
@@ -1351,7 +1356,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
}
// verify that deleted attribute is used correctly
List<DistributionSet> allFoundDS = distributionSetManagement.findByCompleted(true, PAGE).getContent();
List<? extends DistributionSet> allFoundDS = distributionSetManagement.findByCompleted(true, PAGE).getContent();
assertThat(allFoundDS).as("no ds should be founded").isEmpty();
assertThat(distributionSetRepository.findAll(SpecificationsBuilder.combineWithAnd(Arrays
@@ -1359,7 +1364,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
PAGE).getContent()).as("wrong size of founded ds").hasSize(noOfDistributionSets);
IntStream.range(0, deploymentResult.getDistributionSets().size()).forEach(i -> testdataFactory.sendUpdateActionStatusToTargets(
deploymentResult.getDeployedTargets(), Status.FINISHED, Collections.singletonList("blabla alles gut")));
deploymentResult.getDeployedTargets(), Status.FINISHED, Collections.singletonList("blabla alles gut")));
// try to delete again
distributionSetManagement.delete(deploymentResult.getDistributionSetIDs());
@@ -1391,7 +1396,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
deployedTargetPrefix, noOfDeployedTargets, noOfDistributionSets, "myTestDS");
IntStream.range(0, deploymentResult.getDistributionSets().size()).forEach(i -> testdataFactory.sendUpdateActionStatusToTargets(
deploymentResult.getDeployedTargets(), Status.FINISHED, Collections.singletonList("blabla alles gut")));
deploymentResult.getDeployedTargets(), Status.FINISHED, Collections.singletonList("blabla alles gut")));
assertThat(targetManagement.count()).as("size of targets is wrong").isNotZero();
assertThat(actionStatusRepository.count()).as("size of action status is wrong").isNotZero();
@@ -1479,7 +1484,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
/**
* The test verifies that the DS itself is not changed because of an target assignment
* which is a relationship but not a changed on the entity itself..
* which is a relationship but not a changed on the entity itself..
*/
@Test
void checkThatDsRevisionsIsNotChangedWithTargetAssignment() {

View File

@@ -13,12 +13,12 @@ import java.util.List;
import java.util.Map;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.RepositoryManagement;
import org.eclipse.hawkbit.repository.builder.DistributionSetCreate;
import org.eclipse.hawkbit.repository.builder.DistributionSetUpdate;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.jpa.AbstractRepositoryManagementSecurityTest;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.junit.jupiter.api.Test;
/**
@@ -26,22 +26,22 @@ import org.junit.jupiter.api.Test;
* Story: SecurityTests DistributionSetManagement
*/
class DistributionSetManagementSecurityTest
extends AbstractRepositoryManagementSecurityTest<DistributionSet, DistributionSetCreate, DistributionSetUpdate> {
extends AbstractRepositoryManagementSecurityTest<DistributionSet, DistributionSetManagement.Create, DistributionSetManagement.Update> {
@Override
protected RepositoryManagement<DistributionSet, DistributionSetCreate, DistributionSetUpdate> getRepositoryManagement() {
protected DistributionSetManagement getRepositoryManagement() {
return distributionSetManagement;
}
@Override
protected DistributionSetCreate getCreateObject() {
return entityFactory.distributionSet().create().name("name").version("1.0.0").type("type");
protected DistributionSetManagement.Create getCreateObject() {
return DistributionSetManagement.Create.builder().name("name").version("1.0.0").type(defaultDsType()).build();
}
@Override
protected DistributionSetUpdate getUpdateObject() {
return entityFactory.distributionSet().update(0L).name("a new name")
.description("a new description").version("a new version").requiredMigrationStep(true);
protected DistributionSetManagement.Update getUpdateObject() {
return DistributionSetManagement.Update.builder().id(0L).name("a new name")
.description("a new description").version("a new version").requiredMigrationStep(true).build();
}
/**
@@ -87,7 +87,7 @@ class DistributionSetManagementSecurityTest
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void getMetadataPermissiosCheck() {
void getMetadataPermissionsCheck() {
assertPermissions(() -> distributionSetManagement.getMetadata(1L), List.of(SpPermission.READ_REPOSITORY));
}
@@ -97,7 +97,7 @@ class DistributionSetManagementSecurityTest
@Test
void updateMetadataPermissionsCheck() {
assertPermissions(() -> {
distributionSetManagement.updateMetadata(1L,"key", "value");
distributionSetManagement.updateMetadata(1L, "key", "value");
return null;
},
List.of(SpPermission.UPDATE_REPOSITORY));
@@ -287,9 +287,12 @@ class DistributionSetManagementSecurityTest
*/
@Test
void invalidatePermissionsCheck() {
distributionSetTypeManagement.create(entityFactory.distributionSetType().create().key("type").name("name"));
final DistributionSetType dsType = distributionSetTypeManagement.create(DistributionSetTypeManagement.Create.builder()
.key("type").name("name").build());
final DistributionSet ds = distributionSetManagement.create(DistributionSetManagement.Create.builder()
.type(dsType).name("test").version("1.0.0").build());
assertPermissions(() -> {
distributionSetManagement.invalidate(entityFactory.distributionSet().create().name("name").version("1.0").type("type").build());
((DistributionSetManagement) distributionSetManagement).invalidate(ds);
return null;
}, List.of(SpPermission.UPDATE_REPOSITORY, SpPermission.READ_REPOSITORY));
}

View File

@@ -19,6 +19,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
@@ -30,9 +31,10 @@ import jakarta.validation.ConstraintViolationException;
import org.assertj.core.api.Condition;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.builder.DistributionSetCreate;
import org.eclipse.hawkbit.repository.builder.DistributionSetUpdate;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTagCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent;
@@ -96,7 +98,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
}
/**
* Verifies that management queries react as specified on calls for non existing entities by means of
* Verifies that management queries react as specified on calls for non existing entities by means of
* throwing EntityNotFoundException.
*/
@Test
@@ -133,9 +135,6 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
verifyThrownExceptionBy(() -> distributionSetManagement.unassignTag(singletonList(NOT_EXIST_IDL), dsTag.getId()), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.create(
entityFactory.distributionSet().create().name("xxx").type(NOT_EXIST_ID)), "DistributionSetType");
verifyThrownExceptionBy(() -> distributionSetManagement.createMetadata(NOT_EXIST_IDL, Map.of("123", "123")), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.delete(singletonList(NOT_EXIST_IDL)), "DistributionSet");
@@ -154,10 +153,11 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
.hasMessageContaining("DistributionSet");
verifyThrownExceptionBy(
() -> distributionSetManagement.update(entityFactory.distributionSet().update(NOT_EXIST_IDL)), "DistributionSet");
() -> distributionSetManagement.update(DistributionSetManagement.Update.builder().id(NOT_EXIST_IDL).build()),
"DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetadata(NOT_EXIST_IDL,"xxx", "xxx"), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetadata(set.getId(),NOT_EXIST_ID, "xxx"), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetadata(NOT_EXIST_IDL, "xxx", "xxx"), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetadata(set.getId(), NOT_EXIST_ID, "xxx"), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.getOrElseThrowException(NOT_EXIST_IDL), "DistributionSet");
@@ -199,7 +199,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
@Test
void createDistributionSetWithImplicitType() {
final DistributionSet set = distributionSetManagement
.create(entityFactory.distributionSet().create().name("newtypesoft").version("1"));
.create(DistributionSetManagement.Create.builder().type(defaultDsType()).name("newtypesoft").version("1").build());
assertThat(set.getType())
.as("Type should be equal to default type of tenant")
@@ -212,7 +212,8 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
*/
@Test
void createDistributionSetWithDuplicateNameAndVersionFails() {
final DistributionSetCreate distributionSetCreate = entityFactory.distributionSet().create().name("newtypesoft").version("1");
final DistributionSetManagement.Create distributionSetCreate =
DistributionSetManagement.Create.builder().type(defaultDsType()).name("newtypesoft").version("1").build();
distributionSetManagement.create(distributionSetCreate);
assertThatExceptionOfType(EntityAlreadyExistsException.class).isThrownBy(() -> distributionSetManagement.create(distributionSetCreate));
@@ -223,14 +224,14 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
*/
@Test
void createMultipleDistributionSetsWithImplicitType() {
final List<DistributionSetCreate> creates = new ArrayList<>(10);
final List<DistributionSetManagement.Create> creates = new ArrayList<>(10);
for (int i = 0; i < 10; i++) {
creates.add(entityFactory.distributionSet().create().name("newtypesoft" + i).version("1" + i));
creates.add(DistributionSetManagement.Create.builder().type(defaultDsType()).name("newtypesoft" + i).version("1" + i).build());
}
assertThat(distributionSetManagement.create(creates))
.as("Type should be equal to default type of tenant")
.are(new Condition<>() {
.are(new Condition<DistributionSet>() {
@Override
public boolean matches(final DistributionSet value) {
@@ -308,9 +309,10 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
assignDS.add(testdataFactory.createDistributionSet("DS" + i, "1.0", Collections.emptyList()).getId());
}
final DistributionSetTag tag = distributionSetTagManagement.create(entityFactory.tag().create().name(TAG1_NAME));
final DistributionSetTag tag = distributionSetTagManagement.create(
DistributionSetTagManagement.Create.builder().name(TAG1_NAME).build());
final List<DistributionSet> assignedDS = distributionSetManagement.assignTag(assignDS, tag.getId());
final List<? extends DistributionSet> assignedDS = distributionSetManagement.assignTag(assignDS, tag.getId());
assertThat(assignedDS).as("assigned ds has wrong size").hasSize(4);
assignedDS.stream().map(JpaDistributionSet.class::cast).forEach(ds -> assertThat(ds.getTags())
.as("ds has wrong tag size")
@@ -362,7 +364,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
.isInstanceOf(EntityReadOnlyException.class);
// not allowed as it is assigned now
final Long appId = getOrThrow(ds.findFirstModuleByType(appType)).getId();
final Long appId = getOrThrow(findFirstModuleByType(ds, appType)).getId();
assertThatThrownBy(() -> distributionSetManagement.unassignSoftwareModule(dsId, appId))
.isInstanceOf(EntityReadOnlyException.class);
}
@@ -373,17 +375,22 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
@Test
void updateDistributionSetUnsupportedModuleFails() {
final Long setId = distributionSetManagement.create(
entityFactory.distributionSet().create()
DistributionSetManagement.Create.builder()
.type(distributionSetTypeManagement.create(
entityFactory.distributionSetType().create()
DistributionSetTypeManagement.Create.builder()
.key("test")
.name("test")
.mandatory(singletonList(osType.getId()))).getKey())
.mandatoryModuleTypes(Set.of(osType))
.build()))
.name("agent-hub2")
.version("1.0.5")).getId();
.version("1.0.5")
.build()).getId();
final Set<Long> moduleId = Set.of(softwareModuleManagement.create(
entityFactory.softwareModule().create().name("agent-hub2").version("1.0.5").type(appType.getKey())).getId());
SoftwareModuleManagement.Create.builder()
.type(appType)
.name("agent-hub2").version("1.0.5")
.build()).getId());
// update data
assertThatThrownBy(() -> distributionSetManagement.assignSoftwareModules(setId, moduleId))
@@ -403,17 +410,17 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
// legal update of module addition
distributionSetManagement.assignSoftwareModules(ds.getId(), Set.of(os.getId()));
ds = getOrThrow(distributionSetManagement.getWithDetails(ds.getId()));
assertThat(getOrThrow(ds.findFirstModuleByType(osType))).isEqualTo(os);
assertThat(getOrThrow(findFirstModuleByType(ds, osType))).isEqualTo(os);
// legal update of module removal
distributionSetManagement.unassignSoftwareModule(ds.getId(),
getOrThrow(ds.findFirstModuleByType(appType)).getId());
getOrThrow(findFirstModuleByType(ds, appType)).getId());
ds = getOrThrow(distributionSetManagement.getWithDetails(ds.getId()));
assertThat(ds.findFirstModuleByType(appType)).isNotPresent();
assertThat(findFirstModuleByType(ds, appType)).isNotPresent();
// Update description
distributionSetManagement.update(entityFactory.distributionSet().update(ds.getId()).name("a new name")
.description("a new description").version("a new version").requiredMigrationStep(true));
distributionSetManagement.update(DistributionSetManagement.Update.builder().id(ds.getId()).name("a new name")
.description("a new description").version("a new version").requiredMigrationStep(true).build());
ds = getOrThrow(distributionSetManagement.getWithDetails(ds.getId()));
assertThat(ds.getDescription()).isEqualTo("a new description");
assertThat(ds.getName()).isEqualTo("a new name");
@@ -428,7 +435,8 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
void updateInvalidDistributionSet() {
final DistributionSet distributionSet = testdataFactory.createAndInvalidateDistributionSet();
final DistributionSetUpdate update = entityFactory.distributionSet().update(distributionSet.getId()).name("new_name");
final DistributionSetManagement.Update update =
DistributionSetManagement.Update.builder().id(distributionSet.getId()).name("new_name").build();
assertThatExceptionOfType(InvalidDistributionSetException.class)
.as("Invalid distributionSet should throw an exception").isThrownBy(() -> distributionSetManagement.update(update));
}
@@ -548,22 +556,23 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
@Test
void searchDistributionSetsOnFilters() {
DistributionSetTag dsTagA = distributionSetTagManagement
.create(entityFactory.tag().create().name("DistributionSetTag-A"));
.create(DistributionSetTagManagement.Create.builder().name("DistributionSetTag-A").build());
final DistributionSetTag dsTagB = distributionSetTagManagement
.create(entityFactory.tag().create().name("DistributionSetTag-B"));
.create(DistributionSetTagManagement.Create.builder().name("DistributionSetTag-B").build());
final DistributionSetTag dsTagC = distributionSetTagManagement
.create(entityFactory.tag().create().name("DistributionSetTag-C"));
distributionSetTagManagement.create(entityFactory.tag().create().name("DistributionSetTag-D"));
.create(DistributionSetTagManagement.Create.builder().name("DistributionSetTag-C").build());
distributionSetTagManagement.create(DistributionSetTagManagement.Create.builder().name("DistributionSetTag-D").build());
List<DistributionSet> dsGroup1 = testdataFactory.createDistributionSets("", 5);
List<? extends DistributionSet> dsGroup1 = testdataFactory.createDistributionSets("", 5);
final String dsGroup2Prefix = "test";
List<DistributionSet> dsGroup2 = testdataFactory.createDistributionSets(dsGroup2Prefix, 5);
List<? extends DistributionSet> dsGroup2 = testdataFactory.createDistributionSets(dsGroup2Prefix, 5);
DistributionSet dsDeleted = testdataFactory.createDistributionSet("testDeleted");
final DistributionSet dsInComplete = distributionSetManagement.create(entityFactory.distributionSet().create()
.name("notcomplete").version("1").type(standardDsType.getKey()));
final DistributionSet dsInComplete = distributionSetManagement.create(
DistributionSetManagement.Create.builder()
.name("notcomplete").version("1").type(standardDsType).build());
DistributionSetType newType = distributionSetTypeManagement
.create(entityFactory.distributionSetType().create().key("foo").name("bar").description("test"));
.create(DistributionSetTypeManagement.Create.builder().key("foo").name("bar").description("test").build());
distributionSetTypeManagement.assignMandatorySoftwareModuleTypes(newType.getId(),
singletonList(osType.getId()));
@@ -571,8 +580,11 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
Arrays.asList(appType.getId(), runtimeType.getId()));
final DistributionSet dsNewType = distributionSetManagement.create(
entityFactory.distributionSet().create().name("newtype").version("1").type(newType.getKey()).modules(
dsDeleted.getModules().stream().map(SoftwareModule::getId).toList()));
DistributionSetManagement.Create.builder()
.type(newType)
.name("newtype").version("1")
.modules(new HashSet<>(dsDeleted.getModules()))
.build());
assignDistributionSet(dsDeleted, testdataFactory.createTargets(5));
distributionSetManagement.delete(dsDeleted.getId());
@@ -585,10 +597,10 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
dsGroup2 = assignTag(dsGroup2, dsTagA);
dsTagA = getOrThrow(distributionSetTagRepository.findByNameEquals(dsTagA.getName()));
final List<DistributionSet> allDistributionSets = Stream
final List<? extends DistributionSet> allDistributionSets = Stream
.of(dsGroup1, dsGroup2, Arrays.asList(dsDeleted, dsInComplete, dsNewType)).flatMap(Collection::stream)
.toList();
final List<DistributionSet> dsGroup1WithGroup2 = Stream.of(dsGroup1, dsGroup2).flatMap(Collection::stream)
final List<? extends DistributionSet> dsGroup1WithGroup2 = Stream.of(dsGroup1, dsGroup2).flatMap(Collection::stream)
.toList();
final int sizeOfAllDistributionSets = allDistributionSets.size();
@@ -604,8 +616,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
validateDeletedAndCompleted(dsGroup1WithGroup2, dsNewType, dsDeleted);
validateDeletedAndCompletedAndType(dsGroup1WithGroup2, dsDeleted, newType, dsNewType);
validateDeletedAndCompletedAndTypeAndSearchText(dsGroup2, newType, dsGroup2Prefix);
validateDeletedAndCompletedAndTypeAndSearchText(dsGroup1WithGroup2, dsDeleted, dsInComplete, dsNewType, newType,
":1");
validateDeletedAndCompletedAndTypeAndSearchText(dsGroup1WithGroup2, dsDeleted, dsInComplete, dsNewType, newType, ":1");
validateDeletedAndCompletedAndTypeAndSearchTextAndTag(dsGroup2, dsTagA, dsGroup2Prefix);
}
@@ -729,21 +740,22 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
/**
* Test implicit locks for a DS and skip tags.
*/
@SuppressWarnings("rawtypes")
@Test
void isImplicitLockApplicableForDistributionSet() {
final JpaDistributionSetManagement distributionSetManagement = (JpaDistributionSetManagement) this.distributionSetManagement;
final JpaDistributionSetManagement distributionSetManagement = (JpaDistributionSetManagement) (DistributionSetManagement) this.distributionSetManagement;
final DistributionSet distributionSet = testdataFactory.createDistributionSet("ds-non-skip");
// assert that implicit lock is applicable for non skip tags
assertThat(distributionSetManagement.isImplicitLockApplicable(distributionSet)).isTrue();
assertThat(repositoryProperties.getSkipImplicitLockForTags().size()).isNotZero();
final List<DistributionSetTag> skipTags = distributionSetTagManagement.create(
final List<? extends DistributionSetTag> skipTags = distributionSetTagManagement.create(
repositoryProperties.getSkipImplicitLockForTags().stream()
.map(String::toLowerCase)
// remove same in case-insensitive terms tags
// in of case-insensitive db's it will end up as same names and constraint violation (?)
.distinct()
.map(skipTag -> entityFactory.tag().create().name(skipTag))
.map(skipTag -> (DistributionSetTagManagement.Create)DistributionSetTagManagement.Create.builder().name(skipTag).build())
.toList());
// assert that implicit lock locks for every skip tag
skipTags.forEach(skipTag -> {
@@ -829,7 +841,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
}
/**
* Deletes a DS that is in use by either target assignment or rollout. Expected behaviour is a soft delete on the database, i.e. only marked as
* Deletes a DS that is in use by either target assignment or rollout. Expected behaviour is a soft delete on the database, i.e. only marked as
* deleted, kept as reference but unavailable for future use..
*/
@Test
@@ -874,7 +886,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
testdataFactory.createDistributionSet("test" + i);
}
final List<DistributionSet> foundDs = distributionSetManagement.get(searchIds);
final List<? extends DistributionSet> foundDs = distributionSetManagement.get(searchIds);
assertThat(foundDs).hasSize(3);
@@ -1006,91 +1018,92 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
}
private void createAndUpdateDistributionSetWithInvalidDescription(final DistributionSet set) {
final DistributionSetCreate distributionSetCreate =
entityFactory.distributionSet().create().name("a").version("a").description(randomString(513));
final DistributionSetManagement.Create distributionSetCreate =
DistributionSetManagement.Create.builder().name("a").version("a").description(randomString(513)).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("entity with too long description should not be created")
.isThrownBy(() -> distributionSetManagement.create(distributionSetCreate));
final DistributionSetCreate distributionSetCreate2 =
entityFactory.distributionSet().create().name("a").version("a").description(INVALID_TEXT_HTML);
final DistributionSetManagement.Create distributionSetCreate2 =
DistributionSetManagement.Create.builder().name("a").version("a").description(INVALID_TEXT_HTML).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("entity with invalid description should not be created")
.isThrownBy(() -> distributionSetManagement.create(distributionSetCreate2));
final DistributionSetUpdate distributionSetUpdate =
entityFactory.distributionSet().update(set.getId()).description(randomString(513));
final DistributionSetManagement.Update distributionSetUpdate =
DistributionSetManagement.Update.builder().id(set.getId()).description(randomString(513)).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("entity with too long description should not be updated")
.isThrownBy(() -> distributionSetManagement.update(distributionSetUpdate));
final DistributionSetUpdate distributionSetUpdate2 = entityFactory.distributionSet().update(set.getId()).description(INVALID_TEXT_HTML);
final DistributionSetManagement.Update distributionSetUpdate2 =
DistributionSetManagement.Update.builder().id(set.getId()).description(INVALID_TEXT_HTML).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("entity with invalid characters should not be updated")
.isThrownBy(() -> distributionSetManagement.update(distributionSetUpdate2));
}
private void createAndUpdateDistributionSetWithInvalidName(final DistributionSet set) {
final DistributionSetCreate distributionSetCreate = entityFactory.distributionSet().create()
.version("a").name(randomString(NamedEntity.NAME_MAX_SIZE + 1));
final DistributionSetManagement.Create distributionSetCreate = DistributionSetManagement.Create.builder()
.version("a").name(randomString(NamedEntity.NAME_MAX_SIZE + 1)).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("entity with too long name should not be created")
.isThrownBy(() -> distributionSetManagement.create(distributionSetCreate));
final DistributionSetCreate distributionSetCreate2 = entityFactory.distributionSet().create().version("a").name("");
final DistributionSetManagement.Create distributionSetCreate2 = DistributionSetManagement.Create.builder().version("a").name("").build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("entity with too short name should not be created")
.isThrownBy(() -> distributionSetManagement.create(distributionSetCreate2));
final DistributionSetCreate distributionSetCreate3 = entityFactory.distributionSet().create().version("a").name(INVALID_TEXT_HTML);
final DistributionSetManagement.Create distributionSetCreate3 = DistributionSetManagement.Create.builder().version("a").name(INVALID_TEXT_HTML).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("entity with invalid characters in name should not be created")
.isThrownBy(() -> distributionSetManagement.create(distributionSetCreate3));
final DistributionSetUpdate distributionSetUpdate = entityFactory.distributionSet().update(set.getId())
.name(randomString(NamedEntity.NAME_MAX_SIZE + 1));
final DistributionSetManagement.Update distributionSetUpdate = DistributionSetManagement.Update.builder().id(set.getId())
.name(randomString(NamedEntity.NAME_MAX_SIZE + 1)).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("entity with too long name should not be updated")
.isThrownBy(() -> distributionSetManagement.update(distributionSetUpdate));
final DistributionSetUpdate distributionSetUpdate2 = entityFactory.distributionSet().update(set.getId()).name(INVALID_TEXT_HTML);
final DistributionSetManagement.Update distributionSetUpdate2 = DistributionSetManagement.Update.builder().id(set.getId()).name(INVALID_TEXT_HTML).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("entity with invalid characters should not be updated")
.isThrownBy(() -> distributionSetManagement.update(distributionSetUpdate2));
final DistributionSetUpdate distributionSetUpdate3 = entityFactory.distributionSet().update(set.getId()).name("");
final DistributionSetManagement.Update distributionSetUpdate3 = DistributionSetManagement.Update.builder().id(set.getId()).name("").build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("entity with too short name should not be updated")
.isThrownBy(() -> distributionSetManagement.update(distributionSetUpdate3));
}
private void createAndUpdateDistributionSetWithInvalidVersion(final DistributionSet set) {
final DistributionSetCreate distributionSetCreate = entityFactory.distributionSet().create()
.name("a").version(randomString(NamedVersionedEntity.VERSION_MAX_SIZE + 1));
final DistributionSetManagement.Create distributionSetCreate = DistributionSetManagement.Create.builder()
.name("a").version(randomString(NamedVersionedEntity.VERSION_MAX_SIZE + 1)).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("entity with too long version should not be created")
.isThrownBy(() -> distributionSetManagement.create(distributionSetCreate));
final DistributionSetCreate distributionSetCreate2 = entityFactory.distributionSet().create().name("a").version("");
final DistributionSetManagement.Create distributionSetCreate2 = DistributionSetManagement.Create.builder().name("a").version("").build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("entity with too short version should not be created")
.isThrownBy(() -> distributionSetManagement.create(distributionSetCreate2));
final DistributionSetUpdate distributionSetUpdate = entityFactory.distributionSet().update(set.getId())
.version(randomString(NamedVersionedEntity.VERSION_MAX_SIZE + 1));
final DistributionSetManagement.Update distributionSetUpdate = DistributionSetManagement.Update.builder().id(set.getId())
.version(randomString(NamedVersionedEntity.VERSION_MAX_SIZE + 1)).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("entity with too long version should not be updated")
.isThrownBy(() -> distributionSetManagement.update(distributionSetUpdate));
final DistributionSetUpdate distributionSetUpdate2 = entityFactory.distributionSet().update(set.getId()).version("");
final DistributionSetManagement.Update distributionSetUpdate2 = DistributionSetManagement.Update.builder().id(set.getId()).version("").build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("entity with too short version should not be updated")
.isThrownBy(() -> distributionSetManagement.update(distributionSetUpdate2));
}
private void validateFindAll(final List<DistributionSet> expectedDistributionsets) {
private void validateFindAll(final List<? extends DistributionSet> expectedDistributionSets) {
assertThatFilterContainsOnlyGivenDistributionSets(DistributionSetFilter.builder(), expectedDistributionsets);
assertThatFilterContainsOnlyGivenDistributionSets(DistributionSetFilter.builder(), expectedDistributionSets);
}
private void validateDeleted(final DistributionSet deletedDistributionSet, final int notDeletedSize) {
@@ -1119,51 +1132,44 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
DistributionSetFilter.builder().typeId(standardDsType.getId()), standardDsTypeSize, dsNewType);
}
private void validateSearchText(final List<DistributionSet> allDistributionSets, final String dsNamePrefix) {
final List<DistributionSet> withTestNamePrefix = allDistributionSets.stream()
private void validateSearchText(final List<? extends DistributionSet> allDistributionSets, final String dsNamePrefix) {
final List<? extends DistributionSet> withTestNamePrefix = allDistributionSets.stream()
.filter(ds -> ds.getName().startsWith(dsNamePrefix)).toList();
assertThatFilterContainsOnlyGivenDistributionSets(DistributionSetFilter.builder().searchText(dsNamePrefix),
withTestNamePrefix);
final List<DistributionSet> withTestNameExact = withTestNamePrefix.stream()
final List<? extends DistributionSet> withTestNameExact = withTestNamePrefix.stream()
.filter(ds -> ds.getName().equals(dsNamePrefix)).toList();
assertThatFilterContainsOnlyGivenDistributionSets(
DistributionSetFilter.builder().searchText(dsNamePrefix + ":"), withTestNameExact);
final List<DistributionSet> withTestNameExactAndVersionPrefix = withTestNameExact.stream()
final List<? extends DistributionSet> withTestNameExactAndVersionPrefix = withTestNameExact.stream()
.filter(ds -> ds.getVersion().startsWith("1")).toList();
assertThatFilterContainsOnlyGivenDistributionSets(
DistributionSetFilter.builder().searchText(dsNamePrefix + ":1"),
withTestNameExactAndVersionPrefix);
final List<DistributionSet> dsWithExactNameAndVersion = withTestNameExactAndVersionPrefix.stream()
final List<? extends DistributionSet> dsWithExactNameAndVersion = withTestNameExactAndVersionPrefix.stream()
.filter(ds -> ds.getVersion().equals("1.0.0")).toList();
assertThat(dsWithExactNameAndVersion).hasSize(1);
assertThatFilterContainsOnlyGivenDistributionSets(
DistributionSetFilter.builder().searchText(dsNamePrefix + ":1.0.0"), dsWithExactNameAndVersion);
final List<DistributionSet> withVersionPrefix = allDistributionSets.stream()
final List<? extends DistributionSet> withVersionPrefix = allDistributionSets.stream()
.filter(ds -> ds.getVersion().startsWith("1.0.")).toList();
assertThatFilterContainsOnlyGivenDistributionSets(DistributionSetFilter.builder().searchText(":1.0."),
withVersionPrefix);
final List<DistributionSet> withVersionExact = withVersionPrefix.stream()
final List<? extends DistributionSet> withVersionExact = withVersionPrefix.stream()
.filter(ds -> ds.getVersion().equals("1.0.0")).toList();
assertThatFilterContainsOnlyGivenDistributionSets(DistributionSetFilter.builder().searchText(":1.0.0"),
withVersionExact);
assertThatFilterContainsOnlyGivenDistributionSets(DistributionSetFilter.builder().searchText(":"),
allDistributionSets);
assertThatFilterContainsOnlyGivenDistributionSets(DistributionSetFilter.builder().searchText(" : "),
allDistributionSets);
assertThatFilterContainsOnlyGivenDistributionSets(DistributionSetFilter.builder().searchText(":1.0.0"), withVersionExact);
assertThatFilterContainsOnlyGivenDistributionSets(DistributionSetFilter.builder().searchText(":"), allDistributionSets);
assertThatFilterContainsOnlyGivenDistributionSets(DistributionSetFilter.builder().searchText(" : "), allDistributionSets);
}
private void validateTags(final DistributionSetTag dsTagA, final DistributionSetTag dsTagB,
final DistributionSetTag dsTagC, final List<DistributionSet> dsWithTagA,
final List<DistributionSet> dsWithTagB) {
final DistributionSetTag dsTagC, final List<? extends DistributionSet> dsWithTagA,
final List<? extends DistributionSet> dsWithTagB) {
assertThatFilterContainsOnlyGivenDistributionSets(
DistributionSetFilter.builder().tagNames(singletonList(dsTagA.getName())), dsWithTagA);
@@ -1182,7 +1188,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
DistributionSetFilter.builder().tagNames(singletonList(dsTagC.getName())));
}
private void validateDeletedAndCompleted(final List<DistributionSet> completedStandardType,
private void validateDeletedAndCompleted(final List<? extends DistributionSet> completedStandardType,
final DistributionSet dsNewType, final DistributionSet dsDeleted) {
final List<DistributionSet> completedNotDeleted = new ArrayList<>(completedStandardType);
@@ -1199,7 +1205,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
DistributionSetFilter.builder().isComplete(Boolean.FALSE).isDeleted(Boolean.TRUE));
}
private void validateDeletedAndCompletedAndType(final List<DistributionSet> deletedAndCompletedAndStandardType,
private void validateDeletedAndCompletedAndType(final List<? extends DistributionSet> deletedAndCompletedAndStandardType,
final DistributionSet dsDeleted, final DistributionSetType newType, final DistributionSet dsNewType) {
assertThatFilterContainsOnlyGivenDistributionSets(DistributionSetFilter.builder().isDeleted(Boolean.FALSE)
.isComplete(Boolean.TRUE).typeId(standardDsType.getId()), deletedAndCompletedAndStandardType);
@@ -1213,9 +1219,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
}
private void validateDeletedAndCompletedAndTypeAndSearchText(
final List<DistributionSet> completedAndStandardTypeAndSearchText, final DistributionSetType newType,
final String text) {
final List<? extends DistributionSet> completedAndStandardTypeAndSearchText, final DistributionSetType newType, final String text) {
assertThatFilterContainsOnlyGivenDistributionSets(DistributionSetFilter.builder().isDeleted(Boolean.FALSE)
.isComplete(Boolean.TRUE).typeId(standardDsType.getId()).searchText(text),
completedAndStandardTypeAndSearchText);
@@ -1232,7 +1236,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
}
private void validateDeletedAndCompletedAndTypeAndSearchText(
final List<DistributionSet> completedAndNotDeletedStandardTypeAndFilterString,
final List<? extends DistributionSet> completedAndNotDeletedStandardTypeAndFilterString,
final DistributionSet dsDeleted, final DistributionSet dsInComplete, final DistributionSet dsNewType,
final DistributionSetType newType, final String filterString) {
@@ -1263,9 +1267,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
}
private void validateDeletedAndCompletedAndTypeAndSearchTextAndTag(
final List<DistributionSet> completedAndStandartTypeAndSearchTextAndTagA, final DistributionSetTag dsTagA,
final String text) {
final List<? extends DistributionSet> completedAndStandartTypeAndSearchTextAndTagA, final DistributionSetTag dsTagA, final String text) {
assertThatFilterContainsOnlyGivenDistributionSets(
DistributionSetFilter.builder().isComplete(Boolean.TRUE).typeId(standardDsType.getId())
.searchText(text).tagNames(singletonList(dsTagA.getName())),
@@ -1282,9 +1284,9 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
}
private void assertThatFilterContainsOnlyGivenDistributionSets(final DistributionSetFilterBuilder filterBuilder,
final List<DistributionSet> distributionSets) {
final List<? extends DistributionSet> distributionSets) {
final int expectedDsSize = distributionSets.size();
assertThat(distributionSetManagement.findByDistributionSetFilter(filterBuilder.build(), PAGE).getContent())
assertThat(((DistributionSetManagement)distributionSetManagement).findByDistributionSetFilter(filterBuilder.build(), PAGE).getContent())
.hasSize(expectedDsSize).containsOnly(distributionSets.toArray(new DistributionSet[expectedDsSize]));
}
@@ -1295,7 +1297,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
private void assertThatFilterHasSizeAndDoesNotContainDistributionSet(
final DistributionSetFilterBuilder filterBuilder, final int size, final DistributionSet ds) {
assertThat(distributionSetManagement.findByDistributionSetFilter(filterBuilder.build(), PAGE).getContent())
assertThat(((DistributionSetManagement)distributionSetManagement).findByDistributionSetFilter(filterBuilder.build(), PAGE).getContent())
.hasSize(size).doesNotContain(ds);
}

View File

@@ -13,9 +13,8 @@ import java.util.List;
import java.util.Random;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.RepositoryManagement;
import org.eclipse.hawkbit.repository.builder.TagCreate;
import org.eclipse.hawkbit.repository.builder.TagUpdate;
import org.eclipse.hawkbit.repository.jpa.AbstractRepositoryManagementSecurityTest;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.junit.jupiter.api.Test;
@@ -25,21 +24,22 @@ import org.springframework.data.domain.Pageable;
* Feature: SecurityTests - DistributionSetTagManagement<br/>
* Story: SecurityTests DistributionSetTagManagement
*/
class DistributionSetTagManagementSecurityTest extends AbstractRepositoryManagementSecurityTest<DistributionSetTag, TagCreate, TagUpdate> {
class DistributionSetTagManagementSecurityTest
extends AbstractRepositoryManagementSecurityTest<DistributionSetTag, DistributionSetTagManagement.Create, DistributionSetTagManagement.Update> {
@Override
protected RepositoryManagement<DistributionSetTag, TagCreate, TagUpdate> getRepositoryManagement() {
protected RepositoryManagement getRepositoryManagement() {
return distributionSetTagManagement;
}
@Override
protected TagCreate getCreateObject() {
return entityFactory.tag().create().name(String.format("tag-%d", new Random().nextInt()));
protected DistributionSetTagManagement.Create getCreateObject() {
return DistributionSetTagManagement.Create.builder().name(String.format("tag-%d", new Random().nextInt())).build();
}
@Override
protected TagUpdate getUpdateObject() {
return entityFactory.tag().update(1L).name("tag");
protected DistributionSetTagManagement.Update getUpdateObject() {
return DistributionSetTagManagement.Update.builder().id(1L).name("tag").build();
}
/**

View File

@@ -22,8 +22,6 @@ import java.util.Random;
import java.util.stream.Stream;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.builder.TagCreate;
import org.eclipse.hawkbit.repository.builder.TagUpdate;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTagUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagUpdatedEvent;
@@ -60,7 +58,7 @@ class DistributionSetTagManagementTest extends AbstractJpaIntegrationTest {
}
/**
* Verifies that management queries react as specified on calls for non existing entities by means of throwing
* Verifies that management queries react as specified on calls for non existing entities by means of throwing
* EntityNotFoundException.
*/
@Test
@@ -69,10 +67,9 @@ class DistributionSetTagManagementTest extends AbstractJpaIntegrationTest {
@Expect(type = TargetTagUpdatedEvent.class, count = 0) })
void entityQueriesReferringToNotExistingEntitiesThrowsException() {
verifyThrownExceptionBy(() -> distributionSetTagManagement.delete(NOT_EXIST_ID), "DistributionSetTag");
verifyThrownExceptionBy(() -> distributionSetTagManagement.findByDistributionSet(NOT_EXIST_IDL, PAGE),
"DistributionSet");
verifyThrownExceptionBy(() -> distributionSetTagManagement.update(entityFactory.tag().update(NOT_EXIST_IDL)),
"DistributionSetTag");
verifyThrownExceptionBy(() -> distributionSetTagManagement.findByDistributionSet(NOT_EXIST_IDL, PAGE), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetTagManagement.update(
DistributionSetTagManagement.Update.builder().id(NOT_EXIST_IDL).build()), "DistributionSetTag");
}
/**
@@ -88,11 +85,11 @@ class DistributionSetTagManagementTest extends AbstractJpaIntegrationTest {
final Collection<DistributionSet> dsBCs = testdataFactory.createDistributionSets("DS-BC", 13);
final Collection<DistributionSet> dsABCs = testdataFactory.createDistributionSets("DS-ABC", 9);
final DistributionSetTag tagA = distributionSetTagManagement.create(entityFactory.tag().create().name("A"));
final DistributionSetTag tagB = distributionSetTagManagement.create(entityFactory.tag().create().name("B"));
final DistributionSetTag tagC = distributionSetTagManagement.create(entityFactory.tag().create().name("C"));
final DistributionSetTag tagX = distributionSetTagManagement.create(entityFactory.tag().create().name("X"));
final DistributionSetTag tagY = distributionSetTagManagement.create(entityFactory.tag().create().name("Y"));
final DistributionSetTag tagA = distributionSetTagManagement.create(DistributionSetTagManagement.Create.builder().name("A").build());
final DistributionSetTag tagB = distributionSetTagManagement.create(DistributionSetTagManagement.Create.builder().name("B").build());
final DistributionSetTag tagC = distributionSetTagManagement.create(DistributionSetTagManagement.Create.builder().name("C").build());
final DistributionSetTag tagX = distributionSetTagManagement.create(DistributionSetTagManagement.Create.builder().name("X").build());
final DistributionSetTag tagY = distributionSetTagManagement.create(DistributionSetTagManagement.Create.builder().name("Y").build());
assignTag(dsAs, tagA);
assignTag(dsBs, tagB);
@@ -148,13 +145,13 @@ class DistributionSetTagManagementTest extends AbstractJpaIntegrationTest {
final Collection<DistributionSet> groupB = testdataFactory.createDistributionSets("unassigned", 20);
final DistributionSetTag tag = distributionSetTagManagement
.create(entityFactory.tag().create().name("tag1").description("tagdesc1"));
.create(DistributionSetTagManagement.Create.builder().name("tag1").description("tagdesc1").build());
// toggle A only -> A is now assigned
List<DistributionSet> result = assignTag(groupA, tag);
List<? extends DistributionSet> result = assignTag(groupA, tag);
assertThat(result)
.hasSize(20)
.containsAll(distributionSetManagement.get(groupA.stream().map(DistributionSet::getId).toList()));
.containsAll((Collection) distributionSetManagement.get(groupA.stream().map(DistributionSet::getId).toList()));
assertThat(
distributionSetManagement.findByTag(tag.getId(), Pageable.unpaged()).getContent().stream()
.map(DistributionSet::getId)
@@ -165,7 +162,7 @@ class DistributionSetTagManagementTest extends AbstractJpaIntegrationTest {
final Collection<DistributionSet> groupAB = concat(groupA, groupB);
// toggle A+B -> A is still assigned and B is assigned as well
result = assignTag(groupAB, tag);
assertThat(result)
assertThat((List) result)
.hasSize(40)
.containsAll(distributionSetManagement.get(groupAB.stream().map(DistributionSet::getId).toList()));
assertThat(
@@ -177,7 +174,7 @@ class DistributionSetTagManagementTest extends AbstractJpaIntegrationTest {
result = unassignTag(concat(groupA, groupB), tag);
assertThat(result)
.hasSize(40)
.containsAll(distributionSetManagement.get(concat(groupB, groupA).stream().map(DistributionSet::getId).toList()));
.containsAll((List) distributionSetManagement.get(concat(groupB, groupA).stream().map(DistributionSet::getId).toList()));
assertThat(distributionSetManagement.findByTag(tag.getId(), Pageable.unpaged()).getContent()).isEmpty();
}
@@ -187,7 +184,8 @@ class DistributionSetTagManagementTest extends AbstractJpaIntegrationTest {
@Test
void failOnMissingDs() {
final Collection<Long> group = testdataFactory.createDistributionSets(5).stream().map(DistributionSet::getId).toList();
final DistributionSetTag tag = distributionSetTagManagement.create(entityFactory.tag().create().name("tag1").description("tagdesc1"));
final DistributionSetTag tag = distributionSetTagManagement.create(
DistributionSetTagManagement.Create.builder().name("tag1").description("tagdesc1").build());
final List<Long> missing = new ArrayList<>();
for (int i = 0; i < 5; i++) {
while (true) {
@@ -217,7 +215,7 @@ class DistributionSetTagManagementTest extends AbstractJpaIntegrationTest {
@Test
void createDistributionSetTag() {
final Tag tag = distributionSetTagManagement
.create(entityFactory.tag().create().name("kai1").description("kai2").colour("colour"));
.create(DistributionSetTagManagement.Create.builder().name("kai1").description("kai2").colour("colour").build());
assertThat(distributionSetTagRepository.findByNameEquals("kai1").get().getDescription()).as("wrong tag found")
.isEqualTo("kai2");
@@ -261,7 +259,7 @@ class DistributionSetTagManagementTest extends AbstractJpaIntegrationTest {
*/
@Test
void failedDuplicateDsTagNameException() {
final TagCreate tag = entityFactory.tag().create().name("A");
final DistributionSetTagManagement.Create tag = DistributionSetTagManagement.Create.builder().name("A").build();
distributionSetTagManagement.create(tag);
assertThatExceptionOfType(EntityAlreadyExistsException.class).as("should not have worked as tag already exists")
@@ -273,10 +271,10 @@ class DistributionSetTagManagementTest extends AbstractJpaIntegrationTest {
*/
@Test
void failedDuplicateDsTagNameExceptionAfterUpdate() {
distributionSetTagManagement.create(entityFactory.tag().create().name("A"));
final DistributionSetTag tag = distributionSetTagManagement.create(entityFactory.tag().create().name("B"));
distributionSetTagManagement.create(DistributionSetTagManagement.Create.builder().name("A").build());
final DistributionSetTag tag = distributionSetTagManagement.create(DistributionSetTagManagement.Create.builder().name("B").build());
final TagUpdate tagUpdate = entityFactory.tag().update(tag.getId()).name("A");
final DistributionSetTagManagement.Update tagUpdate = DistributionSetTagManagement.Update.builder().id(tag.getId()).name("A").build();
assertThatExceptionOfType(EntityAlreadyExistsException.class).as("should not have worked as tag already exists")
.isThrownBy(() -> distributionSetTagManagement.update(tagUpdate));
}
@@ -291,7 +289,7 @@ class DistributionSetTagManagementTest extends AbstractJpaIntegrationTest {
// change data
final DistributionSetTag savedAssigned = tags.iterator().next();
// persist
distributionSetTagManagement.update(entityFactory.tag().update(savedAssigned.getId()).name("test123"));
distributionSetTagManagement.update(DistributionSetTagManagement.Update.builder().id(savedAssigned.getId()).name("test123").build());
// check data
assertThat(distributionSetTagManagement.findAll(PAGE).getContent()).as("Wrong size of ds tags")
.hasSize(tags.size());
@@ -337,7 +335,7 @@ class DistributionSetTagManagementTest extends AbstractJpaIntegrationTest {
tags.forEach(tag -> assignTag(sets, tag));
return distributionSetTagManagement.findAll(PAGE).getContent();
return distributionSetTagManagement.findAll(PAGE).getContent().stream().map(DistributionSetTag.class::cast).toList();
}
private DistributionSetFilter.DistributionSetFilterBuilder getDistributionSetFilterBuilder() {

View File

@@ -13,9 +13,8 @@ import java.util.List;
import java.util.Random;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.RepositoryManagement;
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeCreate;
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeUpdate;
import org.eclipse.hawkbit.repository.jpa.AbstractRepositoryManagementSecurityTest;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.junit.jupiter.api.Test;
@@ -25,21 +24,21 @@ import org.junit.jupiter.api.Test;
* Story: SecurityTests DistributionSetTypeManagement
*/
class DistributionSetTypeManagementSecurityTest
extends AbstractRepositoryManagementSecurityTest<DistributionSetType, DistributionSetTypeCreate, DistributionSetTypeUpdate> {
extends AbstractRepositoryManagementSecurityTest<DistributionSetType, DistributionSetTypeManagement.Create, DistributionSetTypeManagement.Update> {
@Override
protected RepositoryManagement<DistributionSetType, DistributionSetTypeCreate, DistributionSetTypeUpdate> getRepositoryManagement() {
protected RepositoryManagement getRepositoryManagement() {
return distributionSetTypeManagement;
}
@Override
protected DistributionSetTypeCreate getCreateObject() {
return entityFactory.distributionSetType().create().key(String.format("key-%d", new Random().nextInt())).name(String.format("name-%d", new Random().nextInt()));
protected DistributionSetTypeManagement.Create getCreateObject() {
return DistributionSetTypeManagement.Create.builder().key(String.format("key-%d", new Random().nextInt())).name(String.format("name-%d", new Random().nextInt())).build();
}
@Override
protected DistributionSetTypeUpdate getUpdateObject() {
return entityFactory.distributionSetType().update(1L).description("description");
protected DistributionSetTypeManagement.Update getUpdateObject() {
return DistributionSetTypeManagement.Update.builder().id(1L).description("description").build();
}
/**
@@ -83,4 +82,4 @@ class DistributionSetTypeManagementSecurityTest
void unassignSoftwareModuleTypePermissionsCheck() {
assertPermissions(() -> distributionSetTypeManagement.unassignSoftwareModuleType(1L, 1L), List.of(SpPermission.UPDATE_REPOSITORY));
}
}
}

View File

@@ -22,9 +22,8 @@ import java.util.Set;
import jakarta.validation.ConstraintViolationException;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.builder.DistributionSetCreate;
import org.eclipse.hawkbit.repository.builder.DistributionSetUpdate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeCreate;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTypeCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent;
@@ -90,8 +89,8 @@ class DistributionSetTypeManagementTest extends AbstractJpaIntegrationTest {
verifyThrownExceptionBy(() -> distributionSetTypeManagement.delete(NOT_EXIST_IDL), "DistributionSetType");
verifyThrownExceptionBy(
() -> distributionSetTypeManagement.update(entityFactory.distributionSetType().update(NOT_EXIST_IDL)),
"DistributionSet");
() -> distributionSetTypeManagement.update(DistributionSetTypeManagement.Update.builder().id(NOT_EXIST_IDL).build()),
"DistributionSetType");
}
/**
@@ -116,7 +115,7 @@ class DistributionSetTypeManagementTest extends AbstractJpaIntegrationTest {
@Test
void updateUnassignedDistributionSetTypeModules() {
final DistributionSetType updatableType = distributionSetTypeManagement
.create(entityFactory.distributionSetType().create().key("updatableType").name("to be deleted"));
.create(DistributionSetTypeManagement.Create.builder().key("updatableType").name("to be deleted").build());
assertThat(distributionSetTypeManagement.findByKey("updatableType").get().getMandatoryModuleTypes()).isEmpty();
// add OS
@@ -146,14 +145,14 @@ class DistributionSetTypeManagementTest extends AbstractJpaIntegrationTest {
// create software module types
final List<Long> moduleTypeIds = new ArrayList<>();
for (int i = 0; i < quota + 1; ++i) {
final SoftwareModuleTypeCreate smCreate = entityFactory.softwareModuleType().create().name("smType_" + i)
.description("smType_" + i).maxAssignments(1).colour("blue").key("smType_" + i);
final SoftwareModuleTypeManagement.Create smCreate = SoftwareModuleTypeManagement.Create.builder().name("smType_" + i)
.description("smType_" + i).maxAssignments(1).colour("blue").key("smType_" + i).build();
moduleTypeIds.add(softwareModuleTypeManagement.create(smCreate).getId());
}
// assign all types at once
final DistributionSetType dsType1 = distributionSetTypeManagement
.create(entityFactory.distributionSetType().create().key("dst1").name("dst1"));
.create(DistributionSetTypeManagement.Create.builder().key("dst1").name("dst1").build());
final Long dsType1Id = dsType1.getId();
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> distributionSetTypeManagement.assignMandatorySoftwareModuleTypes(dsType1Id, moduleTypeIds));
@@ -162,7 +161,7 @@ class DistributionSetTypeManagementTest extends AbstractJpaIntegrationTest {
// assign as many mandatory modules as possible
final DistributionSetType dsType2 = distributionSetTypeManagement
.create(entityFactory.distributionSetType().create().key("dst2").name("dst2"));
.create(DistributionSetTypeManagement.Create.builder().key("dst2").name("dst2").build());
final Long dsType2Id = dsType2.getId();
distributionSetTypeManagement.assignMandatorySoftwareModuleTypes(dsType2Id,
moduleTypeIds.subList(0, quota));
@@ -175,7 +174,7 @@ class DistributionSetTypeManagementTest extends AbstractJpaIntegrationTest {
// assign as many optional modules as possible
final DistributionSetType dsType3 = distributionSetTypeManagement
.create(entityFactory.distributionSetType().create().key("dst3").name("dst3"));
.create(DistributionSetTypeManagement.Create.builder().key("dst3").name("dst3").build());
final Long dsType3Id = dsType3.getId();
distributionSetTypeManagement.assignOptionalSoftwareModuleTypes(dsType3Id, moduleTypeIds.subList(0, quota));
assertThat(distributionSetTypeManagement.get(dsType3Id)).isNotEmpty();
@@ -194,11 +193,10 @@ class DistributionSetTypeManagementTest extends AbstractJpaIntegrationTest {
final DistributionSetType nonUpdatableType = createDistributionSetTypeUsedByDs();
distributionSetTypeManagement.update(
entityFactory.distributionSetType().update(nonUpdatableType.getId()).description("a new description"));
DistributionSetTypeManagement.Update.builder().id(nonUpdatableType.getId()).description("a new description").build());
assertThat(distributionSetTypeManagement.findByKey("updatableType").get().getDescription())
.isEqualTo("a new description");
assertThat(distributionSetTypeManagement.findByKey("updatableType").get().getColour()).isEqualTo("test123");
assertThat(distributionSetTypeManagement.findByKey("updatableType").orElseThrow().getDescription()).isEqualTo("a new description");
assertThat(distributionSetTypeManagement.findByKey("updatableType").orElseThrow().getColour()).isEqualTo("test123");
}
/**
@@ -219,13 +217,13 @@ class DistributionSetTypeManagementTest extends AbstractJpaIntegrationTest {
@Test
void removeModuleToAssignedDistributionSetTypeFails() {
DistributionSetType nonUpdatableType = distributionSetTypeManagement
.create(entityFactory.distributionSetType().create().key("updatableType").name("to be deleted"));
.create(DistributionSetTypeManagement.Create.builder().key("updatableType").name("to be deleted").build());
assertThat(distributionSetTypeManagement.findByKey("updatableType").get().getMandatoryModuleTypes()).isEmpty();
final Long osTypeId = osType.getId();
nonUpdatableType = distributionSetTypeManagement.assignMandatorySoftwareModuleTypes(nonUpdatableType.getId(), Set.of(osTypeId));
distributionSetManagement.create(
entityFactory.distributionSet().create().name("newtypesoft").version("1").type(nonUpdatableType.getKey()));
DistributionSetManagement.Create.builder().type(nonUpdatableType).name("newtypesoft").version("1").build());
final Long typeId = nonUpdatableType.getId();
assertThatThrownBy(() -> distributionSetTypeManagement.unassignSoftwareModuleType(typeId, osTypeId))
@@ -238,7 +236,7 @@ class DistributionSetTypeManagementTest extends AbstractJpaIntegrationTest {
@Test
void deleteUnassignedDistributionSetType() {
final JpaDistributionSetType hardDelete = (JpaDistributionSetType) distributionSetTypeManagement
.create(entityFactory.distributionSetType().create().key("delete").name("to be deleted"));
.create(DistributionSetTypeManagement.Create.builder().key("delete").name("to be deleted").build());
assertThat(distributionSetTypeRepository.findAll()).contains(hardDelete);
distributionSetTypeManagement.delete(hardDelete.getId());
@@ -253,16 +251,16 @@ class DistributionSetTypeManagementTest extends AbstractJpaIntegrationTest {
void deleteAssignedDistributionSetType() {
final int existing = (int) distributionSetTypeManagement.count();
final JpaDistributionSetType toBeDeleted = (JpaDistributionSetType) distributionSetTypeManagement
.create(entityFactory.distributionSetType().create().key("softdeleted").name("to be deleted"));
.create(DistributionSetTypeManagement.Create.builder().key("softdeleted").name("to be deleted").build());
assertThat(distributionSetTypeRepository.findAll()).contains(toBeDeleted);
distributionSetManagement.create(
entityFactory.distributionSet().create().name("softdeleted").version("1").type(toBeDeleted.getKey()));
DistributionSetManagement.Create.builder().type(toBeDeleted).name("softdeleted").version("1").build());
distributionSetTypeManagement.delete(toBeDeleted.getId());
final Optional<DistributionSetType> softdeleted = distributionSetTypeManagement.findByKey("softdeleted");
assertThat(softdeleted).isPresent();
assertThat(softdeleted.get().isDeleted()).isTrue();
final Optional<? extends DistributionSetType> softDeleted = distributionSetTypeManagement.findByKey("softdeleted");
assertThat(softDeleted).isPresent();
assertThat(softDeleted.get().isDeleted()).isTrue();
assertThat(distributionSetTypeManagement.findAll(PAGE)).hasSize(existing);
assertThat(distributionSetTypeManagement.findByRsql("name==*", PAGE)).hasSize(existing);
assertThat(distributionSetTypeManagement.count()).isEqualTo(existing);
@@ -273,9 +271,8 @@ class DistributionSetTypeManagementTest extends AbstractJpaIntegrationTest {
*/
@Test
void shouldFailWhenDistributionSetHasNoSoftwareModulesAssigned() {
final JpaDistributionSetType jpaDistributionSetType = (JpaDistributionSetType) distributionSetTypeManagement
.create(entityFactory.distributionSetType().create().key("newType").name("new Type"));
.create(DistributionSetTypeManagement.Create.builder().key("newType").name("new Type").build());
final List<SoftwareModule> softwareModules = new ArrayList<>();
@@ -286,103 +283,113 @@ class DistributionSetTypeManagementTest extends AbstractJpaIntegrationTest {
}
private void createAndUpdateDistributionSetWithInvalidDescription(final DistributionSet set) {
final DistributionSetCreate distributionSetCreate = entityFactory.distributionSet().create()
.name("a").version("a").description(randomString(513));
final DistributionSetManagement.Create distributionSetCreate = DistributionSetManagement.Create.builder()
.name("a").version("a").description(randomString(513)).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set with too long description should not be created")
.isThrownBy(() -> distributionSetManagement.create(distributionSetCreate));
final DistributionSetCreate distributionSetCreate2 = entityFactory.distributionSet().create()
.name("a").version("a").description(INVALID_TEXT_HTML);
final DistributionSetManagement.Create distributionSetCreate2 = DistributionSetManagement.Create.builder()
.name("a").version("a").description(INVALID_TEXT_HTML).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set invalid description text should not be created")
.isThrownBy(() -> distributionSetManagement.create(distributionSetCreate2));
final DistributionSetUpdate distributionSetUpdate = entityFactory.distributionSet().update(set.getId())
.description(randomString(513));
final DistributionSetManagement.Update distributionSetUpdate = DistributionSetManagement.Update.builder().id(set.getId())
.description(randomString(513)).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set with too long description should not be updated")
.isThrownBy(() -> distributionSetManagement.update(distributionSetUpdate));
final DistributionSetUpdate distributionSetUpdate2 = entityFactory.distributionSet().update(set.getId()).description(INVALID_TEXT_HTML);
final DistributionSetManagement.Update distributionSetUpdate2 = DistributionSetManagement.Update.builder().id(set.getId()).description(INVALID_TEXT_HTML).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set with invalid description should not be updated")
.isThrownBy(() -> distributionSetManagement.update(distributionSetUpdate2));
}
private void createAndUpdateDistributionSetWithInvalidName(final DistributionSet set) {
final DistributionSetCreate distributionSetCreate = entityFactory.distributionSet().create()
.version("a").name(randomString(NamedEntity.NAME_MAX_SIZE + 1));
final DistributionSetManagement.Create distributionSetCreate = DistributionSetManagement.Create.builder()
.version("a").name(randomString(NamedEntity.NAME_MAX_SIZE + 1)).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set with too long name should not be created")
.isThrownBy(() -> distributionSetManagement.create(distributionSetCreate));
final DistributionSetCreate distributionSetCreate2 = entityFactory.distributionSet().create().version("a").name(INVALID_TEXT_HTML);
final DistributionSetManagement.Create distributionSetCreate2 =
DistributionSetManagement.Create.builder().version("a").name(INVALID_TEXT_HTML).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set with invalid name should not be created")
.isThrownBy(() -> distributionSetManagement.create(distributionSetCreate2));
final DistributionSetCreate distributionSetCreate3 = entityFactory.distributionSet().create().version("a").name("");
final DistributionSetManagement.Create distributionSetCreate3 =
DistributionSetManagement.Create.builder().version("a").name("").build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set with too short name should not be created")
.isThrownBy(() -> distributionSetManagement.create(distributionSetCreate3));
final DistributionSetCreate distributionSetCreate4 = entityFactory.distributionSet().create().version("a").name(null);
final DistributionSetManagement.Create distributionSetCreate4 =
DistributionSetManagement.Create.builder().version("a").name(null).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set with null name should not be created")
.isThrownBy(() -> distributionSetManagement.create(distributionSetCreate4));
final DistributionSetUpdate distributionSetUpdate = entityFactory.distributionSet().update(set.getId())
.name(randomString(NamedEntity.NAME_MAX_SIZE + 1));
final DistributionSetManagement.Update distributionSetUpdate = DistributionSetManagement.Update.builder().id(set.getId())
.name(randomString(NamedEntity.NAME_MAX_SIZE + 1)).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set with too long name should not be updated")
.isThrownBy(() -> distributionSetManagement.update(distributionSetUpdate));
final DistributionSetUpdate distributionSetUpdate2 = entityFactory.distributionSet().update(set.getId()).name(INVALID_TEXT_HTML);
final DistributionSetManagement.Update distributionSetUpdate2 =
DistributionSetManagement.Update.builder().id(set.getId()).name(INVALID_TEXT_HTML).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set with invalid name should not be updated")
.isThrownBy(() -> distributionSetManagement.update(distributionSetUpdate2));
final DistributionSetUpdate distributionSetUpdate3 = entityFactory.distributionSet().update(set.getId()).name("");
final DistributionSetManagement.Update distributionSetUpdate3 =
DistributionSetManagement.Update.builder().id(set.getId()).name("").build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set with too short name should not be updated")
.isThrownBy(() -> distributionSetManagement.update(distributionSetUpdate3));
}
private void createAndUpdateDistributionSetWithInvalidVersion(final DistributionSet set) {
final DistributionSetCreate distributionSetCreate = entityFactory.distributionSet().create()
.name("a").version(randomString(NamedVersionedEntity.VERSION_MAX_SIZE + 1));
final DistributionSetManagement.Create distributionSetCreate = DistributionSetManagement.Create.builder()
.name("a").version(randomString(NamedVersionedEntity.VERSION_MAX_SIZE + 1)).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set with too long version should not be created")
.isThrownBy(() -> distributionSetManagement.create(distributionSetCreate));
final DistributionSetCreate distributionSetCreate2 = entityFactory.distributionSet().create().name("a").version(INVALID_TEXT_HTML);
final DistributionSetManagement.Create distributionSetCreate2 =
DistributionSetManagement.Create.builder().name("a").version(INVALID_TEXT_HTML).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set with invalid version should not be created")
.isThrownBy(() -> distributionSetManagement.create(distributionSetCreate2));
final DistributionSetCreate distributionSetCreate3 = entityFactory.distributionSet().create().name("a").version("");
final DistributionSetManagement.Create distributionSetCreate3 =
DistributionSetManagement.Create.builder().name("a").version("").build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set with too short version should not be created")
.isThrownBy(() -> distributionSetManagement.create(distributionSetCreate3));
final DistributionSetCreate distributionSetCreate4 = entityFactory.distributionSet().create().name("a").version(null);
final DistributionSetManagement.Create distributionSetCreate4 =
DistributionSetManagement.Create.builder().name("a").version(null).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set with null version should not be created")
.isThrownBy(() -> distributionSetManagement.create(distributionSetCreate4));
final DistributionSetUpdate distributionSetUpdate = entityFactory.distributionSet().update(set.getId())
.version(randomString(NamedVersionedEntity.VERSION_MAX_SIZE + 1));
final DistributionSetManagement.Update distributionSetUpdate = DistributionSetManagement.Update.builder().id(set.getId())
.version(randomString(NamedVersionedEntity.VERSION_MAX_SIZE + 1)).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set with too long version should not be updated")
.isThrownBy(() -> distributionSetManagement.update(distributionSetUpdate));
final DistributionSetUpdate distributionSetUpdate2 = entityFactory.distributionSet().update(set.getId()).version(INVALID_TEXT_HTML);
final DistributionSetManagement.Update distributionSetUpdate2 =
DistributionSetManagement.Update.builder().id(set.getId()).version(INVALID_TEXT_HTML).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set with invalid version should not be updated")
.isThrownBy(() -> distributionSetManagement.update(distributionSetUpdate2));
final DistributionSetUpdate distributionSetUpdate3 = entityFactory.distributionSet().update(set.getId()).version("");
final DistributionSetManagement.Update distributionSetUpdate3 =
DistributionSetManagement.Update.builder().id(set.getId()).version("").build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("set with too short version should not be updated")
.isThrownBy(() -> distributionSetManagement.update(distributionSetUpdate3));
@@ -390,10 +397,12 @@ class DistributionSetTypeManagementTest extends AbstractJpaIntegrationTest {
private DistributionSetType createDistributionSetTypeUsedByDs() {
final DistributionSetType nonUpdatableType = distributionSetTypeManagement.create(
entityFactory.distributionSetType().create().key("updatableType").name("to be deleted").colour("test123"));
assertThat(distributionSetTypeManagement.findByKey("updatableType").get().getMandatoryModuleTypes()).isEmpty();
distributionSetManagement.create(entityFactory.distributionSet().create()
.name("newtypesoft").version("1").type(nonUpdatableType.getKey()));
DistributionSetTypeManagement.Create.builder().key("updatableType").name("to be deleted").colour("test123").build());
assertThat(distributionSetTypeManagement.findByKey("updatableType").orElseThrow().getMandatoryModuleTypes()).isEmpty();
distributionSetManagement.create(DistributionSetManagement.Create.builder()
.type(nonUpdatableType)
.name("newtypesoft").version("1")
.build());
return nonUpdatableType;
}
}

View File

@@ -15,8 +15,8 @@ import jakarta.validation.ConstraintDeclarationException;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.builder.DistributionSetCreate;
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeCreate;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.builder.DynamicRolloutGroupTemplate;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -25,11 +25,11 @@ import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.jupiter.api.Test;
@Slf4j
/**
* Feature: SecurityTests - RolloutManagement<br/>
* Story: SecurityTests RolloutManagement
*/
@Slf4j
class RolloutManagementSecurityTest extends AbstractJpaIntegrationTest {
/**
@@ -101,9 +101,10 @@ class RolloutManagementSecurityTest extends AbstractJpaIntegrationTest {
*/
@Test
void cancelRolloutsForDistributionSetPermissionsCheck() {
final DistributionSetTypeCreate key = entityFactory.distributionSetType().create().name("type").key("type");
final DistributionSetTypeManagement.Create key = DistributionSetTypeManagement.Create.builder().name("type").key("type").build();
distributionSetTypeManagement.create(key);
final DistributionSetCreate dsCreate = entityFactory.distributionSet().create().name("name").version("1.0.0").type("type");
final DistributionSetManagement.Create dsCreate =
DistributionSetManagement.Create.builder().type(defaultDsType()).name("name").version("1.0.0").build();
final DistributionSet ds = distributionSetManagement.create(dsCreate);
assertPermissions(() -> {
rolloutManagement.cancelRolloutsForDistributionSet(ds);
@@ -251,4 +252,4 @@ class RolloutManagementSecurityTest extends AbstractJpaIntegrationTest {
void findByRolloutWithDetailedStatusPermissionsCheck() {
assertPermissions(() -> rolloutGroupManagement.findByRolloutWithDetailedStatus(1L, PAGE), List.of(SpPermission.READ_ROLLOUT));
}
}
}

View File

@@ -13,8 +13,7 @@ import java.util.List;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.RepositoryManagement;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleCreate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleUpdate;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.jpa.AbstractRepositoryManagementSecurityTest;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.junit.jupiter.api.Test;
@@ -24,21 +23,21 @@ import org.junit.jupiter.api.Test;
* Story: SecurityTests SoftwareManagement
*/
class SoftwareManagementSecurityTest
extends AbstractRepositoryManagementSecurityTest<SoftwareModule, SoftwareModuleCreate, SoftwareModuleUpdate> {
extends AbstractRepositoryManagementSecurityTest<SoftwareModule, SoftwareModuleManagement.Create, SoftwareModuleManagement.Update> {
@Override
protected RepositoryManagement<SoftwareModule, SoftwareModuleCreate, SoftwareModuleUpdate> getRepositoryManagement() {
protected RepositoryManagement getRepositoryManagement() {
return softwareModuleManagement;
}
@Override
protected SoftwareModuleCreate getCreateObject() {
return entityFactory.softwareModule().create().name("name").version("version").type("type");
protected SoftwareModuleManagement.Create getCreateObject() {
return SoftwareModuleManagement.Create.builder().type(getASmType()).name("name").version("version").build();
}
@Override
protected SoftwareModuleUpdate getUpdateObject() {
return entityFactory.softwareModule().update(1L).locked(true);
protected SoftwareModuleManagement.Update getUpdateObject() {
return SoftwareModuleManagement.Update.builder().id(1L).locked(true).build();
}
/**
@@ -170,5 +169,4 @@ class SoftwareManagementSecurityTest
assertPermissions(() -> softwareModuleManagement.findMetaDataBySoftwareModuleIdsAndTargetVisible(List.of(1L)),
List.of(SpPermission.READ_REPOSITORY));
}
}
}

View File

@@ -22,6 +22,10 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
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;
@@ -61,7 +65,7 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
private static final PageRequest PAGE_REQUEST_100 = PageRequest.of(0, 100);
/**
* Verifies that management get access reacts as specified on calls for non existing entities by means
* Verifies that management get access reacts as specified on calls for non existing entities by means
* of Optional not present.
*/
@Test
@@ -79,20 +83,19 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
}
/**
* Verifies that management queries react as specfied on calls for non existing entities
* by means of throwing EntityNotFoundException.
* Verifies that management queries react as specfied on calls for non existing entities
* by means of throwing EntityNotFoundException.
*/
@Test
@ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) })
void entityQueriesReferringToNotExistingEntitiesThrowsException() {
final SoftwareModule module = testdataFactory.createSoftwareModuleApp();
verifyThrownExceptionBy(
() -> softwareModuleManagement.create(Collections
.singletonList(entityFactory.softwareModule().create().name("xxx").type(NOT_EXIST_ID))), "SoftwareModuleType");
verifyThrownExceptionBy(
() -> softwareModuleManagement
.create(entityFactory.softwareModule().create().name("xxx").type(NOT_EXIST_ID)), "SoftwareModuleType");
final SoftwareModuleManagement.Create noType = SoftwareModuleManagement.Create.builder().name("xxx").type(null).build();
final List<SoftwareModuleManagement.Create> noTypeList = List.of(noType);
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> softwareModuleManagement.create(noTypeList));
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> softwareModuleManagement.create(noType));
verifyThrownExceptionBy(
() -> softwareModuleManagement.updateMetadata(
@@ -122,7 +125,8 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
verifyThrownExceptionBy(() -> softwareModuleManagement.getMetadata(NOT_EXIST_IDL), "SoftwareModule");
verifyThrownExceptionBy(() -> softwareModuleManagement.findByType(NOT_EXIST_IDL, PAGE), "SoftwareModule");
verifyThrownExceptionBy(() -> softwareModuleManagement.update(entityFactory.softwareModule().update(NOT_EXIST_IDL)), "SoftwareModule");
verifyThrownExceptionBy(() -> softwareModuleManagement.update(SoftwareModuleManagement.Update.builder().id(NOT_EXIST_IDL).build()),
"SoftwareModule");
}
/**
@@ -133,7 +137,7 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
final SoftwareModule ah = testdataFactory.createSoftwareModuleOs();
final SoftwareModule updated = softwareModuleManagement
.update(entityFactory.softwareModule().update(ah.getId()));
.update(SoftwareModuleManagement.Update.builder().id(ah.getId()).build());
assertThat(updated.getOptLockRevision())
.as("Expected version number of updated entity to be equal to created version")
@@ -148,7 +152,7 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
final SoftwareModule ah = testdataFactory.createSoftwareModuleOs();
final SoftwareModule updated = softwareModuleManagement
.update(entityFactory.softwareModule().update(ah.getId()).description("changed").vendor("changed"));
.update(SoftwareModuleManagement.Update.builder().id(ah.getId()).description("changed").vendor("changed").build());
assertThat(updated.getOptLockRevision())
.as("Expected version number of updated entitity is")
@@ -174,17 +178,20 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test
void findSoftwareModuleByFilters() {
final SoftwareModule ah = softwareModuleManagement
.create(entityFactory.softwareModule().create().type(appType).name("agent-hub").version("1.0.1"));
.create(SoftwareModuleManagement.Create.builder().type(appType).name("agent-hub").version("1.0.1").build());
final SoftwareModule jvm = softwareModuleManagement
.create(entityFactory.softwareModule().create().type(runtimeType).name("oracle-jre").version("1.7.2"));
.create(SoftwareModuleManagement.Create.builder().type(runtimeType).name("oracle-jre").version("1.7.2").build());
final SoftwareModule os = softwareModuleManagement
.create(entityFactory.softwareModule().create().type(osType).name("poky").version("3.0.2"));
.create(SoftwareModuleManagement.Create.builder().type(osType).name("poky").version("3.0.2").build());
final SoftwareModule ah2 = softwareModuleManagement
.create(entityFactory.softwareModule().create().type(appType).name("agent-hub").version("1.0.2"));
.create(SoftwareModuleManagement.Create.builder().type(appType).name("agent-hub").version("1.0.2").build());
JpaDistributionSet ds = (JpaDistributionSet) distributionSetManagement
.create(entityFactory.distributionSet().create().name("ds-1").version("1.0.1").type(standardDsType)
.modules(Arrays.asList(os.getId(), jvm.getId(), ah2.getId())));
.create(DistributionSetManagement.Create.builder()
.type(standardDsType)
.name("ds-1").version("1.0.1")
.modules(Set.of(os, jvm, ah2))
.build());
final JpaTarget target = (JpaTarget) testdataFactory.createTarget();
ds = (JpaDistributionSet) assignSet(target, ds).getDistributionSet();
@@ -217,10 +224,7 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
*/
@Test
void findSoftwareModulesById() {
final List<Long> modules = Arrays.asList(testdataFactory.createSoftwareModuleOs().getId(),
testdataFactory.createSoftwareModuleApp().getId(), 624355263L);
final List<Long> modules = List.of(testdataFactory.createSoftwareModuleOs().getId(), testdataFactory.createSoftwareModuleApp().getId());
assertThat(softwareModuleManagement.get(modules)).hasSize(2);
}
@@ -236,7 +240,7 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
softwareModuleManagement.delete(testdataFactory.createSoftwareModuleOs("deleted").getId());
testdataFactory.createSoftwareModuleApp();
assertThat(softwareModuleManagement.findByType(osType.getId(), PAGE).getContent())
assertThat((List) softwareModuleManagement.findByType(osType.getId(), PAGE).getContent())
.as("Expected to find the following number of modules:").hasSize(2).as("with the following elements")
.contains(two, one);
}
@@ -497,8 +501,9 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
// one soft deleted
final SoftwareModule deleted = testdataFactory.createSoftwareModuleApp();
final DistributionSet set = distributionSetManagement.create(entityFactory.distributionSet().create()
.name("set").version("1").modules(Arrays.asList(one.getId(), deleted.getId())));
final DistributionSet set = distributionSetManagement.create(DistributionSetManagement.Create.builder()
.type(defaultDsType())
.name("set").version("1").modules(Set.of(one, deleted)).build());
softwareModuleManagement.delete(deleted.getId());
assertThat(softwareModuleManagement.findByAssignedTo(set.getId(), PAGE).getContent())
@@ -825,7 +830,7 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
final List<SoftwareModule> modules = distributionSet.getModules().stream().toList();
assertThat(modules).hasSizeGreaterThan(1);
// try delete while DS is not locked
// try to delete while DS is not locked
softwareModuleManagement.delete(modules.get(0).getId());
distributionSetManagement.lock(distributionSet.getId());
@@ -833,7 +838,7 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
distributionSetManagement.get(distributionSet.getId()).map(DistributionSet::isLocked).orElse(false))
.isTrue();
// try delete SM of a locked DS
// try to delete SM of a locked DS
final Long moduleId = modules.get(1).getId();
assertThatExceptionOfType(LockedException.class)
.as("Attempt to delete a software module of a locked DS should throw an exception")
@@ -865,8 +870,8 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
final long countSoftwareModule = softwareModuleRepository.count();
// create SoftwareModule
SoftwareModule softwareModule = softwareModuleManagement.create(entityFactory.softwareModule().create()
.type(type).name(name).version(version).description("description of artifact " + name));
SoftwareModule softwareModule = softwareModuleManagement.create(SoftwareModuleManagement.Create.builder()
.type(type).name(name).version(version).description("description of artifact " + name).build());
final int artifactSize = 5 * 1024;
for (int i = 0; i < numberArtifacts; i++) {
@@ -904,4 +909,4 @@ class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
.isNull();
}
}
}
}

View File

@@ -14,8 +14,7 @@ import java.util.Random;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.RepositoryManagement;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeCreate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeUpdate;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.jpa.AbstractRepositoryManagementSecurityTest;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.junit.jupiter.api.Test;
@@ -25,21 +24,21 @@ import org.junit.jupiter.api.Test;
* Story: SecurityTests SoftwareModuleTypeManagement
*/
class SoftwareModuleTypeManagementSecurityTest
extends AbstractRepositoryManagementSecurityTest<SoftwareModuleType, SoftwareModuleTypeCreate, SoftwareModuleTypeUpdate> {
extends AbstractRepositoryManagementSecurityTest<SoftwareModuleType, SoftwareModuleTypeManagement.Create, SoftwareModuleTypeManagement.Update> {
@Override
protected RepositoryManagement<SoftwareModuleType, SoftwareModuleTypeCreate, SoftwareModuleTypeUpdate> getRepositoryManagement() {
protected RepositoryManagement getRepositoryManagement() {
return softwareModuleTypeManagement;
}
@Override
protected SoftwareModuleTypeCreate getCreateObject() {
return entityFactory.softwareModuleType().create().key(String.format("key-%d", new Random().nextInt())).name(String.format("name-%d", new Random().nextInt()));
protected SoftwareModuleTypeManagement.Create getCreateObject() {
return SoftwareModuleTypeManagement.Create.builder().key(String.format("key-%d", new Random().nextInt())).name(String.format("name-%d", new Random().nextInt())).build();
}
@Override
protected SoftwareModuleTypeUpdate getUpdateObject() {
return entityFactory.softwareModuleType().update(1L).description("description");
protected SoftwareModuleTypeManagement.Update getUpdateObject() {
return SoftwareModuleTypeManagement.Update.builder().id(1L).description("description").build();
}
/**
@@ -57,5 +56,4 @@ class SoftwareModuleTypeManagementSecurityTest
void getByNamePermissionsCheck() {
assertPermissions(() -> softwareModuleTypeManagement.findByName("name"), List.of(SpPermission.READ_REPOSITORY));
}
}
}

View File

@@ -17,7 +17,8 @@ import java.util.List;
import jakarta.validation.ConstraintViolationException;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeCreate;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
@@ -26,6 +27,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Slice;
/**
* Feature: Component Tests - Repository<br/>
@@ -34,7 +36,7 @@ import org.junit.jupiter.api.Test;
class SoftwareModuleTypeManagementTest extends AbstractJpaIntegrationTest {
/**
* Verifies that management get access reacts as specfied on calls for non existing entities by means
* Verifies that management get access reacts as specfied on calls for non existing entities by means
* of Optional not present.
*/
@Test
@@ -47,8 +49,8 @@ class SoftwareModuleTypeManagementTest extends AbstractJpaIntegrationTest {
}
/**
* Verifies that management queries react as specfied on calls for non existing entities
* by means of throwing EntityNotFoundException.
* Verifies that management queries react as specfied on calls for non existing entities
* by means of throwing EntityNotFoundException.
*/
@Test
@ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 0) })
@@ -56,7 +58,7 @@ class SoftwareModuleTypeManagementTest extends AbstractJpaIntegrationTest {
verifyThrownExceptionBy(() -> softwareModuleTypeManagement.delete(NOT_EXIST_IDL), "SoftwareModuleType");
verifyThrownExceptionBy(
() -> softwareModuleTypeManagement.update(entityFactory.softwareModuleType().update(NOT_EXIST_IDL)),
() -> softwareModuleTypeManagement.update(SoftwareModuleTypeManagement.Update.builder().id(NOT_EXIST_IDL).build()),
"SoftwareModuleType");
}
@@ -66,10 +68,10 @@ class SoftwareModuleTypeManagementTest extends AbstractJpaIntegrationTest {
@Test
void updateNothingResultsInUnchangedRepositoryForType() {
final SoftwareModuleType created = softwareModuleTypeManagement
.create(entityFactory.softwareModuleType().create().key("test-key").name("test-name"));
.create(SoftwareModuleTypeManagement.Create.builder().key("test-key").name("test-name").build());
final SoftwareModuleType updated = softwareModuleTypeManagement
.update(entityFactory.softwareModuleType().update(created.getId()));
.update(SoftwareModuleTypeManagement.Update.builder().id(created.getId()).build());
assertThat(updated.getOptLockRevision())
.as("Expected version number of updated entitity to be equal to created version")
@@ -82,10 +84,10 @@ class SoftwareModuleTypeManagementTest extends AbstractJpaIntegrationTest {
@Test
void updateSoftwareModuleTypeFieldsToNewValue() {
final SoftwareModuleType created = softwareModuleTypeManagement
.create(entityFactory.softwareModuleType().create().key("test-key").name("test-name"));
.create(SoftwareModuleTypeManagement.Create.builder().key("test-key").name("test-name").build());
final SoftwareModuleType updated = softwareModuleTypeManagement.update(
entityFactory.softwareModuleType().update(created.getId()).description("changed").colour("changed"));
SoftwareModuleTypeManagement.Update.builder().id(created.getId()).description("changed").colour("changed").build());
assertThat(updated.getOptLockRevision()).as("Expected version number of updated entities is")
.isEqualTo(created.getOptLockRevision() + 1);
@@ -98,9 +100,9 @@ class SoftwareModuleTypeManagementTest extends AbstractJpaIntegrationTest {
*/
@Test
void createModuleTypesCallFailsForExistingTypes() {
final List<SoftwareModuleTypeCreate> created = Arrays.asList(
entityFactory.softwareModuleType().create().key("test-key").name("test-name"),
entityFactory.softwareModuleType().create().key("test-key2").name("test-name2"));
final List<SoftwareModuleTypeManagement.Create> created = Arrays.asList(
SoftwareModuleTypeManagement.Create.builder().key("test-key").name("test-name").build(),
SoftwareModuleTypeManagement.Create.builder().key("test-key2").name("test-name2").build());
softwareModuleTypeManagement.create(created);
assertThatExceptionOfType(EntityAlreadyExistsException.class)
.as("should not have worked as module type already exists")
@@ -112,32 +114,30 @@ class SoftwareModuleTypeManagementTest extends AbstractJpaIntegrationTest {
*/
@Test
void deleteAssignedAndUnassignedSoftwareModuleTypes() {
assertThat(softwareModuleTypeManagement.findAll(PAGE)).hasSize(3).contains(osType, runtimeType, appType);
assertThat((Slice) softwareModuleTypeManagement.findAll(PAGE)).hasSize(3).contains(osType, runtimeType, appType);
SoftwareModuleType type = softwareModuleTypeManagement
.create(entityFactory.softwareModuleType().create().key("bundle").name("OSGi Bundle"));
.create(SoftwareModuleTypeManagement.Create.builder().key("bundle").name("OSGi Bundle").build());
assertThat(softwareModuleTypeManagement.findAll(PAGE)).hasSize(4).contains(osType, runtimeType, appType, type);
assertThat((Slice) softwareModuleTypeManagement.findAll(PAGE)).hasSize(4).contains(osType, runtimeType, appType, type);
// delete unassigned
softwareModuleTypeManagement.delete(type.getId());
assertThat(softwareModuleTypeManagement.findAll(PAGE)).hasSize(3).contains(osType, runtimeType, appType);
assertThat(softwareModuleTypeRepository.findAll()).hasSize(3).contains((JpaSoftwareModuleType) osType,
(JpaSoftwareModuleType) runtimeType, (JpaSoftwareModuleType) appType);
assertThat((Slice) softwareModuleTypeManagement.findAll(PAGE)).hasSize(3).contains(osType, runtimeType, appType);
assertThat((List) softwareModuleTypeRepository.findAll()).hasSize(3).contains(osType, runtimeType, appType);
type = softwareModuleTypeManagement
.create(entityFactory.softwareModuleType().create().key("bundle2").name("OSGi Bundle2"));
.create(SoftwareModuleTypeManagement.Create.builder().key("bundle2").name("OSGi Bundle2").build());
assertThat(softwareModuleTypeManagement.findAll(PAGE)).hasSize(4).contains(osType, runtimeType, appType, type);
assertThat((Slice) softwareModuleTypeManagement.findAll(PAGE)).hasSize(4).contains(osType, runtimeType, appType, type);
softwareModuleManagement
.create(entityFactory.softwareModule().create().type(type).name("Test SM").version("1.0"));
.create(SoftwareModuleManagement.Create.builder().type(type).name("Test SM").version("1.0").build());
// delete assigned
softwareModuleTypeManagement.delete(type.getId());
assertThat(softwareModuleTypeManagement.findAll(PAGE)).hasSize(3).contains(osType, runtimeType, appType);
assertThat(softwareModuleTypeManagement.findByRsql("name==*", PAGE)).hasSize(3).contains(osType, runtimeType,
appType);
assertThat((Slice) softwareModuleTypeManagement.findAll(PAGE)).hasSize(3).contains(osType, runtimeType, appType);
assertThat((Slice) softwareModuleTypeManagement.findByRsql("name==*", PAGE)).hasSize(3).contains(osType, runtimeType, appType);
assertThat(softwareModuleTypeManagement.count()).isEqualTo(3);
assertThat(softwareModuleTypeRepository.findAll()).hasSize(4).contains((JpaSoftwareModuleType) osType,
@@ -152,11 +152,12 @@ class SoftwareModuleTypeManagementTest extends AbstractJpaIntegrationTest {
void findSoftwareModuleTypeByName() {
testdataFactory.createSoftwareModuleOs();
final SoftwareModuleType found = softwareModuleTypeManagement
.create(entityFactory.softwareModuleType().create().key("thetype").name("thename"));
.create(SoftwareModuleTypeManagement.Create.builder().key("thetype").name("thename").build());
softwareModuleTypeManagement
.create(entityFactory.softwareModuleType().create().key("thetype2").name("anothername"));
.create(SoftwareModuleTypeManagement.Create.builder().key("thetype2").name("anothername").build());
assertThat(softwareModuleTypeManagement.findByName("thename")).as("Type with given name").contains(found);
assertThat((((SoftwareModuleTypeManagement<SoftwareModuleType>) softwareModuleTypeManagement)).findByName("thename")).as(
"Type with given name").contains(found);
}
/**
@@ -164,12 +165,11 @@ class SoftwareModuleTypeManagementTest extends AbstractJpaIntegrationTest {
*/
@Test
void createSoftwareModuleTypeFailsWithExistingEntity() {
final SoftwareModuleTypeCreate create = entityFactory.softwareModuleType().create().key("thetype").name("thename");
final SoftwareModuleTypeManagement.Create create = SoftwareModuleTypeManagement.Create.builder().key("thetype").name("thename").build();
softwareModuleTypeManagement.create(create);
assertThatExceptionOfType(EntityAlreadyExistsException.class)
.as("should not have worked as module type already exists")
.isThrownBy(() -> softwareModuleTypeManagement
.create(create));
.isThrownBy(() -> softwareModuleTypeManagement.create(create));
}
/**
@@ -177,10 +177,10 @@ class SoftwareModuleTypeManagementTest extends AbstractJpaIntegrationTest {
*/
@Test
void createSoftwareModuleTypesFailsWithExistingEntity() {
softwareModuleTypeManagement.create(entityFactory.softwareModuleType().create().key("thetype").name("thename"));
final List<SoftwareModuleTypeCreate> creates = List.of(
entityFactory.softwareModuleType().create().key("thetype").name("thename"),
entityFactory.softwareModuleType().create().key("anothertype").name("anothername"));
softwareModuleTypeManagement.create(SoftwareModuleTypeManagement.Create.builder().key("thetype").name("thename").build());
final List<SoftwareModuleTypeManagement.Create> creates = List.of(
SoftwareModuleTypeManagement.Create.builder().key("thetype").name("thename").build(),
SoftwareModuleTypeManagement.Create.builder().key("anothertype").name("anothername").build());
assertThatExceptionOfType(EntityAlreadyExistsException.class)
.as("should not have worked as module type already exists")
.isThrownBy(() -> softwareModuleTypeManagement.create(creates));
@@ -191,7 +191,8 @@ class SoftwareModuleTypeManagementTest extends AbstractJpaIntegrationTest {
*/
@Test
void createSoftwareModuleTypesFailsWithInvalidMaxAssignment() {
final SoftwareModuleTypeCreate create = entityFactory.softwareModuleType().create().key("type").name("name").maxAssignments(0);
final SoftwareModuleTypeManagement.Create create =
SoftwareModuleTypeManagement.Create.builder().key("type").name("name").maxAssignments(0).build();
assertThatExceptionOfType(ConstraintViolationException.class)
.as("should not have worked as max assignment is invalid. Should be greater than 0")
.isThrownBy(() -> softwareModuleTypeManagement.create(create));
@@ -202,12 +203,12 @@ class SoftwareModuleTypeManagementTest extends AbstractJpaIntegrationTest {
*/
@Test
void createMultipleSoftwareModuleTypes() {
final List<SoftwareModuleType> created = softwareModuleTypeManagement
.create(Arrays.asList(entityFactory.softwareModuleType().create().key("thetype").name("thename"),
entityFactory.softwareModuleType().create().key("thetype2").name("thename2")));
final List<? extends SoftwareModuleType> created = softwareModuleTypeManagement
.create(List.of(
SoftwareModuleTypeManagement.Create.builder().key("thetype").name("thename").build(),
SoftwareModuleTypeManagement.Create.builder().key("thetype2").name("thename2").build()));
assertThat(created).as("Number of created types").hasSize(2);
assertThat(softwareModuleTypeManagement.count()).as("Number of types in repository").isEqualTo(5);
}
}
}

View File

@@ -18,11 +18,11 @@ import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.junit.jupiter.api.Test;
@Slf4j
/**
* Feature: SecurityTests - SystemManagement<br/>
* Story: SecurityTests SystemManagement
*/
@Slf4j
class SystemManagementSecurityTest extends AbstractJpaIntegrationTest {
/**

View File

@@ -25,6 +25,7 @@ import jakarta.validation.ConstraintViolationException;
import org.assertj.core.api.Assertions;
import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
@@ -588,8 +589,10 @@ class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest {
private void verifyAutoAssignmentWithIncompleteDs(final TargetFilterQuery targetFilterQuery) {
final DistributionSet incompleteDistributionSet = distributionSetManagement
.create(entityFactory.distributionSet().create().name("incomplete").version("1")
.type(testdataFactory.findOrCreateDefaultTestDsType()));
.create(DistributionSetManagement.Create.builder()
.type(testdataFactory.findOrCreateDefaultTestDsType())
.name("incomplete").version("1")
.build());
final AutoAssignDistributionSetUpdate autoAssignDistributionSetUpdate = entityFactory.targetFilterQuery()
.updateAutoAssign(targetFilterQuery.getId()).ds(incompleteDistributionSet.getId());

View File

@@ -16,8 +16,8 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.FilterParams;
import org.eclipse.hawkbit.repository.builder.DistributionSetCreate;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -62,7 +62,7 @@ class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
}
/**
* Tests different parameter combinations for target search operations.
* Tests different parameter combinations for target search operations.
* and query definitions by RSQL (named and un-named).
*/
@Test
@@ -278,13 +278,12 @@ class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
*/
@Test
void shouldNotFindTargetsIncompatibleWithDS() {
final DistributionSetType dsType = testdataFactory.findOrCreateDistributionSetType("test-ds-type",
"test-ds-type");
final DistributionSet testDs = createDistSetWithType(dsType);
final TargetType compatibleTargetType = testdataFactory.createTargetType("compTestType",
Collections.singletonList(dsType));
final TargetType incompatibleTargetType = testdataFactory.createTargetType("incompTestType",
Collections.singletonList(testdataFactory.createDistributionSet().getType()));
final DistributionSetType dsType = testdataFactory.findOrCreateDistributionSetType("test-ds-type", "test-ds-type");
final DistributionSet testDs = distributionSetManagement.create(DistributionSetManagement.Create.builder()
.type(dsType).name("test-ds").version("1.0").build());
final TargetType compatibleTargetType = testdataFactory.createTargetType("compTestType", List.of(dsType));
final TargetType incompatibleTargetType = testdataFactory.createTargetType(
"incompTestType", List.of(testdataFactory.createDistributionSet().getType()));
final TargetFilterQuery tfq = targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("test-filter").query("name==*"));
@@ -659,10 +658,4 @@ class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected);
}
private DistributionSet createDistSetWithType(final DistributionSetType type) {
final DistributionSetCreate dsCreate = entityFactory.distributionSet().create().name("test-ds").version("1.0")
.type(type);
return distributionSetManagement.create(dsCreate);
}
}

View File

@@ -20,11 +20,11 @@ import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.jupiter.api.Test;
@Slf4j
/**
* Feature: SecurityTests - TargetManagement<br/>
* Story: SecurityTests TargetManagement
*/
@Slf4j
class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
/**
@@ -184,9 +184,9 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
@Test
void findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatablePermissionsCheck() {
assertPermissions(
() -> targetManagement.findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(List.of(1L), "controllerId==id",
entityFactory.distributionSetType().create().build(), PAGE
), List.of(SpPermission.READ_TARGET, SpPermission.READ_ROLLOUT));
() -> targetManagement.findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
List.of(1L), "controllerId==id", defaultDsType(), PAGE),
List.of(SpPermission.READ_TARGET, SpPermission.READ_ROLLOUT));
}
/**
@@ -203,8 +203,10 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
*/
@Test
void countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatablePermissionsCheck() {
assertPermissions(() -> targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable("controllerId==id", List.of(1L),
entityFactory.distributionSetType().create().build()), List.of(SpPermission.READ_TARGET, SpPermission.READ_ROLLOUT));
assertPermissions(
() -> targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
"controllerId==id", List.of(1L), defaultDsType()),
List.of(SpPermission.READ_TARGET, SpPermission.READ_ROLLOUT));
}
/**

View File

@@ -16,11 +16,11 @@ import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.junit.jupiter.api.Test;
@Slf4j
/**
* Feature: SecurityTests - TargetTagManagement<br/>
* Story: SecurityTests TargetTagManagement
*/
@Slf4j
class TargetTagManagementSecurityTest extends AbstractJpaIntegrationTest {
/**
@@ -106,4 +106,4 @@ class TargetTagManagementSecurityTest extends AbstractJpaIntegrationTest {
void updatePermissionsCheck() {
assertPermissions(() -> targetTagManagement.update(entityFactory.tag().update(1L)), List.of(SpPermission.UPDATE_TARGET));
}
}
}

View File

@@ -17,11 +17,11 @@ import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.jupiter.api.Test;
@Slf4j
/**
* Feature: SecurityTests - TargetTypeManagement<br/>
* Story: SecurityTests TargetTypeManagement
*/
@Slf4j
class TargetTypeManagementSecurityTest extends AbstractJpaIntegrationTest {
/**
@@ -151,5 +151,4 @@ class TargetTypeManagementSecurityTest extends AbstractJpaIntegrationTest {
assertPermissions(() -> targetTypeManagement.unassignDistributionSetType(1L, 1L),
List.of(SpPermission.UPDATE_TARGET, SpPermission.READ_REPOSITORY));
}
}
}

View File

@@ -17,11 +17,11 @@ import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.junit.jupiter.api.Test;
@Slf4j
/**
* Feature: SecurityTests - TargetManagement<br/>
* Story: SecurityTests TargetManagement
*/
@Slf4j
class TenantConfigurationManagementSecurityTest extends AbstractJpaIntegrationTest {
/**
@@ -87,4 +87,4 @@ class TenantConfigurationManagementSecurityTest extends AbstractJpaIntegrationTe
void pollStatusResolverPermissionsCheck() {
assertPermissions(() -> tenantConfigurationManagement.pollStatusResolver(), List.of(SpPermission.READ_TARGET));
}
}
}

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.model;
import static org.assertj.core.api.Assertions.assertThat;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.EntityInterceptor;
import org.eclipse.hawkbit.repository.jpa.model.helper.EntityInterceptorHolder;
@@ -115,7 +116,7 @@ class EntityInterceptorListenerTest extends AbstractJpaIntegrationTest {
private void executeDeleteAndAssertCallbackResult(final AbstractEntityListener entityInterceptor) {
EntityInterceptorHolder.getInstance().getEntityInterceptors().add(entityInterceptor);
final SoftwareModuleType type = softwareModuleTypeManagement
.create(entityFactory.softwareModuleType().create().name("test").key("test"));
.create(SoftwareModuleTypeManagement.Create.builder().name("test").key("test").build());
softwareModuleTypeManagement.delete(type.getId());
assertThat(entityInterceptor.getEntity()).isNotNull();
@@ -198,4 +199,4 @@ class EntityInterceptorListenerTest extends AbstractJpaIntegrationTest {
setEntity(entity);
}
}
}
}

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.model;
import static org.assertj.core.api.Assertions.assertThat;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.junit.jupiter.api.Test;
@@ -59,12 +60,12 @@ class ModelEqualsHashcodeTest extends AbstractJpaIntegrationTest {
@Test
void changedEntitiesAreNotEqual() {
final SoftwareModuleType type = softwareModuleTypeManagement
.create(entityFactory.softwareModuleType().create().key("test").name("test"));
.create(SoftwareModuleTypeManagement.Create.builder().key("test").name("test").build());
assertThat(type).as("persited entity is not equal to regular object")
.isNotEqualTo(entityFactory.softwareModuleType().create().key("test").name("test").build());
.isNotEqualTo(SoftwareModuleTypeManagement.Create.builder().key("test").name("test").build());
final SoftwareModuleType updated = softwareModuleTypeManagement.update(
entityFactory.softwareModuleType().update(type.getId()).description("another"));
SoftwareModuleTypeManagement.Update.builder().id(type.getId()).description("another").build());
assertThat(type).as("Changed entity is not equal to the previous version").isNotEqualTo(updated);
}
@@ -72,9 +73,9 @@ class ModelEqualsHashcodeTest extends AbstractJpaIntegrationTest {
* Verify that no proxy of the entity manager has an influence on the equals or hashcode result.
*/
@Test
void managedEntityIsEqualToUnamangedObjectWithSameKey() {
void managedEntityIsEqualToUnmanagedObjectWithSameKey() {
final SoftwareModuleType type = softwareModuleTypeManagement.create(
entityFactory.softwareModuleType().create().key("test").name("test").description("test"));
SoftwareModuleTypeManagement.Create.builder().key("test").name("test").description("test").build());
final JpaSoftwareModuleType mock = new JpaSoftwareModuleType("test", "test", "test", 1);
mock.setId(type.getId());
@@ -86,5 +87,4 @@ class ModelEqualsHashcodeTest extends AbstractJpaIntegrationTest {
.as("managed entity has same hash code as regular object with same content")
.hasSameHashCodeAs(mock.hashCode());
}
}
}

View File

@@ -12,6 +12,7 @@ package org.eclipse.hawkbit.repository.jpa.rsql;
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;
@@ -33,20 +34,20 @@ class RsqlSoftwareModuleFieldTest extends AbstractJpaIntegrationTest {
@BeforeEach
void setupBeforeTest() {
ah = softwareModuleManagement.create(entityFactory.softwareModule().create().type(appType).name("agent-hub")
.version("1.0.1").description("agent-hub"));
softwareModuleManagement.create(entityFactory.softwareModule().create().type(runtimeType).name("oracle-jre")
.version("1.7.2").description("aa"));
ah = softwareModuleManagement.create(SoftwareModuleManagement.Create.builder().type(appType).name("agent-hub")
.version("1.0.1").description("agent-hub").build());
softwareModuleManagement.create(SoftwareModuleManagement.Create.builder().type(runtimeType).name("oracle-jre")
.version("1.7.2").description("aa").build());
softwareModuleManagement.create(
entityFactory.softwareModule().create().type(osType).name("poki").version("3.0.2").description("aa"));
SoftwareModuleManagement.Create.builder().type(osType).name("poki").version("3.0.2").description("aa").build());
softwareModuleManagement
.create(entityFactory.softwareModule().create().type(osType).name("*§$%&/&%ÄÜ*Ö@").version("1.0.0")
.description("wildcard testing"));
.create(SoftwareModuleManagement.Create.builder().type(osType).name("*§$%&/&%ÄÜ*Ö@").version("1.0.0")
.description("wildcard testing").build());
softwareModuleManagement
.create(entityFactory.softwareModule().create().type(osType).name("noDesc").version("noDesc"));
.create(SoftwareModuleManagement.Create.builder().type(osType).name("noDesc").version("noDesc").build());
final JpaSoftwareModule ah2 = (JpaSoftwareModule) softwareModuleManagement.create(entityFactory.softwareModule()
.create().type(appType).name("agent-hub2").version("1.0.1").description("agent-hub2"));
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");
@@ -163,7 +164,7 @@ class RsqlSoftwareModuleFieldTest extends AbstractJpaIntegrationTest {
}
private void assertRSQLQuery(final String rsql, final long expectedEntity) {
final Page<SoftwareModule> find = softwareModuleManagement.findByRsql(rsql, PageRequest.of(0, 100));
final Page<? extends SoftwareModule> find = softwareModuleManagement.findByRsql(rsql, PageRequest.of(0, 100));
final long countAll = find.getTotalElements();
assertThat(find).isNotNull();
assertThat(countAll).isEqualTo(expectedEntity);

View File

@@ -87,9 +87,9 @@ class RsqlSoftwareModuleTypeFieldsTest extends AbstractJpaIntegrationTest {
}
private void assertRSQLQuery(final String rsql, final long expectedEntity) {
final Page<SoftwareModuleType> find = softwareModuleTypeManagement.findByRsql(rsql, PageRequest.of(0, 100));
final Page<? extends SoftwareModuleType> find = softwareModuleTypeManagement.findByRsql(rsql, PageRequest.of(0, 100));
final long countAll = find.getTotalElements();
assertThat(find).isNotNull();
assertThat(countAll).isEqualTo(expectedEntity);
}
}
}

View File

@@ -11,8 +11,8 @@ package org.eclipse.hawkbit.repository.jpa.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.TagFields;
import org.eclipse.hawkbit.repository.builder.TagCreate;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.TargetTag;
@@ -29,12 +29,11 @@ class RsqlTagFieldsTest extends AbstractJpaIntegrationTest {
@BeforeEach
void seuptBeforeTest() {
for (int i = 0; i < 5; i++) {
final TagCreate targetTag = entityFactory.tag().create()
.name(Integer.toString(i)).description(Integer.toString(i)).colour(i % 2 == 0 ? "red" : "blue");
targetTagManagement.create(targetTag);
distributionSetTagManagement.create(targetTag);
targetTagManagement.create(entityFactory.tag().create()
.name(Integer.toString(i)).description(Integer.toString(i)).colour(i % 2 == 0 ? "red" : "blue"));
distributionSetTagManagement.create(DistributionSetTagManagement.Create.builder()
.name(Integer.toString(i)).description(Integer.toString(i)).colour(i % 2 == 0 ? "red" : "blue").build());
}
}
@@ -129,9 +128,9 @@ class RsqlTagFieldsTest extends AbstractJpaIntegrationTest {
}
private void assertRSQLQueryDistributionSet(final String rsql, final long expectedEntities) {
final Page<DistributionSetTag> findEnitity = distributionSetTagManagement.findByRsql(rsql, PageRequest.of(0, 100));
final long countAllEntities = findEnitity.getTotalElements();
assertThat(findEnitity).isNotNull();
final Page<? extends DistributionSetTag> findEntity = distributionSetTagManagement.findByRsql(rsql, PageRequest.of(0, 100));
final long countAllEntities = findEntity.getTotalElements();
assertThat(findEntity).isNotNull();
assertThat(countAllEntities).isEqualTo(expectedEntities);
}

View File

@@ -167,11 +167,11 @@ class MultiTenancyEntityTest extends AbstractJpaIntegrationTest {
createDistributionSetForTenant(anotherTenant);
// ensure both tenants see their distribution sets
final Slice<DistributionSet> findDistributionSetsForTenant = findDistributionSetForTenant(tenant);
final Slice<? extends DistributionSet> findDistributionSetsForTenant = findDistributionSetForTenant(tenant);
assertThat(findDistributionSetsForTenant).hasSize(1);
assertThat(findDistributionSetsForTenant.getContent().get(0).getTenant().toUpperCase())
.isEqualTo(tenant.toUpperCase());
final Slice<DistributionSet> findDistributionSetsForAnotherTenant = findDistributionSetForTenant(anotherTenant);
final Slice<? extends DistributionSet> findDistributionSetsForAnotherTenant = findDistributionSetForTenant(anotherTenant);
assertThat(findDistributionSetsForAnotherTenant).hasSize(1);
assertThat(findDistributionSetsForAnotherTenant.getContent().get(0).getTenant().toUpperCase())
.isEqualTo(anotherTenant.toUpperCase());
@@ -200,7 +200,7 @@ class MultiTenancyEntityTest extends AbstractJpaIntegrationTest {
return runAsTenant(tenant, () -> testdataFactory.createDistributionSet());
}
private Slice<DistributionSet> findDistributionSetForTenant(final String tenant) throws Exception {
private Slice<? extends DistributionSet> findDistributionSetForTenant(final String tenant) throws Exception {
return runAsTenant(tenant, () -> distributionSetManagement.findByCompleted(true, PAGE));
}
}
}