diff --git a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtTargetFilterQueryRestApi.java b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtTargetFilterQueryRestApi.java index e4993e832..b12efed17 100644 --- a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtTargetFilterQueryRestApi.java +++ b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtTargetFilterQueryRestApi.java @@ -68,7 +68,8 @@ public interface MgmtTargetFilterQueryRestApi { @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam, - @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam); + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_REPRESENTATION_MODE, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_REPRESENTATION_MODE_DEFAULT) String representationModeParam); /** * Handles the POST request of creating new target filters. The request body diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java index 9746a06ba..0b49af13c 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java @@ -281,7 +281,7 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi { return ResponseEntity .ok(new PagedList<>(MgmtTargetFilterQueryMapper.toResponse(targetFilterQueries.getContent(), - tenantConfigHelper.isConfirmationFlowEnabled()), targetFilterQueries.getTotalElements())); + tenantConfigHelper.isConfirmationFlowEnabled(), false), targetFilterQueries.getTotalElements())); } @Override diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java index 05add94ae..1782e4e3a 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java @@ -18,6 +18,9 @@ import java.util.stream.Collectors; import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtDistributionSetAutoAssignment; import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery; import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryRequestBody; +import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetRestApi; +import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode; +import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetFilterQueryRestApi; import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate; @@ -25,6 +28,7 @@ import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate; import org.eclipse.hawkbit.repository.model.Action.ActionType; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; +import org.springframework.hateoas.Link; import org.springframework.util.CollectionUtils; /** @@ -39,14 +43,15 @@ public final class MgmtTargetFilterQueryMapper { } static List toResponse(final List filters, - final boolean confirmationFlowEnabled) { + final boolean confirmationFlowEnabled, final boolean isRepresentationFull) { if (CollectionUtils.isEmpty(filters)) { return Collections.emptyList(); } - return filters.stream().map(filter -> toResponse(filter, confirmationFlowEnabled)).collect(Collectors.toList()); + return filters.stream().map(filter -> toResponse(filter, confirmationFlowEnabled, isRepresentationFull)).collect(Collectors.toList()); } - static MgmtTargetFilterQuery toResponse(final TargetFilterQuery filter, final boolean confirmationFlowEnabled) { + static MgmtTargetFilterQuery toResponse(final TargetFilterQuery filter, final boolean confirmationFlowEnabled, + final boolean isReprentationFull) { final MgmtTargetFilterQuery targetRest = new MgmtTargetFilterQuery(); targetRest.setFilterId(filter.getId()); targetRest.setName(filter.getName()); @@ -70,6 +75,12 @@ public final class MgmtTargetFilterQueryMapper { targetRest.add( linkTo(methodOn(MgmtTargetFilterQueryRestApi.class).getFilter(filter.getId())).withSelfRel().expand()); + if (isReprentationFull && distributionSet != null) { + targetRest.add( + linkTo(methodOn(MgmtDistributionSetRestApi.class).getDistributionSets(Integer.parseInt(MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET), + Integer.parseInt(MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT), null, + "name==" + distributionSet.getName() + ";version==" + distributionSet.getVersion())).withRel("DS").expand()); + } return targetRest; } diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java index ce4fb3f04..b09aa3bef 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java @@ -15,6 +15,7 @@ import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet; import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtDistributionSetAutoAssignment; import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery; import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryRequestBody; +import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetFilterQueryRestApi; import org.eclipse.hawkbit.repository.EntityFactory; @@ -66,7 +67,7 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA final TargetFilterQuery findTarget = findFilterWithExceptionIfNotFound(filterId); // to single response include poll status final MgmtTargetFilterQuery response = MgmtTargetFilterQueryMapper.toResponse(findTarget, - tenantConfigHelper.isConfirmationFlowEnabled()); + tenantConfigHelper.isConfirmationFlowEnabled(), true); MgmtTargetFilterQueryMapper.addLinks(response); return ResponseEntity.ok(response); @@ -77,7 +78,9 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam, - @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam) { + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam, + @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_REPRESENTATION_MODE, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_REPRESENTATION_MODE_DEFAULT) String representationModeParam) { + final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam); final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam); @@ -95,8 +98,10 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA countTargetsAll = filterManagement.count(); } + final boolean isRepresentationFull = parseRepresentationMode(representationModeParam) == MgmtRepresentationMode.FULL; + final List rest = MgmtTargetFilterQueryMapper - .toResponse(findTargetFiltersAll.getContent(), tenantConfigHelper.isConfirmationFlowEnabled()); + .toResponse(findTargetFiltersAll.getContent(), tenantConfigHelper.isConfirmationFlowEnabled(), isRepresentationFull); return ResponseEntity.ok(new PagedList<>(rest, countTargetsAll)); } @@ -107,7 +112,7 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA .create(MgmtTargetFilterQueryMapper.fromRequest(entityFactory, filter)); final MgmtTargetFilterQuery response = MgmtTargetFilterQueryMapper.toResponse(createdTarget, - tenantConfigHelper.isConfirmationFlowEnabled()); + tenantConfigHelper.isConfirmationFlowEnabled(), false); MgmtTargetFilterQueryMapper.addLinks(response); return new ResponseEntity<>(response, HttpStatus.CREATED); @@ -123,7 +128,7 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA .query(targetFilterRest.getQuery())); final MgmtTargetFilterQuery response = MgmtTargetFilterQueryMapper.toResponse(updateFilter, - tenantConfigHelper.isConfirmationFlowEnabled()); + tenantConfigHelper.isConfirmationFlowEnabled(), false); MgmtTargetFilterQueryMapper.addLinks(response); return ResponseEntity.ok(response); @@ -151,7 +156,7 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA final TargetFilterQuery updateFilter = filterManagement.updateAutoAssignDS(update); final MgmtTargetFilterQuery response = MgmtTargetFilterQueryMapper.toResponse(updateFilter, - tenantConfigHelper.isConfirmationFlowEnabled()); + tenantConfigHelper.isConfirmationFlowEnabled(), false); MgmtTargetFilterQueryMapper.addLinks(response); return ResponseEntity.ok(response); @@ -185,4 +190,12 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA .orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, filterId)); } + private static MgmtRepresentationMode parseRepresentationMode(final String representationModeParam) { + return MgmtRepresentationMode.fromValue(representationModeParam).orElseGet(() -> { + // no need for a 400, just apply a safe fallback + LOG.warn("Received an invalid representation mode: {}", representationModeParam); + return MgmtRepresentationMode.COMPACT; + }); + } + } diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResourceTest.java index 698975d44..e59d9a5a8 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResourceTest.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResourceTest.java @@ -11,6 +11,7 @@ package org.eclipse.hawkbit.mgmt.rest.resource; import static org.assertj.core.api.Assertions.assertThat; import static org.eclipse.hawkbit.rest.util.MockMvcResultPrinter.print; import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.hasSize; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; @@ -20,6 +21,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.stream.Stream; @@ -37,6 +39,7 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.rest.exception.MessageNotReadableException; import org.eclipse.hawkbit.rest.json.model.ExceptionInfo; import org.json.JSONObject; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -49,6 +52,7 @@ import io.qameta.allure.Description; import io.qameta.allure.Feature; import io.qameta.allure.Step; import io.qameta.allure.Story; +import org.springframework.web.util.UriUtils; /** * Spring MVC Tests against the MgmtTargetResource. @@ -220,6 +224,47 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte .andExpect(jsonPath("$.content.[?(@.name=='" + idA + "')].query", contains(testQuery))); } + @Test + public void checkIfFullRepresentationInTargetFilterReturnsDistributionSetHrefWithFilter() throws Exception { + final String testQuery = "name==test"; + + final DistributionSet set = testdataFactory.createDistributionSet(); + final TargetFilterQuery filterQuery = createSingleTargetFilterQuery("a", testQuery); + final String hrefPrefix = "http://localhost" + MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + + filterQuery.getId(); + final String distributionsetHrefPrefix = "http://localhost" + MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING; + + final String dsQuery = "?offset=0&limit=50&q=name==" + set.getName() + ";" + "version==" + set.getVersion(); + + mvc.perform( + post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId() + "/autoAssignDS") + .content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON)) + .andDo(print()).andExpect(status().isOk()); + + final String result = mvc.perform( + get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId())) + .andExpect(jsonPath("$._links.autoAssignDS.href", equalTo(hrefPrefix + "/autoAssignDS"))) + .andExpect(jsonPath("$._links.DS.href", startsWith(distributionsetHrefPrefix))) + .andReturn().getResponse().getContentAsString(); + + final String multipleResult = mvc.perform( + get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "?representation=full")) + .andExpect(jsonPath("$.content", hasSize(1))).andExpect(jsonPath("$.total", equalTo(1))) + .andExpect(jsonPath("$.content[0]._links.DS.href", startsWith(distributionsetHrefPrefix))) + .andReturn().getResponse().getContentAsString(); + + final JSONObject singleJson = new JSONObject(result); + final JSONObject multipleJson = new JSONObject(multipleResult); + + final String resultDSURI = singleJson.getJSONObject("_links").getJSONObject("DS").getString("href"); + final String resultDSURIFromMultipleJson = multipleJson.getJSONArray("content").getJSONObject(0) + .getJSONObject("_links").getJSONObject("DS").getString("href"); + + Assertions.assertEquals(distributionsetHrefPrefix + dsQuery, UriUtils.decode(resultDSURI, StandardCharsets.UTF_8)); + Assertions.assertEquals(distributionsetHrefPrefix + dsQuery, + UriUtils.decode(resultDSURIFromMultipleJson, StandardCharsets.UTF_8)); + } + @Test @Description("Ensures that request returns list of filters in defined format in size reduced by given limit and offset parameter.") public void getTargetWithPagingLimitAndOffsetRequestParameter() throws Exception { @@ -333,6 +378,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte final DistributionSet set = testdataFactory.createDistributionSet(); final TargetFilterQuery filterQuery = createSingleTargetFilterQuery("1", "controllerId==target*"); + mvc.perform( post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId() + "/autoAssignDS") .content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON)) diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetFilterQueriesResourceDocumentationTest.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetFilterQueriesResourceDocumentationTest.java index 1e47462d5..4033d4d1f 100644 --- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetFilterQueriesResourceDocumentationTest.java +++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetFilterQueriesResourceDocumentationTest.java @@ -250,7 +250,8 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes fieldWithPath(arrayPrefix + "lastModifiedAt").description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT), fieldWithPath(arrayPrefix + "lastModifiedBy").description(ApiModelPropertiesGeneric.LAST_MODIFIED_BY), fieldWithPath(arrayPrefix + "_links.self").ignored(), fieldWithPath(arrayPrefix + "_links.autoAssignDS") - .description(MgmtApiModelProperties.TARGET_FILTER_QUERY_LINK_AUTO_ASSIGN_DS)); + .description(MgmtApiModelProperties.TARGET_FILTER_QUERY_LINK_AUTO_ASSIGN_DS), + optionalRequestFieldWithPath(arrayPrefix + "_links.DS").ignored()); } private String createTargetFilterQueryJson(final String name, final String query)