Introduce soft deleted list option for soft deletable entities (#3093)

* Introduce soft deleted list option for soft deletable entities

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>

* fix verify build

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>

* fix typo in license

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>

* Add sorting option on deleted field

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>

* add missing import in tests

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>

* Rename SoftDeletedFilter to SoftDeletedMode and its values

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>

* Introduce MgmtSoftDeletedMode on api layer

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>

* remove unused imports

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>

* Integrate the enum on API layer

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>

* Fix OpenApi spec

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>

* address some comments

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>

* Get rid of count(SoftDeletedMode) at all

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>

* remove formatter for enum - stop supporting lowercase values in API

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>

---------

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>
This commit is contained in:
Stanislav Trailov
2026-06-08 16:37:05 +03:00
committed by GitHub
parent 95680962cc
commit f44b6268b0
45 changed files with 1230 additions and 101 deletions

View File

@@ -10,6 +10,8 @@
package org.eclipse.hawkbit.mgmt.rest.api;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.DISTRIBUTION_SET_ORDER;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE_DEFAULT;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT;
@@ -103,7 +105,10 @@ public interface MgmtDistributionSetRestApi {
@Schema(description = "The query parameter sort allows to define the sort order for the result of a query. " +
"A sort criteria consists of the name of a field and the sort direction (ASC for ascending and DESC descending)." +
"The sequence of the sort criteria (multiple can be used) defines the sort order of the entities in the result.")
String sortParam);
String sortParam,
@RequestParam(value = REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, defaultValue = REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE_DEFAULT, required = false)
@Schema(description = "Controls whether soft-deleted distribution sets are included in the result.")
MgmtSoftDeletedMode softDeletedMode);
/**
* Handles the GET request of retrieving a single DistributionSet .

View File

@@ -10,6 +10,8 @@
package org.eclipse.hawkbit.mgmt.rest.api;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.DISTRIBUTION_SET_TYPE_ORDER;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE_DEFAULT;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT;
@@ -91,7 +93,10 @@ public interface MgmtDistributionSetTypeRestApi {
@Schema(description = "The query parameter sort allows to define the sort order for the result of a query. " +
"A sort criteria consists of the name of a field and the sort direction (ASC for ascending and DESC descending)." +
"The sequence of the sort criteria (multiple can be used) defines the sort order of the entities in the result.")
String sortParam);
String sortParam,
@RequestParam(value = REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, defaultValue = REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE_DEFAULT, required = false)
@Schema(description = "Controls whether soft-deleted distribution set types are included in the result.")
MgmtSoftDeletedMode softDeletedMode);
/**
* Handles the GET request of retrieving a single DistributionSetType within.

View File

@@ -39,6 +39,17 @@ public final class MgmtRestConstants {
* The request parameter for specifying the representation mode. The value of this parameter can either be "full" or "compact".
*/
public static final String REQUEST_PARAMETER_REPRESENTATION_MODE = "representation";
/**
* The request parameter for specifying the soft deletion listing of entities. The value of this parameter
* can be ONLY_SOFT_DELETED, EXCLUDE_SOFT_DELETED or INCLUDE_SOFT_DELETED
*/
public static final String REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE = "soft_deleted_mode";
/**
* Default value of the soft deleted mode parameter.
*/
public static final String REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE_DEFAULT = "EXCLUDE_SOFT_DELETED";
/**
* The default representation mode.
*/

View File

