Fix exception handling on repository (#546)
* Fix constraint violation handling (400 instead of 500). Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Dont map constraintvioalation Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Added test in target repo. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Extended dialect handler. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Fix broken constraint handling. Added target tests and docs. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Further restricted aspect. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Add macro test. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Reduce duplicate code. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * No need to open a new transaction here. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Remove comment. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Remove flush from assign DS. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Remove commented line Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Fix exception handling for non-SQL cause. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Remove deprecated comment. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Documentation Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * More tests and documentation. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Private final. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Fix loop skip. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Fix test description. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Completed test coverage. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
@@ -208,6 +208,11 @@ public final class MgmtRestConstants {
|
|||||||
*/
|
*/
|
||||||
public static final String SOFTWAREMODULE_V1_ARTIFACT = "artifacts";
|
public static final String SOFTWAREMODULE_V1_ARTIFACT = "artifacts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The target URL mapping, href link for software module access.
|
||||||
|
*/
|
||||||
|
public static final String DISTRIBUTIONSET_V1_MODULE = "modules";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The target URL mapping, href link for type information.
|
* The target URL mapping, href link for type information.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -117,13 +117,17 @@ public final class MgmtDistributionSetMapper {
|
|||||||
response.add(linkTo(methodOn(MgmtDistributionSetRestApi.class).getDistributionSet(response.getDsId()))
|
response.add(linkTo(methodOn(MgmtDistributionSetRestApi.class).getDistributionSet(response.getDsId()))
|
||||||
.withSelfRel());
|
.withSelfRel());
|
||||||
|
|
||||||
|
response.add(linkTo(methodOn(MgmtDistributionSetRestApi.class).getAssignedSoftwareModules(response.getDsId(),
|
||||||
|
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE,
|
||||||
|
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, null))
|
||||||
|
.withRel(MgmtRestConstants.DISTRIBUTIONSET_V1_MODULE));
|
||||||
|
|
||||||
response.add(linkTo(methodOn(MgmtDistributionSetTypeRestApi.class)
|
response.add(linkTo(methodOn(MgmtDistributionSetTypeRestApi.class)
|
||||||
.getDistributionSetType(distributionSet.getType().getId())).withRel("type"));
|
.getDistributionSetType(distributionSet.getType().getId())).withRel("type"));
|
||||||
|
|
||||||
response.add(linkTo(methodOn(MgmtDistributionSetRestApi.class).getMetadata(response.getDsId(),
|
response.add(linkTo(methodOn(MgmtDistributionSetRestApi.class).getMetadata(response.getDsId(),
|
||||||
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE,
|
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE,
|
||||||
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, null, null))
|
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, null, null)).withRel("metadata"));
|
||||||
.withRel("metadata"));
|
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -352,6 +352,26 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
|
|||||||
assertThat(findTargetByControllerID.getName()).isEqualTo(knownNameNotModiy);
|
assertThat(findTargetByControllerID.getName()).isEqualTo(knownNameNotModiy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Ensures that target update request fails is updated value fails against a constraint.")
|
||||||
|
public void updateTargetDescriptionFailsIfInvalidLength() throws Exception {
|
||||||
|
final String knownControllerId = "123";
|
||||||
|
final String knownNewDescription = RandomStringUtils.randomAlphabetic(513);
|
||||||
|
final String knownNameNotModiy = "nameNotModiy";
|
||||||
|
final String body = new JSONObject().put("description", knownNewDescription).toString();
|
||||||
|
|
||||||
|
// prepare
|
||||||
|
targetManagement.createTarget(entityFactory.target().create().controllerId(knownControllerId)
|
||||||
|
.name(knownNameNotModiy).description("old description"));
|
||||||
|
|
||||||
|
mvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownControllerId).content(body)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||||
|
.andExpect(status().isBadRequest());
|
||||||
|
|
||||||
|
final Target findTargetByControllerID = targetManagement.findTargetByControllerID(knownControllerId).get();
|
||||||
|
assertThat(findTargetByControllerID.getDescription()).isEqualTo("old description");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Ensures that target update request is reflected by repository.")
|
@Description("Ensures that target update request is reflected by repository.")
|
||||||
public void updateTargetSecurityToken() throws Exception {
|
public void updateTargetSecurityToken() throws Exception {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintViolationException;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||||
@@ -158,6 +159,12 @@ public interface TargetManagement {
|
|||||||
* @param create
|
* @param create
|
||||||
* to be created
|
* to be created
|
||||||
* @return the created {@link Target}
|
* @return the created {@link Target}
|
||||||
|
*
|
||||||
|
* @throws EntityAlreadyExistsException
|
||||||
|
* given target already exists.
|
||||||
|
* @throws ConstraintViolationException
|
||||||
|
* if fields are not filled as specified. Check
|
||||||
|
* {@link TargetCreate} for field constraints.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR
|
||||||
@@ -176,6 +183,9 @@ public interface TargetManagement {
|
|||||||
*
|
*
|
||||||
* @throws EntityAlreadyExistsException
|
* @throws EntityAlreadyExistsException
|
||||||
* of one of the given targets already exist.
|
* of one of the given targets already exist.
|
||||||
|
* @throws ConstraintViolationException
|
||||||
|
* if fields are not filled as specified. Check
|
||||||
|
* {@link TargetCreate} for field constraints.
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
|
||||||
List<Target> createTargets(@NotNull Collection<TargetCreate> creates);
|
List<Target> createTargets(@NotNull Collection<TargetCreate> creates);
|
||||||
@@ -612,6 +622,9 @@ public interface TargetManagement {
|
|||||||
*
|
*
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
* if given target does not exist
|
* if given target does not exist
|
||||||
|
* @throws ConstraintViolationException
|
||||||
|
* if fields are not filled as specified. Check
|
||||||
|
* {@link TargetUpdate} for field constraints.
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR
|
||||||
+ SpringEvalExpressions.IS_CONTROLLER)
|
+ SpringEvalExpressions.IS_CONTROLLER)
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ package org.eclipse.hawkbit.repository.builder;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||||
import org.eclipse.hawkbit.repository.model.BaseEntity;
|
import org.eclipse.hawkbit.repository.model.BaseEntity;
|
||||||
@@ -26,7 +28,7 @@ public interface ActionStatusCreate {
|
|||||||
* {@link ActionStatus#getStatus()}
|
* {@link ActionStatus#getStatus()}
|
||||||
* @return updated {@link ActionStatusCreate} object
|
* @return updated {@link ActionStatusCreate} object
|
||||||
*/
|
*/
|
||||||
ActionStatusCreate status(Status status);
|
ActionStatusCreate status(@NotNull Status status);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param occurredAt
|
* @param occurredAt
|
||||||
|
|||||||
@@ -8,10 +8,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.builder;
|
package org.eclipse.hawkbit.repository.builder;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.repository.model.BaseEntity;
|
import org.eclipse.hawkbit.repository.model.BaseEntity;
|
||||||
|
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||||
import org.hibernate.validator.constraints.NotEmpty;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder to create a new {@link Target} entry. Defines all fields that can be
|
* Builder to create a new {@link Target} entry. Defines all fields that can be
|
||||||
@@ -26,28 +29,30 @@ public interface TargetCreate {
|
|||||||
* for {@link Target#getControllerId()}
|
* for {@link Target#getControllerId()}
|
||||||
* @return updated builder instance
|
* @return updated builder instance
|
||||||
*/
|
*/
|
||||||
TargetCreate controllerId(@NotEmpty String controllerId);
|
TargetCreate controllerId(@Size(min = 1, max = Target.CONTROLLER_ID_MAX_SIZE) @NotNull String controllerId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param name
|
* @param name
|
||||||
* for {@link Target#getName()}
|
* for {@link Target#getName()} filled with
|
||||||
|
* {@link #controllerId(String)} as default if not set explicitly
|
||||||
* @return updated builder instance
|
* @return updated builder instance
|
||||||
*/
|
*/
|
||||||
TargetCreate name(@NotEmpty String name);
|
TargetCreate name(@Size(min = 1, max = NamedEntity.NAME_MAX_SIZE) @NotNull String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param description
|
* @param description
|
||||||
* for {@link Target#getDescription()}
|
* for {@link Target#getDescription()}
|
||||||
* @return updated builder instance
|
* @return updated builder instance
|
||||||
*/
|
*/
|
||||||
TargetCreate description(String description);
|
TargetCreate description(@Size(max = NamedEntity.DESCRIPTION_MAX_SIZE) String description);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param securityToken
|
* @param securityToken
|
||||||
* for {@link Target#getSecurityToken()}
|
* for {@link Target#getSecurityToken()} is generated with a
|
||||||
|
* random sequence as default if not set explicitly
|
||||||
* @return updated builder instance
|
* @return updated builder instance
|
||||||
*/
|
*/
|
||||||
TargetCreate securityToken(String securityToken);
|
TargetCreate securityToken(@Size(min = 1, max = Target.SECURITY_TOKEN_MAX_SIZE) @NotNull String securityToken);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param address
|
* @param address
|
||||||
@@ -58,7 +63,7 @@ public interface TargetCreate {
|
|||||||
*
|
*
|
||||||
* @return updated builder instance
|
* @return updated builder instance
|
||||||
*/
|
*/
|
||||||
TargetCreate address(String address);
|
TargetCreate address(@Size(max = Target.ADDRESS_MAX_SIZE) String address);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param lastTargetQuery
|
* @param lastTargetQuery
|
||||||
@@ -69,10 +74,12 @@ public interface TargetCreate {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param status
|
* @param status
|
||||||
* for {@link Target#getUpdateStatus()}
|
* for {@link Target#getUpdateStatus()} is
|
||||||
|
* {@link TargetUpdateStatus#UNKNOWN} as default if not set
|
||||||
|
* explicitly
|
||||||
* @return updated builder instance
|
* @return updated builder instance
|
||||||
*/
|
*/
|
||||||
TargetCreate status(TargetUpdateStatus status);
|
TargetCreate status(@NotNull TargetUpdateStatus status);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return peek on current state of {@link Target} in the builder
|
* @return peek on current state of {@link Target} in the builder
|
||||||
|
|||||||
@@ -8,9 +8,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.builder;
|
package org.eclipse.hawkbit.repository.builder;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||||
import org.hibernate.validator.constraints.NotEmpty;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder to update an existing {@link Target} entry. Defines all fields that
|
* Builder to update an existing {@link Target} entry. Defines all fields that
|
||||||
@@ -24,21 +27,21 @@ public interface TargetUpdate {
|
|||||||
* for {@link Target#getName()}
|
* for {@link Target#getName()}
|
||||||
* @return updated builder instance
|
* @return updated builder instance
|
||||||
*/
|
*/
|
||||||
TargetUpdate name(@NotEmpty String name);
|
TargetUpdate name(@Size(min = 1, max = NamedEntity.NAME_MAX_SIZE) @NotNull String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param description
|
* @param description
|
||||||
* for {@link Target#getDescription()}
|
* for {@link Target#getDescription()}
|
||||||
* @return updated builder instance
|
* @return updated builder instance
|
||||||
*/
|
*/
|
||||||
TargetUpdate description(String description);
|
TargetUpdate description(@Size(max = NamedEntity.DESCRIPTION_MAX_SIZE) String description);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param securityToken
|
* @param securityToken
|
||||||
* for {@link Target#getSecurityToken()}
|
* for {@link Target#getSecurityToken()}
|
||||||
* @return updated builder instance
|
* @return updated builder instance
|
||||||
*/
|
*/
|
||||||
TargetUpdate securityToken(@NotEmpty String securityToken);
|
TargetUpdate securityToken(@Size(min = 1, max = Target.SECURITY_TOKEN_MAX_SIZE) @NotNull String securityToken);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param address
|
* @param address
|
||||||
@@ -49,7 +52,7 @@ public interface TargetUpdate {
|
|||||||
*
|
*
|
||||||
* @return updated builder instance
|
* @return updated builder instance
|
||||||
*/
|
*/
|
||||||
TargetUpdate address(String address);
|
TargetUpdate address(@Size(max = Target.ADDRESS_MAX_SIZE) String address);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param lastTargetQuery
|
* @param lastTargetQuery
|
||||||
@@ -63,5 +66,5 @@ public interface TargetUpdate {
|
|||||||
* for {@link Target#getUpdateStatus()}
|
* for {@link Target#getUpdateStatus()}
|
||||||
* @return updated builder instance
|
* @return updated builder instance
|
||||||
*/
|
*/
|
||||||
TargetUpdate status(TargetUpdateStatus status);
|
TargetUpdate status(@NotNull TargetUpdateStatus status);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.model;
|
|||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
@@ -23,7 +24,7 @@ import org.springframework.util.CollectionUtils;
|
|||||||
public class DistributionSetAssignmentResult extends AssignmentResult<Target> {
|
public class DistributionSetAssignmentResult extends AssignmentResult<Target> {
|
||||||
|
|
||||||
private final List<String> assignedTargets;
|
private final List<String> assignedTargets;
|
||||||
private final List<Long> actions;
|
private final List<Action> actions;
|
||||||
|
|
||||||
private final TargetManagement targetManagement;
|
private final TargetManagement targetManagement;
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ public class DistributionSetAssignmentResult extends AssignmentResult<Target> {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public DistributionSetAssignmentResult(final List<String> assignedTargets, final int assigned,
|
public DistributionSetAssignmentResult(final List<String> assignedTargets, final int assigned,
|
||||||
final int alreadyAssigned, final List<Long> actions, final TargetManagement targetManagement) {
|
final int alreadyAssigned, final List<Action> actions, final TargetManagement targetManagement) {
|
||||||
super(assigned, alreadyAssigned, 0, Collections.emptyList(), Collections.emptyList());
|
super(assigned, alreadyAssigned, 0, Collections.emptyList(), Collections.emptyList());
|
||||||
this.assignedTargets = assignedTargets;
|
this.assignedTargets = assignedTargets;
|
||||||
this.actions = actions;
|
this.actions = actions;
|
||||||
@@ -60,7 +61,7 @@ public class DistributionSetAssignmentResult extends AssignmentResult<Target> {
|
|||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Collections.unmodifiableList(actions);
|
return actions.stream().map(Action::getId).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -8,19 +8,34 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.model;
|
package org.eclipse.hawkbit.repository.model;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entities that have a name and description.
|
* Entities that have a name and description.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public interface NamedEntity extends TenantAwareBaseEntity {
|
public interface NamedEntity extends TenantAwareBaseEntity {
|
||||||
|
/**
|
||||||
|
* Maximum length of name.
|
||||||
|
*/
|
||||||
|
public static final int NAME_MAX_SIZE = 64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum length of description.
|
||||||
|
*/
|
||||||
|
public static final int DESCRIPTION_MAX_SIZE = 512;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the description of the entity.
|
* @return the description of the entity.
|
||||||
*/
|
*/
|
||||||
|
@Size(max = DESCRIPTION_MAX_SIZE)
|
||||||
String getDescription();
|
String getDescription();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the name of the entity.
|
* @return the name of the entity.
|
||||||
*/
|
*/
|
||||||
|
@Size(min = 1, max = NAME_MAX_SIZE)
|
||||||
|
@NotNull
|
||||||
String getName();
|
String getName();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,20 @@ import java.util.concurrent.TimeUnit;
|
|||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public interface Target extends NamedEntity {
|
public interface Target extends NamedEntity {
|
||||||
|
/**
|
||||||
|
* Maximum length of controllerId.
|
||||||
|
*/
|
||||||
|
public static final int CONTROLLER_ID_MAX_SIZE = 64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum length of securityToken.
|
||||||
|
*/
|
||||||
|
public static final int SECURITY_TOKEN_MAX_SIZE = 128;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum length of address.
|
||||||
|
*/
|
||||||
|
public static final int ADDRESS_MAX_SIZE = 512;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return business identifier of the {@link Target}
|
* @return business identifier of the {@link Target}
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* 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.jpa;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import javax.persistence.PersistenceException;
|
||||||
|
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
|
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
|
||||||
|
import org.springframework.orm.jpa.JpaSystemException;
|
||||||
|
import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link EclipseLinkJpaDialect} with additional exception translation
|
||||||
|
* mechanisms based on {@link SQLStateSQLExceptionTranslator}.
|
||||||
|
*
|
||||||
|
* There are multiple variations of exceptions coming out of persistence
|
||||||
|
* provider:
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 1) {@link PersistenceException}s that can be mapped by
|
||||||
|
* {@link EclipseLinkJpaDialect} into corresponding {@link DataAccessException}.
|
||||||
|
* <p>
|
||||||
|
* 2) {@link PersistenceException}s that could not be mapped by
|
||||||
|
* {@link EclipseLinkJpaDialect} directly but instead are wrapped into
|
||||||
|
* {@link JpaSystemException}.
|
||||||
|
* <p>
|
||||||
|
* 2.a) here the wrapped exception's causes might be an {@link SQLException}
|
||||||
|
* which might be mappable by {@link SQLStateSQLExceptionTranslator} or
|
||||||
|
* <p>
|
||||||
|
* 2.b.) the wrapped exception's causes due not contain an {@link SQLException}
|
||||||
|
* and as a result cannot be mapped.
|
||||||
|
* <p>
|
||||||
|
* 3) A {@link RuntimeException} that is no {@link PersistenceException}.
|
||||||
|
* <p>
|
||||||
|
* 3.a) here a cause might be an {@link SQLException} which might be mappable by
|
||||||
|
* {@link SQLStateSQLExceptionTranslator} or
|
||||||
|
* <p>
|
||||||
|
* 3.b.) the the cause is not an {@link SQLException} and as a result cannot be
|
||||||
|
* mapped.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class HawkBitEclipseLinkJpaDialect extends EclipseLinkJpaDialect {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private static final SQLStateSQLExceptionTranslator SQLSTATE_EXCEPTION_TRANSLATOR = new SQLStateSQLExceptionTranslator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataAccessException translateExceptionIfPossible(final RuntimeException ex) {
|
||||||
|
final DataAccessException dataAccessException = super.translateExceptionIfPossible(ex);
|
||||||
|
|
||||||
|
if (dataAccessException == null) {
|
||||||
|
return searchAndTranslateSqlException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return translateJpaSystemExceptionIfPossible(dataAccessException);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DataAccessException translateJpaSystemExceptionIfPossible(
|
||||||
|
final DataAccessException accessException) {
|
||||||
|
if (!(accessException instanceof JpaSystemException)) {
|
||||||
|
return accessException;
|
||||||
|
}
|
||||||
|
|
||||||
|
final DataAccessException sql = searchAndTranslateSqlException(accessException);
|
||||||
|
if (sql == null) {
|
||||||
|
return accessException;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DataAccessException searchAndTranslateSqlException(final RuntimeException ex) {
|
||||||
|
final SQLException sqlException = findSqlException(ex);
|
||||||
|
|
||||||
|
if (sqlException == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SQLSTATE_EXCEPTION_TRANSLATOR.translate(null, null, sqlException);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SQLException findSqlException(final RuntimeException jpaSystemException) {
|
||||||
|
Throwable exception = jpaSystemException;
|
||||||
|
do {
|
||||||
|
final Throwable cause = exception.getCause();
|
||||||
|
if (cause instanceof SQLException) {
|
||||||
|
return (SQLException) cause;
|
||||||
|
}
|
||||||
|
exception = cause;
|
||||||
|
} while (exception != null);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
package org.eclipse.hawkbit.repository.jpa;
|
package org.eclipse.hawkbit.repository.jpa;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -24,7 +24,6 @@ import org.eclipse.hawkbit.repository.EntityFactory;
|
|||||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
|
||||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
|
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
|
import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
|
||||||
@@ -91,9 +90,6 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private SoftwareModuleRepository softwareModuleRepository;
|
private SoftwareModuleRepository softwareModuleRepository;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private TargetManagement targetManagement;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ActionStatusRepository actionStatusRepository;
|
private ActionStatusRepository actionStatusRepository;
|
||||||
|
|
||||||
@@ -214,10 +210,10 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
final JpaTarget target = targetRepository.findOne(spec);
|
final JpaTarget target = targetRepository.findOne(spec);
|
||||||
|
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
final Target result = targetManagement.createTarget(entityFactory.target().create()
|
final Target result = targetRepository.save((JpaTarget) entityFactory.target().create()
|
||||||
.controllerId(controllerId).description("Plug and Play target: " + controllerId).name(controllerId)
|
.controllerId(controllerId).description("Plug and Play target: " + controllerId).name(controllerId)
|
||||||
.status(TargetUpdateStatus.REGISTERED).lastTargetQuery(System.currentTimeMillis())
|
.status(TargetUpdateStatus.REGISTERED).lastTargetQuery(System.currentTimeMillis())
|
||||||
.address(Optional.ofNullable(address).map(URI::toString).orElse(null)));
|
.address(Optional.ofNullable(address).map(URI::toString).orElse(null)).build());
|
||||||
|
|
||||||
afterCommit.afterCommit(
|
afterCommit.afterCommit(
|
||||||
() -> eventPublisher.publishEvent(new TargetPollEvent(result, applicationContext.getId())));
|
() -> eventPublisher.publishEvent(new TargetPollEvent(result, applicationContext.getId())));
|
||||||
@@ -535,7 +531,7 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
public List<String> getActionHistoryMessages(final Long actionId, final int messageCount) {
|
public List<String> getActionHistoryMessages(final Long actionId, final int messageCount) {
|
||||||
// Just return empty list in case messageCount is zero.
|
// Just return empty list in case messageCount is zero.
|
||||||
if (messageCount == 0) {
|
if (messageCount == 0) {
|
||||||
return new ArrayList<>();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// For negative and large value of messageCount, limit the number of
|
// For negative and large value of messageCount, limit the number of
|
||||||
|
|||||||
@@ -44,8 +44,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
|||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus_;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus_;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||||
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
||||||
@@ -138,16 +136,15 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
private PlatformTransactionManager txManager;
|
private PlatformTransactionManager txManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||||
@Retryable(include = {
|
@Retryable(include = {
|
||||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||||
// Exception squid:S2095: see
|
|
||||||
// https://jira.sonarsource.com/browse/SONARJAVA-1478
|
|
||||||
@SuppressWarnings({ "squid:S2095" })
|
|
||||||
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID, final ActionType actionType,
|
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID, final ActionType actionType,
|
||||||
final long forcedTimestamp, final Collection<String> targetIDs) {
|
final long forcedTimestamp, final Collection<String> targetIDs) {
|
||||||
return assignDistributionSet(dsID, targetIDs.stream()
|
|
||||||
.map(t -> new TargetWithActionType(t, actionType, forcedTimestamp)).collect(Collectors.toList()));
|
return assignDistributionSetToTargets(dsID, targetIDs.stream()
|
||||||
|
.map(t -> new TargetWithActionType(t, actionType, forcedTimestamp)).collect(Collectors.toList()), null);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -156,7 +153,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||||
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID,
|
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID,
|
||||||
final Collection<TargetWithActionType> targets) {
|
final Collection<TargetWithActionType> targets) {
|
||||||
return assignDistributionSet(dsID, targets, null);
|
|
||||||
|
return assignDistributionSetToTargets(dsID, targets, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -165,26 +163,18 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||||
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID,
|
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID,
|
||||||
final Collection<TargetWithActionType> targets, final String actionMessage) {
|
final Collection<TargetWithActionType> targets, final String actionMessage) {
|
||||||
final JpaDistributionSet set = distributionSetRepository.findOne(dsID);
|
|
||||||
if (set == null) {
|
|
||||||
throw new EntityNotFoundException(DistributionSet.class, dsID);
|
|
||||||
}
|
|
||||||
|
|
||||||
return assignDistributionSetToTargets(set, targets, null, null, actionMessage);
|
return assignDistributionSetToTargets(dsID, targets, actionMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* method assigns the {@link DistributionSet} to all {@link Target}s by
|
* method assigns the {@link DistributionSet} to all {@link Target}s by
|
||||||
* their IDs with a specific {@link ActionType} and {@code forcetime}.
|
* their IDs with a specific {@link ActionType} and {@code forcetime}.
|
||||||
*
|
*
|
||||||
* @param set
|
* @param dsID
|
||||||
* the ID of the distribution set to assign
|
* the ID of the distribution set to assign
|
||||||
* @param targetsWithActionType
|
* @param targetsWithActionType
|
||||||
* a list of all targets and their action type
|
* a list of all targets and their action type
|
||||||
* @param rollout
|
|
||||||
* the rollout for this assignment
|
|
||||||
* @param rolloutGroup
|
|
||||||
* the rollout group for this assignment
|
|
||||||
* @param actionMessage
|
* @param actionMessage
|
||||||
* an optional message to be written into the action status
|
* an optional message to be written into the action status
|
||||||
* @return the assignment result
|
* @return the assignment result
|
||||||
@@ -193,9 +183,13 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
* {@link SoftwareModuleType} are not assigned as define by the
|
* {@link SoftwareModuleType} are not assigned as define by the
|
||||||
* {@link DistributionSetType}.
|
* {@link DistributionSetType}.
|
||||||
*/
|
*/
|
||||||
private DistributionSetAssignmentResult assignDistributionSetToTargets(@NotNull final JpaDistributionSet set,
|
private DistributionSetAssignmentResult assignDistributionSetToTargets(final Long dsID,
|
||||||
final Collection<TargetWithActionType> targetsWithActionType, final JpaRollout rollout,
|
final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage) {
|
||||||
final JpaRolloutGroup rolloutGroup, final String actionMessage) {
|
|
||||||
|
final JpaDistributionSet set = distributionSetRepository.findOne(dsID);
|
||||||
|
if (set == null) {
|
||||||
|
throw new EntityNotFoundException(DistributionSet.class, dsID);
|
||||||
|
}
|
||||||
|
|
||||||
if (!set.isComplete()) {
|
if (!set.isComplete()) {
|
||||||
throw new IncompleteDistributionSetException(
|
throw new IncompleteDistributionSetException(
|
||||||
@@ -222,7 +216,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
|
|
||||||
if (targets.isEmpty()) {
|
if (targets.isEmpty()) {
|
||||||
// detaching as it is not necessary to persist the set itself
|
// detaching as it is not necessary to persist the set itself
|
||||||
entityManager.clear();
|
entityManager.detach(set);
|
||||||
// return with nothing as all targets had the DS already assigned
|
// return with nothing as all targets had the DS already assigned
|
||||||
return new DistributionSetAssignmentResult(Collections.emptyList(), 0, targetsWithActionType.size(),
|
return new DistributionSetAssignmentResult(Collections.emptyList(), 0, targetsWithActionType.size(),
|
||||||
Collections.emptyList(), targetManagement);
|
Collections.emptyList(), targetManagement);
|
||||||
@@ -253,8 +247,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
|
|
||||||
targetIds.forEach(tIds -> targetRepository.setAssignedDistributionSetAndUpdateStatus(TargetUpdateStatus.PENDING,
|
targetIds.forEach(tIds -> targetRepository.setAssignedDistributionSetAndUpdateStatus(TargetUpdateStatus.PENDING,
|
||||||
set, System.currentTimeMillis(), currentUser, tIds));
|
set, System.currentTimeMillis(), currentUser, tIds));
|
||||||
final Map<String, JpaAction> targetIdsToActions = targets.stream().map(
|
final Map<String, JpaAction> targetIdsToActions = targets.stream()
|
||||||
t -> actionRepository.save(createTargetAction(targetsWithActionMap, t, set, rollout, rolloutGroup)))
|
.map(t -> actionRepository.save(createTargetAction(targetsWithActionMap, t, set)))
|
||||||
.collect(Collectors.toMap(a -> a.getTarget().getControllerId(), Function.identity()));
|
.collect(Collectors.toMap(a -> a.getTarget().getControllerId(), Function.identity()));
|
||||||
|
|
||||||
// create initial action status when action is created so we remember
|
// create initial action status when action is created so we remember
|
||||||
@@ -263,22 +257,17 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
// action history.
|
// action history.
|
||||||
targetIdsToActions.values().forEach(action -> setRunningActionStatus(action, actionMessage));
|
targetIdsToActions.values().forEach(action -> setRunningActionStatus(action, actionMessage));
|
||||||
|
|
||||||
// flush to get action IDs
|
// detaching as it is not necessary to persist the set itself
|
||||||
entityManager.flush();
|
entityManager.detach(set);
|
||||||
// detaching as everything that needs to be stored is already flushed
|
// detaching as the entity has been updated by the JPQL query above
|
||||||
entityManager.clear();
|
targets.forEach(entityManager::detach);
|
||||||
|
|
||||||
// collect updated target and actions IDs in order to return them
|
|
||||||
final DistributionSetAssignmentResult result = new DistributionSetAssignmentResult(
|
|
||||||
targets.stream().map(Target::getControllerId).collect(Collectors.toList()), targets.size(),
|
|
||||||
controllerIDs.size() - targets.size(),
|
|
||||||
targetIdsToActions.values().stream().map(Action::getId).collect(Collectors.toList()), targetManagement);
|
|
||||||
|
|
||||||
LOG.debug("assignDistribution({}) finished {}", set, result);
|
|
||||||
|
|
||||||
sendAssignmentEvents(targets, targetIdsCancellList, targetIdsToActions);
|
sendAssignmentEvents(targets, targetIdsCancellList, targetIdsToActions);
|
||||||
|
|
||||||
return result;
|
return new DistributionSetAssignmentResult(
|
||||||
|
targets.stream().map(Target::getControllerId).collect(Collectors.toList()), targets.size(),
|
||||||
|
controllerIDs.size() - targets.size(), Lists.newArrayList(targetIdsToActions.values()),
|
||||||
|
targetManagement);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendAssignmentEvents(final List<JpaTarget> targets, final Set<Long> targetIdsCancellList,
|
private void sendAssignmentEvents(final List<JpaTarget> targets, final Set<Long> targetIdsCancellList,
|
||||||
@@ -295,8 +284,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static JpaAction createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap,
|
private static JpaAction createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap,
|
||||||
final JpaTarget target, final JpaDistributionSet set, final JpaRollout rollout,
|
final JpaTarget target, final JpaDistributionSet set) {
|
||||||
final JpaRolloutGroup rolloutGroup) {
|
|
||||||
final JpaAction actionForTarget = new JpaAction();
|
final JpaAction actionForTarget = new JpaAction();
|
||||||
final TargetWithActionType targetWithActionType = targetsWithActionMap.get(target.getControllerId());
|
final TargetWithActionType targetWithActionType = targetsWithActionMap.get(target.getControllerId());
|
||||||
actionForTarget.setActionType(targetWithActionType.getActionType());
|
actionForTarget.setActionType(targetWithActionType.getActionType());
|
||||||
@@ -305,8 +293,6 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
actionForTarget.setStatus(Status.RUNNING);
|
actionForTarget.setStatus(Status.RUNNING);
|
||||||
actionForTarget.setTarget(target);
|
actionForTarget.setTarget(target);
|
||||||
actionForTarget.setDistributionSet(set);
|
actionForTarget.setDistributionSet(set);
|
||||||
actionForTarget.setRollout(rollout);
|
|
||||||
actionForTarget.setRolloutGroup(rolloutGroup);
|
|
||||||
return actionForTarget;
|
return actionForTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
|||||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
import org.springframework.integration.support.locks.LockRegistry;
|
import org.springframework.integration.support.locks.LockRegistry;
|
||||||
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
|
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
|
||||||
|
import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect;
|
||||||
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
|
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
|
||||||
import org.springframework.retry.annotation.EnableRetry;
|
import org.springframework.retry.annotation.EnableRetry;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
@@ -288,7 +289,14 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
|
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
|
||||||
return new EclipseLinkJpaVendorAdapter();
|
return new EclipseLinkJpaVendorAdapter() {
|
||||||
|
private final HawkBitEclipseLinkJpaDialect jpaDialect = new HawkBitEclipseLinkJpaDialect();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EclipseLinkJpaDialect getJpaDialect() {
|
||||||
|
return jpaDialect;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>,
|
|||||||
*/
|
*/
|
||||||
@Modifying
|
@Modifying
|
||||||
@Transactional
|
@Transactional
|
||||||
@Query("UPDATE JpaTarget t SET t.assignedDistributionSet = :set, t.lastModifiedAt = :lastModifiedAt, t.lastModifiedBy = :lastModifiedBy, t.updateStatus = :status WHERE t.id IN :targets")
|
@Query("UPDATE JpaTarget t SET t.assignedDistributionSet = :set, t.lastModifiedAt = :lastModifiedAt, t.lastModifiedBy = :lastModifiedBy, t.updateStatus = :status WHERE t.id IN :targets")
|
||||||
void setAssignedDistributionSetAndUpdateStatus(@Param("status") TargetUpdateStatus status,
|
void setAssignedDistributionSetAndUpdateStatus(@Param("status") TargetUpdateStatus status,
|
||||||
@Param("set") JpaDistributionSet set, @Param("lastModifiedAt") Long modifiedAt,
|
@Param("set") JpaDistributionSet set, @Param("lastModifiedAt") Long modifiedAt,
|
||||||
@Param("lastModifiedBy") String modifiedBy, @Param("targets") Collection<Long> targets);
|
@Param("lastModifiedBy") String modifiedBy, @Param("targets") Collection<Long> targets);
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import org.springframework.data.jpa.repository.Modifying;
|
|||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.transaction.annotation.Propagation;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,7 +32,6 @@ public interface TenantMetaDataRepository extends PagingAndSortingRepository<Jpa
|
|||||||
* to search for
|
* to search for
|
||||||
* @return found {@link TenantMetaData} or <code>null</code>
|
* @return found {@link TenantMetaData} or <code>null</code>
|
||||||
*/
|
*/
|
||||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
|
||||||
TenantMetaData findByTenantIgnoreCase(String tenant);
|
TenantMetaData findByTenantIgnoreCase(String tenant);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.jpa.aspects;
|
package org.eclipse.hawkbit.repository.jpa.aspects;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -22,15 +21,10 @@ import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
|||||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.dao.DataAccessException;
|
|
||||||
import org.springframework.dao.DataIntegrityViolationException;
|
import org.springframework.dao.DataIntegrityViolationException;
|
||||||
import org.springframework.dao.DuplicateKeyException;
|
import org.springframework.dao.DuplicateKeyException;
|
||||||
import org.springframework.dao.OptimisticLockingFailureException;
|
import org.springframework.dao.OptimisticLockingFailureException;
|
||||||
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
|
|
||||||
import org.springframework.orm.jpa.JpaSystemException;
|
|
||||||
import org.springframework.orm.jpa.JpaVendorAdapter;
|
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.transaction.TransactionSystemException;
|
import org.springframework.transaction.TransactionSystemException;
|
||||||
|
|
||||||
@@ -55,10 +49,6 @@ public class ExceptionMappingAspectHandler implements Ordered {
|
|||||||
* exception.
|
* exception.
|
||||||
*/
|
*/
|
||||||
private static final List<Class<?>> MAPPED_EXCEPTION_ORDER = Lists.newArrayListWithExpectedSize(4);
|
private static final List<Class<?>> MAPPED_EXCEPTION_ORDER = Lists.newArrayListWithExpectedSize(4);
|
||||||
@Autowired
|
|
||||||
private JpaVendorAdapter jpaVendorAdapter;
|
|
||||||
|
|
||||||
private final SQLStateSQLExceptionTranslator sqlStateExceptionTranslator = new SQLStateSQLExceptionTranslator();
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|
||||||
@@ -84,88 +74,48 @@ public class ExceptionMappingAspectHandler implements Ordered {
|
|||||||
* the thrown and catched exception
|
* the thrown and catched exception
|
||||||
* @throws Throwable
|
* @throws Throwable
|
||||||
*/
|
*/
|
||||||
@AfterThrowing(pointcut = "( execution( * org.springframework.transaction..*.*(..)) "
|
@AfterThrowing(pointcut = "execution( * org.eclipse.hawkbit.repository.jpa.*Management.*(..))", throwing = "ex")
|
||||||
+ " || execution( * org.eclipse.hawkbit.repository.*.*(..)) )", throwing = "ex")
|
|
||||||
// Exception for squid:S00112, squid:S1162
|
// Exception for squid:S00112, squid:S1162
|
||||||
// It is a AspectJ proxy which deals with exceptions.
|
// It is a AspectJ proxy which deals with exceptions.
|
||||||
@SuppressWarnings({ "squid:S00112", "squid:S1162" })
|
@SuppressWarnings({ "squid:S00112", "squid:S1162" })
|
||||||
public void catchAndWrapJpaExceptionsService(final Exception ex) throws Throwable {
|
public void catchAndWrapJpaExceptionsService(final Exception ex) throws Throwable {
|
||||||
|
|
||||||
LOG.trace("exception occured", ex);
|
// Workarround for EclipseLink merge where it does not throw
|
||||||
Exception translatedAccessException = translateEclipseLinkExceptionIfPossible(ex);
|
// ConstraintViolationException directly in case of existing entity
|
||||||
|
// update
|
||||||
if (translatedAccessException == null && ex instanceof TransactionSystemException) {
|
if (ex instanceof TransactionSystemException) {
|
||||||
final TransactionSystemException systemException = (TransactionSystemException) ex;
|
throw replaceWithCauseIfConstraintViolationException((TransactionSystemException) ex);
|
||||||
translatedAccessException = translateEclipseLinkExceptionIfPossible(
|
|
||||||
(Exception) systemException.getOriginalException());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (translatedAccessException == null) {
|
|
||||||
translatedAccessException = ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
Exception mappingException = translatedAccessException;
|
|
||||||
|
|
||||||
LOG.trace("translated excpetion is", translatedAccessException);
|
|
||||||
for (final Class<?> mappedEx : MAPPED_EXCEPTION_ORDER) {
|
for (final Class<?> mappedEx : MAPPED_EXCEPTION_ORDER) {
|
||||||
|
|
||||||
if (mappedEx.isAssignableFrom(translatedAccessException.getClass())) {
|
if (!mappedEx.isAssignableFrom(ex.getClass())) {
|
||||||
if (!EXCEPTION_MAPPING.containsKey(mappedEx.getName())) {
|
continue;
|
||||||
LOG.error("there is no mapping configured for exception class {}", mappedEx.getName());
|
|
||||||
mappingException = new GenericSpServerException(ex);
|
|
||||||
} else {
|
|
||||||
mappingException = (Exception) Class.forName(EXCEPTION_MAPPING.get(mappedEx.getName()))
|
|
||||||
.getConstructor(Throwable.class).newInstance(ex);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (EXCEPTION_MAPPING.containsKey(mappedEx.getName())) {
|
||||||
|
throw (Exception) Class.forName(EXCEPTION_MAPPING.get(mappedEx.getName()))
|
||||||
|
.getConstructor(Throwable.class).newInstance(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.error("there is no mapping configured for exception class {}", mappedEx.getName());
|
||||||
|
throw new GenericSpServerException(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.trace("mapped exception {} to {}", translatedAccessException.getClass(), mappingException.getClass());
|
throw ex;
|
||||||
throw mappingException;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataAccessException translateEclipseLinkExceptionIfPossible(final Exception exception) {
|
private static Exception replaceWithCauseIfConstraintViolationException(final TransactionSystemException rex) {
|
||||||
final DataAccessException translatedAccessException = jpaVendorAdapter.getJpaDialect()
|
Throwable exception = rex;
|
||||||
.translateExceptionIfPossible((RuntimeException) exception);
|
do {
|
||||||
return translateSQLStateExceptionIfPossible(translatedAccessException);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* There is no EclipseLinkExceptionTranslator. So we have to check and
|
|
||||||
* translate the exception by the sql error code. Luckily, there we can use
|
|
||||||
* {@link SQLStateSQLExceptionTranslator} if we can get a
|
|
||||||
* {@link SQLException}.
|
|
||||||
*
|
|
||||||
* @param accessException
|
|
||||||
* the base access exception from jpa
|
|
||||||
* @return the translated accessException
|
|
||||||
*/
|
|
||||||
private DataAccessException translateSQLStateExceptionIfPossible(final DataAccessException accessException) {
|
|
||||||
if (!(accessException instanceof JpaSystemException)) {
|
|
||||||
return accessException;
|
|
||||||
}
|
|
||||||
|
|
||||||
final SQLException ex = findSqlException((JpaSystemException) accessException);
|
|
||||||
|
|
||||||
if (ex == null) {
|
|
||||||
return accessException;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sqlStateExceptionTranslator.translate(null, null, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SQLException findSqlException(final JpaSystemException jpaSystemException) {
|
|
||||||
Throwable exception = jpaSystemException.getCause();
|
|
||||||
while (exception != null) {
|
|
||||||
final Throwable cause = exception.getCause();
|
final Throwable cause = exception.getCause();
|
||||||
if (cause instanceof SQLException) {
|
if (cause instanceof javax.validation.ConstraintViolationException) {
|
||||||
return (SQLException) cause;
|
return (Exception) cause;
|
||||||
}
|
}
|
||||||
exception = cause;
|
exception = cause;
|
||||||
}
|
} while (exception != null);
|
||||||
return null;
|
|
||||||
|
return rex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ package org.eclipse.hawkbit.repository.jpa.model;
|
|||||||
|
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.MappedSuperclass;
|
import javax.persistence.MappedSuperclass;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
import javax.validation.constraints.Size;
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||||
import org.hibernate.validator.constraints.NotEmpty;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link TenantAwareBaseEntity} extension for all entities that are named in
|
* {@link TenantAwareBaseEntity} extension for all entities that are named in
|
||||||
@@ -27,13 +27,13 @@ import org.hibernate.validator.constraints.NotEmpty;
|
|||||||
public abstract class AbstractJpaNamedEntity extends AbstractJpaTenantAwareBaseEntity implements NamedEntity {
|
public abstract class AbstractJpaNamedEntity extends AbstractJpaTenantAwareBaseEntity implements NamedEntity {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Column(name = "name", nullable = false, length = 64)
|
@Column(name = "name", nullable = false, length = NamedEntity.NAME_MAX_SIZE)
|
||||||
@Size(max = 64)
|
@Size(min = 1, max = NamedEntity.NAME_MAX_SIZE)
|
||||||
@NotEmpty
|
@NotNull
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@Column(name = "description", nullable = true, length = 512)
|
@Column(name = "description", nullable = true, length = NamedEntity.DESCRIPTION_MAX_SIZE)
|
||||||
@Size(max = 512)
|
@Size(max = NamedEntity.DESCRIPTION_MAX_SIZE)
|
||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.model;
|
|||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.MappedSuperclass;
|
import javax.persistence.MappedSuperclass;
|
||||||
import javax.persistence.PrePersist;
|
import javax.persistence.PrePersist;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
import javax.validation.constraints.Size;
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
|
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
|
||||||
@@ -20,7 +21,6 @@ import org.eclipse.hawkbit.repository.model.helper.SystemManagementHolder;
|
|||||||
import org.eclipse.persistence.annotations.Multitenant;
|
import org.eclipse.persistence.annotations.Multitenant;
|
||||||
import org.eclipse.persistence.annotations.MultitenantType;
|
import org.eclipse.persistence.annotations.MultitenantType;
|
||||||
import org.eclipse.persistence.annotations.TenantDiscriminatorColumn;
|
import org.eclipse.persistence.annotations.TenantDiscriminatorColumn;
|
||||||
import org.hibernate.validator.constraints.NotEmpty;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holder of the base attributes common to all tenant aware entities.
|
* Holder of the base attributes common to all tenant aware entities.
|
||||||
@@ -33,8 +33,8 @@ public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEn
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Column(name = "tenant", nullable = false, insertable = false, updatable = false, length = 40)
|
@Column(name = "tenant", nullable = false, insertable = false, updatable = false, length = 40)
|
||||||
@Size(max = 40)
|
@Size(min = 1, max = 40)
|
||||||
@NotEmpty
|
@NotNull
|
||||||
private String tenant;
|
private String tenant;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
|
|||||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||||
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
||||||
import org.eclipse.persistence.descriptors.DescriptorEvent;
|
import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||||
import org.hibernate.validator.constraints.NotEmpty;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -92,9 +91,9 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
|||||||
private static final List<String> TARGET_UPDATE_EVENT_IGNORE_FIELDS = Arrays.asList("lastTargetQuery",
|
private static final List<String> TARGET_UPDATE_EVENT_IGNORE_FIELDS = Arrays.asList("lastTargetQuery",
|
||||||
"lastTargetQuery", "address", "optLockRevision", "lastModifiedAt", "lastModifiedBy");
|
"lastTargetQuery", "address", "optLockRevision", "lastModifiedAt", "lastModifiedBy");
|
||||||
|
|
||||||
@Column(name = "controller_id", length = 64)
|
@Column(name = "controller_id", length = Target.CONTROLLER_ID_MAX_SIZE)
|
||||||
@Size(min = 1, max = 64)
|
@Size(min = 1, max = Target.CONTROLLER_ID_MAX_SIZE)
|
||||||
@NotEmpty
|
@NotNull
|
||||||
@Pattern(regexp = "[.\\S]*", message = "has whitespaces which are not allowed")
|
@Pattern(regexp = "[.\\S]*", message = "has whitespaces which are not allowed")
|
||||||
private String controllerId;
|
private String controllerId;
|
||||||
|
|
||||||
@@ -113,17 +112,17 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
|||||||
* the security token of the target which allows if enabled to authenticate
|
* the security token of the target which allows if enabled to authenticate
|
||||||
* with this security token.
|
* with this security token.
|
||||||
*/
|
*/
|
||||||
@Column(name = "sec_token", updatable = true, nullable = false, length = 128)
|
@Column(name = "sec_token", updatable = true, nullable = false, length = Target.SECURITY_TOKEN_MAX_SIZE)
|
||||||
@Size(max = 128)
|
@Size(min = 1, max = Target.SECURITY_TOKEN_MAX_SIZE)
|
||||||
@NotEmpty
|
@NotNull
|
||||||
private String securityToken;
|
private String securityToken;
|
||||||
|
|
||||||
@CascadeOnDelete
|
@CascadeOnDelete
|
||||||
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
|
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
|
||||||
private List<RolloutTargetGroup> rolloutTargetGroup;
|
private List<RolloutTargetGroup> rolloutTargetGroup;
|
||||||
|
|
||||||
@Column(name = "address", length = 512)
|
@Column(name = "address", length = Target.ADDRESS_MAX_SIZE)
|
||||||
@Size(max = 512)
|
@Size(max = Target.ADDRESS_MAX_SIZE)
|
||||||
private String address;
|
private String address;
|
||||||
|
|
||||||
@Column(name = "last_target_query")
|
@Column(name = "last_target_query")
|
||||||
|
|||||||
@@ -9,14 +9,21 @@
|
|||||||
package org.eclipse.hawkbit.repository.jpa;
|
package org.eclipse.hawkbit.repository.jpa;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintViolationException;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
|
||||||
|
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent;
|
||||||
|
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
|
||||||
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||||
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||||
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
||||||
@@ -26,6 +33,7 @@ import com.google.common.collect.Sets;
|
|||||||
|
|
||||||
import ru.yandex.qatools.allure.annotations.Description;
|
import ru.yandex.qatools.allure.annotations.Description;
|
||||||
import ru.yandex.qatools.allure.annotations.Features;
|
import ru.yandex.qatools.allure.annotations.Features;
|
||||||
|
import ru.yandex.qatools.allure.annotations.Step;
|
||||||
import ru.yandex.qatools.allure.annotations.Stories;
|
import ru.yandex.qatools.allure.annotations.Stories;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,6 +83,92 @@ public class DistributionSetTypeManagementTest extends AbstractJpaIntegrationTes
|
|||||||
"DistributionSet");
|
"DistributionSet");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verify that a DistributionSet with invalid properties cannot be created or updated")
|
||||||
|
@ExpectEvents({ @Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||||
|
@Expect(type = DistributionSetUpdatedEvent.class, count = 0) })
|
||||||
|
public void createAndUpdateDistributionSetWithInvalidFields() {
|
||||||
|
final DistributionSet set = testdataFactory.createDistributionSet();
|
||||||
|
|
||||||
|
createAndUpdateDistributionSetWithInvalidDescription(set);
|
||||||
|
createAndUpdateDistributionSetWithInvalidName(set);
|
||||||
|
createAndUpdateDistributionSetWithInvalidVersion(set);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private void createAndUpdateDistributionSetWithInvalidDescription(final DistributionSet set) {
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> distributionSetManagement.createDistributionSet(entityFactory.distributionSet()
|
||||||
|
.create().name("a").version("a").description(RandomStringUtils.randomAlphanumeric(513))))
|
||||||
|
.as("set with too long description should not be created");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> distributionSetManagement.updateDistributionSet(entityFactory.distributionSet()
|
||||||
|
.update(set.getId()).description(RandomStringUtils.randomAlphanumeric(513))))
|
||||||
|
.as("set with too long description should not be updated");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private void createAndUpdateDistributionSetWithInvalidName(final DistributionSet set) {
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> distributionSetManagement.createDistributionSet(entityFactory.distributionSet()
|
||||||
|
.create().version("a").name(RandomStringUtils.randomAlphanumeric(65))))
|
||||||
|
.as("set with too long name should not be created");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> distributionSetManagement
|
||||||
|
.createDistributionSet(entityFactory.distributionSet().create().version("a").name("")))
|
||||||
|
.as("set with too short name should not be created");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> distributionSetManagement
|
||||||
|
.createDistributionSet(entityFactory.distributionSet().create().version("a").name(null)))
|
||||||
|
.as("set with null name should not be created");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> distributionSetManagement.updateDistributionSet(entityFactory.distributionSet()
|
||||||
|
.update(set.getId()).name(RandomStringUtils.randomAlphanumeric(65))))
|
||||||
|
.as("set with too long name should not be updated");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> distributionSetManagement
|
||||||
|
.updateDistributionSet(entityFactory.distributionSet().update(set.getId()).name("")))
|
||||||
|
.as("set with too short name should not be updated");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private void createAndUpdateDistributionSetWithInvalidVersion(final DistributionSet set) {
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> distributionSetManagement.createDistributionSet(entityFactory.distributionSet()
|
||||||
|
.create().name("a").version(RandomStringUtils.randomAlphanumeric(65))))
|
||||||
|
.as("set with too long name should not be created");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> distributionSetManagement
|
||||||
|
.createDistributionSet(entityFactory.distributionSet().create().name("a").version("")))
|
||||||
|
.as("set with too short name should not be created");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> distributionSetManagement
|
||||||
|
.createDistributionSet(entityFactory.distributionSet().create().name("a").version(null)))
|
||||||
|
.as("set with null name should not be created");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> distributionSetManagement.updateDistributionSet(entityFactory.distributionSet()
|
||||||
|
.update(set.getId()).version(RandomStringUtils.randomAlphanumeric(65))))
|
||||||
|
.as("set with too long name should not be updated");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> distributionSetManagement
|
||||||
|
.updateDistributionSet(entityFactory.distributionSet().update(set.getId()).version("")))
|
||||||
|
.as("set with too short name should not be updated");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Tests the successfull module update of unused distribution set type which is in fact allowed.")
|
@Description("Tests the successfull module update of unused distribution set type which is in fact allowed.")
|
||||||
public void updateUnassignedDistributionSetTypeModules() {
|
public void updateUnassignedDistributionSetTypeModules() {
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
* 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.jpa;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import javax.persistence.OptimisticLockException;
|
||||||
|
import javax.persistence.PersistenceException;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.dao.ConcurrencyFailureException;
|
||||||
|
import org.springframework.dao.UncategorizedDataAccessException;
|
||||||
|
|
||||||
|
import ru.yandex.qatools.allure.annotations.Description;
|
||||||
|
import ru.yandex.qatools.allure.annotations.Features;
|
||||||
|
import ru.yandex.qatools.allure.annotations.Stories;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping tests for {@link HawkBitEclipseLinkJpaDialect}.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Features("Unit Tests - Repository")
|
||||||
|
@Stories("Exception handling")
|
||||||
|
public class HawkBitEclipseLinkJpaDialectTest {
|
||||||
|
private final HawkBitEclipseLinkJpaDialect hawkBitEclipseLinkJpaDialectUnderTest = new HawkBitEclipseLinkJpaDialect();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Use Case: PersistenceException that can be mapped by EclipseLinkJpaDialect into corresponding DataAccessException.")
|
||||||
|
public void jpaOptimisticLockExceptionIsConcurrencyFailureException() {
|
||||||
|
assertThat(
|
||||||
|
hawkBitEclipseLinkJpaDialectUnderTest.translateExceptionIfPossible(mock(OptimisticLockException.class)))
|
||||||
|
.isInstanceOf(ConcurrencyFailureException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Use Case: PersistenceException that could not be mapped by EclipseLinkJpaDialect directly but "
|
||||||
|
+ "instead is wrapped into JpaSystemException. Cause of PersistenceException is an SQLException.")
|
||||||
|
public void jpaSystemExceptionWithSqlDeadLockExceptionIsConcurrencyFailureException() {
|
||||||
|
final PersistenceException persEception = mock(PersistenceException.class);
|
||||||
|
when(persEception.getCause()).thenReturn(new SQLException("simulated transaction ER_LOCK_DEADLOCK", "40001"));
|
||||||
|
|
||||||
|
assertThat(hawkBitEclipseLinkJpaDialectUnderTest.translateExceptionIfPossible(persEception))
|
||||||
|
.isInstanceOf(ConcurrencyFailureException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Use Case: PersistenceException that could not be mapped by EclipseLinkJpaDialect directly but instead is wrapped"
|
||||||
|
+ " into JpaSystemException. Cause of PersistenceException is not an SQLException.")
|
||||||
|
public void jpaSystemExceptionWithNumberFormatExceptionIsNull() {
|
||||||
|
final PersistenceException persEception = mock(PersistenceException.class);
|
||||||
|
when(persEception.getCause()).thenReturn(new NumberFormatException());
|
||||||
|
|
||||||
|
assertThat(hawkBitEclipseLinkJpaDialectUnderTest.translateExceptionIfPossible(persEception))
|
||||||
|
.isInstanceOf(UncategorizedDataAccessException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Use Case: RuntimeException that could not be mapped by EclipseLinkJpaDialect directly. Cause of "
|
||||||
|
+ "RuntimeException is an SQLException.")
|
||||||
|
public void runtimeExceptionWithSqlDeadLockExceptionIsConcurrencyFailureException() {
|
||||||
|
final RuntimeException persEception = mock(RuntimeException.class);
|
||||||
|
when(persEception.getCause()).thenReturn(new SQLException("simulated transaction ER_LOCK_DEADLOCK", "40001"));
|
||||||
|
|
||||||
|
assertThat(hawkBitEclipseLinkJpaDialectUnderTest.translateExceptionIfPossible(persEception))
|
||||||
|
.isInstanceOf(ConcurrencyFailureException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Use Case: RuntimeException that could not be mapped by EclipseLinkJpaDialect directly. Cause of "
|
||||||
|
+ "RuntimeException is not an SQLException.")
|
||||||
|
public void runtimeExceptionWithNumberFormatExceptionIsNull() {
|
||||||
|
final RuntimeException persEception = mock(RuntimeException.class);
|
||||||
|
when(persEception.getCause()).thenReturn(new NumberFormatException());
|
||||||
|
|
||||||
|
assertThat(hawkBitEclipseLinkJpaDialectUnderTest.translateExceptionIfPossible(persEception)).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import javax.validation.ConstraintViolationException;
|
import javax.validation.ConstraintViolationException;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
|
||||||
@@ -55,12 +56,15 @@ import com.google.common.collect.Iterables;
|
|||||||
|
|
||||||
import ru.yandex.qatools.allure.annotations.Description;
|
import ru.yandex.qatools.allure.annotations.Description;
|
||||||
import ru.yandex.qatools.allure.annotations.Features;
|
import ru.yandex.qatools.allure.annotations.Features;
|
||||||
|
import ru.yandex.qatools.allure.annotations.Step;
|
||||||
import ru.yandex.qatools.allure.annotations.Stories;
|
import ru.yandex.qatools.allure.annotations.Stories;
|
||||||
|
|
||||||
@Features("Component Tests - Repository")
|
@Features("Component Tests - Repository")
|
||||||
@Stories("Target Management")
|
@Stories("Target Management")
|
||||||
public class TargetManagementTest extends AbstractJpaIntegrationTest {
|
public class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||||
|
|
||||||
|
private static final String WHITESPACE_ERROR = "target with whitespaces in controller id should not be created";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Verifies that management get access react as specfied on calls for non existing entities by means "
|
@Description("Verifies that management get access react as specfied on calls for non existing entities by means "
|
||||||
+ "of Optional not present.")
|
+ "of Optional not present.")
|
||||||
@@ -174,25 +178,6 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@Description("Verify that a target with empty controller id cannot be created")
|
|
||||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
|
||||||
public void createTargetWithNoControllerId() {
|
|
||||||
try {
|
|
||||||
targetManagement.createTarget(entityFactory.target().create().controllerId(""));
|
|
||||||
fail("target with empty controller id should not be created");
|
|
||||||
} catch (final ConstraintViolationException e) {
|
|
||||||
// ok
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
targetManagement.createTarget(entityFactory.target().create().controllerId(null));
|
|
||||||
fail("target with empty controller id should not be created");
|
|
||||||
} catch (final ConstraintViolationException e) {
|
|
||||||
// ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Verify that a target with same controller ID than another device cannot be created.")
|
@Description("Verify that a target with same controller ID than another device cannot be created.")
|
||||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) })
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) })
|
||||||
@@ -204,50 +189,125 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Verify that a target with whitespaces in controller id cannot be created")
|
@Description("Verify that a target with with invalid properties cannot be created or updated")
|
||||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
public void createTargetWithWhitespaces() {
|
@Expect(type = TargetUpdatedEvent.class, count = 0) })
|
||||||
try {
|
public void createAndUpdateTargetWithInvalidFields() {
|
||||||
targetManagement.createTarget(entityFactory.target().create().controllerId(" "));
|
final Target target = testdataFactory.createTarget();
|
||||||
fail("target with whitespaces in controller id should not be created");
|
|
||||||
} catch (final ConstraintViolationException e) {
|
|
||||||
// ok
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
createTargetWithInvalidControllerId();
|
||||||
targetManagement.createTarget(entityFactory.target().create().controllerId(" a"));
|
createAndUpdateTargetWithInvalidDescription(target);
|
||||||
fail("target with whitespaces in controller id should not be created");
|
createAndUpdateTargetWithInvalidName(target);
|
||||||
} catch (final ConstraintViolationException e) {
|
createAndUpdateTargetWithInvalidSecurityToken(target);
|
||||||
// ok
|
createAndUpdateTargetWithInvalidAddress(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
@Step
|
||||||
targetManagement.createTarget(entityFactory.target().create().controllerId("a "));
|
private void createAndUpdateTargetWithInvalidDescription(final Target target) {
|
||||||
fail("target with whitespaces in controller id should not be created");
|
|
||||||
} catch (final ConstraintViolationException e) {
|
|
||||||
// ok
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
targetManagement.createTarget(entityFactory.target().create().controllerId("a b"));
|
.isThrownBy(() -> targetManagement.createTarget(entityFactory.target().create().controllerId("a")
|
||||||
fail("target with whitespaces in controller id should not be created");
|
.description(RandomStringUtils.randomAlphanumeric(513))))
|
||||||
} catch (final ConstraintViolationException e) {
|
.as("target with too long description should not be created");
|
||||||
// ok
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
targetManagement.createTarget(entityFactory.target().create().controllerId(" "));
|
.isThrownBy(() -> targetManagement.updateTarget(entityFactory.target().update(target.getControllerId())
|
||||||
fail("target with whitespaces in controller id should not be created");
|
.description(RandomStringUtils.randomAlphanumeric(513))))
|
||||||
} catch (final ConstraintViolationException e) {
|
.as("target with too long description should not be updated");
|
||||||
// ok
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
}
|
||||||
targetManagement.createTarget(entityFactory.target().create().controllerId("aaa bbb"));
|
|
||||||
fail("target with whitespaces in controller id should not be created");
|
@Step
|
||||||
} catch (final ConstraintViolationException e) {
|
private void createAndUpdateTargetWithInvalidName(final Target target) {
|
||||||
// ok
|
|
||||||
}
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> targetManagement.createTarget(entityFactory.target().create().controllerId("a")
|
||||||
|
.name(RandomStringUtils.randomAlphanumeric(65))))
|
||||||
|
.as("target with too long name should not be created");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> targetManagement.updateTarget(entityFactory.target().update(target.getControllerId())
|
||||||
|
.name(RandomStringUtils.randomAlphanumeric(65))))
|
||||||
|
.as("target with too long name should not be updated");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
|
||||||
|
() -> targetManagement.updateTarget(entityFactory.target().update(target.getControllerId()).name("")))
|
||||||
|
.as("target with too short name should not be updated");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private void createAndUpdateTargetWithInvalidSecurityToken(final Target target) {
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> targetManagement.createTarget(entityFactory.target().create().controllerId("a")
|
||||||
|
.securityToken(RandomStringUtils.randomAlphanumeric(129))))
|
||||||
|
.as("target with too long token should not be created");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> targetManagement.updateTarget(entityFactory.target().update(target.getControllerId())
|
||||||
|
.securityToken(RandomStringUtils.randomAlphanumeric(129))))
|
||||||
|
.as("target with too long token should not be updated");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> targetManagement
|
||||||
|
.updateTarget(entityFactory.target().update(target.getControllerId()).securityToken("")))
|
||||||
|
.as("target with too short token should not be updated");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private void createAndUpdateTargetWithInvalidAddress(final Target target) {
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> targetManagement.createTarget(entityFactory.target().create().controllerId("a")
|
||||||
|
.address(RandomStringUtils.randomAlphanumeric(513))))
|
||||||
|
.as("target with too long address should not be created");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> targetManagement.updateTarget(entityFactory.target().update(target.getControllerId())
|
||||||
|
.address(RandomStringUtils.randomAlphanumeric(513))))
|
||||||
|
.as("target with too long address should not be updated");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private void createTargetWithInvalidControllerId() {
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> targetManagement.createTarget(entityFactory.target().create().controllerId("")))
|
||||||
|
.as("target with empty controller id should not be created");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> targetManagement.createTarget(entityFactory.target().create().controllerId(null)))
|
||||||
|
.as("target with null controller id should not be created");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> targetManagement.createTarget(
|
||||||
|
entityFactory.target().create().controllerId(RandomStringUtils.randomAlphanumeric(65))))
|
||||||
|
.as("target with too long controller id should not be created");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> targetManagement.createTarget(entityFactory.target().create().controllerId(" ")))
|
||||||
|
.as(WHITESPACE_ERROR);
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> targetManagement.createTarget(entityFactory.target().create().controllerId(" a")))
|
||||||
|
.as(WHITESPACE_ERROR);
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> targetManagement.createTarget(entityFactory.target().create().controllerId("a ")))
|
||||||
|
.as(WHITESPACE_ERROR);
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> targetManagement.createTarget(entityFactory.target().create().controllerId("a b")))
|
||||||
|
.as(WHITESPACE_ERROR);
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(() -> targetManagement.createTarget(entityFactory.target().create().controllerId(" ")))
|
||||||
|
.as(WHITESPACE_ERROR);
|
||||||
|
|
||||||
|
assertThatExceptionOfType(ConstraintViolationException.class)
|
||||||
|
.isThrownBy(
|
||||||
|
() -> targetManagement.createTarget(entityFactory.target().create().controllerId("aaa bbb")))
|
||||||
|
.as(WHITESPACE_ERROR);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -177,6 +177,8 @@ public class RSQLTargetFieldTest extends AbstractJpaIntegrationTest {
|
|||||||
assertRSQLQuery(TargetFields.LASTCONTROLLERREQUESTAT.name() + "=lt=" + target2.getLastTargetQuery(), 1);
|
assertRSQLQuery(TargetFields.LASTCONTROLLERREQUESTAT.name() + "=lt=" + target2.getLastTargetQuery(), 1);
|
||||||
assertRSQLQuery(TargetFields.LASTCONTROLLERREQUESTAT.name() + "=gt=" + target.getLastTargetQuery(), 1);
|
assertRSQLQuery(TargetFields.LASTCONTROLLERREQUESTAT.name() + "=gt=" + target.getLastTargetQuery(), 1);
|
||||||
assertRSQLQuery(TargetFields.LASTCONTROLLERREQUESTAT.name() + "=gt=" + target2.getLastTargetQuery(), 0);
|
assertRSQLQuery(TargetFields.LASTCONTROLLERREQUESTAT.name() + "=gt=" + target2.getLastTargetQuery(), 0);
|
||||||
|
assertRSQLQuery(TargetFields.LASTCONTROLLERREQUESTAT.name() + "=le=${NOW_TS}", 2);
|
||||||
|
assertRSQLQuery(TargetFields.LASTCONTROLLERREQUESTAT.name() + "=gt=${OVERDUE_TS}", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertRSQLQuery(final String rsqlParam, final long expcetedTargets) {
|
private void assertRSQLQuery(final String rsqlParam, final long expcetedTargets) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
package org.eclipse.hawkbit.repository.test.matcher;
|
package org.eclipse.hawkbit.repository.test.matcher;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@@ -18,6 +19,9 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.event.remote.RemoteIdEvent;
|
||||||
|
import org.eclipse.hawkbit.repository.event.remote.RemoteTenantAwareEvent;
|
||||||
|
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||||
import org.eclipse.hawkbit.repository.test.util.TestContextProvider;
|
import org.eclipse.hawkbit.repository.test.util.TestContextProvider;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.rules.TestRule;
|
import org.junit.rules.TestRule;
|
||||||
@@ -120,6 +124,21 @@ public class EventVerifier implements TestRule {
|
|||||||
@Override
|
@Override
|
||||||
public void onApplicationEvent(final RemoteApplicationEvent event) {
|
public void onApplicationEvent(final RemoteApplicationEvent event) {
|
||||||
LOGGER.debug("Received event {}", event.getClass().getSimpleName());
|
LOGGER.debug("Received event {}", event.getClass().getSimpleName());
|
||||||
|
|
||||||
|
if (event instanceof RemoteTenantAwareEvent) {
|
||||||
|
assertThat(((RemoteTenantAwareEvent) event).getTenant()).isNotEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event instanceof RemoteIdEvent) {
|
||||||
|
assertThat(((RemoteIdEvent) event).getEntityId()).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event instanceof TargetAssignDistributionSetEvent) {
|
||||||
|
assertThat(((TargetAssignDistributionSetEvent) event).getActionId()).isNotNull();
|
||||||
|
assertThat(((TargetAssignDistributionSetEvent) event).getControllerId()).isNotEmpty();
|
||||||
|
assertThat(((TargetAssignDistributionSetEvent) event).getDistributionSetId()).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
capturedEvents.add(event.getClass());
|
capturedEvents.add(event.getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user