Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX: Improved error message in the case of SQL constraint violation on foreign keys #4167

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*******************************************************************************
* Copyright (c) 2018, 2022 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Eurotech - initial API and implementation
*******************************************************************************/
package org.eclipse.kapua.commons.rest.errors;

import org.eclipse.kapua.kapuaIntegrityConstraintViolationException;
import org.eclipse.kapua.commons.rest.model.errors.ExceptionInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Provider
public class KapuaIntegrityConstraintViolationExceptionMapper implements ExceptionMapper<kapuaIntegrityConstraintViolationException> {
private static final Logger LOG = LoggerFactory.getLogger(kapuaIntegrityConstraintViolationException.class);

private static final Status STATUS = Status.CONFLICT;
@Inject
public ExceptionConfigurationProvider exceptionConfigurationProvider;

@Override
public Response toResponse(kapuaIntegrityConstraintViolationException kapuaIntegrityConstraintViolationException) {
final boolean showStackTrace = exceptionConfigurationProvider.showStackTrace();
LOG.error(kapuaIntegrityConstraintViolationException.getMessage(), kapuaIntegrityConstraintViolationException);
return Response
.status(STATUS)
.entity(new ExceptionInfo(STATUS.getStatusCode(), kapuaIntegrityConstraintViolationException, showStackTrace))
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
import org.eclipse.kapua.KapuaException;
import org.eclipse.kapua.KapuaIllegalNullArgumentException;
import org.eclipse.kapua.KapuaOptimisticLockingException;
import org.eclipse.kapua.kapuaIntegrityConstraintViolationException;
import org.eclipse.persistence.exceptions.DatabaseException;

import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import javax.persistence.RollbackException;
import java.sql.SQLIntegrityConstraintViolationException;

/**
* Exception utilities
Expand Down Expand Up @@ -99,6 +101,12 @@ public static KapuaException convertPersistenceException(Exception he) {

}
break;
default: {
if (cve.getInternalException() instanceof SQLIntegrityConstraintViolationException) {
String message = cve.getMessage().contains("FOREIGN KEY") ? "This entity relates to other entities and cannot be deleted." : "";
ee = new kapuaIntegrityConstraintViolationException(message);
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package org.eclipse.kapua.commons.util;

import java.lang.reflect.Constructor;
import java.sql.SQLIntegrityConstraintViolationException;

import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
Expand All @@ -22,6 +23,7 @@
import org.eclipse.kapua.KapuaException;
import org.eclipse.kapua.KapuaIllegalNullArgumentException;
import org.eclipse.kapua.KapuaOptimisticLockingException;
import org.eclipse.kapua.kapuaIntegrityConstraintViolationException;
import org.eclipse.kapua.qa.markers.junit.JUnitTests;

import org.eclipse.persistence.exceptions.DatabaseException;
Expand Down Expand Up @@ -111,9 +113,21 @@ public void convertPersistenceDatabaseExceptionTest() {
Mockito.when(mockedDatabaseException.getInternalException()).thenReturn(mockedDatabaseException);
Assert.assertEquals("ComparisonFailure not expected for: " + exception,kapuaException.toString(), KapuaExceptionUtils.convertPersistenceException(exception).toString());

Mockito.verify(mockedDatabaseException, Mockito.times(12)).getInternalException();
Mockito.verify(mockedDatabaseException, Mockito.times(12)).getMessage();
Mockito.verify(mockedDatabaseException, Mockito.times(13)).getErrorCode();
//SQL foreign key constraint violation
SQLIntegrityConstraintViolationException mockedDatabaseException2 = Mockito.mock(SQLIntegrityConstraintViolationException.class);
Mockito.when(mockedDatabaseException.getInternalException()).thenReturn(mockedDatabaseException2);
Mockito.when(mockedDatabaseException.getMessage()).thenReturn("FOREIGN KEY");
kapuaIntegrityConstraintViolationException ke = new kapuaIntegrityConstraintViolationException("This entity relates to other entities and cannot be deleted.");
Assert.assertEquals("ComparisonFailure not expected for: " + exception,ke.toString(), KapuaExceptionUtils.convertPersistenceException(exception).toString());

//generic SQL constraint violation
Mockito.when(mockedDatabaseException.getMessage()).thenReturn("another message different from for3ign key but always SQL integrity constraint violation stuff");
kapuaIntegrityConstraintViolationException ke2 = new kapuaIntegrityConstraintViolationException("");
Assert.assertEquals("ComparisonFailure not expected for: " + exception,ke2.toString(), KapuaExceptionUtils.convertPersistenceException(exception).toString());

Mockito.verify(mockedDatabaseException, Mockito.times(15)).getInternalException();
Mockito.verify(mockedDatabaseException, Mockito.times(14)).getMessage();
Mockito.verify(mockedDatabaseException, Mockito.times(15)).getErrorCode();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Feature: Domain Service tests
| test_name_1 | read,write |
Then A domain was created
And The domain matches the creator
Given I expect the exception "KapuaException" with the text "Error during Persistence Operation"
Given I expect the exception "kapuaIntegrityConstraintViolationException" with the text "Entity constraint violation error."
When I create the domain
| name | actions |
| test_name_1 | read,write |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ Feature: User Coupling
And I set the reserved user for the connection from device "device-1" in account "test-acc-1" to "test-user-1"
Then I set the user coupling mode for the connection from device "device-2" in account "test-acc-1" to "STRICT"
# Try to set a duplicate reserved user
Given I expect the exception "KapuaException" with the text "Error during Persistence Operation"
Given I expect the exception "kapuaIntegrityConstraintViolationException" with the text "Entity constraint violation error."
When I set the reserved user for the connection from device "device-2" in account "test-acc-1" to "test-user-1"
Then An exception was thrown
# Reserved users must be unique!
Expand Down Expand Up @@ -873,7 +873,7 @@ Feature: User Coupling
And I set the reserved user for the connection from device "device-1" in account "test-acc-1" to "test-user-1"
Then I set the user coupling mode for the connection from device "device-2" in account "test-acc-1" to "STRICT"
# Try to set a duplicate reserved user
Given I expect the exception "KapuaException" with the text "Error during Persistence Operation"
Given I expect the exception "kapuaIntegrityConstraintViolationException" with the text "Entity constraint violation error."
When I set the reserved user for the connection from device "device-2" in account "test-acc-1" to "test-user-1"
Then An exception was thrown
# Reserved users must be unique!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,13 @@ public enum KapuaErrorCodes implements KapuaErrorCode {
* Some parsing failed for some reason
* @since 2.0.0
*/
PARSING_ERROR
PARSING_ERROR,

/**
* Sql integrity has been violated for some reason
* @since 2.0.0
*/
DATASTORE_INTEGRITY_VIOLATION


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*******************************************************************************
* Copyright (c) 2016, 2022 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Eurotech - initial API and implementation
*******************************************************************************/
package org.eclipse.kapua;

/**
* kapuaIntegrityConstraintViolationException is thrown when the integrity constraints of the underlying datastore have been violated
*
* @since 2.0.0
*/
public class kapuaIntegrityConstraintViolationException extends KapuaException {

/**
* Constructor.
*
* @since 2.0.0
*/
public kapuaIntegrityConstraintViolationException() {
super(KapuaErrorCodes.DATASTORE_INTEGRITY_VIOLATION);
}

/**
* Constructor.
*
* @since 2.0.0
*/
public kapuaIntegrityConstraintViolationException(String detailedMessage) {
super(KapuaErrorCodes.DATASTORE_INTEGRITY_VIOLATION, detailedMessage);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ UNAUTHENTICATED=No authenticated Subject found in context.
# Deprecated codes
USER_ALREADY_RESERVED_BY_ANOTHER_CONNECTION=This user is already reserved for another connection. Please select different user for this connection.
DEVICE_NOT_FOUND=The selected devices were not found. Please refresh device list.
PARSING_ERROR=Error while parsing: {0}
PARSING_ERROR=Error while parsing: {0}
DATASTORE_INTEGRITY_VIOLATION=Entity constraint violation error. {0}