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

[JBPM-9841] Start process with correlation key rest api should return… #2564

Merged
merged 1 commit into from Aug 24, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -17,6 +17,7 @@

import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.SQLIntegrityConstraintViolationException;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -215,7 +216,10 @@ public static Response noContent(Variant v, Header... customHeaders) {
public static Response serviceUnavailable(Header... customHeaders) {
return createResponse("", ERROR_VARIANT, Response.Status.SERVICE_UNAVAILABLE, customHeaders);
}


public static Response conflict(String reason, Variant v, Header... customHeaders) {
return createResponse("\"" +reason + "\"", ERROR_VARIANT, Response.Status.CONFLICT, customHeaders);
}
protected static void applyCustomHeaders(Response.ResponseBuilder builder, Header... customHeaders) {
if (customHeaders != null && customHeaders.length > 0) {
for (Header header : customHeaders) {
Expand Down Expand Up @@ -264,4 +268,24 @@ public static String errorMessage(Throwable e, String defaultMessage) {
public static String errorMessage(Throwable e) {
return errorMessage(e, MessageFormat.format(UNEXPECTED_ERROR, e.getMessage()));
}

public static Boolean isCorrelationKeyAlreadyExists(Exception e) {
SQLIntegrityConstraintViolationException constraintViolationException = null;

Throwable t = e.getCause();
while ((t != null) && !(t instanceof SQLIntegrityConstraintViolationException))
t = t.getCause();

constraintViolationException = (SQLIntegrityConstraintViolationException) t;

/*
* 23000 SQLState is used by MySQL, MSSQL,Oracle,Postgres and MariaDB for constraint Violation.
* 'already exists' keyword is used in RuntimeException which is thrown by org.jbpm.persistence.JpaProcessPersistenceContext class to indicate that Correlation key already exists in database.
* */
if ((constraintViolationException != null && "23000".equals(constraintViolationException.getSQLState())) || e.getMessage().contains("already exists")) {
return true;
}
return false;

}
}
Expand Up @@ -47,7 +47,35 @@
"methodName": "queryUserTasksByVariables",
"elementKind": "method",
"justification": "https://issues.redhat.com/browse/JBPM-9714"
}]
},
{
"code":"java.annotation.attributeValueChanged",
"old":"method javax.ws.rs.core.Response org.kie.server.remote.rest.jbpm.ProcessResource::startProcessWithCorrelationKeyFromNodeIds(javax.ws.rs.core.HttpHeaders, java.lang.String, java.lang.String, java.lang.String, java.lang.String)",
"new":"method javax.ws.rs.core.Response org.kie.server.remote.rest.jbpm.ProcessResource::startProcessWithCorrelationKeyFromNodeIds(javax.ws.rs.core.HttpHeaders, java.lang.String, java.lang.String, java.lang.String, java.lang.String)","annotationType":"io.swagger.annotations.ApiResponses","annotation":"@io.swagger.annotations.ApiResponses({@io.swagger.annotations.ApiResponse(code = 201, response = java.lang.Long.class, message = \"Process instance created\", examples = @io.swagger.annotations.Example({@io.swagger.annotations.ExampleProperty(mediaType = \"application\/json\", value = \"10\"), @io.swagger.annotations.ExampleProperty(mediaType = \"application\/xml\", value = \"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<long-type>\n <value>10<\/value>\n<\/long-type>\")})), @io.swagger.annotations.ApiResponse(code = 500, message = \"Unexpected error\"), @io.swagger.annotations.ApiResponse(code = 404, message = \"Process ID or Container Id not found\"), @io.swagger.annotations.ApiResponse(code = 403, message = \"User does not have permission to access this asset\"), @io.swagger.annotations.ApiResponse(code = 409, message = \"Duplicate correlation key\")})",
"attribute":"value",
"oldValue":"{@io.swagger.annotations.ApiResponse(code = 201, response = java.lang.Long.class, message = \"Process instance created\", examples = @io.swagger.annotations.Example({@io.swagger.annotations.ExampleProperty(mediaType = \"application\/json\", value = \"10\"), @io.swagger.annotations.ExampleProperty(mediaType = \"application\/xml\", value = \"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<long-type>\n <value>10<\/value>\n<\/long-type>\")})), @io.swagger.annotations.ApiResponse(code = 500, message = \"Unexpected error\"), @io.swagger.annotations.ApiResponse(code = 404, message = \"Process ID or Container Id not found\"), @io.swagger.annotations.ApiResponse(code = 403, message = \"User does not have permission to access this asset\")}",
"newValue":"{@io.swagger.annotations.ApiResponse(code = 201, response = java.lang.Long.class, message = \"Process instance created\", examples = @io.swagger.annotations.Example({@io.swagger.annotations.ExampleProperty(mediaType = \"application\/json\", value = \"10\"), @io.swagger.annotations.ExampleProperty(mediaType = \"application\/xml\", value = \"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<long-type>\n <value>10<\/value>\n<\/long-type>\")})), @io.swagger.annotations.ApiResponse(code = 500, message = \"Unexpected error\"), @io.swagger.annotations.ApiResponse(code = 404, message = \"Process ID or Container Id not found\"), @io.swagger.annotations.ApiResponse(code = 403, message = \"User does not have permission to access this asset\"), @io.swagger.annotations.ApiResponse(code = 409, message = \"Duplicate correlation key\")}",
"package":"org.kie.server.remote.rest.jbpm",
"classSimpleName":"ProcessResource",
"methodName":"startProcessWithCorrelationKeyFromNodeIds",
"elementKind":"method",
"justification":"https://issues.redhat.com/browse/JBPM-9841"
},
{
"code": "java.annotation.attributeValueChanged",
"old": "method javax.ws.rs.core.Response org.kie.server.remote.rest.jbpm.ProcessResource::startProcessWithCorrelation(javax.ws.rs.core.HttpHeaders, java.lang.String, java.lang.String, java.lang.String, java.lang.String)",
"new": "method javax.ws.rs.core.Response org.kie.server.remote.rest.jbpm.ProcessResource::startProcessWithCorrelation(javax.ws.rs.core.HttpHeaders, java.lang.String, java.lang.String, java.lang.String, java.lang.String)",
"annotationType": "io.swagger.annotations.ApiResponses",
"annotation": "@io.swagger.annotations.ApiResponses({@io.swagger.annotations.ApiResponse(code = 201, response = java.lang.Long.class, message = \"Process instance started\", examples = @io.swagger.annotations.Example({@io.swagger.annotations.ExampleProperty(mediaType = \"application\/json\", value = \"10\"), @io.swagger.annotations.ExampleProperty(mediaType = \"application\/xml\", value = \"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<long-type>\n <value>10<\/value>\n<\/long-type>\")})), @io.swagger.annotations.ApiResponse(code = 500, message = \"Unexpected error\"), @io.swagger.annotations.ApiResponse(code = 404, message = \"Process ID or Container Id not found\"), @io.swagger.annotations.ApiResponse(code = 403, message = \"User does not have permission to access this asset\"), @io.swagger.annotations.ApiResponse(code = 409, message = \"Duplicate correlation key\")})",
"attribute": "value",
"oldValue": "{@io.swagger.annotations.ApiResponse(code = 201, response = java.lang.Long.class, message = \"Process instance started\", examples = @io.swagger.annotations.Example({@io.swagger.annotations.ExampleProperty(mediaType = \"application\/json\", value = \"10\"), @io.swagger.annotations.ExampleProperty(mediaType = \"application\/xml\", value = \"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<long-type>\n <value>10<\/value>\n<\/long-type>\")})), @io.swagger.annotations.ApiResponse(code = 500, message = \"Unexpected error\"), @io.swagger.annotations.ApiResponse(code = 404, message = \"Process ID or Container Id not found\"), @io.swagger.annotations.ApiResponse(code = 403, message = \"User does not have permission to access this asset\")}",
"newValue": "{@io.swagger.annotations.ApiResponse(code = 201, response = java.lang.Long.class, message = \"Process instance started\", examples = @io.swagger.annotations.Example({@io.swagger.annotations.ExampleProperty(mediaType = \"application\/json\", value = \"10\"), @io.swagger.annotations.ExampleProperty(mediaType = \"application\/xml\", value = \"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<long-type>\n <value>10<\/value>\n<\/long-type>\")})), @io.swagger.annotations.ApiResponse(code = 500, message = \"Unexpected error\"), @io.swagger.annotations.ApiResponse(code = 404, message = \"Process ID or Container Id not found\"), @io.swagger.annotations.ApiResponse(code = 403, message = \"User does not have permission to access this asset\"), @io.swagger.annotations.ApiResponse(code = 409, message = \"Duplicate correlation key\")}",
"package": "org.kie.server.remote.rest.jbpm",
"classSimpleName": "ProcessResource",
"methodName": "startProcessWithCorrelation",
"elementKind": "method",
"justification": "https://issues.redhat.com/browse/JBPM-9841"
}]
}
}
}
Expand Up @@ -96,12 +96,14 @@
import static org.kie.server.api.rest.RestURI.COMPUTE_PROCESS_OUTCOME_POST_URI;
import static org.kie.server.remote.rest.common.util.RestUtils.badRequest;
import static org.kie.server.remote.rest.common.util.RestUtils.buildConversationIdHeader;
import static org.kie.server.remote.rest.common.util.RestUtils.conflict;
import static org.kie.server.remote.rest.common.util.RestUtils.createCorrectVariant;
import static org.kie.server.remote.rest.common.util.RestUtils.createResponse;
import static org.kie.server.remote.rest.common.util.RestUtils.errorMessage;
import static org.kie.server.remote.rest.common.util.RestUtils.forbidden;
import static org.kie.server.remote.rest.common.util.RestUtils.getContentType;
import static org.kie.server.remote.rest.common.util.RestUtils.getVariant;
import static org.kie.server.remote.rest.common.util.RestUtils.isCorrelationKeyAlreadyExists;
import static org.kie.server.remote.rest.common.util.RestUtils.internalServerError;
import static org.kie.server.remote.rest.common.util.RestUtils.noContent;
import static org.kie.server.remote.rest.common.util.RestUtils.notFound;
Expand Down Expand Up @@ -295,7 +297,8 @@ public Response startProcessFromNodeIds(@javax.ws.rs.core.Context HttpHeaders he
@ExampleProperty(mediaType = XML, value = LONG_RESPONSE_XML)})),
@ApiResponse(code = 500, message = "Unexpected error"),
@ApiResponse(code = 404, message = "Process ID or Container Id not found"),
@ApiResponse(code = 403, message = "User does not have permission to access this asset")})
@ApiResponse(code = 403, message = "User does not have permission to access this asset"),
@ApiResponse(code = 409, message = "Duplicate correlation key")})
@POST
@Path(START_PROCESS_FROM_NODES_WITH_CORRELATION_KEY_POST_URI)
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
Expand Down Expand Up @@ -328,9 +331,12 @@ public Response startProcessWithCorrelationKeyFromNodeIds(@javax.ws.rs.core.Cont
} catch (SecurityException e) {
return forbidden(errorMessage(e, e.getMessage()), v, conversationIdHeader);
} catch (Exception e) {
if (isCorrelationKeyAlreadyExists(e)) {
return conflict(errorMessage(e, e.getMessage()), v, conversationIdHeader);
}
logger.error("Unexpected error during processing {}", e.getMessage(), e);
elguardian marked this conversation as resolved.
Show resolved Hide resolved
return internalServerError(
MessageFormat.format(CREATE_RESPONSE_ERROR, e.getMessage()), v);
MessageFormat.format(CREATE_RESPONSE_ERROR, e.getMessage()), v);
}
}

