Feature target type entity (#1162)

* Added Target Type model

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added Target Type JPA model

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added Target Type repository model classes

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Removed the name entity from Target Type

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Refactored the Target Type models

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added the DB migration script and updated the Target Type models

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added target type in target Mapper

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Changed the target type ID to Long

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added MYSQL DB migration script and removed the deleted column for target type

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Updated the DB migration script for target table

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added manyToMany reltation between target type and Ds type

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added POSTGRESQL DB migration script

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added MSSQL SERVER DB migration script

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added DB2 DB migration script

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added missing license header and java docs

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added on delete cascade in DB migration script

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added Target Type specification

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Removed the delete cascade and Added type API
Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* fixed API doc build

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added target type management test

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added target type events test

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added target type update and unassign to target

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added API tests for assigning target type to target

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added missing license header

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added missing docs

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Fixed sonar issues

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Fixed license header build issue

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Updated the attribute name to target type

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Fixed the review comments

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Removed unused error status variable

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added target API to assign target type

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Added the tests for assigning target type to target

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Fixed the review comments for null check

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>
This commit is contained in:
Anand Kumar
2021-08-25 12:13:27 +02:00
committed by GitHub
parent 2574581b2c
commit 3fa1dd1be4
76 changed files with 4915 additions and 41 deletions

View File

@@ -32,6 +32,7 @@ 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;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetTypeSpecification;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
@@ -66,6 +67,8 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
private final DistributionSetRepository distributionSetRepository;
private final TargetTypeRepository targetTypeRepository;
private final VirtualPropertyReplacer virtualPropertyReplacer;
private final Database database;
@@ -74,12 +77,13 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
JpaDistributionSetTypeManagement(final DistributionSetTypeRepository distributionSetTypeRepository,
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
final DistributionSetRepository distributionSetRepository,
final DistributionSetRepository distributionSetRepository, final TargetTypeRepository targetTypeRepository,
final VirtualPropertyReplacer virtualPropertyReplacer, final Database database,
final QuotaManagement quotaManagement) {
this.distributionSetTypeRepository = distributionSetTypeRepository;
this.softwareModuleTypeRepository = softwareModuleTypeRepository;
this.distributionSetRepository = distributionSetRepository;
this.targetTypeRepository = targetTypeRepository;
this.virtualPropertyReplacer = virtualPropertyReplacer;
this.database = database;
this.quotaManagement = quotaManagement;
@@ -190,7 +194,7 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
}
/**
* Enforces the quota specifiying the maximum number of
* Enforces the quota specifying the maximum number of
* {@link SoftwareModuleType}s per {@link DistributionSetType}.
*
* @param id
@@ -268,18 +272,28 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final long typeId) {
final JpaDistributionSetType toDelete = distributionSetTypeRepository.findById(typeId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, typeId));
unassignDsTypeFromTargetTypes(typeId);
if (distributionSetRepository.countByTypeId(typeId) > 0) {
toDelete.setDeleted(true);
distributionSetTypeRepository.save(toDelete);
} else {
}
else {
distributionSetTypeRepository.deleteById(typeId);
}
}
private void unassignDsTypeFromTargetTypes(long typeId) {
List<JpaTargetType> targetTypesByDsType = targetTypeRepository.findByDsType(typeId);
targetTypesByDsType.forEach(targetType -> {
targetType.removeDistributionSetType(typeId);
targetTypeRepository.save(targetType);
});
}
@Override
@Transactional
@Retryable(include = {

View File

@@ -20,6 +20,7 @@ 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;
@@ -42,6 +43,9 @@ public class JpaEntityFactory implements EntityFactory {
@Autowired
private DistributionSetBuilder distributionSetBuilder;
@Autowired
private TargetBuilder targetBuilder;
@Autowired
private DistributionSetTypeBuilder distributionSetTypeBuilder;
@@ -57,6 +61,9 @@ public class JpaEntityFactory implements EntityFactory {
@Autowired
private SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder;
@Autowired
private TargetTypeBuilder targetTypeBuilder;
@Override
public MetaData generateDsMetadata(final String key, final String value) {
return new JpaDistributionSetMetadata(key, StringUtils.trimWhitespace(value));
@@ -79,7 +86,12 @@ public class JpaEntityFactory implements EntityFactory {
@Override
public TargetBuilder target() {
return new JpaTargetBuilder();
return targetBuilder;
}
@Override
public TargetTypeBuilder targetType() {
return targetTypeBuilder;
}
@Override

View File

@@ -25,6 +25,7 @@ import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.configuration.MultiTenantJpaTransactionManager;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTenantMetaData;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
@@ -93,6 +94,9 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
@Autowired
private TargetTagRepository targetTagRepository;
@Autowired
private TargetTypeRepository targetTypeRepository;
@Autowired
private DistributionSetTagRepository distributionSetTagRepository;
@@ -255,6 +259,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
targetRepository.deleteByTenant(tenant);
targetFilterQueryRepository.deleteByTenant(tenant);
rolloutRepository.deleteByTenant(tenant);
targetTypeRepository.deleteByTenant(tenant);
targetTagRepository.deleteByTenant(tenant);
distributionSetTagRepository.deleteByTenant(tenant);
distributionSetRepository.deleteByTenant(tenant);

View File

@@ -49,6 +49,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
@@ -63,6 +64,7 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
@@ -98,6 +100,8 @@ public class JpaTargetManagement implements TargetManagement {
private final TargetRepository targetRepository;
private final TargetTypeRepository targetTypeRepository;
private final TargetMetadataRepository targetMetadataRepository;
private final RolloutGroupRepository rolloutGroupRepository;
@@ -119,7 +123,7 @@ public class JpaTargetManagement implements TargetManagement {
private final Database database;
public JpaTargetManagement(final EntityManager entityManager, final QuotaManagement quotaManagement,
final TargetRepository targetRepository, final TargetMetadataRepository targetMetadataRepository,
final TargetRepository targetRepository, final TargetTypeRepository targetTypeRepository, final TargetMetadataRepository targetMetadataRepository,
final RolloutGroupRepository rolloutGroupRepository,
final DistributionSetRepository distributionSetRepository,
final TargetFilterQueryRepository targetFilterQueryRepository,
@@ -130,6 +134,7 @@ public class JpaTargetManagement implements TargetManagement {
this.entityManager = entityManager;
this.quotaManagement = quotaManagement;
this.targetRepository = targetRepository;
this.targetTypeRepository = targetTypeRepository;
this.targetMetadataRepository = targetMetadataRepository;
this.rolloutGroupRepository = rolloutGroupRepository;
this.distributionSetRepository = distributionSetRepository;
@@ -152,6 +157,10 @@ public class JpaTargetManagement implements TargetManagement {
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
}
private JpaTargetType getTargetTypeByIdAndThrowIfNotFound(final long id) {
return targetTypeRepository.findById(id).orElseThrow(() -> new EntityNotFoundException(TargetType.class, id));
}
@Override
public List<Target> getByControllerID(final Collection<String> controllerIDs) {
return Collections.unmodifiableList(
@@ -333,6 +342,10 @@ public class JpaTargetManagement implements TargetManagement {
update.getDescription().ifPresent(target::setDescription);
update.getAddress().ifPresent(target::setAddress);
update.getSecurityToken().ifPresent(target::setSecurityToken);
if (update.getTargetTypeId() != null){
TargetType targetType = getTargetTypeByIdAndThrowIfNotFound(update.getTargetTypeId());
target.setTargetType(targetType);
}
return targetRepository.save(target);
}
@@ -589,6 +602,24 @@ public class JpaTargetManagement implements TargetManagement {
return result;
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target unAssignType(final String controllerID) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerID);
target.setTargetType(null);
return targetRepository.save(target);
}
@Override
public Target assignType(String controllerID, long targetTypeId) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerID);
final JpaTargetType targetType = getTargetTypeByIdAndThrowIfNotFound(targetTypeId);
target.setTargetType(targetType);
return targetRepository.save(target);
}
@Override
public Slice<Target> findByFilterOrderByLinkedDistributionSet(final Pageable pageable,
final long orderByDistributionId, final FilterParams filterParams) {

View File

@@ -0,0 +1,250 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.TargetTypeFields;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.builder.GenericTargetTypeUpdate;
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
import org.eclipse.hawkbit.repository.builder.TargetTypeUpdate;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.TargetTypeInUseException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetTypeCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
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.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.orm.jpa.vendor.Database;
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 java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* JPA implementation of {@link TargetTypeManagement}.
*
*/
@Transactional(readOnly = true)
@Validated
public class JpaTargetTypeManagement implements TargetTypeManagement {
private final TargetTypeRepository targetTypeRepository;
private final TargetRepository targetRepository;
private final DistributionSetTypeRepository distributionSetTypeRepository;
private final VirtualPropertyReplacer virtualPropertyReplacer;
private final Database database;
private final QuotaManagement quotaManagement;
/**
* Constructor
*
* @param targetTypeRepository
* Target type repository
* @param targetRepository
* Target repository
* @param virtualPropertyReplacer
* replacer
* @param database
* database
*/
public JpaTargetTypeManagement(final TargetTypeRepository targetTypeRepository,
final TargetRepository targetRepository, final DistributionSetTypeRepository distributionSetTypeRepository,
final VirtualPropertyReplacer virtualPropertyReplacer, final Database database,
final QuotaManagement quotaManagement) {
this.targetTypeRepository = targetTypeRepository;
this.targetRepository = targetRepository;
this.distributionSetTypeRepository = distributionSetTypeRepository;
this.virtualPropertyReplacer = virtualPropertyReplacer;
this.database = database;
this.quotaManagement = quotaManagement;
}
@Override
public Optional<TargetType> getByName(String name) {
return targetTypeRepository.findByName(name).map(TargetType.class::cast);
}
@Override
public long count() {
return targetTypeRepository.count();
}
@Override
public TargetType create(TargetTypeCreate create) {
final JpaTargetTypeCreate typeCreate = (JpaTargetTypeCreate) create;
return targetTypeRepository.save(typeCreate.build());
}
@Override
public List<TargetType> create(Collection<TargetTypeCreate> creates) {
return creates.stream().map(this::create).collect(Collectors.toList());
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final Long targetTypeId) {
throwExceptionIfTargetTypeDoesNotExist(targetTypeId);
if (targetRepository.countByTargetTypeId(targetTypeId) > 0) {
throw new TargetTypeInUseException("Cannot delete target type that is in use");
}
targetTypeRepository.deleteById(targetTypeId);
}
@Override
public Page<TargetType> findAll(Pageable pageable) {
return convertPage(targetTypeRepository.findAll(pageable), pageable);
}
@Override
public Page<TargetType> findByRsql(Pageable pageable, String rsqlParam) {
final Specification<JpaTargetType> spec = RSQLUtility.buildRsqlSpecification(rsqlParam, TargetTypeFields.class,
virtualPropertyReplacer, database);
return convertPage(targetTypeRepository.findAll(spec, pageable), pageable);
}
@Override
public Optional<TargetType> get(long id) {
return targetTypeRepository.findById(id).map(targetType -> targetType);
}
@Override
public Optional<TargetType> findByTargetId(long targetId) {
return targetTypeRepository
.findOne(TargetTypeSpecification.hasTarget(targetId)).map(TargetType.class::cast);
}
@Override
public List<TargetType> findByTargetIds(Collection<Long> targetIds) {
return targetTypeRepository.findAll(TargetTypeSpecification.hasTarget(targetIds))
.stream().map(TargetType.class::cast).collect(Collectors.toList());
}
@Override
public Optional<TargetType> findByTargetControllerId(String controllerId) {
return targetTypeRepository
.findOne(TargetTypeSpecification.hasTargetControllerId(controllerId)).map(TargetType.class::cast);
}
@Override
public List<TargetType> findByTargetControllerIds(Collection<String> controllerIds) {
return targetTypeRepository.findAll(TargetTypeSpecification.hasTargetControllerIdIn(controllerIds))
.stream().map(TargetType.class::cast).collect(Collectors.toList());
}
@Override
public List<TargetType> get(Collection<Long> ids) {
return Collections.unmodifiableList(targetTypeRepository.findAllById(ids));
}
@Override
public TargetType update(TargetTypeUpdate update) {
final GenericTargetTypeUpdate typeUpdate = (GenericTargetTypeUpdate) update;
final JpaTargetType type = findTargetTypeAndThrowExceptionIfNotFound(typeUpdate.getId());
typeUpdate.getName().ifPresent((type::setName));
typeUpdate.getDescription().ifPresent(type::setDescription);
typeUpdate.getColour().ifPresent(type::setColour);
return targetTypeRepository.save(type);
}
@Override
public TargetType assignCompatibleDistributionSetTypes(long targetTypeId, Collection<Long> distributionSetTypeIds) {
final Collection<JpaDistributionSetType> dsTypes = distributionSetTypeRepository
.findAllById(distributionSetTypeIds);
if (dsTypes.size() < distributionSetTypeIds.size()) {
throw new EntityNotFoundException(DistributionSetType.class, distributionSetTypeIds,
dsTypes.stream().map(DistributionSetType::getId).collect(Collectors.toList()));
}
final JpaTargetType type = findTargetTypeAndThrowExceptionIfNotFound(targetTypeId);
assertDistributionSetTypeQuota(targetTypeId, distributionSetTypeIds.size());
dsTypes.forEach(type::addCompatibleDistributionSetType);
return targetTypeRepository.save(type);
}
@Override
public TargetType unassignDistributionSetType(long targetTypeId, long distributionSetTypeId) {
final JpaTargetType type = findTargetTypeAndThrowExceptionIfNotFound(targetTypeId);
findDsTypeAndThrowExceptionIfNotFound(distributionSetTypeId);
type.removeDistributionSetType(distributionSetTypeId);
return targetTypeRepository.save(type);
}
private JpaTargetType findTargetTypeAndThrowExceptionIfNotFound(final Long typeId) {
return (JpaTargetType) get(typeId).orElseThrow(() -> new EntityNotFoundException(TargetType.class, typeId));
}
private void findDsTypeAndThrowExceptionIfNotFound(final Long typeId) {
distributionSetTypeRepository.findById(typeId).orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, typeId));
}
private void throwExceptionIfTargetTypeDoesNotExist(final Long typeId) {
if (!targetTypeRepository.existsById(typeId)) {
throw new EntityNotFoundException(TargetType.class, typeId);
}
}
/**
* Enforces the quota specifying the maximum number of
* {@link DistributionSetType}s per {@link TargetType}.
*
* @param id
* of the target type
* @param requested
* number of distribution set types to check
*
* @throws AssignmentQuotaExceededException
* if the software module type quota is exceeded
*/
private void assertDistributionSetTypeQuota(final long id, final int requested) {
QuotaHelper.assertAssignmentQuota(id, requested, quotaManagement.getMaxDistributionSetTypesPerTargetType(),
DistributionSetType.class, TargetType.class, targetTypeRepository::countDsSetTypesById);
}
private static Page<TargetType> convertPage(final Page<JpaTargetType> findAll, final Pageable pageable) {
return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements());
}
private static Slice<TargetType> convertPage(final Slice<JpaTargetType> findAll, final Pageable pageable) {
return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, 0);
}
}

