Refactor rest core (#1955)

* remove unused methods
* move mgmt resource dedicated classes to mgmt-resource
* clean up code

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2024-11-06 14:09:20 +02:00
committed by GitHub
parent 57e6d35324
commit 64c7feded3
48 changed files with 516 additions and 638 deletions

View File

@@ -34,7 +34,7 @@ import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.rest.data.ResponseList; import org.eclipse.hawkbit.rest.json.model.ResponseList;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.hateoas.Link; import org.springframework.hateoas.Link;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;

View File

@@ -711,7 +711,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
Collections.singletonList("test")); Collections.singletonList("test"));
postDeploymentFeedback("1080", action.getId(), missingResultInFeedback, status().isBadRequest()) postDeploymentFeedback("1080", action.getId(), missingResultInFeedback, status().isBadRequest())
.andExpect(jsonPath("$.*", hasSize(3))) .andExpect(jsonPath("$.*", hasSize(3)))
.andExpect(jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getCanonicalName()))); .andExpect(jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getName())));
} }
@Test @Test
@@ -736,7 +736,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
postDeploymentFeedback("1080", action.getId(), missingFinishedResultInFeedback, status().isBadRequest()) postDeploymentFeedback("1080", action.getId(), missingFinishedResultInFeedback, status().isBadRequest())
.andExpect(jsonPath("$.*", hasSize(3))) .andExpect(jsonPath("$.*", hasSize(3)))
.andExpect(jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getCanonicalName()))); .andExpect(jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getName())));
} }
public long countActionStatusAll() { public long countActionStatusAll() {

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* 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;
/**
* A definition of possible sorting direction.
*/
public enum SortDirection {
/**
* Ascending.
*/
ASC,
/**
* Descending.
*/
DESC;
}

View File

@@ -19,7 +19,7 @@ import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction; import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.rest.data.ResponseList; import org.eclipse.hawkbit.rest.json.model.ResponseList;
/** /**
* A mapper which maps repository model to RESTful model representation and * A mapper which maps repository model to RESTful model representation and

View File

@@ -14,6 +14,7 @@ import org.eclipse.hawkbit.mgmt.json.model.PagedList;
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction; import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtActionRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtActionRestApi;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode;
import org.eclipse.hawkbit.mgmt.rest.resource.util.PagingUtility;
import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;

View File

@@ -34,7 +34,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata; import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.MetaData; import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.rest.data.ResponseList; import org.eclipse.hawkbit.rest.json.model.ResponseList;
/** /**
* A mapper which maps repository model to RESTful model representation and * A mapper which maps repository model to RESTful model representation and

View File

@@ -38,6 +38,7 @@ import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery; import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetRestApi;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.resource.util.PagingUtility;
import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.DistributionSetInvalidationManagement; import org.eclipse.hawkbit.repository.DistributionSetInvalidationManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.DistributionSetManagement;

View File

@@ -21,6 +21,7 @@ import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTag;
import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTagRequestBodyPut; import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTagRequestBodyPut;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetTagRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetTagRestApi;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.resource.util.PagingUtility;
import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement; import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.EntityFactory;

View File

@@ -26,7 +26,7 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeCreate; import org.eclipse.hawkbit.repository.builder.DistributionSetTypeCreate;
import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.rest.data.ResponseList; import org.eclipse.hawkbit.rest.json.model.ResponseList;
/** /**
* A mapper which maps repository model to RESTful model representation and * A mapper which maps repository model to RESTful model representation and

View File

@@ -20,6 +20,7 @@ import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionS
import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleType; import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleType;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetTypeRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetTypeRestApi;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.resource.util.PagingUtility;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement; import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;

View File

@@ -27,6 +27,7 @@ import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRolloutRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRolloutRestApi;
import org.eclipse.hawkbit.mgmt.rest.resource.util.PagingUtility;
import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;

View File

@@ -38,7 +38,7 @@ import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.rest.data.ResponseList; import org.eclipse.hawkbit.rest.json.model.ResponseList;
import org.springframework.hateoas.Link; import org.springframework.hateoas.Link;
/** /**

View File

@@ -30,6 +30,7 @@ import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleRequ
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftwareModuleRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftwareModuleRestApi;
import org.eclipse.hawkbit.mgmt.rest.resource.util.PagingUtility;
import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
@@ -42,7 +43,7 @@ import org.eclipse.hawkbit.repository.model.ArtifactUpload;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.rest.data.ResponseList; import org.eclipse.hawkbit.rest.json.model.ResponseList;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice; import org.springframework.data.domain.Slice;

View File

@@ -23,7 +23,7 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftwareModuleTypeRestApi;
import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeCreate; import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeCreate;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.rest.data.ResponseList; import org.eclipse.hawkbit.rest.json.model.ResponseList;
/** /**
* A mapper which maps repository model to RESTful model representation and * A mapper which maps repository model to RESTful model representation and

View File

@@ -17,6 +17,7 @@ import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModule
import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleTypeRequestBodyPut; import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleTypeRequestBodyPut;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftwareModuleTypeRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftwareModuleTypeRestApi;
import org.eclipse.hawkbit.mgmt.rest.resource.util.PagingUtility;
import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;

View File

@@ -29,7 +29,7 @@ import org.eclipse.hawkbit.repository.builder.TagCreate;
import org.eclipse.hawkbit.repository.model.DistributionSetTag; import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.Tag; import org.eclipse.hawkbit.repository.model.Tag;
import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.rest.data.ResponseList; import org.eclipse.hawkbit.rest.json.model.ResponseList;
/** /**
* A mapper which maps repository model to RESTful model representation and back. * A mapper which maps repository model to RESTful model representation and back.

View File

@@ -20,6 +20,7 @@ import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryReq
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetFilterQueryRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetFilterQueryRestApi;
import org.eclipse.hawkbit.mgmt.rest.resource.util.PagingUtility;
import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;

View File

@@ -34,6 +34,7 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRolloutRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRolloutRestApi;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetRestApi;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetTypeRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetTypeRestApi;
import org.eclipse.hawkbit.mgmt.rest.api.SortDirection;
import org.eclipse.hawkbit.repository.ActionFields; import org.eclipse.hawkbit.repository.ActionFields;
import org.eclipse.hawkbit.repository.ActionStatusFields; import org.eclipse.hawkbit.repository.ActionStatusFields;
import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.DeploymentManagement;
@@ -49,8 +50,7 @@ import org.eclipse.hawkbit.repository.model.PollStatus;
import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetMetadata; import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.rest.data.ResponseList; import org.eclipse.hawkbit.rest.json.model.ResponseList;
import org.eclipse.hawkbit.rest.data.SortDirection;
import org.eclipse.hawkbit.util.IpUtil; import org.eclipse.hawkbit.util.IpUtil;
import org.eclipse.hawkbit.utils.TenantConfigHelper; import org.eclipse.hawkbit.utils.TenantConfigHelper;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;

View File

@@ -41,6 +41,7 @@ import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetAutoConfirmUpdate;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetRequestBody; import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetRequestBody;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetRestApi;
import org.eclipse.hawkbit.mgmt.rest.resource.util.PagingUtility;
import org.eclipse.hawkbit.repository.ConfirmationManagement; import org.eclipse.hawkbit.repository.ConfirmationManagement;
import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.EntityFactory;

View File

@@ -23,6 +23,7 @@ import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTargetTagAssigmentResult;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget; import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetTagRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetTagRestApi;
import org.eclipse.hawkbit.mgmt.rest.resource.util.PagingUtility;
import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.TargetManagement;

View File

@@ -26,7 +26,7 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetTypeRestApi;
import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate; import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
import org.eclipse.hawkbit.repository.model.TargetType; import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.rest.data.ResponseList; import org.eclipse.hawkbit.rest.json.model.ResponseList;
/** /**
* A mapper which maps repository model to RESTful model representation and back. * A mapper which maps repository model to RESTful model representation and back.

View File

@@ -21,6 +21,7 @@ import org.eclipse.hawkbit.mgmt.json.model.targettype.MgmtTargetTypeRequestBodyP
import org.eclipse.hawkbit.mgmt.json.model.targettype.MgmtTargetTypeRequestBodyPut; import org.eclipse.hawkbit.mgmt.json.model.targettype.MgmtTargetTypeRequestBodyPut;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetTypeRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetTypeRestApi;
import org.eclipse.hawkbit.mgmt.rest.resource.util.PagingUtility;
import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.TargetTypeManagement; import org.eclipse.hawkbit.repository.TargetTypeManagement;

View File

@@ -7,7 +7,9 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.rest.exception; package org.eclipse.hawkbit.mgmt.rest.resource.exception;
import java.io.Serial;
import org.eclipse.hawkbit.exception.AbstractServerRtException; import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.exception.SpServerError; import org.eclipse.hawkbit.exception.SpServerError;
@@ -17,16 +19,13 @@ import org.eclipse.hawkbit.exception.SpServerError;
*/ */
public class SortParameterSyntaxErrorException extends AbstractServerRtException { public class SortParameterSyntaxErrorException extends AbstractServerRtException {
/** @Serial
*
*/
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* Creates a new SortParameterSyntaxErrorException with * Creates a new SortParameterSyntaxErrorException with {@link SpServerError#SP_REST_SORT_PARAM_SYNTAX} error.
* {@link SpServerError#SP_REST_SORT_PARAM_SYNTAX} error.
*/ */
public SortParameterSyntaxErrorException() { public SortParameterSyntaxErrorException() {
super(SpServerError.SP_REST_SORT_PARAM_SYNTAX); super(SpServerError.SP_REST_SORT_PARAM_SYNTAX);
} }
} }

View File

@@ -7,38 +7,28 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.rest.exception; package org.eclipse.hawkbit.mgmt.rest.resource.exception;
import java.io.Serial;
import org.eclipse.hawkbit.exception.AbstractServerRtException; import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.exception.SpServerError; import org.eclipse.hawkbit.exception.SpServerError;
/** /**
* Exception used by the REST API in case of invalid sort parameter direction * Exception used by the REST API in case of invalid sort parameter direction name.
* name.
*/ */
public class SortParameterUnsupportedDirectionException extends AbstractServerRtException { public class SortParameterUnsupportedDirectionException extends AbstractServerRtException {
/** @Serial
*
*/
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* Creates a new SortParameterSyntaxErrorException. * Creates a new SortParameterSyntaxErrorException with {@link SpServerError#SP_REST_SORT_PARAM_INVALID_DIRECTION} error.
*/
public SortParameterUnsupportedDirectionException() {
super(SpServerError.SP_REST_SORT_PARAM_INVALID_DIRECTION);
}
/**
* Creates a new SortParameterSyntaxErrorException with
* {@link SpServerError#SP_REST_SORT_PARAM_INVALID_DIRECTION} error.
* *
* @param cause the cause (which is saved for later retrieval by the * @param cause the cause (which is saved for later retrieval by the getCause() method). (A null value is permitted, and indicates
* getCause() method). (A null value is permitted, and indicates * that the cause is nonexistent or unknown.)
* that the cause is nonexistent or unknown.)
*/ */
public SortParameterUnsupportedDirectionException(final Throwable cause) { public SortParameterUnsupportedDirectionException(final Throwable cause) {
super(SpServerError.SP_REST_SORT_PARAM_INVALID_DIRECTION, cause); super(SpServerError.SP_REST_SORT_PARAM_INVALID_DIRECTION, cause);
} }
} }

View File

@@ -7,39 +7,28 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.rest.exception; package org.eclipse.hawkbit.mgmt.rest.resource.exception;
import java.io.Serial;
import org.eclipse.hawkbit.exception.AbstractServerRtException; import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.exception.SpServerError; import org.eclipse.hawkbit.exception.SpServerError;
/** /**
* Exception used by the REST API in case of invalid field name in the sort * Exception used by the REST API in case of invalid field name in the sort parameter.
* parameter.
*/ */
public class SortParameterUnsupportedFieldException extends AbstractServerRtException { public class SortParameterUnsupportedFieldException extends AbstractServerRtException {
/** @Serial
*
*/
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* Creates a new SortParameterSyntaxErrorException with * Creates a new SortParameterSyntaxErrorException with {@link SpServerError#SP_REST_SORT_PARAM_INVALID_FIELD} error.
* {@link SpServerError#SP_REST_SORT_PARAM_INVALID_FIELD} error.
*/
public SortParameterUnsupportedFieldException() {
super(SpServerError.SP_REST_SORT_PARAM_INVALID_FIELD);
}
/**
* Creates a new SortParameterSyntaxErrorException with
* {@link SpServerError#SP_REST_SORT_PARAM_INVALID_FIELD} error.
* *
* @param cause the cause (which is saved for later retrieval by the * @param cause the cause (which is saved for later retrieval by the getCause() method). (A null value is permitted, and indicates
* getCause() method). (A null value is permitted, and indicates * that the cause is nonexistent or unknown.)
* that the cause is nonexistent or unknown.)
*/ */
public SortParameterUnsupportedFieldException(final Throwable cause) { public SortParameterUnsupportedFieldException(final Throwable cause) {
super(SpServerError.SP_REST_SORT_PARAM_INVALID_FIELD, cause); super(SpServerError.SP_REST_SORT_PARAM_INVALID_FIELD, cause);
} }
} }

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.mgmt.rest.resource; package org.eclipse.hawkbit.mgmt.rest.resource.util;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.ActionFields; import org.eclipse.hawkbit.repository.ActionFields;
@@ -24,7 +24,6 @@ import org.eclipse.hawkbit.repository.TagFields;
import org.eclipse.hawkbit.repository.TargetFields; import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TargetFilterQueryFields; import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
import org.eclipse.hawkbit.repository.TargetTypeFields; import org.eclipse.hawkbit.repository.TargetTypeFields;
import org.eclipse.hawkbit.rest.util.SortUtility;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Direction;
@@ -39,14 +38,14 @@ public final class PagingUtility {
private PagingUtility() { private PagingUtility() {
} }
static int sanitizeOffsetParam(final int offset) { public static int sanitizeOffsetParam(final int offset) {
if (offset < 0) { if (offset < 0) {
return MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE; return MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE;
} }
return offset; return offset;
} }
static int sanitizePageLimitParam(final int pageLimit) { public static int sanitizePageLimitParam(final int pageLimit) {
if (pageLimit < 1) { if (pageLimit < 1) {
return MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE; return MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE;
} else if (pageLimit > MgmtRestConstants.REQUEST_PARAMETER_PAGING_MAX_LIMIT) { } else if (pageLimit > MgmtRestConstants.REQUEST_PARAMETER_PAGING_MAX_LIMIT) {
@@ -55,7 +54,7 @@ public final class PagingUtility {
return pageLimit; return pageLimit;
} }
static Sort sanitizeTargetSortParam(final String sortParam) { public static Sort sanitizeTargetSortParam(final String sortParam) {
if (sortParam == null) { if (sortParam == null) {
// default // default
return Sort.by(Direction.ASC, TargetFields.CONTROLLERID.getJpaEntityFieldName()); return Sort.by(Direction.ASC, TargetFields.CONTROLLERID.getJpaEntityFieldName());
@@ -63,7 +62,7 @@ public final class PagingUtility {
return Sort.by(SortUtility.parse(TargetFields.class, sortParam)); return Sort.by(SortUtility.parse(TargetFields.class, sortParam));
} }
static Sort sanitizeTargetTypeSortParam(final String sortParam) { public static Sort sanitizeTargetTypeSortParam(final String sortParam) {
if (sortParam == null) { if (sortParam == null) {
// default // default
return Sort.by(Direction.ASC, TargetTypeFields.ID.getJpaEntityFieldName()); return Sort.by(Direction.ASC, TargetTypeFields.ID.getJpaEntityFieldName());
@@ -71,7 +70,7 @@ public final class PagingUtility {
return Sort.by(SortUtility.parse(TargetTypeFields.class, sortParam)); return Sort.by(SortUtility.parse(TargetTypeFields.class, sortParam));
} }
static Sort sanitizeTagSortParam(final String sortParam) { public static Sort sanitizeTagSortParam(final String sortParam) {
if (sortParam == null) { if (sortParam == null) {
// default // default
return Sort.by(Direction.ASC, TagFields.ID.getJpaEntityFieldName()); return Sort.by(Direction.ASC, TagFields.ID.getJpaEntityFieldName());
@@ -79,7 +78,7 @@ public final class PagingUtility {
return Sort.by(SortUtility.parse(TagFields.class, sortParam)); return Sort.by(SortUtility.parse(TagFields.class, sortParam));
} }
static Sort sanitizeTargetFilterQuerySortParam(final String sortParam) { public static Sort sanitizeTargetFilterQuerySortParam(final String sortParam) {
if (sortParam == null) { if (sortParam == null) {
// default // default
return Sort.by(Direction.ASC, TargetFilterQueryFields.ID.getJpaEntityFieldName()); return Sort.by(Direction.ASC, TargetFilterQueryFields.ID.getJpaEntityFieldName());
@@ -87,7 +86,7 @@ public final class PagingUtility {
return Sort.by(SortUtility.parse(TargetFilterQueryFields.class, sortParam)); return Sort.by(SortUtility.parse(TargetFilterQueryFields.class, sortParam));
} }
static Sort sanitizeSoftwareModuleSortParam(final String sortParam) { public static Sort sanitizeSoftwareModuleSortParam(final String sortParam) {
if (sortParam == null) { if (sortParam == null) {
// default // default
return Sort.by(Direction.ASC, SoftwareModuleFields.ID.getJpaEntityFieldName()); return Sort.by(Direction.ASC, SoftwareModuleFields.ID.getJpaEntityFieldName());
@@ -95,7 +94,7 @@ public final class PagingUtility {
return Sort.by(SortUtility.parse(SoftwareModuleFields.class, sortParam)); return Sort.by(SortUtility.parse(SoftwareModuleFields.class, sortParam));
} }
static Sort sanitizeSoftwareModuleTypeSortParam(final String sortParam) { public static Sort sanitizeSoftwareModuleTypeSortParam(final String sortParam) {
if (sortParam == null) { if (sortParam == null) {
// default // default
return Sort.by(Direction.ASC, SoftwareModuleTypeFields.ID.getJpaEntityFieldName()); return Sort.by(Direction.ASC, SoftwareModuleTypeFields.ID.getJpaEntityFieldName());
@@ -103,7 +102,7 @@ public final class PagingUtility {
return Sort.by(SortUtility.parse(SoftwareModuleTypeFields.class, sortParam)); return Sort.by(SortUtility.parse(SoftwareModuleTypeFields.class, sortParam));
} }
static Sort sanitizeDistributionSetSortParam(final String sortParam) { public static Sort sanitizeDistributionSetSortParam(final String sortParam) {
if (sortParam == null) { if (sortParam == null) {
// default // default
return Sort.by(Direction.ASC, DistributionSetFields.ID.getJpaEntityFieldName()); return Sort.by(Direction.ASC, DistributionSetFields.ID.getJpaEntityFieldName());
@@ -111,7 +110,7 @@ public final class PagingUtility {
return Sort.by(SortUtility.parse(DistributionSetFields.class, sortParam)); return Sort.by(SortUtility.parse(DistributionSetFields.class, sortParam));
} }
static Sort sanitizeDistributionSetTypeSortParam(final String sortParam) { public static Sort sanitizeDistributionSetTypeSortParam(final String sortParam) {
if (sortParam == null) { if (sortParam == null) {
// default // default
return Sort.by(Direction.ASC, DistributionSetTypeFields.ID.getJpaEntityFieldName()); return Sort.by(Direction.ASC, DistributionSetTypeFields.ID.getJpaEntityFieldName());
@@ -119,7 +118,7 @@ public final class PagingUtility {
return Sort.by(SortUtility.parse(DistributionSetTypeFields.class, sortParam)); return Sort.by(SortUtility.parse(DistributionSetTypeFields.class, sortParam));
} }
static Sort sanitizeActionSortParam(final String sortParam) { public static Sort sanitizeActionSortParam(final String sortParam) {
if (sortParam == null) { if (sortParam == null) {
// default sort is DESC in case of action to match behavior // default sort is DESC in case of action to match behavior
// of management UI (last entry on top) // of management UI (last entry on top)
@@ -128,7 +127,7 @@ public final class PagingUtility {
return Sort.by(SortUtility.parse(ActionFields.class, sortParam)); return Sort.by(SortUtility.parse(ActionFields.class, sortParam));
} }
static Sort sanitizeActionStatusSortParam(final String sortParam) { public static Sort sanitizeActionStatusSortParam(final String sortParam) {
if (sortParam == null) { if (sortParam == null) {
// default sort is DESC in case of action status to match behavior // default sort is DESC in case of action status to match behavior
// of management UI (last entry on top) // of management UI (last entry on top)
@@ -137,7 +136,7 @@ public final class PagingUtility {
return Sort.by(SortUtility.parse(ActionStatusFields.class, sortParam)); return Sort.by(SortUtility.parse(ActionStatusFields.class, sortParam));
} }
static Sort sanitizeDistributionSetMetadataSortParam(final String sortParam) { public static Sort sanitizeDistributionSetMetadataSortParam(final String sortParam) {
if (sortParam == null) { if (sortParam == null) {
// default // default
return Sort.by(Direction.ASC, DistributionSetMetadataFields.KEY.getJpaEntityFieldName()); return Sort.by(Direction.ASC, DistributionSetMetadataFields.KEY.getJpaEntityFieldName());
@@ -145,7 +144,7 @@ public final class PagingUtility {
return Sort.by(SortUtility.parse(DistributionSetMetadataFields.class, sortParam)); return Sort.by(SortUtility.parse(DistributionSetMetadataFields.class, sortParam));
} }
static Sort sanitizeSoftwareModuleMetadataSortParam(final String sortParam) { public static Sort sanitizeSoftwareModuleMetadataSortParam(final String sortParam) {
if (sortParam == null) { if (sortParam == null) {
// default // default
return Sort.by(Direction.ASC, SoftwareModuleMetadataFields.KEY.getJpaEntityFieldName()); return Sort.by(Direction.ASC, SoftwareModuleMetadataFields.KEY.getJpaEntityFieldName());
@@ -153,7 +152,7 @@ public final class PagingUtility {
return Sort.by(SortUtility.parse(SoftwareModuleMetadataFields.class, sortParam)); return Sort.by(SortUtility.parse(SoftwareModuleMetadataFields.class, sortParam));
} }
static Sort sanitizeRolloutSortParam(final String sortParam) { public static Sort sanitizeRolloutSortParam(final String sortParam) {
if (sortParam == null) { if (sortParam == null) {
// default // default
return Sort.by(Direction.ASC, RolloutFields.ID.getJpaEntityFieldName()); return Sort.by(Direction.ASC, RolloutFields.ID.getJpaEntityFieldName());
@@ -161,7 +160,7 @@ public final class PagingUtility {
return Sort.by(SortUtility.parse(RolloutFields.class, sortParam)); return Sort.by(SortUtility.parse(RolloutFields.class, sortParam));
} }
static Sort sanitizeRolloutGroupSortParam(final String sortParam) { public static Sort sanitizeRolloutGroupSortParam(final String sortParam) {
if (sortParam == null) { if (sortParam == null) {
// default // default
return Sort.by(Direction.ASC, RolloutGroupFields.ID.getJpaEntityFieldName()); return Sort.by(Direction.ASC, RolloutGroupFields.ID.getJpaEntityFieldName());

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* 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.resource.util;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.mgmt.rest.resource.exception.SortParameterUnsupportedDirectionException;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.mgmt.rest.resource.exception.SortParameterSyntaxErrorException;
import org.eclipse.hawkbit.mgmt.rest.resource.exception.SortParameterUnsupportedFieldException;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
/**
* A utility class for parsing query parameters which define the sorting of elements.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class SortUtility {
/**
* the delimiter between the different field directions in the sort request.
*/
public static final String DELIMITER_SORT_TUPLE = ",";
/**
* the delimiter between the field and direction in the sort request.
*/
public static final String DELIMITER_FIELD_DIRECTION = ":";
/**
* Parses the sort string e.g. given in a REST call based on the definition
* of sorting: <code>http://localhost/entity?s=field1:ASC,field2:DESC</code> The fields
* will be split into the keys of the returned map. The direction of the
* sorting will be mapped into the {@link Direction} enum.
*
* @param enumType the class of the enum which the fields in the sort string should be related to.
* @param <T> the type of the enumeration which must be derived from {@link RsqlQueryField}
* @param sortString the string representation of the query parameters. Might be {@code null} or an empty string.
* @return a list which holds the {@link RsqlQueryField} and the specific {@link Direction} for them as a tuple. Never {@code null}.
* In case of no sorting parameters an empty map will be returned.
* @throws SortParameterSyntaxErrorException if the sorting query parameter is not well-formed
* @throws SortParameterUnsupportedFieldException if a field name cannot be mapped to the enum type
* @throws SortParameterUnsupportedDirectionException if the given direction is not "ASC" or "DESC"
*/
public static <T extends Enum<T> & RsqlQueryField> List<Order> parse(final Class<T> enumType, final String sortString)
throws SortParameterSyntaxErrorException {
final List<Order> orders = new ArrayList<>();
// scan the sort tuples e.g. field:direction
if (sortString != null) {
final StringTokenizer tupleTokenizer = new StringTokenizer(sortString, DELIMITER_SORT_TUPLE);
while (tupleTokenizer.hasMoreTokens()) {
final String sortTuple = tupleTokenizer.nextToken().trim();
final StringTokenizer fieldDirectionTokenizer = new StringTokenizer(sortTuple, DELIMITER_FIELD_DIRECTION);
if (fieldDirectionTokenizer.countTokens() == 2) {
final String fieldName = fieldDirectionTokenizer.nextToken().trim().toUpperCase();
final String sortDirectionStr = fieldDirectionTokenizer.nextToken().trim();
final T identifier;
try {
identifier = Enum.valueOf(enumType, fieldName.toUpperCase());
} catch (final IllegalArgumentException e) {
throw new SortParameterUnsupportedFieldException(e);
}
final Direction sortDirection;
try {
sortDirection = Direction.fromString(sortDirectionStr);
} catch (final IllegalArgumentException e) {
throw new SortParameterUnsupportedDirectionException(e);
}
orders.add(new Order(sortDirection, identifier.getJpaEntityFieldName()));
} else {
throw new SortParameterSyntaxErrorException();
}
}
}
return orders;
}
}

View File

@@ -45,6 +45,7 @@ import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.exception.SpServerError; import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType; import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.resource.util.ResourceUtility;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository; import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;

View File

@@ -32,6 +32,7 @@ import io.qameta.allure.Description;
import io.qameta.allure.Feature; import io.qameta.allure.Feature;
import io.qameta.allure.Story; import io.qameta.allure.Story;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.resource.util.ResourceUtility;
import org.eclipse.hawkbit.repository.event.remote.DistributionSetTagDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.DistributionSetTagDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTagCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTagCreatedEvent;

View File

@@ -48,6 +48,7 @@ import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.mgmt.json.model.artifact.MgmtArtifact; import org.eclipse.hawkbit.mgmt.json.model.artifact.MgmtArtifact;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.resource.util.ResourceUtility;
import org.eclipse.hawkbit.repository.Constants; import org.eclipse.hawkbit.repository.Constants;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;

View File

@@ -33,6 +33,7 @@ import io.qameta.allure.Story;
import org.eclipse.hawkbit.exception.SpServerError; import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType; import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.resource.util.ResourceUtility;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.DeletedException; import org.eclipse.hawkbit.repository.exception.DeletedException;
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException; import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;

View File

@@ -60,6 +60,7 @@ import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType; import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetAutoConfirmUpdate; import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetAutoConfirmUpdate;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.resource.util.ResourceUtility;
import org.eclipse.hawkbit.repository.ActionFields; import org.eclipse.hawkbit.repository.ActionFields;
import org.eclipse.hawkbit.repository.Identifiable; import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate; import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;

View File

@@ -34,6 +34,7 @@ import io.qameta.allure.Feature;
import io.qameta.allure.Story; import io.qameta.allure.Story;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetTagRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetTagRestApi;
import org.eclipse.hawkbit.mgmt.rest.resource.util.ResourceUtility;
import org.eclipse.hawkbit.repository.event.remote.TargetTagDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.TargetTagDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagCreatedEvent;

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.mgmt.rest.resource; package org.eclipse.hawkbit.mgmt.rest.resource.util;
import java.io.IOException; import java.io.IOException;
@@ -25,18 +25,18 @@ public final class ResourceUtility {
private static final ObjectMapper mapper = new ObjectMapper(); private static final ObjectMapper mapper = new ObjectMapper();
static ExceptionInfo convertException(final String jsonExceptionResponse) public static ExceptionInfo convertException(final String jsonExceptionResponse)
throws JsonParseException, JsonMappingException, IOException { throws JsonParseException, JsonMappingException, IOException {
return mapper.readValue(jsonExceptionResponse, ExceptionInfo.class); return mapper.readValue(jsonExceptionResponse, ExceptionInfo.class);
} }
static MgmtArtifact convertArtifactResponse(final String jsonResponse) public static MgmtArtifact convertArtifactResponse(final String jsonResponse)
throws JsonParseException, JsonMappingException, IOException { throws JsonParseException, JsonMappingException, IOException {
return mapper.readValue(jsonResponse, MgmtArtifact.class); return mapper.readValue(jsonResponse, MgmtArtifact.class);
} }
static <T> PagedList<T> mapResponse(final Class<T> clazz, final String responseBody) public static <T> PagedList<T> mapResponse(final Class<T> clazz, final String responseBody)
throws JsonParseException, JsonMappingException, IOException { throws JsonParseException, JsonMappingException, IOException {
return mapper.readValue(responseBody, PagedList.class); return mapper.readValue(responseBody, PagedList.class);
} }

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.rest.util; package org.eclipse.hawkbit.mgmt.rest.resource.util;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -17,16 +17,13 @@ import java.util.List;
import io.qameta.allure.Description; import io.qameta.allure.Description;
import io.qameta.allure.Feature; import io.qameta.allure.Feature;
import io.qameta.allure.Story; import io.qameta.allure.Story;
import org.eclipse.hawkbit.mgmt.rest.resource.exception.SortParameterUnsupportedDirectionException;
import org.eclipse.hawkbit.repository.TargetFields; import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.rest.exception.SortParameterSyntaxErrorException; import org.eclipse.hawkbit.mgmt.rest.resource.exception.SortParameterSyntaxErrorException;
import org.eclipse.hawkbit.rest.exception.SortParameterUnsupportedDirectionException; import org.eclipse.hawkbit.mgmt.rest.resource.exception.SortParameterUnsupportedFieldException;
import org.eclipse.hawkbit.rest.exception.SortParameterUnsupportedFieldException;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Sort.Order; import org.springframework.data.domain.Sort.Order;
/**
*
*/
@Feature("Component Tests - Management API") @Feature("Component Tests - Management API")
@Story("Sorting parameter") @Story("Sorting parameter")
public class SortUtilityTest { public class SortUtilityTest {

View File

@@ -9,15 +9,43 @@
*/ */
package org.eclipse.hawkbit.rest; package org.eclipse.hawkbit.rest;
import org.eclipse.hawkbit.rest.exception.ResponseExceptionHandler; import java.io.IOException;
import org.eclipse.hawkbit.rest.filter.ExcludePathAwareShallowETagFilter; import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.ValidationException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.rest.exception.MessageNotReadableException;
import org.eclipse.hawkbit.rest.exception.MultiPartFileUploadException;
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
import org.eclipse.hawkbit.rest.util.FileStreamingFailedException;
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.config.EnableHypermediaSupport; import org.springframework.hateoas.config.EnableHypermediaSupport;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType; import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.filter.ShallowEtagHeaderFilter;
import org.springframework.web.method.annotation.HandlerMethodValidationException;
import org.springframework.web.multipart.MultipartException;
/** /**
* Configuration for Rest api. * Configuration for Rest api.
@@ -27,8 +55,9 @@ import org.springframework.web.bind.annotation.ControllerAdvice;
public class RestConfiguration { public class RestConfiguration {
/** /**
* {@link ControllerAdvice} for mapping {@link RuntimeException}s from the * {@link ControllerAdvice} for mapping {@link RuntimeException}s from the repository to {@link HttpStatus} codes.
* repository to {@link HttpStatus} codes. *
* @return a controller advice for handling exceptions
*/ */
@Bean @Bean
ResponseExceptionHandler responseExceptionHandler() { ResponseExceptionHandler responseExceptionHandler() {
@@ -38,17 +67,15 @@ public class RestConfiguration {
/** /**
* Filter registration bean for spring etag filter. * Filter registration bean for spring etag filter.
* *
* @return the spring filter registration bean for registering an etag * @return the spring filter registration bean for registering an etag filter in the filter chain
* filter in the filter chain
*/ */
@Bean @Bean
FilterRegistrationBean<ExcludePathAwareShallowETagFilter> eTagFilter() { FilterRegistrationBean<ExcludePathAwareShallowETagFilter> eTagFilter() {
final FilterRegistrationBean<ExcludePathAwareShallowETagFilter> filterRegBean = new FilterRegistrationBean<>(); final FilterRegistrationBean<ExcludePathAwareShallowETagFilter> filterRegBean = new FilterRegistrationBean<>();
// Exclude the URLs for downloading artifacts, so no eTag is generated // Exclude the URLs for downloading artifacts, so no eTag is generated
// in the ShallowEtagHeaderFilter, just using the SH1 hash of the // in the ShallowEtagHeaderFilter, just using the SHA1 hash of the
// artifact itself as 'ETag', because otherwise the file will be copied // artifact itself as 'ETag', because otherwise the file will be copied in memory!
// in memory!
filterRegBean.setFilter(new ExcludePathAwareShallowETagFilter( filterRegBean.setFilter(new ExcludePathAwareShallowETagFilter(
"/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download", "/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download",
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/**", "/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/**",
@@ -56,4 +83,254 @@ public class RestConfiguration {
return filterRegBean; return filterRegBean;
} }
}
/**
* General controller advice for exception handling.
*/
@Slf4j
@ControllerAdvice
public static class ResponseExceptionHandler {
private static final Map<SpServerError, HttpStatus> ERROR_TO_HTTP_STATUS = new EnumMap<>(SpServerError.class);
private static final HttpStatus DEFAULT_RESPONSE_STATUS = HttpStatus.INTERNAL_SERVER_ERROR;
private static final String MESSAGE_FORMATTER_SEPARATOR = " ";
static {
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_ENTITY_NOT_EXISTS, HttpStatus.NOT_FOUND);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_ENTITY_ALREADY_EXISTS, HttpStatus.CONFLICT);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_ENTITY_READ_ONLY, HttpStatus.FORBIDDEN);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REST_SORT_PARAM_INVALID_DIRECTION, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REST_SORT_PARAM_INVALID_FIELD, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REST_SORT_PARAM_SYNTAX, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REST_RSQL_PARAM_INVALID_FIELD, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REST_RSQL_SEARCH_PARAM_SYNTAX, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_INSUFFICIENT_PERMISSION, HttpStatus.FORBIDDEN);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED, HttpStatus.INTERNAL_SERVER_ERROR);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_ENCRYPTION_NOT_SUPPORTED, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_ENCRYPTION_FAILED, HttpStatus.INTERNAL_SERVER_ERROR);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED_SHA1_MATCH, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED_SHA256_MATCH, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED_MD5_MATCH, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_DELETE_FAILED, HttpStatus.INTERNAL_SERVER_ERROR);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_BINARY_DELETED, HttpStatus.GONE);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_LOAD_FAILED, HttpStatus.INTERNAL_SERVER_ERROR);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_QUOTA_EXCEEDED, HttpStatus.FORBIDDEN);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_FILE_SIZE_QUOTA_EXCEEDED, HttpStatus.FORBIDDEN);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_STORAGE_QUOTA_EXCEEDED, HttpStatus.FORBIDDEN);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ACTION_NOT_CANCELABLE, HttpStatus.METHOD_NOT_ALLOWED);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ACTION_NOT_FORCE_QUITABLE, HttpStatus.METHOD_NOT_ALLOWED);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DS_CREATION_FAILED_MISSING_MODULE, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DS_MODULE_UNSUPPORTED, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DS_TYPE_UNDEFINED, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_TENANT_NOT_EXISTS, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ENTITY_LOCKED, HttpStatus.LOCKED);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ROLLOUT_ILLEGAL_STATE, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_CONFIGURATION_VALUE_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_CONFIGURATION_KEY_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_INVALID_TARGET_ADDRESS, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_CONSTRAINT_VIOLATION, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_OPERATION_NOT_SUPPORTED, HttpStatus.GONE);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_CONCURRENT_MODIFICATION, HttpStatus.CONFLICT);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_MAINTENANCE_SCHEDULE_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_TARGET_ATTRIBUTES_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_AUTO_CONFIRMATION_ALREADY_ACTIVE, HttpStatus.CONFLICT);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_AUTO_ASSIGN_ACTION_TYPE_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_CONFIGURATION_VALUE_CHANGE_NOT_ALLOWED, HttpStatus.FORBIDDEN);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_MULTIASSIGNMENT_NOT_ENABLED, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_NO_WEIGHT_PROVIDED_IN_MULTIASSIGNMENT_MODE, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_TARGET_TYPE_IN_USE, HttpStatus.CONFLICT);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_TARGET_TYPE_INCOMPATIBLE, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_TARGET_TYPE_KEY_OR_NAME_REQUIRED, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DS_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DS_INCOMPLETE, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_LOCKED, HttpStatus.LOCKED);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DELETED, HttpStatus.NOT_FOUND);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_STOP_ROLLOUT_FAILED, HttpStatus.LOCKED);
}
/**
* method for handling exception of type AbstractServerRtException. Called by the Spring-Framework for exception handling.
*
* @param request the Http request
* @param ex the exception which occurred
* @return the entity to be responded containing the exception information as entity.
*/
@ExceptionHandler(AbstractServerRtException.class)
public ResponseEntity<ExceptionInfo> handleSpServerRtExceptions(final HttpServletRequest request, final Exception ex) {
logRequest(request, ex);
final ExceptionInfo response = createExceptionInfo(ex);
final HttpStatus responseStatus;
if (ex instanceof AbstractServerRtException) {
responseStatus = getStatusOrDefault(((AbstractServerRtException) ex).getError());
} else {
responseStatus = DEFAULT_RESPONSE_STATUS;
}
return new ResponseEntity<>(response, responseStatus);
}
/**
* Method for handling exception of type {@link FileStreamingFailedException} which is thrown in case the streaming of a file failed
* due to an internal server error. As the streaming of the file has already begun, no JSON response but only the ResponseStatus 500
* is returned.
* Called by the Spring-Framework for exception handling.
*
* @param request the Http request
* @param ex the exception which occurred
* @return the entity to be responded containing the response status 500
*/
@ExceptionHandler(FileStreamingFailedException.class)
public ResponseEntity<Object> handleFileStreamingFailedException(final HttpServletRequest request, final Exception ex) {
logRequest(request, ex);
log.warn("File streaming failed: {}", ex.getMessage());
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
/**
* Method for handling exception of type HttpMessageNotReadableException and MethodArgumentNotValidException which are thrown in case
* the request body is not well-formed (e.g. syntax failures, missing/invalid parameters) and cannot be deserialized.
* Called by the Spring-Framework for exception handling.
*
* @param request the Http request
* @param ex the exception which occurred
* @return the entity to be responded containing the exception information as entity.
*/
@ExceptionHandler({
HttpMessageNotReadableException.class,
MethodArgumentNotValidException.class, HandlerMethodValidationException.class,
IllegalArgumentException.class })
public ResponseEntity<ExceptionInfo> handleExceptionCausedByIncorrectRequestBody(final HttpServletRequest request, final Exception ex) {
logRequest(request, ex);
final ExceptionInfo response = createExceptionInfo(new MessageNotReadableException());
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
/**
* Method for handling exception of type ConstraintViolationException which is thrown in case the request is rejected due to a
* constraint violation.
* Called by the Spring-Framework for exception handling.
*
* @param request the Http request
* @param ex the exception which occurred
* @return the entity to be responded containing the exception information as entity.
*/
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<ExceptionInfo> handleConstraintViolationException(final HttpServletRequest request, final ConstraintViolationException ex) {
logRequest(request, ex);
final ExceptionInfo response = new ExceptionInfo();
response.setMessage(ex.getConstraintViolations().stream()
.map(violation -> violation.getPropertyPath() + MESSAGE_FORMATTER_SEPARATOR + violation.getMessage() + ".")
.collect(Collectors.joining(MESSAGE_FORMATTER_SEPARATOR)));
response.setExceptionClass(ex.getClass().getName());
response.setErrorCode(SpServerError.SP_REPO_CONSTRAINT_VIOLATION.getKey());
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
/**
* Method for handling exception of type ValidationException which is thrown in case the request is rejected due to invalid requests.
* Called by the Spring-Framework for exception handling.
*
* @param request the Http request
* @param ex the exception which occurred
* @return the entity to be responded containing the exception information as entity.
*/
@ExceptionHandler(ValidationException.class)
public ResponseEntity<ExceptionInfo> handleValidationException(final HttpServletRequest request, final ValidationException ex) {
logRequest(request, ex);
final ExceptionInfo response = new ExceptionInfo();
response.setMessage(ex.getMessage());
response.setExceptionClass(ex.getClass().getName());
response.setErrorCode(SpServerError.SP_REPO_CONSTRAINT_VIOLATION.getKey());
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
/**
* Method for handling exception of type {@link MultipartException} which is thrown in case the request body is not well-formed and
* cannot be deserialized.
* Called by the Spring-Framework for exception handling.
*
* @param request the Http request
* @param ex the exception which occurred
* @return the entity to be responded containing the exception information as entity.
*/
@ExceptionHandler(MultipartException.class)
public ResponseEntity<ExceptionInfo> handleMultipartException(final HttpServletRequest request, final Exception ex) {
logRequest(request, ex);
final List<Throwable> throwables = ExceptionUtils.getThrowableList(ex);
final Throwable responseCause = throwables.get(throwables.size() - 1);
if (ObjectUtils.isEmpty(responseCause.getMessage())) {
log.warn("Request {} lead to MultipartException without root cause message:\n{}", request.getRequestURL(),
ex.getStackTrace());
}
return new ResponseEntity<>(createExceptionInfo(new MultiPartFileUploadException(responseCause)), HttpStatus.BAD_REQUEST);
}
private static HttpStatus getStatusOrDefault(final SpServerError error) {
return ERROR_TO_HTTP_STATUS.getOrDefault(error, DEFAULT_RESPONSE_STATUS);
}
private void logRequest(final HttpServletRequest request, final Exception ex) {
log.debug("Handling exception {} of request {}", ex.getClass().getName(), request.getRequestURL());
}
private ExceptionInfo createExceptionInfo(final Exception ex) {
final ExceptionInfo response = new ExceptionInfo();
response.setMessage(ex.getMessage());
response.setExceptionClass(ex.getClass().getName());
if (ex instanceof AbstractServerRtException) {
response.setErrorCode(((AbstractServerRtException) ex).getError().getKey());
response.setInfo(((AbstractServerRtException) ex).getInfo());
}
return response;
}
}
/**
* An {@link ShallowEtagHeaderFilter} with exclusion paths to exclude some paths
* where no ETag header should be generated due that calculating the ETag is an
* expensive operation and the response output need to be copied in memory which
* should be excluded in case of artifact downloads which could be big of size.
*/
static class ExcludePathAwareShallowETagFilter extends ShallowEtagHeaderFilter {
private final String[] excludeAntPaths;
private final AntPathMatcher antMatcher = new AntPathMatcher();
/**
* @param excludeAntPaths
*/
public ExcludePathAwareShallowETagFilter(final String... excludeAntPaths) {
this.excludeAntPaths = excludeAntPaths;
}
@Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain)
throws ServletException, IOException {
final boolean shouldExclude = shouldExclude(request);
if (shouldExclude) {
filterChain.doFilter(request, response);
} else {
super.doFilterInternal(request, response, filterChain);
}
}
private boolean shouldExclude(final HttpServletRequest request) {
for (final String pattern : excludeAntPaths) {
if (antMatcher.match(request.getContextPath() + pattern, request.getRequestURI())) {
// exclude this request from eTag filter
return true;
}
}
return false;
}
}
}

View File

@@ -1,41 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* 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.rest.data;
import org.eclipse.hawkbit.rest.exception.SortParameterUnsupportedDirectionException;
/**
* A definition of possible sorting direction.
*/
public enum SortDirection {
/**
* Ascending.
*/
ASC,
/**
* Descending.
*/
DESC;
/**
* Returns the sort direction for the given name.
*
* @param name the name of the enum
* @return the corresponding enum
* @throws SortParameterUnsupportedDirectionException if there is no matching enum for the specified name
*/
public static SortDirection getByName(final String name) {
try {
return valueOf(name.toUpperCase());
} catch (final IllegalArgumentException ex) {
throw new SortParameterUnsupportedDirectionException();
}
}
}

View File

@@ -9,21 +9,20 @@
*/ */
package org.eclipse.hawkbit.rest.exception; package org.eclipse.hawkbit.rest.exception;
import java.io.Serial;
import org.eclipse.hawkbit.exception.AbstractServerRtException; import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.exception.SpServerError; import org.eclipse.hawkbit.exception.SpServerError;
/** /**
* Thrown if a multi part exception occurred. * Thrown if a multipart exception occurred.
*/ */
public final class MultiPartFileUploadException extends AbstractServerRtException { public final class MultiPartFileUploadException extends AbstractServerRtException {
@Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/**
* @param cause for the exception
*/
public MultiPartFileUploadException(final Throwable cause) { public MultiPartFileUploadException(final Throwable cause) {
super(cause.getMessage(), SpServerError.SP_ARTIFACT_UPLOAD_FAILED, cause); super(cause.getMessage(), SpServerError.SP_ARTIFACT_UPLOAD_FAILED, cause);
} }
}
}

View File

@@ -1,262 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* 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.rest.exception;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.ValidationException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
import org.eclipse.hawkbit.rest.util.FileStreamingFailedException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.annotation.HandlerMethodValidationException;
import org.springframework.web.multipart.MultipartException;
/**
* General controller advice for exception handling.
*/
@Slf4j
@ControllerAdvice
public class ResponseExceptionHandler {
private static final Map<SpServerError, HttpStatus> ERROR_TO_HTTP_STATUS = new EnumMap<>(SpServerError.class);
private static final HttpStatus DEFAULT_RESPONSE_STATUS = HttpStatus.INTERNAL_SERVER_ERROR;
private static final String MESSAGE_FORMATTER_SEPARATOR = " ";
static {
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_ENTITY_NOT_EXISTS, HttpStatus.NOT_FOUND);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_ENTITY_ALREADY_EXISTS, HttpStatus.CONFLICT);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_ENTITY_READ_ONLY, HttpStatus.FORBIDDEN);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REST_SORT_PARAM_INVALID_DIRECTION, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REST_SORT_PARAM_INVALID_FIELD, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REST_SORT_PARAM_SYNTAX, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REST_RSQL_PARAM_INVALID_FIELD, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REST_RSQL_SEARCH_PARAM_SYNTAX, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_INSUFFICIENT_PERMISSION, HttpStatus.FORBIDDEN);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED, HttpStatus.INTERNAL_SERVER_ERROR);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_ENCRYPTION_NOT_SUPPORTED, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_ENCRYPTION_FAILED, HttpStatus.INTERNAL_SERVER_ERROR);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED_SHA1_MATCH, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED_SHA256_MATCH, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED_MD5_MATCH, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_DELETE_FAILED, HttpStatus.INTERNAL_SERVER_ERROR);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_BINARY_DELETED, HttpStatus.GONE);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_LOAD_FAILED, HttpStatus.INTERNAL_SERVER_ERROR);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_QUOTA_EXCEEDED, HttpStatus.FORBIDDEN);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_FILE_SIZE_QUOTA_EXCEEDED, HttpStatus.FORBIDDEN);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_STORAGE_QUOTA_EXCEEDED, HttpStatus.FORBIDDEN);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ACTION_NOT_CANCELABLE, HttpStatus.METHOD_NOT_ALLOWED);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ACTION_NOT_FORCE_QUITABLE, HttpStatus.METHOD_NOT_ALLOWED);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DS_CREATION_FAILED_MISSING_MODULE, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DS_MODULE_UNSUPPORTED, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DS_TYPE_UNDEFINED, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_TENANT_NOT_EXISTS, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ENTITY_LOCKED, HttpStatus.LOCKED);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ROLLOUT_ILLEGAL_STATE, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_CONFIGURATION_VALUE_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_CONFIGURATION_KEY_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_INVALID_TARGET_ADDRESS, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_CONSTRAINT_VIOLATION, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_OPERATION_NOT_SUPPORTED, HttpStatus.GONE);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_CONCURRENT_MODIFICATION, HttpStatus.CONFLICT);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_MAINTENANCE_SCHEDULE_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_TARGET_ATTRIBUTES_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_AUTO_CONFIRMATION_ALREADY_ACTIVE, HttpStatus.CONFLICT);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_AUTO_ASSIGN_ACTION_TYPE_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_CONFIGURATION_VALUE_CHANGE_NOT_ALLOWED, HttpStatus.FORBIDDEN);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_MULTIASSIGNMENT_NOT_ENABLED, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_NO_WEIGHT_PROVIDED_IN_MULTIASSIGNMENT_MODE, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_TARGET_TYPE_IN_USE, HttpStatus.CONFLICT);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_TARGET_TYPE_INCOMPATIBLE, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_TARGET_TYPE_KEY_OR_NAME_REQUIRED, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DS_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DS_INCOMPLETE, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_LOCKED, HttpStatus.LOCKED);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DELETED, HttpStatus.NOT_FOUND);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_STOP_ROLLOUT_FAILED, HttpStatus.LOCKED);
}
/**
* method for handling exception of type AbstractServerRtException. Called
* by the Spring-Framework for exception handling.
*
* @param request the Http request
* @param ex the exception which occurred
* @return the entity to be responded containing the exception information
* as entity.
*/
@ExceptionHandler(AbstractServerRtException.class)
public ResponseEntity<ExceptionInfo> handleSpServerRtExceptions(final HttpServletRequest request,
final Exception ex) {
logRequest(request, ex);
final ExceptionInfo response = createExceptionInfo(ex);
final HttpStatus responseStatus;
if (ex instanceof AbstractServerRtException) {
responseStatus = getStatusOrDefault(((AbstractServerRtException) ex).getError());
} else {
responseStatus = DEFAULT_RESPONSE_STATUS;
}
return new ResponseEntity<>(response, responseStatus);
}
/**
* Method for handling exception of type
* {@link FileStreamingFailedException} which is thrown in case the
* streaming of a file failed due to an internal server error. As the
* streaming of the file has already begun, no JSON response but only the
* ResponseStatus 500 is returned. Called by the Spring-Framework for
* exception handling.
*
* @param request the Http request
* @param ex the exception which occurred
* @return the entity to be responded containing the response status 500
*/
@ExceptionHandler(FileStreamingFailedException.class)
public ResponseEntity<Object> handleFileStreamingFailedException(final HttpServletRequest request,
final Exception ex) {
logRequest(request, ex);
log.warn("File streaming failed: {}", ex.getMessage());
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
/**
* Method for handling exception of type HttpMessageNotReadableException and
* MethodArgumentNotValidException which are thrown in case the request body
* is not well formed (e.g. syntax failures, missing/invalid parameters) and
* cannot be deserialized. Called by the Spring-Framework for exception
* handling.
*
* @param request the Http request
* @param ex the exception which occurred
* @return the entity to be responded containing the exception information
* as entity.
*/
@ExceptionHandler({
HttpMessageNotReadableException.class,
MethodArgumentNotValidException.class, HandlerMethodValidationException.class,
IllegalArgumentException.class })
public ResponseEntity<ExceptionInfo> handleExceptionCausedByIncorrectRequestBody(final HttpServletRequest request,
final Exception ex) {
logRequest(request, ex);
final ExceptionInfo response = createExceptionInfo(new MessageNotReadableException());
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
/**
* Method for handling exception of type ConstraintViolationException which
* is thrown in case the request is rejected due to a constraint violation.
* Called by the Spring-Framework for exception handling.
*
* @param request the Http request
* @param ex the exception which occurred
* @return the entity to be responded containing the exception information
* as entity.
*/
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<ExceptionInfo> handleConstraintViolationException(final HttpServletRequest request,
final ConstraintViolationException ex) {
logRequest(request, ex);
final ExceptionInfo response = new ExceptionInfo();
response.setMessage(ex.getConstraintViolations().stream().map(
violation -> violation.getPropertyPath() + MESSAGE_FORMATTER_SEPARATOR + violation.getMessage() + ".")
.collect(Collectors.joining(MESSAGE_FORMATTER_SEPARATOR)));
response.setExceptionClass(ex.getClass().getName());
response.setErrorCode(SpServerError.SP_REPO_CONSTRAINT_VIOLATION.getKey());
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
/**
* Method for handling exception of type ValidationException which is thrown
* in case the request is rejected due to invalid requests. Called by the
* Spring-Framework for exception handling.
*
* @param request the Http request
* @param ex the exception which occurred
* @return the entity to be responded containing the exception information
* as entity.
*/
@ExceptionHandler(ValidationException.class)
public ResponseEntity<ExceptionInfo> handleValidationException(final HttpServletRequest request,
final ValidationException ex) {
logRequest(request, ex);
final ExceptionInfo response = new ExceptionInfo();
response.setMessage(ex.getMessage());
response.setExceptionClass(ex.getClass().getName());
response.setErrorCode(SpServerError.SP_REPO_CONSTRAINT_VIOLATION.getKey());
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
/**
* Method for handling exception of type {@link MultipartException} which is
* thrown in case the request body is not well formed and cannot be
* deserialized. Called by the Spring-Framework for exception handling.
*
* @param request the Http request
* @param ex the exception which occurred
* @return the entity to be responded containing the exception information
* as entity.
*/
@ExceptionHandler(MultipartException.class)
public ResponseEntity<ExceptionInfo> handleMultipartException(final HttpServletRequest request,
final Exception ex) {
logRequest(request, ex);
final List<Throwable> throwables = ExceptionUtils.getThrowableList(ex);
final Throwable responseCause = throwables.get(throwables.size() - 1);
if (ObjectUtils.isEmpty(responseCause.getMessage())) {
log.warn("Request {} lead to MultipartException without root cause message:\n{}", request.getRequestURL(),
ex.getStackTrace());
}
final ExceptionInfo response = createExceptionInfo(new MultiPartFileUploadException(responseCause));
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
private static HttpStatus getStatusOrDefault(final SpServerError error) {
return ERROR_TO_HTTP_STATUS.getOrDefault(error, DEFAULT_RESPONSE_STATUS);
}
private void logRequest(final HttpServletRequest request, final Exception ex) {
log.debug("Handling exception {} of request {}", ex.getClass().getName(), request.getRequestURL());
}
private ExceptionInfo createExceptionInfo(final Exception ex) {
final ExceptionInfo response = new ExceptionInfo();
response.setMessage(ex.getMessage());
response.setExceptionClass(ex.getClass().getName());
if (ex instanceof AbstractServerRtException) {
response.setErrorCode(((AbstractServerRtException) ex).getError().getKey());
response.setInfo(((AbstractServerRtException) ex).getInfo());
}
return response;
}
}

View File

@@ -1,60 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* 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.rest.filter;
import java.io.IOException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.ShallowEtagHeaderFilter;
/**
* An {@link ShallowEtagHeaderFilter} with exclusion paths to exclude some paths
* where no ETag header should be generated due that calculating the ETag is an
* expensive operation and the response output need to be copied in memory which
* should be excluded in case of artifact downloads which could be big of size.
*/
public class ExcludePathAwareShallowETagFilter extends ShallowEtagHeaderFilter {
private final String[] excludeAntPaths;
private final AntPathMatcher antMatcher = new AntPathMatcher();
/**
* @param excludeAntPaths
*/
public ExcludePathAwareShallowETagFilter(final String... excludeAntPaths) {
this.excludeAntPaths = excludeAntPaths;
}
@Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
final FilterChain filterChain) throws ServletException, IOException {
final boolean shouldExclude = shouldExclude(request);
if (shouldExclude) {
filterChain.doFilter(request, response);
} else {
super.doFilterInternal(request, response, filterChain);
}
}
private boolean shouldExclude(final HttpServletRequest request) {
for (final String pattern : excludeAntPaths) {
if (antMatcher.match(request.getContextPath() + pattern, request.getRequestURI())) {
// exclude this request from eTag filter
return true;
}
}
return false;
}
}

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.rest.data; package org.eclipse.hawkbit.rest.json.model;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;

View File

@@ -9,6 +9,8 @@
*/ */
package org.eclipse.hawkbit.rest.util; package org.eclipse.hawkbit.rest.util;
import java.io.Serial;
import org.eclipse.hawkbit.exception.AbstractServerRtException; import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.exception.SpServerError; import org.eclipse.hawkbit.exception.SpServerError;
@@ -17,25 +19,9 @@ import org.eclipse.hawkbit.exception.SpServerError;
*/ */
public final class FileStreamingFailedException extends AbstractServerRtException { public final class FileStreamingFailedException extends AbstractServerRtException {
@Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/**
* Creates a new FileUploadFailedException with
* {@link SpServerError#SP_ARTIFACT_LOAD_FAILED} error.
*/
public FileStreamingFailedException() {
super(SpServerError.SP_ARTIFACT_LOAD_FAILED);
}
/**
* Constructor with Throwable.
*
* @param cause for the exception
*/
public FileStreamingFailedException(final Throwable cause) {
super(SpServerError.SP_ARTIFACT_LOAD_FAILED, cause);
}
/** /**
* Constructor with error string. * Constructor with error string.
* *
@@ -54,4 +40,4 @@ public final class FileStreamingFailedException extends AbstractServerRtExceptio
public FileStreamingFailedException(final String message, final Throwable cause) { public FileStreamingFailedException(final String message, final Throwable cause) {
super(message, SpServerError.SP_ARTIFACT_LOAD_FAILED, cause); super(message, SpServerError.SP_ARTIFACT_LOAD_FAILED, cause);
} }
} }

View File

@@ -1,26 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* 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.rest.util;
/**
* Listener for progress on artifact file streaming.
*/
@FunctionalInterface
public interface FileStreamingProgressListener {
/**
* Called multiple times during streaming.
*
* @param requestedBytes requested bytes of the request
* @param shippedBytesSinceLast since the last report
* @param shippedBytesOverall during the request
*/
void progress(long requestedBytes, long shippedBytesSinceLast, long shippedBytesOverall);
}

View File

@@ -443,4 +443,19 @@ public final class FileStreamingUtil {
} }
/**
* Listener for progress on artifact file streaming.
*/
@FunctionalInterface
public interface FileStreamingProgressListener {
/**
* Called multiple times during streaming.
*
* @param requestedBytes requested bytes of the request
* @param shippedBytesSinceLast since the last report
* @param shippedBytesOverall during the request
*/
void progress(long requestedBytes, long shippedBytesSinceLast, long shippedBytesOverall);
}
} }

View File

@@ -11,15 +11,15 @@ package org.eclipse.hawkbit.rest.util;
import java.util.Arrays; import java.util.Arrays;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
/** /**
* Utility class for the Rest Source API. * Utility class for the Rest Source API.
*/ */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class HttpUtil { public final class HttpUtil {
private HttpUtil() {
}
/** /**
* Checks given CSV string for defined match value or wildcard. * Checks given CSV string for defined match value or wildcard.
* *
@@ -32,5 +32,4 @@ public final class HttpUtil {
Arrays.sort(matchValues); Arrays.sort(matchValues);
return Arrays.binarySearch(matchValues, toMatch) > -1 || Arrays.binarySearch(matchValues, "*") > -1; return Arrays.binarySearch(matchValues, toMatch) > -1 || Arrays.binarySearch(matchValues, "*") > -1;
} }
}
}

View File

@@ -1,114 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* 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.rest.util;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.rest.exception.SortParameterSyntaxErrorException;
import org.eclipse.hawkbit.rest.exception.SortParameterUnsupportedDirectionException;
import org.eclipse.hawkbit.rest.exception.SortParameterUnsupportedFieldException;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
/**
* A utility class for parsing query parameters which define the sorting of
* elements.
*/
public final class SortUtility {
/**
* the delimiter between the field and direction in the sort request.
*/
public static final String DELIMITER_FIELD_DIRECTION = ":";
private static final String DELIMITER_SORT_TUPLE = ",";
/*
* utility constructor private.
*/
private SortUtility() {
}
/**
* Parses the sort string e.g. given in a REST call based on the definition
* of sorting: http://localhost/entity?s=field1:ASC, field2:DESC The fields
* will be split into the keys of the returned map. The direction of the
* sorting will be mapped into the {@link Direction} enum.
*
* @param enumType the class of the enum which the fields in the sort string
* should be related to.
* @param <T> the type of the enumeration which must be derived from
* {@link RsqlQueryField}
* @param sortString the string representation of the query parameters. Might be
* {@code null} or an empty string.
* @return a list which holds the {@link RsqlQueryField} and the specific
* {@link Direction} for them as a tuple. Never {@code null}. In
* case of no sorting parameters an empty map will be returned.
* @throws SortParameterSyntaxErrorException if the sorting query parameter is not well-formed
* @throws SortParameterUnsupportedFieldException if a field name cannot be mapped to the enum type
* @throws SortParameterUnsupportedDirectionException if the given direction is not "ASC" or "DESC"
*/
public static <T extends Enum<T> & RsqlQueryField> List<Order> parse(final Class<T> enumType,
final String sortString) throws SortParameterSyntaxErrorException {
final List<Order> parsedSortings = new ArrayList<>();
// scan the sort tuples e.g. field:direction
if (sortString != null) {
final StringTokenizer tupleTokenizer = new StringTokenizer(sortString, DELIMITER_SORT_TUPLE);
while (tupleTokenizer.hasMoreTokens()) {
final String sortTuple = tupleTokenizer.nextToken().trim();
final StringTokenizer fieldDirectionTokenizer = new StringTokenizer(sortTuple,
DELIMITER_FIELD_DIRECTION);
if (fieldDirectionTokenizer.countTokens() == 2) {
final String fieldName = fieldDirectionTokenizer.nextToken().trim().toUpperCase();
final String sortDirectionStr = fieldDirectionTokenizer.nextToken().trim();
final T identifier = getAttributeIdentifierByName(enumType, fieldName);
final Direction sortDirection = getDirection(sortDirectionStr);
parsedSortings.add(new Order(sortDirection, identifier.getJpaEntityFieldName()));
} else {
throw new SortParameterSyntaxErrorException();
}
}
}
return parsedSortings;
}
/**
* Returns the attribute identifier for the given name.
*
* @param enumType the class of the enum which the fields in the sort string
* should be related to.
* @param name the name of the enum
* @param <T> the type of the enumeration which must be derived from
* {@link RsqlQueryField}
* @return the corresponding enum
* @throws SortParameterUnsupportedFieldException if there is no matching enum for the specified name
*/
private static <T extends Enum<T> & RsqlQueryField> T getAttributeIdentifierByName(final Class<T> enumType,
final String name) {
try {
return Enum.valueOf(enumType, name.toUpperCase());
} catch (final IllegalArgumentException e) {
throw new SortParameterUnsupportedFieldException(e);
}
}
private static Direction getDirection(final String sortDirectionStr) {
try {
return Direction.fromString(sortDirectionStr);
} catch (final IllegalArgumentException e) {
throw new SortParameterUnsupportedDirectionException(e);
}
}
}

View File

@@ -12,7 +12,6 @@ package org.eclipse.hawkbit.rest;
import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration; import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration;
import org.eclipse.hawkbit.repository.test.TestConfiguration; import org.eclipse.hawkbit.repository.test.TestConfiguration;
import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest; import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
import org.eclipse.hawkbit.rest.filter.ExcludePathAwareShallowETagFilter;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
@@ -55,7 +54,7 @@ public abstract class AbstractRestIntegrationTest extends AbstractIntegrationTes
// CharacterEncodingFilter is needed for the encoding properties to be imported properly // CharacterEncodingFilter is needed for the encoding properties to be imported properly
createMvcWebAppContext.addFilter(characterEncodingFilter); createMvcWebAppContext.addFilter(characterEncodingFilter);
createMvcWebAppContext.addFilter( createMvcWebAppContext.addFilter(
new ExcludePathAwareShallowETagFilter("/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download", new RestConfiguration.ExcludePathAwareShallowETagFilter("/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download",
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/**", "/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/**",
"/api/v1/downloadserver/**")); "/api/v1/downloadserver/**"));

View File

@@ -7,7 +7,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.rest.filter; package org.eclipse.hawkbit.rest;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mockingDetails; import static org.mockito.Mockito.mockingDetails;
@@ -55,7 +55,7 @@ public class ExcludePathAwareShallowETagFilterTest {
when(servletRequestMock.getContextPath()).thenReturn(knownContextPath); when(servletRequestMock.getContextPath()).thenReturn(knownContextPath);
when(servletRequestMock.getRequestURI()).thenReturn(knownUri); when(servletRequestMock.getRequestURI()).thenReturn(knownUri);
final ExcludePathAwareShallowETagFilter filterUnderTest = new ExcludePathAwareShallowETagFilter( final RestConfiguration.ExcludePathAwareShallowETagFilter filterUnderTest = new RestConfiguration.ExcludePathAwareShallowETagFilter(
antPathExclusion); antPathExclusion);
filterUnderTest.doFilterInternal(servletRequestMock, servletResponseMock, filterChainMock); filterUnderTest.doFilterInternal(servletRequestMock, servletResponseMock, filterChainMock);
@@ -77,7 +77,7 @@ public class ExcludePathAwareShallowETagFilterTest {
when(servletRequestMock.getContextPath()).thenReturn(knownContextPath); when(servletRequestMock.getContextPath()).thenReturn(knownContextPath);
when(servletRequestMock.getRequestURI()).thenReturn(knownUri); when(servletRequestMock.getRequestURI()).thenReturn(knownUri);
final ExcludePathAwareShallowETagFilter filterUnderTest = new ExcludePathAwareShallowETagFilter( final RestConfiguration.ExcludePathAwareShallowETagFilter filterUnderTest = new RestConfiguration.ExcludePathAwareShallowETagFilter(
antPathExclusion); antPathExclusion);
final ArgumentCaptor<HttpServletResponse> responseArgumentCaptor = ArgumentCaptor final ArgumentCaptor<HttpServletResponse> responseArgumentCaptor = ArgumentCaptor