Expand All @@ -340,7 +346,8 @@ public Response startProcessWithCorrelationKeyFromNodeIds(@javax.ws.rs.core.Cont
@ExampleProperty(mediaType=XML, value=LONG_RESPONSE_XML)})),
@ApiResponse(code = 500, message = "Unexpected error"),
@ApiResponse(code = 404, message = "Process ID or Container Id not found"),
@ApiResponse(code = 403, message = "User does not have permission to access this asset")})
@ApiResponse(code = 403, message = "User does not have permission to access this asset"),
@ApiResponse(code = 409, message = "Duplicate correlation key")})
@POST
@Path(START_PROCESS_WITH_CORRELATION_KEY_POST_URI)
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
Expand Down Expand Up @@ -373,6 +380,9 @@ public Response startProcessWithCorrelation(@javax.ws.rs.core.Context HttpHeader
} catch (SecurityException e) {
return forbidden(errorMessage(e, e.getMessage()), v, conversationIdHeader);
} catch (Exception e) {
if (isCorrelationKeyAlreadyExists(e)) {
return conflict(errorMessage(e, e.getMessage()), v, conversationIdHeader);
}
logger.error("Unexpected error during processing {}", e.getMessage(), e);
return internalServerError(
MessageFormat.format(CREATE_RESPONSE_ERROR, e.getMessage()), v);
Expand Down
Expand Up @@ -165,10 +165,10 @@ protected void assertClientException(ThrowableAssert.ThrowingCallable callable,
}

protected List<ExecutionErrorInstance> filterErrorsByProcessId(Collection<ExecutionErrorInstance> errors, String processId) {
return errors.stream().filter(error -> error.getProcessId().equals(processId)).collect(Collectors.toList());
return errors.stream().filter(error -> processId.equals(error.getProcessId())).collect(Collectors.toList());
}

protected List<ExecutionErrorInstance> filterErrorsByProcessInstanceId(Collection<ExecutionErrorInstance> errors, Long processInstanceId) {
return errors.stream().filter(error -> error.getProcessInstanceId().equals(processInstanceId)).collect(Collectors.toList());
return errors.stream().filter(error -> processInstanceId.equals(error.getProcessInstanceId())).collect(Collectors.toList());
}
}
Expand Up @@ -45,6 +45,7 @@
import static org.kie.server.api.rest.RestURI.PROCESS_INST_ID;
import static org.kie.server.api.rest.RestURI.PROCESS_URI;
import static org.kie.server.api.rest.RestURI.START_PROCESS_POST_URI;
import static org.kie.server.api.rest.RestURI.START_PROCESS_WITH_CORRELATION_KEY_POST_URI;
import static org.kie.server.api.rest.RestURI.VAR_NAME;
import static org.kie.server.api.rest.RestURI.WORK_ITEM_ID;
import static org.kie.server.api.rest.RestURI.build;
Expand All @@ -57,6 +58,7 @@ public class ProcessServiceRestOnlyIntegrationTest extends RestJbpmBaseIntegrati

