Skip to content

Commit

Permalink
Add action invocation status INCOMPLETE.
Browse files Browse the repository at this point in the history
  • Loading branch information
Thisara-Welmilla committed Jan 6, 2025
1 parent b242fe4 commit 48a7790
Show file tree
Hide file tree
Showing 9 changed files with 298 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@
import org.wso2.carbon.identity.action.execution.model.ActionExecutionStatus;
import org.wso2.carbon.identity.action.execution.model.ActionInvocationErrorResponse;
import org.wso2.carbon.identity.action.execution.model.ActionInvocationFailureResponse;
import org.wso2.carbon.identity.action.execution.model.ActionInvocationIncompleteResponse;
import org.wso2.carbon.identity.action.execution.model.ActionInvocationSuccessResponse;
import org.wso2.carbon.identity.action.execution.model.ActionType;
import org.wso2.carbon.identity.action.execution.model.Error;
import org.wso2.carbon.identity.action.execution.model.ErrorStatus;
import org.wso2.carbon.identity.action.execution.model.Event;
import org.wso2.carbon.identity.action.execution.model.FailedStatus;
import org.wso2.carbon.identity.action.execution.model.Failure;
import org.wso2.carbon.identity.action.execution.model.Incomplete;
import org.wso2.carbon.identity.action.execution.model.IncompleteStatus;
import org.wso2.carbon.identity.action.execution.model.Success;

import java.util.Map;
Expand All @@ -47,6 +50,24 @@ ActionExecutionStatus<Success> processSuccessResponse(Map<String, Object> eventC
ActionInvocationSuccessResponse successResponse) throws
ActionExecutionResponseProcessorException;

/**
* This method processes the incomplete response received from the action execution.
*
* @param eventContext The event context.
* @param actionEvent The action event.
* @param incompleteResponse The incomplete response.
* @return The incomplete status.
* @throws ActionExecutionResponseProcessorException If an error occurs while processing the response.
*/
default ActionExecutionStatus<Incomplete> processIncompleteResponse(Map<String, Object> eventContext,
Event actionEvent, ActionInvocationIncompleteResponse incompleteResponse) throws
ActionExecutionResponseProcessorException {

return new IncompleteStatus.Builder()
.incomplete(new Incomplete(incompleteResponse.getOperations()))
.responseContext(eventContext).build();
}

