diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/AbstractServerRtException.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/AbstractServerRtException.java index cb695d2f7..429df6ed5 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/AbstractServerRtException.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/AbstractServerRtException.java @@ -9,11 +9,7 @@ package org.eclipse.hawkbit.exception; /** - * - * - * * Generic Custom Exception to wrap the Runtime and checked exception - * */ public abstract class AbstractServerRtException extends RuntimeException { diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java index 5acd11463..bda7a5d77 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java @@ -9,11 +9,7 @@ package org.eclipse.hawkbit.exception; /** - * - * - * * Define the Error code for Error handling - * */ public enum SpServerError { @@ -25,7 +21,10 @@ public enum SpServerError { * */ SP_REPO_ENTITY_ALRREADY_EXISTS("hawkbit.server.error.repo.entitiyAlreayExists", "The given entity already exists in database"), - + /** + * + */ + SP_REPO_CONSTRAINT_VIOLATION("hawkbit.server.error.repo.constraintViolation", "The given entity cannot be saved due to Constraint Violation"), /** * */ diff --git a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResourceTest.java b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResourceTest.java index 36d975138..137628e35 100644 --- a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResourceTest.java +++ b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResourceTest.java @@ -133,6 +133,26 @@ public class MgmtSoftwareModuleTypeResourceTest extends AbstractRestIntegrationT .andExpect(jsonPath("$content.[2].key", equalTo("test123"))).andExpect(jsonPath("$total", equalTo(4))); } + @Test + @WithUser(principal = "uploadTester", allSpPermissions = true) + @Description("Checks the correct behaviour of /rest/v1/softwaremoduletypes POST requests when max assignment is smaller than 1") + public void createSoftwareModuleTypesInvalidAssignmentBadRequest() throws JSONException, Exception { + + final List types = new ArrayList<>(); + types.add(entityFactory.generateSoftwareModuleType("test-1", "TestName-1", "Desc-1", -1)); + + mvc.perform(post("/rest/v1/softwaremoduletypes/").content(JsonBuilder.softwareModuleTypes(types)) + .contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest()); + + types.clear(); + types.add(entityFactory.generateSoftwareModuleType("test0", "TestName0", "Desc0", 0)); + + mvc.perform(post("/rest/v1/softwaremoduletypes/").content(JsonBuilder.softwareModuleTypes(types)) + .contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest()); + } + @Test @WithUser(principal = "uploadTester", allSpPermissions = true) @Description("Checks the correct behaviour of /rest/v1/softwaremoduletypes POST requests.") diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/ConstraintViolationException.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/ConstraintViolationException.java new file mode 100644 index 000000000..f7a7660cf --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/ConstraintViolationException.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.repository.exception; + +import java.util.stream.Collectors; + +import org.eclipse.hawkbit.exception.AbstractServerRtException; +import org.eclipse.hawkbit.exception.SpServerError; + +/** + * the {@link ConstraintViolationException} is thrown when an entity is tried to + * be saved which has constraint violations + */ +public class ConstraintViolationException extends AbstractServerRtException { + + private static final long serialVersionUID = 1L; + + private static final String MESSAGE_FORMATTER_SEPARATOR = " "; + + /** + * Constructor for {@link ConstraintViolationException} + * + * @param ex + * the javax.validation.ConstraintViolationException which is + * thrown + */ + public ConstraintViolationException(final javax.validation.ConstraintViolationException ex) { + super(getExceptionMessage(ex), SpServerError.SP_REPO_CONSTRAINT_VIOLATION); + } + + /** + * Uses the information of + * {@link javax.validation.ConstraintViolationException} to provide a proper + * error message for {@link ConstraintViolationException} + * + * @param ex + * javax.validation.ConstraintViolationException which is thrown + * @return message String with proper error information + */ + public static String getExceptionMessage(final javax.validation.ConstraintViolationException ex) { + return ex + .getConstraintViolations().stream().map(violation -> violation.getPropertyPath() + + MESSAGE_FORMATTER_SEPARATOR + violation.getMessage() + ".") + .collect(Collectors.joining(MESSAGE_FORMATTER_SEPARATOR)); + } + +} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/EntityAlreadyExistsException.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/EntityAlreadyExistsException.java index 73b14c57a..cf2c36cee 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/EntityAlreadyExistsException.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/EntityAlreadyExistsException.java @@ -8,15 +8,12 @@ */ package org.eclipse.hawkbit.repository.exception; -import org.eclipse.hawkbit.exception.SpServerError; import org.eclipse.hawkbit.exception.AbstractServerRtException; +import org.eclipse.hawkbit.exception.SpServerError; /** - * the {@link EntityAlreadyExistsException} is thrown when a entity is tried to + * the {@link EntityAlreadyExistsException} is thrown when an entity is tried to * be saved which already exists or which violates unique key constraints. - * - * - * */ public class EntityAlreadyExistsException extends AbstractServerRtException { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModuleType.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModuleType.java index f2a716c40..f521922b6 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModuleType.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModuleType.java @@ -13,6 +13,7 @@ import javax.persistence.Entity; import javax.persistence.Index; import javax.persistence.Table; import javax.persistence.UniqueConstraint; +import javax.validation.constraints.Min; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; @@ -36,6 +37,7 @@ public class JpaSoftwareModuleType extends AbstractJpaNamedEntity implements Sof private String key; @Column(name = "max_ds_assignments", nullable = false) + @Min(1) private int maxAssignments; @Column(name = "colour", nullable = true, length = 16) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_7_0__swmType_maxAssignment_greater_0__H2.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_7_0__swmType_maxAssignment_greater_0__H2.sql new file mode 100644 index 000000000..f79c9375c --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_7_0__swmType_maxAssignment_greater_0__H2.sql @@ -0,0 +1 @@ +Update sp_software_module_type set max_ds_assignments = 1 where max_ds_assignments < 1; \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_7_0__swmType_maxAssignment_greater_0__MYSQL.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_7_0__swmType_maxAssignment_greater_0__MYSQL.sql new file mode 100644 index 000000000..f79c9375c --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_7_0__swmType_maxAssignment_greater_0__MYSQL.sql @@ -0,0 +1 @@ +Update sp_software_module_type set max_ds_assignments = 1 where max_ds_assignments < 1; \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagementTest.java index 75633a85a..739cf7f64 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagementTest.java @@ -19,6 +19,8 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import javax.validation.ConstraintViolationException; + import org.apache.commons.lang3.RandomUtils; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; @@ -785,9 +787,20 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD } } + @Test + @Description("Verifies that the creation of a softwareModuleType is failing because of invalid max assignment") + public void createSoftwareModuleTypesFailsWithInvalidMaxAssignment() { + try { + softwareManagement.createSoftwareModuleType(new JpaSoftwareModuleType("type", "name", "desc", 0)); + fail("should not have worked as max assignment is invalid. Should be greater than 0."); + } catch (final ConstraintViolationException e) { + + } + } + @Test @Description("Verfies that multiple types are created as requested.") - public void createMultipleoftwareModuleTypes() { + public void createMultipleSoftwareModuleTypes() { final List created = softwareManagement.createSoftwareModuleType( Lists.newArrayList(new JpaSoftwareModuleType("thetype", "thename", "desc", 100), new JpaSoftwareModuleType("thetype2", "thename2", "desc2", 100))); @@ -797,7 +810,7 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD } @Test - @Description("Verfies that sofwtare modules are resturned that are assigned to given DS.") + @Description("Verfies that software modules are resturned that are assigned to given DS.") public void findSoftwareModuleByAssignedTo() { // test meta data final SoftwareModuleType testType = softwareManagement @@ -810,8 +823,6 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD softwareManagement.createSoftwareModule(new JpaSoftwareModule(testType, "asis", "found", null, "")); final SoftwareModule one = softwareManagement .createSoftwareModule(new JpaSoftwareModule(testType, "found", "b", null, "")); - final SoftwareModule two = softwareManagement - .createSoftwareModule(new JpaSoftwareModule(testType, "found", "c", null, "")); // one soft deleted final SoftwareModule deleted = softwareManagement diff --git a/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java b/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java index 81d70103b..41313d6ff 100644 --- a/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java +++ b/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java @@ -13,10 +13,11 @@ import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; +import javax.validation.ConstraintViolationException; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.eclipse.hawkbit.exception.SpServerError; import org.eclipse.hawkbit.exception.AbstractServerRtException; +import org.eclipse.hawkbit.exception.SpServerError; import org.eclipse.hawkbit.repository.exception.MultiPartFileUploadException; import org.eclipse.hawkbit.rest.json.model.ExceptionInfo; import org.slf4j.Logger; @@ -75,8 +76,8 @@ public class ResponseExceptionHandler { } /** - * method for handling exception of type AbstractServerRtException. Called by the - * Spring-Framework for exception handling. + * method for handling exception of type AbstractServerRtException. Called + * by the Spring-Framework for exception handling. * * @param request * the Http request @@ -120,6 +121,30 @@ public class ResponseExceptionHandler { 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 handleConstraintViolationException(final HttpServletRequest request, + final Exception ex) { + logRequest(request, ex); + + final ExceptionInfo response = createExceptionInfo( + new org.eclipse.hawkbit.repository.exception.ConstraintViolationException( + (ConstraintViolationException) ex)); + + 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