diff --git a/.gitignore b/.gitignore index a1c2a23..a69f5c4 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,7 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* + +.idea/ +*.iml +target diff --git a/components/org.wso2.carbon.identity.workflow.engine/pom.xml b/components/org.wso2.carbon.identity.workflow.engine/pom.xml new file mode 100644 index 0000000..4ded88a --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/pom.xml @@ -0,0 +1,106 @@ + + + 4.0.0 + + default.workflow.engine + org.wso2.carbon.identity.workflow.engine + 1.0-SNAPSHOT + ../../pom.xml + + workflow.engine + bundle + WSO2 Carbon - Workflow Engine Simple + + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.configuration.mgt.core + + + org.wso2.carbon.identity.framework + org.wso2.carbon.user.mgt.common + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.workflow.mgt + + + org.apache.felix + org.apache.felix.scr.ds-annotations + + + org.wso2.carbon.identity.framework + org.wso2.carbon.user.mgt + + + org.testng + testng + + + org.powermock + powermock-api-mockito + + + org.powermock + powermock-module-testng + test + + + com.h2database + h2 + test + + + org.ops4j.pax.logging + pax-logging-api + + + + + + + org.apache.felix + maven-bundle-plugin + 3.2.0 + true + + NONE + + ${buildNumber} + + org.wso2.carbon.identity.workflow.engine.internal, + + + !org.wso2.carbon.identity.workflow.engine.internal, + org.wso2.carbon.identity.workflow.engine.*; + + + + + + org.codehaus.mojo + findbugs-maven-plugin + ${maven.findbugsplugin.version} + + findbugs-exclude.xml + Max + Low + true + ${project.build.directory}/findbugs + + + + analyze-compile + compile + + check + + + + + + + + diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/ApprovalEventService.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/ApprovalEventService.java new file mode 100644 index 0000000..a399687 --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/ApprovalEventService.java @@ -0,0 +1,541 @@ +package org.wso2.carbon.identity.workflow.engine; + +import org.apache.commons.collections.CollectionUtils; +import org.wso2.carbon.context.CarbonContext; +import org.wso2.carbon.identity.workflow.engine.dto.PropertyDTO; +import org.wso2.carbon.identity.workflow.engine.dto.StateDTO; +import org.wso2.carbon.identity.workflow.engine.dto.TaskDataDTO; +import org.wso2.carbon.identity.workflow.engine.dto.TaskSummaryDTO; +import org.wso2.carbon.identity.workflow.engine.exception.WorkflowEngineClientException; +import org.wso2.carbon.identity.workflow.engine.exception.WorkflowEngineException; +import org.wso2.carbon.identity.workflow.engine.exception.WorkflowEngineServerException; +import org.wso2.carbon.identity.workflow.engine.internal.dao.WorkflowEventRequestDAO; +import org.wso2.carbon.identity.workflow.engine.internal.dao.impl.WorkflowEventRequestDAOImpl; +import org.wso2.carbon.identity.workflow.engine.model.PagePagination; +import org.wso2.carbon.identity.workflow.engine.model.TStatus; +import org.wso2.carbon.identity.workflow.engine.model.TaskDetails; +import org.wso2.carbon.identity.workflow.engine.model.TaskModel; +import org.wso2.carbon.identity.workflow.engine.model.TaskParam; +import org.wso2.carbon.identity.workflow.engine.util.WorkflowEngineConstants; +import org.wso2.carbon.identity.workflow.mgt.WorkflowExecutorManagerService; +import org.wso2.carbon.identity.workflow.mgt.WorkflowExecutorManagerServiceImpl; +import org.wso2.carbon.identity.workflow.mgt.WorkflowManagementService; +import org.wso2.carbon.identity.workflow.mgt.WorkflowManagementServiceImpl; +import org.wso2.carbon.identity.workflow.mgt.bean.Parameter; +import org.wso2.carbon.identity.workflow.mgt.bean.RequestParameter; +import org.wso2.carbon.identity.workflow.mgt.bean.WorkflowAssociation; +import org.wso2.carbon.identity.workflow.mgt.callback.WSWorkflowCallBackService; +import org.wso2.carbon.identity.workflow.mgt.callback.WSWorkflowResponse; +import org.wso2.carbon.identity.workflow.mgt.dto.WorkflowRequest; +import org.wso2.carbon.identity.workflow.mgt.exception.InternalWorkflowException; +import org.wso2.carbon.identity.workflow.mgt.exception.WorkflowException; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Call internal osgi services to perform user's approval task related operations. + */ +public class ApprovalEventService { + + private static final String PENDING = "PENDING"; + private static final String APPROVED = "APPROVED"; + private static final String REJECTED = "REJECTED"; + private static final String RELEASED = "RELEASED"; + private static final String CLAIMED = "CLAIMED"; + private static final Integer LIMIT = 20; + private static final Integer OFFSET = 0; + protected long localCreatedTime; + + /** + * Search available approval tasks for the current authenticated user. + * + * @param limit number of records to be returned. + * @param offset start page. + * @param status state of the tasks [RESERVED, READY or COMPLETED]. + * @return taskSummaryDTO list. + */ + public List listTasks(Integer limit, Integer offset, List status) { + + try { + PagePagination pagePagination = new PagePagination(); + if (limit == null || offset == null) { + pagePagination.setPageSize(LIMIT); + pagePagination.setPageNumber(OFFSET); + } + + if (limit != null && limit > 0) { + pagePagination.setPageSize(limit); + } + if (offset != null && offset > 0) { + pagePagination.setPageNumber(offset); + } + + Set taskSummaryDTOs = null; + List tasks = listTasksOfApprovers(status); + int taskListSize = tasks.size(); + for (int i = 0; i < taskListSize; ++i) { + taskSummaryDTOs = new HashSet<>(tasks); + } + if (taskSummaryDTOs == null) { + return new ArrayList<>(0); + } + return new ArrayList<>(taskSummaryDTOs); + } catch (WorkflowEngineServerException e) { + throw new WorkflowEngineClientException( + WorkflowEngineConstants.ErrorMessages.ERROR_OCCURRED_WHILE_RETRIEVING_APPROVALS_FOR_USER.getCode(), + WorkflowEngineConstants.ErrorMessages.ERROR_OCCURRED_WHILE_RETRIEVING_APPROVALS_FOR_USER. + getDescription()); + } + } + + private List listTasksOfApprovers(List status) { + + WorkflowEventRequestDAO workflowEventRequestDAO = new WorkflowEventRequestDAOImpl(); + DefaultWorkflowEventRequest defaultWorkflowEventRequest = new DefaultWorkflowEventRequestService(); + List allRequestsList = getAllRequestRelatedUserAndRole(); + List taskSummaryDTOList = new ArrayList<>(); + for (String task : allRequestsList) { + TaskSummaryDTO summeryDTO = new TaskSummaryDTO(); + String eventId = getTaskRelatedStatus(task, status); + if (eventId != null) { + WorkflowRequest request = getWorkflowRequest(eventId); + TaskDetails taskDetails = getTaskDetails(request); + String eventType = workflowEventRequestDAO.getEventType(request.getUuid()); + String taskId = defaultWorkflowEventRequest.getApprovalOfRequest(request.getUuid()); + String taskStatus = workflowEventRequestDAO.getTaskStatusOfRequest(taskId); + String[] taskStatusValue = taskStatus.split(",", 0); + String workflowID = workflowEventRequestDAO.getWorkflowID(taskId); + String workflowName = workflowEventRequestDAO.getWorkflowName(workflowID); + String entityNameOfRequest = workflowEventRequestDAO.getEntityNameOfRequest(request.getUuid()); + Timestamp createdTime = workflowEventRequestDAO.getCreatedAtTimeInMill(request.getUuid()); + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(createdTime.getTime()); + long cal = calendar.getTimeInMillis(); + setCreatedTime(cal); + summeryDTO.setId(taskId.concat(" " + workflowName).concat(WorkflowEngineConstants.ParameterName. + ENTITY_NAME + entityNameOfRequest)); + summeryDTO.setName(WorkflowEngineConstants.ParameterName.APPROVAL_TASK); + summeryDTO.setTaskType(eventType); + summeryDTO.setPresentationName(taskDetails.getTaskSubject()); + summeryDTO.setPresentationSubject(taskDetails.getTaskDescription()); + summeryDTO.setCreatedTimeInMillis(String.valueOf(getCreatedTime())); + summeryDTO.setPriority(WorkflowEngineConstants.ParameterName.PRIORITY); + summeryDTO.setStatus(TaskSummaryDTO.StatusEnum.valueOf(taskStatusValue[0])); + taskSummaryDTOList.add(summeryDTO); + } + } + return taskSummaryDTOList; + } + + private List getAllRequestRelatedUserAndRole() { + + String userName = CarbonContext.getThreadLocalCarbonContext().getUsername(); + WorkflowEventRequestDAO workflowEventRequestDAO = new WorkflowEventRequestDAOImpl(); + List roleNames = getRoleNamesFromUser(userName); + List roleRequestsList; + List lst = new ArrayList<>(); + for (String roleName : roleNames) { + String[] names = roleName.split("/", 0); + String[] newName = roleName.split("_", 0); + if (names[0].equals(WorkflowEngineConstants.ParameterName.APPLICATION_USER)) { + roleRequestsList = workflowEventRequestDAO.getRequestsList(roleName); + lst.addAll(roleRequestsList); + } else if (newName[0].equals(WorkflowEngineConstants.ParameterName.SYSTEM_USER)) { + String newRoleName = WorkflowEngineConstants.ParameterName.SYSTEM_PRIMARY_USER.concat(names[0]); + roleRequestsList = workflowEventRequestDAO.getRequestsList(newRoleName); + lst.addAll(roleRequestsList); + } else { + String newRoleName = WorkflowEngineConstants.ParameterName.INTERNAL_USER.concat(names[0]); + roleRequestsList = workflowEventRequestDAO.getRequestsList(newRoleName); + lst.addAll(roleRequestsList); + } + } + + List userRequestList = workflowEventRequestDAO.getRequestsList(userName); + return Stream.concat(lst.stream(), userRequestList.stream()).collect(Collectors.toList()); + } + + private List getRoleNamesFromUser(String approverName) { + + WorkflowEventRequestDAO workflowEventRequestDAO = new WorkflowEventRequestDAOImpl(); + List roleIDList = workflowEventRequestDAO.getRolesID(approverName); + List roleNameList; + List lst = new ArrayList<>(); + for (Integer roleId : roleIDList) { + roleNameList = workflowEventRequestDAO.getRoleNames(roleId); + lst.addAll(roleNameList); + } + return new ArrayList<>(lst); + } + + private String getTaskRelatedStatus(String requestId, List status) { + + WorkflowEventRequestDAO workflowEventRequestDAO = new WorkflowEventRequestDAOImpl(); + TStatus[] tStatuses = getRequiredTStatuses(status); + String taskStatus = workflowEventRequestDAO.getStatusOfTask(requestId); + String[] taskStatusValue = taskStatus.split(",", 0); + String eventId = null; + String value; + for (TStatus tStatus : tStatuses) { + value = tStatus.getTStatus(); + if (value.equals(taskStatusValue[0])) { + eventId = requestId; + break; + } + } + return eventId; + } + + private long getCreatedTime() { + + return this.localCreatedTime; + } + + private void setCreatedTime(long param) { + + this.localCreatedTime = param; + } + + /** + * Get details of a task identified by the taskId. + * + * @param task the unique ID. + * @return TaskDataDto object. + */ + public TaskDataDTO getTaskData(String task) { + + try { + WorkflowEventRequestDAO workflowEventRequestDAO = new WorkflowEventRequestDAOImpl(); + String[] taskArray = task.split(" ", 0); + String taskId = taskArray[0]; + String requestId = workflowEventRequestDAO.getRequestID(taskId); + WorkflowRequest request = getWorkflowRequest(requestId); + TaskDetails taskDetails = getTaskDetails(request); + String initiator = workflowEventRequestDAO.getInitiatedUser(requestId); + List approvers = workflowEventRequestDAO.listApprovers(taskId); + Map assigneeMap = null; + for (String assignee : approvers) { + assigneeMap = new HashMap<>(); + assigneeMap.put(WorkflowEngineConstants.ParameterName.ASSIGNEE_TYPE, assignee); + } + List params = getRequestParameters(request); + List properties = getPropertyDTOs(params); + TaskDataDTO taskDataDTO = new TaskDataDTO(); + taskDataDTO.setId(taskId); + taskDataDTO.setSubject(taskDetails.getTaskSubject()); + taskDataDTO.setDescription(taskDetails.getTaskDescription()); + String statusValue = setStatusOfTask(taskId); + taskDataDTO.setApprovalStatus(TaskDataDTO.ApprovalStatusEnum.valueOf(statusValue)); + taskDataDTO.setInitiator(WorkflowEngineConstants.ParameterName.INITIATED_BY + initiator); + taskDataDTO.setPriority(WorkflowEngineConstants.ParameterName.PRIORITY); + TaskModel taskModel = new TaskModel(); + taskModel.setAssignees(assigneeMap); + taskDataDTO.setAssignees(getPropertyDTOs(taskModel.getAssignees())); + taskDataDTO.setProperties(properties); + return taskDataDTO; + } catch (WorkflowEngineClientException e) { + throw new WorkflowEngineClientException( + WorkflowEngineConstants.ErrorMessages.USER_ERROR_NON_EXISTING_TASK_ID.getCode(), + WorkflowEngineConstants.ErrorMessages.USER_ERROR_NON_EXISTING_TASK_ID.getDescription()); + } catch (WorkflowEngineServerException e) { + throw new WorkflowEngineClientException( + WorkflowEngineConstants.ErrorMessages.ERROR_OCCURRED_WHILE_RETRIEVING_APPROVAL_OF_USER.getCode(), + WorkflowEngineConstants.ErrorMessages.ERROR_OCCURRED_WHILE_RETRIEVING_APPROVAL_OF_USER. + getDescription()); + } + } + + private String setStatusOfTask(String taskId) { + + WorkflowEventRequestDAO workflowEventRequestDAO = new WorkflowEventRequestDAOImpl(); + String status = workflowEventRequestDAO.getTaskStatusOfRequest(taskId); + String statusValue; + if (status.equals(String.valueOf(WorkflowEngineConstants.TaskStatus.RESERVED))) { + statusValue = PENDING; + } else if (status.equals(String.valueOf(WorkflowEngineConstants.TaskStatus.READY))) { + statusValue = PENDING; + } else if (status.equals(String.valueOf(WorkflowEngineConstants.TaskStatus.COMPLETED))) { + statusValue = APPROVED; + } else { + statusValue = REJECTED; + } + return statusValue; + } + + /** + * Update the state of a task identified by the task id. + * User can reserve the task by claiming, or release a reserved task to himself. + * Or user can approve or reject a task. + * + * @param taskId the unique ID to update the state. + * @param nextState event status. + */ + public void updateStatus(String taskId, StateDTO nextState) { + + WorkflowEventRequestDAO workflowEventRequestDAO = new WorkflowEventRequestDAOImpl(); + DefaultWorkflowEventRequest defaultWorkflowEventRequest = new DefaultWorkflowEventRequestService(); + validateApprovers(taskId); + try { + switch (nextState.getAction()) { + case APPROVE: + updateTaskStatusOfRequest(taskId, APPROVED); + updateStepDetailsOfRequest(taskId); + break; + case REJECT: + String eventId = workflowEventRequestDAO.getRequestID(taskId); + updateTaskStatusOfRequest(taskId, REJECTED); + defaultWorkflowEventRequest.deleteApprovalOfRequest(taskId); + completeRequest(eventId, REJECTED); + break; + case RELEASE: + updateTaskStatusOfRequest(taskId, RELEASED); + break; + case CLAIM: + updateTaskStatusOfRequest(taskId, CLAIMED); + break; + default: + throw new WorkflowEngineClientException( + WorkflowEngineConstants.ErrorMessages.USER_ERROR_NOT_ACCEPTABLE_INPUT_FOR_NEXT_STATE. + getCode(), + WorkflowEngineConstants.ErrorMessages.USER_ERROR_NOT_ACCEPTABLE_INPUT_FOR_NEXT_STATE. + getDescription()); + } + } catch (WorkflowEngineClientException e) { + throw new WorkflowEngineClientException( + WorkflowEngineConstants.ErrorMessages.USER_ERROR_NON_EXISTING_TASK_ID.getCode(), + WorkflowEngineConstants.ErrorMessages.USER_ERROR_NON_EXISTING_TASK_ID.getDescription()); + } catch (WorkflowEngineServerException e) { + throw new WorkflowEngineClientException( + WorkflowEngineConstants.ErrorMessages.ERROR_OCCURRED_WHILE_CHANGING_APPROVALS_STATE.getCode(), + WorkflowEngineConstants.ErrorMessages.ERROR_OCCURRED_WHILE_CHANGING_APPROVALS_STATE. + getDescription()); + } + } + + private WorkflowRequest getWorkflowRequest(String requestId) { + + WorkflowExecutorManagerService workFlowExecutorManagerService = new WorkflowExecutorManagerServiceImpl(); + WorkflowRequest request; + try { + request = workFlowExecutorManagerService.retrieveWorkflow(requestId); + } catch (InternalWorkflowException e) { + throw new WorkflowEngineException( + WorkflowEngineConstants.ErrorMessages.ERROR_OCCURRED_WHILE_RETRIEVING_WORKFLOW_REQUEST.getCode(), + WorkflowEngineConstants.ErrorMessages.ERROR_OCCURRED_WHILE_RETRIEVING_WORKFLOW_REQUEST. + getDescription()); + } + return request; + } + + private void updateStepDetailsOfRequest(String taskId) { + + WorkflowEventRequestDAO workflowEventRequestDAO = new WorkflowEventRequestDAOImpl(); + String eventId = workflowEventRequestDAO.getRequestID(taskId); + WorkflowRequest request = getWorkflowRequest(eventId); + DefaultWorkflowEventRequest defaultWorkflowEventRequest = new DefaultWorkflowEventRequestService(); + List parameterList = getParameterList(request); + String workflowId = defaultWorkflowEventRequest.getWorkflowId(request); + defaultWorkflowEventRequest.deleteApprovalOfRequest(taskId); + int stepValue = defaultWorkflowEventRequest.getStateOfRequest(eventId, workflowId); + if (stepValue < numOfStates(request)) { + defaultWorkflowEventRequest.addApproversOfRequests(request, parameterList); + } else { + completeRequest(eventId, ApprovalEventService.APPROVED); + } + } + + private void validateApprovers(String taskId) { + + WorkflowEventRequestDAO workflowEventRequestDAO = new WorkflowEventRequestDAOImpl(); + List eventList = getAllRequestRelatedUserAndRole(); + List taskList; + List lst = new ArrayList<>(); + for (String event : eventList) { + taskList = workflowEventRequestDAO.getTaskId(event); + lst.addAll(taskList); + } + + for (String task : lst) { + if (taskId.equals(task)) { + return; + } + } + } + + private int numOfStates(WorkflowRequest request) { + + List parameterList = getParameterList(request); + int count = 0; + for (Parameter parameter : parameterList) { + if (parameter.getParamName().equals(WorkflowEngineConstants.ParameterName.USER_AND_ROLE_STEP) + && !parameter.getParamValue().isEmpty()) { + count++; + } + } + return count; + } + + private void updateTaskStatusOfRequest(String taskId, String status) { + + WorkflowEventRequestDAO workflowEventRequestDAO = new WorkflowEventRequestDAOImpl(); + try { + switch (status) { + case APPROVED: + status = WorkflowEngineConstants.TaskStatus.COMPLETED.toString(); + break; + case REJECTED: + status = WorkflowEngineConstants.TaskStatus.COMPLETED.toString().concat(",").concat(REJECTED); + break; + case RELEASED: + status = WorkflowEngineConstants.TaskStatus.READY.toString(); + break; + case CLAIMED: + status = WorkflowEngineConstants.TaskStatus.RESERVED.toString(); + break; + default: + throw new WorkflowEngineClientException( + WorkflowEngineConstants.ErrorMessages.USER_ERROR_NOT_ACCEPTABLE_INPUT_FOR_NEXT_STATE. + getCode(), + WorkflowEngineConstants.ErrorMessages.USER_ERROR_NOT_ACCEPTABLE_INPUT_FOR_NEXT_STATE. + getDescription()); + } + workflowEventRequestDAO.updateStatusOfRequest(taskId, status); + } catch (WorkflowEngineClientException e) { + throw new WorkflowEngineClientException( + WorkflowEngineConstants.ErrorMessages.USER_ERROR_NON_EXISTING_TASK_ID.getCode(), + WorkflowEngineConstants.ErrorMessages.USER_ERROR_NON_EXISTING_TASK_ID.getDescription()); + } catch (WorkflowEngineServerException e) { + throw new WorkflowEngineClientException( + WorkflowEngineConstants.ErrorMessages.ERROR_OCCURRED_WHILE_CHANGING_APPROVALS_STATE.getCode(), + WorkflowEngineConstants.ErrorMessages.ERROR_OCCURRED_WHILE_CHANGING_APPROVALS_STATE. + getDescription()); + } + } + + private void completeRequest(String eventId, String status) { + + WSWorkflowResponse wsWorkflowResponse = new WSWorkflowResponse(); + WorkflowEventRequestDAO workflowEventRequestDAO = new WorkflowEventRequestDAOImpl(); + DefaultWorkflowEventRequest defaultWorkflowEventRequest = new DefaultWorkflowEventRequestService(); + String relationshipId = workflowEventRequestDAO.getRelationshipId(eventId); + wsWorkflowResponse.setUuid(relationshipId); + wsWorkflowResponse.setStatus(status); + WSWorkflowCallBackService wsWorkflowCallBackService = new WSWorkflowCallBackService(); + wsWorkflowCallBackService.onCallback(wsWorkflowResponse); + defaultWorkflowEventRequest.deleteStateOfRequest(eventId); + } + + private TaskDetails getTaskDetails(WorkflowRequest workflowRequest) { + + List parameterList = getParameterList(workflowRequest); + TaskDetails taskDetails = new TaskDetails(); + for (Parameter parameter : parameterList) { + if (parameter.getParamName().equals(WorkflowEngineConstants.ParameterName.TASK_SUBJECT)) { + taskDetails.setTaskSubject(parameter.getParamValue()); + } + if (parameter.getParamName().equals(WorkflowEngineConstants.ParameterName.TASK_DESCRIPTION)) { + taskDetails.setTaskDescription(parameter.getParamValue()); + } + } + return taskDetails; + } + + private List getParameterList(WorkflowRequest request) { + + WorkflowManagementService workflowManagementService = new WorkflowManagementServiceImpl(); + DefaultWorkflowEventRequest defaultWorkflowEventRequest = new DefaultWorkflowEventRequestService(); + List associations = defaultWorkflowEventRequest.getAssociations(request); + List parameterList = null; + for (WorkflowAssociation association : associations) { + try { + parameterList = workflowManagementService.getWorkflowParameters(association.getWorkflowId()); + } catch (WorkflowException e) { + throw new WorkflowEngineException( + WorkflowEngineConstants.ErrorMessages.ERROR_OCCURRED_WHILE_RETRIEVING_PARAMETER_LIST.getCode(), + WorkflowEngineConstants.ErrorMessages.ERROR_OCCURRED_WHILE_RETRIEVING_PARAMETER_LIST. + getDescription() + ); + } + } + return parameterList; + } + + private List getRequestParameters(WorkflowRequest request) { + + List requestParameter; + List taskParamsList = new ArrayList<>(); + for (int i = 0; i < request.getRequestParameters().size(); i++) { + requestParameter = request.getRequestParameters(); + TaskParam taskParam = new TaskParam(); + Object value = requestParameter.get(i).getValue(); + if (requestParameter.get(i).getName().equals(WorkflowEngineConstants.ParameterName.CREDENTIAL)) { + continue; + } + if (value != null) { + taskParam.setItemValue(requestParameter.get(i).getValue().toString()); + taskParam.setItemName(requestParameter.get(i).getName()); + taskParamsList.add(taskParam); + } + } + return taskParamsList; + } + + private List getPropertyDTOs(Map props) { + + return props.entrySet().stream().map(p -> getPropertyDTO(p.getKey(), p.getValue())) + .collect(Collectors.toList()); + } + + private List getPropertyDTOs(List props) { + + return props.stream().map(p -> getPropertyDTO(p.getItemName(), p.getItemValue())) + .collect(Collectors.toList()); + } + + private PropertyDTO getPropertyDTO(String key, String value) { + + PropertyDTO prop = new PropertyDTO(); + prop.setKey(key); + prop.setValue(value); + return prop; + } + + private TStatus[] getRequiredTStatuses(List status) { + + List allStatuses = Arrays.asList(WorkflowEngineConstants.TaskStatus.RESERVED.toString(), + WorkflowEngineConstants.TaskStatus.READY.toString(), + WorkflowEngineConstants.TaskStatus.COMPLETED.toString()); + TStatus[] tStatuses = getTStatus(allStatuses); + + if (CollectionUtils.isNotEmpty(status)) { + List requestedStatus = status.stream().filter(allStatuses::contains).collect + (Collectors.toList()); + if (CollectionUtils.isNotEmpty(requestedStatus)) { + tStatuses = getTStatus(requestedStatus); + } + } + return tStatuses; + } + + private TStatus[] getTStatus(List statuses) { + + return statuses.stream().map(this::getTStatus).toArray(TStatus[]::new); + } + + private TStatus getTStatus(String status) { + + TStatus tStatus = new TStatus(); + tStatus.setTStatus(status); + return tStatus; + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/DefaultApprovalWorkflow.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/DefaultApprovalWorkflow.java new file mode 100644 index 0000000..c42e25f --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/DefaultApprovalWorkflow.java @@ -0,0 +1,63 @@ +package org.wso2.carbon.identity.workflow.engine; + +import org.wso2.carbon.identity.workflow.mgt.bean.Parameter; +import org.wso2.carbon.identity.workflow.mgt.bean.metadata.InputData; +import org.wso2.carbon.identity.workflow.mgt.bean.metadata.Item; +import org.wso2.carbon.identity.workflow.mgt.bean.metadata.MapType; +import org.wso2.carbon.identity.workflow.mgt.bean.metadata.ParameterMetaData; +import org.wso2.carbon.identity.workflow.mgt.exception.WorkflowException; +import org.wso2.carbon.identity.workflow.mgt.exception.WorkflowRuntimeException; +import org.wso2.carbon.identity.workflow.mgt.workflow.AbstractWorkflow; +import org.wso2.carbon.identity.workflow.mgt.workflow.TemplateInitializer; +import org.wso2.carbon.identity.workflow.mgt.workflow.WorkFlowExecutor; + +import java.util.List; + +import static org.wso2.carbon.identity.workflow.engine.util.WorkflowEngineConstants.ParameterName.HT_SUBJECT; + +/** + * The class that extends the AbstractWorkflow class. + */ +public class DefaultApprovalWorkflow extends AbstractWorkflow { + + public DefaultApprovalWorkflow(Class templateInitializerClass, + Class workFlowExecutorClass, String metaDataXML) + throws WorkflowRuntimeException { + + super(templateInitializerClass, workFlowExecutorClass, metaDataXML); + } + + /** + *{@inheritDoc} + */ + @Override + protected InputData getInputData(ParameterMetaData parameterMetaData) { + + InputData inputData = null; + if (parameterMetaData != null && parameterMetaData.getName() != null) { + String parameterName = parameterMetaData.getName(); + if (HT_SUBJECT.equals(parameterName)) { + inputData = new InputData(); + MapType mapType = new MapType(); + inputData.setMapType(mapType); + Item item1 = new Item(); + item1.setKey("subject1"); + item1.setValue("subject1"); + Item item2 = new Item(); + item2.setKey("subject2"); + item2.setValue("subject2"); + mapType.setItem(new Item[]{item1, item2}); + } + } + return inputData; + } + + /** + *{@inheritDoc} + */ + @Override + public void deploy(List parameterList) throws WorkflowException { + + super.deploy(parameterList); + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/DefaultTemplateInitializer.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/DefaultTemplateInitializer.java new file mode 100644 index 0000000..deee0bc --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/DefaultTemplateInitializer.java @@ -0,0 +1,60 @@ +package org.wso2.carbon.identity.workflow.engine; + +import org.apache.commons.lang.StringUtils; +import org.wso2.carbon.context.CarbonContext; +import org.wso2.carbon.identity.workflow.engine.util.WorkflowEngineConstants; +import org.wso2.carbon.identity.workflow.mgt.bean.Parameter; +import org.wso2.carbon.identity.workflow.mgt.util.WorkflowManagementUtil; +import org.wso2.carbon.identity.workflow.mgt.workflow.TemplateInitializer; +import org.wso2.carbon.utils.multitenancy.MultitenantConstants; + +import java.util.List; + +/** + * Implementation of TemplateInitializer interface that serves as the extension point for external workflow templates. + */ +public class DefaultTemplateInitializer implements TemplateInitializer { + + private String processName; + private String htName; + private String role; + private String tenantContext = "" ; + private static final String HT_SUFFIX = "Task"; + + /** + * Initialize template at start up. + * + * @return false. + */ + @Override + public boolean initNeededAtStartUp() { + + return false; + } + + /** + * Initialize template. + * + * @param list parameters of template. + */ + @Override + public void initialize(List list) { + + Parameter wfNameParameter = WorkflowManagementUtil + .getParameter(list, WorkflowEngineConstants.ParameterValue.WORKFLOW_NAME, + WorkflowEngineConstants.ParameterHolder + .WORKFLOW_IMPL); + + if (wfNameParameter != null) { + processName = StringUtils.deleteWhitespace(wfNameParameter.getParamValue()); + role = WorkflowManagementUtil + .createWorkflowRoleName(StringUtils.deleteWhitespace(wfNameParameter.getParamValue())); + } + + String tenantDomain = CarbonContext.getThreadLocalCarbonContext().getTenantDomain(); + if(!MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain)){ + tenantContext = "t/" + tenantDomain + "/"; + } + htName = processName + DefaultTemplateInitializer.HT_SUFFIX; + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/DefaultWorkflowEventRequest.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/DefaultWorkflowEventRequest.java new file mode 100644 index 0000000..60d82e4 --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/DefaultWorkflowEventRequest.java @@ -0,0 +1,85 @@ +package org.wso2.carbon.identity.workflow.engine; + +import org.wso2.carbon.identity.workflow.mgt.bean.Parameter; +import org.wso2.carbon.identity.workflow.mgt.bean.WorkflowAssociation; +import org.wso2.carbon.identity.workflow.mgt.dto.WorkflowRequest; + +import java.util.List; + +/** + * Default Workflow Event Request service interface. + */ +public interface DefaultWorkflowEventRequest { + + /** + * Add who approves the relevant request. + * + * @param request workflow request object. + * @param parameterList parameterList. + */ + void addApproversOfRequests(WorkflowRequest request, List parameterList); + + /** + * Get taskId from WF_REQUEST_APPROVAL_RELATION table. + * + * @param eventId the request ID that need to be checked. + * @return task Id. + */ + String getApprovalOfRequest(String eventId); + + /** + * Delete approver details using task Id. + * + * @param taskId random generated unique Id. + */ + void deleteApprovalOfRequest(String taskId); + + /** + * Add current step. + * + * @param eventId the request ID that need to be checked. + * @param workflowId workflow id. + * @param currentStep current step of the flow. + */ + void createStatesOfRequest(String eventId, String workflowId, int currentStep); + + /** + * Get current step from the table. + * + * @param eventId the request ID that need to be checked. + * @param workflowId workflow Id. + * @return currentStep. + */ + int getStateOfRequest(String eventId, String workflowId); + + /** + *Update current step according to the eventId and workflowId. + * + * @param eventId the request ID that need to be checked. + * @param workflowId workflow Id. + */ + void updateStateOfRequest(String eventId, String workflowId); + + /** + * Get related associations. + * + * @param workflowRequest request object. + * @return association list. + */ + List getAssociations(WorkflowRequest workflowRequest); + + /** + * Get relevant workflow id to request. + * + * @param request request object. + * @return workflow Id. + */ + String getWorkflowId(WorkflowRequest request); + + /** + * Delete the current step using giving eventId + * + * @param eventId the request ID that need to be checked. + */ + void deleteStateOfRequest(String eventId); +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/DefaultWorkflowEventRequestService.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/DefaultWorkflowEventRequestService.java new file mode 100644 index 0000000..84f0728 --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/DefaultWorkflowEventRequestService.java @@ -0,0 +1,224 @@ +package org.wso2.carbon.identity.workflow.engine; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.workflow.engine.exception.WorkflowEngineClientException; +import org.wso2.carbon.identity.workflow.engine.exception.WorkflowEngineServerException; +import org.wso2.carbon.identity.workflow.engine.internal.dao.WorkflowEventRequestDAO; +import org.wso2.carbon.identity.workflow.engine.internal.dao.impl.WorkflowEventRequestDAOImpl; +import org.wso2.carbon.identity.workflow.engine.util.WorkflowEngineConstants; +import org.wso2.carbon.identity.workflow.mgt.WorkflowExecutorManagerService; +import org.wso2.carbon.identity.workflow.mgt.WorkflowExecutorManagerServiceImpl; +import org.wso2.carbon.identity.workflow.mgt.WorkflowManagementService; +import org.wso2.carbon.identity.workflow.mgt.WorkflowManagementServiceImpl; +import org.wso2.carbon.identity.workflow.mgt.bean.Parameter; +import org.wso2.carbon.identity.workflow.mgt.bean.RequestParameter; +import org.wso2.carbon.identity.workflow.mgt.bean.Workflow; +import org.wso2.carbon.identity.workflow.mgt.bean.WorkflowAssociation; +import org.wso2.carbon.identity.workflow.mgt.dto.WorkflowRequest; +import org.wso2.carbon.identity.workflow.mgt.exception.InternalWorkflowException; +import org.wso2.carbon.identity.workflow.mgt.exception.WorkflowException; + +import java.util.List; +import java.util.UUID; + +/** + * Default Workflow Event Request service implementation. + */ +public class DefaultWorkflowEventRequestService implements DefaultWorkflowEventRequest { + + private WorkflowEventRequestDAO workflowEventRequestDAO = new WorkflowEventRequestDAOImpl(); + private static final Log log = LogFactory.getLog(DefaultWorkflowEventRequestService.class); + + /** + * {@inheritDoc} + */ + @Override + public void addApproversOfRequests(WorkflowRequest request, List parameterList) { + + String taskId = UUID.randomUUID().toString(); + String eventId = getRequestId(request); + String workflowId = getWorkflowId(request); + String approverType; + String approverName; + int currentStepValue = getStateOfRequest(eventId, workflowId); + if (currentStepValue == 0) { + createStatesOfRequest(eventId, workflowId, currentStepValue); + } + currentStepValue += 1; + updateStateOfRequest(eventId, workflowId); + for (Parameter parameter : parameterList) { + if (parameter.getParamName().equals(WorkflowEngineConstants.ParameterName.USER_AND_ROLE_STEP)) { + String[] stepName = parameter.getqName().split("-"); + int step = Integer.parseInt(stepName[2]); + if (currentStepValue == step) { + approverType = stepName[stepName.length - 1]; + + String approver = parameter.getParamValue(); + if (approver != null && !approver.isEmpty()) { + String[] approvers = approver.split(",", 0); + for (String name : approvers) { + approverName = name; + String taskStatus= WorkflowEngineConstants.ParameterName.TASK_STATUS_DEFAULT; + workflowEventRequestDAO.addApproversOfRequest(taskId, eventId, workflowId, + approverType, approverName,taskStatus); + } + } + } + } + } + } + + private String getRequestId(WorkflowRequest request) { + + List requestParameter; + Object event = null; + for (int i = 0; i < request.getRequestParameters().size(); i++) { + requestParameter = request.getRequestParameters(); + if (requestParameter.get(i).getName().equals(WorkflowEngineConstants.ParameterName.REQUEST_ID)) { + event = requestParameter.get(i).getValue(); + } + } + return (String) event; + } + + /** + * {@inheritDoc} + */ + @Override + public String getWorkflowId(WorkflowRequest request) { + + WorkflowManagementService workflowManagementService = new WorkflowManagementServiceImpl(); + List associations = getAssociations(request); + String workflowId = null; + for (WorkflowAssociation association : associations) { + try { + Workflow workflow = workflowManagementService.getWorkflow(association.getWorkflowId()); + workflowId = workflow.getWorkflowId(); + } catch (WorkflowException e) { + throw new WorkflowEngineClientException( + WorkflowEngineConstants.ErrorMessages.ASSOCIATION_NOT_FOUND.getCode(), + WorkflowEngineConstants.ErrorMessages.WORKFLOW_ID_NOT_FOUND.getDescription()); + } + } + return workflowId; + } + + /** + * {@inheritDoc} + */ + @Override + public List getAssociations(WorkflowRequest workflowRequest) { + + List associations; + WorkflowExecutorManagerService workFlowExecutorManagerService = new WorkflowExecutorManagerServiceImpl(); + try { + associations = workFlowExecutorManagerService.getWorkflowAssociationsForRequest( + workflowRequest.getEventType(), workflowRequest.getTenantId()); + } catch (InternalWorkflowException e) { + throw new WorkflowEngineClientException( + WorkflowEngineConstants.ErrorMessages.ASSOCIATION_NOT_FOUND.getCode(), + WorkflowEngineConstants.ErrorMessages.ASSOCIATION_NOT_FOUND.getDescription()); + } + return associations; + } + + /** + * {@inheritDoc} + */ + @Override + public String getApprovalOfRequest(String eventId) { + + if (StringUtils.isEmpty(eventId)) { + if (log.isDebugEnabled()) { + log.debug("Cannot retrieve task from eventID: " + eventId); + } + throw buildTaskNotFoundError(); + } + return workflowEventRequestDAO.getApproversOfRequest(eventId); + } + + /** + * {@inheritDoc} + */ + @Override + public void deleteApprovalOfRequest(String taskId) { + + if (StringUtils.isEmpty(taskId)) { + if (log.isDebugEnabled()) { + log.debug("Cannot delete task from taskID: " + taskId); + } + } + workflowEventRequestDAO.deleteApproversOfRequest(taskId); + } + + /** + * {@inheritDoc} + */ + @Override + public void createStatesOfRequest(String eventId, String workflowId, int currentStep) { + + if (StringUtils.isEmpty(eventId) || StringUtils.isEmpty(workflowId)) { + if (log.isDebugEnabled()) { + log.debug("Cannot add task from event ID: " + eventId); + } + throw buildTaskNotFoundError(); + } + workflowEventRequestDAO.createStatesOfRequest(eventId, workflowId, currentStep); + } + + /** + * {@inheritDoc} + */ + @Override + public int getStateOfRequest(String eventId, String workflowId) { + + if (StringUtils.isEmpty(eventId) || StringUtils.isEmpty(workflowId)) { + if (log.isDebugEnabled()) { + log.debug("Cannot retrieve task step from eventID: " + eventId); + } + throw buildTaskNotFoundError(); + } + return workflowEventRequestDAO.getStateOfRequest(eventId, workflowId); + } + + /** + * {@inheritDoc} + */ + @Override + public void updateStateOfRequest(String eventId, String workflowId) { + + if (StringUtils.isEmpty(eventId) || StringUtils.isEmpty(workflowId)) { + if (log.isDebugEnabled()) { + log.debug("Cannot update task from eventID: " + eventId); + } + throw buildTaskNotFoundError(); + } + int currentStep = getStateOfRequest(eventId, workflowId); + currentStep += 1; + workflowEventRequestDAO.updateStateOfRequest(eventId, workflowId, currentStep); + } + + /** + * {@inheritDoc} + */ + @Override + public void deleteStateOfRequest(String eventId) { + + if (StringUtils.isEmpty(eventId)) { + if (log.isDebugEnabled()) { + log.debug("Cannot delete task from eventID: " + eventId); + } + throw buildTaskNotFoundError(); + } + workflowEventRequestDAO.deleteCurrentStepOfRequest(eventId); + } + + private WorkflowEngineServerException buildTaskNotFoundError() { + + return new WorkflowEngineServerException( + WorkflowEngineConstants.ErrorMessages.TASK_NOT_FOUND.getCode(), + WorkflowEngineConstants.ErrorMessages.TASK_NOT_FOUND.getDescription()); + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/DefaultWorkflowExecutor.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/DefaultWorkflowExecutor.java new file mode 100644 index 0000000..f5fc18c --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/DefaultWorkflowExecutor.java @@ -0,0 +1,53 @@ +package org.wso2.carbon.identity.workflow.engine; + +import org.wso2.carbon.identity.workflow.mgt.bean.Parameter; +import org.wso2.carbon.identity.workflow.mgt.dto.WorkflowRequest; +import org.wso2.carbon.identity.workflow.mgt.workflow.WorkFlowExecutor; + +import java.util.List; + +/** + * Implementation of Workflow Executor Interface. + */ +public class DefaultWorkflowExecutor implements WorkFlowExecutor { + + List parameterList; + private static final String EXECUTOR_NAME = "WorkflowEngineSimple"; + + /** + *{@inheritDoc} + */ + @Override + public boolean canHandle(WorkflowRequest workflowRequest) { + + return true; + } + + /** + *{@inheritDoc} + */ + @Override + public void initialize(List parameterList) { + + this.parameterList = parameterList; + } + + /** + *{@inheritDoc} + */ + @Override + public void execute(WorkflowRequest request) { + + DefaultWorkflowEventRequest defaultWorkflowEventRequest = new DefaultWorkflowEventRequestService(); + defaultWorkflowEventRequest.addApproversOfRequests(request, parameterList); + } + + /** + *{@inheritDoc} + */ + @Override + public String getName() { + + return EXECUTOR_NAME; + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/dto/PropertyDTO.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/dto/PropertyDTO.java new file mode 100644 index 0000000..385fccf --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/dto/PropertyDTO.java @@ -0,0 +1,54 @@ +package org.wso2.carbon.identity.workflow.engine.dto; + +/** + * DTO class for task properties, map the task properties as key & value. + */ +public class PropertyDTO { + + private String key = null; + + private String value = null; + + /** + * The key of the task property. + **/ + public String getKey() { + + return key; + } + + /** + * Set Key of the task property. + **/ + public void setKey(String key) { + + this.key = key; + } + + /** + * The value of the key of the task property. + **/ + public String getValue() { + + return value; + } + + /** + * Set Value of the key of the task property. + **/ + public void setValue(String value) { + + this.value = value; + } + + @Override + public String toString() { + + StringBuilder builder = new StringBuilder(); + builder.append("class PropertyDTO {\n"); + builder.append(" key: ").append(key).append("\n"); + builder.append(" value: ").append(value).append("\n"); + builder.append("}\n"); + return builder.toString(); + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/dto/StateDTO.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/dto/StateDTO.java new file mode 100644 index 0000000..38866e3 --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/dto/StateDTO.java @@ -0,0 +1,39 @@ +package org.wso2.carbon.identity.workflow.engine.dto; + +/** + * Action to perform on the task dto. + */ +public class StateDTO { + + public enum ActionEnum { + CLAIM, RELEASE, APPROVE, REJECT, + }; + + private ActionEnum action = null; + + /** + * getAction to perform on the task. + **/ + public ActionEnum getAction() { + + return action; + } + + /** + * Set Action to perform on the task. + **/ + public void setAction(ActionEnum action) { + + this.action = action; + } + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + sb.append("class StateDTO {\n"); + sb.append(" action: ").append(action).append("\n"); + sb.append("}\n"); + return sb.toString(); + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/dto/TaskDataDTO.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/dto/TaskDataDTO.java new file mode 100644 index 0000000..1ca5ebe --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/dto/TaskDataDTO.java @@ -0,0 +1,193 @@ +package org.wso2.carbon.identity.workflow.engine.dto; + +import java.util.ArrayList; +import java.util.List; + +/** + * DTO class for task details. + */ +public class TaskDataDTO { + + private String id = null; + + private String subject = null; + + private String description = null; + + private String priority = null; + + private String initiator = null; + + private String createdTimeInMillis = null; + + public enum ApprovalStatusEnum { + PENDING, APPROVED, REJECTED, + } + + private ApprovalStatusEnum approvalStatus = null; + + private List assignees = new ArrayList(); + + private List properties = new ArrayList(); + + /** + * Unique ID to represent a approval task. + **/ + public String getId() { + + return id; + } + + /** + * Set Unique ID to represent a approval task. + **/ + public void setId(String id) { + + this.id = id; + } + + /** + * Subject of the Approval. + **/ + public String getSubject() { + + return subject; + } + + /** + * Set Subject of the Approval. + **/ + public void setSubject(String subject) { + + this.subject = subject; + } + + /** + * Description on the Approval task. + **/ + + public String getDescription() { + + return description; + } + + /** + * Set Description on the Approval task. + **/ + public void setDescription(String description) { + + this.description = description; + } + + /** + * Priority of the Approval task. + **/ + public String getPriority() { + + return priority; + } + + /** + * Set Priority of the Approval task. + **/ + public void setPriority(String priority) { + + this.priority = priority; + } + + /** + * The user who initiated the task. + **/ + + public String getInitiator() { + + return initiator; + } + + /** + * Set the user who initiated the task. + **/ + public void setInitiator(String initiator) { + + this.initiator = initiator; + } + + /** + * Available only for the completed Tasks, APPROVED or REJECTED if the task has been completed, PENDING otherwise\n + **/ + public ApprovalStatusEnum getApprovalStatus() { + + return approvalStatus; + } + + /** + * Set available only for the completed Tasks, APPROVED or REJECTED if the task has been completed, PENDING otherwise\n + **/ + public void setApprovalStatus(ApprovalStatusEnum approvalStatus) { + + this.approvalStatus = approvalStatus; + } + + /** + * To whom the task is assigned:\n * user - username(s) if the task is reserved for specific user(s).\n * group - role name(s) if the task is assignable for group(s).\n + **/ + public List getAssignees() { + + return assignees; + } + + public void setAssignees(List assignees) { + + this.assignees = assignees; + } + + /** + * Task parameters: username, role, claims, requestId. + **/ + public List getProperties() { + + return properties; + } + + /** + * Set Task parameters: username, role, claims, requestId. + **/ + public void setProperties(List properties) { + + this.properties = properties; + } + + /** + * The created time of the request. + **/ + public String getCreatedTimeInMillis() { + + return createdTimeInMillis; + } + + /** + * Set the created time of the request. + **/ + public void setCreatedTimeInMillis(String createdTimeInMillis) { + + this.createdTimeInMillis = createdTimeInMillis; + } + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + sb.append("class TaskDataDTO {\n"); + sb.append(" id: ").append(id).append("\n"); + sb.append(" subject: ").append(subject).append("\n"); + sb.append(" description: ").append(description).append("\n"); + sb.append(" priority: ").append(priority).append("\n"); + sb.append(" initiator: ").append(initiator).append("\n"); + sb.append(" approvalStatus: ").append(approvalStatus).append("\n"); + sb.append(" assignees: ").append(assignees).append("\n"); + sb.append(" properties: ").append(properties).append("\n"); + sb.append(" createdTimeInMillis: ").append(createdTimeInMillis).append("\n"); + sb.append("}\n"); + return sb.toString(); + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/dto/TaskSummaryDTO.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/dto/TaskSummaryDTO.java new file mode 100644 index 0000000..a4ca744 --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/dto/TaskSummaryDTO.java @@ -0,0 +1,172 @@ +package org.wso2.carbon.identity.workflow.engine.dto; + +/** + * DTO class for tasks list. + */ +public class TaskSummaryDTO { + + private String id = null; + + private String name = null; + + private String presentationSubject = null; + + private String presentationName = null; + + private String taskType = null; + + public enum StatusEnum { + READY, RESERVED, COMPLETED, + } + + private StatusEnum status = null; + + private String priority = null; + + private String createdTimeInMillis = null; + + /** + * Unique ID to represent an Approval Task + **/ + public String getId() { + + return id; + } + + /** + * Set Unique ID to represent an Approval Task + **/ + public void setId(String id) { + + this.id = id; + } + + /** + * Unique name for the Approval Task + **/ + public String getName() { + + return name; + } + + /** + * Set Unique name for the Approval Task + **/ + public void setName(String name) { + + this.name = name; + } + + /** + * Display value for Approval Operation + **/ + public String getPresentationSubject() { + + return presentationSubject; + } + + /** + * Set display value for Approval Operation + **/ + public void setPresentationSubject(String presentationSubject) { + + this.presentationSubject = presentationSubject; + } + + /** + * Display value for Approval Task + **/ + public String getPresentationName() { + + return presentationName; + } + + /** + * Set display value for Approval Task + **/ + public void setPresentationName(String presentationName) { + + this.presentationName = presentationName; + } + + /** + * Type of the Approval + **/ + public String getTaskType() { + + return taskType; + } + + /** + * Set type of the Approval + **/ + public void setTaskType(String taskType) { + + this.taskType = taskType; + } + + /** + * State of the Approval task + **/ + public StatusEnum getStatus() { + + return status; + } + + /** + * Set state of the Approval task + **/ + public void setStatus(StatusEnum status) { + + this.status = status; + } + + /** + * Priority of the Approval task + **/ + public String getPriority() { + + return priority; + } + + /** + * Set priority of the Approval task + **/ + public void setPriority(String priority) { + + this.priority = priority; + } + + /** + * The time that the operation for approval initiated + **/ + public String getCreatedTimeInMillis() { + + return createdTimeInMillis; + } + + /** + * Set the time that the operation for approval initiated + **/ + public void setCreatedTimeInMillis(String createdTimeInMillis) { + + this.createdTimeInMillis = createdTimeInMillis; + } + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + sb.append("class TaskSummaryDTO {\n"); + sb.append(" id: ").append(id).append("\n"); + sb.append(" name: ").append(name).append("\n"); + sb.append(" presentationSubject: ").append(presentationSubject).append("\n"); + sb.append(" presentationName: ").append(presentationName).append("\n"); + sb.append(" taskType: ").append(taskType).append("\n"); + sb.append(" status: ").append(status).append("\n"); + sb.append(" priority: ").append(priority).append("\n"); + sb.append(" createdTimeInMillis: ").append(createdTimeInMillis).append("\n"); + sb.append("}\n"); + return sb.toString(); + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/exception/WorkflowEngineClientException.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/exception/WorkflowEngineClientException.java new file mode 100644 index 0000000..fbbb152 --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/exception/WorkflowEngineClientException.java @@ -0,0 +1,12 @@ +package org.wso2.carbon.identity.workflow.engine.exception; + +/** + * Exception class to handle client exceptions. + */ +public class WorkflowEngineClientException extends WorkflowEngineException { + + public WorkflowEngineClientException(String message, String errorCode) { + + super(message, errorCode); + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/exception/WorkflowEngineException.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/exception/WorkflowEngineException.java new file mode 100644 index 0000000..c446961 --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/exception/WorkflowEngineException.java @@ -0,0 +1,20 @@ +package org.wso2.carbon.identity.workflow.engine.exception; + +/** + * Base exception for handling workflow engine exceptions. + */ +public class WorkflowEngineException extends java.lang.RuntimeException { + + private String errorCode; + + public WorkflowEngineException(String message, String errorCode) { + + super(message); + this.errorCode = errorCode; + } + + public WorkflowEngineException(String message, Throwable cause) { + + super(message, cause); + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/exception/WorkflowEngineServerException.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/exception/WorkflowEngineServerException.java new file mode 100644 index 0000000..7ea5985 --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/exception/WorkflowEngineServerException.java @@ -0,0 +1,17 @@ +package org.wso2.carbon.identity.workflow.engine.exception; + +/** + * This class is used to define the server side errors which need to be handled. + */ +public class WorkflowEngineServerException extends WorkflowEngineException { + + public WorkflowEngineServerException(String message, String errorCode) { + + super(message, errorCode); + } + + public WorkflowEngineServerException(String message, Throwable cause) { + + super(message, cause); + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/internal/WorkflowEngineServiceComponent.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/internal/WorkflowEngineServiceComponent.java new file mode 100644 index 0000000..47aed3e --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/internal/WorkflowEngineServiceComponent.java @@ -0,0 +1,93 @@ +package org.wso2.carbon.identity.workflow.engine.internal; + +import org.osgi.framework.BundleContext; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.wso2.carbon.identity.workflow.engine.ApprovalEventService; +import org.wso2.carbon.identity.workflow.engine.DefaultApprovalWorkflow; +import org.wso2.carbon.identity.workflow.engine.DefaultTemplateInitializer; +import org.wso2.carbon.identity.workflow.engine.DefaultWorkflowExecutor; +import org.wso2.carbon.identity.workflow.mgt.WorkflowExecutorManagerService; +import org.wso2.carbon.identity.workflow.mgt.WorkflowManagementService; +import org.wso2.carbon.identity.workflow.mgt.workflow.AbstractWorkflow; + +/** + * OSGi declarative services component which handles registration and un-registration of workflow engine management + * service. + */ +@Component( + name = "simple.workflow.engine", + immediate = true) +public class WorkflowEngineServiceComponent { + + /** + * Register Default Approval Workflow as an OSGi service. + * + * @param context OSGi service component context. + */ + @Activate + protected void activate(ComponentContext context) { + + BundleContext bundleContext = context.getBundleContext(); + bundleContext.registerService(AbstractWorkflow.class, new DefaultApprovalWorkflow(DefaultTemplateInitializer.class, + DefaultWorkflowExecutor.class, getMetaDataXML()), null); + ApprovalEventService approvalEventService=new ApprovalEventService(); + bundleContext.registerService(ApprovalEventService.class, approvalEventService, null); + } + + private String getMetaDataXML() { + + return "\n" + + "\n" + + " workflowImplSimple\n" + + " SimpleWorkflowEngine\n" + + " Simple WorkflowEngine\n" + + " MultiStepApprovalTemplate\n" + + " \n" + + " \n" + + " Subject(Approval task subject to display)\n" + + " \n" + + " \n" + + " Detail(Approval task description)\n" + + " \n" + + " \n" + + "\n" + + ""; + } + + @Reference( + name = "org.wso2.carbon.identity.workflow.mgt", + service = org.wso2.carbon.identity.workflow.mgt.WorkflowManagementService.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetWorkflowManagementService") + protected void setWorkflowManagementService(WorkflowManagementService workflowManagementService) { + + WorkflowEngineServiceDataHolder.getInstance().setWorkflowManagementService(workflowManagementService); + } + + protected void unsetWorkflowManagementService(WorkflowManagementService workflowManagementService) { + + WorkflowEngineServiceDataHolder.getInstance().setWorkflowManagementService(null); + } + + @Reference( + name = "org.wso2.carbon.identity.workflow.mgt.executor", + service = org.wso2.carbon.identity.workflow.mgt.WorkflowExecutorManagerService.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetWorkflowExecutorManagerService") + protected void setWorkflowExecutorManagerService(WorkflowExecutorManagerService workflowExecutorManagerService) { + + WorkflowEngineServiceDataHolder.getInstance().setWorkflowExecutorManagerService(workflowExecutorManagerService); + } + + protected void unsetWorkflowExecutorManagerService(WorkflowExecutorManagerService workflowExecutorManagerService) { + + WorkflowEngineServiceDataHolder.getInstance().setWorkflowExecutorManagerService(workflowExecutorManagerService); + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/internal/WorkflowEngineServiceDataHolder.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/internal/WorkflowEngineServiceDataHolder.java new file mode 100644 index 0000000..188de1e --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/internal/WorkflowEngineServiceDataHolder.java @@ -0,0 +1,71 @@ +package org.wso2.carbon.identity.workflow.engine.internal; + +import org.osgi.framework.BundleContext; +import org.wso2.carbon.identity.workflow.mgt.WorkflowExecutorManagerService; +import org.wso2.carbon.identity.workflow.mgt.WorkflowManagementService; +import org.wso2.carbon.identity.workflow.mgt.workflow.AbstractWorkflow; + +import java.util.HashMap; +import java.util.Map; + +/** + * A class to keep the data of the workflow engine component. + */ +public class WorkflowEngineServiceDataHolder { + + private static WorkflowEngineServiceDataHolder instance = new WorkflowEngineServiceDataHolder(); + + private WorkflowManagementService workflowManagementService; + + private WorkflowExecutorManagerService workflowExecutorManagerService; + + private BundleContext bundleContext; + + private WorkflowEngineServiceDataHolder() { + + } + + public static WorkflowEngineServiceDataHolder getInstance() { + + return instance; + } + + public WorkflowManagementService getWorkflowManagementService() { + + return workflowManagementService; + } + + public BundleContext getBundleContext() { + + return bundleContext; + } + + public void setBundleContext(BundleContext bundleContext) { + + this.bundleContext = bundleContext; + } + + public void setWorkflowManagementService( + WorkflowManagementService workflowManagementService) { + + this.workflowManagementService = workflowManagementService; + } + + public WorkflowExecutorManagerService getWorkflowExecutorManagerService() { + + return workflowExecutorManagerService; + } + + public void setWorkflowExecutorManagerService( + WorkflowExecutorManagerService workflowExecutorManagerService) { + + this.workflowExecutorManagerService = workflowExecutorManagerService; + } + + private Map> workflowImpls = new HashMap>(); + + public Map> getWorkflowImpls() { + + return workflowImpls; + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/internal/dao/WorkflowEventRequestDAO.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/internal/dao/WorkflowEventRequestDAO.java new file mode 100644 index 0000000..5f91725 --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/internal/dao/WorkflowEventRequestDAO.java @@ -0,0 +1,199 @@ +package org.wso2.carbon.identity.workflow.engine.internal.dao; + +import java.sql.Timestamp; +import java.util.List; + +/** + * Perform CRUD operations for workflow Event Request properties. + */ +public interface WorkflowEventRequestDAO { + + /** + * Add who approves the relevant request. + * + * @param taskId random generated unique Id. + * @param eventId the request ID that need to be checked. + * @param workflowId workflow ID. + * @param approverType the type of the approved user EX: user or Role. + * @param approverName the value of the approver type. + * @param taskStatus state of the tasks [RESERVED, READY or COMPLETED]. + */ + void addApproversOfRequest(String taskId, String eventId, String workflowId, String approverType, String approverName, String taskStatus); + + /** + * Get taskId from table. + * + * @param eventId the request ID that need to be checked. + * @return task Id. + */ + String getApproversOfRequest(String eventId); + + /** + * Delete approver details using task Id. + * + * @param taskId random generated unique Id. + */ + void deleteApproversOfRequest(String taskId); + + /** + * Add what step to approve. + * + * @param eventId the request ID that need to be checked. + * @param workflowId workflow ID. + * @param currentStep the current step. + */ + void createStatesOfRequest(String eventId, String workflowId, int currentStep); + + /** + * Returns the current step given the event ID and workflow ID. + * + * @param eventId the request ID that need to be checked. + * @param workflowId workflow ID. + * @return current step value. + */ + int getStateOfRequest(String eventId, String workflowId); + + /** + * Updates a state of request given the event ID, workflow ID and current step. + * + * @param eventId the request ID that need to be checked. + * @param workflowId workflow ID. + * @param currentStep the current step. + */ + void updateStateOfRequest(String eventId, String workflowId, int currentStep); + + /** + * Returns the request ID given the task ID. + * + * @param taskId random generated unique Id. + * @return request Id. + */ + String getRequestID(String taskId); + + /** + * Returns the initiator given the request ID. + * + * @param requestId the request ID that need to be checked. + * @return string initiator. + */ + String getInitiatedUser(String requestId); + + /** + * Retrieve the role id list giving the username. + * + * @param userName the username that need to be checked. + * @return role ID list. + */ + List getRolesID(String userName); + + /** + * Get the role name giving the role ID. + * + * @param roleId the roleID that need to be checked. + * @return role name list. + */ + List getRoleNames(int roleId); + + /** + * Returns the events list according to the user. + * + * @param approverName admin user. + * @return events list. + */ + List getRequestsList(String approverName); + + /** + * Returns the event type given the request ID. + * + * @param requestId the request ID that need to be checked. + * @return event type of the request. + */ + String getEventType(String requestId); + + /** + * Returns the task status given the task ID [RESERVED, READY or COMPLETED]. + * + * @param taskId the task ID that need to be checked. + * @return task Status. + */ + String getTaskStatusOfRequest(String taskId); + + /** + * Update the task status given the task ID. + * + * @param taskId the task ID that need to be checked. + * @param taskStatus state of the tasks [RESERVED, READY or COMPLETED]. + */ + void updateStatusOfRequest(String taskId, String taskStatus); + + /** + * Returns the created time of the request. + * + * @param requestId the request ID that need to be checked. + * @return the created time. + */ + Timestamp getCreatedAtTimeInMill(String requestId); + + /** + * Returns the relationship ID given the request ID. + * + * @param eventId the event ID that need to be checked. + * @return the relationship ID. + */ + String getRelationshipId(String eventId); + + /** + * Returns the approvers list given the authenticated approver name. + * + * @param taskId the task ID that need to be checked. + * @return approvers list. + */ + List listApprovers(String taskId); + + /** + * Returns the task status given the request ID [RESERVED, READY or COMPLETED]. + * + * @param requestId the request ID that need to be checked. + * @return task status. + */ + String getStatusOfTask(String requestId); + + /** + * Retrieve the tasks list giving event ID. + * + * @param eventId the request ID that need to be checked. + * @return tasks list. + */ + List getTaskId(String eventId); + + /** + * Delete the current step using giving event ID. + * + * @param eventId the request ID that need to be checked. + */ + void deleteCurrentStepOfRequest(String eventId); + + /** + * Retrieve the workflow ID giving task ID. + * + * @param taskId the task ID that need to be checked. + * @return workflow ID. + */ + String getWorkflowID(String taskId); + + /** + * Retrieve the workflow name giving workflow ID. + * + * @param workflowID workflow ID. + * @return workflow definition name. + */ + String getWorkflowName(String workflowID); + + /** + * Retrieve the entity name giving request ID. + * + * @param requestID the request ID that need to be checked. + * @return entity name of the request + */ + String getEntityNameOfRequest(String requestID); +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/internal/dao/impl/WorkflowEventRequestDAOImpl.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/internal/dao/impl/WorkflowEventRequestDAOImpl.java new file mode 100644 index 0000000..6d199c5 --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/internal/dao/impl/WorkflowEventRequestDAOImpl.java @@ -0,0 +1,604 @@ +package org.wso2.carbon.identity.workflow.engine.internal.dao.impl; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.database.utils.jdbc.JdbcTemplate; +import org.wso2.carbon.database.utils.jdbc.exceptions.DataAccessException; +import org.wso2.carbon.identity.configuration.mgt.core.util.JdbcUtils; +import org.wso2.carbon.identity.workflow.engine.exception.WorkflowEngineServerException; +import org.wso2.carbon.identity.workflow.engine.internal.dao.WorkflowEventRequestDAO; +import org.wso2.carbon.identity.workflow.engine.util.WorkflowEngineConstants; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.List; + +/** + * Workflow Event Request DAO implementation. + */ +public class WorkflowEventRequestDAOImpl implements WorkflowEventRequestDAO { + + private static final Log log = LogFactory.getLog(WorkflowEventRequestDAOImpl.class.getName()); + + /** + * {@inheritDoc} + */ + @Override + public void addApproversOfRequest(String taskId, String eventId, String workflowId, String approverType, + String approverName, String taskStatus) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + try { + jdbcTemplate.executeUpdate(WorkflowEngineConstants.SqlQueries.ADD_APPROVAL_LIST_RELATED_TO_USER, + preparedStatement -> { + preparedStatement.setString(1, taskId); + preparedStatement.setString(2, eventId); + preparedStatement.setString(3, workflowId); + preparedStatement.setString(4, approverType); + preparedStatement.setString(5, approverName); + preparedStatement.setString(6, taskStatus); + }); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while adding request details" + + "in eventId: %s & workflowId: %s", eventId, workflowId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage,e); + } + } + + /** + *{@inheritDoc} + */ + @Override + public String getApproversOfRequest(String eventId) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + String taskIdExists; + try { + taskIdExists = jdbcTemplate.fetchSingleRecord(WorkflowEngineConstants.SqlQueries. + GET_TASK_ID_RELATED_TO_USER, + ((resultSet, i) -> ( + resultSet.getString(WorkflowEngineConstants.TASK_ID_COLUMN))), + preparedStatement -> preparedStatement.setString(1, eventId)); + if (taskIdExists == null) { + return null; + } + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while retrieving taskId from" + + "requestId: %s", eventId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + return taskIdExists; + } + + /** + *{@inheritDoc} + */ + @Override + public void deleteApproversOfRequest(String taskId) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + try { + jdbcTemplate.executeUpdate(WorkflowEngineConstants.SqlQueries.DELETE_APPROVAL_LIST_RELATED_TO_USER, + preparedStatement -> preparedStatement.setString(1, taskId)); + } catch (DataAccessException e) { + String errorMessage = String.format("Error while deleting the approver details from taskId:%s", taskId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + } + + /** + *{@inheritDoc} + */ + @Override + public void createStatesOfRequest(String eventId, String workflowId, int currentStep) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + try { + jdbcTemplate.executeUpdate(WorkflowEngineConstants.SqlQueries.ADD_CURRENT_STEP_FOR_EVENT, + preparedStatement -> { + preparedStatement.setString(1, eventId); + preparedStatement.setString(2, workflowId); + preparedStatement.setInt(3, currentStep); + }); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while adding request approval steps" + + "in event Id: %s & workflowId: %s", eventId, workflowId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public int getStateOfRequest(String eventId, String workflowId) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + String stepExists; + try { + stepExists = jdbcTemplate.fetchSingleRecord(WorkflowEngineConstants.SqlQueries.GET_CURRENT_STEP, + ((resultSet, i) -> ( + Integer.toString(resultSet.getInt(WorkflowEngineConstants.CURRENT_STEP_COLUMN)))), + preparedStatement -> { + preparedStatement.setString(1, eventId); + preparedStatement.setString(2, workflowId); + }); + if (stepExists == null) { + return 0; + } + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while retrieving currentStep from" + + "event Id: %s & workflowId: %s", eventId, workflowId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + return Integer.parseInt(stepExists); + } + + /** + *{@inheritDoc} + */ + @Override + public void updateStateOfRequest(String eventId, String workflowId, int currentStep) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + try { + jdbcTemplate.executeUpdate(WorkflowEngineConstants.SqlQueries.UPDATE_STATE_OF_REQUEST, + (preparedStatement -> { + setPreparedStatementForStateOfRequest(currentStep, eventId, workflowId, preparedStatement); + preparedStatement.setInt(1, currentStep); + preparedStatement.setString(2, eventId); + preparedStatement.setString(3, workflowId); + })); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while updating state from" + + "eventId: %s", eventId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + } + + private void setPreparedStatementForStateOfRequest(int currentStep, String eventId, String workflowId, + PreparedStatement preparedStatement) throws SQLException { + + preparedStatement.setInt(1, currentStep); + preparedStatement.setString(2, eventId); + preparedStatement.setString(3, workflowId); + } + + /** + * {@inheritDoc} + */ + @Override + public List listApprovers(String taskId) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + List requestsList; + try { + requestsList = jdbcTemplate.executeQuery(WorkflowEngineConstants.SqlQueries. + GET_APPROVER_NAME_RELATED_TO_CURRENT_TASK_ID, (resultSet, rowNumber) -> + resultSet.getString(WorkflowEngineConstants.APPROVER_NAME_COLUMN), + preparedStatement -> preparedStatement.setString(1, taskId)); + } catch (DataAccessException e) { + String errorMessage = "Error occurred while retrieving approvers"; + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + return requestsList; + } + + /** + * {@inheritDoc} + */ + @Override + public List getRolesID(String userName) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + List roleIdList; + try { + roleIdList = jdbcTemplate.executeQuery(WorkflowEngineConstants.SqlQueries. + GET_ROLE_ID, (resultSet, rowNumber) -> + resultSet.getInt(WorkflowEngineConstants.ROLE_ID_COLUMN), + preparedStatement -> preparedStatement.setString(1, userName)); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while retrieving Role ID from" + + "user name: %s", userName); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + return roleIdList; + } + + /** + * {@inheritDoc} + */ + @Override + public List getRoleNames(int roleId) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + List roleNameList; + try { + roleNameList = jdbcTemplate.executeQuery(WorkflowEngineConstants.SqlQueries. + GET_ROLE_NAME, (resultSet, rowNumber) -> + resultSet.getString(WorkflowEngineConstants.ROLE_NAME), + preparedStatement -> preparedStatement.setInt(1, roleId)); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while retrieving Role name from" + + "role ID: %d", roleId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + return roleNameList; + } + + /** + * {@inheritDoc} + */ + @Override + public String getRequestID(String taskId) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + String requestId; + try { + requestId = jdbcTemplate.fetchSingleRecord(WorkflowEngineConstants.SqlQueries. + GET_REQUEST_ID, + ((resultSet, i) -> ( + resultSet.getString(WorkflowEngineConstants.EVENT_ID))), + preparedStatement -> preparedStatement.setString(1, taskId)); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while retrieving request ID from" + + "taskID: %s", taskId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + return requestId; + } + + /** + *{@inheritDoc} + */ + @Override + public String getInitiatedUser(String requestId) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + String createdBy; + try { + createdBy = jdbcTemplate.fetchSingleRecord(WorkflowEngineConstants.SqlQueries. + GET_CREATED_USER, + ((resultSet, i) -> ( + resultSet.getString(WorkflowEngineConstants.CREATED_USER_COLUMN))), + preparedStatement -> preparedStatement.setString(1, requestId)); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while retrieving initiator from" + + "requestId: %s", requestId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + return createdBy; + } + + /** + * {@inheritDoc} + */ + @Override + public List getRequestsList(String approverName) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + List requestIdList; + try { + requestIdList = jdbcTemplate.executeQuery(WorkflowEngineConstants.SqlQueries. + GET_REQUEST_ID_FROM_APPROVER, (resultSet, rowNumber) -> + resultSet.getString(WorkflowEngineConstants.EVENT_ID), + preparedStatement -> preparedStatement.setString(1, approverName)); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while retrieving request id from" + + "approver name: %s", approverName); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + return requestIdList; + } + + /** + * {@inheritDoc} + */ + @Override + public String getEventType(String requestId) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + String eventType; + try { + eventType = jdbcTemplate.fetchSingleRecord(WorkflowEngineConstants.SqlQueries. + GET_EVENT_TYPE, + ((resultSet, i) -> ( + resultSet.getString(WorkflowEngineConstants.EVENT_TYPE_COLUMN))), + preparedStatement -> preparedStatement.setString(1, requestId)); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while retrieving event type from" + + "request : %s", requestId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + return eventType; + } + + /** + * {@inheritDoc} + */ + @Override + public String getTaskStatusOfRequest(String taskId) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + String taskStatus; + try { + taskStatus = jdbcTemplate.fetchSingleRecord(WorkflowEngineConstants.SqlQueries. + GET_TASK_STATUS, + ((resultSet, i) -> ( + resultSet.getString(WorkflowEngineConstants.TASK_STATUS_COLUMN))), + preparedStatement -> preparedStatement.setString(1, taskId)); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while retrieving task status from" + + "task Id: %s", taskId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + return taskStatus; + } + + /** + * {@inheritDoc} + */ + @Override + public String getStatusOfTask(String requestId) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + String taskStatus; + try { + taskStatus = jdbcTemplate.fetchSingleRecord(WorkflowEngineConstants.SqlQueries. + GET_STATUS, + ((resultSet, i) -> ( + resultSet.getString(WorkflowEngineConstants.TASK_STATUS_COLUMN))), + preparedStatement -> preparedStatement.setString(1, requestId)); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while retrieving task status from" + + "request Id: %s", requestId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + return taskStatus; + } + + /** + * {@inheritDoc} + */ + @Override + public void updateStatusOfRequest(String taskId, String taskStatus) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + try { + jdbcTemplate.executeUpdate(WorkflowEngineConstants.SqlQueries.UPDATE_TASK_STATUS, + (preparedStatement -> { + setPreparedStatementForStatusOfRequest(taskStatus, taskId, preparedStatement); + preparedStatement.setString(1, taskStatus); + preparedStatement.setString(2, taskId); + })); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while updating status from" + + "taskID: %s", taskId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public String getRelationshipId(String eventId) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + String taskStatus; + try { + taskStatus = jdbcTemplate.fetchSingleRecord(WorkflowEngineConstants.SqlQueries. + GET_REQUEST_ID_OF_RELATIONSHIP, + ((resultSet, i) -> ( + resultSet.getString(WorkflowEngineConstants.RELATIONSHIP_ID_IN_REQUEST_COLUMN))), + preparedStatement -> preparedStatement.setString(1, eventId)); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while retrieving relationship ID from" + + "event Id: %s", eventId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + return taskStatus; + } + + /** + * {@inheritDoc} + */ + @Override + public List getTaskId(String eventId) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + List taskIdList; + try { + taskIdList = jdbcTemplate.executeQuery(WorkflowEngineConstants.SqlQueries. + GET_TASK_ID_FROM_REQUEST, (resultSet, rowNumber) -> + resultSet.getString(WorkflowEngineConstants.TASK_ID_COLUMN), + preparedStatement -> preparedStatement.setString(1, eventId)); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while retrieving tasks from" + + "request id : %s", eventId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + return taskIdList; + } + + /** + * {@inheritDoc} + */ + @Override + public Timestamp getCreatedAtTimeInMill(String requestId) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + java.sql.Timestamp createdTime; + try { + createdTime = jdbcTemplate.fetchSingleRecord(WorkflowEngineConstants.SqlQueries. + GET_CREATED_TIME_IN_MILL, + ((resultSet, i) -> ( + resultSet.getTimestamp(WorkflowEngineConstants.CREATED_AT_IN_MILL_COLUMN))), + preparedStatement -> preparedStatement.setString(1, requestId)); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while retrieving createdAt time from" + + "request Id: %s", requestId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + return createdTime; + } + + private void setPreparedStatementForStatusOfRequest(String taskStatus, String taskId, + PreparedStatement preparedStatement) throws SQLException { + + preparedStatement.setString(1, taskStatus); + preparedStatement.setString(2, taskId); + } + + /** + * {@inheritDoc} + */ + @Override + public void deleteCurrentStepOfRequest(String eventId) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + try { + jdbcTemplate.executeUpdate(WorkflowEngineConstants.SqlQueries.DELETE_CURRENT_STEP_OF_REQUEST, + preparedStatement -> preparedStatement.setString(1, eventId)); + } catch (DataAccessException e) { + String errorMessage = String.format("Error while deleting the current step from eventID:%s", eventId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + } + + /** + *{@inheritDoc} + */ + @Override + public String getWorkflowID(String taskId) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + String taskStatus; + try { + taskStatus = jdbcTemplate.fetchSingleRecord(WorkflowEngineConstants.SqlQueries. + GET_WORKFLOW_ID, + ((resultSet, i) -> ( + resultSet.getString(WorkflowEngineConstants.WORKFLOW_ID))), + preparedStatement -> preparedStatement.setString(1, taskId)); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while retrieving workflow from" + + "task Id: %s", taskId); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + return taskStatus; + } + + /** + *{@inheritDoc} + */ + @Override + public String getWorkflowName(String workflowID) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + String taskStatus; + try { + taskStatus = jdbcTemplate.fetchSingleRecord(WorkflowEngineConstants.SqlQueries. + GET_WORKFLOW_NAME, + ((resultSet, i) -> ( + resultSet.getString(WorkflowEngineConstants.WORKFLOW_NAME))), + preparedStatement -> preparedStatement.setString(1, workflowID)); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while retrieving workflow name from" + + "workflow Id: %s", workflowID); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + return taskStatus; + } + + /** + *{@inheritDoc} + */ + @Override + public String getEntityNameOfRequest(String requestID) { + + JdbcTemplate jdbcTemplate = JdbcUtils.getNewTemplate(); + String taskStatus; + try { + taskStatus = jdbcTemplate.fetchSingleRecord(WorkflowEngineConstants.SqlQueries. + GET_ENTITY_NAME, + ((resultSet, i) -> ( + resultSet.getString(WorkflowEngineConstants.ENTITY_NAME))), + preparedStatement -> preparedStatement.setString(1, requestID)); + } catch (DataAccessException e) { + String errorMessage = String.format("Error occurred while retrieving workflow name from" + + "workflow Id: %s", requestID); + if (log.isDebugEnabled()) { + log.debug(errorMessage, e); + } + throw new WorkflowEngineServerException(errorMessage, e); + } + return taskStatus; + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/model/PagePagination.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/model/PagePagination.java new file mode 100644 index 0000000..2c746ce --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/model/PagePagination.java @@ -0,0 +1,46 @@ +package org.wso2.carbon.identity.workflow.engine.model; + +/** + * The model class for set up the page pagination. + */ +public class PagePagination { + + private int localPageSize; + private boolean localPageSizeTracker = false; + private int localPageNumber; + private boolean localPageNumberTracker = false; + + /** + * Maximum number of results expected. + */ + public int getPageSize() { + + return this.localPageSize; + } + + /** + * Set maximum number of results expected. + */ + public void setPageSize(int param) { + + this.localPageSizeTracker = param != Integer.MIN_VALUE; + this.localPageSize = param; + } + + /** + * Start index of the search. + */ + public int getPageNumber() { + + return this.localPageNumber; + } + + /** + * Set start index of the search. + */ + public void setPageNumber(int param) { + + this.localPageNumberTracker = param != Integer.MIN_VALUE; + this.localPageNumber = param; + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/model/TStatus.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/model/TStatus.java new file mode 100644 index 0000000..a5cfad9 --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/model/TStatus.java @@ -0,0 +1,29 @@ +package org.wso2.carbon.identity.workflow.engine.model; + +/** + * Model class for task status [RESERVED, READY or COMPLETED]. + */ +public class TStatus { + + protected String localTStatus; + + public TStatus() { + + } + + /** + * Get the status of the task [RESERVED, READY or COMPLETED]. + */ + public String getTStatus() { + + return this.localTStatus; + } + + /** + * Set the status of the task [RESERVED, READY or COMPLETED]. + */ + public void setTStatus(String param) { + + this.localTStatus = param; + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/model/TaskDetails.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/model/TaskDetails.java new file mode 100644 index 0000000..3a7adc9 --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/model/TaskDetails.java @@ -0,0 +1,42 @@ +package org.wso2.carbon.identity.workflow.engine.model; + +/** + * model class for task subject & task description. + */ +public class TaskDetails { + + String taskDescription; + String taskSubject; + + /** + * The subject of the task. + */ + public String getTaskDescription() { + + return taskDescription; + } + + /** + * Set the subject of the task. + */ + public void setTaskDescription(String taskDescription) { + + this.taskDescription = taskDescription; + } + + /** + * The description of the task. + */ + public String getTaskSubject() { + + return taskSubject; + } + + /** + * Set the description of the task. + */ + public void setTaskSubject(String taskSubject) { + + this.taskSubject = taskSubject; + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/model/TaskModel.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/model/TaskModel.java new file mode 100644 index 0000000..ab72009 --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/model/TaskModel.java @@ -0,0 +1,44 @@ +package org.wso2.carbon.identity.workflow.engine.model; + +import java.util.Map; + +/** + * Model Class for task. + */ +public class TaskModel { + + private String id; + private Map assignees; + + /** + * Unique ID to represent a approval task + **/ + public String getId() { + + return id; + } + + /** + * Set unique ID to represent a approval task + **/ + public void setId(String id) { + + this.id = id; + } + + /** + * To whom the task is assigned:\n * user - username(s) if the task is reserved for specific user(s).\n * group - role name(s) if the task is assignable for group(s).\n + **/ + public Map getAssignees() { + + return assignees; + } + + /** + * Set to whom the task is assigned:\n * user - username(s) if the task is reserved for specific user(s).\n * group - role name(s) if the task is assignable for group(s).\n + **/ + public void setAssignees(Map assignees) { + + this.assignees = assignees; + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/model/TaskParam.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/model/TaskParam.java new file mode 100644 index 0000000..6924c71 --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/model/TaskParam.java @@ -0,0 +1,46 @@ +package org.wso2.carbon.identity.workflow.engine.model; + +/** + * Task Parameter model, Get task parameters as list. + */ +public class TaskParam { + + private String itemName; + private String itemValue; + + /** + * Get the Item name of the task parameters. + * + * @return The Item name of the task parameters. + */ + public String getItemName() { + return itemName; + } + + /** + * Set the Item name for the task parameters. + * + * @param itemName The Item name for the task parameters. + */ + public void setItemName(String itemName) { + this.itemName = itemName; + } + + /** + * Get the Item value of the task parameters. + * + * @return The Item value of the task parameters. + */ + public String getItemValue() { + return itemValue; + } + + /** + * Set the Item value for the task parameters. + * + * @param itemValue The Item value for the task parameters. + */ + public void setItemValue(String itemValue) { + this.itemValue = itemValue; + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/util/WorkflowEngineConstants.java b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/util/WorkflowEngineConstants.java new file mode 100644 index 0000000..b27dc06 --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/main/java/org/wso2/carbon/identity/workflow/engine/util/WorkflowEngineConstants.java @@ -0,0 +1,147 @@ +package org.wso2.carbon.identity.workflow.engine.util; + +/** + * This class holds the constants used in the module, identity-workflow-simple. + */ +public class WorkflowEngineConstants { + + public static final String CURRENT_STEP_COLUMN = "CURRENT_STEP"; + public static final String TASK_ID_COLUMN = "TASK_ID"; + public static final String APPROVER_NAME_COLUMN = "APPROVER_NAME"; + public static final String EVENT_ID = "EVENT_ID"; + public static final String WORKFLOW_ID = "WORKFLOW_ID"; + public static final String WORKFLOW_NAME = "WF_NAME"; + public static final String CREATED_USER_COLUMN = "CREATED_BY"; + public static final String EVENT_TYPE_COLUMN = "OPERATION_TYPE"; + public static final String ENTITY_NAME = "ENTITY_NAME"; + public static final String TASK_STATUS_COLUMN = "TASK_STATUS"; + public static final String CREATED_AT_IN_MILL_COLUMN = "CREATED_AT"; + public static final String RELATIONSHIP_ID_IN_REQUEST_COLUMN = "RELATIONSHIP_ID"; + public static final String ROLE_ID_COLUMN = "UM_ROLE_ID"; + public static final String ROLE_NAME = "UM_ROLE_NAME"; + + /** + * SQL Query definitions. + */ + public static class SqlQueries { + + public static final String ADD_APPROVAL_LIST_RELATED_TO_USER = "INSERT INTO WF_WORKFLOW_APPROVAL_RELATION (TASK_ID,EVENT_ID,WORKFLOW_ID,APPROVER_TYPE,APPROVER_NAME, TASK_STATUS) VALUES (?,?,?,?,?,?)"; + public static final String GET_TASK_ID_RELATED_TO_USER = "SELECT DISTINCT TASK_ID FROM WF_WORKFLOW_APPROVAL_RELATION WHERE EVENT_ID = ?"; + public static final String DELETE_APPROVAL_LIST_RELATED_TO_USER = "DELETE FROM WF_WORKFLOW_APPROVAL_RELATION WHERE TASK_ID=?"; + public static final String ADD_CURRENT_STEP_FOR_EVENT = "INSERT INTO WF_WORKFLOW_APPROVAL_STATE (EVENT_ID,WORKFLOW_ID, CURRENT_STEP) VALUES (?,?,?)"; + public static final String GET_CURRENT_STEP = "SELECT CURRENT_STEP FROM WF_WORKFLOW_APPROVAL_STATE WHERE EVENT_ID = ? AND WORKFLOW_ID = ?"; + public static final String UPDATE_STATE_OF_REQUEST = "UPDATE WF_WORKFLOW_APPROVAL_STATE SET CURRENT_STEP=? WHERE EVENT_ID = ? AND WORKFLOW_ID = ?"; + public static final String DELETE_CURRENT_STEP_OF_REQUEST = "DELETE FROM WF_WORKFLOW_APPROVAL_STATE WHERE EVENT_ID=?"; + public static final String GET_APPROVER_NAME_RELATED_TO_CURRENT_TASK_ID = "SELECT DISTINCT APPROVER_NAME FROM WF_WORKFLOW_APPROVAL_RELATION WHERE TASK_ID = ?"; + public static final String GET_WORKFLOW_ID = "SELECT DISTINCT WORKFLOW_ID FROM WF_WORKFLOW_APPROVAL_RELATION WHERE TASK_ID = ?"; + public static final String UPDATE_TASK_STATUS = "UPDATE WF_WORKFLOW_APPROVAL_RELATION SET TASK_STATUS=? WHERE TASK_ID=?"; + public static final String GET_REQUEST_ID = "SELECT DISTINCT EVENT_ID FROM WF_WORKFLOW_APPROVAL_RELATION WHERE TASK_ID = ?"; + public static final String GET_ENTITY_NAME = "SELECT ENTITY_NAME FROM WF_REQUEST_ENTITY_RELATIONSHIP WHERE REQUEST_ID = ?"; + public static final String GET_TASK_ID_FROM_REQUEST = "SELECT TASK_ID FROM WF_WORKFLOW_APPROVAL_RELATION WHERE EVENT_ID= ?"; + public static final String GET_REQUEST_ID_FROM_APPROVER = "SELECT EVENT_ID FROM WF_WORKFLOW_APPROVAL_RELATION WHERE APPROVER_NAME= ?"; + public static final String GET_TASK_STATUS = "SELECT DISTINCT TASK_STATUS FROM WF_WORKFLOW_APPROVAL_RELATION WHERE TASK_ID = ?"; + public static final String GET_STATUS = "SELECT DISTINCT TASK_STATUS FROM WF_WORKFLOW_APPROVAL_RELATION WHERE EVENT_ID = ?"; + public static final String GET_CREATED_USER = "SELECT CREATED_BY FROM WF_REQUEST WHERE UUID= ?"; + public static final String GET_EVENT_TYPE = "SELECT OPERATION_TYPE FROM WF_REQUEST WHERE UUID= ?"; + public static final String GET_CREATED_TIME_IN_MILL = "SELECT CREATED_AT FROM WF_REQUEST WHERE UUID= ?"; + public static final String GET_REQUEST_ID_OF_RELATIONSHIP = "SELECT RELATIONSHIP_ID FROM " + + "WF_WORKFLOW_REQUEST_RELATION WHERE REQUEST_ID = ?"; + public static final String GET_WORKFLOW_NAME = "SELECT WF_NAME FROM WF_WORKFLOW WHERE ID =? "; + public static final String GET_ROLE_ID = "SELECT UM_ROLE_ID FROM UM_HYBRID_USER_ROLE WHERE UM_USER_NAME=?"; + public static final String GET_ROLE_NAME = "SELECT UM_ROLE_NAME FROM UM_HYBRID_ROLE WHERE UM_ID=?"; + } + + public static class ParameterName { + + public static final String USER_AND_ROLE_STEP = "UserAndRole"; + public static final String TASK_STATUS_DEFAULT = "RESERVED"; + public static final String REQUEST_ID = "REQUEST ID"; + public static final String TASK_SUBJECT = "HTSubject"; + public static final String TASK_DESCRIPTION = "HTDescription"; + public static final String PRIORITY = "High"; + public static final String APPROVAL_TASK = "Approval task"; + public static final String INTERNAL_USER = "Internal/"; + public static final String APPLICATION_USER = "Application"; + public static final String SYSTEM_USER = "system"; + public static final String SYSTEM_PRIMARY_USER = "system_primary_"; + public static final String ASSIGNEE_TYPE = "Type"; + public static final String CREDENTIAL = "Credential"; + public static final String HT_SUBJECT = "HTSubject"; + public static final String INITIATED_BY = "Initiated_by "; + public static final String ENTITY_NAME = " User_Name:"; + } + + public enum TaskStatus { + READY, + RESERVED, + COMPLETED; + + TaskStatus() { + + } + } + + public static class ParameterHolder { + + public static final String WORKFLOW_IMPL = "WorkflowImpl"; + } + + public static class ParameterValue { + + public static final String WORKFLOW_NAME = "WorkflowName"; + } + + /** + * Enum contains Error Codes and Error Messages. + */ + public enum ErrorMessages { + + TASK_NOT_FOUND("SWE_00001", "Invalid event ID"), + ASSOCIATION_NOT_FOUND("SWE_00002", "The associations are not connecting with any request"), + WORKFLOW_ID_NOT_FOUND("SWE_00003", "The workflow Id is not valid"), + ERROR_OCCURRED_WHILE_RETRIEVING_WORKFLOW_REQUEST("SWE_00004", + "Cannot get the workflow request given the request Id"), + ERROR_OCCURRED_WHILE_RETRIEVING_PARAMETER_LIST("SWE_00005", + "The parameterList can't get given the associated workflowId"), + ERROR_OCCURRED_WHILE_CHANGING_APPROVALS_STATE("SWE_00006", "Unable to update the approval status, " + + "Server encountered an error while updating the approval task status."), + ERROR_OCCURRED_WHILE_RETRIEVING_APPROVAL_OF_USER("SWE_00007", "Unable to retrieve the user approval, " + + "Server encountered an error while retrieving information on the approval task."), + ERROR_OCCURRED_WHILE_RETRIEVING_APPROVALS_FOR_USER("SWE_00008", "Unable to retrieve approvals for the user, " + + "Server encountered an error while retrieving approvals for user."), + USER_ERROR_NON_EXISTING_TASK_ID("SWE_10001", "Task does not exist."), + USER_ERROR_NOT_ACCEPTABLE_INPUT_FOR_NEXT_STATE("10005", "Unacceptable input provided, " + + "Only [CLAIM, RELEASE, APPROVED, REJECTED] are acceptable."); + private final String code; + private final String description; + + /** + * Error Messages + * + * @param code Code of the error message. + * @param description Error message string. + */ + ErrorMessages(String code, String description) { + + this.code = code; + this.description = description; + } + + public String getCode() { + + return code; + } + + public String getDescription() { + + return this.description; + } + + @Override + public String toString() { + + return this.code + " - " + this.description; + } + } + +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/test/java/org/wso2/carbon/identity/workflow/engine/DefaultWorkflowEventRequestServiceTest.java b/components/org.wso2.carbon.identity.workflow.engine/src/test/java/org/wso2/carbon/identity/workflow/engine/DefaultWorkflowEventRequestServiceTest.java new file mode 100644 index 0000000..9495c01 --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/test/java/org/wso2/carbon/identity/workflow/engine/DefaultWorkflowEventRequestServiceTest.java @@ -0,0 +1,237 @@ +package org.wso2.carbon.identity.workflow.engine; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.testng.PowerMockTestCase; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil; +import org.wso2.carbon.identity.core.util.IdentityUtil; +import org.wso2.carbon.identity.workflow.engine.exception.WorkflowEngineServerException; +import org.wso2.carbon.identity.workflow.engine.internal.dao.WorkflowEventRequestDAO; +import org.wso2.carbon.identity.workflow.engine.internal.dao.impl.WorkflowEventRequestDAOImpl; +import org.wso2.carbon.identity.workflow.engine.util.TestUtils; + +import java.sql.Connection; +import java.sql.SQLException; +import javax.sql.DataSource; + +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.when; +import static org.testng.Assert.assertEquals; + +@PrepareForTest({IdentityDatabaseUtil.class, IdentityUtil.class, DefaultWorkflowEventRequestServiceTest.class}) +public class DefaultWorkflowEventRequestServiceTest extends PowerMockTestCase { + + DefaultWorkflowEventRequest defaultWorkflowEventRequest = new DefaultWorkflowEventRequestService(); + WorkflowEventRequestDAO workflowEventRequestDAO = new WorkflowEventRequestDAOImpl(); + private static final Log log = LogFactory.getLog(DefaultWorkflowEventRequestServiceTest.class); + + @BeforeMethod + public void setUp() throws Exception { + + TestUtils.initiateH2Base(); + DataSource dataSource = mock(DataSource.class); + TestUtils.mockDataSource(dataSource); + mockStatic(IdentityUtil.class); + + try (Connection connection = TestUtils.getConnection()) { + Connection spyConnection = TestUtils.spyConnection(connection); + when(dataSource.getConnection()).thenReturn(spyConnection); + try { + workflowEventRequestDAO + .addApproversOfRequest("task1", "event1", "wf1", "role", + "Manager", "Reserved"); + workflowEventRequestDAO + .addApproversOfRequest("task2", "event2", "wf2", "role", + "Supervisor", null); + workflowEventRequestDAO + .addApproversOfRequest("task3", "event3", "wf3", "user", + "Senior manager", "Reserved"); + } catch (WorkflowEngineServerException e) { + log.error("Error while adding a task", e); + } + } + } + + @AfterMethod + public void tearDown() throws Exception { + + TestUtils.closeH2Base(); + } + + @DataProvider(name = "testGetApproversOfRequestsData") + public Object[][] testGetApproversOfRequestsData() { + + return new Object[][]{ + // eventId + // taskId + {"event4", "task4"}, + {"event5", "task5"}, + {"event6", "task6"} + }; + } + + @Test(dataProvider = "testGetApproversOfRequestsData") + public void testGetApprovalOfRequest(String eventId, String expected) { + + DataSource dataSource = mock(DataSource.class); + TestUtils.mockDataSource(dataSource); + try { + try (Connection connection = TestUtils.getConnection()) { + Connection spyConnection = TestUtils.spyConnection(connection); + when(dataSource.getConnection()).thenReturn(spyConnection); + try { + workflowEventRequestDAO + .addApproversOfRequest("task4", "event4", "wf4", "user", + "Bob", "Reserved"); + workflowEventRequestDAO + .addApproversOfRequest("task5", "event5", "wf5", "role", + "Supervisor", "Ready"); + workflowEventRequestDAO + .addApproversOfRequest("task6", "event6", "wf6", "user", + "Sam", "Reserved"); + String task = workflowEventRequestDAO.getApproversOfRequest(eventId); + assertEquals(task, expected); + } catch (WorkflowEngineServerException e) { + log.error(String.format("Error while selecting a task: %s", eventId), e); + } + } + } catch (SQLException e) { + log.error("SQL Exception", e); + } + } + + @DataProvider(name = "testDeleteApprovalOfRequestData") + public Object[][] testDeleteApprovalOfRequestData() { + + return new Object[][]{ + // taskId + // eventId + {"task1", "event1"}, + {"task2", "event2"}, + {"task3", "event3"} + }; + } + + @Test(dataProvider = "testDeleteApprovalOfRequestData") + public void testDeleteApprovalOfRequest(String taskId, String eventId) { + + DataSource dataSource = mock(DataSource.class); + TestUtils.mockDataSource(dataSource); + try { + try (Connection connection = TestUtils.getConnection()) { + Connection spyConnection = TestUtils.spyConnection(connection); + when(dataSource.getConnection()).thenReturn(spyConnection); + try { + defaultWorkflowEventRequest.deleteApprovalOfRequest(taskId); + String task = defaultWorkflowEventRequest.getApprovalOfRequest(eventId); + Assert.assertNull(task); + } catch (WorkflowEngineServerException e) { + log.error(String.format("Error while selecting a task: %s", taskId), e); + } + } + } catch (SQLException e) { + log.error("SQL Exception", e); + } + } + + @DataProvider(name = "testStatesOfRequest") + public Object[][] testCreateStatesOfRequestData() { + + return new Object[][]{ + // eventId + // workflowId + // currentStep + {"event1", "wf1", 1}, + {"event1", "wf1", 2}, + {"event2", "wf2", 2}, + {"event3", "wf3", 1} + }; + } + + @Test(dataProvider = "testStatesOfRequest") + public void testStatesOfRequest(String eventId, String workflowId, int currentStep) { + + DataSource dataSource = mock(DataSource.class); + TestUtils.mockDataSource(dataSource); + + try { + try (Connection connection = TestUtils.getConnection()) { + Connection spyConnection = TestUtils.spyConnection(connection); + when(dataSource.getConnection()).thenReturn(spyConnection); + try { + defaultWorkflowEventRequest.createStatesOfRequest(eventId, workflowId, currentStep); + int step = defaultWorkflowEventRequest.getStateOfRequest(eventId, workflowId); + assertEquals(step, currentStep); + } catch (WorkflowEngineServerException e) { + log.error(String.format("Error while selecting current step: %s", eventId), e); + } + } + } catch (SQLException e) { + log.error("SQL Exception", e); + } + } + + @Test(dataProvider = "testStatesOfRequest") + public void testUpdateStateOfRequest(String eventId, String workflowId, int currentStep) { + + DataSource dataSource = mock(DataSource.class); + TestUtils.mockDataSource(dataSource); + try { + try (Connection connection = TestUtils.getConnection()) { + Connection spyConnection = TestUtils.spyConnection(connection); + when(dataSource.getConnection()).thenReturn(spyConnection); + try { + defaultWorkflowEventRequest.createStatesOfRequest(eventId, workflowId, currentStep); + int step = defaultWorkflowEventRequest.getStateOfRequest(eventId, workflowId); + assertEquals(step, currentStep); + } catch (WorkflowEngineServerException e) { + log.error(String.format("Error while selecting current step: %s", eventId), e); + } + } + } catch (SQLException e) { + log.error("SQL Exception", e); + } + } + + @DataProvider(name = "testDeleteStateOfRequestData") + public Object[][] testDeleteStateOfRequestData() { + + return new Object[][]{ + // eventId + // workflowId + {"event1", "wf1"}, + {"event2", "wf2"}, + {"event3", "wf3"} + }; + } + + @Test(dataProvider = "testDeleteStateOfRequestData") + public void testDeleteStateOfRequest(String eventId, String workflowId) { + + DataSource dataSource = mock(DataSource.class); + TestUtils.mockDataSource(dataSource); + try { + try (Connection connection = TestUtils.getConnection()) { + Connection spyConnection = TestUtils.spyConnection(connection); + when(dataSource.getConnection()).thenReturn(spyConnection); + try { + defaultWorkflowEventRequest.deleteStateOfRequest(eventId); + int step = defaultWorkflowEventRequest.getStateOfRequest(eventId, workflowId); + + assertEquals(step, 0); + } catch (WorkflowEngineServerException e) { + log.error(String.format("Error while selecting current step: %s", eventId), e); + } + } + } catch (SQLException e) { + log.error("SQL Exception", e); + } + } +} \ No newline at end of file diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/test/java/org/wso2/carbon/identity/workflow/engine/internal/dao/impl/WorkflowEventRequestDAOImplTest.java b/components/org.wso2.carbon.identity.workflow.engine/src/test/java/org/wso2/carbon/identity/workflow/engine/internal/dao/impl/WorkflowEventRequestDAOImplTest.java new file mode 100644 index 0000000..5090985 --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/test/java/org/wso2/carbon/identity/workflow/engine/internal/dao/impl/WorkflowEventRequestDAOImplTest.java @@ -0,0 +1,368 @@ +package org.wso2.carbon.identity.workflow.engine.internal.dao.impl; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.testng.PowerMockTestCase; +import org.testng.Assert; +import org.testng.IObjectFactory; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.ObjectFactory; +import org.testng.annotations.Test; +import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil; +import org.wso2.carbon.identity.workflow.engine.util.TestUtils; +import org.wso2.carbon.identity.workflow.engine.exception.WorkflowEngineServerException; +import org.wso2.carbon.identity.workflow.engine.internal.dao.WorkflowEventRequestDAO; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import javax.sql.DataSource; + +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.wso2.carbon.identity.workflow.engine.util.TestUtils.getConnection; +import static org.wso2.carbon.identity.workflow.engine.util.TestUtils.mockDataSource; +import static org.wso2.carbon.identity.workflow.engine.util.TestUtils.spyConnection; + +@PrepareForTest({IdentityDatabaseUtil.class}) +public class WorkflowEventRequestDAOImplTest extends PowerMockTestCase { + + private static final Log log = LogFactory.getLog(WorkflowEventRequestDAOImplTest.class); + private WorkflowEventRequestDAO workflowEventRequestDAO = new WorkflowEventRequestDAOImpl(); + + @BeforeMethod + public void setUp() throws Exception { + + TestUtils.initiateH2Base(); + mockStatic(IdentityDatabaseUtil.class); + } + + @AfterMethod + public void tearDown() throws Exception { + + TestUtils.closeH2Base(); + } + + @DataProvider(name = "testAddApproversOfRequestsData") + public Object[][] testAddApproversOfRequestsData() { + + return new Object[][]{ + // taskId + // eventId + // workflowId + // approverType + // approverName + // taskStatus + {"task1", "event1", "workflowId1", "role", "role1", "Reserved"}, + {"task2", "event2", "workflowId2", "role", "role2", "Reserved"}, + {"task3", "event3", "workflowId3", "user", "user2", "Ready"} + }; + } + + @Test(dataProvider = "testAddApproversOfRequestsData") + public void testAddApproversOfRequest(String taskId, String eventId, String workflowId, String approverType, + String approverName, String taskStatus) { + + { + DataSource dataSource = mock(DataSource.class); + TestUtils.mockDataSource(dataSource); + + try (Connection connection = TestUtils.getConnection()) { + Connection spyConnection = TestUtils.spyConnection(connection); + when(dataSource.getConnection()).thenReturn(spyConnection); + try { + workflowEventRequestDAO.addApproversOfRequest(taskId, eventId, workflowId, approverType, approverName, taskStatus); + String task = workflowEventRequestDAO.getApproversOfRequest(eventId); + assertNotNull(task); + Assert.assertEquals(workflowEventRequestDAO.getWorkflowID(taskId), workflowId); + Assert.assertEquals(workflowEventRequestDAO.getRequestID(taskId), eventId); + Assert.assertEquals(workflowEventRequestDAO.getApproversOfRequest(eventId), taskId); + Assert.assertEquals(workflowEventRequestDAO.getTaskStatusOfRequest(taskId), taskStatus); + } catch (WorkflowEngineServerException e) { + log.error(String.format("Error while adding task: %s", taskId), e); + } + } catch (SQLException e) { + //Mock behaviour. Hence, ignored. + } catch (WorkflowEngineServerException e) { + assertEquals(e.getMessage(), + String.format("Error occurred while adding the taskId: %s, for event: %s, for workflow" + + " id: %s, having the approver type: %s, with approver name " + + ": %s, task status: %s", + taskId, eventId, workflowId, approverType, approverName, taskStatus)); + } + } + } + + @DataProvider(name = "testApprovalOfRequestData") + public Object[][] testDeleteApprovalOfRequestData() { + + return new Object[][]{ + // taskId + // eventId + {"task1", "event1"}, + {"task2", "event2"} + }; + } + + @Test(dataProvider = "testApprovalOfRequestData") + public void testDeleteApproversOfRequest(String taskId, String eventId) { + + DataSource dataSource = mock(DataSource.class); + TestUtils.mockDataSource(dataSource); + + try (Connection connection = TestUtils.getConnection()) { + Connection spyConnection = TestUtils.spyConnection(connection); + when(dataSource.getConnection()).thenReturn(spyConnection); + try { + workflowEventRequestDAO.deleteApproversOfRequest(taskId); + String task = workflowEventRequestDAO.getApproversOfRequest(eventId); + Assert.assertNull(task); + } catch (WorkflowEngineServerException e) { + log.error(String.format("Error while adding task: %s", taskId), e); + } + } catch (SQLException e) { + //Mock behaviour. Hence, ignored. + } catch (WorkflowEngineServerException e) { + assertEquals(e.getMessage(), + String.format("Error occurred while adding the taskId: %s, for event: %s", + taskId, eventId)); + } + } + + @DataProvider(name = "testStatesOfRequestData") + public Object[][] testCreateStatesOfRequestData() { + + return new Object[][]{ + // eventId + // workflowId + // currentStep + {"event1", "wf1", 1}, + {"event2", "wf2", 2}, + {"event3", "wf3", 0} + }; + } + + @Test(dataProvider = "testStatesOfRequestData") + public void testCreateStatesOfRequest(String eventId, String workflowId, int currentStep) { + + DataSource dataSource = mock(DataSource.class); + mockDataSource(dataSource); + + try (Connection connection = getConnection()) { + Connection spyConnection = spyConnection(connection); + when(dataSource.getConnection()).thenReturn(spyConnection); + workflowEventRequestDAO.createStatesOfRequest(eventId, workflowId, currentStep); + int step = workflowEventRequestDAO.getStateOfRequest(eventId, workflowId); + assertEquals(step, currentStep); + } catch (SQLException | WorkflowEngineServerException e) { + //Mock behaviour. Hence, ignored. + } + } + + @Test(dataProvider = "testStatesOfRequestData") + public void testUpdateStateOfRequest(String eventId, String workflowId, int currentStep) { + + DataSource dataSource = mock(DataSource.class); + mockDataSource(dataSource); + + try (Connection connection = getConnection()) { + Connection spyConnection = spyConnection(connection); + when(dataSource.getConnection()).thenReturn(spyConnection); + workflowEventRequestDAO.createStatesOfRequest(eventId, workflowId, currentStep); + int step = workflowEventRequestDAO.getStateOfRequest(eventId, workflowId); + step += 1; + workflowEventRequestDAO.updateStateOfRequest(eventId, workflowId, step); + int updatedStep = workflowEventRequestDAO.getStateOfRequest(eventId, workflowId); + assertEquals(step, updatedStep); + } catch (SQLException | WorkflowEngineServerException e) { + //Mock behaviour. Hence, ignored. + } + } + + @DataProvider(name = "testDeleteStateOfRequestData") + public Object[][] testDeleteStateOfRequestData() { + + return new Object[][]{ + // eventId + // workflowId + {"event1", "wf1"}, + }; + } + + @Test(dataProvider = "testDeleteStateOfRequestData") + public void testDeleteCurrentStepOfRequest(String eventId, String workflowId) { + + DataSource dataSource = mock(DataSource.class); + mockDataSource(dataSource); + + try (Connection connection = getConnection()) { + Connection spyConnection = spyConnection(connection); + when(dataSource.getConnection()).thenReturn(spyConnection); + workflowEventRequestDAO.deleteCurrentStepOfRequest(eventId); + int step = workflowEventRequestDAO.getStateOfRequest(eventId, workflowId); + assertEquals(step, 0); + } catch (SQLException | WorkflowEngineServerException e) { + //Mock behaviour. Hence, ignored. + } + } + + @DataProvider(name = "testListApproversData") + public Object[][] testListApproversData() { + + List approverList = new ArrayList<>(); + approverList.add("Alex"); + approverList.add("Bob"); + + List approverList1 = new ArrayList<>(); + approverList1.add("Sam"); + + return new Object[][]{ + // eventId + // expected List + {"task4", approverList}, + {"task5", approverList1} + }; + } + + @Test(dataProvider = "testListApproversData") + public void testListApprovers(String taskId, List expectedList) { + + DataSource dataSource = mock(DataSource.class); + mockDataSource(dataSource); + + try (Connection connection = getConnection()) { + Connection spyConnection = spyConnection(connection); + when(dataSource.getConnection()).thenReturn(spyConnection); + workflowEventRequestDAO.addApproversOfRequest("task4", "event4", "wf1", + "user", "Alex", "Reserved"); + workflowEventRequestDAO.addApproversOfRequest("task4", "event4", "wf1", + "user", "Bob", "Reserved"); + workflowEventRequestDAO.addApproversOfRequest("task5", "event5", "wf2", + "user", "Sam", "Reserved"); + + List approvers = workflowEventRequestDAO.listApprovers(taskId); + Assert.assertEquals(approvers, expectedList); + + } catch (SQLException | WorkflowEngineServerException e) { + //Mock behaviour. Hence, ignored. + } + } + + @DataProvider(name = "testGetRequestIdFromApproverData") + public Object[][] testGetRequestIdFromApproverData() { + + return new Object[][]{ + // eventId + // workflowId + {"Alex", "event6"}, + {"Bob", "event5"} + }; + } + + @Test(dataProvider = "testGetRequestIdFromApproverData") + public void testGetRequestIdFromApprover(String approverName, String eventId) { + + DataSource dataSource = mock(DataSource.class); + mockDataSource(dataSource); + + try (Connection connection = getConnection()) { + Connection spyConnection = spyConnection(connection); + when(dataSource.getConnection()).thenReturn(spyConnection); + workflowEventRequestDAO.addApproversOfRequest("task6", "event6", "wf6", + "user", "Alex", "Reserved"); + Assert.assertFalse(approverName.isEmpty()); + + } catch (SQLException | WorkflowEngineServerException e) { + //Mock behaviour. Hence, ignored. + } + } + + @DataProvider(name = "testGetRequestsListData") + public Object[][] testGetRequestsListData() { + + //List approverList = Arrays.asList(new String[]{"Bob"}); + List requestList = new ArrayList<>(); + requestList.add("request1"); + requestList.add("request2"); + + List requestList1 = new ArrayList<>(); + requestList1.add("request3"); + + return new Object[][]{ + // eventId + // expected List + {"Alex", requestList}, + {"Sam", requestList1} + }; + } + + @Test(dataProvider = "testGetRequestsListData") + public void testGetRequestsList(String approverName, List exceptedList) { + + DataSource dataSource = mock(DataSource.class); + mockDataSource(dataSource); + + try (Connection connection = getConnection()) { + Connection spyConnection = spyConnection(connection); + when(dataSource.getConnection()).thenReturn(spyConnection); + workflowEventRequestDAO.addApproversOfRequest("task7", "request1", "wf1", + "user", "Alex", "Reserved"); + workflowEventRequestDAO.addApproversOfRequest("task8", "request2", "wf1", + "user", "Alex", "Reserved"); + workflowEventRequestDAO.addApproversOfRequest("task9", "request3", "wf2", + "user", "Sam", "Reserved"); + + List requests = workflowEventRequestDAO.getRequestsList(approverName); + Assert.assertEquals(requests, exceptedList); + + } catch (SQLException | WorkflowEngineServerException e) { + //Mock behaviour. Hence, ignored. + } + } + + @DataProvider(name = "testUpdateStatusOfRequestData") + public Object[][] testUpdateStatusOfRequestData() { + + return new Object[][]{ + // taskId + // taskStatus + {"task10", "Completed"}, + {"task11", "Reserved"} + }; + } + + @Test(dataProvider = "testUpdateStatusOfRequestData") + public void testUpdateStatusOfRequest(String taskId, String taskStatus) { + + DataSource dataSource = mock(DataSource.class); + mockDataSource(dataSource); + + try (Connection connection = getConnection()) { + Connection spyConnection = spyConnection(connection); + when(dataSource.getConnection()).thenReturn(spyConnection); + workflowEventRequestDAO.addApproversOfRequest("task10", "event4", "wf1", + "user", "Alex", "Reserved"); + workflowEventRequestDAO.addApproversOfRequest("task11", "event5", "wf2", + "role", "Bob", "Ready"); + workflowEventRequestDAO.updateStatusOfRequest(taskId, taskStatus); + String updatedStatus = workflowEventRequestDAO.getTaskStatusOfRequest(taskId); + Assert.assertEquals(updatedStatus, taskStatus); + + } catch (SQLException | WorkflowEngineServerException e) { + //Mock behaviour. Hence, ignored. + } + } + + @ObjectFactory + public IObjectFactory getObjectFactory() { + + return new org.powermock.modules.testng.PowerMockObjectFactory(); + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/test/java/org/wso2/carbon/identity/workflow/engine/util/TestUtils.java b/components/org.wso2.carbon.identity.workflow.engine/src/test/java/org/wso2/carbon/identity/workflow/engine/util/TestUtils.java new file mode 100644 index 0000000..2adb24d --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/test/java/org/wso2/carbon/identity/workflow/engine/util/TestUtils.java @@ -0,0 +1,82 @@ +package org.wso2.carbon.identity.workflow.engine.util; + +import org.apache.commons.dbcp.BasicDataSource; +import org.apache.commons.lang.StringUtils; +import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil; + +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import javax.sql.DataSource; + +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.mockStatic; + +public class TestUtils { + + public static final String DB_NAME = "Test_db"; + public static final String H2_SCRIPT_NAME = "h2.sql"; + public static Map dataSourceMap = new HashMap<>(); + public static void mockDataSource(DataSource dataSource) { + + mockStatic(IdentityDatabaseUtil.class); + when(IdentityDatabaseUtil.getDataSource()).thenReturn(dataSource); + } + + public static Connection getConnection() throws SQLException { + + if (dataSourceMap.get(DB_NAME) != null) { + return dataSourceMap.get(DB_NAME).getConnection(); + } + throw new RuntimeException("No data source initiated for database: " + DB_NAME); + } + + public static Connection spyConnection(Connection connection) throws SQLException { + + Connection spy = spy(connection); + doNothing().when(spy).close(); + return spy; + } + public static Connection spyConnectionWithError(Connection connection) throws SQLException { + + Connection spy = spy(connection); + doThrow(new SQLException("Test Exception")).when(spy).prepareStatement(anyString()); + return spy; + } + + public static String getFilePath(String fileName) { + + if (StringUtils.isNotBlank(fileName)) { + return Paths.get(System.getProperty("user.dir"), "src", "test", "resources", "dbscripts", + fileName).toString(); + } + throw new IllegalArgumentException("DB Script file name cannot be empty."); + } + + public static void initiateH2Base() throws Exception { + + BasicDataSource dataSource = new BasicDataSource(); + dataSource.setDriverClassName("org.h2.Driver"); + dataSource.setUsername("username"); + dataSource.setPassword("password"); + dataSource.setUrl("jdbc:h2:mem:test" + DB_NAME); + try (Connection connection = dataSource.getConnection()) { + connection.createStatement().executeUpdate("RUNSCRIPT FROM '" + getFilePath(H2_SCRIPT_NAME) + "'"); + } + dataSourceMap.put(DB_NAME, dataSource); + } + + public static void closeH2Base() throws Exception { + + BasicDataSource dataSource = dataSourceMap.get(DB_NAME); + if (dataSource != null) { + dataSource.close(); + } + } +} diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/test/resources/dbscripts/h2.sql b/components/org.wso2.carbon.identity.workflow.engine/src/test/resources/dbscripts/h2.sql new file mode 100644 index 0000000..c2017dc --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/test/resources/dbscripts/h2.sql @@ -0,0 +1,39 @@ +CREATE TABLE IF NOT EXISTS WF_REQUEST ( + UUID VARCHAR (45), + CREATED_BY VARCHAR (255), + TENANT_ID INTEGER DEFAULT -1, + OPERATION_TYPE VARCHAR (50), + CREATED_AT TIMESTAMP, + UPDATED_AT TIMESTAMP, + STATUS VARCHAR (30), + REQUEST BLOB, + PRIMARY KEY (UUID) +); + +CREATE TABLE IF NOT EXISTS WF_WORKFLOW( + ID VARCHAR (45), + WF_NAME VARCHAR (45), + DESCRIPTION VARCHAR (255), + TEMPLATE_ID VARCHAR (45), + IMPL_ID VARCHAR (45), + TENANT_ID INTEGER DEFAULT -1, + PRIMARY KEY (ID) +); + +CREATE TABLE IF NOT EXISTS WF_WORKFLOW_APPROVAL_RELATION ( +TASK_ID VARCHAR(45) NOT NULL, +EVENT_ID VARCHAR(45) NOT NULL, +WORKFLOW_ID VARCHAR(45) NOT NULL, +APPROVER_TYPE VARCHAR(45) NOT NULL, +APPROVER_NAME VARCHAR(255) NOT NULL, +TASK_STATUS VARCHAR(255), +CONSTRAINT PK_WF_WORKFLOW_RELATION_CONSTRAINT PRIMARY KEY (TASK_ID, APPROVER_TYPE, APPROVER_NAME) +); + +CREATE TABLE IF NOT EXISTS WF_WORKFLOW_APPROVAL_STATE ( +EVENT_ID VARCHAR(45) NOT NULL, +WORKFLOW_ID VARCHAR(45) NOT NULL, +CURRENT_STEP INTEGER, +CONSTRAINT PK_WF_APPROVAL_STATE_CONSTRAINT PRIMARY KEY (EVENT_ID, WORKFLOW_ID) +); + diff --git a/components/org.wso2.carbon.identity.workflow.engine/src/test/resources/testng.xml b/components/org.wso2.carbon.identity.workflow.engine/src/test/resources/testng.xml new file mode 100644 index 0000000..5ec564e --- /dev/null +++ b/components/org.wso2.carbon.identity.workflow.engine/src/test/resources/testng.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dbfiles/Defaultworkflowengine.sql b/dbfiles/Defaultworkflowengine.sql new file mode 100644 index 0000000..d6258ab --- /dev/null +++ b/dbfiles/Defaultworkflowengine.sql @@ -0,0 +1,26 @@ +CREATE TABLE WF_WORKFLOW_APPROVAL_RELATION ( +TASK_ID VARCHAR(45) NOT NULL, +EVENT_ID VARCHAR(45) NOT NULL, +WORKFLOW_ID VARCHAR(45) NOT NULL, +APPROVER_TYPE VARCHAR(45) NOT NULL, +APPROVER_NAME VARCHAR(255) NOT NULL, +TASK_STATUS VARCHAR(255), +CONSTRAINT PK_WF_WORKFLOW_RELATION_CONSTRAINT PRIMARY KEY (TASK_ID, APPROVER_TYPE, APPROVER_NAME), +FOREIGN KEY (EVENT_ID) REFERENCES WF_REQUEST(UUID) ON DELETE CASCADE, +FOREIGN KEY (WORKFLOW_ID) REFERENCES WF_WORKFLOW(ID) ON DELETE CASCADE +); + +CREATE TABLE WF_WORKFLOW_APPROVAL_STATE ( +EVENT_ID VARCHAR(45) NOT NULL, +WORKFLOW_ID VARCHAR(45) NOT NULL, +CURRENT_STEP INTEGER, +CONSTRAINT PK_WF_APPROVAL_STATE_CONSTRAINT PRIMARY KEY (EVENT_ID, WORKFLOW_ID), +FOREIGN KEY (EVENT_ID) REFERENCES WF_REQUEST(UUID) ON DELETE CASCADE, +FOREIGN KEY (WORKFLOW_ID) REFERENCES WF_WORKFLOW(ID) ON DELETE CASCADE +); + +CREATE INDEX IDX_WF_APPROVAL_STATE ON WF_WORKFLOW_APPROVAL_STATE(EVENT_ID,WORKFLOW_ID); + +CREATE INDEX IDX_WF_APPROVAL_RELATION ON WF_WORKFLOW_APPROVAL_RELATION(TASK_ID, APPROVER_TYPE, APPROVER_NAME); + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..6d6277f --- /dev/null +++ b/pom.xml @@ -0,0 +1,280 @@ + + + + org.wso2 + wso2 + 1.4 + + + org.wso2.carbon.identity.workflow.engine + 4.0.0 + default.workflow.engine + 1.0-SNAPSHOT + pom + WSO2 Carbon - Identity Workflow Simple Module + + + http://wso2.org + + + https://github.com/wso2-incubator/identity-workflow-simple.git + scm:git:https://github.com/wso2-incubator/identity-workflow-simple.git + + scm:git:https://github.com/wso2-incubator/identity-workflow-simple.git + HEAD + + + + components/org.wso2.carbon.identity.workflow.engine + + + + + + org.apache.felix + maven-bundle-plugin + 3.2.0 + true + + NONE + + ${buildNumber} + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${maven.checkstyleplugin.version} + + + validate + validate + + + https://raw.githubusercontent.com/wso2/code-quality-tools/v1.3/checkstyle/checkstyle.xml + + + https://raw.githubusercontent.com/wso2/code-quality-tools/v1.3/checkstyle/suppressions.xml + + UTF-8 + true + true + true + + + check + + + + + + org.codehaus.mojo + buildnumber-maven-plugin + ${maven.buildnumber.plugin.version} + + + validate + + create + + + + + false + false + + + + + + + + wso2.releases + WSO2 internal Repository + https://maven.wso2.org/nexus/content/repositories/releases/ + + true + daily + ignore + + + + wso2.snapshots + WSO2 Snapshot Repository + https://maven.wso2.org/nexus/content/repositories/snapshots/ + + true + daily + + + false + + + + wso2-nexus + WSO2 internal Repository + https://maven.wso2.org/nexus/content/groups/wso2-public/ + + true + daily + ignore + + + + + + + wso2-nexus + WSO2 internal Repository + https://maven.wso2.org/nexus/content/groups/wso2-public/ + + true + daily + ignore + + + + wso2.releases + WSO2 internal Repository + https://maven.wso2.org/nexus/content/repositories/releases/ + + true + daily + ignore + + + + wso2.snapshots + Apache Snapshot Repository + https://maven.wso2.org/nexus/content/repositories/snapshots/ + + true + daily + + + false + + + + + + + + org.apache.maven + maven-core + ${maven.version} + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.configuration.mgt.core + ${carbon.identity.framework.version} + + + org.wso2.carbon.identity.framework + org.wso2.carbon.user.mgt.common + ${carbon.identity.framework.version} + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.workflow.mgt + ${carbon.identity.framework.version} + + + org.apache.ws.commons.axiom.wso2 + axiom + ${axiom.wso2.version} + + + org.apache.felix + org.apache.felix.scr.ds-annotations + ${apache.felix.scr.ds.annotations.version} + + + org.wso2.carbon.identity.framework + org.wso2.carbon.user.mgt + ${carbon.identity.framework.version} + + + org.wso2.carbon.identity.framework + identity-framework + ${carbon.identity.framework.version} + + + org.testng + testng + ${testng.version} + test + + + org.powermock + powermock-api-mockito + ${powermock.version} + + + org.powermock + powermock-module-testng-common + ${powermock.version} + + + org.powermock + powermock-module-testng + ${powermock.version} + + + com.h2database + h2 + test + ${h2database.version} + + + org.ops4j.pax.logging + pax-logging-api + ${pax.logging.api.version} + + + org.someexternallib + someexternallibartifact + ... + + + org.slf4j + slf4j-log4j12 + + + org.slf4j + slf4j-simple + + + org.slf4j + slf4j-api + + + log4j + log4j + + + + + + + + 8 + 8 + 1.4 + 3.1.0 + 3.0.5 + 3.8.0 + 1.2.8 + 5.18.187 + 1.2.11-wso2v16 + 7.4.0 + 3.2.4 + 1.7.4 + 2.1.210 + 1.10.1 + + + diff --git a/test-output/All Test Suite/_home_ishara_identity-workflow-simple_components_org.wso2.carbon.identity.workflow.engine_src_test_java_org_wso2_carbon_identity_workflow_engine.html b/test-output/All Test Suite/_home_ishara_identity-workflow-simple_components_org.wso2.carbon.identity.workflow.engine_src_test_java_org_wso2_carbon_identity_workflow_engine.html new file mode 100644 index 0000000..40b7379 --- /dev/null +++ b/test-output/All Test Suite/_home_ishara_identity-workflow-simple_components_org.wso2.carbon.identity.workflow.engine_src_test_java_org_wso2_carbon_identity_workflow_engine.html @@ -0,0 +1,71 @@ + + +TestNG: /home/ishara/identity-workflow-simple/components/org.wso2.carbon.identity.workflow.engine/src/test/java/org/wso2/carbon/identity/workflow/engine + + + + + + + + +

/home/ishara/identity-workflow-simple/components/org.wso2.carbon.identity.workflow.engine/src/test/java/org/wso2/carbon/identity/workflow/engine

+ + + + + + + + + + + +
Tests passed/Failed/Skipped:0/0/0
Started on:Fri Jul 15 11:20:20 IST 2022
Total time:0 seconds (9 ms)
Included groups:
Excluded groups:

+(Hover the method name to see the test class name)

+ + \ No newline at end of file diff --git a/test-output/All Test Suite/_home_ishara_identity-workflow-simple_components_org.wso2.carbon.identity.workflow.engine_src_test_java_org_wso2_carbon_identity_workflow_engine.xml b/test-output/All Test Suite/_home_ishara_identity-workflow-simple_components_org.wso2.carbon.identity.workflow.engine_src_test_java_org_wso2_carbon_identity_workflow_engine.xml new file mode 100644 index 0000000..22559cf --- /dev/null +++ b/test-output/All Test Suite/_home_ishara_identity-workflow-simple_components_org.wso2.carbon.identity.workflow.engine_src_test_java_org_wso2_carbon_identity_workflow_engine.xml @@ -0,0 +1,4 @@ + + + +