default ActionExecutionStatus<Error> processErrorResponse(Map<String, Object> eventContext,
Event actionEvent,
ActionInvocationErrorResponse errorResponse) throws
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,18 @@ public interface ActionExecutorService {
ActionExecutionStatus execute(ActionType actionType, Map<String, Object> eventContext, String tenantDomain) throws
ActionExecutionException;

/**
* Resolve the actions by given the action id list and execute them.
*
* @param actionType Action Type.
* @param actionIdList Lis of action Ids of the actions that need to be executed.
* @param eventContext The event context of the corresponding flow.
* @param tenantDomain Tenant domain.
* @return {@link ActionExecutionStatus} The status of the action execution and the response context.
* @throws ActionExecutionException If an error occurs while executing the action.
*/
ActionExecutionStatus execute(ActionType actionType, String[] actionIdList,
Map<String, Object> eventContext, String tenantDomain)
throws ActionExecutionException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@
import org.wso2.carbon.identity.action.execution.model.ActionExecutionStatus;
import org.wso2.carbon.identity.action.execution.model.ActionInvocationErrorResponse;
import org.wso2.carbon.identity.action.execution.model.ActionInvocationFailureResponse;
import org.wso2.carbon.identity.action.execution.model.ActionInvocationIncompleteResponse;
import org.wso2.carbon.identity.action.execution.model.ActionInvocationResponse;
import org.wso2.carbon.identity.action.execution.model.ActionInvocationSuccessResponse;
import org.wso2.carbon.identity.action.execution.model.ActionType;
import org.wso2.carbon.identity.action.execution.model.AllowedOperation;
import org.wso2.carbon.identity.action.execution.model.Error;
import org.wso2.carbon.identity.action.execution.model.Failure;
import org.wso2.carbon.identity.action.execution.model.Incomplete;
import org.wso2.carbon.identity.action.execution.model.PerformableOperation;
import org.wso2.carbon.identity.action.execution.model.Request;
import org.wso2.carbon.identity.action.execution.model.Success;
Expand Down Expand Up @@ -106,6 +108,7 @@ public boolean isExecutionEnabled(ActionType actionType) {
* @param tenantDomain Tenant domain.
* @return Action execution status.
*/
@Override
public ActionExecutionStatus<?> execute(ActionType actionType, Map<String, Object> eventContext,
String tenantDomain) throws ActionExecutionException {

Expand All @@ -131,6 +134,7 @@ public ActionExecutionStatus<?> execute(ActionType actionType, Map<String, Objec
* @param tenantDomain Tenant domain.
* @return Action execution status.
*/
@Override
public ActionExecutionStatus<?> execute(ActionType actionType, String[] actionIdList,
Map<String, Object> eventContext, String tenantDomain)
throws ActionExecutionException {
Expand Down Expand Up @@ -310,6 +314,10 @@ private ActionExecutionStatus<?> processActionResponse(Action action,
return processSuccessResponse(action,
(ActionInvocationSuccessResponse) actionInvocationResponse.getResponse(),
eventContext, actionRequest, actionExecutionResponseProcessor);
} else if (actionInvocationResponse.isIncomplete()) {
return processIncompleteResponse(action,
(ActionInvocationIncompleteResponse) actionInvocationResponse.getResponse(),
eventContext, actionRequest, actionExecutionResponseProcessor);
} else if (actionInvocationResponse.isFailure() && actionInvocationResponse.getResponse() != null) {
return processFailureResponse(action, (ActionInvocationFailureResponse) actionInvocationResponse
.getResponse(), eventContext, actionRequest, actionExecutionResponseProcessor);
Expand All @@ -333,14 +341,35 @@ private ActionExecutionStatus<Success> processSuccessResponse(Action action,
logSuccessResponse(action, successResponse);

List<PerformableOperation> allowedPerformableOperations =
validatePerformableOperations(actionRequest, successResponse, action);
validatePerformableOperations(actionRequest, successResponse.getOperations(), action);
ActionInvocationSuccessResponse.Builder successResponseBuilder =
new ActionInvocationSuccessResponse.Builder().actionStatus(ActionInvocationResponse.Status.SUCCESS)
.operations(allowedPerformableOperations);
.operations(allowedPerformableOperations)
.data(successResponse.getData());
return actionExecutionResponseProcessor.processSuccessResponse(eventContext,
actionRequest.getEvent(), successResponseBuilder.build());
}

private ActionExecutionStatus<Incomplete> processIncompleteResponse(
Action action,
ActionInvocationIncompleteResponse incompleteResponse,
Map<String, Object> eventContext,
ActionExecutionRequest actionRequest,
ActionExecutionResponseProcessor actionExecutionResponseProcessor)
throws ActionExecutionResponseProcessorException {

//logSuccessResponse(action, successResponse);

List<PerformableOperation> allowedPerformableOperations =
validatePerformableOperations(actionRequest, incompleteResponse.getOperations(), action);
ActionInvocationIncompleteResponse.Builder incompleteResponseBuilder =
new ActionInvocationIncompleteResponse.Builder().actionStatus(
ActionInvocationResponse.Status.INCOMPLETE)
.operations(allowedPerformableOperations);
return actionExecutionResponseProcessor.processIncompleteResponse(eventContext,
actionRequest.getEvent(), incompleteResponseBuilder.build());
}

private ActionExecutionStatus<Error> processErrorResponse(Action action,
ActionInvocationErrorResponse errorResponse,
Map<String, Object> eventContext,
Expand Down Expand Up @@ -472,11 +501,11 @@ private String serializeFailureResponse(ActionInvocationFailureResponse response
}

private List<PerformableOperation> validatePerformableOperations(
ActionExecutionRequest request, ActionInvocationSuccessResponse response, Action action) {
ActionExecutionRequest request, List<PerformableOperation> operations, Action action) {

List<AllowedOperation> allowedOperations = request.getAllowedOperations();

List<PerformableOperation> allowedPerformableOperations = response.getOperations().stream()
List<PerformableOperation> allowedPerformableOperations = operations.stream()
.filter(performableOperation -> allowedOperations.stream()
.anyMatch(allowedOperation -> OperationComparator.compare(allowedOperation,
performableOperation)))
Expand All @@ -486,7 +515,7 @@ private List<PerformableOperation> validatePerformableOperations(
List<String> allowedOps = new ArrayList<>();
List<String> notAllowedOps = new ArrayList<>();

response.getOperations().forEach(operation -> {
operations.forEach(operation -> {
String operationDetails = "Operation: " + operation.getOp() + " Path: " + operation.getPath();
if (allowedPerformableOperations.contains(operation)) {
allowedOps.add(operationDetails);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
* Action Execution Status is the status object that is returned by the Action Executor Service after executing an
* action. It contains the status of the action execution and the response context.
*
* @param <T> Status type (i.e. SUCCESS {@link Success}, FAILED {@link Failure}, ERROR {@link Error})
* @param <T> Status type (i.e. SUCCESS {@link Success}, FAILED {@link Failure}, ERROR {@link Error},
* INCOMPLETE {@link Incomplete})
*/
public abstract class ActionExecutionStatus<T> {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); 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.wso2.carbon.identity.action.execution.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;

import java.util.List;

/**
* This class is used to represent the incomplete response of an action invocation.
* This response will contain the list of operations that need to be performed.
*/
@JsonDeserialize(builder = ActionInvocationIncompleteResponse.Builder.class)
public class ActionInvocationIncompleteResponse implements ActionInvocationResponse.APIResponse {

private final ActionInvocationResponse.Status actionStatus;

private final List<PerformableOperation> operations;

private ActionInvocationIncompleteResponse(Builder builder) {

this.actionStatus = builder.actionStatus;
this.operations = builder.operations;
}

@Override
public ActionInvocationResponse.Status getActionStatus() {

return actionStatus;
}

public List<PerformableOperation> getOperations() {

return operations;
}

/**
* This class is used to build the {@link ActionInvocationIncompleteResponse}.
*/
@JsonPOJOBuilder(withPrefix = "")
public static class Builder {

private ActionInvocationResponse.Status actionStatus;
private List<PerformableOperation> operations;

@JsonProperty("actionStatus")
public Builder actionStatus(ActionInvocationResponse.Status actionStatus) {

this.actionStatus = actionStatus;
return this;
}

@JsonProperty("operations")
public Builder operations(@JsonProperty("operations") List<PerformableOperation> operations) {

this.operations = operations;
return this;
}

public ActionInvocationIncompleteResponse build() {

if (this.actionStatus == null) {
throw new IllegalArgumentException("actionStatus must not be null.");
}

if (!ActionInvocationResponse.Status.INCOMPLETE.equals(actionStatus)) {
throw new IllegalArgumentException("actionStatus must be INCOMPLETE.");
}

if (this.operations == null) {
throw new IllegalArgumentException("operations must not be null.");
}

return new ActionInvocationIncompleteResponse(this);
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ public boolean isSuccess() {
return Status.SUCCESS.equals(actionStatus);
}

public boolean isIncomplete() {

return Status.INCOMPLETE.equals(actionStatus);
}

public boolean isFailure() {

return Status.FAILED.equals(actionStatus);
Expand All @@ -70,6 +75,7 @@ public String getErrorLog() {
*/
public enum Status {
SUCCESS,
INCOMPLETE,
FAILED,
ERROR
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import org.apache.commons.lang.StringUtils;

import java.util.List;

Expand All @@ -32,13 +33,14 @@
public class ActionInvocationSuccessResponse implements ActionInvocationResponse.APIResponse {

private final ActionInvocationResponse.Status actionStatus;

private final List<PerformableOperation> operations;
private final String data;

private ActionInvocationSuccessResponse(Builder builder) {

this.actionStatus = builder.actionStatus;
this.operations = builder.operations;
this.data = builder.data;
}

@Override
Expand All @@ -52,6 +54,11 @@ public List<PerformableOperation> getOperations() {
return operations;
}

public String getData() {

return data;
}

/**
* This class is used to build the {@link ActionInvocationSuccessResponse}.
*/
Expand All @@ -60,6 +67,7 @@ public static class Builder {

private ActionInvocationResponse.Status actionStatus;
private List<PerformableOperation> operations;
private String data = StringUtils.EMPTY;

@JsonProperty("actionStatus")
public Builder actionStatus(ActionInvocationResponse.Status actionStatus) {
Expand All @@ -75,6 +83,13 @@ public Builder operations(@JsonProperty("operations") List<PerformableOperation>
return this;
}

@JsonProperty("data")
public Builder data(@JsonProperty("data") String data) {

this.data = data;
return this;
}

public ActionInvocationSuccessResponse build() {

if (this.actionStatus == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); 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.wso2.carbon.identity.action.execution.model;

import java.util.List;

/**
* This interface models the Incomplete status.
* If the downstream extension needs to compose the responses with INCOMPLETE status and communicate it back,
* this class can be used by consumers to implement the model for that incomplete response.
*/
public class Incomplete {

private final List<PerformableOperation> performableOperations;

public Incomplete(List<PerformableOperation> incompleteReasons) {

this.performableOperations = incompleteReasons;
}

public List<PerformableOperation> getPerformableOperations() {

return performableOperations;
}
}
Loading

0 comments on commit 48a7790

Please sign in to comment.