View File

@@ -38,6 +38,7 @@ 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.autoassign.AutoAssignExecutor;
@@ -46,7 +47,9 @@ 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;
import org.eclipse.hawkbit.repository.builder.TargetTypeBuilder;
import org.eclipse.hawkbit.repository.event.ApplicationEventFilter;
import org.eclipse.hawkbit.repository.event.remote.EventEntityManager;
import org.eclipse.hawkbit.repository.event.remote.EventEntityManagerHolder;
@@ -62,7 +65,9 @@ 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.configuration.MultiTenantJpaTransactionManager;
import org.eclipse.hawkbit.repository.jpa.event.JpaEventEntityManager;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitDefaultServiceExecutor;
@@ -85,6 +90,7 @@ import org.eclipse.hawkbit.repository.model.Rollout;
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.EventPublisherHolder;
import org.eclipse.hawkbit.repository.model.helper.SystemManagementHolder;
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
@@ -231,6 +237,21 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
return new JpaDistributionSetBuilder(distributionSetTypeManagement, softwareManagement);
}
@Bean
TargetBuilder targetBuilder(final TargetTypeManagement targetTypeManagement){
return new JpaTargetBuilder(targetTypeManagement);
}
/**
* @param dsTypeManagement
* for loading {@link TargetType#getCompatibleDistributionSetTypes()}
* @return TargetTypeBuilder bean
*/
@Bean
TargetTypeBuilder targetTypeBuilder(final DistributionSetTypeManagement dsTypeManagement) {
return new JpaTargetTypeBuilder(dsTypeManagement);
}
@Bean
SoftwareModuleMetadataBuilder softwareModuleMetadataBuilder(
final SoftwareModuleManagement softwareModuleManagement) {
@@ -465,10 +486,26 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final DistributionSetTypeRepository distributionSetTypeRepository,
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
final DistributionSetRepository distributionSetRepository,
final TargetTypeRepository targetTypeRepository,
final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties,
final QuotaManagement quotaManagement) {
return new JpaDistributionSetTypeManagement(distributionSetTypeRepository, softwareModuleTypeRepository,
distributionSetRepository, virtualPropertyReplacer, properties.getDatabase(), quotaManagement);
distributionSetRepository, targetTypeRepository, virtualPropertyReplacer, properties.getDatabase(), quotaManagement);
}
/**
* {@link JpaTargetTypeManagement} bean.
*
* @return a new {@link TargetTypeManagement}
*/
@Bean
@ConditionalOnMissingBean
TargetTypeManagement targetTypeManagement(final TargetTypeRepository targetTypeRepository,
final TargetRepository targetRepository, final DistributionSetTypeRepository distributionSetTypeRepository,
final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties,
final QuotaManagement quotaManagement) {
return new JpaTargetTypeManagement(targetTypeRepository, targetRepository, distributionSetTypeRepository,
virtualPropertyReplacer, properties.getDatabase(), quotaManagement);
}
/**
@@ -504,11 +541,11 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final TargetRepository targetRepository, final TargetMetadataRepository targetMetadataRepository,
final RolloutGroupRepository rolloutGroupRepository,
final DistributionSetRepository distributionSetRepository,
final TargetFilterQueryRepository targetFilterQueryRepository,
final TargetFilterQueryRepository targetFilterQueryRepository, final TargetTypeRepository targetTypeRepository,
final TargetTagRepository targetTagRepository, final EventPublisherHolder eventPublisherHolder,
final TenantAware tenantAware, final AfterTransactionCommitExecutor afterCommit,
final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties) {
return new JpaTargetManagement(entityManager, quotaManagement, targetRepository, targetMetadataRepository,
return new JpaTargetManagement(entityManager, quotaManagement, targetRepository, targetTypeRepository, targetMetadataRepository,
rolloutGroupRepository, distributionSetRepository, targetFilterQueryRepository, targetTagRepository,
eventPublisherHolder, tenantAware, afterCommit, virtualPropertyReplacer, properties.getDatabase());
}

View File

@@ -20,6 +20,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.domain.Page;
@@ -200,6 +201,15 @@ public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>,
return this.count(Specification.where(null));
}
/**
* Counts {@link Target} instances of given type in the repository.
*
* @param targetTypeId
* to search for
* @return number of found {@link Target}s
*/
long countByTargetTypeId(Long targetTypeId);
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant

