From c1de86b29ecd58d7a7f674a5ab44e533169d8669 Mon Sep 17 00:00:00 2001 From: Avgustin Marinov Date: Fri, 19 Jul 2024 13:56:30 +0300 Subject: [PATCH] Remove target tag to target reference (#1772) * Remove target tag to target reference it is not used and could lead to extensive memory usage if JPA provider load targets while loading tags Also, remove search field controller id as not meaningful Signed-off-by: Marinov Avgustin * Fix review findings --------- Signed-off-by: Marinov Avgustin --- .../hawkbit/repository/TargetTagFields.java | 29 +------ .../repository/TargetTagManagement.java | 17 +--- .../management/JpaTargetTagManagement.java | 15 +--- .../repository/jpa/model/JpaTarget.java | 81 +++++++++---------- .../repository/jpa/model/JpaTargetTag.java | 48 +++-------- .../jpa/specifications/TagSpecification.java | 44 ++-------- .../specifications/TargetSpecifications.java | 27 ++----- .../jpa/AbstractJpaIntegrationTest.java | 11 +++ .../jpa/management/TargetManagementTest.java | 16 ++-- .../management/TargetTagManagementTest.java | 6 +- .../resource/MgmtTargetTagResourceTest.java | 56 ++----------- 11 files changed, 98 insertions(+), 252 deletions(-) diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/TargetTagFields.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/TargetTagFields.java index b3e744775..a16766021 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/TargetTagFields.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/TargetTagFields.java @@ -9,14 +9,10 @@ */ package org.eclipse.hawkbit.repository; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - /** * Describing the fields of the Tag model which can be used in the REST API e.g. * for sorting etc. - * Additionally here were added fields for Target in order + * Additionally, here were added fields for Target in order * filtering over target fields also. */ public enum TargetTagFields implements FieldNameProvider { @@ -36,31 +32,12 @@ public enum TargetTagFields implements FieldNameProvider { /** * The controllerId field. */ - COLOUR(TagFields.COLOUR.getFieldName()), - - /** - * Target fields - */ - TARGET("assignedToTargets", - TargetFields.ID.getFieldName(), TargetFields.NAME.getFieldName()); + COLOUR(TagFields.COLOUR.getFieldName()); private final String fieldName; - private final List subEntityAttributes; - - private TargetTagFields(final String fieldName) { + TargetTagFields(final String fieldName) { this.fieldName = fieldName; - this.subEntityAttributes = Collections.emptyList(); - } - - private TargetTagFields(final String fieldName, final String... subEntityAttributes) { - this.fieldName = fieldName; - this.subEntityAttributes = Arrays.asList(subEntityAttributes); - } - - @Override - public List getSubEntityAttributes() { - return Collections.unmodifiableList(subEntityAttributes); } @Override diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetTagManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetTagManagement.java index 7307c116a..d715dd401 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetTagManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetTagManagement.java @@ -12,6 +12,7 @@ package org.eclipse.hawkbit.repository; import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.Set; import jakarta.validation.ConstraintViolationException; import jakarta.validation.Valid; @@ -101,22 +102,6 @@ public interface TargetTagManagement { @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) Page findAll(@NotNull Pageable pageable); - /** - * Returns all {@link TargetTag}s assigned to {@link Target} with given ID. - * - * @param pageable - * page parameter - * @param controllerId - * of the assigned target - * - * @return {@link TargetTag}s assigned to {@link Target} with given ID - * - * @throws EntityNotFoundException - * if target with given ID does not exist - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - Page findByTarget(@NotNull Pageable pageable, @NotEmpty String controllerId); - /** * Retrieves all target tags based on the given specification. * diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetTagManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetTagManagement.java index d67011e47..d0d3590f1 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetTagManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetTagManagement.java @@ -29,9 +29,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag_; import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository; import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; -import org.eclipse.hawkbit.repository.jpa.specifications.TagSpecification; -import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications; -import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.springframework.dao.ConcurrencyFailureException; @@ -146,14 +143,4 @@ public class JpaTargetTagManagement implements TargetTagManagement { public Page findAll(final Pageable pageable) { return JpaManagementHelper.findAllWithCountBySpec(targetTagRepository, pageable, null); } - - @Override - public Page findByTarget(final Pageable pageable, final String controllerId) { - if (!targetRepository.exists(TargetSpecifications.hasControllerId(controllerId))) { - throw new EntityNotFoundException(Target.class, controllerId); - } - - return JpaManagementHelper.findAllWithCountBySpec(targetTagRepository, pageable, - Collections.singletonList(TagSpecification.ofTarget(controllerId))); - } -} +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java index 4f737a093..4863fbcd0 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java @@ -96,13 +96,6 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw @Pattern(regexp = "[\\S]*", message = "has whitespaces which are not allowed") private String controllerId; - @CascadeOnDelete - @ManyToMany(targetEntity = JpaTargetTag.class) - @JoinTable(name = "sp_target_target_tag", joinColumns = { - @JoinColumn(name = "target", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_target")) }, inverseJoinColumns = { - @JoinColumn(name = "tag", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_tag")) }) - private Set tags; - @CascadeOnDelete @OneToMany(mappedBy = "target", fetch = FetchType.LAZY, targetEntity = JpaAction.class) private List actions; @@ -111,7 +104,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw * the security token of the target which allows if enabled to authenticate * with this security token. */ - @Column(name = "sec_token", updatable = true, nullable = false, length = Target.SECURITY_TOKEN_MAX_SIZE) + @Column(name = "sec_token", nullable = false, length = Target.SECURITY_TOKEN_MAX_SIZE) @Size(min = 1, max = Target.SECURITY_TOKEN_MAX_SIZE) @NotNull private String securityToken; @@ -149,40 +142,50 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw @JoinColumn(name = "assigned_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_assign_ds")) private JpaDistributionSet assignedDistributionSet; - /** - * Read only on management API. Are committed by controller. - */ - @CascadeOnDelete - @ElementCollection - @Column(name = "attribute_value", length = Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE) - @MapKeyColumn(name = "attribute_key", nullable = false, length = Target.CONTROLLER_ATTRIBUTE_KEY_SIZE) - @CollectionTable(name = "sp_target_attributes", joinColumns = { - @JoinColumn(name = "target_id", nullable = false, updatable = false) }, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target")) - private Map 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 + // set default request controller attributes to true, because we want to request them the first time @Column(name = "request_controller_attributes", nullable = false) private boolean requestControllerAttributes = true; - @CascadeOnDelete - @OneToMany(mappedBy = "target", fetch = FetchType.LAZY, targetEntity = JpaTargetMetadata.class) - private List metadata; - @OneToOne(fetch = FetchType.LAZY, mappedBy = "target", orphanRemoval = true) @PrimaryKeyJoinColumn private JpaAutoConfirmationStatus autoConfirmationStatus; + @ManyToOne(fetch = FetchType.LAZY, targetEntity = JpaTargetType.class) + @JoinColumn(name = "target_type", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_relation_target_type")) + private TargetType targetType; + + @CascadeOnDelete + @ManyToMany(targetEntity = JpaTargetTag.class) + @JoinTable( + name = "sp_target_target_tag", + joinColumns = { + @JoinColumn(name = "target", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_target")) }, + inverseJoinColumns = { + @JoinColumn(name = "tag", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_tag")) + }) + private Set tags; + + /** + * Supplied / committed by the controller. Read-only via management API. + */ + @CascadeOnDelete + @ElementCollection + @Column(name = "attribute_value", length = Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE) + @MapKeyColumn(name = "attribute_key", nullable = false, length = Target.CONTROLLER_ATTRIBUTE_KEY_SIZE) + @CollectionTable( + name = "sp_target_attributes", + joinColumns = { + @JoinColumn(name = "target_id", nullable = false, updatable = false) }, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target")) + private Map controllerAttributes; + + @CascadeOnDelete + @OneToMany(mappedBy = "target", fetch = FetchType.LAZY, targetEntity = JpaTargetMetadata.class) + private List metadata; + /** * Constructor. * - * @param controllerId - * controller ID of the {@link Target} + * @param controllerId controller ID of the {@link Target} */ public JpaTarget(final String controllerId) { this(controllerId, SecurityTokenGeneratorHolder.getInstance().generateToken()); @@ -191,22 +194,16 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw /** * Constructor. * - * @param controllerId - * controller ID of the {@link Target} - * @param securityToken - * for target authentication if enabled + * @param controllerId controller ID of the {@link Target} + * @param securityToken for target authentication if enabled */ public JpaTarget(final String controllerId, final String securityToken) { this.controllerId = controllerId; - setName(truncateControllerIdToMaxNameLength(controllerId)); + // truncate controller ID to max name length (if needed) + setName(controllerId != null && controllerId.length() > NAME_MAX_SIZE ? controllerId.substring(0, NAME_MAX_SIZE) : controllerId); this.securityToken = securityToken; } - private static String truncateControllerIdToMaxNameLength(final String controllerId) { - return controllerId != null && controllerId.length() > NAME_MAX_SIZE ? controllerId.substring(0, NAME_MAX_SIZE) - : controllerId; - } - /** * Constructor */ diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetTag.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetTag.java index 0a327aa10..bea23d7d6 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetTag.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetTag.java @@ -9,72 +9,44 @@ */ package org.eclipse.hawkbit.repository.jpa.model; -import java.util.Collections; -import java.util.List; +import java.io.Serial; import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; import jakarta.persistence.Index; -import jakarta.persistence.ManyToMany; import jakarta.persistence.Table; import jakarta.persistence.UniqueConstraint; +import lombok.NoArgsConstructor; +import lombok.ToString; import org.eclipse.hawkbit.repository.event.remote.TargetTagDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagUpdatedEvent; -import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder; -import org.eclipse.persistence.annotations.CascadeOnDelete; import org.eclipse.persistence.descriptors.DescriptorEvent; /** - * A {@link TargetTag} is used to describe Target attributes and use them also - * for filtering the target list. - * + * A {@link TargetTag} is used to describe Target attributes and use them also for filtering the target list. */ +@NoArgsConstructor // Default constructor for JPA. +@ToString(callSuper = true) @Entity @Table(name = "sp_target_tag", indexes = { @Index(name = "sp_idx_target_tag_prim", columnList = "tenant,id"), @Index(name = "sp_idx_target_tag_01", columnList = "tenant,name") }, uniqueConstraints = @UniqueConstraint(columnNames = { "name", "tenant" }, name = "uk_targ_tag")) public class JpaTargetTag extends JpaTag implements TargetTag, EventAwareEntity { + + @Serial private static final long serialVersionUID = 1L; - @CascadeOnDelete - @ManyToMany(mappedBy = "tags", targetEntity = JpaTarget.class, fetch = FetchType.LAZY) - private List assignedToTargets; - - /** - * Constructor. - * - * @param name - * of {@link TargetTag} - * @param description - * of {@link TargetTag} - * @param colour - * of {@link TargetTag} - */ public JpaTargetTag(final String name, final String description, final String colour) { super(name, description, colour); } - public JpaTargetTag() { - // Default constructor for JPA. - } - - public List getAssignedToTargets() { - if (assignedToTargets == null) { - return Collections.emptyList(); - } - - return Collections.unmodifiableList(assignedToTargets); - } - @Override public void fireCreateEvent(final DescriptorEvent descriptorEvent) { EventPublisherHolder.getInstance().getEventPublisher() .publishEvent(new TargetTagCreatedEvent(this, EventPublisherHolder.getInstance().getApplicationId())); - } @Override @@ -87,7 +59,5 @@ public class JpaTargetTag extends JpaTag implements TargetTag, EventAwareEntity public void fireDeleteEvent(final DescriptorEvent descriptorEvent) { EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new TargetTagDeletedEvent(getTenant(), getId(), getClass(), EventPublisherHolder.getInstance().getApplicationId())); - } - -} +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TagSpecification.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TagSpecification.java index c11f559ec..a95fc2338 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TagSpecification.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TagSpecification.java @@ -11,57 +11,28 @@ package org.eclipse.hawkbit.repository.jpa.specifications; import jakarta.persistence.criteria.Join; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag_; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_; -import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; -import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag; -import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag_; -import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetTag; -import org.eclipse.hawkbit.repository.model.Target; -import org.eclipse.hawkbit.repository.model.TargetTag; import org.springframework.data.jpa.domain.Specification; /** - * Specifications class for {@link Tag}s. The class provides Spring Data JPQL - * Specifications. - * + * Specifications class for {@link org.eclipse.hawkbit.repository.model.Tag}s. + * The class provides Spring Data JPQL Specifications. */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) public final class TagSpecification { - private TagSpecification() { - // utility class - } - - /** - * {@link Specification} for retrieving {@link TargetTag}s by assigned - * {@link Target}. - * - * @param controllerId - * of the target - * - * @return the {@link JpaTargetTag} {@link Specification} - */ - public static Specification ofTarget(final String controllerId) { - return (targetRoot, query, criteriaBuilder) -> { - final Join tagJoin = targetRoot.join(JpaTargetTag_.assignedToTargets); - - query.distinct(true); - - return criteriaBuilder.equal(tagJoin.get(JpaTarget_.controllerId), controllerId); - }; - - } /** * {@link Specification} for retrieving {@link DistributionSetTag}s by * assigned {@link DistributionSet}. * - * @param dsId - * of the distribution set - * + * @param dsId of the distribution set * @return the {@link JpaDistributionSetTag} {@link Specification} */ public static Specification ofDistributionSet(final Long dsId) { @@ -75,5 +46,4 @@ public final class TagSpecification { }; } - -} +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java index 5c7d2657d..d40946c32 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java @@ -18,7 +18,6 @@ import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Join; import jakarta.persistence.criteria.JoinType; import jakarta.persistence.criteria.ListJoin; -import jakarta.persistence.criteria.MapJoin; import jakarta.persistence.criteria.Order; import jakarta.persistence.criteria.Path; import jakarta.persistence.criteria.Predicate; @@ -27,13 +26,14 @@ import jakarta.persistence.criteria.SetJoin; import jakarta.persistence.criteria.Subquery; import jakarta.validation.constraints.NotNull; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaAction_; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType_; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_; -import org.eclipse.hawkbit.repository.jpa.model.JpaRollout; import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup_; import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; @@ -57,22 +57,15 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.query.QueryUtils; /** - * Specifications class for {@link Target}s. The class provides Spring Data JPQL - * Specifications. - * + * Specifications class for {@link Target}s. The class provides Spring Data JPQL Specifications. */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) public final class TargetSpecifications { - private TargetSpecifications() { - // utility class - } /** - * {@link Specification} for retrieving {@link Target}s including - * {@link TargetTag}s. - * - * @param controllerIDs - * to search for + * {@link Specification} for retrieving {@link Target}s including {@link TargetTag}s. * + * @param controllerIDs to search for * @return the {@link Target} {@link Specification} */ public static Specification byControllerIdWithTagsInJoin(final Collection controllerIDs) { @@ -87,9 +80,7 @@ public final class TargetSpecifications { /** * {@link Specification} for retrieving {@link Target}s by controllerId * - * @param controllerID - * to search for - * + * @param controllerID to search for * @return the {@link Target} {@link Specification} */ public static Specification hasControllerId(final String controllerID) { @@ -99,9 +90,7 @@ public final class TargetSpecifications { /** * {@link Specification} for retrieving {@link Target}s by controllerId * - * @param controllerIDs - * to search for - * + * @param controllerIDs to search for * @return the {@link Target} {@link Specification} */ public static Specification hasControllerIdIn(final Collection controllerIDs) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java index 998e963b7..d21270703 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java @@ -14,6 +14,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @@ -28,6 +29,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaRollout; import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; +import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository; import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository; import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository; @@ -44,6 +46,7 @@ import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository; import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository; import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository; import org.eclipse.hawkbit.repository.jpa.repository.TenantMetaDataRepository; +import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetTag; @@ -203,6 +206,14 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest .addUpdateActionStatus(entityFactory.actionStatus().create(action.getId()).status(Action.Status.FINISHED)); } + protected Set getTargetTags(final String controllerId) { + return targetRepository + .findOne(TargetSpecifications.hasControllerId(controllerId)) + .map(JpaTarget.class::cast) + .map(JpaTarget::getTags) + .orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId)); + } + private JpaRollout refresh(final Rollout rollout) { return rolloutRepository.findById(rollout.getId()).get(); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementTest.java index 6b5196186..23d4c60b1 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementTest.java @@ -402,7 +402,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { final List assignedTargets = targetManagement.assignTag(assignTarget, targetTag.getId()); assertThat(assignedTargets.size()).as("Assigned targets are wrong").isEqualTo(4); assignedTargets.forEach(target -> assertThat( - targetTagManagement.findByTarget(PAGE, target.getControllerId()).getNumberOfElements()).isEqualTo(1)); + getTargetTags(target.getControllerId()).size()).isEqualTo(1)); final TargetTag findTargetTag = targetTagManagement.getByName("Tag1").orElseThrow(IllegalStateException::new); assertThat(assignedTargets.size()).as("Assigned targets are wrong") @@ -410,7 +410,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { final Target unAssignTarget = targetManagement.unassignTag("targetId123", findTargetTag.getId()); assertThat(unAssignTarget.getControllerId()).as("Controller id is wrong").isEqualTo("targetId123"); - assertThat(targetTagManagement.findByTarget(PAGE, unAssignTarget.getControllerId())).as("Tag size is wrong") + assertThat(getTargetTags(unAssignTarget.getControllerId())).as("Tag size is wrong") .isEmpty(); targetTagManagement.getByName("Tag1").orElseThrow(NoSuchElementException::new); assertThat(targetManagement.findByTag(PAGE, targetTag.getId())).as("Assigned targets are wrong").hasSize(3); @@ -569,7 +569,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { */ private void checkTargetHasTags(final boolean strict, final Iterable targets, final TargetTag... tags) { _target: for (final Target tl : targets) { - for (final Tag tt : targetTagManagement.findByTarget(PAGE, tl.getControllerId())) { + for (final Tag tt : getTargetTags(tl.getControllerId())) { for (final Tag tag : tags) { if (tag.getName().equals(tt.getName())) { continue _target; @@ -588,7 +588,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { targetManagement.getByControllerID(tl.getControllerId()).get(); for (final Tag tag : tags) { - for (final Tag tt : targetTagManagement.findByTarget(PAGE, tl.getControllerId())) { + for (final Tag tt : getTargetTags(tl.getControllerId())) { if (tag.getName().equals(tt.getName())) { fail("Target should have no tags"); } @@ -719,16 +719,16 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { final Target t11 = targetManagement.getByControllerID(t1.getControllerId()) .orElseThrow(IllegalStateException::new); - assertThat(targetTagManagement.findByTarget(PAGE, t11.getControllerId()).getContent()).as("Tag size is wrong") + assertThat(getTargetTags(t11.getControllerId())).as("Tag size is wrong") .hasSize(noT1Tags).containsAll(t1Tags); - assertThat(targetTagManagement.findByTarget(PAGE, t11.getControllerId()).getContent()).as("Tag size is wrong") + assertThat(getTargetTags(t11.getControllerId())).as("Tag size is wrong") .hasSize(noT1Tags).doesNotContain(toArray(t2Tags, TargetTag.class)); final Target t21 = targetManagement.getByControllerID(t2.getControllerId()) .orElseThrow(IllegalStateException::new); - assertThat(targetTagManagement.findByTarget(PAGE, t21.getControllerId()).getContent()).as("Tag size is wrong") + assertThat(getTargetTags(t21.getControllerId())).as("Tag size is wrong") .hasSize(noT2Tags).containsAll(t2Tags); - assertThat(targetTagManagement.findByTarget(PAGE, t21.getControllerId()).getContent()).as("Tag size is wrong") + assertThat(getTargetTags(t21.getControllerId())).as("Tag size is wrong") .hasSize(noT2Tags).doesNotContain(toArray(t1Tags, TargetTag.class)); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetTagManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetTagManagementTest.java index 09f16abc6..041fcad7c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetTagManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetTagManagementTest.java @@ -69,7 +69,7 @@ class TargetTagManagementTest extends AbstractJpaIntegrationTest { verifyThrownExceptionBy(() -> targetTagManagement.update(entityFactory.tag().update(NOT_EXIST_IDL)), "TargetTag"); - verifyThrownExceptionBy(() -> targetTagManagement.findByTarget(PAGE, NOT_EXIST_ID), "Target"); + verifyThrownExceptionBy(() -> getTargetTags(NOT_EXIST_ID), "Target"); } @Test @@ -237,7 +237,7 @@ class TargetTagManagementTest extends AbstractJpaIntegrationTest { final TargetTag toDelete = tags.iterator().next(); for (final Target target : targetRepository.findAll()) { - assertThat(targetTagManagement.findByTarget(PAGE, target.getControllerId()).getContent()) + assertThat(getTargetTags(target.getControllerId())) .contains(toDelete); } @@ -246,7 +246,7 @@ class TargetTagManagementTest extends AbstractJpaIntegrationTest { // check for (final Target target : targetRepository.findAll()) { - assertThat(targetTagManagement.findByTarget(PAGE, target.getControllerId()).getContent()) + assertThat(getTargetTags(target.getControllerId())) .doesNotContain(toDelete); } assertThat(targetTagRepository.findById(toDelete.getId())).as("No tag should be found").isNotPresent(); diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetTagResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetTagResourceTest.java index e9b275c0e..09f5d43e0 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetTagResourceTest.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetTagResourceTest.java @@ -53,8 +53,7 @@ import io.qameta.allure.Story; @Story("Target Tag Resource") public class MgmtTargetTagResourceTest extends AbstractManagementApiIntegrationTest { - private static final String TARGETTAGS_ROOT = "http://localhost" + MgmtRestConstants.TARGET_TAG_V1_REQUEST_MAPPING - + "/"; + private static final String TARGETTAGS_ROOT = "http://localhost" + MgmtRestConstants.TARGET_TAG_V1_REQUEST_MAPPING + "/"; @Test @Description("Verfies that a paged result list of target tags reflects the content on the repository side.") @@ -86,49 +85,9 @@ public class MgmtTargetTagResourceTest extends AbstractManagementApiIntegrationT .andExpect(status().isOk()).andDo(MockMvcResultPrinter.print()); } - @Test - @Description("Verifies that a paged result list of target tags reflects on the content of assigned tags for specific controller/target ID") - public void getTargetTagsByTargetId() throws Exception { - final String controllerId1 = "controllerTestId1"; - final String controllerId2 = "controllerTestId2"; - testdataFactory.createTarget(controllerId1); - testdataFactory.createTarget(controllerId2); - - final List tags = testdataFactory.createTargetTags(2, ""); - final TargetTag tag1 = tags.get(0); - final TargetTag tag2 = tags.get(1); - - targetManagement.toggleTagAssignment(List.of(controllerId1, controllerId2), tag1.getName()); - targetManagement.toggleTagAssignment(List.of(controllerId2), tag2.getName()); - - mvc.perform(get(MgmtRestConstants.TARGET_TAG_V1_REQUEST_MAPPING) - .queryParam(MgmtRestConstants.REQUEST_PARAMETER_SEARCH, "target.controllerId==" + controllerId2) - .accept(MediaType.APPLICATION_JSON)) - .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) - .andExpect(applyTagMatcherOnPagedResult(tag1)) - .andExpect(applyTagMatcherOnPagedResult(tag2)) - .andExpect(applySelfLinkMatcherOnPagedResult(tag1, TARGETTAGS_ROOT + tag1.getId())) - .andExpect(applySelfLinkMatcherOnPagedResult(tag2, TARGETTAGS_ROOT + tag2.getId())) - .andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_TOTAL, equalTo(2))) - .andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_SIZE, equalTo(2))) - .andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_CONTENT, hasSize(2))); - - mvc.perform(get(MgmtRestConstants.TARGET_TAG_V1_REQUEST_MAPPING) - .queryParam(MgmtRestConstants.REQUEST_PARAMETER_SEARCH, "target.controllerId==" + controllerId1) - .accept(MediaType.APPLICATION_JSON)) - .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) - .andExpect(applyTagMatcherOnPagedResult(tag1)) - .andExpect(applySelfLinkMatcherOnPagedResult(tag1, TARGETTAGS_ROOT + tag1.getId())) - .andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_TOTAL, equalTo(1))) - .andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_SIZE, equalTo(1))) - .andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_CONTENT, hasSize(1))); - } - @Test @Description("Verifies that a page result when listing tags reflects on the content in the repository when filtered by 2 fields - one tag field and one target field") - public void getTargetTagsFilteredByColorAndTargetId() throws Exception { + public void getTargetTagsFilteredByColor() throws Exception { final String controllerId1 = "controllerTestId1"; final String controllerId2 = "controllerTestId2"; testdataFactory.createTarget(controllerId1); @@ -139,21 +98,22 @@ public class MgmtTargetTagResourceTest extends AbstractManagementApiIntegrationT final TargetTag tag2 = tags.get(1); targetManagement.toggleTagAssignment(List.of(controllerId1, controllerId2), tag1.getName()); - targetManagement.toggleTagAssignment(List.of(controllerId2), tag2.getName()); - + targetManagement.toggleTagAssignment(List.of(controllerId2), tag2.getName()) + ; // pass here q directly as a pure string because .queryParam method delimiters the parameters in q with , // which is logical OR, we want AND here mvc.perform(get(MgmtRestConstants.TARGET_TAG_V1_REQUEST_MAPPING + - "?" + MgmtRestConstants.REQUEST_PARAMETER_SEARCH + "=target.controllerId==" + controllerId2 + ";colour==" + tag1.getColour()) + "?" + MgmtRestConstants.REQUEST_PARAMETER_SEARCH + "=colour==" + tag2.getColour()) .accept(MediaType.APPLICATION_JSON)) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) - .andExpect(applyTagMatcherOnPagedResult(tag1)) - .andExpect(applySelfLinkMatcherOnPagedResult(tag1, TARGETTAGS_ROOT + tag1.getId())) + .andExpect(applyTagMatcherOnPagedResult(tag2)) + .andExpect(applySelfLinkMatcherOnPagedResult(tag2, TARGETTAGS_ROOT + tag2.getId())) .andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_TOTAL, equalTo(1))) .andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_SIZE, equalTo(1))) .andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_CONTENT, hasSize(1))); } + @Test @Description("Verfies that a single result of a target tag reflects the content on the repository side.") @ExpectEvents({ @Expect(type = TargetTagCreatedEvent.class, count = 2) })