Implement target tag management using common RepositoryManagement (and abstract JPA impl) (#2580)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-08-01 13:41:20 +03:00
committed by GitHub
parent 5d75c9b0cc
commit fd9c0fc50d
28 changed files with 235 additions and 539 deletions

View File

@@ -13,14 +13,11 @@ import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.builder.ActionStatusBuilder;
import org.eclipse.hawkbit.repository.builder.RolloutBuilder;
import org.eclipse.hawkbit.repository.builder.RolloutGroupBuilder;
import org.eclipse.hawkbit.repository.builder.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.JpaTagBuilder;
import org.eclipse.hawkbit.repository.model.Tag;
import org.springframework.validation.annotation.Validated;
/**
@@ -49,12 +46,6 @@ public class JpaEntityFactory implements EntityFactory {
return new JpaActionStatusBuilder();
}
@Override
public TagBuilder<Tag> tag() {
return (TagBuilder)new JpaTagBuilder();
}
@Override
public RolloutGroupBuilder rolloutGroup() {
return new JpaRolloutGroupBuilder();

View File

@@ -453,8 +453,7 @@ public class JpaRepositoryConfiguration {
final TargetBuilder targetBuilder, final TargetTypeBuilder targetTypeBuilder,
final TargetFilterQueryBuilder targetFilterQueryBuilder,
final RolloutBuilder rolloutBuilder) {
return new JpaEntityFactory(
targetBuilder, targetTypeBuilder, targetFilterQueryBuilder, rolloutBuilder);
return new JpaEntityFactory(targetBuilder, targetTypeBuilder, targetFilterQueryBuilder, rolloutBuilder);
}
/**

View File

@@ -1,33 +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.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<JpaTag> {
@Override
public TagUpdate update(final long id) {
return new GenericTagUpdate(id);
}
@Override
public TagCreate<JpaTag> create() {
return new JpaTagCreate();
}
}

View File

@@ -1,33 +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.AbstractTagUpdateCreate;
import org.eclipse.hawkbit.repository.builder.TagCreate;
import org.eclipse.hawkbit.repository.jpa.model.JpaTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
/**
* Create/build implementation.
*/
@NoArgsConstructor(access = AccessLevel.PACKAGE)
public class JpaTagCreate extends AbstractTagUpdateCreate<TagCreate<JpaTag>> implements TagCreate<JpaTag> {
public JpaTargetTag buildTargetTag() {
return new JpaTargetTag(name, description, colour);
}
@Override
public JpaTag build() {
return new JpaTag(name, description, colour);
}
}

View File

@@ -142,8 +142,8 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
}
@Override
public Slice<T> findAll(final Pageable pageable) {
return JpaManagementHelper.findAllWithoutCountBySpec(
public Page<T> findAll(final Pageable pageable) {
return JpaManagementHelper.findAllWithCountBySpec(
jpaRepository, isNotDeleted().map(List::of).orElseGet(Collections::emptyList), pageable);
}

View File

@@ -9,33 +9,16 @@
*/
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.TargetTagFields;
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.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;
@@ -47,91 +30,19 @@ import org.springframework.validation.annotation.Validated;
@Validated
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "target-tag-management" }, matchIfMissing = true)
public class JpaTargetTagManagement implements TargetTagManagement {
public class JpaTargetTagManagement
extends AbstractJpaRepositoryManagement<JpaTargetTag, TargetTagManagement.Create, TargetTagManagement.Update, TargetTagRepository, TargetTagFields>
implements TargetTagManagement<JpaTargetTag> {
private final TargetTagRepository targetTagRepository;
protected JpaTargetTagManagement(final TargetTagRepository targetTagRepository) {
this.targetTagRepository = targetTagRepository;
protected JpaTargetTagManagement(
final TargetTagRepository targetTagRepository,
final EntityManager entityManager) {
super(targetTagRepository, entityManager);
}
@Override
public long count() {
return targetTagRepository.count();
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetTag create(final TagCreate c) {
final JpaTagCreate create = (JpaTagCreate) c;
return targetTagRepository.save(AccessController.Operation.CREATE, create.buildTargetTag());
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
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(
targetTagRepository.saveAll(AccessController.Operation.CREATE, targetTagList));
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final String targetTagName) {
targetTagRepository.delete(
targetTagRepository
.findOne(((root, query, cb) -> cb.equal(root.get(AbstractJpaNamedEntity_.name), targetTagName)))
.orElseThrow(() -> new EntityNotFoundException(TargetTag.class, targetTagName)));
}
@Override
public Page<TargetTag> findAll(final Pageable pageable) {
return JpaManagementHelper.findAllWithCountBySpec(targetTagRepository, null, pageable);
}
@Override
public Page<TargetTag> findByRsql(final String rsql, final Pageable pageable) {
return JpaManagementHelper.findAllWithCountBySpec(targetTagRepository, Collections.singletonList(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetTagFields.class)), pageable);
}
@Override
public Optional<TargetTag> getByName(final String name) {
return targetTagRepository.findByNameEquals(name);
}
@Override
public Optional<TargetTag> get(final long id) {
return targetTagRepository.findById(id).map(TargetTag.class::cast);
}
@Override
public List<TargetTag> get(final Collection<Long> ids) {
return Collections.unmodifiableList(targetTagRepository.findAllById(ids));
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetTag update(final TagUpdate u) {
final GenericTagUpdate update = (GenericTagUpdate) u;
final JpaTargetTag tag = targetTagRepository.findById(update.getId())
.orElseThrow(() -> new EntityNotFoundException(TargetTag.class, update.getId()));
update.getName().ifPresent(tag::setName);
update.getDescription().ifPresent(tag::setDescription);
update.getColour().ifPresent(tag::setColour);
return targetTagRepository.save(tag);
return jpaRepository.findByNameEquals(name);
}
}