diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/workflow/WorkflowDefaultActionView.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/workflow/WorkflowDefaultActionView.java index dcf2b2eec519..41f4b8b39614 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/workflow/WorkflowDefaultActionView.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/workflow/WorkflowDefaultActionView.java @@ -2,6 +2,7 @@ import com.dotmarketing.portlets.workflows.model.WorkflowAction; import com.dotmarketing.portlets.workflows.model.WorkflowScheme; +import com.dotmarketing.portlets.workflows.model.WorkflowStep; /** * View just to encapsulate a scheme and an action for the default workflow actions @@ -11,11 +12,13 @@ public class WorkflowDefaultActionView { private final WorkflowScheme scheme; private final WorkflowAction action; + private final WorkflowStep firstStep; - public WorkflowDefaultActionView(WorkflowScheme scheme, WorkflowAction action) { + public WorkflowDefaultActionView(final WorkflowScheme scheme, final WorkflowAction action, final WorkflowStep step) { this.scheme = scheme; this.action = action; + this.firstStep = step; } public WorkflowScheme getScheme() { @@ -25,4 +28,8 @@ public WorkflowScheme getScheme() { public WorkflowAction getAction() { return action; } + + public WorkflowStep getFirstStep() { + return firstStep; + } } diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/workflow/WorkflowResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/workflow/WorkflowResource.java index 3280af829666..7df87cebd624 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/workflow/WorkflowResource.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/workflow/WorkflowResource.java @@ -5211,7 +5211,7 @@ public final Response copyScheme(@Context final HttpServletRequest httpServletRe @ApiResponse(responseCode = "500", description = "Internal Server Error") } ) - public final Response findAvailableDefaultActionsByContentType(@Context final HttpServletRequest request, + public final ResponseEntityDefaultWorkflowActionsView findAvailableDefaultActionsByContentType(@Context final HttpServletRequest request, @Context final HttpServletResponse response, @PathParam("contentTypeId") @Parameter( required = true, @@ -5219,21 +5219,14 @@ public final Response findAvailableDefaultActionsByContentType(@Context final Ht "Example ID: `c541abb1-69b3-4bc5-8430-5e09e5239cc8` (Default page content type)\n\n" + "Example Variable: `htmlpageasset` (Default page content type)", schema = @Schema(type = "string") - ) final String contentTypeId) { + ) final String contentTypeId) throws NotFoundInDbException { final InitDataObject initDataObject = this.webResource.init (null, request, response, true, null); - try { Logger.debug(this, () -> "Getting the available workflow schemes default action for the ContentType: " + contentTypeId ); final List actions = this.workflowHelper.findAvailableDefaultActionsByContentType(contentTypeId, initDataObject.getUser()); - return Response.ok(new ResponseEntityView<>(actions)).build(); // 200 - } catch (Exception e) { - Logger.error(this.getClass(), - "Exception on find Available Default Actions exception message: " + e.getMessage(), e); - return ResponseUtil.mapExceptionResponse(e); - } - + return new ResponseEntityDefaultWorkflowActionsView(actions); } // findAvailableDefaultActionsByContentType. /** diff --git a/dotCMS/src/main/java/com/dotcms/workflow/helper/WorkflowHelper.java b/dotCMS/src/main/java/com/dotcms/workflow/helper/WorkflowHelper.java index 43ed7d2b3ad1..674d9c41dfb7 100644 --- a/dotCMS/src/main/java/com/dotcms/workflow/helper/WorkflowHelper.java +++ b/dotCMS/src/main/java/com/dotcms/workflow/helper/WorkflowHelper.java @@ -75,6 +75,7 @@ import com.liferay.portal.language.LanguageUtil; import com.liferay.portal.model.User; import com.liferay.util.StringPool; +import io.vavr.control.Try; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.lang.time.StopWatch; import org.apache.velocity.context.Context; @@ -1772,9 +1773,10 @@ public WorkflowAction getAction() { * @return List */ @CloseDBIfOpened - public List findAvailableDefaultActionsByContentType(final String contentTypeId, final User user) { + public List findAvailableDefaultActionsByContentType(final String contentTypeId, final User user) throws NotFoundInDbException { final ContentTypeAPI contentTypeAPI = APILocator.getContentTypeAPI(user); final ImmutableList.Builder results = new ImmutableList.Builder<>(); + final Map steps = new HashMap<>(); try { Logger.debug(this, () -> "Getting the available default workflows actions by content type: " + contentTypeId); @@ -1782,11 +1784,17 @@ public List findAvailableDefaultActionsByContentType( final List actions = this.workflowAPI.findAvailableDefaultActionsByContentType(contentTypeAPI.find(contentTypeId), user); for (final WorkflowAction action : actions){ final WorkflowScheme scheme = this.workflowAPI.findScheme(action.getSchemeId()); - final WorkflowDefaultActionView value = new WorkflowDefaultActionView(scheme, action); + steps.computeIfAbsent(scheme.getId(), k -> Try.of(()->this.workflowAPI.findFirstStep(scheme.getId()).orElse(null)).get()); + final WorkflowDefaultActionView value = new WorkflowDefaultActionView(scheme, action, steps.get(scheme.getId())); results.add(value); } - } catch (DotDataException | DotSecurityException e) { + } catch (NotFoundInDbException e) { + Logger.error(this, e.getMessage()); + Logger.debug(this, e.getMessage(), e); + throw e; + } + catch (DotDataException | DotSecurityException e) { Logger.error(this, e.getMessage()); Logger.debug(this, e.getMessage(), e); @@ -1888,9 +1896,11 @@ public List findInitialAvailableActionsByContentType( */ private List buildDefaultActionsViewObj(final List actions) throws DotDataException, DotSecurityException { final ImmutableList.Builder results = new ImmutableList.Builder<>(); + final Map steps = new HashMap<>(); for (final WorkflowAction action : actions) { final WorkflowScheme scheme = this.workflowAPI.findScheme(action.getSchemeId()); - final WorkflowDefaultActionView value = new WorkflowDefaultActionView(scheme, action); + steps.computeIfAbsent(scheme.getId(), k -> Try.of(()->this.workflowAPI.findFirstStep(scheme.getId()).orElse(null)).get()); + final WorkflowDefaultActionView value = new WorkflowDefaultActionView(scheme, action, steps.get(scheme.getId())); results.add(value); } return results.build(); diff --git a/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/workflow/WorkflowResourceLicenseIntegrationTest.java b/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/workflow/WorkflowResourceLicenseIntegrationTest.java index cf981ce3066e..6224ab012342 100644 --- a/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/workflow/WorkflowResourceLicenseIntegrationTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/workflow/WorkflowResourceLicenseIntegrationTest.java @@ -32,6 +32,7 @@ import com.dotmarketing.db.LocalTransaction; import com.dotmarketing.portlets.contentlet.business.ContentletAPI; import com.dotmarketing.portlets.contentlet.model.Contentlet; +import com.dotmarketing.portlets.workflows.business.DotWorkflowException; import com.dotmarketing.portlets.workflows.business.WorkflowAPI; import com.dotmarketing.portlets.workflows.business.WorkflowAPIImpl; import com.dotmarketing.portlets.workflows.model.WorkflowAction; @@ -45,7 +46,6 @@ import org.junit.Test; import javax.servlet.http.HttpServletRequest; - import javax.servlet.http.HttpServletResponse; import javax.ws.rs.container.AsyncResponse; import javax.ws.rs.core.Response; @@ -73,9 +73,9 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -614,17 +614,16 @@ public void Find_Available_Actions_Invalid_License() throws Exception { public void Find_Available_Default_Actions_Invalid_License() throws Exception { final HttpServletRequest request = mock(HttpServletRequest.class); final ContentType contentType = new ContentTypeDataGen().nextPersisted();// Uses the System Workflow by default - final Response response = nonLicenseWorkflowResource.findAvailableDefaultActionsByContentType(request, new EmptyHttpResponse(), contentType.id()); - assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); - ResponseEntityView ev = ResponseEntityView.class.cast(response.getEntity()); - List actions = List.class.cast(ev.getEntity()); + final ResponseEntityDefaultWorkflowActionsView response = nonLicenseWorkflowResource.findAvailableDefaultActionsByContentType(request, new EmptyHttpResponse(), contentType.id()); + assertNotNull(response); + List actions = response.getEntity(); for(WorkflowDefaultActionView av:actions){ assertTrue(av.getScheme().isSystem()); } } @SuppressWarnings("unchecked") - @Test + @Test(expected = DotWorkflowException.class) public void Find_Available_Default_Actions_No_Read_Permission_Invalid_License() throws Exception { @@ -637,10 +636,8 @@ public void Find_Available_Default_Actions_No_Read_Permission_Invalid_License() BaseContentType.CONTENT, editPermission, role.getId()); final HttpServletRequest request = mock(HttpServletRequest.class); - final Response findResponse = nonLicenseWorkflowResource + final ResponseEntityDefaultWorkflowActionsView findResponse = nonLicenseWorkflowResource .findAvailableDefaultActionsByContentType(request, new EmptyHttpResponse(), contentType.id()); - assertEquals(Status.FORBIDDEN.getStatusCode(), findResponse.getStatus()); - assertEquals(ACCESS_CONTROL_HEADER_PERMISSION_VIOLATION, findResponse.getHeaderString("access-control")); } finally { if(contentType != null){ APILocator.getContentTypeAPI(APILocator.systemUser()).delete(contentType); diff --git a/dotcms-postman/src/main/resources/postman/Workflow_Resource_Tests.json b/dotcms-postman/src/main/resources/postman/Workflow_Resource_Tests.json index dc680b8c93d9..008718ee28fb 100644 --- a/dotcms-postman/src/main/resources/postman/Workflow_Resource_Tests.json +++ b/dotcms-postman/src/main/resources/postman/Workflow_Resource_Tests.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "679e6657-065b-41a2-b666-672f59b9e95c", + "_postman_id": "769562e3-1639-4158-b011-3a6e3d68cf06", "name": "Workflow Resource Tests [/api/v1/workflows]", "description": "Test the necesary validations to every end point of the worlflow resource ", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", @@ -4109,34 +4109,29 @@ "", "pm.test(\"Valid response\", function () {", " pm.expect(pm.response.text()).to.include(\"Save\");", + "});", + "", + "", + "pm.test(\"Response has expected properties\", function () {", + " const jsonData = pm.response.json();", + " pm.expect(jsonData.entity[0]).to.have.property(\"action\");", + " pm.expect(jsonData.entity[0]).to.have.property(\"firstStep\");", + " pm.expect(jsonData.entity[0]).to.have.property(\"scheme\");", "});" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], "request": { "auth": { - "type": "basic", - "basic": [ - { - "key": "password", - "value": "admin", - "type": "string" - }, + "type": "bearer", + "bearer": [ { - "key": "username", - "value": "admin@dotcms.com", + "key": "token", + "value": "{{jwt}}", "type": "string" - }, - { - "key": "saveHelperData", - "type": "any" - }, - { - "key": "showPassword", - "value": false, - "type": "boolean" } ] }, @@ -4183,32 +4178,19 @@ "", "" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], "request": { "auth": { - "type": "basic", - "basic": [ - { - "key": "password", - "value": "admin", - "type": "string" - }, + "type": "bearer", + "bearer": [ { - "key": "username", - "value": "admin@dotcms.com", + "key": "token", + "value": "{{jwt}}", "type": "string" - }, - { - "key": "saveHelperData", - "type": "any" - }, - { - "key": "showPassword", - "value": false, - "type": "boolean" } ] }, @@ -18245,194 +18227,6 @@ "response": [] } ] - }, - { - "name": "Commentable", - "item": [ - { - "name": "CreateContentPreconditions", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200 \", function () {", - " pm.response.to.have.status(200);", - "});", - "", - "", - "var jsonData = pm.response.json();", - "pm.collectionVariables.set(\"contentletIdentifier\", jsonData.entity.identifier);", - "pm.collectionVariables.set(\"contentletInode\", jsonData.entity.inode);", - "", - "" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "packages": {}, - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{jwt}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"contentlet\": {\n \"contentType\": \"webPageContent\",\n \"title\": \"Test Title\",\n \"body\": \"Test Body\",\n \"contentHost\": \"default\"\n }\n}" - }, - "url": { - "raw": "{{serverURL}}/api/v1/workflow/actions/default/fire/PUBLISH", - "host": [ - "{{serverURL}}" - ], - "path": [ - "api", - "v1", - "workflow", - "actions", - "default", - "fire", - "PUBLISH" - ] - }, - "description": "Fire any action using the actionId\n\nOptional: If you pass ?inode={inode}, you don't need body here.\n\n@Path(\"/actions/{actionId}/fire\")" - }, - "response": [] - }, - { - "name": "CheckCommentable", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.have.status(200);", - "});", - "", - "pm.test(\"Response has expected properties\", function () {", - " const jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property(\"entity\");", - " pm.expect(jsonData).to.have.property(\"errors\");", - " pm.expect(jsonData).to.have.property(\"i18nMessagesMap\");", - " pm.expect(jsonData).to.have.property(\"messages\");", - " pm.expect(jsonData).to.have.property(\"pagination\");", - " pm.expect(jsonData).to.have.property(\"permissions\");", - "});", - "", - "pm.test(\"Entity is an array and contains items\", function () {", - " const entity = pm.response.json().entity;", - " pm.expect(entity).to.be.an(\"array\").that.is.not.empty;", - "});", - "", - "pm.test(\"Each entity item has expected properties\", function () {", - " const entity = pm.response.json().entity;", - " entity.forEach(item => {", - " pm.expect(item).to.have.property(\"actionInputs\").that.is.an(\"array\");", - " pm.expect(item).to.have.property(\"assignable\").that.is.a(\"boolean\");", - " pm.expect(item).to.have.property(\"commentable\").that.is.a(\"boolean\");", - " pm.expect(item).to.have.property(\"condition\").that.is.a(\"string\");", - " pm.expect(item).to.have.property(\"hasArchiveActionlet\").that.is.a(\"boolean\");", - " pm.expect(item).to.have.property(\"hasDeleteActionlet\").that.is.a(\"boolean\");", - " pm.expect(item).to.have.property(\"hasDestroyActionlet\").that.is.a(\"boolean\");", - " pm.expect(item).to.have.property(\"hasMoveActionletActionlet\").that.is.a(\"boolean\");", - " pm.expect(item).to.have.property(\"hasMoveActionletHasPathActionlet\").that.is.a(\"boolean\");", - " pm.expect(item).to.have.property(\"hasOnlyBatchActionlet\").that.is.a(\"boolean\");", - " pm.expect(item).to.have.property(\"hasPublishActionlet\").that.is.a(\"boolean\");", - " pm.expect(item).to.have.property(\"hasPushPublishActionlet\").that.is.a(\"boolean\");", - " pm.expect(item).to.have.property(\"hasSaveActionlet\").that.is.a(\"boolean\");", - " pm.expect(item).to.have.property(\"hasUnarchiveActionlet\").that.is.a(\"boolean\");", - " pm.expect(item).to.have.property(\"hasUnpublishActionlet\").that.is.a(\"boolean\");", - " pm.expect(item).to.have.property(\"hasCommentActionlet\").that.is.a(\"boolean\");", - " pm.expect(item).to.have.property(\"icon\").that.is.a(\"string\");", - " pm.expect(item).to.have.property(\"id\").that.is.a(\"string\");", - " pm.expect(item).to.have.property(\"name\").that.is.a(\"string\");", - " pm.expect(item).to.have.property(\"nextAssign\").that.is.a(\"string\");", - " pm.expect(item).to.have.property(\"nextStep\").that.is.a(\"string\");", - " pm.expect(item).to.have.property(\"nextStepCurrentStep\").that.is.a(\"boolean\");", - " pm.expect(item).to.have.property(\"order\").that.is.a(\"number\");", - " pm.expect(item).to.have.property(\"owner\").that.is.null;", - " pm.expect(item).to.have.property(\"hasResetActionlet\").that.is.a(\"boolean\");", - " pm.expect(item).to.have.property(\"roleHierarchyForAssign\").that.is.a(\"boolean\");", - " pm.expect(item).to.have.property(\"schemeId\").that.is.a(\"string\");", - " pm.expect(item).to.have.property(\"showOn\").that.is.an(\"array\");", - " ", - " const allowedShowOnValues = [\"NEW\", \"LISTING\", \"PUBLISHED\", \"UNLOCKED\", \"ARCHIVED\", \"UNPUBLISHED\", \"EDITING\", \"LOCKED\"];", - " item.showOn.forEach(status => {", - " pm.expect(allowedShowOnValues).to.include(status);", - " });", - " });", - "});", - "", - "pm.test(\"Errors array is empty\", function () {", - " const errors = pm.response.json().errors;", - " pm.expect(errors).to.be.an(\"array\").that.is.empty;", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{jwt}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{serverURL}}/api/v1/workflow/contentlet/{{contentletInode}}/actions?renderMode=EDITING", - "host": [ - "{{serverURL}}" - ], - "path": [ - "api", - "v1", - "workflow", - "contentlet", - "{{contentletInode}}", - "actions" - ], - "query": [ - { - "key": "renderMode", - "value": "EDITING" - } - ] - } - }, - "response": [] - } - ] } ], "event": [