View File

@@ -0,0 +1,186 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetTypeSpecification;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.lang.NonNull;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
/**
* {@link PagingAndSortingRepository} for {@link JpaTargetType}.
*
*/
@Transactional(readOnly = true)
public interface TargetTypeRepository
extends BaseEntityRepository<JpaTargetType, Long>, JpaSpecificationExecutor<JpaTargetType> {
/**
* Finds {@link TargetType} in the repository matching an id.
*
* Calls version based on spec to allow injecting further specs
*
* @param id id to filter for
* @return {@link Optional} of {@link TargetType}
*/
@Override
@NonNull
default Optional<JpaTargetType> findById(@NonNull Long id) {
return this.findOne(Specification.where(TargetTypeSpecification.hasId(id)));
}
/**
* @param ids
* List of ID
* @return Target type list
*/
@Override
@NonNull
default List<JpaTargetType> findAllById(Iterable<Long> ids) {
final List<Long> collectedIds = StreamSupport.stream(ids.spliterator(), true).collect(Collectors.toList());
return this.findAll(Specification.where(TargetTypeSpecification.hasIdIn(collectedIds)));
}
/**
* Deletes the {@link TargetType}s with the given target IDs.
*
* @param targetTypeIDs
* to be deleted
*/
@Modifying
@Transactional
@Query("DELETE FROM JpaTargetType t WHERE t.id IN ?1")
void deleteByIdIn(Collection<Long> targetTypeIDs);
/**
* Finds all {@link TargetType}s in the repository.
*
* Calls version with (empty) spec to allow injecting further specs
*
* @return {@link List} of {@link TargetType}s
*/
@Override
@NonNull
default List<JpaTargetType> findAll() {
return this.findAll(Specification.where(null));
}
/**
* Finds all {@link TargetType}s in the repository sorted.
*
* Calls version with (empty) spec to allow injecting further specs
*
* @param sort instructions to sort result by
* @return {@link List} of {@link TargetType}s
*/
@Override
@NonNull
default Iterable<JpaTargetType> findAll(@NonNull Sort sort) {
return this.findAll(Specification.where(null), sort);
}
/**
* Finds a page of {@link TargetType}s in the repository.
*
* Calls version with (empty) spec to allow injecting further specs
*
* @param pageable paging context
* @return {@link List} of {@link TargetType}s
*/
@Override
@NonNull
default Page<JpaTargetType> findAll(@NonNull Pageable pageable) {
return this.findAll(Specification.where(null), pageable);
}
/**
* Checks whether {@link TargetType} in the repository matching an id exists or not.
*
* Calls version based on spec to allow injecting further specs
*
* @param id id to check for
* @return true if TargetType with id exists
*/
@Override
default boolean existsById(@NonNull Long id) {
return this.exists(TargetTypeSpecification.hasId(id));
}
/**
* Checks whether {@link TargetType} in the repository matching a spec exists or not.
*
* @param spec to check for existence
* @return true if target with id exists
*/
default boolean exists(@NonNull Specification<JpaTargetType> spec) {
return this.count(spec) > 0;
}
/**
* Count number of {@link TargetType}s in the repository.
*
* Calls version with an empty spec to allow injecting further specs
*
* @return number of targetTypes in the repository
*/
@Override
default long count() {
return this.count(Specification.where(null));
}
/**
* @param tenant
* Tenant
*/
@Modifying
@Transactional
@Query("DELETE FROM JpaTargetType t WHERE t.tenant = :tenant")
void deleteByTenant(@Param("tenant") String tenant);
@Query(value = "SELECT COUNT (t.id) FROM JpaDistributionSetType t JOIN t.compatibleToTargetTypes tt WHERE tt.id = :id")
long countDsSetTypesById(@Param("id") Long id);
/**
*
* @param dsTypeId
* to search for
* @return all {@link TargetType}s in the repository with given
* {@link TargetType#getName()}
*/
default List<JpaTargetType> findByDsType(@Param("id") Long dsTypeId) {
return this.findAll(Specification.where(TargetTypeSpecification.hasDsSetType(dsTypeId)));
}
/**
*
* @param name
* to search for
* @return all {@link TargetType}s in the repository with given
* {@link TargetType#getName()}
*/
default Optional<JpaTargetType> findByName(String name){
return this.findOne(Specification.where(TargetTypeSpecification.hasName(name)));
};
}

