diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetTagManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetTagManagement.java index 4382b099b..762b0a4ef 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetTagManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetTagManagement.java @@ -70,4 +70,7 @@ public interface DistributionSetTagManagement extends RepositoryManagement findByDistributionSet(@NotNull Pageable pageable, long setId); + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + Page findByRsqlWithDistributionSetTagSpec(@NotNull Pageable pageable, @NotNull String rsqlParam); + } 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 a4fc90e62..7cdec0787 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 @@ -135,6 +135,9 @@ public interface TargetTagManagement { @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) Page findByRsql(@NotNull Pageable pageable, @NotNull String rsqlParam); + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + Page findByRsqlWithTargetTagSpec(@NotNull Pageable pageable, @NotNull String rsqlParam); + /** * Find {@link TargetTag} based on given Name. * diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTagManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTagManagement.java index 9ac715853..2e0df2f13 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTagManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTagManagement.java @@ -8,12 +8,15 @@ */ package org.eclipse.hawkbit.repository.jpa; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import org.eclipse.hawkbit.repository.DistributionSetFields; +import org.eclipse.hawkbit.repository.DistributionSetTagFields; import org.eclipse.hawkbit.repository.DistributionSetTagManagement; import org.eclipse.hawkbit.repository.TagFields; import org.eclipse.hawkbit.repository.TargetTagManagement; @@ -23,8 +26,10 @@ import org.eclipse.hawkbit.repository.builder.TagUpdate; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.jpa.builder.JpaTagCreate; import org.eclipse.hawkbit.repository.jpa.configuration.Constants; +import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; +import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetSpecification; import org.eclipse.hawkbit.repository.jpa.specifications.TagSpecification; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetTag; @@ -146,6 +151,15 @@ public class JpaDistributionSetTagManagement implements DistributionSetTagManage Collections.singletonList(TagSpecification.ofDistributionSet(setId))); } + @Override + public Page findByRsqlWithDistributionSetTagSpec(Pageable pageable, String rsqlParam) { + final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetTagFields.class, + virtualPropertyReplacer, database); + + return JpaManagementHelper.findAllWithCountBySpec(distributionSetTagRepository, pageable, + Collections.singletonList(spec)); + } + @Override @Transactional @Retryable(include = { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetTagManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetTagManagement.java index cfa16dd19..1b21da9ca 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetTagManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetTagManagement.java @@ -15,6 +15,7 @@ import java.util.Optional; import java.util.stream.Collectors; import org.eclipse.hawkbit.repository.TagFields; +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; @@ -108,6 +109,12 @@ public class JpaTargetTagManagement implements TargetTagManagement { RSQLUtility.buildRsqlSpecification(rsqlParam, TagFields.class, virtualPropertyReplacer, database))); } + @Override + public Page findByRsqlWithTargetTagSpec(Pageable pageable, String rsqlParam) { + return JpaManagementHelper.findAllWithCountBySpec(targetTagRepository, pageable, Collections.singletonList( + RSQLUtility.buildRsqlSpecification(rsqlParam, TargetTagFields.class, virtualPropertyReplacer, database))); + } + @Override public long count() { return targetTagRepository.count(); diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTagResource.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTagResource.java index 81a058ab8..3e70d6e00 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTagResource.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTagResource.java @@ -80,7 +80,7 @@ public class MgmtDistributionSetTagResource implements MgmtDistributionSetTagRes count = distributionSetTagManagement.count(); } else { - final Page page = distributionSetTagManagement.findByRsql(pageable, rsqlParam); + final Page page = distributionSetTagManagement.findByRsqlWithDistributionSetTagSpec(pageable, rsqlParam); distributionSetTags = page; count = page.getTotalElements(); diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetTagResource.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetTagResource.java index cdf8cd442..f8dcd3d4f 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetTagResource.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetTagResource.java @@ -11,6 +11,8 @@ package org.eclipse.hawkbit.mgmt.rest.resource; import java.util.List; import java.util.stream.Collectors; +import cz.jirutka.rsql.parser.RSQLParser; +import cz.jirutka.rsql.parser.ast.RSQLOperators; import org.eclipse.hawkbit.mgmt.json.model.PagedList; import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtAssignedTargetRequestBody; import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTag; @@ -85,7 +87,8 @@ public class MgmtTargetTagResource implements MgmtTargetTagRestApi { findTargetsAll = this.tagManagement.findAll(pageable); } else { - findTargetsAll = this.tagManagement.findByRsql(pageable, rsqlParam); + findTargetsAll = this.tagManagement.findByRsqlWithTargetTagSpec(pageable, rsqlParam); + } final List rest = MgmtTagMapper.toResponse(findTargetsAll.getContent()); diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTagResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTagResourceTest.java index dff042c6b..f863e88f7 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTagResourceTest.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTagResourceTest.java @@ -73,6 +73,68 @@ public class MgmtDistributionSetTagResourceTest extends AbstractManagementApiInt .andExpect(jsonPath(MgmtTargetResourceTest.JSON_PATH_PAGED_LIST_CONTENT, hasSize(2))); } + @Test + @Description("Verfies that a paged result list of DS tags reflects the content on the repository side when filtered by distribution set id.") + public void getDistributionSetTagsByDistributionSetId() throws Exception { + final List tags = testdataFactory.createDistributionSetTags(2); + final DistributionSetTag tag1 = tags.get(0); + final DistributionSetTag tag2 = tags.get(1); + + final DistributionSet distributionSet1 = testdataFactory.createDistributionSet(); + final DistributionSet distributionSet2 = testdataFactory.createDistributionSet(); + distributionSetManagement.toggleTagAssignment(List.of(distributionSet1.getId(), distributionSet2.getId()), tag1.getName()); + distributionSetManagement.toggleTagAssignment(List.of(distributionSet1.getId()), tag2.getName()); + + mvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING) + .queryParam(MgmtRestConstants.REQUEST_PARAMETER_SEARCH, "distributionset.id==" + distributionSet1.getId()) + .accept(MediaType.APPLICATION_JSON)) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(applyBaseEntityMatcherOnPagedResult(tag1)) + .andExpect(applyBaseEntityMatcherOnPagedResult(tag2)) + .andExpect(applySelfLinkMatcherOnPagedResult(tag1, DISTRIBUTIONSETTAGS_ROOT + tag1.getId())) + .andExpect(applySelfLinkMatcherOnPagedResult(tag2, DISTRIBUTIONSETTAGS_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.DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING) + .queryParam(MgmtRestConstants.REQUEST_PARAMETER_SEARCH, "distributionset.id==" + distributionSet2.getId()) + .accept(MediaType.APPLICATION_JSON)) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(applyBaseEntityMatcherOnPagedResult(tag1)) + .andExpect(applySelfLinkMatcherOnPagedResult(tag1, DISTRIBUTIONSETTAGS_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("Verfies that a paged result list of DS tags reflects the content on the repository side when filtered by distribution set id field AND tag field.") + public void getDistributionSetTagsByDistributionSetIdAndTagDescription() throws Exception { + final List tags = testdataFactory.createDistributionSetTags(2); + final DistributionSetTag tag1 = tags.get(0); + final DistributionSetTag tag2 = tags.get(1); + + final DistributionSet distributionSet1 = testdataFactory.createDistributionSet(); + final DistributionSet distributionSet2 = testdataFactory.createDistributionSet(); + distributionSetManagement.toggleTagAssignment(List.of(distributionSet1.getId(), distributionSet2.getId()), tag1.getName()); + distributionSetManagement.toggleTagAssignment(List.of(distributionSet1.getId()), tag2.getName()); + + mvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING + + "?" + MgmtRestConstants.REQUEST_PARAMETER_SEARCH + + "=distributionset.id==" + distributionSet1.getId() + ";description==" + tag1.getDescription()) + .accept(MediaType.APPLICATION_JSON)) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(applyBaseEntityMatcherOnPagedResult(tag1)) + .andExpect(applySelfLinkMatcherOnPagedResult(tag1, DISTRIBUTIONSETTAGS_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("Verfies that a single result of a DS tag reflects the content on the repository side.") @ExpectEvents({ @Expect(type = DistributionSetTagCreatedEvent.class, count = 2) }) 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 6369d698d..2e6476364 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 @@ -75,6 +75,74 @@ public class MgmtTargetTagResourceTest extends AbstractManagementApiIntegrationT } + @Test + @Description("Verfies 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 { + 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()); + + // 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()) + .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("Verfies that a single result of a target tag reflects the content on the repository side.") @ExpectEvents({ @Expect(type = TargetTagCreatedEvent.class, count = 2) }) diff --git a/hawkbit-runtime/hawkbit-update-server/pom.xml b/hawkbit-runtime/hawkbit-update-server/pom.xml index b75a5dc20..531c2b53d 100644 --- a/hawkbit-runtime/hawkbit-update-server/pom.xml +++ b/hawkbit-runtime/hawkbit-update-server/pom.xml @@ -96,7 +96,7 @@ org.mariadb.jdbc mariadb-java-client - test + org.eclipse.hawkbit