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:
@@ -34,7 +34,7 @@ import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
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.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
|
||||
|
||||
@@ -711,7 +711,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
Collections.singletonList("test"));
|
||||
postDeploymentFeedback("1080", action.getId(), missingResultInFeedback, status().isBadRequest())
|
||||
.andExpect(jsonPath("$.*", hasSize(3)))
|
||||
.andExpect(jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getCanonicalName())));
|
||||
.andExpect(jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getName())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -736,7 +736,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
postDeploymentFeedback("1080", action.getId(), missingFinishedResultInFeedback, status().isBadRequest())
|
||||
.andExpect(jsonPath("$.*", hasSize(3)))
|
||||
.andExpect(jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getCanonicalName())));
|
||||
.andExpect(jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getName())));
|
||||
}
|
||||
|
||||
public long countActionStatusAll() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode;
|
||||
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
|
||||
|
||||
@@ -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.rest.api.MgmtActionRestApi;
|
||||
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.OffsetBasedPageRequest;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
|
||||
@@ -34,7 +34,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
||||
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
|
||||
|
||||
@@ -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.rest.api.MgmtDistributionSetRestApi;
|
||||
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.DistributionSetInvalidationManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
|
||||
@@ -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.rest.api.MgmtDistributionSetTagRestApi;
|
||||
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.DistributionSetTagManagement;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeCreate;
|
||||
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
|
||||
|
||||
@@ -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.rest.api.MgmtDistributionSetTypeRestApi;
|
||||
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.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
|
||||
|
||||
@@ -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.MgmtRestConstants;
|
||||
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.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
|
||||
|
||||
@@ -38,7 +38,7 @@ import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
|
||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.MgmtRestConstants;
|
||||
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.EntityFactory;
|
||||
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.SoftwareModuleMetadata;
|
||||
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.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftwareModuleTypeRestApi;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeCreate;
|
||||
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
|
||||
|
||||
@@ -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.rest.api.MgmtRestConstants;
|
||||
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.OffsetBasedPageRequest;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.eclipse.hawkbit.repository.builder.TagCreate;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.model.Tag;
|
||||
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.
|
||||
|
||||
@@ -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.MgmtRestConstants;
|
||||
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.OffsetBasedPageRequest;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
|
||||
@@ -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.MgmtTargetRestApi;
|
||||
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.ActionStatusFields;
|
||||
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.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.eclipse.hawkbit.rest.data.ResponseList;
|
||||
import org.eclipse.hawkbit.rest.data.SortDirection;
|
||||
import org.eclipse.hawkbit.rest.json.model.ResponseList;
|
||||
import org.eclipse.hawkbit.util.IpUtil;
|
||||
import org.eclipse.hawkbit.utils.TenantConfigHelper;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
|
||||
@@ -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.rest.api.MgmtRestConstants;
|
||||
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.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
|
||||
@@ -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.rest.api.MgmtRestConstants;
|
||||
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.OffsetBasedPageRequest;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetTypeRestApi;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
|
||||
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.
|
||||
|
||||
@@ -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.rest.api.MgmtRestConstants;
|
||||
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.OffsetBasedPageRequest;
|
||||
import org.eclipse.hawkbit.repository.TargetTypeManagement;
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
*
|
||||
* 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.SpServerError;
|
||||
@@ -17,14 +19,11 @@ import org.eclipse.hawkbit.exception.SpServerError;
|
||||
*/
|
||||
public class SortParameterSyntaxErrorException extends AbstractServerRtException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates a new SortParameterSyntaxErrorException with
|
||||
* {@link SpServerError#SP_REST_SORT_PARAM_SYNTAX} error.
|
||||
* Creates a new SortParameterSyntaxErrorException with {@link SpServerError#SP_REST_SORT_PARAM_SYNTAX} error.
|
||||
*/
|
||||
public SortParameterSyntaxErrorException() {
|
||||
super(SpServerError.SP_REST_SORT_PARAM_SYNTAX);
|
||||
@@ -7,36 +7,26 @@
|
||||
*
|
||||
* 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.SpServerError;
|
||||
|
||||
/**
|
||||
* Exception used by the REST API in case of invalid sort parameter direction
|
||||
* name.
|
||||
* Exception used by the REST API in case of invalid sort parameter direction name.
|
||||
*/
|
||||
public class SortParameterUnsupportedDirectionException extends AbstractServerRtException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates a new SortParameterSyntaxErrorException.
|
||||
*/
|
||||
public SortParameterUnsupportedDirectionException() {
|
||||
super(SpServerError.SP_REST_SORT_PARAM_INVALID_DIRECTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SortParameterSyntaxErrorException with
|
||||
* {@link SpServerError#SP_REST_SORT_PARAM_INVALID_DIRECTION} error.
|
||||
* 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
|
||||
* getCause() method). (A null value is permitted, and indicates
|
||||
* that the cause is nonexistent or unknown.)
|
||||
* @param cause the cause (which is saved for later retrieval by the getCause() method). (A null value is permitted, and indicates
|
||||
* that the cause is nonexistent or unknown.)
|
||||
*/
|
||||
public SortParameterUnsupportedDirectionException(final Throwable cause) {
|
||||
super(SpServerError.SP_REST_SORT_PARAM_INVALID_DIRECTION, cause);
|
||||
@@ -7,37 +7,26 @@
|
||||
*
|
||||
* 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.SpServerError;
|
||||
|
||||
/**
|
||||
* Exception used by the REST API in case of invalid field name in the sort
|
||||
* parameter.
|
||||
* Exception used by the REST API in case of invalid field name in the sort parameter.
|
||||
*/
|
||||
public class SortParameterUnsupportedFieldException extends AbstractServerRtException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates a new SortParameterSyntaxErrorException with
|
||||
* {@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.
|
||||
* 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
|
||||
* getCause() method). (A null value is permitted, and indicates
|
||||
* that the cause is nonexistent or unknown.)
|
||||
* @param cause the cause (which is saved for later retrieval by the getCause() method). (A null value is permitted, and indicates
|
||||
* that the cause is nonexistent or unknown.)
|
||||
*/
|
||||
public SortParameterUnsupportedFieldException(final Throwable cause) {
|
||||
super(SpServerError.SP_REST_SORT_PARAM_INVALID_FIELD, cause);
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
* 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.repository.ActionFields;
|
||||
@@ -24,7 +24,6 @@ import org.eclipse.hawkbit.repository.TagFields;
|
||||
import org.eclipse.hawkbit.repository.TargetFields;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
|
||||
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.Direction;
|
||||
|
||||
@@ -39,14 +38,14 @@ public final class PagingUtility {
|
||||
private PagingUtility() {
|
||||
}
|
||||
|
||||
static int sanitizeOffsetParam(final int offset) {
|
||||
public static int sanitizeOffsetParam(final int offset) {
|
||||
if (offset < 0) {
|
||||
return MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int sanitizePageLimitParam(final int pageLimit) {
|
||||
public static int sanitizePageLimitParam(final int pageLimit) {
|
||||
if (pageLimit < 1) {
|
||||
return MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE;
|
||||
} else if (pageLimit > MgmtRestConstants.REQUEST_PARAMETER_PAGING_MAX_LIMIT) {
|
||||
@@ -55,7 +54,7 @@ public final class PagingUtility {
|
||||
return pageLimit;
|
||||
}
|
||||
|
||||
static Sort sanitizeTargetSortParam(final String sortParam) {
|
||||
public static Sort sanitizeTargetSortParam(final String sortParam) {
|
||||
if (sortParam == null) {
|
||||
// default
|
||||
return Sort.by(Direction.ASC, TargetFields.CONTROLLERID.getJpaEntityFieldName());
|
||||
@@ -63,7 +62,7 @@ public final class PagingUtility {
|
||||
return Sort.by(SortUtility.parse(TargetFields.class, sortParam));
|
||||
}
|
||||
|
||||
static Sort sanitizeTargetTypeSortParam(final String sortParam) {
|
||||
public static Sort sanitizeTargetTypeSortParam(final String sortParam) {
|
||||
if (sortParam == null) {
|
||||
// default
|
||||
return Sort.by(Direction.ASC, TargetTypeFields.ID.getJpaEntityFieldName());
|
||||
@@ -71,7 +70,7 @@ public final class PagingUtility {
|
||||
return Sort.by(SortUtility.parse(TargetTypeFields.class, sortParam));
|
||||
}
|
||||
|
||||
static Sort sanitizeTagSortParam(final String sortParam) {
|
||||
public static Sort sanitizeTagSortParam(final String sortParam) {
|
||||
if (sortParam == null) {
|
||||
// default
|
||||
return Sort.by(Direction.ASC, TagFields.ID.getJpaEntityFieldName());
|
||||
@@ -79,7 +78,7 @@ public final class PagingUtility {
|
||||
return Sort.by(SortUtility.parse(TagFields.class, sortParam));
|
||||
}
|
||||
|
||||
static Sort sanitizeTargetFilterQuerySortParam(final String sortParam) {
|
||||
public static Sort sanitizeTargetFilterQuerySortParam(final String sortParam) {
|
||||
if (sortParam == null) {
|
||||
// default
|
||||
return Sort.by(Direction.ASC, TargetFilterQueryFields.ID.getJpaEntityFieldName());
|
||||
@@ -87,7 +86,7 @@ public final class PagingUtility {
|
||||
return Sort.by(SortUtility.parse(TargetFilterQueryFields.class, sortParam));
|
||||
}
|
||||
|
||||
static Sort sanitizeSoftwareModuleSortParam(final String sortParam) {
|
||||
public static Sort sanitizeSoftwareModuleSortParam(final String sortParam) {
|
||||
if (sortParam == null) {
|
||||
// default
|
||||
return Sort.by(Direction.ASC, SoftwareModuleFields.ID.getJpaEntityFieldName());
|
||||
@@ -95,7 +94,7 @@ public final class PagingUtility {
|
||||
return Sort.by(SortUtility.parse(SoftwareModuleFields.class, sortParam));
|
||||
}
|
||||
|
||||
static Sort sanitizeSoftwareModuleTypeSortParam(final String sortParam) {
|
||||
public static Sort sanitizeSoftwareModuleTypeSortParam(final String sortParam) {
|
||||
if (sortParam == null) {
|
||||
// default
|
||||
return Sort.by(Direction.ASC, SoftwareModuleTypeFields.ID.getJpaEntityFieldName());
|
||||
@@ -103,7 +102,7 @@ public final class PagingUtility {
|
||||
return Sort.by(SortUtility.parse(SoftwareModuleTypeFields.class, sortParam));
|
||||
}
|
||||
|
||||
static Sort sanitizeDistributionSetSortParam(final String sortParam) {
|
||||
public static Sort sanitizeDistributionSetSortParam(final String sortParam) {
|
||||
if (sortParam == null) {
|
||||
// default
|
||||
return Sort.by(Direction.ASC, DistributionSetFields.ID.getJpaEntityFieldName());
|
||||
@@ -111,7 +110,7 @@ public final class PagingUtility {
|
||||
return Sort.by(SortUtility.parse(DistributionSetFields.class, sortParam));
|
||||
}
|
||||
|
||||
static Sort sanitizeDistributionSetTypeSortParam(final String sortParam) {
|
||||
public static Sort sanitizeDistributionSetTypeSortParam(final String sortParam) {
|
||||
if (sortParam == null) {
|
||||
// default
|
||||
return Sort.by(Direction.ASC, DistributionSetTypeFields.ID.getJpaEntityFieldName());
|
||||
@@ -119,7 +118,7 @@ public final class PagingUtility {
|
||||
return Sort.by(SortUtility.parse(DistributionSetTypeFields.class, sortParam));
|
||||
}
|
||||
|
||||
static Sort sanitizeActionSortParam(final String sortParam) {
|
||||
public static Sort sanitizeActionSortParam(final String sortParam) {
|
||||
if (sortParam == null) {
|
||||
// default sort is DESC in case of action to match behavior
|
||||
// of management UI (last entry on top)
|
||||
@@ -128,7 +127,7 @@ public final class PagingUtility {
|
||||
return Sort.by(SortUtility.parse(ActionFields.class, sortParam));
|
||||
}
|
||||
|
||||
static Sort sanitizeActionStatusSortParam(final String sortParam) {
|
||||
public static Sort sanitizeActionStatusSortParam(final String sortParam) {
|
||||
if (sortParam == null) {
|
||||
// default sort is DESC in case of action status to match behavior
|
||||
// of management UI (last entry on top)
|
||||
@@ -137,7 +136,7 @@ public final class PagingUtility {
|
||||
return Sort.by(SortUtility.parse(ActionStatusFields.class, sortParam));
|
||||
}
|
||||
|
||||
static Sort sanitizeDistributionSetMetadataSortParam(final String sortParam) {
|
||||
public static Sort sanitizeDistributionSetMetadataSortParam(final String sortParam) {
|
||||
if (sortParam == null) {
|
||||
// default
|
||||
return Sort.by(Direction.ASC, DistributionSetMetadataFields.KEY.getJpaEntityFieldName());
|
||||
@@ -145,7 +144,7 @@ public final class PagingUtility {
|
||||
return Sort.by(SortUtility.parse(DistributionSetMetadataFields.class, sortParam));
|
||||
}
|
||||
|
||||
static Sort sanitizeSoftwareModuleMetadataSortParam(final String sortParam) {
|
||||
public static Sort sanitizeSoftwareModuleMetadataSortParam(final String sortParam) {
|
||||
if (sortParam == null) {
|
||||
// default
|
||||
return Sort.by(Direction.ASC, SoftwareModuleMetadataFields.KEY.getJpaEntityFieldName());
|
||||
@@ -153,7 +152,7 @@ public final class PagingUtility {
|
||||
return Sort.by(SortUtility.parse(SoftwareModuleMetadataFields.class, sortParam));
|
||||
}
|
||||
|
||||
static Sort sanitizeRolloutSortParam(final String sortParam) {
|
||||
public static Sort sanitizeRolloutSortParam(final String sortParam) {
|
||||
if (sortParam == null) {
|
||||
// default
|
||||
return Sort.by(Direction.ASC, RolloutFields.ID.getJpaEntityFieldName());
|
||||
@@ -161,7 +160,7 @@ public final class PagingUtility {
|
||||
return Sort.by(SortUtility.parse(RolloutFields.class, sortParam));
|
||||
}
|
||||
|
||||
static Sort sanitizeRolloutGroupSortParam(final String sortParam) {
|
||||
public static Sort sanitizeRolloutGroupSortParam(final String sortParam) {
|
||||
if (sortParam == null) {
|
||||
// default
|
||||
return Sort.by(Direction.ASC, RolloutGroupFields.ID.getJpaEntityFieldName());
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,7 @@ import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.eclipse.hawkbit.exception.SpServerError;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
|
||||
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.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
|
||||
|
||||
@@ -32,6 +32,7 @@ import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
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.entity.DistributionSetCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTagCreatedEvent;
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.eclipse.hawkbit.exception.SpServerError;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.artifact.MgmtArtifact;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRepresentationMode;
|
||||
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.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
|
||||
@@ -33,6 +33,7 @@ import io.qameta.allure.Story;
|
||||
import org.eclipse.hawkbit.exception.SpServerError;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
|
||||
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.DeletedException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
|
||||
@@ -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.target.MgmtTargetAutoConfirmUpdate;
|
||||
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.Identifiable;
|
||||
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
|
||||
|
||||
@@ -34,6 +34,7 @@ import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
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.entity.TargetCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagCreatedEvent;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
* 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;
|
||||
|
||||
@@ -25,18 +25,18 @@ public final class ResourceUtility {
|
||||
|
||||
private static final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
static ExceptionInfo convertException(final String jsonExceptionResponse)
|
||||
public static ExceptionInfo convertException(final String jsonExceptionResponse)
|
||||
throws JsonParseException, JsonMappingException, IOException {
|
||||
return mapper.readValue(jsonExceptionResponse, ExceptionInfo.class);
|
||||
}
|
||||
|
||||
static MgmtArtifact convertArtifactResponse(final String jsonResponse)
|
||||
public static MgmtArtifact convertArtifactResponse(final String jsonResponse)
|
||||
throws JsonParseException, JsonMappingException, IOException {
|
||||
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 {
|
||||
return mapper.readValue(responseBody, PagedList.class);
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
* 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.junit.jupiter.api.Assertions.assertThrows;
|
||||
@@ -17,16 +17,13 @@ import java.util.List;
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
import org.eclipse.hawkbit.mgmt.rest.resource.exception.SortParameterUnsupportedDirectionException;
|
||||
import org.eclipse.hawkbit.repository.TargetFields;
|
||||
import org.eclipse.hawkbit.rest.exception.SortParameterSyntaxErrorException;
|
||||
import org.eclipse.hawkbit.rest.exception.SortParameterUnsupportedDirectionException;
|
||||
import org.eclipse.hawkbit.rest.exception.SortParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.mgmt.rest.resource.exception.SortParameterSyntaxErrorException;
|
||||
import org.eclipse.hawkbit.mgmt.rest.resource.exception.SortParameterUnsupportedFieldException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.domain.Sort.Order;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Feature("Component Tests - Management API")
|
||||
@Story("Sorting parameter")
|
||||
public class SortUtilityTest {
|
||||
@@ -9,15 +9,43 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.rest;
|
||||
|
||||
import org.eclipse.hawkbit.rest.exception.ResponseExceptionHandler;
|
||||
import org.eclipse.hawkbit.rest.filter.ExcludePathAwareShallowETagFilter;
|
||||
import java.io.IOException;
|
||||
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.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
|
||||
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.ExceptionHandler;
|
||||
import org.springframework.web.filter.ShallowEtagHeaderFilter;
|
||||
import org.springframework.web.method.annotation.HandlerMethodValidationException;
|
||||
import org.springframework.web.multipart.MultipartException;
|
||||
|
||||
/**
|
||||
* Configuration for Rest api.
|
||||
@@ -27,8 +55,9 @@ import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
public class RestConfiguration {
|
||||
|
||||
/**
|
||||
* {@link ControllerAdvice} for mapping {@link RuntimeException}s from the
|
||||
* repository to {@link HttpStatus} codes.
|
||||
* {@link ControllerAdvice} for mapping {@link RuntimeException}s from the repository to {@link HttpStatus} codes.
|
||||
*
|
||||
* @return a controller advice for handling exceptions
|
||||
*/
|
||||
@Bean
|
||||
ResponseExceptionHandler responseExceptionHandler() {
|
||||
@@ -38,17 +67,15 @@ public class RestConfiguration {
|
||||
/**
|
||||
* Filter registration bean for spring etag filter.
|
||||
*
|
||||
* @return the spring filter registration bean for registering an etag
|
||||
* filter in the filter chain
|
||||
* @return the spring filter registration bean for registering an etag filter in the filter chain
|
||||
*/
|
||||
@Bean
|
||||
FilterRegistrationBean<ExcludePathAwareShallowETagFilter> eTagFilter() {
|
||||
|
||||
final FilterRegistrationBean<ExcludePathAwareShallowETagFilter> filterRegBean = new FilterRegistrationBean<>();
|
||||
|
||||
// Exclude the URLs for downloading artifacts, so no eTag is generated
|
||||
// in the ShallowEtagHeaderFilter, just using the SH1 hash of the
|
||||
// artifact itself as 'ETag', because otherwise the file will be copied
|
||||
// in memory!
|
||||
// in the ShallowEtagHeaderFilter, just using the SHA1 hash of the
|
||||
// artifact itself as 'ETag', because otherwise the file will be copied in memory!
|
||||
filterRegBean.setFilter(new ExcludePathAwareShallowETagFilter(
|
||||
"/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download",
|
||||
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/**",
|
||||
@@ -56,4 +83,254 @@ public class RestConfiguration {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,21 +9,20 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.rest.exception;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import org.eclipse.hawkbit.exception.AbstractServerRtException;
|
||||
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 {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* @param cause for the exception
|
||||
*/
|
||||
public MultiPartFileUploadException(final Throwable cause) {
|
||||
super(cause.getMessage(), SpServerError.SP_ARTIFACT_UPLOAD_FAILED, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
* 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.Iterator;
|
||||
@@ -9,6 +9,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.rest.util;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import org.eclipse.hawkbit.exception.AbstractServerRtException;
|
||||
import org.eclipse.hawkbit.exception.SpServerError;
|
||||
|
||||
@@ -17,25 +19,9 @@ import org.eclipse.hawkbit.exception.SpServerError;
|
||||
*/
|
||||
public final class FileStreamingFailedException extends AbstractServerRtException {
|
||||
|
||||
@Serial
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,15 +11,15 @@ package org.eclipse.hawkbit.rest.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Utility class for the Rest Source API.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class HttpUtil {
|
||||
|
||||
private HttpUtil() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks given CSV string for defined match value or wildcard.
|
||||
*
|
||||
@@ -32,5 +32,4 @@ public final class HttpUtil {
|
||||
Arrays.sort(matchValues);
|
||||
return Arrays.binarySearch(matchValues, toMatch) > -1 || Arrays.binarySearch(matchValues, "*") > -1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ package org.eclipse.hawkbit.rest;
|
||||
import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration;
|
||||
import org.eclipse.hawkbit.repository.test.TestConfiguration;
|
||||
import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
|
||||
import org.eclipse.hawkbit.rest.filter.ExcludePathAwareShallowETagFilter;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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
|
||||
createMvcWebAppContext.addFilter(characterEncodingFilter);
|
||||
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/**",
|
||||
"/api/v1/downloadserver/**"));
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
* 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.mockito.Mockito.mockingDetails;
|
||||
@@ -55,7 +55,7 @@ public class ExcludePathAwareShallowETagFilterTest {
|
||||
when(servletRequestMock.getContextPath()).thenReturn(knownContextPath);
|
||||
when(servletRequestMock.getRequestURI()).thenReturn(knownUri);
|
||||
|
||||
final ExcludePathAwareShallowETagFilter filterUnderTest = new ExcludePathAwareShallowETagFilter(
|
||||
final RestConfiguration.ExcludePathAwareShallowETagFilter filterUnderTest = new RestConfiguration.ExcludePathAwareShallowETagFilter(
|
||||
antPathExclusion);
|
||||
|
||||
filterUnderTest.doFilterInternal(servletRequestMock, servletResponseMock, filterChainMock);
|
||||
@@ -77,7 +77,7 @@ public class ExcludePathAwareShallowETagFilterTest {
|
||||
when(servletRequestMock.getContextPath()).thenReturn(knownContextPath);
|
||||
when(servletRequestMock.getRequestURI()).thenReturn(knownUri);
|
||||
|
||||
final ExcludePathAwareShallowETagFilter filterUnderTest = new ExcludePathAwareShallowETagFilter(
|
||||
final RestConfiguration.ExcludePathAwareShallowETagFilter filterUnderTest = new RestConfiguration.ExcludePathAwareShallowETagFilter(
|
||||
antPathExclusion);
|
||||
|
||||
final ArgumentCaptor<HttpServletResponse> responseArgumentCaptor = ArgumentCaptor
|
||||
Reference in New Issue
Block a user