Add getting tenant with details (#2195)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-01-14 09:32:16 +02:00
committed by GitHub
parent b294798ae5
commit 626d4cc537
17 changed files with 162 additions and 112 deletions

View File

@@ -994,9 +994,9 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr
registerAndAssertTargetWithExistingTenant(controllerId); registerAndAssertTargetWithExistingTenant(controllerId);
assertThat(targetManagement.getByControllerID(controllerId).map(Target::getAutoConfirmationStatus)).isEmpty(); assertThat(targetManagement.getWithAutoConfigurationStatus(controllerId).getAutoConfirmationStatus()).isNull();
confirmationManagement.activateAutoConfirmation(controllerId, null, null); confirmationManagement.activateAutoConfirmation(controllerId, null, null);
assertThat(targetManagement.getByControllerID(controllerId).map(Target::getAutoConfirmationStatus)).isPresent(); assertThat(targetManagement.getWithAutoConfigurationStatus(controllerId).getAutoConfirmationStatus()).isNotNull();
final DistributionSetAssignmentResult assignmentResult = prepareDistributionSetAndAssign(controllerId); final DistributionSetAssignmentResult assignmentResult = prepareDistributionSetAndAssign(controllerId);
final Long actionId = getFirstAssignedActionId(assignmentResult); final Long actionId = getFirstAssignedActionId(assignmentResult);

View File

@@ -306,7 +306,7 @@ public final class MgmtTargetMapper {
return Collections.emptyList(); return Collections.emptyList();
} }
return actions.stream().map(action -> toResponse(targetId, action)).collect(Collectors.toList()); return actions.stream().map(action -> toResponse(targetId, action)).toList();
} }
static MgmtMetadata toResponseTargetMetadata(final TargetMetadata metadata) { static MgmtMetadata toResponseTargetMetadata(final TargetMetadata metadata) {

View File

@@ -416,7 +416,7 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
@Override @Override
public ResponseEntity<MgmtTargetAutoConfirm> getAutoConfirmStatus(final String targetId) { public ResponseEntity<MgmtTargetAutoConfirm> getAutoConfirmStatus(final String targetId) {
final Target findTarget = findTargetWithExceptionIfNotFound(targetId); final Target findTarget = targetManagement.getWithAutoConfigurationStatus(targetId);
final MgmtTargetAutoConfirm state = MgmtTargetMapper.getTargetAutoConfirmResponse(findTarget); final MgmtTargetAutoConfirm state = MgmtTargetMapper.getTargetAutoConfirmResponse(findTarget);
return ResponseEntity.ok(state); return ResponseEntity.ok(state);
} }

View File

@@ -50,6 +50,11 @@ import org.springframework.security.access.prepost.PreAuthorize;
*/ */
public interface TargetManagement { public interface TargetManagement {
String DETAILS_BASE = "base";
String DETAILS_AUTO_CONFIRMATION_STATUS = "autoConfirmationStatus";
String DETAILS_TAGS = "tags";
String DETAILS_ACTIONS = "actions";
/** /**
* Counts number of targets with the given distribution set assigned. * Counts number of targets with the given distribution set assigned.
* *
@@ -221,8 +226,8 @@ public interface TargetManagement {
* @throws EntityNotFoundException if distribution set with given ID does not exist * @throws EntityNotFoundException if distribution set with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET)
Slice<Target> findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(@NotNull Pageable pageRequest, Slice<Target> findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(
long distributionSetId, @NotNull String rsqlParam); @NotNull Pageable pageRequest, long distributionSetId, @NotNull String rsqlParam);
/** /**
* Counts all targets for all the given parameter {@link TargetFilterQuery} and * Counts all targets for all the given parameter {@link TargetFilterQuery} and
@@ -361,6 +366,37 @@ public interface TargetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Optional<Target> getByControllerID(@NotEmpty String controllerId); Optional<Target> getByControllerID(@NotEmpty String controllerId);
/**
* Gets a {@link Target} based a given controller id and includes the details specified by the details key.
*
* @param controllerId to look for.
* @param detailsKey the key of the details to include, e.g. {@link #DETAILS_AUTO_CONFIRMATION_STATUS}
* @return {@link Target}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Target getWithDetails(@NotEmpty String controllerId, String detailsKey);
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
default Target getWithDetails(@NotEmpty String controllerId) {
return getWithDetails(controllerId, DETAILS_BASE);
}
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
default Target getWithAutoConfigurationStatus(@NotEmpty String controllerId) {
return getWithDetails(controllerId, DETAILS_AUTO_CONFIRMATION_STATUS);
}
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
default Target getWithTags(@NotEmpty String controllerId) {
return getWithDetails(controllerId, DETAILS_TAGS);
}
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
default Target getWithActions(@NotEmpty String controllerId) {
return getWithDetails(controllerId, DETAILS_ACTIONS);
}
/** /**
* Filter {@link Target}s for all the given parameters. If all parameters except * Filter {@link Target}s for all the given parameters. If all parameters except
* pageable are null, all available {@link Target}s are returned. * pageable are null, all available {@link Target}s are returned.

View File

@@ -77,14 +77,14 @@ public class JpaActionManagement {
return handleAddUpdateActionStatus(actionStatus, action); return handleAddUpdateActionStatus(actionStatus, action);
} }
log.debug("Update of actionStatus {} for action {} not possible since action not active anymore.", log.debug(
"Update of actionStatus {} for action {} not possible since action not active anymore.",
actionStatus.getStatus(), action.getId()); actionStatus.getStatus(), action.getId());
return action; return action;
} }
protected JpaAction getActionAndThrowExceptionIfNotFound(final Long actionId) { protected JpaAction getActionAndThrowExceptionIfNotFound(final Long actionId) {
return actionRepository.findById(actionId) return actionRepository.findById(actionId).orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
} }
protected void onActionStatusUpdate(final JpaActionStatus newActionStatus, final JpaAction action) { protected void onActionStatusUpdate(final JpaActionStatus newActionStatus, final JpaAction action) {

View File

@@ -34,6 +34,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus; import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
import org.eclipse.hawkbit.repository.jpa.model.JpaAutoConfirmationStatus; import org.eclipse.hawkbit.repository.jpa.model.JpaAutoConfirmationStatus;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository; import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository; import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository; import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
@@ -84,7 +85,7 @@ public class JpaConfirmationManagement extends JpaActionManagement implements Co
public AutoConfirmationStatus activateAutoConfirmation(final String controllerId, final String initiator, final String remark) { public AutoConfirmationStatus activateAutoConfirmation(final String controllerId, final String initiator, final String remark) {
log.trace( log.trace(
"'activateAutoConfirmation' was called with values: controllerId={}; initiator={}; remark={}", controllerId, initiator, remark); "'activateAutoConfirmation' was called with values: controllerId={}; initiator={}; remark={}", controllerId, initiator, remark);
final JpaTarget target = targetRepository.getByControllerId(controllerId); final JpaTarget target = targetRepository.getWithDetailsByControllerId(controllerId, JpaTarget_.GRAPH_TARGET_AUTO_CONFIRMATION_STATUS);
if (target.getAutoConfirmationStatus() != null) { if (target.getAutoConfirmationStatus() != null) {
log.debug("'activateAutoConfirmation' was called for an controller id {} with active auto confirmation.", controllerId); log.debug("'activateAutoConfirmation' was called for an controller id {} with active auto confirmation.", controllerId);
throw new AutoConfirmationAlreadyActiveException(controllerId); throw new AutoConfirmationAlreadyActiveException(controllerId);
@@ -109,18 +110,20 @@ public class JpaConfirmationManagement extends JpaActionManagement implements Co
@Override @Override
public Optional<AutoConfirmationStatus> getStatus(final String controllerId) { public Optional<AutoConfirmationStatus> getStatus(final String controllerId) {
return Optional.of(targetRepository.getByControllerId(controllerId)).map(JpaTarget::getAutoConfirmationStatus); return Optional.of(targetRepository.getWithDetailsByControllerId(controllerId, JpaTarget_.GRAPH_TARGET_AUTO_CONFIRMATION_STATUS))
.map(JpaTarget::getAutoConfirmationStatus);
} }
@Override @Override
@Transactional @Transactional
public List<Action> autoConfirmActiveActions(final String controllerId) { public List<Action> autoConfirmActiveActions(final String controllerId) {
final JpaTarget target = targetRepository.getByControllerId(controllerId); final JpaTarget target = targetRepository.getWithDetailsByControllerId(controllerId, JpaTarget_.GRAPH_TARGET_AUTO_CONFIRMATION_STATUS);
if (target.getAutoConfirmationStatus() == null) { if (target.getAutoConfirmationStatus() == null) {
// auto-confirmation is not active // auto-confirmation is not active
return Collections.emptyList(); return Collections.emptyList();
} else {
return giveConfirmationForActiveActions(target.getAutoConfirmationStatus());
} }
return giveConfirmationForActiveActions(target.getAutoConfirmationStatus());
} }
@Override @Override

View File

@@ -62,6 +62,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus_; import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository; import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository; import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository; import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
@@ -494,12 +495,12 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
@Override @Override
public Optional<DistributionSet> getAssignedDistributionSet(final String controllerId) { public Optional<DistributionSet> getAssignedDistributionSet(final String controllerId) {
return targetRepository.findByControllerId(controllerId).map(JpaTarget::getAssignedDistributionSet); return targetRepository.findWithDetailsByControllerId(controllerId, JpaTarget_.GRAPH_TARGET_ASSIGNED_DISTRIBUTION_SET).map(JpaTarget::getAssignedDistributionSet);
} }
@Override @Override
public Optional<DistributionSet> getInstalledDistributionSet(final String controllerId) { public Optional<DistributionSet> getInstalledDistributionSet(final String controllerId) {
return targetRepository.findByControllerId(controllerId).map(JpaTarget::getInstalledDistributionSet); return targetRepository.findWithDetailsByControllerId(controllerId, JpaTarget_.GRAPH_TARGET_INSTALLED_DISTRIBUTION_SET).map(JpaTarget::getInstalledDistributionSet);
} }
@Override @Override

View File

@@ -192,15 +192,12 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
return null; return null;
})); }));
} while ((query = tenants.nextPageable()) != Pageable.unpaged()); } while ((query = tenants.nextPageable()) != Pageable.unpaged());
} }
@Override @Override
public SystemUsageReportWithTenants getSystemUsageStatisticsWithTenants() { public SystemUsageReportWithTenants getSystemUsageStatisticsWithTenants() {
final SystemUsageReportWithTenants result = (SystemUsageReportWithTenants) getSystemUsageStatistics(); final SystemUsageReportWithTenants result = (SystemUsageReportWithTenants) getSystemUsageStatistics();
usageStatsPerTenant(result); usageStatsPerTenant(result);
return result; return result;
} }
@@ -213,19 +210,11 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
sumOfArtifacts = count.longValue(); sumOfArtifacts = count.longValue();
} }
// we use native queries to punch through the tenant boundaries. This // we use native queries to punch through the tenant boundaries. This has to be used with care!
// has to be used with care! final long targets = ((Number) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_target").getSingleResult()).longValue();
final long targets = ((Number) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_target") final long artifacts = ((Number) entityManager.createNativeQuery(countArtifactQuery).getSingleResult()).longValue();
.getSingleResult()).longValue(); final long actions = ((Number) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_action").getSingleResult()).longValue();
return new SystemUsageReportWithTenants(targets, artifacts, actions, sumOfArtifacts, tenantMetaDataRepository.count());
final long artifacts = ((Number) entityManager.createNativeQuery(countArtifactQuery).getSingleResult())
.longValue();
final long actions = ((Number) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_action")
.getSingleResult()).longValue();
return new SystemUsageReportWithTenants(targets, artifacts, actions, sumOfArtifacts,
tenantMetaDataRepository.count());
} }
@Override @Override

View File

@@ -409,6 +409,11 @@ public class JpaTargetManagement implements TargetManagement {
return targetRepository.findByControllerId(controllerId).map(Target.class::cast); return targetRepository.findByControllerId(controllerId).map(Target.class::cast);
} }
@Override
public Target getWithDetails(final String controllerId, final String detailsKey) {
return targetRepository.getWithDetailsByControllerId(controllerId, "Target." + detailsKey);
}
@Override @Override
public Slice<Target> findByFilters(final Pageable pageable, final FilterParams filterParams) { public Slice<Target> findByFilters(final Pageable pageable, final FilterParams filterParams) {
final List<Specification<JpaTarget>> specList = buildSpecificationList(filterParams); final List<Specification<JpaTarget>> specList = buildSpecificationList(filterParams);
@@ -719,8 +724,7 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public Set<TargetTag> getTagsByControllerId(@NotEmpty String controllerId) { public Set<TargetTag> getTagsByControllerId(@NotEmpty String controllerId) {
// the method has PreAuthorized by itself // the method has PreAuthorized by itself
return getByControllerID(controllerId).map(JpaTarget.class::cast).map(JpaTarget::getTags) return ((JpaTarget)getWithTags(controllerId)).getTags();
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
} }
@Override @Override

View File

@@ -65,13 +65,13 @@ import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
@Index(name = "sp_idx_action_prim", columnList = "tenant,id") @Index(name = "sp_idx_action_prim", columnList = "tenant,id")
}) })
@NamedEntityGraphs({ @NamedEntityGraphs({
@NamedEntityGraph(name = "Action.ds", attributeNodes = { @NamedAttributeNode("distributionSet") }),
@NamedEntityGraph(name = "Action.all", attributeNodes = { @NamedEntityGraph(name = "Action.all", attributeNodes = {
@NamedAttributeNode(value = "target", subgraph = "target.ds"), @NamedAttributeNode(value = "target", subgraph = "target.ds"),
@NamedAttributeNode("distributionSet") }, @NamedAttributeNode("distributionSet") },
subgraphs = @NamedSubgraph( subgraphs = @NamedSubgraph(
name = "target.ds", name = "target.ds",
attributeNodes = @NamedAttributeNode("assignedDistributionSet"))) attributeNodes = @NamedAttributeNode("assignedDistributionSet"))),
@NamedEntityGraph(name = "Action.ds", attributeNodes = { @NamedAttributeNode("distributionSet") })
}) })
@Entity @Entity
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities // exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities

View File

@@ -34,6 +34,10 @@ import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import jakarta.persistence.MapKeyColumn; import jakarta.persistence.MapKeyColumn;
import jakarta.persistence.NamedAttributeNode;
import jakarta.persistence.NamedEntityGraph;
import jakarta.persistence.NamedEntityGraphs;
import jakarta.persistence.NamedSubgraph;
import jakarta.persistence.OneToMany; import jakarta.persistence.OneToMany;
import jakarta.persistence.PrimaryKeyJoinColumn; import jakarta.persistence.PrimaryKeyJoinColumn;
import jakarta.persistence.Table; import jakarta.persistence.Table;
@@ -69,6 +73,7 @@ import org.springframework.util.ObjectUtils;
* JPA implementation of {@link Target}. * JPA implementation of {@link Target}.
*/ */
@NoArgsConstructor // Default constructor for JPA @NoArgsConstructor // Default constructor for JPA
@Slf4j
@Entity @Entity
@Table(name = "sp_target", @Table(name = "sp_target",
indexes = { indexes = {
@@ -78,9 +83,23 @@ import org.springframework.util.ObjectUtils;
@Index(name = "sp_idx_target_05", columnList = "tenant,last_modified_at"), @Index(name = "sp_idx_target_05", columnList = "tenant,last_modified_at"),
@Index(name = "sp_idx_target_prim", columnList = "tenant,id") }, @Index(name = "sp_idx_target_prim", columnList = "tenant,id") },
uniqueConstraints = @UniqueConstraint(columnNames = { "controller_id", "tenant" }, name = "uk_tenant_controller_id")) uniqueConstraints = @UniqueConstraint(columnNames = { "controller_id", "tenant" }, name = "uk_tenant_controller_id"))
@NamedEntityGraphs({
@NamedEntityGraph(name = "Target.details", attributeNodes = {
@NamedAttributeNode("targetType"),
@NamedAttributeNode("installedDistributionSet"),
@NamedAttributeNode("assignedDistributionSet") }),
@NamedEntityGraph(name = "Target.targetType", attributeNodes = { @NamedAttributeNode("targetType") }),
@NamedEntityGraph(name = "Target.installedDistributionSet", attributeNodes = { @NamedAttributeNode("installedDistributionSet") },
subgraphs = { @NamedSubgraph(
name = "installedDistributionSet.optLockRevision",
attributeNodes = @NamedAttributeNode("optLockRevision")) }),
@NamedEntityGraph(name = "Target.assignedDistributionSet", attributeNodes = { @NamedAttributeNode("assignedDistributionSet") }),
@NamedEntityGraph(name = "Target.autoConfirmationStatus", attributeNodes = { @NamedAttributeNode("autoConfirmationStatus") }),
@NamedEntityGraph(name = "Target.tags", attributeNodes = { @NamedAttributeNode("tags") }),
@NamedEntityGraph(name = "Target.actions", attributeNodes = { @NamedAttributeNode("actions") }),
})
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities // exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities
@SuppressWarnings("squid:S2160") @SuppressWarnings("squid:S2160")
@Slf4j
public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAwareEntity { public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAwareEntity {
@Serial @Serial
@@ -281,10 +300,6 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
actions.add((JpaAction) action); actions.add((JpaAction) action);
} }
public List<Action> getActions() {
return actions == null ? Collections.emptyList() : Collections.unmodifiableList(actions);
}
@Override @Override
public void fireCreateEvent() { public void fireCreateEvent() {
EventPublisherHolder.getInstance().getEventPublisher() EventPublisherHolder.getInstance().getEventPublisher()

View File

@@ -10,20 +10,19 @@
package org.eclipse.hawkbit.repository.jpa.repository; package org.eclipse.hawkbit.repository.jpa.repository;
import java.io.Serializable; import java.io.Serializable;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import jakarta.persistence.EntityGraph; import jakarta.persistence.EntityGraph;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException; import jakarta.persistence.NoResultException;
import jakarta.persistence.Query;
import jakarta.persistence.TypedQuery; import jakarta.persistence.TypedQuery;
import jakarta.transaction.Transactional; import jakarta.transaction.Transactional;
import org.eclipse.hawkbit.repository.BaseRepositoryTypeProvider; import org.eclipse.hawkbit.repository.BaseRepositoryTypeProvider;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController; import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@@ -46,15 +45,18 @@ public class HawkbitBaseRepository<T, ID extends Serializable> extends SimpleJpa
implements JpaSpecificationEntityGraphExecutor<T>, NoCountSliceRepository<T>, ACMRepository<T> { implements JpaSpecificationEntityGraphExecutor<T>, NoCountSliceRepository<T>, ACMRepository<T> {
private final EntityManager entityManager; private final EntityManager entityManager;
private final Logger log;
public HawkbitBaseRepository(final Class<T> domainClass, final EntityManager entityManager) { public HawkbitBaseRepository(final Class<T> domainClass, final EntityManager entityManager) {
super(domainClass, entityManager); super(domainClass, entityManager);
this.entityManager = entityManager; this.entityManager = entityManager;
log = LoggerFactory.getLogger(getDomainClass());
} }
public HawkbitBaseRepository(final JpaEntityInformation<T, ?> entityInformation, final EntityManager entityManager) { public HawkbitBaseRepository(final JpaEntityInformation<T, ?> entityInformation, final EntityManager entityManager) {
super(entityInformation, entityManager); super(entityInformation, entityManager);
this.entityManager = entityManager; this.entityManager = entityManager;
log = LoggerFactory.getLogger(getDomainClass());
} }
@Override @Override
@@ -149,8 +151,17 @@ public class HawkbitBaseRepository<T, ID extends Serializable> extends SimpleJpa
} }
private TypedQuery<T> withEntityGraph(final TypedQuery<T> query, final String entityGraph) { private TypedQuery<T> withEntityGraph(final TypedQuery<T> query, final String entityGraph) {
final EntityGraph<?> graph = ObjectUtils.isEmpty(entityGraph) ? null : entityManager.createEntityGraph(entityGraph); if (ObjectUtils.isEmpty(entityGraph)) {
return graph == null ? query : query.setHint("jakarta.persistence.loadgraph", graph); return query;
} else {
final EntityGraph<?> graph = entityManager.createEntityGraph(entityGraph);
if (graph == null) {
log.warn("Entity graph {} not found", entityGraph);
return query;
} else {
return query.setHint("jakarta.persistence.loadgraph", graph);
}
}
} }
private <S extends T> Page<S> readPageWithoutCount(final TypedQuery<S> query, final Pageable pageable) { private <S extends T> Page<S> readPageWithoutCount(final TypedQuery<S> query, final Pageable pageable) {

View File

@@ -35,10 +35,14 @@ public interface TargetRepository extends BaseEntityRepository<JpaTarget> {
default Optional<JpaTarget> findByControllerId(final String controllerId) { default Optional<JpaTarget> findByControllerId(final String controllerId) {
return findOne(TargetSpecifications.hasControllerId(controllerId)); return findOne(TargetSpecifications.hasControllerId(controllerId));
} }
default Optional<JpaTarget> findWithDetailsByControllerId(final String controllerId, final String entityGraph) {
return findOne(TargetSpecifications.hasControllerId(controllerId), entityGraph);
}
default JpaTarget getByControllerId(final String controllerId) { default JpaTarget getByControllerId(final String controllerId) {
return findOne(TargetSpecifications.hasControllerId(controllerId)) return findByControllerId(controllerId).orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId)); }
default JpaTarget getWithDetailsByControllerId(final String controllerId, final String entityGraph) {
return findWithDetailsByControllerId(controllerId, entityGraph).orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
} }
// TODO AC - remove it and use specification // TODO AC - remove it and use specification

View File

@@ -259,16 +259,15 @@ class ConfirmationManagementTest extends AbstractJpaIntegrationTest {
final String controllerId = testdataFactory.createTarget().getControllerId(); final String controllerId = testdataFactory.createTarget().getControllerId();
confirmationManagement.activateAutoConfirmation(controllerId, initiator, remark); confirmationManagement.activateAutoConfirmation(controllerId, initiator, remark);
assertThat(targetManagement.getByControllerID(controllerId)).hasValueSatisfying(target -> { assertThat(targetManagement.getWithAutoConfigurationStatus(controllerId).getAutoConfirmationStatus())
assertThat(target.getAutoConfirmationStatus()).isNotNull() .isNotNull()
.matches(status -> status.getTarget().getControllerId().equals(controllerId)) .matches(status -> status.getTarget().getControllerId().equals(controllerId))
.matches(status -> Objects.equals(status.getInitiator(), initiator)) .matches(status -> Objects.equals(status.getInitiator(), initiator))
.matches(status -> Objects.equals(status.getCreatedBy(), "bumlux")) .matches(status -> Objects.equals(status.getCreatedBy(), "bumlux"))
.matches(status -> Objects.equals(status.getRemark(), remark)).satisfies(status -> { .matches(status -> Objects.equals(status.getRemark(), remark)).satisfies(status -> {
final Instant activationTime = Instant.ofEpochMilli(status.getActivatedAt()); final Instant activationTime = Instant.ofEpochMilli(status.getActivatedAt());
assertThat(activationTime).isAfterOrEqualTo(activationTime.minusSeconds(3L)); assertThat(activationTime).isAfterOrEqualTo(activationTime.minusSeconds(3L));
}); });
});
confirmationManagement.deactivateAutoConfirmation(controllerId); confirmationManagement.deactivateAutoConfirmation(controllerId);
verifyAutoConfirmationIsDisabled(controllerId); verifyAutoConfirmationIsDisabled(controllerId);
@@ -280,8 +279,7 @@ class ConfirmationManagementTest extends AbstractJpaIntegrationTest {
final String controllerId = testdataFactory.createTarget().getControllerId(); final String controllerId = testdataFactory.createTarget().getControllerId();
confirmationManagement.activateAutoConfirmation(controllerId, "any", "any"); confirmationManagement.activateAutoConfirmation(controllerId, "any", "any");
assertThat(targetManagement.getByControllerID(controllerId)) assertThat(targetManagement.getWithAutoConfigurationStatus(controllerId).getAutoConfirmationStatus()).isNotNull();
.hasValueSatisfying(target -> assertThat(target.getAutoConfirmationStatus()).isNotNull());
assertThatThrownBy(() -> confirmationManagement.activateAutoConfirmation(controllerId, "any", "any")) assertThatThrownBy(() -> confirmationManagement.activateAutoConfirmation(controllerId, "any", "any"))
.isInstanceOf(AutoConfirmationAlreadyActiveException.class) .isInstanceOf(AutoConfirmationAlreadyActiveException.class)
@@ -299,8 +297,11 @@ class ConfirmationManagementTest extends AbstractJpaIntegrationTest {
} }
private static Stream<Arguments> getAutoConfirmationArguments() { private static Stream<Arguments> getAutoConfirmationArguments() {
return Stream.of(Arguments.of("TestUser", "TestRemark"), Arguments.of("TestUser", null), return Stream.of(
Arguments.of(null, "TestRemark"), Arguments.of(null, null)); Arguments.of("TestUser", "TestRemark"),
Arguments.of("TestUser", null),
Arguments.of(null, "TestRemark"),
Arguments.of(null, null));
} }
private static DeploymentRequest toDeploymentRequest(final String controllerId, final Long distributionSetId) { private static DeploymentRequest toDeploymentRequest(final String controllerId, final Long distributionSetId) {
@@ -308,8 +309,6 @@ class ConfirmationManagementTest extends AbstractJpaIntegrationTest {
} }
private void verifyAutoConfirmationIsDisabled(final String controllerId) { private void verifyAutoConfirmationIsDisabled(final String controllerId) {
assertThat(targetManagement.getByControllerID(controllerId)) assertThat(targetManagement.getWithAutoConfigurationStatus(controllerId).getAutoConfirmationStatus()).isNull();
.hasValueSatisfying(target -> assertThat(target.getAutoConfirmationStatus()).isNull());
} }
}
}

View File

@@ -233,8 +233,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
final List<Target> testTarget = testdataFactory.createTargets(1); final List<Target> testTarget = testdataFactory.createTargets(1);
// one action with one action status is generated // one action with one action status is generated
final Long actionId = getFirstAssignedActionId(assignDistributionSet(testDs, testTarget)); final Long actionId = getFirstAssignedActionId(assignDistributionSet(testDs, testTarget));
final Slice<Action> actions = deploymentManagement.findActionsByTarget(testTarget.get(0).getControllerId(), final Slice<Action> actions = deploymentManagement.findActionsByTarget(testTarget.get(0).getControllerId(), PAGE);
PAGE);
final ActionStatus expectedActionStatus = ((JpaAction) actions.getContent().get(0)).getActionStatus().get(0); final ActionStatus expectedActionStatus = ((JpaAction) actions.getContent().get(0)).getActionStatus().get(0);
// act // act
@@ -374,7 +373,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
final DistributionSet dsInstalled = action.getDistributionSet(); final DistributionSet dsInstalled = action.getDistributionSet();
// check initial status // check initial status
assertThat(targetManagement.getByControllerID("4712").get().getUpdateStatus()).as("wrong update status") assertThat(targetManagement.getByControllerID("4712").get().getUpdateStatus())
.as("wrong update status")
.isEqualTo(TargetUpdateStatus.IN_SYNC); .isEqualTo(TargetUpdateStatus.IN_SYNC);
// assign the two sets in a row // assign the two sets in a row
@@ -404,10 +404,12 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
controllerManagement.addCancelActionStatus( controllerManagement.addCancelActionStatus(
entityFactory.actionStatus().create(secondAction.getId()).status(Status.CANCELED)); entityFactory.actionStatus().create(secondAction.getId()).status(Status.CANCELED));
// cancelled success -> back to dsInstalled // cancelled success -> back to dsInstalled
assertThat(deploymentManagement.getAssignedDistributionSet("4712")).as("wrong installed ds") assertThat(deploymentManagement.getAssignedDistributionSet("4712"))
.as("wrong installed ds")
.contains(dsInstalled); .contains(dsInstalled);
assertThat(targetManagement.getByControllerID("4712").get().getUpdateStatus()) assertThat(targetManagement.getByControllerID("4712").get().getUpdateStatus())
.as("wrong target info update status").isEqualTo(TargetUpdateStatus.IN_SYNC); .as("wrong target info update status")
.isEqualTo(TargetUpdateStatus.IN_SYNC);
} }
@Test @Test
@@ -780,8 +782,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
confirmationManagement.activateAutoConfirmation(target.getControllerId(), "not_bumlux", "my personal remark"); confirmationManagement.activateAutoConfirmation(target.getControllerId(), "not_bumlux", "my personal remark");
assertThat(targetManagement.getByControllerID(target.getControllerId())) assertThat(targetManagement.getWithAutoConfigurationStatus(target.getControllerId()).getAutoConfirmationStatus()).isNotNull();
.hasValueSatisfying(t -> assertThat(t.getAutoConfirmationStatus()).isNotNull());
final DistributionSet distributionSet = testdataFactory.createDistributionSet(); final DistributionSet distributionSet = testdataFactory.createDistributionSet();
@@ -1038,26 +1039,21 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
final Iterable<Target> allFoundTargets = targetManagement.findAll(PAGE).getContent(); final Iterable<Target> allFoundTargets = targetManagement.findAll(PAGE).getContent();
// get final updated version of targets // get final updated version of targets
savedDeployedTargets = targetManagement.getByControllerID( savedDeployedTargets = targetManagement.getByControllerID(savedDeployedTargets.stream().map(Target::getControllerId).toList());
savedDeployedTargets.stream().map(target -> target.getControllerId()).collect(Collectors.toList()));
assertThat(allFoundTargets).as("founded targets are wrong").containsAll(savedDeployedTargets) assertThat(allFoundTargets).as("founded targets are wrong").containsAll(savedDeployedTargets)
.containsAll(savedNakedTargets); .containsAll(savedNakedTargets);
assertThat(savedDeployedTargets).as("saved target are wrong") assertThat(savedDeployedTargets).as("saved target are wrong").doesNotContain(toArray(savedNakedTargets, Target.class));
.doesNotContain(toArray(savedNakedTargets, Target.class)); assertThat(savedNakedTargets).as("saved target are wrong").doesNotContain(toArray(savedDeployedTargets, Target.class));
assertThat(savedNakedTargets).as("saved target are wrong")
.doesNotContain(toArray(savedDeployedTargets, Target.class));
for (final Target myt : savedNakedTargets) { for (final Target myt : savedNakedTargets) {
final Target t = targetManagement.getByControllerID(myt.getControllerId()).get(); final Target t = targetManagement.getByControllerID(myt.getControllerId()).get();
assertThat(deploymentManagement.countActionsByTarget(t.getControllerId())).as("action should be empty") assertThat(deploymentManagement.countActionsByTarget(t.getControllerId())).as("action should be empty").isZero();
.isZero();
} }
for (final Target myt : savedDeployedTargets) { for (final Target myt : savedDeployedTargets) {
final Target t = targetManagement.getByControllerID(myt.getControllerId()).get(); final Target t = targetManagement.getByControllerID(myt.getControllerId()).get();
final List<Action> activeActionsByTarget = deploymentManagement final List<Action> activeActionsByTarget = deploymentManagement.findActiveActionsByTarget(PAGE, t.getControllerId()).getContent();
.findActiveActionsByTarget(PAGE, t.getControllerId()).getContent();
assertThat(activeActionsByTarget).as("action should not be empty").isNotEmpty(); assertThat(activeActionsByTarget).as("action should not be empty").isNotEmpty();
assertThat(t.getUpdateStatus()).as("wrong target update status").isEqualTo(TargetUpdateStatus.PENDING); assertThat(t.getUpdateStatus()).as("wrong target update status").isEqualTo(TargetUpdateStatus.PENDING);
for (final Action ua : activeActionsByTarget) { for (final Action ua : activeActionsByTarget) {
@@ -1373,7 +1369,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
assertThat(deploymentManagement.getAssignedDistributionSet(targ.getControllerId())) assertThat(deploymentManagement.getAssignedDistributionSet(targ.getControllerId()))
.as("Assigned distribution set of target is wrong").contains(dsA); .as("Assigned distribution set of target is wrong").contains(dsA);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, targ.getControllerId()).getContent().get(0) assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, targ.getControllerId()).getContent().get(0)
.getDistributionSet()).as("Distribution set of actionn is wrong").isEqualTo(dsA); .getDistributionSet()).as("Distribution set of action is wrong").isEqualTo(dsA);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, targ.getControllerId()).getContent().get(0) assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, targ.getControllerId()).getContent().get(0)
.getDistributionSet()).as("Installed distribution set of action should be null").isNotNull(); .getDistributionSet()).as("Installed distribution set of action should be null").isNotNull();
@@ -1554,31 +1550,25 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
@Description("Verify that the DistributionSet assignments work for multiple targets of different target types.") @Description("Verify that the DistributionSet assignments work for multiple targets of different target types.")
void verifyDSAssignmentForMultipleTargetsWithDifferentTargetTypes() { void verifyDSAssignmentForMultipleTargetsWithDifferentTargetTypes() {
final DistributionSet ds = testdataFactory.createDistributionSet("test-ds"); final DistributionSet ds = testdataFactory.createDistributionSet("test-ds");
final TargetType targetType1 = testdataFactory.createTargetType("test-type1", final TargetType targetType1 = testdataFactory.createTargetType("test-type1", Collections.singletonList(ds.getType()));
Collections.singletonList(ds.getType())); final TargetType targetType2 = testdataFactory.createTargetType("test-type2", Collections.singletonList(ds.getType()));
final TargetType targetType2 = testdataFactory.createTargetType("test-type2",
Collections.singletonList(ds.getType()));
final Target target1 = testdataFactory.createTarget("test-target1", "test-target1", targetType1.getId()); final Target target1 = testdataFactory.createTarget("test-target1", "test-target1", targetType1.getId());
final Target target2 = testdataFactory.createTarget("test-target2", "test-target2", targetType2.getId()); final Target target2 = testdataFactory.createTarget("test-target2", "test-target2", targetType2.getId());
final DeploymentRequest deployment1 = DeploymentManagement final DeploymentRequest deployment1 = DeploymentManagement.deploymentRequest(target1.getControllerId(), ds.getId()).build();
.deploymentRequest(target1.getControllerId(), ds.getId()).build(); final DeploymentRequest deployment2 = DeploymentManagement.deploymentRequest(target2.getControllerId(), ds.getId()).build();
final DeploymentRequest deployment2 = DeploymentManagement
.deploymentRequest(target2.getControllerId(), ds.getId()).build();
final List<DeploymentRequest> deploymentRequests = Arrays.asList(deployment1, deployment2); final List<DeploymentRequest> deploymentRequests = Arrays.asList(deployment1, deployment2);
deploymentManagement.assignDistributionSets(deploymentRequests); deploymentManagement.assignDistributionSets(deploymentRequests);
implicitLock(ds); implicitLock(ds);
final Optional<DistributionSet> assignedDsTarget1 = targetManagement final DistributionSet assignedDsTarget1 = ((JpaTarget)targetManagement
.getByControllerID(target1.getControllerId()).map(JpaTarget.class::cast) .getWithDetails(target1.getControllerId(), "assignedDistributionSet")).getAssignedDistributionSet();
.map(JpaTarget::getAssignedDistributionSet); final DistributionSet assignedDsTarget2 = ((JpaTarget)targetManagement
final Optional<DistributionSet> assignedDsTarget2 = targetManagement .getWithDetails(target1.getControllerId(), "assignedDistributionSet")).getAssignedDistributionSet();
.getByControllerID(target2.getControllerId()).map(JpaTarget.class::cast)
.map(JpaTarget::getAssignedDistributionSet);
assertThat(assignedDsTarget1).contains(ds); assertThat(assignedDsTarget1).isEqualTo(ds);
assertThat(assignedDsTarget2).contains(ds); assertThat(assignedDsTarget2).isEqualTo(ds);
} }
@Test @Test

View File

@@ -176,14 +176,12 @@ class TargetTagManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Ensures that a deleted tag is removed from the repository as defined.") @Description("Ensures that a deleted tag is removed from the repository as defined.")
void deleteTargetTags() { void deleteTargetTags() {
// create test data // create test data
final Iterable<JpaTargetTag> tags = createTargetsWithTags(); final Iterable<JpaTargetTag> tags = createTargetsWithTags();
final TargetTag toDelete = tags.iterator().next(); final TargetTag toDelete = tags.iterator().next();
for (final Target target : targetRepository.findAll()) { for (final Target target : targetRepository.findAll()) {
assertThat(getTargetTags(target.getControllerId())) assertThat(getTargetTags(target.getControllerId())).contains(toDelete);
.contains(toDelete);
} }
// delete // delete

View File

@@ -51,6 +51,7 @@ import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TargetTagManagement; import org.eclipse.hawkbit.repository.TargetTagManagement;
import org.eclipse.hawkbit.repository.TargetTypeManagement; import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType; import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DeploymentRequest; import org.eclipse.hawkbit.repository.model.DeploymentRequest;
@@ -430,24 +431,23 @@ public abstract class AbstractIntegrationTest {
return prepareFinishedUpdate(TestdataFactory.DEFAULT_CONTROLLER_ID, "", false); return prepareFinishedUpdate(TestdataFactory.DEFAULT_CONTROLLER_ID, "", false);
} }
protected Action prepareFinishedUpdate(final String controllerId, final String distributionSet, protected Action prepareFinishedUpdate(final String controllerId, final String distributionSet, final boolean isRequiredMigrationStep) {
final boolean isRequiredMigrationStep) {
final DistributionSet ds = testdataFactory.createDistributionSet(distributionSet, isRequiredMigrationStep); final DistributionSet ds = testdataFactory.createDistributionSet(distributionSet, isRequiredMigrationStep);
Target savedTarget = testdataFactory.createTarget(controllerId); Target savedTarget = testdataFactory.createTarget(controllerId);
savedTarget = getFirstAssignedTarget( savedTarget = getFirstAssignedTarget(assignDistributionSet(ds.getId(), savedTarget.getControllerId(), ActionType.FORCED));
assignDistributionSet(ds.getId(), savedTarget.getControllerId(), ActionType.FORCED)); final Action savedAction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId()).getContent().get(0);
Action savedAction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
if (savedAction.getStatus() == Action.Status.WAIT_FOR_CONFIRMATION) { if (savedAction.getStatus() == Action.Status.WAIT_FOR_CONFIRMATION) {
confirmationManagement.confirmAction(savedAction.getId(), null, null); confirmationManagement.confirmAction(savedAction.getId(), null, null);
} }
savedAction = controllerManagement.addUpdateActionStatus( controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(savedAction.getId()).status(Action.Status.RUNNING)); entityFactory.actionStatus().create(savedAction.getId()).status(Action.Status.RUNNING));
controllerManagement.addUpdateActionStatus(
return controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(savedAction.getId()).status(Action.Status.FINISHED)); entityFactory.actionStatus().create(savedAction.getId()).status(Action.Status.FINISHED));
return controllerManagement.findActionWithDetails(savedAction.getId())
.orElseThrow(() -> new EntityNotFoundException(Action.class, savedAction.getId()));
} }
protected void enableBatchAssignments() { protected void enableBatchAssignments() {