diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/DistributionSetTagFields.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/DistributionSetTagFields.java new file mode 100644 index 000000000..db8aaa5de --- /dev/null +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/DistributionSetTagFields.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2023 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.repository; + +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 DistributionSet in order + * filtering over distribution set fields also. + */ +public enum DistributionSetTagFields implements FieldNameProvider { + /** + * The id field. + */ + ID(TagFields.ID.getFieldName()), + + /** + * The name field. + */ + NAME(TagFields.NAME.getFieldName()), + /** + * The description field. + */ + DESCRIPTION(TagFields.DESCRIPTION.getFieldName()), + /** + * The controllerId field. + */ + COLOUR(TagFields.COLOUR.getFieldName()), + + /** + * Distribution set fields + */ + DISTRIBUTIONSET("assignedToDistributionSet", + DistributionSetFields.ID.getFieldName(), DistributionSetFields.NAME.getFieldName()); + + private final String fieldName; + + private final List subEntityAttributes; + + private DistributionSetTagFields(final String fieldName) { + this.fieldName = fieldName; + this.subEntityAttributes = Collections.emptyList(); + } + + private DistributionSetTagFields(final String fieldName, final String... subEntityAttributes) { + this.fieldName = fieldName; + this.subEntityAttributes = Arrays.asList(subEntityAttributes); + } + + @Override + public List getSubEntityAttributes() { + return Collections.unmodifiableList(subEntityAttributes); + } + + @Override + public String getFieldName() { + return fieldName; + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..f042b4bb1 --- /dev/null +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/TargetTagFields.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2023 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.repository; + +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 + * filtering over target fields also. + */ +public enum TargetTagFields implements FieldNameProvider { + /** + * The id field. + */ + ID(TagFields.ID.getFieldName()), + + /** + * The name field. + */ + NAME(TagFields.NAME.getFieldName()), + /** + * The description field. + */ + DESCRIPTION(TagFields.DESCRIPTION.getFieldName()), + /** + * The controllerId field. + */ + COLOUR(TagFields.COLOUR.getFieldName()), + + /** + * Target fields + */ + TARGET("assignedToTargets", + TargetFields.ID.getFieldName(), TargetFields.NAME.getFieldName()); + + private final String fieldName; + + private final List subEntityAttributes; + + private 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 + public String getFieldName() { + return fieldName; + } +} \ No newline at end of file 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..4659601d0 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 @@ -14,6 +14,7 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import org.eclipse.hawkbit.repository.DistributionSetTagFields; import org.eclipse.hawkbit.repository.DistributionSetTagManagement; import org.eclipse.hawkbit.repository.TagFields; import org.eclipse.hawkbit.repository.TargetTagManagement; @@ -129,7 +130,7 @@ public class JpaDistributionSetTagManagement implements DistributionSetTagManage @Override public Page findByRsql(final Pageable pageable, final String rsqlParam) { - final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam, TagFields.class, + final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetTagFields.class, virtualPropertyReplacer, database); return JpaManagementHelper.findAllWithCountBySpec(distributionSetTagRepository, pageable, 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..c77418c0b 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; @@ -105,7 +106,7 @@ public class JpaTargetTagManagement implements TargetTagManagement { @Override public Page findByRsql(final Pageable pageable, final String rsqlParam) { return JpaManagementHelper.findAllWithCountBySpec(targetTagRepository, pageable, Collections.singletonList( - RSQLUtility.buildRsqlSpecification(rsqlParam, TagFields.class, virtualPropertyReplacer, database))); + RSQLUtility.buildRsqlSpecification(rsqlParam, TargetTagFields.class, virtualPropertyReplacer, database))); } @Override 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..fb639b121 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 @@ -33,7 +33,6 @@ import org.eclipse.hawkbit.utils.TenantConfigHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.http.HttpStatus; @@ -86,6 +85,7 @@ public class MgmtTargetTagResource implements MgmtTargetTagRestApi { } else { findTargetsAll = this.tagManagement.findByRsql(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..e136d606f 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,70 @@ 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()); + + // 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.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) })