View File

@@ -8,15 +8,26 @@
*/
package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.builder.TargetBuilder;
import org.eclipse.hawkbit.repository.builder.TargetCreate;
import org.eclipse.hawkbit.repository.builder.TargetUpdate;
import org.eclipse.hawkbit.repository.model.Target;
/**
* Builder implementation for {@link Target}.
*
*/
public class JpaTargetBuilder implements TargetBuilder {
final private TargetTypeManagement targetTypeManagement;
/**
* @param targetTypeManagement
* Target type management
*/
public JpaTargetBuilder(TargetTypeManagement targetTypeManagement) {
this.targetTypeManagement = targetTypeManagement;
}
@Override
public TargetUpdate update(final String controllerId) {
@@ -25,7 +36,7 @@ public class JpaTargetBuilder implements TargetBuilder {
@Override
public TargetCreate create() {
return new JpaTargetCreate();
return new JpaTargetCreate(targetTypeManagement);
}
}

View File

@@ -8,9 +8,12 @@
*/
package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.builder.AbstractTargetUpdateCreate;
import org.eclipse.hawkbit.repository.builder.TargetCreate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.springframework.util.StringUtils;
@@ -20,8 +23,17 @@ import org.springframework.util.StringUtils;
*/
public class JpaTargetCreate extends AbstractTargetUpdateCreate<TargetCreate> implements TargetCreate {
JpaTargetCreate() {
private final TargetTypeManagement targetTypeManagement;
/**
* Constructor
*
* @param targetTypeManagement
* Target type management
*/
JpaTargetCreate(final TargetTypeManagement targetTypeManagement) {
super(null);
this.targetTypeManagement = targetTypeManagement;
}
@Override
@@ -38,6 +50,12 @@ public class JpaTargetCreate extends AbstractTargetUpdateCreate<TargetCreate> im
target.setName(name);
}
if (targetTypeId != null){
TargetType targetType = targetTypeManagement.get(targetTypeId)
.orElseThrow(() -> new EntityNotFoundException(TargetType.class, targetTypeId));
target.setTargetType(targetType);
}
target.setDescription(description);
target.setAddress(address);
target.setUpdateStatus(getStatus().orElse(TargetUpdateStatus.UNKNOWN));

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.builder.GenericTargetTypeUpdate;
import org.eclipse.hawkbit.repository.builder.TargetTypeBuilder;
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
import org.eclipse.hawkbit.repository.builder.TargetTypeUpdate;
import org.eclipse.hawkbit.repository.model.TargetType;
/**
* Builder implementation for {@link TargetType}.
*
*/
public class JpaTargetTypeBuilder implements TargetTypeBuilder {
private final DistributionSetTypeManagement distributionSetTypeManagement;
/**
* Constructor
*
* @param distributionSetTypeManagement
* Distribution set type management
*/
public JpaTargetTypeBuilder(DistributionSetTypeManagement distributionSetTypeManagement) {
this.distributionSetTypeManagement = distributionSetTypeManagement;
}
@Override
public TargetTypeUpdate update(long id) {
return new GenericTargetTypeUpdate(id);
}
@Override
public TargetTypeCreate create() {
return new JpaTargetTypeCreate(distributionSetTypeManagement);
}
}

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.builder.AbstractTargetTypeUpdateCreate;
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.Collections;
/**
* Create/build implementation.
*
*/
public class JpaTargetTypeCreate extends AbstractTargetTypeUpdateCreate<TargetTypeCreate>
implements TargetTypeCreate {
private final DistributionSetTypeManagement distributionSetTypeManagement;
/**
* Constructor
*
* @param distributionSetTypeManagement
* Distribution set type management
*/
JpaTargetTypeCreate(final DistributionSetTypeManagement distributionSetTypeManagement) {
this.distributionSetTypeManagement = distributionSetTypeManagement;
}
@Override
public JpaTargetType build() {
final JpaTargetType result = new JpaTargetType(name, description, colour);
findDistributionSetTypeWithExceptionIfNotFound(compatible).forEach(result::addCompatibleDistributionSetType);
return result;
}
private Collection<DistributionSetType> findDistributionSetTypeWithExceptionIfNotFound(
final Collection<Long> distributionSetTypeId) {
if (CollectionUtils.isEmpty(distributionSetTypeId)) {
return Collections.emptyList();
}
final Collection<DistributionSetType> type = distributionSetTypeManagement.get(distributionSetTypeId);
if (type.size() < distributionSetTypeId.size()) {
throw new EntityNotFoundException(SoftwareModuleType.class, distributionSetTypeId);
}
return type;
}
}

View File

@@ -8,24 +8,6 @@
*/
package org.eclipse.hawkbit.repository.jpa.model;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Index;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.eclipse.hawkbit.repository.event.remote.DistributionSetTypeDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTypeCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTypeUpdatedEvent;
@@ -33,11 +15,30 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.springframework.util.CollectionUtils;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Index;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
* A distribution set type defines which software module types can or have to be
* {@link DistributionSet}.
@@ -72,6 +73,10 @@ public class JpaDistributionSetType extends AbstractJpaNamedEntity implements Di
@Column(name = "deleted")
private boolean deleted;
@CascadeOnDelete
@ManyToMany(mappedBy = "distributionSetTypes", targetEntity = JpaTargetType.class, fetch = FetchType.LAZY)
private List<TargetType> compatibleToTargetTypes;
public JpaDistributionSetType() {
// default public constructor for JPA
}

View File

@@ -55,6 +55,7 @@ import org.eclipse.hawkbit.repository.model.PollStatus;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
@@ -162,6 +163,10 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
@JoinColumn(name = "target_id", nullable = false, updatable = false) }, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target"))
private Map<String, String> controllerAttributes;
@ManyToOne(fetch = FetchType.LAZY, optional = true, targetEntity = JpaTargetType.class)
@JoinColumn(name = "target_type", nullable = true, updatable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_relation_target_type"))
private TargetType targetType;
// set default request controller attributes to true, because we want to
// request them the first
// time
@@ -201,10 +206,16 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
: controllerId;
}
JpaTarget() {
/**
* Constructor
*/
public JpaTarget() {
// empty constructor for JPA.
}
/**
* @return assigned distribution set
*/
public DistributionSet getAssignedDistributionSet() {
return assignedDistributionSet;
}
@@ -214,6 +225,9 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return controllerId;
}
/**
* @return tags
*/
public Set<TargetTag> getTags() {
if (tags == null) {
return Collections.emptySet();
@@ -222,6 +236,9 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return Collections.unmodifiableSet(tags);
}
/**
* @return rollouts target group
*/
public List<RolloutTargetGroup> getRolloutTargetGroup() {
if (rolloutTargetGroup == null) {
return Collections.emptyList();
@@ -230,6 +247,11 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return Collections.unmodifiableList(rolloutTargetGroup);
}
/**
* @param tag
* tag
* @return boolean true or false
*/
public boolean addTag(final TargetTag tag) {
if (tags == null) {
tags = new HashSet<>();
@@ -238,6 +260,11 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return tags.add(tag);
}
/**
* @param tag
* tag
* @return boolean true or false
*/
public boolean removeTag(final TargetTag tag) {
if (tags == null) {
return false;
@@ -246,14 +273,25 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return tags.remove(tag);
}
/**
* @param assignedDistributionSet
* Distribution set
*/
public void setAssignedDistributionSet(final DistributionSet assignedDistributionSet) {
this.assignedDistributionSet = (JpaDistributionSet) assignedDistributionSet;
}
/**
* @param controllerId
* Controller ID
*/
public void setControllerId(final String controllerId) {
this.controllerId = controllerId;
}
/**
* @return list of action
*/
public List<Action> getActions() {
if (actions == null) {
return Collections.emptyList();
@@ -262,6 +300,11 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return Collections.unmodifiableList(actions);
}
/**
* @param action
* Action
* @return boolean true or false
*/
public boolean addAction(final Action action) {
if (actions == null) {
actions = new ArrayList<>();
@@ -285,6 +328,10 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return null;
}
/**
* @param securityToken
* token value
*/
public void setSecurityToken(final String securityToken) {
this.securityToken = securityToken;
}
@@ -348,10 +395,29 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return updateStatus;
}
@Override
public TargetType getTargetType() {
return targetType;
}
/**
* @param type
* Target type
*/
public void setTargetType(final TargetType type) {
this.targetType = type;
}
/**
* @return Distribution set
*/
public JpaDistributionSet getInstalledDistributionSet() {
return installedDistributionSet;
}
/**
* @return controller attributes
*/
public Map<String, String> getControllerAttributes() {
return controllerAttributes;
}
@@ -361,6 +427,9 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
return requestControllerAttributes;
}
/**
* @return target metadata
*/
public List<TargetMetadata> getMetadata() {
if (metadata == null) {
return Collections.emptyList();
@@ -375,26 +444,50 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
+ "]";
}
/**
* @param address
* Address
*/
public void setAddress(final String address) {
this.address = address;
}
/**
* @param lastTargetQuery
* last query ID
*/
public void setLastTargetQuery(final Long lastTargetQuery) {
this.lastTargetQuery = lastTargetQuery;
}
/**
* @param installationDate
* installation date
*/
public void setInstallationDate(final Long installationDate) {
this.installationDate = installationDate;
}
/**
* @param installedDistributionSet
* Distribution set
*/
public void setInstalledDistributionSet(final JpaDistributionSet installedDistributionSet) {
this.installedDistributionSet = installedDistributionSet;
}
/**
* @param updateStatus
* Status
*/
public void setUpdateStatus(final TargetUpdateStatus updateStatus) {
this.updateStatus = updateStatus;
}
/**
* @param requestControllerAttributes
* Attributes
*/
public void setRequestControllerAttributes(final boolean requestControllerAttributes) {
this.requestControllerAttributes = requestControllerAttributes;
}

View File

@@ -0,0 +1,156 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa.model;
import org.eclipse.hawkbit.repository.event.remote.TargetTypeDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeUpdatedEvent;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.Size;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* A target type defines which distribution set types can or have to be
* {@link Target}.
*
*/
@Entity
@Table(name = "sp_target_type", indexes = {
@Index(name = "sp_idx_target_type_prim", columnList = "tenant,id") }, uniqueConstraints = {
@UniqueConstraint(columnNames = { "name", "tenant" }, name = "uk_target_type_name")})
public class JpaTargetType extends AbstractJpaNamedEntity implements TargetType, EventAwareEntity{
private static final long serialVersionUID = 1L;
@Column(name = "colour", nullable = true, length = TargetType.COLOUR_MAX_SIZE)
@Size(max = TargetType.COLOUR_MAX_SIZE)
private String colour;
@CascadeOnDelete
@ManyToMany(targetEntity = JpaDistributionSetType.class)
@JoinTable(name = "sp_target_type_ds_type_relation", joinColumns = {
@JoinColumn(name = "target_type", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_type_relation_target_type"))}, inverseJoinColumns = {
@JoinColumn(name = "distribution_set_type", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_type_relation_ds_type"))})
private Set<DistributionSetType> distributionSetTypes;
@OneToMany(targetEntity = JpaTarget.class, mappedBy = "targetType", fetch = FetchType.LAZY)
private Set<Target> targets;
/**
* Constructor
*/
public JpaTargetType() {
// default public constructor for JPA
}
/**
* Constructor
*
* @param name
* Type name
* @param description
* Description
* @param colour
* Colour
*/
public JpaTargetType(String name, String description, String colour) {
super(name,description);
this.colour = colour;
}
/**
* @param dsSetType
* Distribution set type
* @return Target type
*/
public JpaTargetType addCompatibleDistributionSetType(final DistributionSetType dsSetType) {
if (distributionSetTypes == null) {
distributionSetTypes = new HashSet<>();
}
distributionSetTypes.add(dsSetType);
return this;
}
/**
* @param dsTypeId
* Distribution set type ID
* @return Target type
*/
public JpaTargetType removeDistributionSetType(final Long dsTypeId) {
if (distributionSetTypes == null) {
return this;
}
distributionSetTypes.stream().filter(element -> element.getId().equals(dsTypeId)).findAny()
.ifPresent(distributionSetTypes::remove);
return this;
}
@Override
public Set<DistributionSetType> getCompatibleDistributionSetTypes() {
if (distributionSetTypes == null) {
return Collections.emptySet();
}
return Collections.unmodifiableSet(distributionSetTypes);
}
@Override
public Set<Target> getTargets() {
return targets;
}
@Override
public String getColour() {
return colour;
}
public void setColour(final String colour) {
this.colour = colour;
}
@Override
public void fireCreateEvent(DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(
new TargetTypeCreatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
}
@Override
public void fireUpdateEvent(DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(
new TargetTypeUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
}
@Override
public void fireDeleteEvent(DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new TargetTypeDeletedEvent(
getTenant(), getId(), getClass().getName(), EventPublisherHolder.getInstance().getApplicationId()));
}
}

View File

@@ -0,0 +1,149 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa.specifications;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.SetJoin;
import java.util.Collection;
/**
* Specifications class for {@link TargetType}s. The class provides Spring Data JPQL
* Specifications.
*
*/
public final class TargetTypeSpecification {
private TargetTypeSpecification() {
// utility class
}
/**
* {@link Specification} for retrieving {@link TargetType}s by controllerId
*
* @param id
* to search for
*
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasId(final Long id) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaTargetType_.id), id);
}
/**
* {@link Specification} for retrieving {@link TargetType}s by controllerId
*
* @param id
* to search for
*
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasTarget(final long id) {
return (targetRoot, query, cb) -> {
final SetJoin<JpaTargetType, JpaTarget> join = targetRoot.join(JpaTargetType_.targets);
return cb.equal(join.get(JpaTarget_.id), id);
};
}
/**
* {@link Specification} for retrieving {@link TargetType}s by controllerId
*
* @param ids
* to search for
*
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasTarget(final Collection<Long> ids) {
return (targetRoot, query, cb) -> {
final SetJoin<JpaTargetType, JpaTarget> join = targetRoot.join(JpaTargetType_.targets);
return join.get(JpaTarget_.id).in(ids);
};
}
/**
* {@link Specification} for retrieving {@link TargetType}s by controllerId
*
* @param controllerId
* to search for
*
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasTargetControllerId(final String controllerId) {
return (targetRoot, query, cb) -> {
final SetJoin<JpaTargetType, JpaTarget> join = targetRoot.join(JpaTargetType_.targets);
return cb.equal(join.get(JpaTarget_.controllerId), controllerId);
};
}
/**
* {@link Specification} for retrieving {@link TargetType}s by controllerId
*
* @param controllerIds
* to search for
*
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasTargetControllerIdIn(final Collection<String> controllerIds) {
return (targetRoot, query, cb) -> {
final SetJoin<JpaTargetType, JpaTarget> join = targetRoot.join(JpaTargetType_.targets);
return join.get(JpaTarget_.controllerId).in(controllerIds);
};
}
/**
* {@link Specification} for retrieving {@link TargetType}s by controllerId
*
* @param ids
* to search for
*
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasIdIn(final Collection<Long> ids) {
return (targetRoot, query, cb) -> targetRoot.get(JpaTargetType_.id).in(ids);
}
/**
* {@link Specification} for retrieving {@link TargetType}s based on a
* {@link DistributionSetType} name.
*
* @param dsTypeId
* to search for
*
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasDsSetType(final Long dsTypeId) {
return (targetRoot, query, cb) -> {
final SetJoin<JpaTargetType, JpaDistributionSetType> join = targetRoot.join(JpaTargetType_.distributionSetTypes);
return cb.equal(join.get(JpaDistributionSetType_.id), dsTypeId);
};
}
/**
* {@link Specification} for retrieving {@link TargetType} with
* given {@link TargetType#getName()} including fetching the
* elements list.
*
* @param name
* to search
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasName(final String name) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaTargetType_.name), name);
}
}

View File

@@ -0,0 +1,32 @@
CREATE TABLE sp_target_type
(
id BIGINT GENERATED always AS IDENTITY NOT NULL,
tenant VARCHAR(40) NOT NULL,
colour VARCHAR(16),
created_at BIGINT NOT NULL,
created_by VARCHAR(64) NOT NULL,
description VARCHAR(512),
last_modified_at BIGINT NOT NULL,
last_modified_by VARCHAR(64) NOT NULL,
name VARCHAR(64) NOT NULL,
optlock_revision INTEGER,
PRIMARY KEY (id)
);
CREATE INDEX sp_idx_target_type_prim
ON sp_target_type (tenant, id);
CREATE TABLE sp_target_type_ds_type_relation
(
target_type BIGINT NOT NULL,
distribution_set_type BIGINT NOT NULL,
PRIMARY KEY (target_type, distribution_set_type)
);
ALTER TABLE sp_target_type ADD CONSTRAINT uk_target_type_name UNIQUE (name, tenant);
ALTER TABLE sp_target ADD COLUMN target_type BIGINT;
ALTER TABLE sp_target ADD CONSTRAINT fk_target_relation_target_type FOREIGN KEY (target_type) REFERENCES sp_target_type (id) ON DELETE SET NULL;
ALTER TABLE sp_target_type_ds_type_relation ADD CONSTRAINT fk_target_type_relation_target_type FOREIGN KEY (target_type) REFERENCES sp_target_type (id) ON DELETE CASCADE;
ALTER TABLE sp_target_type_ds_type_relation ADD CONSTRAINT fk_target_type_relation_ds_type FOREIGN KEY (distribution_set_type) REFERENCES sp_distribution_set_type (id) ON DELETE CASCADE;

View File

@@ -0,0 +1,50 @@
create table sp_target_type
(
id bigint generated by default as identity,
created_at bigint,
created_by varchar(64),
last_modified_at bigint,
last_modified_by varchar(64),
optlock_revision bigint,
tenant varchar(40) not null,
description varchar(512),
name varchar(64) not null,
colour varchar(16),
primary key (id)
);
create table sp_target_type_ds_type_relation
(
target_type bigint not null,
distribution_set_type bigint not null,
primary key (target_type, distribution_set_type)
);
alter table sp_target_type
add constraint uk_target_type_name unique (name, tenant);
create index sp_idx_target_type_prim on sp_target_type (tenant, id);
alter table sp_target
add column target_type bigint;
alter table sp_target
add constraint fk_target_relation_target_type
foreign key (target_type)
references sp_target_type
on delete set null;
alter table sp_target_type_ds_type_relation
add constraint fk_target_type_relation_target_type
foreign key (target_type)
references sp_target_type
on delete cascade;
alter table sp_target_type_ds_type_relation
add constraint fk_target_type_relation_ds_type
foreign key (distribution_set_type)
references sp_distribution_set_type
on delete cascade;

View File

@@ -0,0 +1,47 @@
create table sp_target_type
(
id bigint not null auto_increment,
created_at bigint,
created_by varchar(64),
last_modified_at bigint,
last_modified_by varchar(64),
optlock_revision bigint,
tenant varchar(40) not null,
description varchar(512),
name varchar(64) not null,
colour varchar(16),
primary key (id)
);
create table sp_target_type_ds_type_relation
(
target_type bigint not null,
distribution_set_type bigint not null,
primary key (target_type, distribution_set_type)
);
alter table sp_target_type
add constraint uk_target_type_name unique (name, tenant);
create index sp_idx_target_type_prim on sp_target_type (tenant, id);
alter table sp_target
add column target_type bigint;
alter table sp_target
add constraint fk_target_relation_target_type
foreign key (target_type)
references sp_target_type (id)
on delete set null;
alter table sp_target_type_ds_type_relation
add constraint fk_target_type_relation_target_type
foreign key (target_type)
references sp_target_type (id)
on delete cascade;
alter table sp_target_type_ds_type_relation
add constraint fk_target_type_relation_ds_type
foreign key (distribution_set_type)
references sp_distribution_set_type (id)
on delete cascade;

View File

@@ -0,0 +1,65 @@
-- ------------ Write CREATE-SEQUENCE-stage scripts -----------
CREATE SEQUENCE IF NOT EXISTS sp_target_type_seq
INCREMENT BY 1
START WITH 1
NO CYCLE;
-- ------------ Write CREATE-TABLE-stage scripts -----------
CREATE TABLE sp_target_type(
id BIGINT NOT NULL DEFAULT nextval('sp_target_type_seq'),
created_at BIGINT,
created_by VARCHAR(64),
last_modified_at BIGINT,
last_modified_by VARCHAR(64),
optlock_revision BIGINT,
tenant VARCHAR(40) NOT NULL,
description VARCHAR(512),
name VARCHAR(64),
colour VARCHAR(16)
)
WITH (
OIDS=FALSE
);
CREATE TABLE sp_target_type_ds_type_relation(
target_type BIGINT NOT NULL,
distribution_set_type BIGINT NOT NULL
)
WITH (
OIDS=FALSE
);
-- ------------ Alter Table and Write INDEX scripts -----------
ALTER TABLE sp_target_type
ADD CONSTRAINT pk_sp_target_type PRIMARY KEY (id);
ALTER TABLE sp_target_type
ADD CONSTRAINT uk_target_type_name UNIQUE (name, tenant);
CREATE INDEX sp_idx_target_type_prim
ON sp_target_type
USING BTREE (tenant, id);
ALTER TABLE sp_target
ADD COLUMN target_type BIGINT;
ALTER TABLE sp_target
ADD CONSTRAINT fk_target_relation_target_type FOREIGN KEY (target_type)
REFERENCES sp_target_type (id)
ON UPDATE RESTRICT
ON DELETE SET NULL;
ALTER TABLE sp_target_type_ds_type_relation
ADD CONSTRAINT fk_target_type_relation_target_type FOREIGN KEY (target_type)
REFERENCES sp_target_type (id)
ON UPDATE RESTRICT
ON DELETE CASCADE;
ALTER TABLE sp_target_type_ds_type_relation
ADD CONSTRAINT fk_target_type_relation_ds_type FOREIGN KEY (distribution_set_type)
REFERENCES sp_distribution_set_type (id)
ON UPDATE RESTRICT
ON DELETE CASCADE;

View File

@@ -0,0 +1,27 @@
CREATE TABLE sp_target_type
(
id NUMERIC(19) IDENTITY NOT NULL,
tenant VARCHAR(40) NOT NULL,
colour VARCHAR(16) NULL,
created_at NUMERIC(19) NOT NULL,
created_by VARCHAR(64) NOT NULL,
description VARCHAR(512) NULL,
last_modified_at NUMERIC(19) NOT NULL,
last_modified_by VARCHAR(64) NOT NULL,
name VARCHAR(64) NOT NULL,
optlock_revision INTEGER NULL,
PRIMARY KEY (id)
);
CREATE INDEX sp_idx_target_type_prim ON sp_target_type (tenant, id);
CREATE TABLE sp_target_type_ds_type_relation
(
target_type NUMERIC(19) NOT NULL,
distribution_set_type NUMERIC(19) NOT NULL,
PRIMARY KEY (target_type, distribution_set_type)
);
ALTER TABLE sp_target_type ADD CONSTRAINT uk_target_type_name UNIQUE (name, tenant);
ALTER TABLE sp_target ADD target_type NUMERIC(19) NULL;
ALTER TABLE sp_target ADD CONSTRAINT fk_target_relation_target_type FOREIGN KEY (target_type) REFERENCES sp_target_type (id) ON DELETE SET NULL;
ALTER TABLE sp_target_type_ds_type_relation ADD CONSTRAINT fk_target_type_relation_target_type FOREIGN KEY (target_type) REFERENCES sp_target_type (id) ON DELETE CASCADE;
ALTER TABLE sp_target_type_ds_type_relation ADD CONSTRAINT fk_target_type_relation_ds_type FOREIGN KEY (distribution_set_type) REFERENCES sp_distribution_set_type (id) ON DELETE CASCADE;

View File

@@ -76,6 +76,9 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
@Autowired
protected TargetTagRepository targetTagRepository;
@Autowired
protected TargetTypeRepository targetTypeRepository;
@Autowired
protected DistributionSetTagRepository distributionSetTagRepository;

View File

@@ -20,6 +20,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.validation.ConstraintViolationException;
@@ -27,6 +28,7 @@ import javax.validation.ConstraintViolationException;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.FilterParams;
import org.eclipse.hawkbit.repository.builder.TargetUpdate;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
@@ -53,6 +55,7 @@ import org.eclipse.hawkbit.repository.model.Tag;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
@@ -1038,6 +1041,67 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
assertThat(updated.getTarget().getId()).isEqualTo(target.getId());
}
@Test
@WithUser(allSpPermissions = true)
@Description("Checks that target type for a target can be created, updated and unassigned.")
public void createAndUpdateTargetTypeInTarget() {
// create a target type
List<TargetType> targetTypes = testdataFactory.createTargetTypes("targettype", 2);
assertThat(targetTypes).hasSize(2);
// create a target
final Target target = testdataFactory.createTarget("target1", "testtarget", targetTypes.get(0).getId());
// initial opt lock revision must be one
Optional<JpaTarget> targetFound = targetRepository.findById(target.getId());
assertThat(targetFound).isPresent();
assertThat(targetFound.get().getOptLockRevision()).isEqualTo(1);
assertThat(targetFound.get().getTargetType().getId()).isEqualTo(targetTypes.get(0).getId());
// update the target type
TargetUpdate targetUpdate = entityFactory.target().update(target.getControllerId()).targetType(targetTypes.get(1).getId());
targetManagement.update(targetUpdate);
// opt lock revision must be changed
Optional<JpaTarget> targetFound1 = targetRepository.findById(target.getId());
assertThat(targetFound1).isPresent();
assertThat(targetFound1.get().getOptLockRevision()).isEqualTo(2);
assertThat(targetFound1.get().getTargetType().getId()).isEqualTo(targetTypes.get(1).getId());
// unassign the target type
targetManagement.unAssignType(target.getControllerId());
// opt lock revision must be changed
Optional<JpaTarget> targetFound2 = targetRepository.findById(target.getId());
assertThat(targetFound2).isPresent();
assertThat(targetFound2.get().getOptLockRevision()).isEqualTo(3);
assertThat(targetFound2.get().getTargetType()).isNull();
}
@Test
@WithUser(allSpPermissions = true)
@Description("Checks that target type to a target can be assigned.")
public void assignTargetTypeInTarget() {
// create a target
final Target target = testdataFactory.createTarget("target1", "testtarget");
// initial opt lock revision must be one
Optional<JpaTarget> targetFound = targetRepository.findById(target.getId());
assertThat(targetFound).isPresent();
assertThat(targetFound.get().getOptLockRevision()).isEqualTo(1);
assertThat(targetFound.get().getTargetType()).isNull();
// create a target type
TargetType targetType = testdataFactory.findOrCreateTargetType("targettype");
assertThat(targetType).isNotNull();
// assign target type to target
targetManagement.assignType(targetFound.get().getControllerId(), targetType.getId());
// opt lock revision must be changed
Optional<JpaTarget> targetFound1 = targetRepository.findById(target.getId());
assertThat(targetFound1).isPresent();
assertThat(targetFound1.get().getOptLockRevision()).isEqualTo(2);
assertThat(targetFound1.get().getTargetType().getId()).isEqualTo(targetType.getId());
}
@Test
@Description("Queries and loads the metadata related to a given target.")
public void findAllTargetMetadataByControllerId() {

View File

@@ -0,0 +1,230 @@
/**
* Copyright (c) 2021 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Step;
import io.qameta.allure.Story;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.NamedEntity;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.junit.jupiter.api.Test;
import javax.validation.ConstraintViolationException;
import java.util.Collections;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.junit.jupiter.api.Assertions.assertThrows;
@Feature("Component Tests - Repository")
@Story("Target Type Management")
public class TargetTypeManagementTest extends AbstractJpaIntegrationTest{
@Test
@Description("Verifies that management get access react as specified on calls for non existing entities by means "
+ "of Optional not present.")
@ExpectEvents({ @Expect(type = TargetTypeCreatedEvent.class, count = 0) })
public void nonExistingEntityAccessReturnsNotPresent() {
assertThat(targetTypeManagement.get(NOT_EXIST_IDL)).isNotPresent();
assertThat(targetTypeManagement.getByName(NOT_EXIST_ID)).isNotPresent();
}
@Test
@Description("Verifies that management queries react as specified on calls for non existing entities "
+ " by means of throwing EntityNotFoundException.")
@ExpectEvents({ @Expect(type = TargetTypeUpdatedEvent.class, count = 0) })
public void entityQueriesReferringToNotExistingEntitiesThrowsException() {
verifyThrownExceptionBy(() -> targetTypeManagement.delete(NOT_EXIST_IDL), "TargetType");
verifyThrownExceptionBy(() -> targetTypeManagement.update(entityFactory.targetType().update(NOT_EXIST_IDL)),
"TargetType");
}
@Test
@Description("Verify that a target type with invalid properties cannot be created or updated")
public void createAndUpdateTargetTypeWithInvalidFields() {
final TargetType targetType = targetTypeManagement
.create(entityFactory.targetType().create().name("targettype1").description("targettypedes1"));
createAndUpdateTargetTypeWithInvalidDescription(targetType);
createAndUpdateTargetTypeWithInvalidColour(targetType);
createAndUpdateTargetTypeWithInvalidName(targetType);
}
@Step
private void createAndUpdateTargetTypeWithInvalidDescription(final TargetType targetType) {
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement.create(
entityFactory.targetType().create().name("a").description(RandomStringUtils.randomAlphanumeric(TargetType.DESCRIPTION_MAX_SIZE + 1))))
.as("targetType with too long description should not be created");
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
() -> targetTypeManagement.create(entityFactory.targetType().create().name("a").description(INVALID_TEXT_HTML)))
.as("targetType with invalid description should not be created");
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement.update(
entityFactory.targetType().update(targetType.getId()).description(RandomStringUtils.randomAlphanumeric(TargetType.DESCRIPTION_MAX_SIZE + 1))))
.as("targetType with too long description should not be updated");
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement
.update(entityFactory.targetType().update(targetType.getId()).description(INVALID_TEXT_HTML)))
.as("targetType with invalid description should not be updated");
}
@Step
private void createAndUpdateTargetTypeWithInvalidColour(final TargetType targetType) {
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement.create(
entityFactory.targetType().create().name("a").colour(RandomStringUtils.randomAlphanumeric(TargetType.COLOUR_MAX_SIZE + 1))))
.as("targetType with too long colour should not be created");
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
() -> targetTypeManagement.create(entityFactory.targetType().create().name("a").colour(INVALID_TEXT_HTML)))
.as("targetType with invalid colour should not be created");
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement.update(
entityFactory.targetType().update(targetType.getId()).colour(RandomStringUtils.randomAlphanumeric(TargetType.COLOUR_MAX_SIZE + 1))))
.as("targetType with too long colour should not be updated");
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
() -> targetTypeManagement.update(entityFactory.targetType().update(targetType.getId()).colour(INVALID_TEXT_HTML)))
.as("targetType with invalid colour should not be updated");
}
@Step
private void createAndUpdateTargetTypeWithInvalidName(final TargetType targetType) {
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement
.create(entityFactory.targetType().create().name(RandomStringUtils.randomAlphanumeric(
NamedEntity.NAME_MAX_SIZE + 1))))
.as("targetType with too long name should not be created");
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement.create(entityFactory.targetType().create().name(INVALID_TEXT_HTML)))
.as("targetType with invalid name should not be created");
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement
.update(entityFactory.targetType().update(targetType.getId()).name(RandomStringUtils.randomAlphanumeric(
NamedEntity.NAME_MAX_SIZE + 1))))
.as("targetType with too long name should not be updated");
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
() -> targetTypeManagement.update(entityFactory.targetType().update(targetType.getId()).name(INVALID_TEXT_HTML)))
.as("targetType with invalid name should not be updated");
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetTypeManagement.update(entityFactory.targetType().update(targetType.getId()).name("")))
.as("targetType with too short name should not be updated");
}
@Test
@Description("Tests the successful assignment of compatible distribution set types to a target type")
public void assignCompatibleDistributionSetTypesToTargetType(){
final TargetType targetType = targetTypeManagement
.create(entityFactory.targetType().create().name("targettype1").description("targettypedes1"));
DistributionSetType distributionSetType = testdataFactory.findOrCreateDistributionSetType("testDst", "dst1");
targetTypeManagement.assignCompatibleDistributionSetTypes(targetType.getId(), Collections.singletonList(distributionSetType.getId()));
Optional<JpaTargetType> targetTypeWithDsTypes = targetTypeRepository.findById(targetType.getId());
assertThat(targetTypeWithDsTypes).isPresent();
assertThat(targetTypeWithDsTypes.get().getCompatibleDistributionSetTypes()).extracting("key").contains("testDst");
}
@Test
@Description("Tests the successful removal of compatible distribution set types to a target type")
public void unassignCompatibleDistributionSetTypesToTargetType(){
final TargetType targetType = targetTypeManagement
.create(entityFactory.targetType().create().name("targettype11").description("targettypedes11"));
DistributionSetType distributionSetType = testdataFactory.findOrCreateDistributionSetType("testDst1", "dst11");
targetTypeManagement.assignCompatibleDistributionSetTypes(targetType.getId(), Collections.singletonList(distributionSetType.getId()));
Optional<JpaTargetType> targetTypeWithDsTypes = targetTypeRepository.findById(targetType.getId());
assertThat(targetTypeWithDsTypes).isPresent();
assertThat(targetTypeWithDsTypes.get().getCompatibleDistributionSetTypes()).extracting("key").contains("testDst1");
targetTypeManagement.unassignDistributionSetType(targetType.getId(),distributionSetType.getId());
Optional<JpaTargetType> targetTypeWithDsTypes1 = targetTypeRepository.findById(targetType.getId());
assertThat(targetTypeWithDsTypes1).isPresent();
assertThat(targetTypeWithDsTypes1.get().getCompatibleDistributionSetTypes()).hasSize(0);
}
@Test
@Description("Ensures that all types are retrieved through repository.")
public void findAllTargetTypes() {
testdataFactory.createTargetTypes("targettype", 10);
assertThat(targetTypeRepository.findAll()).as("Target type size").hasSize(10);
}
@Test
@Description("Ensures that a created target type is persisted in the repository as defined.")
public void createTargetType() {
final TargetType targetType = targetTypeManagement
.create(entityFactory.targetType().create().name("targettype1").description("targettypedes1").colour("colour1"));
assertThat(targetTypeRepository.findByName("targettype1").get().getDescription()).as("type found")
.isEqualTo("targettypedes1");
assertThat(targetTypeManagement.getByName("targettype1").get().getColour()).as("type found").isEqualTo("colour1");
assertThat(targetTypeManagement.get(targetType.getId()).get().getColour()).as("type found").isEqualTo("colour1");
}
@Test
@Description("Ensures that a deleted target type is removed from the repository as defined.")
public void deleteTargetType() {
// create test data
final TargetType targetType = targetTypeManagement
.create(entityFactory.targetType().create().name("targettype11").description("targettypedes11"));
assertThat(targetTypeRepository.findByName("targettype11").get().getDescription()).as("type found")
.isEqualTo("targettypedes11");
targetTypeManagement.delete(targetType.getId());
assertThat(targetTypeRepository.findById(targetType.getId())).as("No target type should be found").isNotPresent();
}
@Test
@Description("Tests the name update of a target type.")
public void updateTargetType() {
final TargetType targetType = targetTypeManagement
.create(entityFactory.targetType().create().name("targettype111").description("targettypedes111"));
assertThat(targetTypeRepository.findByName("targettype111").get().getDescription()).as("type found")
.isEqualTo("targettypedes111");
targetTypeManagement.update(entityFactory.targetType().update(targetType.getId()).name("updatedtargettype111"));
assertThat(targetTypeRepository.findByName("updatedtargettype111")).as("Updated target type should be found").isPresent();
}
@Test
@Description("Ensures that a target type cannot be created if one exists already with that name (expects EntityAlreadyExistsException).")
public void failedDuplicateTargetTypeNameException() {
targetTypeManagement.create(entityFactory.targetType().create().name("targettype123"));
assertThrows(EntityAlreadyExistsException.class, () -> targetTypeManagement.create(entityFactory.targetType().create().name("targettype123")));
}
@Test
@Description("Ensures that a target type cannot be updated to a name that already exists (expects EntityAlreadyExistsException).")
public void failedDuplicateTargetTypeNameExceptionAfterUpdate() {
targetTypeManagement.create(entityFactory.targetType().create().name("targettype1234"));
TargetType targetType = targetTypeManagement.create(entityFactory.targetType().create().name("targettype12345"));
assertThrows(EntityAlreadyExistsException.class, () -> targetTypeManagement.update(entityFactory.targetType().update(targetType.getId()).name("targettype1234")));
}
}

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa.event;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -20,10 +21,13 @@ import org.eclipse.hawkbit.repository.event.remote.DistributionSetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetTypeDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.event.RepositoryEntityEventTest.RepositoryTestConfiguration;
@@ -31,6 +35,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -88,6 +93,38 @@ public class RepositoryEntityEventTest extends AbstractJpaIntegrationTest {
assertThat(targetDeletedEvent.getEntityId()).isEqualTo(createdTarget.getId());
}
@Test
@Description("Verifies that the target type created event is published when a target type has been created")
public void targetTypeCreatedEventIsPublished() throws InterruptedException {
TargetType createdTargetType = testdataFactory.findOrCreateTargetType("targettype");
TargetTypeCreatedEvent targetTypeCreatedEvent = eventListener.waitForEvent(TargetTypeCreatedEvent.class);
assertThat(targetTypeCreatedEvent).isNotNull();
assertThat(targetTypeCreatedEvent.getEntity().getId()).isEqualTo(createdTargetType.getId());
}
@Test
@Description("Verifies that the target type updated event is published when a target type has been updated")
public void targetTypeUpdatedEventIsPublished() throws InterruptedException {
TargetType createdTargetType = testdataFactory.findOrCreateTargetType("targettype");
targetTypeManagement.update(entityFactory.targetType().update(createdTargetType.getId()).name("updatedtargettype"));
TargetTypeUpdatedEvent targetTypeUpdatedEvent = eventListener.waitForEvent(TargetTypeUpdatedEvent.class);
assertThat(targetTypeUpdatedEvent).isNotNull();
assertThat(targetTypeUpdatedEvent.getEntity().getId()).isEqualTo(createdTargetType.getId());
}
@Test
@Description("Verifies that the target type deleted event is published when a target type has been deleted")
public void targetTypeDeletedEventIsPublished() throws InterruptedException {
TargetType createdTargetType = testdataFactory.findOrCreateTargetType("targettype");
targetTypeManagement.delete(createdTargetType.getId());
TargetTypeDeletedEvent targetTypeDeletedEvent = eventListener.waitForEvent(TargetTypeDeletedEvent.class);
assertThat(targetTypeDeletedEvent).isNotNull();
assertThat(targetTypeDeletedEvent.getEntityId()).isEqualTo(createdTargetType.getId());
}
@Test
@Description("Verifies that the rollout deleted event is published when a rollout has been deleted")
public void rolloutDeletedEventIsPublished() throws InterruptedException {