@@ -9,6 +9,8 @@
*/
package org.eclipse.hawkbit.mgmt.rest.api;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE_DEFAULT;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT;
@@ -93,7 +95,10 @@ public interface MgmtRolloutRestApi {
"The sequence of the sort criteria (multiple can be used) defines the sort order of the entities in the result.")
String sortParam,
@RequestParam(value = REQUEST_PARAMETER_REPRESENTATION_MODE, defaultValue = REQUEST_PARAMETER_REPRESENTATION_MODE_DEFAULT)
String representationModeParam);
String representationModeParam,
@RequestParam(value = REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, defaultValue = REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE_DEFAULT, required = false)
@Schema(description = "Controls whether soft-deleted rollouts are included in the result.")
MgmtSoftDeletedMode softDeletedMode);
/**
* Handles the GET request of retrieving a single rollout.

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2026 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.mgmt.rest.api;
import java.util.Optional;
public enum MgmtSoftDeletedMode {
ONLY_SOFT_DELETED,
EXCLUDE_SOFT_DELETED,
INCLUDE_SOFT_DELETED;
public static Optional<MgmtSoftDeletedMode> fromValue(final String value) {
if (value == null) {
return Optional.empty();
}
try {
return Optional.of(MgmtSoftDeletedMode.valueOf(value.toUpperCase()));
} catch (final IllegalArgumentException e) {
return Optional.empty();
}
}
}

View File

@@ -9,6 +9,8 @@
*/
package org.eclipse.hawkbit.mgmt.rest.api;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE_DEFAULT;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT;
@@ -186,7 +188,10 @@ public interface MgmtSoftwareModuleRestApi {
@Schema(description = "The query parameter sort allows to define the sort order for the result of a query. " +
"A sort criteria consists of the name of a field and the sort direction (ASC for ascending and DESC descending)." +
"The sequence of the sort criteria (multiple can be used) defines the sort order of the entities in the result.")
String sortParam);
String sortParam,
@RequestParam(value = REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, defaultValue = REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE_DEFAULT, required = false)
@Schema(description = "Controls whether soft-deleted software modules are included in the result.")
MgmtSoftDeletedMode softDeletedMode);
/**
* Handles the GET request of retrieving a single software module.

View File

@@ -9,6 +9,8 @@
*/
package org.eclipse.hawkbit.mgmt.rest.api;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE_DEFAULT;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT;
@@ -86,7 +88,10 @@ public interface MgmtSoftwareModuleTypeRestApi {
@Schema(description = "The query parameter sort allows to define the sort order for the result of a query. " +
"A sort criteria consists of the name of a field and the sort direction (ASC for ascending and DESC descending)." +
"The sequence of the sort criteria (multiple can be used) defines the sort order of the entities in the result.")
String sortParam);
String sortParam,
@RequestParam(value = REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, defaultValue = REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE_DEFAULT, required = false)
@Schema(description = "Controls whether soft-deleted software module types are included in the result.")
MgmtSoftDeletedMode softDeletedMode);
/**
* Handles the GET request of retrieving a single software module type .

View File

@@ -28,4 +28,4 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@ComponentScan
@Import({ RestConfiguration.class, OpenApi.class })
@PropertySource("classpath:/hawkbit-mgmt-api-defaults.properties")
public class MgmtApiConfiguration implements WebMvcConfigurer {}
public class MgmtApiConfiguration implements WebMvcConfigurer {}

View File

@@ -51,6 +51,8 @@ import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.DistributionSetInvalidationManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftDeletedMode;
import org.eclipse.hawkbit.repository.SoftDeletedMode;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
@@ -112,16 +114,17 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
@Override
public ResponseEntity<PagedList<MgmtDistributionSet>> getDistributionSets(
final String rsqlParam, final int pagingOffsetParam, final int pagingLimitParam, final String sortParam) {
final String rsqlParam, final int pagingOffsetParam, final int pagingLimitParam, final String sortParam, final MgmtSoftDeletedMode softDeletedModeParam) {
if (rsqlParam != null && rsqlParam.toLowerCase().contains("complete")) {
LogUtility.logDeprecated("Usage of MgmtDistributionSetResource.getActions with 'complete': 'complete' distribution set search field is limited and may be removed.");
}
final Pageable pageable = PagingUtility.toPageable(pagingOffsetParam, pagingLimitParam, sanitizeDistributionSetSortParam(sortParam));
final SoftDeletedMode softDeletedMode = SoftDeletedMode.valueOf(softDeletedModeParam.name());
final Page<? extends DistributionSet> findDsPage;
if (rsqlParam != null) {
findDsPage = distributionSetManagement.findByRsql(rsqlParam, pageable);
findDsPage = distributionSetManagement.findByRsql(rsqlParam, softDeletedMode, pageable);
} else {
findDsPage = distributionSetManagement.findAll(pageable);
findDsPage = distributionSetManagement.findAll(softDeletedMode, pageable);
}
final List<MgmtDistributionSet> rest = MgmtDistributionSetMapper.toResponseFromDsList(findDsPage.getContent());

View File

@@ -26,6 +26,8 @@ import org.eclipse.hawkbit.mgmt.rest.resource.mapper.MgmtDistributionSetTypeMapp
import org.eclipse.hawkbit.mgmt.rest.resource.mapper.MgmtSoftwareModuleTypeMapper;
import org.eclipse.hawkbit.mgmt.rest.resource.util.PagingUtility;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftDeletedMode;
import org.eclipse.hawkbit.repository.SoftDeletedMode;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.SoftwareModuleTypeNotInDistributionSetTypeException;
@@ -59,21 +61,19 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR
@Override
public ResponseEntity<PagedList<MgmtDistributionSetType>> getDistributionSetTypes(
final String rsqlParam, final int pagingOffsetParam, final int pagingLimitParam, final String sortParam) {
final String rsqlParam, final int pagingOffsetParam, final int pagingLimitParam, final String sortParam, final MgmtSoftDeletedMode softDeletedModeParam) {
final Pageable pageable = PagingUtility.toPageable(
pagingOffsetParam, pagingLimitParam, sanitizeDistributionSetTypeSortParam(sortParam));
final Slice<? extends DistributionSetType> findModuleTypesAll;
long countModulesAll;
final SoftDeletedMode softDeletedMode = SoftDeletedMode.valueOf(softDeletedModeParam.name());
final Page<? extends DistributionSetType> findModuleTypesAll;
if (rsqlParam != null) {
findModuleTypesAll = distributionSetTypeManagement.findByRsql(rsqlParam, pageable);
countModulesAll = ((Page<?>) findModuleTypesAll).getTotalElements();
findModuleTypesAll = distributionSetTypeManagement.findByRsql(rsqlParam, softDeletedMode, pageable);
} else {
findModuleTypesAll = distributionSetTypeManagement.findAll(pageable);
countModulesAll = distributionSetTypeManagement.count();
findModuleTypesAll = distributionSetTypeManagement.findAll(softDeletedMode, pageable);
}
final List<MgmtDistributionSetType> rest = MgmtDistributionSetTypeMapper.toListResponse(findModuleTypesAll.getContent());
return ResponseEntity.ok(new PagedList<>(rest, countModulesAll));
return ResponseEntity.ok(new PagedList<>(rest, findModuleTypesAll.getTotalElements()));
}
@Override

View File

@@ -36,6 +36,8 @@ import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.RolloutManagement.Create;
import org.eclipse.hawkbit.repository.RolloutManagement.GroupCreate;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftDeletedMode;
import org.eclipse.hawkbit.repository.SoftDeletedMode;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
@@ -78,7 +80,7 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
@Override
public ResponseEntity<PagedList<MgmtRolloutResponseBody>> getRollouts(
final String rsqlParam, final int pagingOffsetParam, final int pagingLimitParam, final String sortParam,
final String representationModeParam) {
final String representationModeParam, final MgmtSoftDeletedMode softDeletedModeParam) {
final Pageable pageable = PagingUtility.toPageable(pagingOffsetParam, pagingLimitParam, sanitizeRolloutSortParam(sortParam));
final boolean isFullMode = parseRepresentationMode(representationModeParam) == MgmtRepresentationMode.FULL;
@@ -90,9 +92,10 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
: rolloutManagement.findByRsqlWithDetailedStatus(rsqlParam, false, pageable);
rest = MgmtRolloutMapper.toResponseRolloutWithDetails(rollouts.getContent());
} else {
final SoftDeletedMode softDeletedMode = SoftDeletedMode.valueOf(softDeletedModeParam.name());
rollouts = rsqlParam == null
? rolloutManagement.findAll(false, pageable)
: rolloutManagement.findByRsql(rsqlParam, false, pageable);
? rolloutManagement.findAll(softDeletedMode, pageable)
: rolloutManagement.findByRsql(rsqlParam, softDeletedMode, pageable);
rest = MgmtRolloutMapper.toResponseRollout(rollouts.getContent());
}
return ResponseEntity.ok(new PagedList<>(rest, rollouts.getTotalElements()));

View File

@@ -37,6 +37,8 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftwareModuleRestApi;
import org.eclipse.hawkbit.mgmt.rest.resource.mapper.MgmtSoftwareModuleMapper;
import org.eclipse.hawkbit.mgmt.rest.resource.util.PagingUtility;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftDeletedMode;
import org.eclipse.hawkbit.repository.SoftDeletedMode;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
@@ -164,20 +166,19 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
@Override
public ResponseEntity<PagedList<MgmtSoftwareModule>> getSoftwareModules(
final String rsqlParam, final int pagingOffsetParam, final int pagingLimitParam, final String sortParam) {
final String rsqlParam, final int pagingOffsetParam, final int pagingLimitParam, final String sortParam, final MgmtSoftDeletedMode softDeletedModeParam) {
final Pageable pageable = PagingUtility.toPageable(pagingOffsetParam, pagingLimitParam, sanitizeSoftwareModuleSortParam(sortParam));
final Slice<? extends SoftwareModule> findModulesAll;
final long countModulesAll;
final SoftDeletedMode softDeletedMode = SoftDeletedMode.valueOf(softDeletedModeParam.name());
final Page<? extends SoftwareModule> findModulesAll;
if (rsqlParam != null) {
findModulesAll = softwareModuleManagement.findByRsql(rsqlParam, pageable);
countModulesAll = ((Page<?>) findModulesAll).getTotalElements();
findModulesAll = softwareModuleManagement.findByRsql(
rsqlParam, softDeletedMode, pageable);
} else {
findModulesAll = softwareModuleManagement.findAll(pageable);
countModulesAll = softwareModuleManagement.count();
findModulesAll = softwareModuleManagement.findAll(softDeletedMode, pageable);
}
final List<MgmtSoftwareModule> rest = MgmtSoftwareModuleMapper.toResponse(findModulesAll.getContent());
return ResponseEntity.ok(new PagedList<>(rest, countModulesAll));
return ResponseEntity.ok(new PagedList<>(rest, findModulesAll.getTotalElements()));
}
@Override

View File

@@ -21,6 +21,8 @@ import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModule
import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftwareModuleTypeRestApi;
import org.eclipse.hawkbit.mgmt.rest.resource.mapper.MgmtSoftwareModuleTypeMapper;
import org.eclipse.hawkbit.mgmt.rest.resource.util.PagingUtility;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftDeletedMode;
import org.eclipse.hawkbit.repository.SoftDeletedMode;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
@@ -45,20 +47,18 @@ public class MgmtSoftwareModuleTypeResource implements MgmtSoftwareModuleTypeRes
@Override
public ResponseEntity<PagedList<MgmtSoftwareModuleType>> getTypes(
final String rsqlParam, final int pagingOffsetParam, final int pagingLimitParam, final String sortParam) {
final String rsqlParam, final int pagingOffsetParam, final int pagingLimitParam, final String sortParam, final MgmtSoftDeletedMode softDeletedModeParam) {
final Pageable pageable = PagingUtility.toPageable(pagingOffsetParam, pagingLimitParam, sanitizeSoftwareModuleTypeSortParam(sortParam));
final Slice<? extends SoftwareModuleType> findModuleTypessAll;
final long countModulesAll;
final SoftDeletedMode softDeletedMode = SoftDeletedMode.valueOf(softDeletedModeParam.name());
final Page<? extends SoftwareModuleType> findModuleTypessAll;
if (rsqlParam != null) {
findModuleTypessAll = softwareModuleTypeManagement.findByRsql(rsqlParam, pageable);
countModulesAll = ((Page<?>) findModuleTypessAll).getTotalElements();
findModuleTypessAll = softwareModuleTypeManagement.findByRsql(rsqlParam, softDeletedMode, pageable);
} else {
findModuleTypessAll = softwareModuleTypeManagement.findAll(pageable);
countModulesAll = softwareModuleTypeManagement.count();
findModuleTypessAll = softwareModuleTypeManagement.findAll(softDeletedMode, pageable);
}
final List<MgmtSoftwareModuleType> rest = MgmtSoftwareModuleTypeMapper.toTypesResponse(findModuleTypessAll.getContent());
return ResponseEntity.ok(new PagedList<>(rest, countModulesAll));
return ResponseEntity.ok(new PagedList<>(rest, findModuleTypessAll.getTotalElements()));
}
@Override

View File

@@ -73,7 +73,7 @@ public final class MgmtTargetFilterQueryMapper {
targetRest.add(
linkTo(methodOn(MgmtDistributionSetRestApi.class).getDistributionSets(
"name==" + distributionSet.getName() + ";version==" + distributionSet.getVersion(), Integer.parseInt(MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET),
Integer.parseInt(MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT), null
Integer.parseInt(MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT), null, null
)).withRel("DS").expand());
}

View File

@@ -1026,6 +1026,144 @@ class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegrationTe
assertThat(distributionSetManagement.findAll(PAGE)).isEmpty();
}
@Test
void getDistributionSetsFilteredBySoftDeletedMode() throws Exception {
final DistributionSet activeDs = testdataFactory.createDistributionSet("active");
final DistributionSet deletedDs = testdataFactory.createDistributionSet("deleted");
testdataFactory.createTarget("dsTarget");
assignDistributionSet(deletedDs.getId(), "dsTarget");
distributionSetManagement.delete(deletedDs.getId());
// default — only active
mvc.perform(get("/rest/v1/distributionsets").accept(APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo(activeDs.getName())))
.andExpect(jsonPath("content[0].deleted", equalTo(false)));
// only_soft_deleted — only deleted
mvc.perform(get("/rest/v1/distributionsets")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "ONLY_SOFT_DELETED")
.accept(APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo(deletedDs.getName())))
.andExpect(jsonPath("content[0].deleted", equalTo(true)));
// include_soft_deleted — both
mvc.perform(get("/rest/v1/distributionsets")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "INCLUDE_SOFT_DELETED")
.accept(APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(2)))
.andExpect(jsonPath("$.total", equalTo(2)));
// exclude_soft_deleted — explicit, same as default
mvc.perform(get("/rest/v1/distributionsets")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "EXCLUDE_SOFT_DELETED")
.accept(APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].deleted", equalTo(false)));
}
@Test
void getDistributionSetsFilteredBySoftDeletedModeWithRsql() throws Exception {
final DistributionSet activeDs = testdataFactory.createDistributionSet("rsqlActive");
final DistributionSet deletedDs = testdataFactory.createDistributionSet("rsqlDeleted");
testdataFactory.createTarget("rsqlDsTarget");
assignDistributionSet(deletedDs.getId(), "rsqlDsTarget");
distributionSetManagement.delete(deletedDs.getId());
// rsql + soft_deleted — find deleted by name
mvc.perform(get("/rest/v1/distributionsets")
.param("q", "name==" + deletedDs.getName())
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "ONLY_SOFT_DELETED")
.accept(APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo(deletedDs.getName())))
.andExpect(jsonPath("content[0].deleted", equalTo(true)));
// rsql + not_soft_deleted — deleted not found
mvc.perform(get("/rest/v1/distributionsets")
.param("q", "name==" + deletedDs.getName())
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "EXCLUDE_SOFT_DELETED")
.accept(APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(0)))
.andExpect(jsonPath("$.total", equalTo(0)));
// rsql + include_soft_deleted — filter by name narrows to one
mvc.perform(get("/rest/v1/distributionsets")
.param("q", "name==" + activeDs.getName())
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "INCLUDE_SOFT_DELETED")
.accept(APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo(activeDs.getName())));
}
@Test
void updateSoftDeletedDistributionSetRejected() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("toDelete");
testdataFactory.createTarget("updateTarget");
assignDistributionSet(ds.getId(), "updateTarget");
distributionSetManagement.delete(ds.getId());
final String body = new JSONObject().put("description", "updated").toString();
mvc.perform(put("/rest/v1/distributionsets/{dsId}", ds.getId()).content(body)
.contentType(APPLICATION_JSON).accept(APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.deleted")));
}
@Test
void lockSoftDeletedDistributionSetRejected() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("toDelete");
testdataFactory.createTarget("lockTarget");
assignDistributionSet(ds.getId(), "lockTarget");
distributionSetManagement.delete(ds.getId());
final String body = new JSONObject().put("locked", true).toString();
mvc.perform(put("/rest/v1/distributionsets/{dsId}", ds.getId()).content(body)
.contentType(APPLICATION_JSON).accept(APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.deleted")));
}
@Test
void assignSoftwareModuleToSoftDeletedDistributionSetRejected() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("toDelete");
testdataFactory.createTarget("assignSmTarget");
assignDistributionSet(ds.getId(), "assignSmTarget");
distributionSetManagement.delete(ds.getId());
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs("newModule");
mvc.perform(post("/rest/v1/distributionsets/{dsId}/assignedSM", ds.getId())
.content("[{\"id\":" + sm.getId() + "}]")
.contentType(APPLICATION_JSON).accept(APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.deleted")));
}
/**
* Ensures that DS property update request to API is reflected by the repository.
*/

