From 536442d8422c6405e2902228fcbfd76b5040d776 Mon Sep 17 00:00:00 2001 From: yanavasileva Date: Fri, 29 Nov 2024 15:52:46 +0100 Subject: [PATCH] feat(core): add update and delete comment Java/Rest APIs (#4565) DELETE /task/{taskId}/comment/{commentId} - deletes a comment of a given taskId and commentId DELETE /task/{taskId}/comment - deletes all comments of a given taskId PUT /task/comment - updates a comment DELETE /process-instance/{processInstanceId}/comment/{commentId} - deletes a comment of a given processInstanceId and commentId DELETE /process-instance/{processInstanceId}/comment. - deletes all comments of a given processInstanceId PUT /process-instance/comment - updates a comment related to: https://github.com/camunda/camunda-bpm-platform/issues/2551 --------- Co-authored-by: Prajwol Bhandari Co-authored-by: --- .../process-instance/{id}/comment/delete.ftl | 43 ++ .../process-instance/{id}/comment/get.ftl | 2 +- .../process-instance/{id}/comment/put.ftl | 58 +++ .../{id}/comment/{commentId}/delete.ftl | 52 ++ .../paths/task/{id}/comment/delete.ftl | 43 ++ .../templates/paths/task/{id}/comment/put.ftl | 58 +++ .../task/{id}/comment/{commentId}/delete.ftl | 52 ++ .../ProcessInstanceCommentResource.java | 18 +- .../ProcessInstanceCommentResourceImpl.java | 66 ++- .../rest/sub/task/TaskCommentResource.java | 26 +- .../task/impl/TaskCommentResourceImpl.java | 46 +- ...essInstanceRestServiceInteractionTest.java | 341 +++++++++++++ .../rest/TaskRestServiceInteractionTest.java | 310 +++++++++++- .../bpm/engine/rest/helper/MockProvider.java | 5 + .../org/camunda/bpm/engine/EntityTypes.java | 1 + .../org/camunda/bpm/engine/TaskService.java | 74 +++ .../engine/history/UserOperationLogEntry.java | 3 + .../bpm/engine/impl/TaskServiceImpl.java | 27 + .../cfg/auth/AuthorizationCommandChecker.java | 52 +- .../multitenancy/TenantCommandChecker.java | 11 +- .../bpm/engine/impl/cmd/AddCommentCmd.java | 12 +- .../cmd/DeleteProcessInstanceCommentCmd.java | 95 ++++ .../engine/impl/cmd/DeleteTaskCommentCmd.java | 87 ++++ .../bpm/engine/impl/cmd/UpdateCommentCmd.java | 137 +++++ .../impl/db/sql/DbSqlSessionFactory.java | 1 + .../persistence/entity/CommentEntity.java | 49 +- .../persistence/entity/CommentManager.java | 11 +- .../entity/UserOperationLogManager.java | 28 + .../db/create/activiti.db2.create.history.sql | 1 + .../db/create/activiti.h2.create.history.sql | 1 + .../activiti.mariadb.create.history.sql | 1 + .../create/activiti.mssql.create.history.sql | 1 + .../create/activiti.mysql.create.history.sql | 1 + .../create/activiti.oracle.create.history.sql | 1 + .../activiti.postgres.create.history.sql | 1 + .../db/upgrade/db2_engine_7.22_to_7.23.sql | 6 +- .../db/upgrade/h2_engine_7.22_to_7.23.sql | 7 +- .../upgrade/mariadb_engine_7.22_to_7.23.sql | 6 +- .../db/upgrade/mssql_engine_7.22_to_7.23.sql | 6 +- .../db/upgrade/mysql_engine_7.22_to_7.23.sql | 6 +- .../db/upgrade/oracle_engine_7.22_to_7.23.sql | 5 +- .../upgrade/postgres_engine_7.22_to_7.23.sql | 6 +- .../engine/impl/mapping/entity/Comment.xml | 81 ++- .../api/authorization/AuthorizationTest.java | 7 + ...ocessInstanceCommentAuthorizationTest.java | 277 ++++++++++ .../TaskCommentAuthorizationTest.java | 281 ++++++++++ .../ProcessEngineCharacterEncodingTest.java | 38 +- .../test/api/task/TaskLastUpdatedTest.java | 104 +++- .../engine/test/api/task/TaskServiceTest.java | 478 +++++++++++++++++- 49 files changed, 2952 insertions(+), 71 deletions(-) create mode 100644 engine-rest/engine-rest-openapi/src/main/templates/paths/process-instance/{id}/comment/delete.ftl create mode 100644 engine-rest/engine-rest-openapi/src/main/templates/paths/process-instance/{id}/comment/put.ftl create mode 100644 engine-rest/engine-rest-openapi/src/main/templates/paths/process-instance/{id}/comment/{commentId}/delete.ftl create mode 100644 engine-rest/engine-rest-openapi/src/main/templates/paths/task/{id}/comment/delete.ftl create mode 100644 engine-rest/engine-rest-openapi/src/main/templates/paths/task/{id}/comment/put.ftl create mode 100644 engine-rest/engine-rest-openapi/src/main/templates/paths/task/{id}/comment/{commentId}/delete.ftl create mode 100644 engine/src/main/java/org/camunda/bpm/engine/impl/cmd/DeleteProcessInstanceCommentCmd.java create mode 100644 engine/src/main/java/org/camunda/bpm/engine/impl/cmd/DeleteTaskCommentCmd.java create mode 100644 engine/src/main/java/org/camunda/bpm/engine/impl/cmd/UpdateCommentCmd.java create mode 100644 engine/src/test/java/org/camunda/bpm/engine/test/api/authorization/ProcessInstanceCommentAuthorizationTest.java create mode 100644 engine/src/test/java/org/camunda/bpm/engine/test/api/authorization/TaskCommentAuthorizationTest.java diff --git a/engine-rest/engine-rest-openapi/src/main/templates/paths/process-instance/{id}/comment/delete.ftl b/engine-rest/engine-rest-openapi/src/main/templates/paths/process-instance/{id}/comment/delete.ftl new file mode 100644 index 00000000000..4dda8b64234 --- /dev/null +++ b/engine-rest/engine-rest-openapi/src/main/templates/paths/process-instance/{id}/comment/delete.ftl @@ -0,0 +1,43 @@ +<#macro endpoint_macro docsUrl=""> + { + <@lib.endpointInfo + id = "deleteProcessInstanceComments" + tag = "Process Instance Comment" + summary = "Delete ProcessInstance Comments" + desc = "Deletes all comments of a process instance by id." /> + + "parameters": [ + + <@lib.parameter + name = "id" + location = "path" + type = "string" + required = true + last = true + desc = "The id of the process instance for which all comments are to be deleted."/> + + ], + "responses": { + + <@lib.response + code = "204" + desc = "Request successful." /> + + <@lib.response + code = "403" + dto = "AuthorizationExceptionDto" + desc = "The authenticated user is unauthorized to delete this resource. See the + [Introduction](${docsUrl}/reference/rest/overview/#error-handling) + for the error response format."/> + + <@lib.response + code = "400" + dto = "ExceptionDto" + last = true + desc = "Process instance doesn't exist or history is not enabled. + See the [Introduction](${docsUrl}/reference/rest/overview/#error-handling) + for the error response format." /> + + } + } + \ No newline at end of file diff --git a/engine-rest/engine-rest-openapi/src/main/templates/paths/process-instance/{id}/comment/get.ftl b/engine-rest/engine-rest-openapi/src/main/templates/paths/process-instance/{id}/comment/get.ftl index 58f20a2616b..44dc2e3e357 100644 --- a/engine-rest/engine-rest-openapi/src/main/templates/paths/process-instance/{id}/comment/get.ftl +++ b/engine-rest/engine-rest-openapi/src/main/templates/paths/process-instance/{id}/comment/get.ftl @@ -3,7 +3,7 @@ <@lib.endpointInfo id = "getProcessInstanceComments" - tag = "Process Instance" + tag = "Process Instance comment" summary = "Get Process Instance Comments" desc = "Gets the comments for a process instance by id." /> diff --git a/engine-rest/engine-rest-openapi/src/main/templates/paths/process-instance/{id}/comment/put.ftl b/engine-rest/engine-rest-openapi/src/main/templates/paths/process-instance/{id}/comment/put.ftl new file mode 100644 index 00000000000..fb5da8486ac --- /dev/null +++ b/engine-rest/engine-rest-openapi/src/main/templates/paths/process-instance/{id}/comment/put.ftl @@ -0,0 +1,58 @@ +<#macro endpoint_macro docsUrl=""> + { + + <@lib.endpointInfo + id = "updateProcessInstanceComment" + tag = "Process Instance Comment" + summary = "Update" + desc = "Updates a Comment." /> + + "parameters" : [ + + <@lib.parameter + name = "id" + location = "path" + type = "string" + required = true + last = true + desc = "The id associated of a process instance of a comment to be updated."/> + + ], + + <@lib.requestBody + mediaType = "application/json" + dto = "CommentDto" + requestDesc = "**Note:** Only the `id` and `message` properties will be used. Every other + property passed to this endpoint will be ignored." + examples = ['"example-1": { + "summary": "PUT /process-instance/aProcessInstanceId/comment", + "value": { + "id": "75bc161a-12da-11e4-7d3a-f4ccdc10a445", + "message": "a process instance comment" + } + }'] /> + + "responses" : { + + <@lib.response + code = "204" + desc = "Request successful." /> + + <@lib.response + code = "400" + dto = "ExceptionDto" + desc = "Returned if a given process instance id or comment id is invalid or history is disabled in the engine. + See the [Introduction](${docsUrl}/reference/rest/overview/#error-handling) for the error response format."/> + + <@lib.response + code = "404" + dto = "AuthorizationExceptionDto" + last = true + desc = "The authenticated user is unauthorized to update this resource. See the + [Introduction](${docsUrl}/reference/rest/overview/#error-handling) + for the error response format." /> + + } + } + + \ No newline at end of file diff --git a/engine-rest/engine-rest-openapi/src/main/templates/paths/process-instance/{id}/comment/{commentId}/delete.ftl b/engine-rest/engine-rest-openapi/src/main/templates/paths/process-instance/{id}/comment/{commentId}/delete.ftl new file mode 100644 index 00000000000..048551741aa --- /dev/null +++ b/engine-rest/engine-rest-openapi/src/main/templates/paths/process-instance/{id}/comment/{commentId}/delete.ftl @@ -0,0 +1,52 @@ +<#macro endpoint_macro docsUrl=""> + { + + <@lib.endpointInfo + id = "deleteProcessInstanceComment" + tag = "Process Instance Comment" + summary = "Delete" + desc = "Removes a comment from a process instance by id." /> + + "parameters" : [ + + <@lib.parameter + name = "id" + location = "path" + type = "string" + required = true + desc = "The id of the process instance." /> + + <@lib.parameter + name = "commentId" + location = "path" + type = "string" + required = true + last = true + desc = "The id of the comment to be removed." /> + + ], + + "responses" : { + + <@lib.response + code = "204" + desc = "Request successful." /> + + <@lib.response + code = "400" + dto = "ExceptionDto" + desc = "Returned if a given process instance id or comment id is invalid or history is disabled in the engine. + See the [Introduction](${docsUrl}/reference/rest/overview/#error-handling) for the error response format."/> + + <@lib.response + code = "404" + dto = "AuthorizationExceptionDto" + last = true + desc = "The authenticated user is unauthorized to delete this resource. See the + [Introduction](${docsUrl}/reference/rest/overview/#error-handling) + for the error response format." /> + + } + } + + \ No newline at end of file diff --git a/engine-rest/engine-rest-openapi/src/main/templates/paths/task/{id}/comment/delete.ftl b/engine-rest/engine-rest-openapi/src/main/templates/paths/task/{id}/comment/delete.ftl new file mode 100644 index 00000000000..233095a0cc3 --- /dev/null +++ b/engine-rest/engine-rest-openapi/src/main/templates/paths/task/{id}/comment/delete.ftl @@ -0,0 +1,43 @@ +<#macro endpoint_macro docsUrl=""> + { + <@lib.endpointInfo + id = "deleteTaskComments" + tag = "Task Comment" + summary = "Delete Task Comments" + desc = "Deletes all comments of a task by task id." /> + + "parameters": [ + + <@lib.parameter + name = "id" + location = "path" + type = "string" + required = true + last = true + desc = "The id of the task for which all comments are to be deleted."/> + + ], + "responses": { + + <@lib.response + code = "204" + desc = "Request successful." /> + + <@lib.response + code = "400" + dto = "ExceptionDto" + desc = "Returned if a given task id is invalid. Orhe history of the engine is disabled. + See the [Introduction](${docsUrl}/reference/rest/overview/#error-handling) for the error response format."/> + + <@lib.response + code = "404" + dto = "AuthorizationExceptionDto" + last = true + desc = "The authenticated user is unauthorized to delete this resource. See the + [Introduction](${docsUrl}/reference/rest/overview/#error-handling) + for the error response format." + /> + + } + } + \ No newline at end of file diff --git a/engine-rest/engine-rest-openapi/src/main/templates/paths/task/{id}/comment/put.ftl b/engine-rest/engine-rest-openapi/src/main/templates/paths/task/{id}/comment/put.ftl new file mode 100644 index 00000000000..871e9c01558 --- /dev/null +++ b/engine-rest/engine-rest-openapi/src/main/templates/paths/task/{id}/comment/put.ftl @@ -0,0 +1,58 @@ +<#macro endpoint_macro docsUrl=""> + { + + <@lib.endpointInfo + id = "updateTaskComment" + tag = "Task Comment" + summary = "Update" + desc = "Updates a Comment." /> + + "parameters" : [ + + <@lib.parameter + name = "id" + location = "path" + type = "string" + required = true + last = true + desc = "The id associated of a task of a comment to be updated."/> + + ], + + <@lib.requestBody + mediaType = "application/json" + dto = "CommentDto" + requestDesc = "**Note:** Only the `id` and `message` properties will be used. Every other + property passed to this endpoint will be ignored." + examples = ['"example-1": { + "summary": "PUT /task/aTaskId/comment", + "value": { + "id": "86cd272a-23ea-22e5-8e4a-e5bded20a556", + "message": "a task comment" + } + }'] /> + + "responses" : { + + <@lib.response + code = "204" + desc = "Request successful." /> + + <@lib.response + code = "400" + dto = "ExceptionDto" + desc = "Returned if a given task id or comment id is invalid. Or the history of the engine is disabled. + See the [Introduction](${docsUrl}/reference/rest/overview/#error-handling) for the error response format."/> + + <@lib.response + code = "404" + dto = "AuthorizationExceptionDto" + last = true + desc = "The authenticated user is unauthorized to update this resource. See the + [Introduction](${docsUrl}/reference/rest/overview/#error-handling) + for the error response format."/> + + } + } + + \ No newline at end of file diff --git a/engine-rest/engine-rest-openapi/src/main/templates/paths/task/{id}/comment/{commentId}/delete.ftl b/engine-rest/engine-rest-openapi/src/main/templates/paths/task/{id}/comment/{commentId}/delete.ftl new file mode 100644 index 00000000000..65a6045a066 --- /dev/null +++ b/engine-rest/engine-rest-openapi/src/main/templates/paths/task/{id}/comment/{commentId}/delete.ftl @@ -0,0 +1,52 @@ +<#macro endpoint_macro docsUrl=""> + { + + <@lib.endpointInfo + id = "deleteTaskComment" + tag = "Task Comment" + summary = "Delete" + desc = "Removes a comment from a task by id." /> + + "parameters" : [ + + <@lib.parameter + name = "id" + location = "path" + type = "string" + required = true + desc = "The id of the task." /> + + <@lib.parameter + name = "commentId" + location = "path" + type = "string" + required = true + last = true + desc = "The id of the comment to be removed." /> + + ], + + "responses" : { + + <@lib.response + code = "204" + desc = "Request successful." /> + + <@lib.response + code = "400" + dto = "ExceptionDto" + desc = "Returned if a given task id or comment id is invalid. Or history of the engine is disabled. + See the [Introduction](${docsUrl}/reference/rest/overview/#error-handling) for the error response format."/> + + <@lib.response + code = "404" + dto = "AuthorizationExceptionDto" + last = true + desc = "The authenticated user is unauthorized to delete this resource. See the + [Introduction](${docsUrl}/reference/rest/overview/#error-handling) + for the error response format."/> + + } + } + + \ No newline at end of file diff --git a/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/sub/runtime/ProcessInstanceCommentResource.java b/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/sub/runtime/ProcessInstanceCommentResource.java index 50cb90b189d..e1a3fa2d88f 100644 --- a/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/sub/runtime/ProcessInstanceCommentResource.java +++ b/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/sub/runtime/ProcessInstanceCommentResource.java @@ -17,11 +17,14 @@ package org.camunda.bpm.engine.rest.sub.runtime; import java.util.List; - +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; - import org.camunda.bpm.engine.rest.dto.task.CommentDto; public interface ProcessInstanceCommentResource { @@ -30,4 +33,15 @@ public interface ProcessInstanceCommentResource { @Produces(MediaType.APPLICATION_JSON) List getComments(); + @DELETE + @Path("/{commentId}") + @Produces(MediaType.APPLICATION_JSON) + void deleteComment(@PathParam("commentId") String commentId); + + @PUT + @Consumes(MediaType.APPLICATION_JSON) + void updateComment(CommentDto comment); + + @DELETE + void deleteComments(); } diff --git a/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/sub/runtime/impl/ProcessInstanceCommentResourceImpl.java b/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/sub/runtime/impl/ProcessInstanceCommentResourceImpl.java index aec71d23e23..fe175b84fff 100644 --- a/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/sub/runtime/impl/ProcessInstanceCommentResourceImpl.java +++ b/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/sub/runtime/impl/ProcessInstanceCommentResourceImpl.java @@ -19,11 +19,12 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; - import javax.ws.rs.core.Response.Status; - +import org.camunda.bpm.engine.AuthorizationException; import org.camunda.bpm.engine.IdentityService; import org.camunda.bpm.engine.ProcessEngine; +import org.camunda.bpm.engine.TaskService; +import org.camunda.bpm.engine.exception.NullValueException; import org.camunda.bpm.engine.history.HistoricProcessInstance; import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.camunda.bpm.engine.impl.identity.Authentication; @@ -42,6 +43,7 @@ public ProcessInstanceCommentResourceImpl(ProcessEngine engine, String processIn this.processInstanceId = processInstanceId; } + @Override public List getComments() { if (!isHistoryEnabled()) { return Collections.emptyList(); @@ -59,6 +61,66 @@ public List getComments() { return comments; } + /** + * Deletes a comment by a given commentId + */ + @Override + public void deleteComment(String commentId) { + ensureHistoryEnabled(Status.FORBIDDEN); + ensureProcessInstanceExists(Status.NOT_FOUND); + + TaskService taskService = engine.getTaskService(); + try { + taskService.deleteProcessInstanceComment(processInstanceId, commentId); + } catch (AuthorizationException e) { + throw e; + } catch (NullValueException e) { + throw new InvalidRequestException(Status.BAD_REQUEST, e.getMessage()); + } + } + + /** + * Updates message for a given processInstanceId and commentId + */ + @Override + public void updateComment(CommentDto comment) { + ensureHistoryEnabled(Status.FORBIDDEN); + ensureProcessInstanceExists(Status.NOT_FOUND); + + TaskService taskService = engine.getTaskService(); + try { + taskService.updateProcessInstanceComment(processInstanceId, comment.getId(), comment.getMessage()); + } catch (AuthorizationException e) { + throw e; + } catch (NullValueException e) { + throw new InvalidRequestException(Status.BAD_REQUEST, e.getMessage()); + } + } + + /** + * Deletes all comments by a given processInstanceId + */ + @Override + public void deleteComments() { + ensureHistoryEnabled(Status.FORBIDDEN); + ensureProcessInstanceExists(Status.NOT_FOUND); + TaskService taskService = engine.getTaskService(); + + try { + taskService.deleteProcessInstanceComments(processInstanceId); + } catch (AuthorizationException e) { + throw e; + } catch (NullValueException e) { + throw new InvalidRequestException(Status.BAD_REQUEST, e.getMessage()); + } + } + + private void ensureHistoryEnabled(Status status) { + if (!isHistoryEnabled()) { + throw new InvalidRequestException(status, "History is not enabled"); + } + } + private boolean isHistoryEnabled() { IdentityService identityService = engine.getIdentityService(); Authentication currentAuthentication = identityService.getCurrentAuthentication(); diff --git a/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/sub/task/TaskCommentResource.java b/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/sub/task/TaskCommentResource.java index 4815f70ed85..261c6ef077d 100644 --- a/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/sub/task/TaskCommentResource.java +++ b/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/sub/task/TaskCommentResource.java @@ -16,13 +16,19 @@ */ package org.camunda.bpm.engine.rest.sub.task; -import org.camunda.bpm.engine.rest.dto.task.CommentDto; - -import javax.ws.rs.*; +import java.util.List; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriInfo; -import java.util.List; +import org.camunda.bpm.engine.rest.dto.task.CommentDto; public interface TaskCommentResource { @@ -41,4 +47,16 @@ public interface TaskCommentResource { @Produces(MediaType.APPLICATION_JSON) CommentDto createComment(@Context UriInfo uriInfo, CommentDto comment); + @DELETE + @Path("/{commentId}") + @Produces(MediaType.APPLICATION_JSON) + void deleteComment(@PathParam("commentId") String commentId); + + @PUT + @Consumes(MediaType.APPLICATION_JSON) + void updateComment(CommentDto comment); + + @DELETE + void deleteComments(); + } diff --git a/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/sub/task/impl/TaskCommentResourceImpl.java b/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/sub/task/impl/TaskCommentResourceImpl.java index 23d4aecfd3b..8a24336172c 100644 --- a/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/sub/task/impl/TaskCommentResourceImpl.java +++ b/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/sub/task/impl/TaskCommentResourceImpl.java @@ -20,14 +20,15 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; - import javax.ws.rs.HttpMethod; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; - +import org.camunda.bpm.engine.AuthorizationException; import org.camunda.bpm.engine.IdentityService; import org.camunda.bpm.engine.ProcessEngine; import org.camunda.bpm.engine.ProcessEngineException; +import org.camunda.bpm.engine.TaskService; +import org.camunda.bpm.engine.exception.NullValueException; import org.camunda.bpm.engine.history.HistoricTaskInstance; import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.camunda.bpm.engine.impl.identity.Authentication; @@ -77,6 +78,47 @@ public CommentDto getComment(String commentId) { return CommentDto.fromComment(comment); } + public void deleteComment(String commentId) { + ensureHistoryEnabled(Status.FORBIDDEN); + ensureTaskExists(Status.NOT_FOUND); + + TaskService taskService = engine.getTaskService(); + try { + taskService.deleteTaskComment(taskId, commentId); + } catch (AuthorizationException e) { + throw e; + } catch (NullValueException e) { + throw new InvalidRequestException(Status.BAD_REQUEST, e.getMessage()); + } + } + + public void updateComment(CommentDto comment) { + ensureHistoryEnabled(Status.FORBIDDEN); + ensureTaskExists(Status.NOT_FOUND); + + try { + engine.getTaskService().updateTaskComment(taskId, comment.getId(), comment.getMessage()); + } catch (AuthorizationException e) { + throw e; + } catch (NullValueException e) { + throw new InvalidRequestException(Status.BAD_REQUEST, e.getMessage()); + } + } + + public void deleteComments() { + ensureHistoryEnabled(Status.FORBIDDEN); + ensureTaskExists(Status.NOT_FOUND); + TaskService taskService = engine.getTaskService(); + + try { + taskService.deleteTaskComments(taskId); + } catch (AuthorizationException e) { + throw e; + } catch (NullValueException e) { + throw new InvalidRequestException(Status.BAD_REQUEST, e.getMessage()); + } + } + public CommentDto createComment(UriInfo uriInfo, CommentDto commentDto) { ensureHistoryEnabled(Status.FORBIDDEN); ensureTaskExists(Status.BAD_REQUEST); diff --git a/engine-rest/engine-rest/src/test/java/org/camunda/bpm/engine/rest/ProcessInstanceRestServiceInteractionTest.java b/engine-rest/engine-rest/src/test/java/org/camunda/bpm/engine/rest/ProcessInstanceRestServiceInteractionTest.java index 345be01068e..8c966c1c70b 100644 --- a/engine-rest/engine-rest/src/test/java/org/camunda/bpm/engine/rest/ProcessInstanceRestServiceInteractionTest.java +++ b/engine-rest/engine-rest/src/test/java/org/camunda/bpm/engine/rest/ProcessInstanceRestServiceInteractionTest.java @@ -17,11 +17,16 @@ package org.camunda.bpm.engine.rest; import static io.restassured.RestAssured.given; +import static org.camunda.bpm.engine.rest.helper.MockProvider.EXAMPLE_PROCESS_INSTANCE_COMMENT_FULL_MESSAGE; +import static org.camunda.bpm.engine.rest.helper.MockProvider.EXAMPLE_PROCESS_INSTANCE_COMMENT_ID; import static org.camunda.bpm.engine.rest.helper.MockProvider.EXAMPLE_TASK_ID; +import static org.camunda.bpm.engine.rest.helper.MockProvider.NON_EXISTING_ID; import static org.camunda.bpm.engine.rest.helper.MockProvider.createMockBatch; import static org.camunda.bpm.engine.rest.helper.MockProvider.createMockHistoricProcessInstance; import static org.camunda.bpm.engine.rest.util.DateTimeUtils.DATE_FORMAT_WITH_TIMEZONE; +import static org.camunda.bpm.engine.rest.util.DateTimeUtils.withTimezone; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -141,6 +146,8 @@ public class ProcessInstanceRestServiceInteractionTest extends AbstractRestServi protected static final String SINGLE_PROCESS_INSTANCE_URL = PROCESS_INSTANCE_URL + "/{id}"; protected static final String PROCESS_INSTANCE_VARIABLES_URL = SINGLE_PROCESS_INSTANCE_URL + "/variables"; protected static final String PROCESS_INSTANCE_COMMENTS_URL = SINGLE_PROCESS_INSTANCE_URL + "/comment"; + protected static final String SINGLE_PROCESS_INSTANCE_SINGLE_COMMENT_URL = + PROCESS_INSTANCE_COMMENTS_URL + "/{commentId}"; protected static final String DELETE_PROCESS_INSTANCES_ASYNC_URL = PROCESS_INSTANCE_URL + "/delete"; protected static final String DELETE_PROCESS_INSTANCES_ASYNC_HIST_QUERY_URL = PROCESS_INSTANCE_URL + "/delete-historic-query-based"; protected static final String SET_JOB_RETRIES_ASYNC_URL = PROCESS_INSTANCE_URL + "/job-retries"; @@ -838,6 +845,340 @@ public void testGetProcessInstanceCommentsWithHistoryDisabled() { .get(PROCESS_INSTANCE_COMMENTS_URL); } + @Test + public void testDeleteInstanceCommentThrowsAuthorizationException() { + String message = "expected exception"; + doThrow(new AuthorizationException(message)).when(taskServiceMock) + .deleteProcessInstanceComment(EXAMPLE_PROCESS_INSTANCE_ID, EXAMPLE_PROCESS_INSTANCE_COMMENT_ID); + + given().pathParam("id", EXAMPLE_PROCESS_INSTANCE_ID) + .pathParam("commentId", EXAMPLE_PROCESS_INSTANCE_COMMENT_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.FORBIDDEN.getStatusCode()) + .when() + .delete(SINGLE_PROCESS_INSTANCE_SINGLE_COMMENT_URL); + } + + @Test + public void testDeleteInstanceComment() { + mockHistoryFull(); + + given().pathParam("id", EXAMPLE_PROCESS_INSTANCE_ID) + .pathParam("commentId", EXAMPLE_PROCESS_INSTANCE_COMMENT_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.NO_CONTENT.getStatusCode()) + .when() + .delete(SINGLE_PROCESS_INSTANCE_SINGLE_COMMENT_URL); + + verify(taskServiceMock).deleteProcessInstanceComment(EXAMPLE_PROCESS_INSTANCE_ID, EXAMPLE_PROCESS_INSTANCE_COMMENT_ID); + } + + @Test + public void testDeleteInstanceCommentWithHistoryDisabled() { + mockHistoryDisabled(); + + given().pathParam("id", EXAMPLE_PROCESS_INSTANCE_ID) + .pathParam("commentId", EXAMPLE_PROCESS_INSTANCE_COMMENT_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.FORBIDDEN.getStatusCode()) + .body(containsString("History is not enabled")) + .when() + .delete(SINGLE_PROCESS_INSTANCE_SINGLE_COMMENT_URL); + } + + @Test + public void testDeleteInstanceCommentForNonExistingCommentId() { + mockHistoryFull(); + doThrow(new NullValueException()).when(taskServiceMock) + .deleteProcessInstanceComment(EXAMPLE_PROCESS_INSTANCE_ID, NON_EXISTING_ID); + + given().pathParam("id", EXAMPLE_PROCESS_INSTANCE_ID) + .pathParam("commentId", NON_EXISTING_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.BAD_REQUEST.getStatusCode()) + .contentType(ContentType.JSON) + .when() + .delete(SINGLE_PROCESS_INSTANCE_SINGLE_COMMENT_URL); + } + + @Test + public void testDeleteInstanceCommentForNonExistingCommentIdWithHistoryDisabled() { + mockHistoryDisabled(); + + given().pathParam("id", EXAMPLE_PROCESS_INSTANCE_ID) + .pathParam("commentId", NON_EXISTING_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.FORBIDDEN.getStatusCode()) + .body(containsString("History is not enabled")) + .when() + .delete(SINGLE_PROCESS_INSTANCE_SINGLE_COMMENT_URL); + } + + @Test + public void testDeleteInstanceCommentForNonExistingProcessInstance() { + mockHistoryFull(); + historicProcessInstanceQueryMock = mock(HistoricProcessInstanceQuery.class); + when(historyServiceMock.createHistoricProcessInstanceQuery()).thenReturn(historicProcessInstanceQueryMock); + when(historicProcessInstanceQueryMock.processInstanceId(eq(NON_EXISTING_ID))).thenReturn( + historicProcessInstanceQueryMock); + when(historicProcessInstanceQueryMock.singleResult()).thenReturn(null); + + given().pathParam("id", NON_EXISTING_ID) + .pathParam("commentId", EXAMPLE_PROCESS_INSTANCE_COMMENT_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.NOT_FOUND.getStatusCode()) + .body(containsString("No process instance found for id " + NON_EXISTING_ID)) + .when() + .delete(SINGLE_PROCESS_INSTANCE_SINGLE_COMMENT_URL); + } + + @Test + public void testDeleteInstanceCommentForNonExistingWithHistoryDisabled() { + mockHistoryDisabled(); + + given().pathParam("id", NON_EXISTING_ID) + .pathParam("commentId", EXAMPLE_PROCESS_INSTANCE_COMMENT_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.FORBIDDEN.getStatusCode()) + .body(containsString("History is not enabled")) + .when() + .delete(SINGLE_PROCESS_INSTANCE_SINGLE_COMMENT_URL); + } + + @Test + public void testDeleteProcessInstanceCommentsThrowsAuthorizationException() { + String message = "expected exception"; + doThrow(new AuthorizationException(message)).when(taskServiceMock) + .deleteProcessInstanceComments(MockProvider.EXAMPLE_TASK_ID); + + given().pathParam("id", EXAMPLE_PROCESS_INSTANCE_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.FORBIDDEN.getStatusCode()) + .when() + .delete(PROCESS_INSTANCE_COMMENTS_URL); + } + + @Test + public void testDeleteProcessInstanceComments() { + mockHistoryFull(); + given().pathParam("id", EXAMPLE_PROCESS_INSTANCE_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.NO_CONTENT.getStatusCode()) + .when() + .delete(PROCESS_INSTANCE_COMMENTS_URL); + + verify(taskServiceMock).deleteProcessInstanceComments(EXAMPLE_PROCESS_INSTANCE_ID); + } + + @Test + public void testDeleteProcessInstanceCommentsWithHistoryDisabled() { + mockHistoryDisabled(); + + given().pathParam("id", EXAMPLE_PROCESS_INSTANCE_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.FORBIDDEN.getStatusCode()) + .body(containsString("History is not enabled")) + .when() + .delete(PROCESS_INSTANCE_COMMENTS_URL); + } + + @Test + public void testDeleteProcessInstanceCommentsForNonExisting() { + mockHistoryFull(); + historicProcessInstanceQueryMock = mock(HistoricProcessInstanceQuery.class); + when(historyServiceMock.createHistoricProcessInstanceQuery()).thenReturn(historicProcessInstanceQueryMock); + when(historicProcessInstanceQueryMock.processInstanceId(eq(NON_EXISTING_ID))).thenReturn( + historicProcessInstanceQueryMock); + when(historicProcessInstanceQueryMock.singleResult()).thenReturn(null); + + given().pathParam("id", NON_EXISTING_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.NOT_FOUND.getStatusCode()) + .body(containsString("No process instance found for id " + NON_EXISTING_ID)) + .when() + .delete(PROCESS_INSTANCE_COMMENTS_URL); + } + + @Test + public void testDeleteProcessInstanceCommentsForNonExistingWithHistoryDisabled() { + mockHistoryDisabled(); + + given().pathParam("id", NON_EXISTING_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.FORBIDDEN.getStatusCode()) + .body(containsString("History is not enabled")) + .when() + .delete(PROCESS_INSTANCE_COMMENTS_URL); + } + + @Test + public void testUpdateProcessInstanceCommentCommentIdNull() { + mockHistoryFull(); + + String message = "expected exception"; + doThrow(new NullValueException(message)).when(taskServiceMock) + .updateProcessInstanceComment(EXAMPLE_PROCESS_INSTANCE_ID, null, EXAMPLE_PROCESS_INSTANCE_COMMENT_FULL_MESSAGE); + + Map json = new HashMap<>(); + + json.put("id", null); + json.put("message", EXAMPLE_PROCESS_INSTANCE_COMMENT_FULL_MESSAGE); + + given().pathParam("id", EXAMPLE_PROCESS_INSTANCE_ID) + .body(json) + .contentType(ContentType.JSON) + .header("accept", MediaType.APPLICATION_JSON) + .expect() + .statusCode(Status.BAD_REQUEST.getStatusCode()) + .when() + .put(PROCESS_INSTANCE_COMMENTS_URL); + } + + @Test + public void testUpdateProcessInstanceCommentMessageIsNull() { + mockHistoryFull(); + + String message = "expected exception"; + doThrow(new NullValueException(message)).when(taskServiceMock) + .updateProcessInstanceComment(EXAMPLE_PROCESS_INSTANCE_ID, EXAMPLE_PROCESS_INSTANCE_COMMENT_ID, null); + + Map json = new HashMap<>(); + + json.put("id", EXAMPLE_PROCESS_INSTANCE_COMMENT_ID); + json.put("message", null); + + given().pathParam("id", EXAMPLE_PROCESS_INSTANCE_ID) + .body(json) + .contentType(ContentType.JSON) + .header("accept", MediaType.APPLICATION_JSON) + .expect() + .statusCode(Status.BAD_REQUEST.getStatusCode()) + .when() + .put(PROCESS_INSTANCE_COMMENTS_URL); + } + + @Test + public void testUpdateProcessInstanceComment() { + mockHistoryFull(); + Map json = new HashMap<>(); + + json.put("id", EXAMPLE_PROCESS_INSTANCE_COMMENT_ID); + json.put("message", EXAMPLE_PROCESS_INSTANCE_COMMENT_FULL_MESSAGE); + + given().pathParam("id", EXAMPLE_PROCESS_INSTANCE_ID) + .body(json) + .contentType(ContentType.JSON) + .header("accept", MediaType.APPLICATION_JSON) + .expect() + .statusCode(Status.NO_CONTENT.getStatusCode()) + .when() + .put(PROCESS_INSTANCE_COMMENTS_URL); + + verify(taskServiceMock).updateProcessInstanceComment(EXAMPLE_PROCESS_INSTANCE_ID, + EXAMPLE_PROCESS_INSTANCE_COMMENT_ID, EXAMPLE_PROCESS_INSTANCE_COMMENT_FULL_MESSAGE); + } + + @Test + public void testUpdateProcessInstanceCommentExtraProperties() { + mockHistoryFull(); + + Map json = new HashMap<>(); + //Only id and message are used + json.put("id", EXAMPLE_PROCESS_INSTANCE_COMMENT_ID); + json.put("userId", "anyUserId"); + json.put("time", withTimezone("2014-01-01T00:00:00")); + json.put("message", EXAMPLE_PROCESS_INSTANCE_COMMENT_FULL_MESSAGE); + json.put("removalTime", withTimezone("2014-05-01T00:00:00")); + json.put("processInstanceId", MockProvider.EXAMPLE_PROCESS_INSTANCE_ID); + + given().pathParam("id", EXAMPLE_PROCESS_INSTANCE_ID) + .body(json) + .contentType(ContentType.JSON) + .header("accept", MediaType.APPLICATION_JSON) + .expect() + .statusCode(Status.NO_CONTENT.getStatusCode()) + .when() + .put(PROCESS_INSTANCE_COMMENTS_URL); + + verify(taskServiceMock).updateProcessInstanceComment(EXAMPLE_PROCESS_INSTANCE_ID, + EXAMPLE_PROCESS_INSTANCE_COMMENT_ID, EXAMPLE_PROCESS_INSTANCE_COMMENT_FULL_MESSAGE); + } + + @Test + public void testUpdateProcessInstanceCommentProcessInstanceIdNotFound() { + mockHistoryFull(); + historicProcessInstanceQueryMock = mock(HistoricProcessInstanceQuery.class); + when(historyServiceMock.createHistoricProcessInstanceQuery()).thenReturn(historicProcessInstanceQueryMock); + when(historicProcessInstanceQueryMock.processInstanceId(eq(NON_EXISTING_ID))).thenReturn( + historicProcessInstanceQueryMock); + when(historicProcessInstanceQueryMock.singleResult()).thenReturn(null); + + Map json = new HashMap<>(); + + json.put("id", EXAMPLE_PROCESS_INSTANCE_ID); + json.put("message", EXAMPLE_PROCESS_INSTANCE_COMMENT_FULL_MESSAGE); + + given().pathParam("id", NON_EXISTING_ID) + .body(json) + .contentType(ContentType.JSON) + .header("accept", MediaType.APPLICATION_JSON) + .expect() + .statusCode(Status.NOT_FOUND.getStatusCode()) + .contentType(ContentType.JSON) + .body("type", equalTo(InvalidRequestException.class.getSimpleName())) + .body(containsString("No process instance found for id " + NON_EXISTING_ID)) + .when() + .put(PROCESS_INSTANCE_COMMENTS_URL); + } + + @Test + public void testUpdateProcessInstanceCommentThrowsAuthorizationException() { + mockHistoryFull(); + String message = "expected exception"; + doThrow(new AuthorizationException(message)).when(taskServiceMock) + .updateProcessInstanceComment(EXAMPLE_PROCESS_INSTANCE_ID, EXAMPLE_PROCESS_INSTANCE_COMMENT_ID, + EXAMPLE_PROCESS_INSTANCE_COMMENT_FULL_MESSAGE); + + Map json = new HashMap<>(); + json.put("id", EXAMPLE_PROCESS_INSTANCE_COMMENT_ID); + json.put("message", EXAMPLE_PROCESS_INSTANCE_COMMENT_FULL_MESSAGE); + + given().pathParam("id", EXAMPLE_PROCESS_INSTANCE_ID) + .body(json) + .contentType(ContentType.JSON) + .header("accept", MediaType.APPLICATION_JSON) + .expect() + .statusCode(Status.FORBIDDEN.getStatusCode()) + .contentType(ContentType.JSON) + .when() + .put(PROCESS_INSTANCE_COMMENTS_URL); + + } + @Test public void testGetFileVariable() { String variableKey = "aVariableKey"; diff --git a/engine-rest/engine-rest/src/test/java/org/camunda/bpm/engine/rest/TaskRestServiceInteractionTest.java b/engine-rest/engine-rest/src/test/java/org/camunda/bpm/engine/rest/TaskRestServiceInteractionTest.java index 34383b7ddac..a9d8670e89a 100755 --- a/engine-rest/engine-rest/src/test/java/org/camunda/bpm/engine/rest/TaskRestServiceInteractionTest.java +++ b/engine-rest/engine-rest/src/test/java/org/camunda/bpm/engine/rest/TaskRestServiceInteractionTest.java @@ -95,6 +95,7 @@ import org.camunda.bpm.engine.TaskService; import org.camunda.bpm.engine.exception.NotFoundException; import org.camunda.bpm.engine.exception.NotValidException; +import org.camunda.bpm.engine.exception.NullValueException; import org.camunda.bpm.engine.form.TaskFormData; import org.camunda.bpm.engine.history.HistoricTaskInstance; import org.camunda.bpm.engine.history.HistoricTaskInstanceQuery; @@ -146,8 +147,6 @@ import io.restassured.http.ContentType; import io.restassured.path.json.JsonPath; import io.restassured.response.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class TaskRestServiceInteractionTest extends AbstractRestServiceTest { @@ -3760,6 +3759,313 @@ public void testHandleBpmnEscalationMissingEscalationCode() { .post(HANDLE_BPMN_ESCALATION_URL); } + @Test + public void testDeleteTaskCommentThrowsAuthorizationException() { + String message = "expected exception"; + doThrow(new AuthorizationException(message)).when(taskServiceMock) + .deleteTaskComment(MockProvider.EXAMPLE_TASK_ID, EXAMPLE_TASK_COMMENT_ID); + + given().pathParam("id", MockProvider.EXAMPLE_TASK_ID) + .pathParam("commentId", EXAMPLE_TASK_COMMENT_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.FORBIDDEN.getStatusCode()) + .when() + .delete(SINGLE_TASK_SINGLE_COMMENT_URL); + } + + @Test + public void testDeleteTaskComment() { + given().pathParam("id", MockProvider.EXAMPLE_TASK_ID) + .pathParam("commentId", EXAMPLE_TASK_COMMENT_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.NO_CONTENT.getStatusCode()) + .when() + .delete(SINGLE_TASK_SINGLE_COMMENT_URL); + verify(taskServiceMock).deleteTaskComment(EXAMPLE_TASK_ID, EXAMPLE_TASK_COMMENT_ID); + } + + @Test + public void testDeleteTaskCommentWithHistoryDisabled() { + mockHistoryDisabled(); + + given().pathParam("id", MockProvider.EXAMPLE_TASK_ID) + .pathParam("commentId", EXAMPLE_TASK_COMMENT_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.FORBIDDEN.getStatusCode()) + .body(containsString("History is not enabled")) + .when() + .delete(SINGLE_TASK_SINGLE_COMMENT_URL); + } + + @Test + public void testDeleteTaskCommentForNonExistingCommentId() { + doThrow(new NullValueException()).when(taskServiceMock).deleteTaskComment(EXAMPLE_TASK_ID, NON_EXISTING_ID); + + given().pathParam("id", EXAMPLE_TASK_ID) + .pathParam("commentId", NON_EXISTING_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.BAD_REQUEST.getStatusCode()) + .contentType(ContentType.JSON) + .when() + .delete(SINGLE_TASK_SINGLE_COMMENT_URL); + } + + @Test + public void testDeleteTaskCommentForNonExistingCommentIdWithHistoryDisabled() { + mockHistoryDisabled(); + + given().pathParam("id", EXAMPLE_TASK_ID) + .pathParam("commentId", NON_EXISTING_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.FORBIDDEN.getStatusCode()) + .body(containsString("History is not enabled")) + .when() + .delete(SINGLE_TASK_SINGLE_COMMENT_URL); + } + + @Test + public void testDeleteTaskCommentForNonExistingTask() { + when(historicTaskInstanceQueryMock.taskId(NON_EXISTING_ID)).thenReturn(historicTaskInstanceQueryMock); + when(historicTaskInstanceQueryMock.singleResult()).thenReturn(null); + + given().pathParam("id", NON_EXISTING_ID) + .pathParam("commentId", EXAMPLE_TASK_COMMENT_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.NOT_FOUND.getStatusCode()) + .body(containsString("No task found for task id " + NON_EXISTING_ID)) + .when() + .delete(SINGLE_TASK_SINGLE_COMMENT_URL); + } + + @Test + public void testDeleteTaskCommentForNonExistingTaskWithHistoryDisabled() { + mockHistoryDisabled(); + + given().pathParam("id", NON_EXISTING_ID) + .pathParam("commentId", EXAMPLE_TASK_COMMENT_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.FORBIDDEN.getStatusCode()) + .body(containsString("History is not enabled")) + .when() + .delete(SINGLE_TASK_SINGLE_COMMENT_URL); + } + + @Test + public void testDeleteTaskCommentsThrowsAuthorizationException() { + String message = "expected exception"; + doThrow(new AuthorizationException(message)).when(taskServiceMock).deleteTaskComments(MockProvider.EXAMPLE_TASK_ID); + + given().pathParam("id", MockProvider.EXAMPLE_TASK_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.FORBIDDEN.getStatusCode()) + .when() + .delete(SINGLE_TASK_COMMENTS_URL); + } + + @Test + public void testDeleteTaskComments() { + given().pathParam("id", MockProvider.EXAMPLE_TASK_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.NO_CONTENT.getStatusCode()) + .when() + .delete(SINGLE_TASK_COMMENTS_URL); + verify(taskServiceMock).deleteTaskComments(EXAMPLE_TASK_ID); + } + + @Test + public void testDeleteTaskCommentsWithHistoryDisabled() { + mockHistoryDisabled(); + + given().pathParam("id", MockProvider.EXAMPLE_TASK_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.FORBIDDEN.getStatusCode()) + .body(containsString("History is not enabled")) + .when() + .delete(SINGLE_TASK_COMMENTS_URL); + } + + @Test + public void testDeleteTaskCommentsForNonExistingTask() { + when(historicTaskInstanceQueryMock.taskId(NON_EXISTING_ID)).thenReturn(historicTaskInstanceQueryMock); + when(historicTaskInstanceQueryMock.singleResult()).thenReturn(null); + + given().pathParam("id", NON_EXISTING_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.NOT_FOUND.getStatusCode()) + .body(containsString("No task found for task id " + NON_EXISTING_ID)) + .when() + .delete(SINGLE_TASK_COMMENTS_URL); + } + + @Test + public void testDeleteTaskCommentsForNonExistingTaskWithHistoryDisabled() { + mockHistoryDisabled(); + + given().pathParam("id", NON_EXISTING_ID) + .header("accept", MediaType.APPLICATION_JSON) + .then() + .expect() + .statusCode(Status.FORBIDDEN.getStatusCode()) + .body(containsString("History is not enabled")) + .when() + .delete(SINGLE_TASK_COMMENTS_URL); + } + + @Test + public void testUpdateTaskComment() { + Map json = new HashMap<>(); + + json.put("id", EXAMPLE_TASK_COMMENT_ID); + json.put("message", EXAMPLE_TASK_COMMENT_FULL_MESSAGE); + + given().pathParam("id", EXAMPLE_TASK_ID) + .body(json) + .contentType(ContentType.JSON) + .header("accept", MediaType.APPLICATION_JSON) + .expect() + .statusCode(Status.NO_CONTENT.getStatusCode()) + .when() + .put(SINGLE_TASK_COMMENTS_URL); + + verify(taskServiceMock).updateTaskComment(EXAMPLE_TASK_ID, EXAMPLE_TASK_COMMENT_ID, + EXAMPLE_TASK_COMMENT_FULL_MESSAGE); + } + + @Test + public void testUpdateTaskCommentCommentIdNull() { + String message = "expected exception"; + doThrow(new NullValueException(message)).when(taskServiceMock) + .updateTaskComment(EXAMPLE_TASK_ID, null, EXAMPLE_TASK_COMMENT_FULL_MESSAGE); + + Map json = new HashMap<>(); + + json.put("id", null); + json.put("message", EXAMPLE_TASK_COMMENT_FULL_MESSAGE); + + given().pathParam("id", EXAMPLE_TASK_ID) + .body(json) + .contentType(ContentType.JSON) + .header("accept", MediaType.APPLICATION_JSON) + .expect() + .statusCode(Status.BAD_REQUEST.getStatusCode()) + .when() + .put(SINGLE_TASK_COMMENTS_URL); + } + + @Test + public void testUpdateTaskCommentMessageIdNull() { + String message = "expected exception"; + doThrow(new NullValueException(message)).when(taskServiceMock) + .updateTaskComment(EXAMPLE_TASK_ID, EXAMPLE_TASK_COMMENT_ID, null); + + Map json = new HashMap<>(); + + json.put("id", EXAMPLE_TASK_COMMENT_ID); + json.put("message", null); + + given().pathParam("id", EXAMPLE_TASK_ID) + .body(json) + .contentType(ContentType.JSON) + .header("accept", MediaType.APPLICATION_JSON) + .expect() + .statusCode(Status.BAD_REQUEST.getStatusCode()) + .when() + .put(SINGLE_TASK_COMMENTS_URL); + } + + @Test + public void testUpdateTaskCommentExtraProperties() { + Map json = new HashMap<>(); + + //Only id and message are used + json.put("id", EXAMPLE_TASK_COMMENT_ID); + json.put("userId", "anyUserId"); + json.put("time", withTimezone("2014-01-01T00:00:00")); + json.put("message", EXAMPLE_TASK_COMMENT_FULL_MESSAGE); + json.put("removalTime", withTimezone("2014-05-01T00:00:00")); + json.put("rootProcessInstanceId", EXAMPLE_TASK_COMMENT_ROOT_PROCESS_INSTANCE_ID); + json.put("processInstanceId", MockProvider.EXAMPLE_PROCESS_INSTANCE_ID); + + given().pathParam("id", EXAMPLE_TASK_ID) + .body(json) + .contentType(ContentType.JSON) + .header("accept", MediaType.APPLICATION_JSON) + .expect() + .statusCode(Status.NO_CONTENT.getStatusCode()) + .when() + .put(SINGLE_TASK_COMMENTS_URL); + + verify(taskServiceMock).updateTaskComment(EXAMPLE_TASK_ID, EXAMPLE_TASK_COMMENT_ID, + EXAMPLE_TASK_COMMENT_FULL_MESSAGE); + } + + @Test + public void testUpdateTaskCommentTaskIdNotFound() { + when(historicTaskInstanceQueryMock.taskId(NON_EXISTING_ID)).thenReturn(historicTaskInstanceQueryMock); + when(historicTaskInstanceQueryMock.singleResult()).thenReturn(null); + + Map json = new HashMap<>(); + + json.put("id", EXAMPLE_TASK_COMMENT_ID); + json.put("message", EXAMPLE_TASK_COMMENT_FULL_MESSAGE); + + given().pathParam("id", NON_EXISTING_ID) + .body(json) + .contentType(ContentType.JSON) + .header("accept", MediaType.APPLICATION_JSON) + .expect() + .statusCode(Status.NOT_FOUND.getStatusCode()) + .contentType(ContentType.JSON) + .body("type", equalTo(InvalidRequestException.class.getSimpleName())) + .body(containsString("No task found for task id " + NON_EXISTING_ID)) + .when() + .put(SINGLE_TASK_COMMENTS_URL); + } + + @Test + public void testUpdateTaskCommentThrowsAuthorizationException() { + String message = "expected exception"; + doThrow(new AuthorizationException(message)).when(taskServiceMock) + .updateTaskComment(EXAMPLE_TASK_ID, EXAMPLE_TASK_COMMENT_ID, EXAMPLE_TASK_COMMENT_FULL_MESSAGE); + when(historicTaskInstanceQueryMock.taskId(EXAMPLE_TASK_ID)).thenReturn(historicTaskInstanceQueryMock); + + Map json = new HashMap<>(); + json.put("id", EXAMPLE_TASK_COMMENT_ID); + json.put("message", EXAMPLE_TASK_COMMENT_FULL_MESSAGE); + + given().pathParam("id", EXAMPLE_TASK_ID) + .body(json) + .contentType(ContentType.JSON) + .header("accept", MediaType.APPLICATION_JSON) + .expect() + .statusCode(Status.FORBIDDEN.getStatusCode()) + .contentType(ContentType.JSON) + .when() + .put(SINGLE_TASK_COMMENTS_URL); + } + @SuppressWarnings({ "rawtypes", "unchecked" }) private void verifyTaskComments(List mockTaskComments, Response response) { List list = response.as(List.class); diff --git a/engine-rest/engine-rest/src/test/java/org/camunda/bpm/engine/rest/helper/MockProvider.java b/engine-rest/engine-rest/src/test/java/org/camunda/bpm/engine/rest/helper/MockProvider.java index 09d82eb3018..18e8fedb054 100644 --- a/engine-rest/engine-rest/src/test/java/org/camunda/bpm/engine/rest/helper/MockProvider.java +++ b/engine-rest/engine-rest/src/test/java/org/camunda/bpm/engine/rest/helper/MockProvider.java @@ -208,6 +208,11 @@ public abstract class MockProvider { public static final String EXAMPLE_TASK_COMMENT_TIME = withTimezone("2014-04-24T14:10:44"); public static final String EXAMPLE_TASK_COMMENT_ROOT_PROCESS_INSTANCE_ID = "aRootProcInstId"; + //process instance comment + public static final String EXAMPLE_PROCESS_INSTANCE_COMMENT_ID = "aProcessInstanceCommentId"; + public static final String EXAMPLE_PROCESS_INSTANCE_COMMENT_FULL_MESSAGE = "aProcessInstanceCommentFullMessage"; + + // task attachment public static final String EXAMPLE_TASK_ATTACHMENT_ID = "aTaskAttachmentId"; public static final String EXAMPLE_TASK_ATTACHMENT_NAME = "aTaskAttachmentName"; diff --git a/engine/src/main/java/org/camunda/bpm/engine/EntityTypes.java b/engine/src/main/java/org/camunda/bpm/engine/EntityTypes.java index 07850a5ec8f..967a087f4cf 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/EntityTypes.java +++ b/engine/src/main/java/org/camunda/bpm/engine/EntityTypes.java @@ -57,4 +57,5 @@ public class EntityTypes { public static final String OPERATION_LOG = "OperationLog"; public static final String INCIDENT = "Incident"; public static final String SYSTEM = "System"; + public static final String COMMENT = "Comment"; } diff --git a/engine/src/main/java/org/camunda/bpm/engine/TaskService.java b/engine/src/main/java/org/camunda/bpm/engine/TaskService.java index 36ec40b3bbd..8d9d60194b4 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/TaskService.java +++ b/engine/src/main/java/org/camunda/bpm/engine/TaskService.java @@ -1107,6 +1107,80 @@ public interface TaskService { /** Creates a comment to a task and/or process instance and returns the comment. */ Comment createComment(String taskId, String processInstanceId, String message); + /** + * Deletes a comment of a given taskId and commentId + * + * @param taskId id of a task of a comment that is intended to be deleted + * @param commentId id of a comment that is intended to be deleted + * @throws BadUserRequestException if taskId is null + * @throws NullValueException if no task with the given id exists + * @throws AuthorizationException if the user hasn't any of {@link Permissions#UPDATE}, {@link Permissions#TASK_WORK} permissions on {@link Resources#TASK} + * or no {@link Permissions#UPDATE_TASK}, {@link Permissions#TASK_WORK} permissions on {@link Resources#PROCESS_DEFINITION} + * (if the task is part of a running process instance). + */ + void deleteTaskComment(String taskId, String commentId); + + /** + * Deletes a comment by a given processInstanceId and commentId + * + * @param processInstanceId id of a processInstance of a comment that is intended to be deleted + * @param commentId id of a comment that is intended to be deleted + * @throws BadUserRequestException if processInstanceId is null + * @throws NullValueException if no process instance with the given id exists + * @throws AuthorizationException if the user hasn't any of {@link Permissions#UPDATE} permission on {@link Resources#PROCESS_INSTANCE} + * or no {@link Permissions#UPDATE_INSTANCE} permission on {@link Resources#PROCESS_DEFINITION} + */ + void deleteProcessInstanceComment(String processInstanceId, String commentId); + + /** + * Deletes all comments by a given taskId + * + * @param taskId id of a task of all comments that are intended to be deleted + * @throws BadUserRequestException if taskId is null + * @throws NullValueException if no task with the given id exists + * @throws AuthorizationException if the user hasn't any of {@link Permissions#UPDATE}, {@link Permissions#TASK_WORK} permissions on {@link Resources#TASK} + * or no {@link Permissions#UPDATE_TASK}, {@link Permissions#TASK_WORK} permissions on {@link Resources#PROCESS_DEFINITION} + * (if the task is part of a running process instance). + */ + void deleteTaskComments(String taskId); + + /** + * Deletes all comments by a given processInstanceId + * + * @param processInstanceId id of a process instance of comments that are intended to be deleted + * @throws BadUserRequestException if processInstanceId is null + * @throws NullValueException if no process instance with the given id exists + * @throws AuthorizationException if the user hasn't any of {@link Permissions#UPDATE} permission on {@link Resources#PROCESS_INSTANCE} + * or no {@link Permissions#UPDATE_INSTANCE} permission on {@link Resources#PROCESS_DEFINITION} + */ + void deleteProcessInstanceComments(String processInstanceId); + + /** + * Updates a comment on a given task ID + * + * @param taskId id of a task of a comment that is intended to be updated + * @param commentId id of a comment that is intended to be updated + * @param message new message that needs to be updated + * @throws NullValueException if no task or comment are found with the given ids + * @throws AuthorizationException if the user hasn't any of {@link Permissions#UPDATE}, {@link Permissions#TASK_WORK} permissions on {@link Resources#TASK} + * or no {@link Permissions#UPDATE_TASK}, {@link Permissions#TASK_WORK} permissions on {@link Resources#PROCESS_DEFINITION} + * (if the task is part of a running process instance). + */ + void updateTaskComment(String taskId, String commentId, String message); + + /** + * Updates comment on a given processInstance ID + * + * @param processInstanceId id of a process instance of a comment that is intended to be updated + * @param commentId id of a comment that is intended to be updated + * @param message new message that needs to be updated + * @throws NullValueException if no process instance or comment are found with the given ids + * @throws AuthorizationException if the user hasn't any of {@link Permissions#UPDATE}, {@link Permissions#TASK_WORK} permissions on {@link Resources#TASK} + * or no {@link Permissions#UPDATE_TASK}, {@link Permissions#TASK_WORK} permissions on {@link Resources#PROCESS_DEFINITION} + * (if the task is part of a running process instance). + */ + void updateProcessInstanceComment(String processInstanceId, String commentId, String message); + /** The comments related to the given task. */ List getTaskComments(String taskId); diff --git a/engine/src/main/java/org/camunda/bpm/engine/history/UserOperationLogEntry.java b/engine/src/main/java/org/camunda/bpm/engine/history/UserOperationLogEntry.java index 920c7227ad4..5f4f425ca5b 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/history/UserOperationLogEntry.java +++ b/engine/src/main/java/org/camunda/bpm/engine/history/UserOperationLogEntry.java @@ -122,6 +122,9 @@ public interface UserOperationLogEntry { String OPERATION_TYPE_SET_VARIABLE = "SetVariable"; String OPERATION_TYPE_SET_VARIABLES = "SetVariables"; + String OPERATION_TYPE_UPDATE_COMMENT = "UpdateComment"; + String OPERATION_TYPE_DELETE_COMMENT = "DeleteComment"; + String OPERATION_TYPE_REMOVE_VARIABLE = "RemoveVariable"; String OPERATION_TYPE_MODIFY_VARIABLE = "ModifyVariable"; diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/TaskServiceImpl.java b/engine/src/main/java/org/camunda/bpm/engine/impl/TaskServiceImpl.java index 7a627ba6279..a4f7a929829 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/TaskServiceImpl.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/TaskServiceImpl.java @@ -40,7 +40,9 @@ import org.camunda.bpm.engine.impl.cmd.DelegateTaskCmd; import org.camunda.bpm.engine.impl.cmd.DeleteAttachmentCmd; import org.camunda.bpm.engine.impl.cmd.DeleteGroupIdentityLinkCmd; +import org.camunda.bpm.engine.impl.cmd.DeleteProcessInstanceCommentCmd; import org.camunda.bpm.engine.impl.cmd.DeleteTaskCmd; +import org.camunda.bpm.engine.impl.cmd.DeleteTaskCommentCmd; import org.camunda.bpm.engine.impl.cmd.DeleteUserIdentityLinkCmd; import org.camunda.bpm.engine.impl.cmd.GetAttachmentCmd; import org.camunda.bpm.engine.impl.cmd.GetAttachmentContentCmd; @@ -71,6 +73,7 @@ import org.camunda.bpm.engine.impl.cmd.SetTaskOwnerCmd; import org.camunda.bpm.engine.impl.cmd.SetTaskPriorityCmd; import org.camunda.bpm.engine.impl.cmd.SetTaskVariablesCmd; +import org.camunda.bpm.engine.impl.cmd.UpdateCommentCmd; import org.camunda.bpm.engine.impl.util.ExceptionUtil; import org.camunda.bpm.engine.task.Attachment; import org.camunda.bpm.engine.task.Comment; @@ -380,6 +383,30 @@ public Comment createComment(String taskId, String processInstance, String messa return commandExecutor.execute(new AddCommentCmd(taskId, processInstance, message)); } + public void deleteTaskComment(String taskId, String commentId) { + commandExecutor.execute(new DeleteTaskCommentCmd(taskId, commentId)); + } + + public void deleteProcessInstanceComment(String processInstanceId, String commentId) { + commandExecutor.execute(new DeleteProcessInstanceCommentCmd(processInstanceId, commentId)); + } + + public void deleteTaskComments(String taskId) { + commandExecutor.execute(new DeleteTaskCommentCmd(taskId)); + } + + public void deleteProcessInstanceComments(String processInstanceId) { + commandExecutor.execute(new DeleteProcessInstanceCommentCmd(processInstanceId)); + } + + public void updateTaskComment(String taskId, String commentId, String message) { + commandExecutor.execute(new UpdateCommentCmd(taskId, null, commentId, message)); + } + + public void updateProcessInstanceComment(String processInstanceId, String commentId, String message) { + commandExecutor.execute(new UpdateCommentCmd(null, processInstanceId, commentId, message)); + } + public List getTaskComments(String taskId) { return commandExecutor.execute(new GetTaskCommentsCmd(taskId)); } diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/cfg/auth/AuthorizationCommandChecker.java b/engine/src/main/java/org/camunda/bpm/engine/impl/cfg/auth/AuthorizationCommandChecker.java index a1900be62ca..649b2e06344 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/cfg/auth/AuthorizationCommandChecker.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/cfg/auth/AuthorizationCommandChecker.java @@ -16,6 +16,36 @@ */ package org.camunda.bpm.engine.impl.cfg.auth; +import static org.camunda.bpm.engine.authorization.Authorization.ANY; +import static org.camunda.bpm.engine.authorization.Permissions.CREATE; +import static org.camunda.bpm.engine.authorization.Permissions.CREATE_INSTANCE; +import static org.camunda.bpm.engine.authorization.Permissions.DELETE; +import static org.camunda.bpm.engine.authorization.Permissions.DELETE_HISTORY; +import static org.camunda.bpm.engine.authorization.Permissions.DELETE_INSTANCE; +import static org.camunda.bpm.engine.authorization.Permissions.READ; +import static org.camunda.bpm.engine.authorization.Permissions.READ_HISTORY; +import static org.camunda.bpm.engine.authorization.Permissions.READ_INSTANCE; +import static org.camunda.bpm.engine.authorization.Permissions.READ_TASK; +import static org.camunda.bpm.engine.authorization.Permissions.TASK_ASSIGN; +import static org.camunda.bpm.engine.authorization.Permissions.TASK_WORK; +import static org.camunda.bpm.engine.authorization.Permissions.UPDATE; +import static org.camunda.bpm.engine.authorization.Permissions.UPDATE_INSTANCE; +import static org.camunda.bpm.engine.authorization.Permissions.UPDATE_TASK; +import static org.camunda.bpm.engine.authorization.Resources.BATCH; +import static org.camunda.bpm.engine.authorization.Resources.DECISION_DEFINITION; +import static org.camunda.bpm.engine.authorization.Resources.DECISION_REQUIREMENTS_DEFINITION; +import static org.camunda.bpm.engine.authorization.Resources.DEPLOYMENT; +import static org.camunda.bpm.engine.authorization.Resources.PROCESS_DEFINITION; +import static org.camunda.bpm.engine.authorization.Resources.PROCESS_INSTANCE; +import static org.camunda.bpm.engine.authorization.Resources.TASK; + +import org.camunda.bpm.engine.authorization.Permission; +import org.camunda.bpm.engine.authorization.ProcessDefinitionPermissions; +import org.camunda.bpm.engine.authorization.ProcessInstancePermissions; +import org.camunda.bpm.engine.authorization.Resources; +import org.camunda.bpm.engine.authorization.SystemPermissions; +import org.camunda.bpm.engine.authorization.TaskPermissions; +import org.camunda.bpm.engine.authorization.UserOperationLogCategoryPermissions; import org.camunda.bpm.engine.history.HistoricCaseInstance; import org.camunda.bpm.engine.history.HistoricDecisionInstance; import org.camunda.bpm.engine.history.HistoricProcessInstance; @@ -29,24 +59,20 @@ import org.camunda.bpm.engine.impl.dmn.entity.repository.DecisionDefinitionEntity; import org.camunda.bpm.engine.impl.dmn.entity.repository.DecisionRequirementsDefinitionEntity; import org.camunda.bpm.engine.impl.history.event.HistoricExternalTaskLogEntity; -import org.camunda.bpm.engine.impl.persistence.entity.*; +import org.camunda.bpm.engine.impl.persistence.entity.AuthorizationManager; +import org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity; +import org.camunda.bpm.engine.impl.persistence.entity.HistoricJobLogEventEntity; +import org.camunda.bpm.engine.impl.persistence.entity.HistoricProcessInstanceEntity; +import org.camunda.bpm.engine.impl.persistence.entity.HistoricTaskInstanceEntity; +import org.camunda.bpm.engine.impl.persistence.entity.HistoricVariableInstanceEntity; +import org.camunda.bpm.engine.impl.persistence.entity.JobEntity; +import org.camunda.bpm.engine.impl.persistence.entity.ProcessDefinitionEntity; +import org.camunda.bpm.engine.impl.persistence.entity.TaskEntity; import org.camunda.bpm.engine.repository.CaseDefinition; import org.camunda.bpm.engine.repository.DecisionDefinition; import org.camunda.bpm.engine.repository.ProcessDefinition; import org.camunda.bpm.engine.runtime.CaseExecution; -import static org.camunda.bpm.engine.authorization.Authorization.ANY; -import static org.camunda.bpm.engine.authorization.Permissions.*; -import static org.camunda.bpm.engine.authorization.Resources.*; - -import org.camunda.bpm.engine.authorization.Permission; -import org.camunda.bpm.engine.authorization.ProcessDefinitionPermissions; -import org.camunda.bpm.engine.authorization.ProcessInstancePermissions; -import org.camunda.bpm.engine.authorization.Resources; -import org.camunda.bpm.engine.authorization.SystemPermissions; -import org.camunda.bpm.engine.authorization.TaskPermissions; -import org.camunda.bpm.engine.authorization.UserOperationLogCategoryPermissions; - /** * {@link CommandChecker} that uses the {@link AuthorizationManager} to perform * authorization checks. diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/cfg/multitenancy/TenantCommandChecker.java b/engine/src/main/java/org/camunda/bpm/engine/impl/cfg/multitenancy/TenantCommandChecker.java index 58b6379235c..edf8f0cbf1d 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/cfg/multitenancy/TenantCommandChecker.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/cfg/multitenancy/TenantCommandChecker.java @@ -30,7 +30,16 @@ import org.camunda.bpm.engine.impl.dmn.entity.repository.DecisionDefinitionEntity; import org.camunda.bpm.engine.impl.dmn.entity.repository.DecisionRequirementsDefinitionEntity; import org.camunda.bpm.engine.impl.history.event.HistoricExternalTaskLogEntity; -import org.camunda.bpm.engine.impl.persistence.entity.*; +import org.camunda.bpm.engine.impl.persistence.entity.DeploymentEntity; +import org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity; +import org.camunda.bpm.engine.impl.persistence.entity.HistoricJobLogEventEntity; +import org.camunda.bpm.engine.impl.persistence.entity.HistoricProcessInstanceEntity; +import org.camunda.bpm.engine.impl.persistence.entity.HistoricTaskInstanceEntity; +import org.camunda.bpm.engine.impl.persistence.entity.HistoricVariableInstanceEntity; +import org.camunda.bpm.engine.impl.persistence.entity.JobEntity; +import org.camunda.bpm.engine.impl.persistence.entity.ProcessDefinitionEntity; +import org.camunda.bpm.engine.impl.persistence.entity.TaskEntity; +import org.camunda.bpm.engine.impl.persistence.entity.TenantManager; import org.camunda.bpm.engine.repository.CaseDefinition; import org.camunda.bpm.engine.repository.DecisionDefinition; import org.camunda.bpm.engine.repository.ProcessDefinition; diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/AddCommentCmd.java b/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/AddCommentCmd.java index 31e4fc69174..1b2713c40d7 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/AddCommentCmd.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/AddCommentCmd.java @@ -16,9 +16,11 @@ */ package org.camunda.bpm.engine.impl.cmd; +import static org.camunda.bpm.engine.ProcessEngineConfiguration.HISTORY_REMOVAL_TIME_STRATEGY_START; +import static org.camunda.bpm.engine.impl.util.EnsureUtil.ensureNotNull; + import java.io.Serializable; import java.util.Date; - import org.camunda.bpm.engine.ProcessEngineException; import org.camunda.bpm.engine.impl.context.Context; import org.camunda.bpm.engine.impl.history.event.HistoricProcessInstanceEventEntity; @@ -31,9 +33,6 @@ import org.camunda.bpm.engine.task.Comment; import org.camunda.bpm.engine.task.Event; -import static org.camunda.bpm.engine.ProcessEngineConfiguration.HISTORY_REMOVAL_TIME_STRATEGY_START; -import static org.camunda.bpm.engine.impl.util.EnsureUtil.ensureNotNull; - /** * @author Tom Baeyens @@ -78,10 +77,7 @@ public Comment execute(CommandContext commandContext) { provideRemovalTime(comment); } - String eventMessage = message.replaceAll("\\s+", " "); - if (eventMessage.length() > 163) { - eventMessage = eventMessage.substring(0, 160) + "..."; - } + String eventMessage = comment.toEventMessage(message); comment.setMessage(eventMessage); comment.setFullMessage(message); diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/DeleteProcessInstanceCommentCmd.java b/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/DeleteProcessInstanceCommentCmd.java new file mode 100644 index 00000000000..78f4630e785 --- /dev/null +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/DeleteProcessInstanceCommentCmd.java @@ -0,0 +1,95 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. Camunda licenses this file to you under the Apache License, + * Version 2.0; you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.camunda.bpm.engine.impl.cmd; + +import static org.camunda.bpm.engine.impl.util.EnsureUtil.ensureNotNull; + +import java.io.Serializable; +import java.util.Collections; +import java.util.List; + +import org.camunda.bpm.engine.BadUserRequestException; +import org.camunda.bpm.engine.history.UserOperationLogEntry; +import org.camunda.bpm.engine.impl.cfg.CommandChecker; +import org.camunda.bpm.engine.impl.interceptor.Command; +import org.camunda.bpm.engine.impl.interceptor.CommandContext; +import org.camunda.bpm.engine.impl.persistence.entity.CommentEntity; +import org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity; +import org.camunda.bpm.engine.impl.persistence.entity.PropertyChange; +import org.camunda.bpm.engine.task.Comment; + +/** + * Command to delete a comment by a given commentId and processInstanceId or to delete all comments + * of a given processInstanceId + */ + +public class DeleteProcessInstanceCommentCmd implements Command, Serializable { + + private static final long serialVersionUID = 1L; + protected String commentId; + protected String processInstanceId; + + public DeleteProcessInstanceCommentCmd(String processInstanceId, String commentId) { + this.processInstanceId = processInstanceId; + this.commentId = commentId; + } + + public DeleteProcessInstanceCommentCmd(String processInstanceId) { + this.processInstanceId = processInstanceId; + } + + @Override + public Object execute(CommandContext commandContext) { + ensureNotNull(BadUserRequestException.class, "processInstanceId", processInstanceId); + + ExecutionEntity processInstance = commandContext.getExecutionManager().findExecutionById(processInstanceId); + ensureNotNull("No processInstance exists with processInstanceId: " + processInstanceId, "processInstance", + processInstance); + + checkUpdateProcessInstanceById(processInstanceId, commandContext); + if (commentId != null ) { + CommentEntity comment = commandContext.getCommentManager() + .findCommentByProcessInstanceIdAndCommentId(processInstanceId, commentId); + if (comment != null) { + commandContext.getDbEntityManager().delete(comment); + } + } else { // delete all comments + List comments = commandContext.getCommentManager().findCommentsByProcessInstanceId(processInstanceId); + if (!comments.isEmpty()) { + commandContext.getCommentManager() + .deleteCommentsByProcessInstanceIds(Collections.singletonList(processInstanceId)); + } + } + + logOperation(processInstance, commandContext); + + return null; + } + + protected void logOperation(ExecutionEntity processInstance, CommandContext commandContext) { + PropertyChange propertyChange = new PropertyChange("comment", null, null); + commandContext.getOperationLogManager() + .logCommentOperation(UserOperationLogEntry.OPERATION_TYPE_DELETE_COMMENT, processInstance, propertyChange); + } + + protected void checkUpdateProcessInstanceById(String processInstanceId, CommandContext commandContext) { + for (CommandChecker checker : commandContext.getProcessEngineConfiguration().getCommandCheckers()) { + checker.checkUpdateProcessInstanceById(processInstanceId); + } + } + +} diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/DeleteTaskCommentCmd.java b/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/DeleteTaskCommentCmd.java new file mode 100644 index 00000000000..bd13162f398 --- /dev/null +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/DeleteTaskCommentCmd.java @@ -0,0 +1,87 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. Camunda licenses this file to you under the Apache License, + * Version 2.0; you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.camunda.bpm.engine.impl.cmd; + +import static org.camunda.bpm.engine.impl.util.EnsureUtil.ensureNotNull; + +import java.io.Serializable; +import java.util.List; + +import org.camunda.bpm.engine.BadUserRequestException; +import org.camunda.bpm.engine.history.UserOperationLogEntry; +import org.camunda.bpm.engine.impl.cfg.CommandChecker; +import org.camunda.bpm.engine.impl.interceptor.Command; +import org.camunda.bpm.engine.impl.interceptor.CommandContext; +import org.camunda.bpm.engine.impl.persistence.entity.CommentEntity; +import org.camunda.bpm.engine.impl.persistence.entity.PropertyChange; +import org.camunda.bpm.engine.impl.persistence.entity.TaskEntity; +import org.camunda.bpm.engine.task.Comment; + +/** + * Command to delete a comment by a given commentId and taskId or to delete all comments + * of a given task Id + */ + +public class DeleteTaskCommentCmd implements Command, Serializable { + + private static final long serialVersionUID = 1L; + protected String commentId; + protected String taskId; + + public DeleteTaskCommentCmd(String taskId, String commentId) { + this.taskId = taskId; + this.commentId = commentId; + } + + public DeleteTaskCommentCmd(String taskId) { + this.taskId = taskId; + } + + @Override + public Object execute(CommandContext commandContext) { + ensureNotNull(BadUserRequestException.class, "taskId", taskId); + + TaskEntity task = commandContext.getTaskManager().findTaskById(taskId); + ensureNotNull("No task exists with taskId: " + taskId, "task", task); + checkTaskWork(task, commandContext); + + if (commentId != null) { + CommentEntity comment = commandContext.getCommentManager().findCommentByTaskIdAndCommentId(taskId, commentId); + if (comment != null) { + commandContext.getDbEntityManager().delete(comment); + } + } else { // delete all comments + List comments = commandContext.getCommentManager().findCommentsByTaskId(taskId); + if (!comments.isEmpty()) { + commandContext.getCommentManager().deleteCommentsByTaskId(taskId); + } + } + + commandContext.getOperationLogManager() + .logCommentOperation(UserOperationLogEntry.OPERATION_TYPE_DELETE_COMMENT, task, + new PropertyChange("comment", null, null)); + task.triggerUpdateEvent(); + + return null; + } + + protected void checkTaskWork(TaskEntity task, CommandContext commandContext) { + for (CommandChecker checker : commandContext.getProcessEngineConfiguration().getCommandCheckers()) { + checker.checkTaskWork(task); + } + } +} diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/UpdateCommentCmd.java b/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/UpdateCommentCmd.java new file mode 100644 index 00000000000..719ad5afc21 --- /dev/null +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/UpdateCommentCmd.java @@ -0,0 +1,137 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. Camunda licenses this file to you under the Apache License, + * Version 2.0; you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.camunda.bpm.engine.impl.cmd; + +import static org.camunda.bpm.engine.impl.util.EnsureUtil.ensureNotNull; + +import java.io.Serializable; +import org.camunda.bpm.engine.BadUserRequestException; +import org.camunda.bpm.engine.history.UserOperationLogEntry; +import org.camunda.bpm.engine.impl.cfg.CommandChecker; +import org.camunda.bpm.engine.impl.interceptor.Command; +import org.camunda.bpm.engine.impl.interceptor.CommandContext; +import org.camunda.bpm.engine.impl.persistence.entity.CommentEntity; +import org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity; +import org.camunda.bpm.engine.impl.persistence.entity.PropertyChange; +import org.camunda.bpm.engine.impl.persistence.entity.TaskEntity; +import org.camunda.bpm.engine.impl.util.ClockUtil; + +/** + * Command to update a comment by a given task ID or a process Instance ID + */ +public class UpdateCommentCmd implements Command, Serializable { + + private static final long serialVersionUID = 1L; + protected String taskId; + protected String commentId; + protected String processInstanceId; + protected String message; + + public UpdateCommentCmd(String taskId, String processInstanceId, String commentId, String message) { + this.taskId = taskId; + this.processInstanceId = processInstanceId; + this.commentId = commentId; + this.message = message; + } + + @Override + public Object execute(CommandContext commandContext) { + if (processInstanceId == null && taskId == null) { + throw new BadUserRequestException("Both process instance and task ids are null"); + } + + ensureNotNull("commentId", commentId); + ensureNotNull("message", message); + + CommentEntity comment = getComment(commandContext); + String oldMessage = comment != null ? comment.getMessage() : ""; + if (processInstanceId == null) { + ensureNotNull("taskId", taskId); + ensureNotNull("No comment exists with commentId: " + commentId + " and taskId: " + taskId, "comment", comment); + TaskEntity task = updateTaskComment(taskId, commandContext, comment); + commandContext.getOperationLogManager() + .logCommentOperation(UserOperationLogEntry.OPERATION_TYPE_UPDATE_COMMENT, task, getPropertyChange(oldMessage)); + task.triggerUpdateEvent(); + } else { + ensureNotNull("processInstanceId", processInstanceId); + ensureNotNull("No comment exists with commentId: " + commentId + " and processInstanceId: " + processInstanceId, + "comment", comment); + ExecutionEntity processInstance = commandContext.getExecutionManager().findExecutionById(processInstanceId); + ensureNotNull("No processInstance exists with processInstanceId: " + processInstanceId, "processInstance", + processInstance); + updateProcessInstanceComment(processInstanceId, commandContext, comment); + commandContext.getOperationLogManager() + .logCommentOperation(UserOperationLogEntry.OPERATION_TYPE_UPDATE_COMMENT, processInstance, + getPropertyChange(oldMessage)); + } + + return null; + } + + protected TaskEntity updateTaskComment(String taskId, CommandContext commandContext, CommentEntity comment) { + TaskEntity task = commandContext.getTaskManager().findTaskById(taskId); + ensureNotNull("No task exists with taskId: " + taskId, "task", task); + + checkTaskWork(task, commandContext); + updateComment(commandContext, comment); + return task; + } + + protected void updateProcessInstanceComment(String processInstanceId, + CommandContext commandContext, + CommentEntity comment) { + checkUpdateProcessInstanceById(processInstanceId, commandContext); + updateComment(commandContext, comment); + } + + protected CommentEntity getComment(CommandContext commandContext) { + if (taskId != null) { + return commandContext.getCommentManager().findCommentByTaskIdAndCommentId(taskId, commentId); + } + return commandContext.getCommentManager().findCommentByProcessInstanceIdAndCommentId(processInstanceId, commentId); + } + + protected PropertyChange getPropertyChange(String oldMessage) { + return new PropertyChange("comment", oldMessage, message); + } + + protected void checkTaskWork(TaskEntity task, CommandContext commandContext) { + for (CommandChecker checker : commandContext.getProcessEngineConfiguration().getCommandCheckers()) { + checker.checkTaskWork(task); + } + } + + protected void checkUpdateProcessInstanceById(String processInstanceId, CommandContext commandContext) { + for (CommandChecker checker : commandContext.getProcessEngineConfiguration().getCommandCheckers()) { + checker.checkUpdateProcessInstanceById(processInstanceId); + } + } + + private void updateComment(CommandContext commandContext, CommentEntity comment) { + String eventMessage = comment.toEventMessage(message); + + String userId = commandContext.getAuthenticatedUserId(); + + comment.setMessage(eventMessage); + comment.setFullMessage(message); + comment.setTime(ClockUtil.getCurrentTime()); + comment.setAction(UserOperationLogEntry.OPERATION_TYPE_UPDATE_COMMENT); + comment.setUserId(userId); + + commandContext.getDbEntityManager().update(CommentEntity.class, "updateComment", comment); + } +} diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/db/sql/DbSqlSessionFactory.java b/engine/src/main/java/org/camunda/bpm/engine/impl/db/sql/DbSqlSessionFactory.java index 8471de7b613..e826fac396a 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/db/sql/DbSqlSessionFactory.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/db/sql/DbSqlSessionFactory.java @@ -412,6 +412,7 @@ public class DbSqlSessionFactory implements SessionFactory { addDatabaseSpecificStatement(postgresLikeDatabase, "selectCommentsByTaskId", "selectCommentsByTaskId_postgres"); addDatabaseSpecificStatement(postgresLikeDatabase, "selectCommentsByProcessInstanceId", "selectCommentsByProcessInstanceId_postgres"); addDatabaseSpecificStatement(postgresLikeDatabase, "selectCommentByTaskIdAndCommentId", "selectCommentByTaskIdAndCommentId_postgres"); + addDatabaseSpecificStatement(postgresLikeDatabase, "selectCommentByProcessInstanceIdAndCommentId", "selectCommentByProcessInstanceIdAndCommentId_postgres"); addDatabaseSpecificStatement(postgresLikeDatabase, "selectEventsByTaskId", "selectEventsByTaskId_postgres"); addDatabaseSpecificStatement(postgresLikeDatabase, "selectFilterByQueryCriteria", "selectFilterByQueryCriteria_postgres"); addDatabaseSpecificStatement(postgresLikeDatabase, "selectFilter", "selectFilter_postgres"); diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/persistence/entity/CommentEntity.java b/engine/src/main/java/org/camunda/bpm/engine/impl/persistence/entity/CommentEntity.java index 776ff259403..38a69b690c0 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/persistence/entity/CommentEntity.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/persistence/entity/CommentEntity.java @@ -19,10 +19,13 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.StringTokenizer; import org.camunda.bpm.engine.impl.db.DbEntity; +import org.camunda.bpm.engine.impl.db.HasDbRevision; import org.camunda.bpm.engine.impl.db.HistoricEntity; import org.camunda.bpm.engine.impl.util.StringUtil; import org.camunda.bpm.engine.task.Comment; @@ -32,7 +35,7 @@ /** * @author Tom Baeyens */ -public class CommentEntity implements Comment, Event, DbEntity, HistoricEntity, Serializable { +public class CommentEntity implements Comment, Event, HasDbRevision, DbEntity, HistoricEntity, Serializable { private static final long serialVersionUID = 1L; @@ -41,8 +44,6 @@ public class CommentEntity implements Comment, Event, DbEntity, HistoricEntity, protected String id; - // If comments would be removeable, revision needs to be added! - protected String type; protected String userId; protected Date time; @@ -54,9 +55,13 @@ public class CommentEntity implements Comment, Event, DbEntity, HistoricEntity, protected String tenantId; protected String rootProcessInstanceId; protected Date removalTime; + protected int revision; + @Override public Object getPersistentState() { - return CommentEntity.class; + Map persistentState = new HashMap<>(); + persistentState.put("message", message); + return persistentState; } public byte[] getFullMessageBytes() { @@ -86,6 +91,7 @@ public void setMessage(String[] messageParts) { message = stringBuilder.toString(); } + @Override public List getMessageParts() { if (message==null) { return null; @@ -105,14 +111,17 @@ public List getMessageParts() { // getters and setters ////////////////////////////////////////////////////// + @Override public String getId() { return id; } + @Override public void setId(String id) { this.id = id; } + @Override public String getUserId() { return userId; } @@ -121,6 +130,7 @@ public void setUserId(String userId) { this.userId = userId; } + @Override public String getTaskId() { return taskId; } @@ -129,6 +139,7 @@ public void setTaskId(String taskId) { this.taskId = taskId; } + @Override public String getMessage() { return message; } @@ -137,6 +148,7 @@ public void setMessage(String message) { this.message = message; } + @Override public Date getTime() { return time; } @@ -145,6 +157,7 @@ public void setTime(Date time) { this.time = time; } + @Override public String getProcessInstanceId() { return processInstanceId; } @@ -161,6 +174,7 @@ public void setType(String type) { this.type = type; } + @Override public String getFullMessage() { return fullMessage; } @@ -169,6 +183,7 @@ public void setFullMessage(String fullMessage) { this.fullMessage = fullMessage; } + @Override public String getAction() { return action; } @@ -185,6 +200,7 @@ public void setTenantId(String tenantId) { this.tenantId = tenantId; } + @Override public String getRootProcessInstanceId() { return rootProcessInstanceId; } @@ -193,6 +209,7 @@ public void setRootProcessInstanceId(String rootProcessInstanceId) { this.rootProcessInstanceId = rootProcessInstanceId; } + @Override public Date getRemovalTime() { return removalTime; } @@ -201,6 +218,14 @@ public void setRemovalTime(Date removalTime) { this.removalTime = removalTime; } + public String toEventMessage(String message) { + String eventMessage = message.replaceAll("\\s+", " "); + if (eventMessage.length() > 163) { + eventMessage = eventMessage.substring(0, 160) + "..."; + } + return eventMessage; + } + @Override public String toString() { return this.getClass().getSimpleName() @@ -211,6 +236,7 @@ public String toString() { + ", taskId=" + taskId + ", processInstanceId=" + processInstanceId + ", rootProcessInstanceId=" + rootProcessInstanceId + + ", revision= "+ revision + ", removalTime=" + removalTime + ", action=" + action + ", message=" + message @@ -218,4 +244,19 @@ public String toString() { + ", tenantId=" + tenantId + "]"; } + + @Override + public void setRevision(int revision) { + this.revision = revision; + } + + @Override + public int getRevision() { + return revision; + } + + @Override + public int getRevisionNext() { + return revision + 1; + } } diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/persistence/entity/CommentManager.java b/engine/src/main/java/org/camunda/bpm/engine/impl/persistence/entity/CommentManager.java index 42e6fa8b27f..57fa8a219ce 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/persistence/entity/CommentManager.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/persistence/entity/CommentManager.java @@ -108,6 +108,16 @@ public CommentEntity findCommentByTaskIdAndCommentId(String taskId, String comme return (CommentEntity) getDbEntityManager().selectOne("selectCommentByTaskIdAndCommentId", parameters); } + public CommentEntity findCommentByProcessInstanceIdAndCommentId(String processInstanceId, String commentId) { + checkHistoryEnabled(); + + Map parameters = new HashMap<>(); + parameters.put("processInstanceId", processInstanceId); + parameters.put("id", commentId); + + return (CommentEntity) getDbEntityManager().selectOne("selectCommentByProcessInstanceIdAndCommentId", parameters); + } + public DbOperation addRemovalTimeToCommentsByRootProcessInstanceId(String rootProcessInstanceId, Date removalTime, Integer batchSize) { Map parameters = new HashMap<>(); parameters.put("rootProcessInstanceId", rootProcessInstanceId); @@ -141,5 +151,4 @@ public DbOperation deleteCommentsByRemovalTime(Date removalTime, int minuteFrom, .deletePreserveOrder(CommentEntity.class, "deleteCommentsByRemovalTime", new ListQueryParameterObject(parameters, 0, batchSize)); } - } diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/persistence/entity/UserOperationLogManager.java b/engine/src/main/java/org/camunda/bpm/engine/impl/persistence/entity/UserOperationLogManager.java index 3d47d89b431..e7259ec0a45 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/persistence/entity/UserOperationLogManager.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/persistence/entity/UserOperationLogManager.java @@ -493,6 +493,34 @@ public void logAttachmentOperation(String operation, ExecutionEntity processInst } } + public void logCommentOperation(String operation, TaskEntity task, PropertyChange propertyChange) { + if (isUserOperationLogEnabled()) { + UserOperationLogContext context = new UserOperationLogContext(); + + UserOperationLogContextEntryBuilder entryBuilder = UserOperationLogContextEntryBuilder.entry(operation, + EntityTypes.COMMENT) + .category(UserOperationLogEntry.CATEGORY_TASK_WORKER) + .inContextOf(task, Collections.singletonList(propertyChange)); + context.addEntry(entryBuilder.create()); + + fireUserOperationLog(context); + } + } + + public void logCommentOperation(String operation, ExecutionEntity processInstance, PropertyChange propertyChange) { + if (isUserOperationLogEnabled()) { + UserOperationLogContext context = new UserOperationLogContext(); + + UserOperationLogContextEntryBuilder entryBuilder = UserOperationLogContextEntryBuilder.entry(operation, + EntityTypes.COMMENT) + .category(UserOperationLogEntry.CATEGORY_TASK_WORKER) + .inContextOf(processInstance, Collections.singletonList(propertyChange)); + context.addEntry(entryBuilder.create()); + + fireUserOperationLog(context); + } + } + public void logVariableOperation(String operation, String executionId, String taskId, PropertyChange propertyChange) { if(isUserOperationLogEnabled()) { diff --git a/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.db2.create.history.sql b/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.db2.create.history.sql index 29445d439dc..fb29c255bee 100644 --- a/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.db2.create.history.sql +++ b/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.db2.create.history.sql @@ -187,6 +187,7 @@ create table ACT_HI_COMMENT ( FULL_MSG_ BLOB, TENANT_ID_ varchar(64), REMOVAL_TIME_ timestamp, + REV_ integer not null default 1, primary key (ID_) ); diff --git a/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.h2.create.history.sql b/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.h2.create.history.sql index 1edf441710d..27d1441c087 100644 --- a/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.h2.create.history.sql +++ b/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.h2.create.history.sql @@ -186,6 +186,7 @@ create table ACT_HI_COMMENT ( FULL_MSG_ longvarbinary, TENANT_ID_ varchar(64), REMOVAL_TIME_ timestamp, + REV_ integer not null default 1, primary key (ID_) ); diff --git a/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.mariadb.create.history.sql b/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.mariadb.create.history.sql index 228b264ff75..f5a3191a4b9 100644 --- a/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.mariadb.create.history.sql +++ b/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.mariadb.create.history.sql @@ -186,6 +186,7 @@ create table ACT_HI_COMMENT ( FULL_MSG_ LONGBLOB, TENANT_ID_ varchar(64), REMOVAL_TIME_ datetime(3), + REV_ integer not null default 1, primary key (ID_) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin; diff --git a/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.mssql.create.history.sql b/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.mssql.create.history.sql index 076516d4c12..4b0a0e82df2 100644 --- a/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.mssql.create.history.sql +++ b/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.mssql.create.history.sql @@ -185,6 +185,7 @@ create table ACT_HI_COMMENT ( FULL_MSG_ image, TENANT_ID_ nvarchar(64), REMOVAL_TIME_ datetime2, + REV_ integer not null default 1, primary key (ID_) ); diff --git a/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.mysql.create.history.sql b/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.mysql.create.history.sql index 4559ad612b8..5cebeb686fb 100644 --- a/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.mysql.create.history.sql +++ b/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.mysql.create.history.sql @@ -186,6 +186,7 @@ create table ACT_HI_COMMENT ( FULL_MSG_ LONGBLOB, TENANT_ID_ varchar(64), REMOVAL_TIME_ datetime, + REV_ integer not null default 1, primary key (ID_) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin; diff --git a/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.oracle.create.history.sql b/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.oracle.create.history.sql index e28095b8416..9bd42eb85d2 100644 --- a/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.oracle.create.history.sql +++ b/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.oracle.create.history.sql @@ -186,6 +186,7 @@ create table ACT_HI_COMMENT ( FULL_MSG_ BLOB, TENANT_ID_ NVARCHAR2(64), REMOVAL_TIME_ TIMESTAMP(6), + REV_ INTEGER DEFAULT 1 NOT NULL, primary key (ID_) ); diff --git a/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.postgres.create.history.sql b/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.postgres.create.history.sql index c95743b5187..86b3bb3eac1 100644 --- a/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.postgres.create.history.sql +++ b/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.postgres.create.history.sql @@ -186,6 +186,7 @@ create table ACT_HI_COMMENT ( FULL_MSG_ bytea, TENANT_ID_ varchar(64), REMOVAL_TIME_ timestamp, + REV_ integer not null default 1, primary key (ID_) ); diff --git a/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/db2_engine_7.22_to_7.23.sql b/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/db2_engine_7.22_to_7.23.sql index c75ac812e12..c8829377542 100644 --- a/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/db2_engine_7.22_to_7.23.sql +++ b/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/db2_engine_7.22_to_7.23.sql @@ -16,4 +16,8 @@ -- insert into ACT_GE_SCHEMA_LOG -values ('1200', CURRENT_TIMESTAMP, '7.23.0'); \ No newline at end of file +values ('1200', CURRENT_TIMESTAMP, '7.23.0'); + +alter table ACT_HI_COMMENT + add column REV_ integer not null + default 1; diff --git a/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/h2_engine_7.22_to_7.23.sql b/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/h2_engine_7.22_to_7.23.sql index c75ac812e12..f615c7b2975 100644 --- a/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/h2_engine_7.22_to_7.23.sql +++ b/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/h2_engine_7.22_to_7.23.sql @@ -16,4 +16,9 @@ -- insert into ACT_GE_SCHEMA_LOG -values ('1200', CURRENT_TIMESTAMP, '7.23.0'); \ No newline at end of file +values ('1200', CURRENT_TIMESTAMP, '7.23.0'); + +alter table ACT_HI_COMMENT + add column REV_ integer not null + default 1; + diff --git a/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/mariadb_engine_7.22_to_7.23.sql b/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/mariadb_engine_7.22_to_7.23.sql index c75ac812e12..c8829377542 100644 --- a/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/mariadb_engine_7.22_to_7.23.sql +++ b/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/mariadb_engine_7.22_to_7.23.sql @@ -16,4 +16,8 @@ -- insert into ACT_GE_SCHEMA_LOG -values ('1200', CURRENT_TIMESTAMP, '7.23.0'); \ No newline at end of file +values ('1200', CURRENT_TIMESTAMP, '7.23.0'); + +alter table ACT_HI_COMMENT + add column REV_ integer not null + default 1; diff --git a/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/mssql_engine_7.22_to_7.23.sql b/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/mssql_engine_7.22_to_7.23.sql index c75ac812e12..60f293be989 100644 --- a/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/mssql_engine_7.22_to_7.23.sql +++ b/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/mssql_engine_7.22_to_7.23.sql @@ -16,4 +16,8 @@ -- insert into ACT_GE_SCHEMA_LOG -values ('1200', CURRENT_TIMESTAMP, '7.23.0'); \ No newline at end of file +values ('1200', CURRENT_TIMESTAMP, '7.23.0'); + +alter table ACT_HI_COMMENT + add REV_ integer + not null default 1; \ No newline at end of file diff --git a/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/mysql_engine_7.22_to_7.23.sql b/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/mysql_engine_7.22_to_7.23.sql index c75ac812e12..87f8944c858 100644 --- a/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/mysql_engine_7.22_to_7.23.sql +++ b/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/mysql_engine_7.22_to_7.23.sql @@ -16,4 +16,8 @@ -- insert into ACT_GE_SCHEMA_LOG -values ('1200', CURRENT_TIMESTAMP, '7.23.0'); \ No newline at end of file +values ('1200', CURRENT_TIMESTAMP, '7.23.0'); + +alter table ACT_HI_COMMENT + add column REV_ INTEGER not null + default 1; diff --git a/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/oracle_engine_7.22_to_7.23.sql b/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/oracle_engine_7.22_to_7.23.sql index c75ac812e12..298612a40ef 100644 --- a/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/oracle_engine_7.22_to_7.23.sql +++ b/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/oracle_engine_7.22_to_7.23.sql @@ -16,4 +16,7 @@ -- insert into ACT_GE_SCHEMA_LOG -values ('1200', CURRENT_TIMESTAMP, '7.23.0'); \ No newline at end of file +values ('1200', CURRENT_TIMESTAMP, '7.23.0'); + +alter table ACT_HI_COMMENT + add REV_ integer default 1 not null; diff --git a/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/postgres_engine_7.22_to_7.23.sql b/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/postgres_engine_7.22_to_7.23.sql index c75ac812e12..c8829377542 100644 --- a/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/postgres_engine_7.22_to_7.23.sql +++ b/engine/src/main/resources/org/camunda/bpm/engine/db/upgrade/postgres_engine_7.22_to_7.23.sql @@ -16,4 +16,8 @@ -- insert into ACT_GE_SCHEMA_LOG -values ('1200', CURRENT_TIMESTAMP, '7.23.0'); \ No newline at end of file +values ('1200', CURRENT_TIMESTAMP, '7.23.0'); + +alter table ACT_HI_COMMENT + add column REV_ integer not null + default 1; diff --git a/engine/src/main/resources/org/camunda/bpm/engine/impl/mapping/entity/Comment.xml b/engine/src/main/resources/org/camunda/bpm/engine/impl/mapping/entity/Comment.xml index d24345f9219..3e7338b13d8 100644 --- a/engine/src/main/resources/org/camunda/bpm/engine/impl/mapping/entity/Comment.xml +++ b/engine/src/main/resources/org/camunda/bpm/engine/impl/mapping/entity/Comment.xml @@ -20,11 +20,11 @@ - + - + - insert into ${prefix}ACT_HI_COMMENT (ID_, TYPE_, TIME_, USER_ID_, TASK_ID_, ROOT_PROC_INST_ID_, PROC_INST_ID_, ACTION_, MESSAGE_, FULL_MSG_, TENANT_ID_, REMOVAL_TIME_) + insert into ${prefix}ACT_HI_COMMENT (ID_, TYPE_, TIME_, USER_ID_, TASK_ID_, ROOT_PROC_INST_ID_, PROC_INST_ID_,ACTION_, MESSAGE_, FULL_MSG_, TENANT_ID_, REMOVAL_TIME_, REV_) values ( #{id ,jdbcType=VARCHAR}, #{type ,jdbcType=VARCHAR}, @@ -37,12 +37,13 @@ #{message ,jdbcType=VARCHAR}, #{fullMessageBytes ,jdbcType=BLOB}, #{tenantId ,jdbcType=VARCHAR}, - #{removalTime, jdbcType=TIMESTAMP} + #{removalTime, jdbcType=TIMESTAMP}, + 1 ) - insert into ${prefix}ACT_HI_COMMENT (ID_, TYPE_, TIME_, USER_ID_, TASK_ID_, ROOT_PROC_INST_ID_, PROC_INST_ID_, ACTION_, MESSAGE_, FULL_MSG_, TENANT_ID_, REMOVAL_TIME_) + insert into ${prefix}ACT_HI_COMMENT (ID_, TYPE_, TIME_, USER_ID_, TASK_ID_, ROOT_PROC_INST_ID_, PROC_INST_ID_, ACTION_, MESSAGE_, FULL_MSG_, TENANT_ID_, REMOVAL_TIME_, REV_) values ( #{id ,jdbcType=VARCHAR}, #{type ,jdbcType=VARCHAR}, @@ -55,11 +56,24 @@ #{message ,jdbcType=VARCHAR}, #{fullMessageBytes ,jdbcType=BINARY}, #{tenantId ,jdbcType=VARCHAR}, - #{removalTime, jdbcType=TIMESTAMP} + #{removalTime, jdbcType=TIMESTAMP}, + 1 ) + + update + ${prefix}ACT_HI_COMMENT + set MESSAGE_ = #{message ,jdbcType=VARCHAR}, + FULL_MSG_= #{fullMessageBytes ,jdbcType=BLOB}, + TIME_ = #{time ,jdbcType=TIMESTAMP}, + ACTION_ = #{action ,jdbcType=VARCHAR}, + USER_ID_ = #{userId ,jdbcType=VARCHAR}, + REV_ = #{revisionNext, jdbcType=INTEGER} + where ID_ = #{id} and REV_ = #{revision, jdbcType=INTEGER} + + update @@ -213,13 +227,19 @@ + + delete + from ${prefix}ACT_HI_COMMENT + where ID_ = #{id} and REV_ = #{revision} + + - delete from ${prefix}ACT_HI_COMMENT where TASK_ID_ = #{taskId} + delete from ${prefix}ACT_HI_COMMENT where TASK_ID_ = #{taskId} delete from ${prefix}ACT_HI_COMMENT - where + where TASK_ID_ in ( select ID_ @@ -332,6 +352,7 @@ + @@ -347,21 +368,27 @@ + - + + - + @@ -427,29 +454,43 @@ + + + + diff --git a/engine/src/test/java/org/camunda/bpm/engine/test/api/authorization/AuthorizationTest.java b/engine/src/test/java/org/camunda/bpm/engine/test/api/authorization/AuthorizationTest.java index b3c16f8bbde..7426c80036d 100644 --- a/engine/src/test/java/org/camunda/bpm/engine/test/api/authorization/AuthorizationTest.java +++ b/engine/src/test/java/org/camunda/bpm/engine/test/api/authorization/AuthorizationTest.java @@ -50,6 +50,7 @@ import org.camunda.bpm.engine.runtime.CaseInstance; import org.camunda.bpm.engine.runtime.Job; import org.camunda.bpm.engine.runtime.ProcessInstance; +import org.camunda.bpm.engine.task.Comment; import org.camunda.bpm.engine.task.Task; import org.camunda.bpm.engine.test.util.PluggableProcessEngineTest; import org.camunda.bpm.engine.variable.VariableMap; @@ -69,6 +70,8 @@ public abstract class AuthorizationTest extends PluggableProcessEngineTest { protected static final String VARIABLE_NAME = "aVariableName"; protected static final String VARIABLE_VALUE = "aVariableValue"; + protected static final String TASK_ID = "myTask"; + protected List deploymentIds = new ArrayList<>(); @Before @@ -287,6 +290,10 @@ protected void deleteTask(final String taskId, final boolean cascade) { }); } + protected Comment createComment(String taskId, String processInstanceId, String message) { + return runWithoutAuthorization(() -> taskService.createComment(taskId, processInstanceId, message)); + } + protected void addCandidateUser(final String taskId, final String user) { runWithoutAuthorization((Callable) () -> { taskService.addCandidateUser(taskId, user); diff --git a/engine/src/test/java/org/camunda/bpm/engine/test/api/authorization/ProcessInstanceCommentAuthorizationTest.java b/engine/src/test/java/org/camunda/bpm/engine/test/api/authorization/ProcessInstanceCommentAuthorizationTest.java new file mode 100644 index 00000000000..b8d659b4297 --- /dev/null +++ b/engine/src/test/java/org/camunda/bpm/engine/test/api/authorization/ProcessInstanceCommentAuthorizationTest.java @@ -0,0 +1,277 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. Camunda licenses this file to you under the Apache License, + * Version 2.0; you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.camunda.bpm.engine.test.api.authorization; + +import static org.camunda.bpm.engine.authorization.Permissions.UPDATE; +import static org.camunda.bpm.engine.authorization.Resources.PROCESS_INSTANCE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; + +import java.util.Collections; +import java.util.List; +import org.camunda.bpm.engine.AuthorizationException; +import org.camunda.bpm.engine.task.Comment; +import org.camunda.bpm.engine.test.Deployment; +import org.junit.Test; + +public class ProcessInstanceCommentAuthorizationTest extends AuthorizationTest { + protected static final String ONE_TASK_PROCESS_KEY = "oneTaskProcess"; + + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + @Test + public void testDeleteProcessInstanceTaskCommentWithoutAuthorization() { + // given + String processInstanceId = startProcessInstanceByKey(ONE_TASK_PROCESS_KEY).getId(); + createTask(TASK_ID); + + Comment createdComment = createComment(TASK_ID, processInstanceId, "aComment"); + + try { + // when + taskService.deleteProcessInstanceComment(processInstanceId, createdComment.getId()); + fail("Exception expected: It should not be possible to delete a task."); + } catch (AuthorizationException e) { + // then + testRule.assertTextPresent("The user with id 'test' does not have one of the following permissions: 'UPDATE'", + e.getMessage()); + } + + // triggers a db clean up + deleteTask(TASK_ID, true); + } + + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + @Test + public void testDeleteProcessInstanceTaskComment() { + // given + createTask(TASK_ID); + String processInstanceId = startProcessInstanceByKey(ONE_TASK_PROCESS_KEY).getId(); + Comment createdComment = taskService.createComment(TASK_ID, processInstanceId, "aComment"); + createGrantAuthorization(PROCESS_INSTANCE, processInstanceId, userId, UPDATE); + + // when + taskService.deleteProcessInstanceComment(processInstanceId, createdComment.getId()); + + // then + List deletedCommentLst = taskService.getProcessInstanceComments(processInstanceId); + assertEquals("The comments list should be empty", Collections.emptyList(), deletedCommentLst); + + // triggers a db clean up + deleteTask(TASK_ID, true); + } + + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + @Test + public void testDeleteProcessInstanceTaskCommentsWithoutAuthorization() { + // given + createTask(TASK_ID); + String processInstanceId = startProcessInstanceByKey(ONE_TASK_PROCESS_KEY).getId(); + createComment(TASK_ID, processInstanceId, "aComment"); + + try { + // when + taskService.deleteProcessInstanceComments(processInstanceId); + fail("Exception expected: It should not be possible to delete a comment."); + } catch (AuthorizationException e) { + // then + testRule.assertTextPresent("The user with id 'test' does not have one of the following permissions: 'UPDATE'", + e.getMessage()); + } + + // triggers a db clean up + deleteTask(TASK_ID, true); + } + + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + @Test + public void testDeleteProcessInstanceTaskComments() { + // given + createTask(TASK_ID); + String processInstanceId = startProcessInstanceByKey(ONE_TASK_PROCESS_KEY).getId(); + taskService.createComment(TASK_ID, processInstanceId, "aCommentOne"); + taskService.createComment(TASK_ID, processInstanceId, "aCommentTwo"); + + createGrantAuthorization(PROCESS_INSTANCE, processInstanceId, userId, UPDATE); + + // when + taskService.deleteProcessInstanceComments(processInstanceId); + + // then + List comments = taskService.getProcessInstanceComments(processInstanceId); + assertEquals("The comments list should be empty", Collections.emptyList(), comments); + + // triggers a db clean up + deleteTask(TASK_ID, true); + } + + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + @Test + public void testUpdateProcessInstanceTaskCommentWithoutAuthorization() { + // given + createTask(TASK_ID); + String processInstanceId = startProcessInstanceByKey(ONE_TASK_PROCESS_KEY).getId(); + Comment createdComment = createComment(TASK_ID, processInstanceId, "originalComment"); + + try { + // when + taskService.updateProcessInstanceComment(processInstanceId, createdComment.getId(), "updateMessage"); + fail("Exception expected: It should not be possible to delete a task."); + } catch (AuthorizationException e) { + // then + testRule.assertTextPresent("The user with id 'test' does not have one of the following permissions: 'UPDATE'", + e.getMessage()); + } + + deleteTask(TASK_ID, true); + } + + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + @Test + public void testUpdateProcessInstanceTaskComment() { + // given + String taskId = "myTask"; + createTask(taskId); + String processInstanceId = startProcessInstanceByKey(ONE_TASK_PROCESS_KEY).getId(); + + String commentMessage = "OriginalCommentMessage"; + String updatedMessage = "UpdatedCommentMessage"; + Comment comment = taskService.createComment(taskId, processInstanceId, commentMessage); + createGrantAuthorization(PROCESS_INSTANCE, processInstanceId, userId, UPDATE); + + // when + taskService.updateProcessInstanceComment(processInstanceId, comment.getId(), updatedMessage); + + // then + List comments = taskService.getProcessInstanceComments(processInstanceId); + assertFalse("The comments list should not be empty", comments.isEmpty()); + assertEquals(updatedMessage, comments.get(0).getFullMessage()); + + // triggers a db clean up + deleteTask(taskId, true); + } + + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + @Test + public void testDeleteProcessInstanceCommentWithoutAuthorization() { + // given + String processInstanceId = startProcessInstanceByKey(ONE_TASK_PROCESS_KEY).getId(); + + Comment createdComment = createComment(null, processInstanceId, "aComment"); + + try { + // when + taskService.deleteProcessInstanceComment(processInstanceId, createdComment.getId()); + fail("Exception expected: It should not be possible to delete a task."); + } catch (AuthorizationException e) { + // then + testRule.assertTextPresent("The user with id 'test' does not have one of the following permissions: 'UPDATE'", + e.getMessage()); + } + } + + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + @Test + public void testDeleteProcessInstanceComment() { + // given + String processInstanceId = startProcessInstanceByKey(ONE_TASK_PROCESS_KEY).getId(); + Comment createdComment = taskService.createComment(null, processInstanceId, "aComment"); + createGrantAuthorization(PROCESS_INSTANCE, processInstanceId, userId, UPDATE); + + // when + taskService.deleteProcessInstanceComment(processInstanceId, createdComment.getId()); + + // then + List deletedCommentLst = taskService.getProcessInstanceComments(processInstanceId); + assertEquals("The comments list should be empty", Collections.emptyList(), deletedCommentLst); + } + + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + @Test + public void testDeleteProcessInstanceCommentsWithoutAuthorization() { + // given + String processInstanceId = startProcessInstanceByKey(ONE_TASK_PROCESS_KEY).getId(); + createComment(null, processInstanceId, "aComment"); + + try { + // when + taskService.deleteProcessInstanceComments(processInstanceId); + fail("Exception expected: It should not be possible to delete a comment."); + } catch (AuthorizationException e) { + // then + testRule.assertTextPresent("The user with id 'test' does not have one of the following permissions: 'UPDATE'", + e.getMessage()); + } + } + + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + @Test + public void testDeleteProcessInstanceComments() { + // given + String processInstanceId = startProcessInstanceByKey(ONE_TASK_PROCESS_KEY).getId(); + taskService.createComment(null, processInstanceId, "aCommentOne"); + taskService.createComment(null, processInstanceId, "aCommentTwo"); + + createGrantAuthorization(PROCESS_INSTANCE, processInstanceId, userId, UPDATE); + + // when + taskService.deleteProcessInstanceComments(processInstanceId); + + // then + List comments = taskService.getProcessInstanceComments(processInstanceId); + assertEquals("The comments list should be empty", Collections.emptyList(), comments); + } + + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + @Test + public void testUpdateProcessInstanceCommentWithoutAuthorization() { + // given + String processInstanceId = startProcessInstanceByKey(ONE_TASK_PROCESS_KEY).getId(); + Comment createdComment = createComment(null, processInstanceId, "originalComment"); + + try { + // when + taskService.updateProcessInstanceComment(processInstanceId, createdComment.getId(), "updateMessage"); + fail("Exception expected: It should not be possible to delete a task."); + } catch (AuthorizationException e) { + // then + testRule.assertTextPresent("The user with id 'test' does not have one of the following permissions: 'UPDATE'", + e.getMessage()); + } + } + + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + @Test + public void testUpdateProcessInstanceComment() { + // given + String processInstanceId = startProcessInstanceByKey(ONE_TASK_PROCESS_KEY).getId(); + + String commentMessage = "OriginalCommentMessage"; + String updatedMessage = "UpdatedCommentMessage"; + Comment comment = taskService.createComment(null, processInstanceId, commentMessage); + createGrantAuthorization(PROCESS_INSTANCE, processInstanceId, userId, UPDATE); + + // when + taskService.updateProcessInstanceComment(processInstanceId, comment.getId(), updatedMessage); + + // then + List comments = taskService.getProcessInstanceComments(processInstanceId); + assertFalse("The comments list should not be empty", comments.isEmpty()); + assertEquals(updatedMessage, comments.get(0).getFullMessage()); + } + +} diff --git a/engine/src/test/java/org/camunda/bpm/engine/test/api/authorization/TaskCommentAuthorizationTest.java b/engine/src/test/java/org/camunda/bpm/engine/test/api/authorization/TaskCommentAuthorizationTest.java new file mode 100644 index 00000000000..073bbe99a60 --- /dev/null +++ b/engine/src/test/java/org/camunda/bpm/engine/test/api/authorization/TaskCommentAuthorizationTest.java @@ -0,0 +1,281 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. Camunda licenses this file to you under the Apache License, + * Version 2.0; you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.camunda.bpm.engine.test.api.authorization; + +import static org.camunda.bpm.engine.authorization.Permissions.UPDATE; +import static org.camunda.bpm.engine.authorization.Resources.TASK; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import java.util.Collections; +import java.util.List; +import org.camunda.bpm.engine.AuthorizationException; +import org.camunda.bpm.engine.runtime.ProcessInstance; +import org.camunda.bpm.engine.task.Comment; +import org.camunda.bpm.engine.task.Task; +import org.camunda.bpm.engine.test.Deployment; +import org.junit.Test; + +public class TaskCommentAuthorizationTest extends AuthorizationTest { + + protected static final String PROCESS_KEY = "oneTaskProcess"; + + @Test + public void testDeleteTaskCommentWithoutAuthorization() { + // given + createTask(TASK_ID); + Comment createdComment = createComment(TASK_ID, null, "aComment"); + + try { + // when + taskService.deleteTaskComment(TASK_ID, createdComment.getId()); + fail("Exception expected: It should not be possible to delete a comment."); + } catch (AuthorizationException e) { + // then + testRule.assertTextPresent( + "The user with id 'test' does not have one of the following permissions: 'TASK_WORK' permission on resource 'myTask' of type 'Task' or 'UPDATE' permission on resource 'myTask' of type 'Task'", + e.getMessage()); + } + + // triggers a db clean up + deleteTask(TASK_ID, true); + } + + @Test + public void testDeleteTaskComment() { + // given + createTask(TASK_ID); + Comment createdComment = taskService.createComment(TASK_ID, null, "aComment"); + createGrantAuthorization(TASK, TASK_ID, userId, UPDATE); + + // when + taskService.deleteTaskComment(TASK_ID, createdComment.getId()); + + // then + Comment shouldBeDeleletedComment = taskService.getTaskComment(TASK_ID, createdComment.getId()); + assertNull(shouldBeDeleletedComment); + + // triggers a db clean up + deleteTask(TASK_ID, true); + } + + @Test + public void testDeleteTaskCommentsWithoutAuthorization() { + // given + createTask(TASK_ID); + createComment(TASK_ID, null, "aComment"); + + try { + // when + taskService.deleteTaskComments(TASK_ID); + fail("Exception expected: It should not be possible to delete a comment."); + } catch (AuthorizationException e) { + // then + testRule.assertTextPresent( + "The user with id 'test' does not have one of the following permissions: 'TASK_WORK' permission on resource 'myTask' of type 'Task' or 'UPDATE' permission on resource 'myTask' of type 'Task'", + e.getMessage()); + } + + // triggers a db clean up + deleteTask(TASK_ID, true); + } + + @Test + public void testDeleteTaskComments() { + // given + createTask(TASK_ID); + taskService.createComment(TASK_ID, null, "aCommentOne"); + taskService.createComment(TASK_ID, null, "aCommentTwo"); + + createGrantAuthorization(TASK, TASK_ID, userId, UPDATE); + + // when + taskService.deleteTaskComments(TASK_ID); + + // then + List comments = taskService.getTaskComments(TASK_ID); + assertEquals("The comments list should be empty", Collections.emptyList(), comments); + + // triggers a db clean up + deleteTask(TASK_ID, true); + } + + @Test + public void testUpdateTaskCommentWithoutAuthorization() { + // given + createTask(TASK_ID); + Comment createdComment = createComment(TASK_ID, null, "originalComment"); + + try { + // when + taskService.updateTaskComment(TASK_ID, createdComment.getId(), "updateMessage"); + fail("Exception expected: It should not be possible to delete a comment."); + } catch (AuthorizationException e) { + // then + testRule.assertTextPresent( + "The user with id 'test' does not have one of the following permissions: 'TASK_WORK' permission on resource 'myTask' of type 'Task' or 'UPDATE' permission on resource 'myTask' of type 'Task'", + e.getMessage()); + } + + // triggers a db clean up + deleteTask(TASK_ID, true); + } + + @Test + public void testUpdateTaskComment() { + // given + createTask(TASK_ID); + String commentMessage = "OriginalCommentMessage"; + String updatedMessage = "UpdatedCommentMessage"; + Comment comment = taskService.createComment(TASK_ID, null, commentMessage); + createGrantAuthorization(TASK, TASK_ID, userId, UPDATE); + + // when + taskService.updateTaskComment(TASK_ID, comment.getId(), updatedMessage); + + // then + List comments = taskService.getTaskComments(TASK_ID); + assertFalse("The comments list should not be empty", comments.isEmpty()); + assertEquals(updatedMessage, comments.get(0).getFullMessage()); + + // triggers a db clean up + deleteTask(TASK_ID, true); + } + + @Test + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + public void testDeleteProcessTaskCommentWithoutAuthorization() { + // given + ProcessInstance processInstance = startProcessInstanceByKey(PROCESS_KEY); + Task task = selectSingleTask(); + Comment createdComment = createComment(task.getId(), processInstance.getId(), "aComment"); + + try { + // when + taskService.deleteTaskComment(task.getId(), createdComment.getId()); + fail("Exception expected: It should not be possible to delete a comment."); + } catch (AuthorizationException e) { + // then + testRule.assertTextPresent( + "The user with id 'test' does not have one of the following permissions: 'TASK_WORK' permission on resource", + e.getMessage()); + } + } + + @Test + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + public void testDeleteProcessTaskComment() { + // given + ProcessInstance processInstance = startProcessInstanceByKey(PROCESS_KEY); + Task task = selectSingleTask(); + task.setAssignee("demo"); + Comment createdComment = taskService.createComment(task.getId(), processInstance.getId(), "aComment"); + createGrantAuthorization(TASK, task.getId(), userId, UPDATE); + + // when + taskService.deleteTaskComment(task.getId(), createdComment.getId()); + + // then + Comment shouldBeDeleletedComment = taskService.getTaskComment(task.getId(), createdComment.getId()); + assertNull(shouldBeDeleletedComment); + } + + @Test + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + public void testDeleteProcessTaskCommentsWithoutAuthorization() { + // given + ProcessInstance processInstance = startProcessInstanceByKey(PROCESS_KEY); + Task task = selectSingleTask(); + createComment(task.getId(), processInstance.getId(), "aComment"); + + try { + // when + taskService.deleteTaskComments(task.getId()); + fail("Exception expected: It should not be possible to delete a comment."); + } catch (AuthorizationException e) { + // then + testRule.assertTextPresent( + "The user with id 'test' does not have one of the following permissions: 'TASK_WORK' permission on resource", + e.getMessage()); + } + } + + @Test + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + public void testDeleteProcessTaskComments() { + // given + ProcessInstance processInstance = startProcessInstanceByKey(PROCESS_KEY); + Task task = selectSingleTask(); + taskService.createComment(task.getId(), processInstance.getId(), "aCommentOne"); + taskService.createComment(task.getId(), processInstance.getId(), "aCommentTwo"); + + createGrantAuthorization(TASK, task.getId(), userId, UPDATE); + + // when + taskService.deleteTaskComments(task.getId()); + + // then + List comments = taskService.getTaskComments(task.getId()); + assertEquals("The comments list should be empty", Collections.emptyList(), comments); + } + + @Test + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + public void testUpdateProcessTaskCommentWithoutAuthorization() { + // given + ProcessInstance processInstance = startProcessInstanceByKey(PROCESS_KEY); + Task task = selectSingleTask(); + + Comment createdComment = createComment(task.getId(), processInstance.getId(), "originalComment"); + + try { + // when + taskService.updateTaskComment(task.getId(), createdComment.getId(), "updateMessage"); + fail("Exception expected: It should not be possible to delete a comment."); + } catch (AuthorizationException e) { + // then + testRule.assertTextPresent( + "The user with id 'test' does not have one of the following permissions: 'TASK_WORK' permission on resource", + e.getMessage()); + } + } + + @Test + @Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml") + public void testUpdateProcessTaskComment() { + // given + ProcessInstance processInstance = startProcessInstanceByKey(PROCESS_KEY); + Task task = selectSingleTask(); + task.setAssignee("demo"); + + String commentMessage = "OriginalCommentMessage"; + String updatedMessage = "UpdatedCommentMessage"; + Comment comment = taskService.createComment(task.getId(), processInstance.getId(), commentMessage); + createGrantAuthorization(TASK, task.getId(), userId, UPDATE); + + // when + taskService.updateTaskComment(task.getId(), comment.getId(), updatedMessage); + + // then + List comments = taskService.getTaskComments(task.getId()); + assertFalse("The comments list should not be empty", comments.isEmpty()); + assertEquals(updatedMessage, comments.get(0).getFullMessage()); + } + +} diff --git a/engine/src/test/java/org/camunda/bpm/engine/test/api/encoding/ProcessEngineCharacterEncodingTest.java b/engine/src/test/java/org/camunda/bpm/engine/test/api/encoding/ProcessEngineCharacterEncodingTest.java index bd25ac5ddf1..95d61e7c1b0 100644 --- a/engine/src/test/java/org/camunda/bpm/engine/test/api/encoding/ProcessEngineCharacterEncodingTest.java +++ b/engine/src/test/java/org/camunda/bpm/engine/test/api/encoding/ProcessEngineCharacterEncodingTest.java @@ -24,7 +24,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; - import org.camunda.bpm.engine.TaskService; import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.camunda.bpm.engine.task.Comment; @@ -90,6 +89,10 @@ protected Task newTaskWithComment(String message) { return task; } + protected Comment createNewComment(String taskId, String message) { + return taskService.createComment(taskId, null, message); + } + @Test public void shouldPreserveArabicTaskCommentMessageWithCharset() { // given @@ -118,4 +121,37 @@ public void shouldPreserveLatinTaskCommentMessageWithCharset() { assertThat(taskComments.get(0).getFullMessage()).isEqualTo(message); } + @Test + public void shouldPreserveArabicTaskUpdateCommentMessageWithCharset() { + // given + String taskId = newTask().getId(); + Comment comment = createNewComment(taskId, "OriginalMessage"); + + // when + String updatedMessage = "این نمونه است"; + taskService.updateTaskComment(taskId, comment.getId(), updatedMessage); + + Comment updatedComment = taskService.getTaskComment(taskId, comment.getId()); + + // then + assertThat(updatedComment).isNotNull(); + assertThat(updatedComment.getFullMessage()).isEqualTo(updatedMessage); + } + + @Test + public void shouldPreserveLatinTaskUpdateCommentMessageWithCharset() { + // given + String taskId = newTask().getId(); + Comment comment = createNewComment(taskId, "OriginalMessage"); + + // when + String updatedMessage = "This is an example"; + taskService.updateTaskComment(taskId, comment.getId(), updatedMessage); + + Comment updatedComment = taskService.getTaskComment(taskId, comment.getId()); + + // then + assertThat(updatedComment).isNotNull(); + assertThat(updatedComment.getFullMessage()).isEqualTo(updatedMessage); + } } diff --git a/engine/src/test/java/org/camunda/bpm/engine/test/api/task/TaskLastUpdatedTest.java b/engine/src/test/java/org/camunda/bpm/engine/test/api/task/TaskLastUpdatedTest.java index 915c7fb7f4a..0bb79bdf6e8 100644 --- a/engine/src/test/java/org/camunda/bpm/engine/test/api/task/TaskLastUpdatedTest.java +++ b/engine/src/test/java/org/camunda/bpm/engine/test/api/task/TaskLastUpdatedTest.java @@ -20,7 +20,6 @@ import java.util.Date; import java.util.List; - import org.assertj.core.api.Assertions; import org.camunda.bpm.engine.OptimisticLockingException; import org.camunda.bpm.engine.RuntimeService; @@ -30,6 +29,7 @@ import org.camunda.bpm.engine.impl.util.ClockUtil; import org.camunda.bpm.engine.runtime.ProcessInstance; import org.camunda.bpm.engine.task.Attachment; +import org.camunda.bpm.engine.task.Comment; import org.camunda.bpm.engine.task.IdentityLinkType; import org.camunda.bpm.engine.task.Task; import org.camunda.bpm.engine.test.Deployment; @@ -414,4 +414,106 @@ public void shouldNotSaveTaskConcurrentlyUpdatedByDependentEntity() { Assertions.assertThatThrownBy(() -> taskService.saveTask(task)) .isInstanceOf(OptimisticLockingException.class); } + + @Test + public void shouldSetLastUpdatedOnTaskDeleteComment() { + // given + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + Comment comment = taskService.createComment(task.getId(), processInstance.getId(), "message"); + + Date beforeUpdate = getBeforeCurrentTime(); + + // when + taskService.deleteTaskComment(task.getId(), comment.getId()); + + // then + Task taskResult = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertThat(taskResult).isNotNull(); + assertThat(taskResult.getLastUpdated()).isAfter(beforeUpdate); + } + + @Test + public void shouldSetLastUpdatedOnTaskDeleteComments() { + // given + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + taskService.createComment(task.getId(), processInstance.getId(), "message"); + taskService.createComment(task.getId(), null, "message2"); + + Date beforeUpdate = getBeforeCurrentTime(); + + // when + taskService.deleteTaskComments(task.getId()); + + // then + Task taskResult = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertThat(taskResult).isNotNull(); + assertThat(taskResult.getLastUpdated()).isAfter(beforeUpdate); + } + + @Test + public void shouldSetLastUpdatedOnTaskCommentUpdate() { + // given + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + Comment comment = taskService.createComment(task.getId(), null, "aMessage"); + + Date beforeUpdate = getBeforeCurrentTime(); + + // when + taskService.updateTaskComment(task.getId(), comment.getId(), "updatedMessage"); + + // then + Task taskResult = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertThat(taskResult).isNotNull(); + assertThat(taskResult.getLastUpdated()).isAfter(beforeUpdate); + } + + @Test + public void shouldSetLastUpdatedOnProcessInstanceDeleteComment() { + // given + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + Comment comment = taskService.createComment(null, processInstance.getId(), "message"); + + // when + taskService.deleteProcessInstanceComment(processInstance.getId(), comment.getId()); + + // then + Task taskResult = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertThat(taskResult).isNotNull(); + assertThat(taskResult.getLastUpdated()).isNull(); + } + + @Test + public void shouldSetLastUpdatedOnProcessInstanceDeleteComments() { + // given + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + taskService.createComment(null, processInstance.getId(), "message"); + taskService.createComment(null, processInstance.getId(), "message2"); + + // when + taskService.deleteProcessInstanceComments(processInstance.getId()); + + // then + Task taskResult = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertThat(taskResult).isNotNull(); + assertThat(taskResult.getLastUpdated()).isNull(); + } + + @Test + public void shouldSetLastUpdatedOnProcessInstanceCommentUpdate() { + // given + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + Comment comment = taskService.createComment(null, processInstance.getId(), "aMessage"); + + // when + taskService.updateProcessInstanceComment(processInstance.getId(), comment.getId(), "updatedMessage"); + + // then + Task taskResult = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertThat(taskResult).isNotNull(); + assertThat(taskResult.getLastUpdated()).isNull(); + } + } diff --git a/engine/src/test/java/org/camunda/bpm/engine/test/api/task/TaskServiceTest.java b/engine/src/test/java/org/camunda/bpm/engine/test/api/task/TaskServiceTest.java index 38395799c3f..48880bbae16 100644 --- a/engine/src/test/java/org/camunda/bpm/engine/test/api/task/TaskServiceTest.java +++ b/engine/src/test/java/org/camunda/bpm/engine/test/api/task/TaskServiceTest.java @@ -37,7 +37,6 @@ import java.util.List; import java.util.Map; import java.util.Set; - import org.camunda.bpm.engine.BadUserRequestException; import org.camunda.bpm.engine.CaseService; import org.camunda.bpm.engine.HistoryService; @@ -80,7 +79,6 @@ import org.camunda.bpm.engine.test.util.ProcessEngineBootstrapRule; import org.camunda.bpm.engine.test.util.ProcessEngineTestRule; import org.camunda.bpm.engine.test.util.ProvidedProcessEngineRule; -import org.camunda.bpm.engine.test.util.RemoveAfter; import org.camunda.bpm.engine.variable.VariableMap; import org.camunda.bpm.engine.variable.Variables; import org.camunda.bpm.engine.variable.type.ValueType; @@ -284,6 +282,482 @@ public void testTaskOwner() { taskService.deleteTask(task.getId(), true); } + @Test + public void testDeleteTaskCommentNullTaskId() { + try { + taskService.deleteTaskComment(null, "test"); + fail("BadUserRequestException expected"); + } catch (BadUserRequestException ae) { + testRule.assertTextPresent("taskId is null", ae.getMessage()); + } + } + + @Test + public void testDeleteTaskCommentNotExistingTaskId() { + try { + taskService.deleteTaskComment("notExistingId", "notExistingCommentId"); + fail("NullValueException expected"); + } catch (NullValueException ae) { + testRule.assertTextPresent("No task exists with taskId: notExistingId", ae.getMessage()); + } + } + + @Test + public void testDeleteTaskCommentNotExistingCommentId() { + Task task = taskService.newTask(); + taskService.saveTask(task); + String taskId = task.getId(); + + // Deleting non-existing comment should be silently ignored + taskService.deleteTaskComment(taskId, "notExistingCommentId"); + + // Finally, delete task + taskService.deleteTask(taskId, true); + } + + @Test + public void testDeleteTaskCommentWithoutProcessInstance() { + Task task = taskService.newTask(); + taskService.saveTask(task); + String taskId = task.getId(); + + //create a task comment + Comment comment = taskService.createComment(taskId, null, "aMessage"); + String commentId = comment.getId(); + + //delete a comment + taskService.deleteTaskComment(taskId, commentId); + + //make sure the comment is not there. + Comment shouldBeDeleted = taskService.getTaskComment(taskId, commentId); + assertNull(shouldBeDeleted); + + // Finally, delete task + taskService.deleteTask(taskId, true); + } + + @Deployment(resources = { "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml" }) + @Test + public void testDeleteTaskCommentWithProcessInstance() { + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + String taskId = task.getId(); + + //create a task comment + Comment comment = taskService.createComment(taskId, processInstance.getId(), "aMessage"); + String commentId = comment.getId(); + + //delete a comment + taskService.deleteTaskComment(taskId, commentId); + + //make sure the comment is not there. + Comment shouldBeDeleted = taskService.getTaskComment(taskId, commentId); + assertNull(shouldBeDeleted); + } + + @Test + public void testDeleteTaskCommentsNullTaskId() { + try { + taskService.deleteTaskComments(null); + fail("BadUserRequestException expected"); + } catch (BadUserRequestException ae) { + testRule.assertTextPresent("taskId is null", ae.getMessage()); + } + } + + @Test + public void testDeleteTaskCommentsNonExistingTaskId() { + try { + taskService.deleteTaskComments("nonExistingTaskId"); + fail("NullValueException expected"); + } catch (NullValueException ae) { + testRule.assertTextPresent("No task exists with taskId:", ae.getMessage()); + } + } + + @Test + public void testDeleteTaskCommentsNoComments() { + Task task = taskService.newTask(); + taskService.saveTask(task); + String taskId = task.getId(); + + // Deleting comments of a task that doesnt have any comments should silently ignored + taskService.deleteTaskComments(taskId); + + // Finally, delete task + taskService.deleteTask(taskId, true); + } + + @Test + public void testDeleteTaskComments() { + Task task = taskService.newTask(); + taskService.saveTask(task); + String taskId = task.getId(); + + //create a task comment + Comment comment = taskService.createComment(taskId, null, "aMessage"); + + //delete a comment + taskService.deleteTaskComments(taskId); + + //make sure the comment is not there. + Comment shouldBeDeleted = taskService.getTaskComment(taskId, comment.getId()); + assertNull(shouldBeDeleted); + + // Finally, delete task + taskService.deleteTask(taskId, true); + } + + @Deployment(resources = { "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml" }) + @Test + public void testDeleteProcessInstanceTaskComments() { + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + String taskId = task.getId(); + + //create a task comment + Comment comment = taskService.createComment(taskId, processInstance.getId(), "aMessage"); + + //delete a comment + taskService.deleteTaskComments(taskId); + + //make sure the comment is not there. + Comment shouldBeDeleted = taskService.getTaskComment(taskId, comment.getId()); + assertNull(shouldBeDeleted); + } + + @Test + public void testUpdateTaskCommentNullCommentId() { + Task task = taskService.newTask(); + taskService.saveTask(task); + String taskId = task.getId(); + try { + taskService.updateTaskComment(taskId, null, "aMessage"); + + fail("NullValueException expected"); + } catch (NullValueException ae) { + testRule.assertTextPresent("commentId is null", ae.getMessage()); + } finally { + taskService.deleteTask(task.getId(), true); + } + } + + @Test + public void testUpdateTaskCommentNullTaskId() { + Task task = taskService.newTask(); + taskService.saveTask(task); + String taskId = task.getId(); + Comment comment = taskService.createComment(taskId, null, "originalMessage"); + + try { + taskService.updateTaskComment(null, comment.getId(), "updatedMessage"); + fail("BadUserRequestException expected"); + } catch (BadUserRequestException ae) { + testRule.assertTextPresent("Both process instance and task ids are null", ae.getMessage()); + } finally { + taskService.deleteTask(taskId, true); + } + } + + @Test + public void testUpdateTaskCommentNullMessage() { + Task task = taskService.newTask(); + taskService.saveTask(task); + String taskId = task.getId(); + Comment comment = taskService.createComment(taskId, null, "originalMessage"); + + try { + taskService.updateTaskComment(taskId, comment.getId(), null); + fail("NullValueException expected"); + } catch (NullValueException ae) { + testRule.assertTextPresent("message is null", ae.getMessage()); + } finally { + taskService.deleteTask(task.getId(), true); + } + } + + @Test + public void testUpdateTaskCommentNotExistingCommentId() { + Task task = taskService.newTask(); + taskService.saveTask(task); + String taskId = task.getId(); + taskService.createComment(taskId, null, "originalMessage"); + String nonExistingCommentId = "notExistingCommentId"; + + try { + taskService.updateTaskComment(taskId, nonExistingCommentId, "updatedMessage"); + fail("NullValueException expected"); + } catch (NullValueException ae) { + testRule.assertTextPresent("No comment exists with commentId: " + nonExistingCommentId + " and taskId: " + taskId, + ae.getMessage()); + } finally { + taskService.deleteTask(task.getId(), true); + } + } + + @Test + public void testUpdateTaskComment() { + Task task = taskService.newTask(); + taskService.saveTask(task); + String taskId = task.getId(); + Comment comment = taskService.createComment(taskId, null, "originalMessage"); + String updatedMessage = "updatedMessage"; + + taskService.updateTaskComment(taskId, comment.getId(), updatedMessage); + + Comment actual = taskService.getTaskComment(taskId, comment.getId()); + + assertThat(actual).isNotNull(); + assertEquals(updatedMessage, actual.getFullMessage()); + // Finally, delete task + taskService.deleteTask(taskId, true); + } + + @Deployment(resources = { "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml" }) + @Test + public void testUpdateProcessTaskComment() { + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + String taskId = task.getId(); + + Comment comment = taskService.createComment(taskId, null, "originalMessage"); + String updatedMessage = "updatedMessage"; + + taskService.updateTaskComment(taskId, comment.getId(), updatedMessage); + + Comment actual = taskService.getTaskComment(taskId, comment.getId()); + + assertThat(actual).isNotNull(); + assertEquals(updatedMessage, actual.getFullMessage()); + } + + @Test + public void testDeleteProcessInstanceCommentNullId() { + try { + taskService.deleteProcessInstanceComment(null, null); + fail("BadUserRequestException expected"); + } catch (BadUserRequestException ae) { + testRule.assertTextPresent("processInstanceId is null", ae.getMessage()); + } + } + + @Deployment(resources = { "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml" }) + @Test + public void testDeleteProcessInstanceCommentNotExistingCommentId() { + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + + // Deleting non-existing comment should be silently ignored + taskService.deleteProcessInstanceComment(processInstance.getId(), "notExistingCommentId"); + } + + @Deployment(resources = { "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml" }) + @Test + public void testDeleteTaskProcessInstanceComment() { + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + String taskId = task.getId(); + String processInstanceId = processInstance.getId(); + + //create a task comment + Comment comment = taskService.createComment(taskId, null, "aMessage"); + + //delete a comment + taskService.deleteProcessInstanceComment(processInstanceId, comment.getId()); + + //make sure the comment is not there. + List shouldBeDeletedLst = taskService.getProcessInstanceComments(processInstanceId); + assertThat(shouldBeDeletedLst).isEmpty(); + } + + @Deployment(resources = { "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml" }) + @Test + public void testDeleteProcessInstanceComment() { + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + String processInstanceId = processInstance.getId(); + + //create a task comment + Comment comment = taskService.createComment(null, processInstanceId, "aMessage"); + + //delete a comment + taskService.deleteProcessInstanceComment(processInstanceId, comment.getId()); + + //make sure the comment is not there. + List shouldBeDeletedLst = taskService.getProcessInstanceComments(processInstanceId); + assertThat(shouldBeDeletedLst).isEmpty(); + } + + @Test + public void testDeleteProcessInstanceCommentsNullId() { + try { + taskService.deleteProcessInstanceComments(null); + fail("BadUserRequestException expected"); + } catch (BadUserRequestException ae) { + testRule.assertTextPresent("processInstanceId is null", ae.getMessage()); + } + } + + @Test + public void testDeleteProcessInstanceCommentsNonExistingId() { + try { + taskService.deleteProcessInstanceComments("nonExistingId"); + fail("NullValueException expected"); + } catch (NullValueException ae) { + testRule.assertTextPresent("No processInstance exists with processInstanceId:", ae.getMessage()); + } + } + + @Deployment(resources = { "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml" }) + @Test + public void testDeleteProcessInstanceCommentsNoComments() { + + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + + // Deleting comments of a task that doesn't have any comments should silently ignored + taskService.deleteProcessInstanceComments(processInstance.getId()); + } + + @Deployment(resources = { "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml" }) + @Test + public void testDeleteProcessInstanceCommentsWithoutTaskComments() { + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + String processInstanceId = processInstance.getId(); + + //create a task comment + taskService.createComment(null, processInstanceId, "messageOne"); + taskService.createComment(null, processInstanceId, "messageTwo"); + + //delete a comment + taskService.deleteProcessInstanceComments(processInstanceId); + + //make sure the comment is not there. + List shouldBeDeletedLst = taskService.getProcessInstanceComments(processInstanceId); + assertThat(shouldBeDeletedLst).isEmpty(); + } + + @Deployment(resources = { "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml" }) + @Test + public void testDeleteProcessInstanceCommentsWithTask() { + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + String taskId = task.getId(); + String processInstanceId = processInstance.getId(); + + //create a task comment + taskService.createComment(taskId, null, "messageOne"); + taskService.createComment(taskId, null, "messageTwo"); + + //delete a comment + taskService.deleteProcessInstanceComments(processInstanceId); + + //make sure the comment is not there. + List shouldBeDeletedLst = taskService.getProcessInstanceComments(processInstanceId); + assertThat(shouldBeDeletedLst).isEmpty(); + } + + @Deployment(resources = { "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml" }) + @Test + public void testUpdateProcessInstanceCommentNullCommentId() { + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + try { + taskService.updateProcessInstanceComment(processInstance.getId(), null, "aMessage"); + fail("NullValueException expected"); + } catch (NullValueException ae) { + testRule.assertTextPresent("commentId is null", ae.getMessage()); + } + } + + @Deployment(resources = { "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml" }) + @Test + public void testUpdateProcessInstanceCommentNullProcessInstanceId() { + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + + Comment comment = taskService.createComment(null, processInstance.getId(), "originalMessage"); + + try { + taskService.updateProcessInstanceComment(null, comment.getId(), "updatedMessage"); + fail("BadUserRequestException expected"); + } catch (BadUserRequestException ae) { + testRule.assertTextPresent("Both process instance and task ids are null", ae.getMessage()); + } + } + + @Deployment(resources = { "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml" }) + @Test + public void testUpdateProcessInstanceCommentNullMessage() { + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + String processInstanceId = processInstance.getId(); + + Comment comment = taskService.createComment(null, processInstanceId, "originalMessage"); + + try { + taskService.updateProcessInstanceComment(processInstanceId, comment.getId(), null); + fail("NullValueException expected"); + } catch (NullValueException ae) { + testRule.assertTextPresent("message is null", ae.getMessage()); + } + } + + @Deployment(resources = { "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml" }) + @Test + public void testUpdateProcessInstanceCommentNotExistingCommentId() { + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + String processInstanceId = processInstance.getId(); + + taskService.createComment(null, processInstanceId, "originalMessage"); + + String nonExistingCommentId = "notExistingCommentId"; + try { + taskService.updateProcessInstanceComment(processInstanceId, nonExistingCommentId, "updatedMessage"); + fail("NullValueException expected"); + } catch (NullValueException ae) { + testRule.assertTextPresent( + "No comment exists with commentId: " + nonExistingCommentId + " and processInstanceId: " + processInstanceId, + ae.getMessage()); + } + } + + @Deployment(resources = { "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml" }) + @Test + public void testUpdateProcessInstanceCommentWithTask() { + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + String taskId = task.getId(); + String processInstanceId = processInstance.getId(); + + Comment comment = taskService.createComment(taskId, processInstanceId, "originalMessage"); + String updatedMessage = "updatedMessage"; + + taskService.updateProcessInstanceComment(processInstanceId, comment.getId(), updatedMessage); + + List updateCommentLst = taskService.getProcessInstanceComments(processInstanceId); + + assertThat(updateCommentLst).isNotEmpty(); + assertThat(updateCommentLst).hasSize(1); + + Comment actual = updateCommentLst.get(0); + assertEquals(updatedMessage, actual.getFullMessage()); + } + + @Deployment(resources = { "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml" }) + @Test + public void testUpdateProcessInstanceCommentWithoutTask() { + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + String processInstanceId = processInstance.getId(); + + Comment comment = taskService.createComment(null, processInstanceId, "originalMessage"); + String updatedMessage = "updatedMessage"; + + taskService.updateProcessInstanceComment(processInstanceId, comment.getId(), updatedMessage); + + List updateCommentLst = taskService.getProcessInstanceComments(processInstanceId); + + assertThat(updateCommentLst).isNotEmpty(); + assertThat(updateCommentLst).hasSize(1); + + Comment actual = updateCommentLst.get(0); + assertEquals(updatedMessage, actual.getFullMessage()); + } + @Test public void testTaskComments() { int historyLevel = processEngineConfiguration.getHistoryLevel().getId();