Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.dotcms.rest.api.v1.workflow;

import com.dotmarketing.portlets.workflows.model.WorkflowScheme;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import org.immutables.value.Value;

@Value.Style(typeImmutable = "*", typeAbstract = "Abstract*")
@Value.Immutable
@JsonSerialize(as = ContentTypeWorkflowSchemesView.class)
@JsonDeserialize(as = ContentTypeWorkflowSchemesView.class)
@Schema(description = "Workflow schemes associated with a specific content type")
public interface AbstractContentTypeWorkflowSchemesView {

@JsonProperty("contentTypeId")
@Schema(
description = "Identifier of the content type",
requiredMode = Schema.RequiredMode.REQUIRED
)
String contentTypeId();

@JsonProperty("contentTypeSchemes")
@Schema(
description = "Workflow schemes associated with the content type",
requiredMode = Schema.RequiredMode.REQUIRED
)
List<WorkflowScheme> contentTypeSchemes();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.dotcms.rest.api.v1.workflow;

import com.dotcms.rest.ResponseEntityView;
import java.util.List;

/**
* Response entity wrapping a list of {@link ContentTypeWorkflowSchemesView}, one entry per
* content type, each pairing a content type identifier with its associated workflow schemes.
* Used by the {@code GET /api/v1/workflow/contenttypes/schemes} endpoint.
*/
public class ResponseEntityContentTypeWorkflowSchemesView extends
Comment thread
dario-daza marked this conversation as resolved.
ResponseEntityView<List<ContentTypeWorkflowSchemesView>> {

/**
* @param contentTypeWorkflowSchemesView list of content-type/scheme associations to return
*/
public ResponseEntityContentTypeWorkflowSchemesView(
final List<ContentTypeWorkflowSchemesView> contentTypeWorkflowSchemesView) {
super(contentTypeWorkflowSchemesView);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
import com.dotmarketing.portlets.contentlet.transform.DotTransformerBuilder;
import com.dotmarketing.portlets.structure.model.ContentletRelationships;
import com.dotmarketing.portlets.workflows.actionlet.WorkFlowActionlet;
import com.dotmarketing.portlets.workflows.business.DotWorkflowException;
import com.dotmarketing.portlets.workflows.business.WorkflowAPI;
import com.dotmarketing.portlets.workflows.business.WorkflowAPI.SystemAction;
import com.dotmarketing.portlets.workflows.model.SystemActionWorkflowActionMapping;
Expand Down Expand Up @@ -199,6 +200,7 @@
public class WorkflowResource {

public final static String VERSION = "1.0";
private static final int MAX_CONTENT_TYPE_IDS = 100;
private static final String LISTING = "listing";
private static final String EDITING = "editing";
private static final String ASSIGN = "assign";
Expand Down Expand Up @@ -536,6 +538,120 @@ public final Response findAllSchemesAndSchemesByContentType(
}
} // findAllSchemesAndSchemesByContentType.

/**
* Returns workflow schemes grouped by content type for a list of content type identifiers.
* Unknown or invalid IDs are silently skipped — the response only contains entries for IDs
* that were successfully resolved. At most {@value #MAX_CONTENT_TYPE_IDS} distinct IDs may
* be requested in a single call; exceeding this limit returns 400.
* Returns 401 if the user does not have the required backend permissions.
*
* @param request {@link HttpServletRequest}
* @param response {@link HttpServletResponse}
* @param contentTypeIds content type identifiers — repeat the parameter
* ({@code ?contentTypeIds=a&contentTypeIds=b}) or comma-separate values
* ({@code ?contentTypeIds=a,b}); both formats are supported and may be mixed
* @return {@link Response} containing a list of {@link ContentTypeWorkflowSchemesView},
* one entry per resolved content type
*/
@GET
@Path("/contenttypes/schemes")
@JSONP
@NoCache
@Produces(MediaType.APPLICATION_JSON)
@Operation(operationId = "getWorkflowSchemesByContentTypeList",
summary = "Find workflow schemes for multiple content types",
description = "Returns workflow [schemes](https://www.dotcms.com/docs/latest/managing-workflows#Schemes) " +
"grouped by content type for a list of content type identifiers. " +
"Each entry in the response maps a resolved content type to its associated schemes. " +
"Unknown or invalid IDs are silently skipped — only successfully resolved content types appear in the result " +
"(note: the single-content-type endpoint returns 404 for an unknown ID; this batch endpoint omits it instead). " +
"Maximum " + MAX_CONTENT_TYPE_IDS + " tokens per request (checked before deduplication).",
tags = {"Workflow"},
responses = {
@ApiResponse(responseCode = "200", description = "Schemes returned successfully",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = ResponseEntityContentTypeWorkflowSchemesView.class)
)
),
@ApiResponse(responseCode = "400", description = "More than " + MAX_CONTENT_TYPE_IDS + " content type IDs supplied"),
@ApiResponse(responseCode = "401", description = "User does not have permission")
Comment thread
dario-daza marked this conversation as resolved.
}
)
public final Response findAllSchemesByContentTypeList(
@Context final HttpServletRequest request,
@Context final HttpServletResponse response,
@QueryParam("contentTypeIds") @Parameter(
description = "Content type identifiers. Repeat the parameter " +
"(?contentTypeIds=a&contentTypeIds=b) or comma-separate values " +
"(?contentTypeIds=a,b). Both formats may be mixed. Max " +
MAX_CONTENT_TYPE_IDS + " distinct IDs."
) final List<String> contentTypeIds) {

final User user = new WebResource.InitBuilder(webResource)
.requiredBackendUser(true)
.requiredFrontendUser(false)
.requestAndResponse(request, response)
.rejectWhenNoUser(true)
.init()
.getUser();

try {

Logger.debug(this, "Getting the workflow schemes by content type list");

final String rawIds = String.join(",",
contentTypeIds == null ? Collections.emptyList() : contentTypeIds);

final String[] rawTokens = rawIds.split(",");

if (rawTokens.length > MAX_CONTENT_TYPE_IDS) {
throw new IllegalArgumentException(
"contentTypeIds exceeds the maximum allowed size of " + MAX_CONTENT_TYPE_IDS);
}

final List<String> ids = Arrays.stream(rawTokens)
.map(String::trim)
.filter(UtilMethods::isSet)
.distinct()
.collect(Collectors.toList());

final List<String> skippedIds = new ArrayList<>();

final List<ContentTypeWorkflowSchemesView> result = ids.stream()
.map(contentTypeId -> {
try {
return ContentTypeWorkflowSchemesView.builder()
.contentTypeId(contentTypeId)
.contentTypeSchemes(this.workflowHelper.findSchemesByContentType(
contentTypeId, user))
.build();
} catch (DotWorkflowException e) {
if (ExceptionUtil.causedBy(e, NotFoundInDbException.class)) {
skippedIds.add(contentTypeId);
return null;
}
throw e; // permission errors and unexpected causes propagate
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());

if (!skippedIds.isEmpty()) {
Logger.warn(this.getClass(), "Skipped " + skippedIds.size() +
" unknown content type ID(s): " + skippedIds);
}

return Response.ok(new ResponseEntityContentTypeWorkflowSchemesView(result)).build();

} catch (Exception e) {

Logger.error(this.getClass(),
"Exception on findAllSchemesByContentTypeList: " + e.getMessage(), e);
return ResponseUtil.mapExceptionResponse(e);

}
} // findAllSchemesByContentTypeList.

/**
* Return Steps associated to the scheme, 404 if does not exists. 401 if the user does not have permission.
* @param request HttpServletRequest
Expand Down Expand Up @@ -3172,7 +3288,7 @@ public final Response fireActionDefaultSinglePart(@Context final HttpServletRequ
.requestAndResponse(request, response)
.requiredAnonAccess(AnonymousAccess.WRITE)
.init();

try {

Logger.debug(this, ()-> "On Fire Action: systemAction = " + systemAction + ", inode = " + inode +
Expand Down Expand Up @@ -4912,7 +5028,7 @@ private Contentlet populateContentlet(final FireActionForm fireActionForm, final
if(constant instanceof ConstantField)
contentlet.getMap().put(constant.variable(), constant.values());
}

return contentlet;
} // populateContentlet.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.dotmarketing.portlets.templates.design.bean;

import com.dotcms.rest.api.v1.workflow.SchemesAndSchemesContentTypeView;
import com.dotmarketing.business.APILocator;
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.exception.DotSecurityException;
Expand Down
98 changes: 98 additions & 0 deletions dotCMS/src/main/webapp/WEB-INF/openapi/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19526,6 +19526,46 @@ paths:
summary: Finds workflow actions by content inode
tags:
- Workflow
/v1/workflow/contenttypes/schemes:
get:
description: "Returns workflow [schemes](https://www.dotcms.com/docs/latest/managing-workflows#Schemes)\
\ grouped by content type for a list of content type identifiers. Each entry\
\ in the response maps a resolved content type to its associated schemes.\
\ Unknown or invalid IDs are silently skipped — only successfully resolved\
\ content types appear in the result (note: the single-content-type endpoint\
\ returns 404 for an unknown ID; this batch endpoint omits it instead). Maximum\
\ 100 tokens per request (checked before deduplication)."
operationId: getWorkflowSchemesByContentTypeList
parameters:
- description: "Content type identifiers. Repeat the parameter (?contentTypeIds=a&contentTypeIds=b)\
\ or comma-separate values (?contentTypeIds=a,b). Both formats may be mixed.\
\ Max 100 distinct IDs."
in: query
name: contentTypeIds
schema:
type: array
items:
type: string
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/ResponseEntityContentTypeWorkflowSchemesView"
description: Schemes returned successfully
"400":
description: More than 100 content type IDs supplied
"401":
description: User does not have permission
"403":
description: Forbidden
"406":
description: Not Acceptable
"500":
description: Internal Server Error
summary: Find workflow schemes for multiple content types
tags:
- Workflow
/v1/workflow/contenttypes/{contentTypeVarOrId}/system/actions:
get:
description: "Returns a list of [default system actions](https://www.dotcms.com/docs/latest/managing-workflows#DefaultActions)\
Expand Down Expand Up @@ -24803,6 +24843,27 @@ components:
type: string
variable:
type: string
ContentTypeWorkflowSchemesView:
type: object
properties:
contentTypeId:
type: string
description: Identifier of the content type
contentTypeSchemes:
type: array
description: Workflow schemes associated with the content type
items:
$ref: "#/components/schemas/WorkflowScheme"
properties:
empty:
type: boolean
first:
$ref: "#/components/schemas/WorkflowScheme"
last:
$ref: "#/components/schemas/WorkflowScheme"
required:
- contentTypeId
- contentTypeSchemes
ContentView:
type: object
properties:
Expand Down Expand Up @@ -27786,6 +27847,18 @@ components:
$ref: "#/components/schemas/VariantResult"
last:
$ref: "#/components/schemas/VariantResult"
ImmutableListWorkflowScheme:
type: array
description: Workflow schemes associated with the content type
items:
$ref: "#/components/schemas/WorkflowScheme"
properties:
empty:
type: boolean
first:
$ref: "#/components/schemas/WorkflowScheme"
last:
$ref: "#/components/schemas/WorkflowScheme"
ImmutableMapDoubleQuantilePair:
type: object
additionalProperties:
Expand Down Expand Up @@ -30433,6 +30506,31 @@ components:
type: array
items:
type: string
ResponseEntityContentTypeWorkflowSchemesView:
type: object
properties:
entity:
type: array
items:
$ref: "#/components/schemas/ContentTypeWorkflowSchemesView"
errors:
type: array
items:
$ref: "#/components/schemas/ErrorEntity"
i18nMessagesMap:
type: object
additionalProperties:
type: string
messages:
type: array
items:
$ref: "#/components/schemas/MessageEntity"
pagination:
$ref: "#/components/schemas/Pagination"
permissions:
type: array
items:
type: string
ResponseEntityContentView:
type: object
properties:
Expand Down
Loading
Loading