protected static final String CONTAINER_ID = "definition-project";
protected static final String NON_EXISTING_CONTAINER_ID = "non-existing-container";
protected static final String CORRELATION_KEY= "TestCorrelationKey";

@BeforeClass
public static void buildAndDeployArtifacts() {
Expand Down Expand Up @@ -109,6 +111,41 @@ public void testAbortAlreadyAbortedProcess() {
}
}

//JBPM-9841
@Test
public void testStartProcessWithExistingCorrelationKey() {
Map<String, Object> valuesMap = new HashMap<String, Object>();
valuesMap.put(RestURI.CONTAINER_ID, CONTAINER_ID);
valuesMap.put(RestURI.PROCESS_ID, PROCESS_ID_USERTASK);
valuesMap.put(RestURI.CORRELATION_KEY, CORRELATION_KEY);

Response response = null;
try {
// start process instance
WebTarget clientRequest = newRequest(build(TestConfig.getKieServerHttpUrl(), PROCESS_URI + "/" + START_PROCESS_WITH_CORRELATION_KEY_POST_URI, valuesMap));
logger.debug("[POST] " + clientRequest.getUri());
response = clientRequest.request(getMediaType()).post(createEntity(""));
assertThat(response.getStatus()).isEqualTo(Response.Status.CREATED.getStatusCode());

Long result = response.readEntity(JaxbLong.class).unwrap();
assertThat(result).isNotNull();
response.close();

//Start Process with same correlationKey to check response code
clientRequest = newRequest(build(TestConfig.getKieServerHttpUrl(), PROCESS_URI + "/" + START_PROCESS_WITH_CORRELATION_KEY_POST_URI, valuesMap));
logger.debug("[POST] " + clientRequest.getUri());
response = clientRequest.request(getMediaType()).post(createEntity(""));

assertThat(response.getStatus()).isEqualTo(Response.Status.CONFLICT.getStatusCode());
response.close();

} finally {
if (response != null) {
response.close();
}
}
}

@Test
public void testProcessWhichBelongsToAContainer() {
Map<String, Object> valuesMap = new HashMap<String, Object>();
Expand Down