View File

@@ -493,6 +493,142 @@ class MgmtDistributionSetTypeResourceTest extends AbstractManagementApiIntegrati
assertThat(distributionSetTypeManagement.count()).isEqualTo(DEFAULT_DS_TYPES);
}
@Test
void getDistributionSetTypesFilteredBySoftDeletedMode() throws Exception {
final DistributionSetType activeType = distributionSetTypeManagement.create(
DistributionSetTypeManagement.Create.builder()
.key("activeKey").name("activeType").build());
// create type + DS using it, then delete type → soft-delete
final DistributionSetType deletedType = distributionSetTypeManagement.create(
DistributionSetTypeManagement.Create.builder()
.key("deletedKey").name("deletedType").build());
distributionSetManagement.create(DistributionSetManagement.Create.builder()
.type(deletedType).name("ds").version("1.0").build());
distributionSetTypeManagement.delete(deletedType.getId());
// default — built-in + activeType, no deletedType
mvc.perform(get("/rest/v1/distributionsettypes").accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(DEFAULT_DS_TYPES + 1)))
.andExpect(jsonPath("$.total", equalTo(DEFAULT_DS_TYPES + 1)))
.andExpect(jsonPath("$.content.[?(@.key=='deletedKey')]").doesNotExist());
// only_soft_deleted — only deletedType
mvc.perform(get("/rest/v1/distributionsettypes")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "ONLY_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo("deletedType")))
.andExpect(jsonPath("content[0].deleted", equalTo(true)));
// include_soft_deleted — everything
mvc.perform(get("/rest/v1/distributionsettypes")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "INCLUDE_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(DEFAULT_DS_TYPES + 2)))
.andExpect(jsonPath("$.total", equalTo(DEFAULT_DS_TYPES + 2)));
// exclude_soft_deleted — explicit, same as default
mvc.perform(get("/rest/v1/distributionsettypes")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "EXCLUDE_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(DEFAULT_DS_TYPES + 1)))
.andExpect(jsonPath("$.total", equalTo(DEFAULT_DS_TYPES + 1)))
.andExpect(jsonPath("$.content.[?(@.key=='deletedKey')]").doesNotExist());
}
@Test
void getDistributionSetTypesFilteredBySoftDeletedModeWithRsql() throws Exception {
distributionSetTypeManagement.create(
DistributionSetTypeManagement.Create.builder()
.key("rsqlActiveKey").name("rsqlActiveType").build());
final DistributionSetType deletedType = distributionSetTypeManagement.create(
DistributionSetTypeManagement.Create.builder()
.key("rsqlDeletedKey").name("rsqlDeletedType").build());
distributionSetManagement.create(DistributionSetManagement.Create.builder()
.type(deletedType).name("ds").version("1.0").build());
distributionSetTypeManagement.delete(deletedType.getId());
// rsql + soft_deleted — find deleted by name
mvc.perform(get("/rest/v1/distributionsettypes")
.param("q", "name==rsqlDeletedType")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "ONLY_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo("rsqlDeletedType")))
.andExpect(jsonPath("content[0].deleted", equalTo(true)));
// rsql + not_soft_deleted — deleted not found
mvc.perform(get("/rest/v1/distributionsettypes")
.param("q", "name==rsqlDeletedType")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "EXCLUDE_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(0)))
.andExpect(jsonPath("$.total", equalTo(0)));
// rsql + include_soft_deleted — filter by name narrows to one
mvc.perform(get("/rest/v1/distributionsettypes")
.param("q", "name==rsqlActiveType")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "INCLUDE_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo("rsqlActiveType")));
}
@Test
void updateSoftDeletedDistributionSetTypeRejected() throws Exception {
final DistributionSetType deletedType = distributionSetTypeManagement.create(
DistributionSetTypeManagement.Create.builder()
.key("delKey").name("delType").build());
distributionSetManagement.create(DistributionSetManagement.Create.builder()
.type(deletedType).name("ds").version("1.0").build());
distributionSetTypeManagement.delete(deletedType.getId());
final String body = new JSONObject().put("description", "updated").toString();
mvc.perform(put("/rest/v1/distributionsettypes/{dstId}", deletedType.getId()).content(body)
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.deleted")));
}
@Test
void assignSmTypeToSoftDeletedDistributionSetTypeRejected() throws Exception {
final DistributionSetType deletedType = distributionSetTypeManagement.create(
DistributionSetTypeManagement.Create.builder()
.key("delKey2").name("delType2").build());
distributionSetManagement.create(DistributionSetManagement.Create.builder()
.type(deletedType).name("ds2").version("1.0").build());
distributionSetTypeManagement.delete(deletedType.getId());
final SoftwareModuleType smType = softwareModuleTypeManagement.create(
SoftwareModuleTypeManagement.Create.builder().key("newSmType").name("newSmType").build());
mvc.perform(post("/rest/v1/distributionsettypes/{dstId}/mandatorymoduletypes", deletedType.getId())
.content("{\"id\":" + smType.getId() + "}")
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.deleted")));
}
/**
* Checks the correct behaviour of /rest/v1/distributionsettypes/{ID} PUT requests.
*/

View File

@@ -1495,6 +1495,110 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
}
}
@Test
void getRolloutsFilteredBySoftDeletedMode() throws Exception {
testdataFactory.createTargets(20, "rolloutFilter", "rolloutFilter");
final DistributionSet dsA = testdataFactory.createDistributionSet("filterDs");
// create two rollouts
final Rollout rollout1 = createRollout("activeRollout", 4, dsA, "controllerId==rolloutFilter*");
final Rollout rollout2 = createRollout("toDeleteRollout", 4, dsA, "controllerId==rolloutFilter*");
// start and soft-delete rollout2
rolloutManagement.start(rollout2.getId());
rolloutHandler.handleAll();
rolloutManagement.delete(rollout2.getId());
rolloutHandler.handleAll();
// default (exclude_soft_deleted) — only active rollout
mvc.perform(get("/rest/v1/rollouts").accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo(rollout1.getName())))
.andExpect(jsonPath("content[0].deleted", equalTo(false)));
// soft_deleted_mode=only_soft_deleted — only deleted rollout
mvc.perform(get("/rest/v1/rollouts")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "ONLY_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo(rollout2.getName())))
.andExpect(jsonPath("content[0].deleted", equalTo(true)));
// soft_deleted_mode=include_soft_deleted — both rollouts
mvc.perform(get("/rest/v1/rollouts")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "INCLUDE_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(2)))
.andExpect(jsonPath("$.total", equalTo(2)));
// exclude_soft_deleted — explicit, same as default
mvc.perform(get("/rest/v1/rollouts")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "EXCLUDE_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo(rollout1.getName())))
.andExpect(jsonPath("content[0].deleted", equalTo(false)));
}
@Test
void getRolloutsFilteredBySoftDeletedModeWithRsql() throws Exception {
testdataFactory.createTargets(20, "rolloutRsql", "rolloutRsql");
final DistributionSet dsA = testdataFactory.createDistributionSet("rsqlDs");
final Rollout rollout1 = createRollout("rsqlActive", 4, dsA, "controllerId==rolloutRsql*");
final Rollout rollout2 = createRollout("rsqlDeleted", 4, dsA, "controllerId==rolloutRsql*");
// start and soft-delete rollout2
rolloutManagement.start(rollout2.getId());
rolloutHandler.handleAll();
rolloutManagement.delete(rollout2.getId());
rolloutHandler.handleAll();
// rsql + only_soft_deleted — find deleted rollout by name
mvc.perform(get("/rest/v1/rollouts")
.param("q", "name==rsqlDeleted")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "ONLY_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo(rollout2.getName())))
.andExpect(jsonPath("content[0].deleted", equalTo(true)));
// rsql + not_soft_deleted — deleted rollout not found
mvc.perform(get("/rest/v1/rollouts")
.param("q", "name==rsqlDeleted")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "EXCLUDE_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(0)))
.andExpect(jsonPath("$.total", equalTo(0)));
// rsql + include_soft_deleted — both visible, filter by name narrows to one
mvc.perform(get("/rest/v1/rollouts")
.param("q", "name==rsqlActive")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "INCLUDE_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo(rollout1.getName())));
}
@Test
void stopRunningRollout() throws Exception {
final Rollout rollout = testdataFactory.createAndStartRollout();

View File

@@ -1228,6 +1228,130 @@ class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTes
.andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.rest.param.rsqlInvalidField")));
}
@Test
void getSoftwareModulesFilteredBySoftDeletedMode() throws Exception {
final SoftwareModule activeSm = softwareModuleManagement.create(
SoftwareModuleManagement.Create.builder().type(osType).name("activeSm").version("1.0").build());
SoftwareModule deletedSm = softwareModuleManagement.create(
SoftwareModuleManagement.Create.builder().type(osType).name("deletedSm").version("1.0").build());
testdataFactory.createDistributionSet(List.of(deletedSm));
softwareModuleManagement.delete(deletedSm.getId());
// default — only active
mvc.perform(get("/rest/v1/softwaremodules").accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo("activeSm")))
.andExpect(jsonPath("content[0].deleted", equalTo(false)));
// only_soft_deleted — only deleted
mvc.perform(get("/rest/v1/softwaremodules")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "ONLY_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo("deletedSm")))
.andExpect(jsonPath("content[0].deleted", equalTo(true)));
// include_soft_deleted — both
mvc.perform(get("/rest/v1/softwaremodules")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "INCLUDE_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(2)))
.andExpect(jsonPath("$.total", equalTo(2)));
// exclude_soft_deleted — explicit, same as default
mvc.perform(get("/rest/v1/softwaremodules")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "EXCLUDE_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].deleted", equalTo(false)));
}
@Test
void getSoftwareModulesFilteredBySoftDeletedModeWithRsql() throws Exception {
softwareModuleManagement.create(
SoftwareModuleManagement.Create.builder().type(osType).name("rsqlActive").version("1.0").build());
SoftwareModule deletedSm = softwareModuleManagement.create(
SoftwareModuleManagement.Create.builder().type(osType).name("rsqlDeleted").version("1.0").build());
testdataFactory.createDistributionSet(List.of(deletedSm));
softwareModuleManagement.delete(deletedSm.getId());
// rsql + soft_deleted — find deleted by name
mvc.perform(get("/rest/v1/softwaremodules")
.param("q", "name==rsqlDeleted")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "ONLY_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo("rsqlDeleted")))
.andExpect(jsonPath("content[0].deleted", equalTo(true)));
// rsql + not_soft_deleted — deleted not found
mvc.perform(get("/rest/v1/softwaremodules")
.param("q", "name==rsqlDeleted")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "EXCLUDE_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(0)))
.andExpect(jsonPath("$.total", equalTo(0)));
// rsql + include_soft_deleted — filter by name narrows to one
mvc.perform(get("/rest/v1/softwaremodules")
.param("q", "name==rsqlActive")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "INCLUDE_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo("rsqlActive")));
}
@Test
void updateSoftDeletedSoftwareModuleRejected() throws Exception {
SoftwareModule sm = softwareModuleManagement.create(
SoftwareModuleManagement.Create.builder().type(osType).name("delSm").version("1.0").build());
testdataFactory.createDistributionSet(List.of(sm));
softwareModuleManagement.delete(sm.getId());
final String body = new JSONObject().put("description", "updated").toString();
mvc.perform(put("/rest/v1/softwaremodules/{smId}", sm.getId()).content(body)
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.deleted")));
}
@Test
void lockSoftDeletedSoftwareModuleRejected() throws Exception {
SoftwareModule sm = softwareModuleManagement.create(
SoftwareModuleManagement.Create.builder().type(osType).name("delSmLock").version("1.0").build());
testdataFactory.createDistributionSet(List.of(sm));
softwareModuleManagement.delete(sm.getId());
final String body = new JSONObject().put("locked", true).toString();
mvc.perform(put("/rest/v1/softwaremodules/{smId}", sm.getId()).content(body)
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.deleted")));
}
/**
* Tests GET request on /rest/v1/softwaremodules/{smId}.
*/

View File

@@ -489,6 +489,121 @@ public class MgmtSoftwareModuleTypeResourceTest extends AbstractManagementApiInt
}
@Test
void getSoftwareModuleTypesFilteredBySoftDeletedMode() throws Exception {
// 3 built-in types exist (os, runtime, application)
final int builtInTypes = 3;
final SoftwareModuleType activeType = softwareModuleTypeManagement.create(
SoftwareModuleTypeManagement.Create.builder().key("activeKey").name("activeType").build());
// create type + SM using it, then delete type → soft-delete
final SoftwareModuleType deletedType = softwareModuleTypeManagement.create(
SoftwareModuleTypeManagement.Create.builder().key("deletedKey").name("deletedType").build());
softwareModuleManagement.create(
SoftwareModuleManagement.Create.builder().type(deletedType).name("sm").version("1.0").build());
softwareModuleTypeManagement.delete(deletedType.getId());
// default — built-in + activeType, no deletedType
mvc.perform(get("/rest/v1/softwaremoduletypes").accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(builtInTypes + 1)))
.andExpect(jsonPath("$.total", equalTo(builtInTypes + 1)))
.andExpect(jsonPath("$.content.[?(@.key=='deletedKey')]").doesNotExist());
// only_soft_deleted — only deletedType
mvc.perform(get("/rest/v1/softwaremoduletypes")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "ONLY_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo("deletedType")))
.andExpect(jsonPath("content[0].deleted", equalTo(true)));
// include_soft_deleted — everything
mvc.perform(get("/rest/v1/softwaremoduletypes")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "INCLUDE_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(builtInTypes + 2)))
.andExpect(jsonPath("$.total", equalTo(builtInTypes + 2)));
// exclude_soft_deleted — explicit, same as default
mvc.perform(get("/rest/v1/softwaremoduletypes")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "EXCLUDE_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(builtInTypes + 1)))
.andExpect(jsonPath("$.total", equalTo(builtInTypes + 1)))
.andExpect(jsonPath("$.content.[?(@.key=='deletedKey')]").doesNotExist());
}
@Test
void getSoftwareModuleTypesFilteredBySoftDeletedModeWithRsql() throws Exception {
softwareModuleTypeManagement.create(
SoftwareModuleTypeManagement.Create.builder().key("rsqlActiveKey").name("rsqlActiveType").build());
final SoftwareModuleType deletedType = softwareModuleTypeManagement.create(
SoftwareModuleTypeManagement.Create.builder().key("rsqlDeletedKey").name("rsqlDeletedType").build());
softwareModuleManagement.create(
SoftwareModuleManagement.Create.builder().type(deletedType).name("sm").version("1.0").build());
softwareModuleTypeManagement.delete(deletedType.getId());
// rsql + soft_deleted — find deleted by name
mvc.perform(get("/rest/v1/softwaremoduletypes")
.param("q", "name==rsqlDeletedType")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "ONLY_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo("rsqlDeletedType")))
.andExpect(jsonPath("content[0].deleted", equalTo(true)));
// rsql + not_soft_deleted — deleted not found
mvc.perform(get("/rest/v1/softwaremoduletypes")
.param("q", "name==rsqlDeletedType")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "EXCLUDE_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(0)))
.andExpect(jsonPath("$.total", equalTo(0)));
// rsql + include_soft_deleted — filter by name narrows to one
mvc.perform(get("/rest/v1/softwaremoduletypes")
.param("q", "name==rsqlActiveType")
.param(MgmtRestConstants.REQUEST_PARAMETER_LIST_SOFT_DELETED_MODE, "INCLUDE_SOFT_DELETED")
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(1)))
.andExpect(jsonPath("$.total", equalTo(1)))
.andExpect(jsonPath("content[0].name", equalTo("rsqlActiveType")));
}
@Test
void updateSoftDeletedSoftwareModuleTypeRejected() throws Exception {
final SoftwareModuleType deletedType = softwareModuleTypeManagement.create(
SoftwareModuleTypeManagement.Create.builder().key("delKey").name("delType").build());
softwareModuleManagement.create(
SoftwareModuleManagement.Create.builder().type(deletedType).name("sm").version("1.0").build());
softwareModuleTypeManagement.delete(deletedType.getId());
final String body = new JSONObject().put("description", "updated").toString();
mvc.perform(put("/rest/v1/softwaremoduletypes/{smtId}", deletedType.getId()).content(body)
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.deleted")));
}
private SoftwareModuleType createTestType() {
final SoftwareModuleType testType = softwareModuleTypeManagement.create(SoftwareModuleTypeManagement.Create.builder()
.key("test123").name("TestName123").description("Desc123").colour("colour").maxAssignments(5).build());

View File

@@ -13,3 +13,8 @@ logging.level.root=WARN
#logging.level.org.eclipse.hawkbit.rest.util.MockMvcResultPrinter=DEBUG
# Logging END
hawkbit.events.remote.enabled=false
logging.level.org.eclipse.persistence=DEBUG
spring.jpa.properties.eclipselink.logging.level=FINE
spring.jpa.properties.eclipselink.logging.level.sql=FINE
spring.jpa.properties.eclipselink.logging.parameters=true