MINOR: Increase visibility on flowable process (#18792)

* Add annotation to turn delete_recursive test into the last one to be executed

* Improve Fetching Entities by only fetching the FQN

* Cut back default batchsize to 500

* implement logging for flowable process

* Implement Error Handling Flow

* Remove testing code

* Checking if User is Reviewer when resolving glossary task
This commit is contained in:
IceS2 2024-11-26 17:04:59 +01:00 committed by GitHub
parent 2e9efe5b82
commit 8cade39bad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 441 additions and 95 deletions

View File

@ -1,17 +1,21 @@
package org.openmetadata.service.governance.workflows; package org.openmetadata.service.governance.workflows;
import static org.openmetadata.service.governance.workflows.Workflow.STAGE_INSTANCE_STATE_ID_VARIABLE; import static org.openmetadata.service.governance.workflows.Workflow.STAGE_INSTANCE_STATE_ID_VARIABLE;
import static org.openmetadata.service.governance.workflows.WorkflowHandler.getProcessDefinitionKeyFromId;
import java.util.UUID; import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate; import org.flowable.engine.delegate.JavaDelegate;
import org.openmetadata.service.Entity; import org.openmetadata.service.Entity;
import org.openmetadata.service.jdbi3.WorkflowInstanceRepository; import org.openmetadata.service.jdbi3.WorkflowInstanceRepository;
import org.openmetadata.service.jdbi3.WorkflowInstanceStateRepository; import org.openmetadata.service.jdbi3.WorkflowInstanceStateRepository;
@Slf4j
public class MainWorkflowTerminationListener implements JavaDelegate { public class MainWorkflowTerminationListener implements JavaDelegate {
@Override @Override
public void execute(DelegateExecution execution) { public void execute(DelegateExecution execution) {
try {
WorkflowInstanceStateRepository workflowInstanceStateRepository = WorkflowInstanceStateRepository workflowInstanceStateRepository =
(WorkflowInstanceStateRepository) (WorkflowInstanceStateRepository)
Entity.getEntityTimeSeriesRepository(Entity.WORKFLOW_INSTANCE_STATE); Entity.getEntityTimeSeriesRepository(Entity.WORKFLOW_INSTANCE_STATE);
@ -21,10 +25,18 @@ public class MainWorkflowTerminationListener implements JavaDelegate {
workflowInstanceStateId, System.currentTimeMillis(), execution.getVariables()); workflowInstanceStateId, System.currentTimeMillis(), execution.getVariables());
WorkflowInstanceRepository workflowInstanceRepository = WorkflowInstanceRepository workflowInstanceRepository =
(WorkflowInstanceRepository) Entity.getEntityTimeSeriesRepository(Entity.WORKFLOW_INSTANCE); (WorkflowInstanceRepository)
Entity.getEntityTimeSeriesRepository(Entity.WORKFLOW_INSTANCE);
UUID workflowInstanceId = UUID.fromString(execution.getProcessInstanceBusinessKey()); UUID workflowInstanceId = UUID.fromString(execution.getProcessInstanceBusinessKey());
workflowInstanceRepository.updateWorkflowInstance( workflowInstanceRepository.updateWorkflowInstance(
workflowInstanceId, System.currentTimeMillis()); workflowInstanceId, System.currentTimeMillis(), execution.getVariables());
} catch (Exception exc) {
LOG.error(
String.format(
"[%s] Failed due to: %s ",
getProcessDefinitionKeyFromId(execution.getProcessDefinitionId()), exc.getMessage()),
exc);
}
} }
} }

View File

@ -13,6 +13,8 @@ public class Workflow {
public static final String STAGE_INSTANCE_STATE_ID_VARIABLE = "stageInstanceStateId"; public static final String STAGE_INSTANCE_STATE_ID_VARIABLE = "stageInstanceStateId";
public static final String WORKFLOW_INSTANCE_EXECUTION_ID_VARIABLE = public static final String WORKFLOW_INSTANCE_EXECUTION_ID_VARIABLE =
"workflowInstanceExecutionId"; "workflowInstanceExecutionId";
public static final String WORKFLOW_RUNTIME_EXCEPTION = "workflowRuntimeException";
public static final String EXCEPTION_VARIABLE = "exception";
private final TriggerWorkflow triggerWorkflow; private final TriggerWorkflow triggerWorkflow;
private final MainWorkflow mainWorkflow; private final MainWorkflow mainWorkflow;

View File

@ -0,0 +1,33 @@
package org.openmetadata.service.governance.workflows;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
@Slf4j
public class WorkflowFailureListener implements FlowableEventListener {
@Override
public void onEvent(FlowableEvent event) {
if (FlowableEngineEventType.JOB_EXECUTION_FAILURE.equals(event.getType())) {
LOG.error("Workflow Failed: " + event);
}
}
@Override
public boolean isFailOnException() {
// Return true if the listener should fail the operation on an exception
return false;
}
@Override
public boolean isFireOnTransactionLifecycleEvent() {
return false;
}
@Override
public String getOnTransaction() {
return null;
}
}

View File

@ -50,6 +50,9 @@ public class WorkflowHandler {
.setJdbcDriver(config.getDataSourceFactory().getDriverClass()) .setJdbcDriver(config.getDataSourceFactory().getDriverClass())
.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE); .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE);
// Add Global Failure Listener
processEngineConfiguration.setEventListeners(List.of(new WorkflowFailureListener()));
if (ConnectionType.MYSQL.label.equals(config.getDataSourceFactory().getDriverClass())) { if (ConnectionType.MYSQL.label.equals(config.getDataSourceFactory().getDriverClass())) {
processEngineConfiguration.setDatabaseType(ProcessEngineConfiguration.DATABASE_TYPE_MYSQL); processEngineConfiguration.setDatabaseType(ProcessEngineConfiguration.DATABASE_TYPE_MYSQL);
} else { } else {
@ -256,4 +259,16 @@ public class WorkflowHandler {
repositoryService.activateProcessDefinitionByKey( repositoryService.activateProcessDefinitionByKey(
getTriggerWorkflowId(workflowName), true, null); getTriggerWorkflowId(workflowName), true, null);
} }
public void terminateWorkflow(String workflowName) {
runtimeService
.createProcessInstanceQuery()
.processDefinitionKey(getTriggerWorkflowId(workflowName))
.list()
.forEach(
instance -> {
runtimeService.deleteProcessInstance(
instance.getId(), "Terminating all instances due to user request.");
});
}
} }

View File

@ -1,15 +1,34 @@
package org.openmetadata.service.governance.workflows; package org.openmetadata.service.governance.workflows;
import static org.openmetadata.service.governance.workflows.Workflow.RELATED_ENTITY_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.WORKFLOW_INSTANCE_EXECUTION_ID_VARIABLE; import static org.openmetadata.service.governance.workflows.Workflow.WORKFLOW_INSTANCE_EXECUTION_ID_VARIABLE;
import static org.openmetadata.service.governance.workflows.WorkflowHandler.getProcessDefinitionKeyFromId;
import java.util.UUID; import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate; import org.flowable.engine.delegate.JavaDelegate;
@Slf4j
public class WorkflowInstanceExecutionIdSetterListener implements JavaDelegate { public class WorkflowInstanceExecutionIdSetterListener implements JavaDelegate {
@Override @Override
public void execute(DelegateExecution execution) { public void execute(DelegateExecution execution) {
try {
String workflowName = getProcessDefinitionKeyFromId(execution.getProcessDefinitionId());
String relatedEntity = (String) execution.getVariable(RELATED_ENTITY_VARIABLE);
LOG.debug(
String.format(
"New Execution for Workflow '%s'. Related Entity: '%s'",
workflowName, relatedEntity));
UUID workflowInstanceExecutionId = UUID.randomUUID(); UUID workflowInstanceExecutionId = UUID.randomUUID();
execution.setVariable(WORKFLOW_INSTANCE_EXECUTION_ID_VARIABLE, workflowInstanceExecutionId); execution.setVariable(WORKFLOW_INSTANCE_EXECUTION_ID_VARIABLE, workflowInstanceExecutionId);
} catch (Exception exc) {
LOG.error(
String.format(
"[%s] Failed due to: %s ",
getProcessDefinitionKeyFromId(execution.getProcessDefinitionId()), exc.getMessage()),
exc);
}
} }
} }

View File

@ -13,8 +13,10 @@ import org.openmetadata.service.jdbi3.WorkflowInstanceRepository;
public class WorkflowInstanceListener implements JavaDelegate { public class WorkflowInstanceListener implements JavaDelegate {
@Override @Override
public void execute(DelegateExecution execution) { public void execute(DelegateExecution execution) {
try {
WorkflowInstanceRepository workflowInstanceRepository = WorkflowInstanceRepository workflowInstanceRepository =
(WorkflowInstanceRepository) Entity.getEntityTimeSeriesRepository(Entity.WORKFLOW_INSTANCE); (WorkflowInstanceRepository)
Entity.getEntityTimeSeriesRepository(Entity.WORKFLOW_INSTANCE);
switch (execution.getEventName()) { switch (execution.getEventName()) {
case "start" -> addWorkflowInstance(execution, workflowInstanceRepository); case "start" -> addWorkflowInstance(execution, workflowInstanceRepository);
@ -24,6 +26,13 @@ public class WorkflowInstanceListener implements JavaDelegate {
"WorkflowStageUpdaterListener does not support listening for the event: '%s'", "WorkflowStageUpdaterListener does not support listening for the event: '%s'",
execution.getEventName())); execution.getEventName()));
} }
} catch (Exception exc) {
LOG.error(
String.format(
"[%s] Failed due to: %s ",
getProcessDefinitionKeyFromId(execution.getProcessDefinitionId()), exc.getMessage()),
exc);
}
} }
private void updateBusinessKey(String processInstanceId) { private void updateBusinessKey(String processInstanceId) {
@ -45,13 +54,22 @@ public class WorkflowInstanceListener implements JavaDelegate {
workflowInstanceId, workflowInstanceId,
System.currentTimeMillis(), System.currentTimeMillis(),
execution.getVariables()); execution.getVariables());
LOG.debug(
String.format(
"Workflow '%s' Triggered. Instance: '%s'", workflowDefinitionName, workflowInstanceId));
} }
private void updateWorkflowInstance( private void updateWorkflowInstance(
DelegateExecution execution, WorkflowInstanceRepository workflowInstanceRepository) { DelegateExecution execution, WorkflowInstanceRepository workflowInstanceRepository) {
String workflowDefinitionName =
getMainWorkflowDefinitionNameFromTrigger(
getProcessDefinitionKeyFromId(execution.getProcessDefinitionId()));
UUID workflowInstanceId = UUID.fromString(execution.getProcessInstanceBusinessKey()); UUID workflowInstanceId = UUID.fromString(execution.getProcessInstanceBusinessKey());
workflowInstanceRepository.updateWorkflowInstance( workflowInstanceRepository.updateWorkflowInstance(
workflowInstanceId, System.currentTimeMillis()); workflowInstanceId, System.currentTimeMillis(), execution.getVariables());
LOG.debug(
String.format(
"Workflow '%s' Finished. Instance: '%s'", workflowDefinitionName, workflowInstanceId));
} }
private String getMainWorkflowDefinitionNameFromTrigger(String triggerWorkflowDefinitionName) { private String getMainWorkflowDefinitionNameFromTrigger(String triggerWorkflowDefinitionName) {

View File

@ -16,6 +16,7 @@ import org.openmetadata.service.jdbi3.WorkflowInstanceStateRepository;
public class WorkflowInstanceStageListener implements JavaDelegate { public class WorkflowInstanceStageListener implements JavaDelegate {
@Override @Override
public void execute(DelegateExecution execution) { public void execute(DelegateExecution execution) {
try {
WorkflowInstanceStateRepository workflowInstanceStateRepository = WorkflowInstanceStateRepository workflowInstanceStateRepository =
(WorkflowInstanceStateRepository) (WorkflowInstanceStateRepository)
Entity.getEntityTimeSeriesRepository(Entity.WORKFLOW_INSTANCE_STATE); Entity.getEntityTimeSeriesRepository(Entity.WORKFLOW_INSTANCE_STATE);
@ -28,6 +29,13 @@ public class WorkflowInstanceStageListener implements JavaDelegate {
"WorkflowStageUpdaterListener does not support listening for the event: '%s'", "WorkflowStageUpdaterListener does not support listening for the event: '%s'",
execution.getEventName())); execution.getEventName()));
} }
} catch (Exception exc) {
LOG.error(
String.format(
"[%s] Failed due to: %s ",
getProcessDefinitionKeyFromId(execution.getProcessDefinitionId()), exc.getMessage()),
exc);
}
} }
private void addNewStage( private void addNewStage(

View File

@ -1,9 +1,15 @@
package org.openmetadata.service.governance.workflows.elements; package org.openmetadata.service.governance.workflows.elements;
import static org.openmetadata.service.governance.workflows.Workflow.WORKFLOW_RUNTIME_EXCEPTION;
import static org.openmetadata.service.governance.workflows.Workflow.getFlowableElementId;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.flowable.bpmn.model.Activity;
import org.flowable.bpmn.model.BoundaryEvent;
import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent; import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.ErrorEventDefinition;
import org.flowable.bpmn.model.FlowNode; import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.FlowableListener; import org.flowable.bpmn.model.FlowableListener;
import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.Process;
@ -16,6 +22,10 @@ import org.openmetadata.service.governance.workflows.flowable.builders.FlowableL
public interface NodeInterface { public interface NodeInterface {
void addToWorkflow(BpmnModel model, Process process); void addToWorkflow(BpmnModel model, Process process);
default BoundaryEvent getRuntimeExceptionBoundaryEvent() {
return null;
}
default void attachWorkflowInstanceStageListeners(FlowNode flowableNode) { default void attachWorkflowInstanceStageListeners(FlowNode flowableNode) {
List<String> events = List.of("start", "end"); List<String> events = List.of("start", "end");
attachWorkflowInstanceStageListeners(flowableNode, events); attachWorkflowInstanceStageListeners(flowableNode, events);
@ -59,4 +69,17 @@ public interface NodeInterface {
.build(); .build();
endEvent.getExecutionListeners().add(listener); endEvent.getExecutionListeners().add(listener);
} }
default BoundaryEvent getRuntimeExceptionBoundaryEvent(Activity activity) {
ErrorEventDefinition runtimeExceptionDefinition = new ErrorEventDefinition();
runtimeExceptionDefinition.setErrorCode(WORKFLOW_RUNTIME_EXCEPTION);
BoundaryEvent runtimeExceptionBoundaryEvent = new BoundaryEvent();
runtimeExceptionBoundaryEvent.setId(
getFlowableElementId(activity.getId(), "runtimeExceptionBoundaryEvent"));
runtimeExceptionBoundaryEvent.addEventDefinition(runtimeExceptionDefinition);
runtimeExceptionBoundaryEvent.setAttachedToRef(activity);
return runtimeExceptionBoundaryEvent;
}
} }

View File

@ -2,6 +2,7 @@ package org.openmetadata.service.governance.workflows.elements.nodes.automatedTa
import static org.openmetadata.service.governance.workflows.Workflow.getFlowableElementId; import static org.openmetadata.service.governance.workflows.Workflow.getFlowableElementId;
import org.flowable.bpmn.model.BoundaryEvent;
import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent; import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.FieldExtension; import org.flowable.bpmn.model.FieldExtension;
@ -21,6 +22,7 @@ import org.openmetadata.service.governance.workflows.flowable.builders.SubProces
public class CheckEntityAttributesTask implements NodeInterface { public class CheckEntityAttributesTask implements NodeInterface {
private final SubProcess subProcess; private final SubProcess subProcess;
private final BoundaryEvent runtimeExceptionBoundaryEvent;
public CheckEntityAttributesTask(CheckEntityAttributesTaskDefinition nodeDefinition) { public CheckEntityAttributesTask(CheckEntityAttributesTaskDefinition nodeDefinition) {
String subProcessId = nodeDefinition.getName(); String subProcessId = nodeDefinition.getName();
@ -45,9 +47,15 @@ public class CheckEntityAttributesTask implements NodeInterface {
attachWorkflowInstanceStageListeners(subProcess); attachWorkflowInstanceStageListeners(subProcess);
this.runtimeExceptionBoundaryEvent = getRuntimeExceptionBoundaryEvent(subProcess);
this.subProcess = subProcess; this.subProcess = subProcess;
} }
@Override
public BoundaryEvent getRuntimeExceptionBoundaryEvent() {
return runtimeExceptionBoundaryEvent;
}
private ServiceTask getCheckEntityAttributesServiceTask(String subProcessId, String rules) { private ServiceTask getCheckEntityAttributesServiceTask(String subProcessId, String rules) {
FieldExtension rulesExpr = FieldExtension rulesExpr =
new FieldExtensionBuilder().fieldName("rulesExpr").fieldValue(rules).build(); new FieldExtensionBuilder().fieldName("rulesExpr").fieldValue(rules).build();
@ -63,5 +71,6 @@ public class CheckEntityAttributesTask implements NodeInterface {
public void addToWorkflow(BpmnModel model, Process process) { public void addToWorkflow(BpmnModel model, Process process) {
process.addFlowElement(subProcess); process.addFlowElement(subProcess);
process.addFlowElement(runtimeExceptionBoundaryEvent);
} }
} }

View File

@ -3,6 +3,7 @@ package org.openmetadata.service.governance.workflows.elements.nodes.automatedTa
import static org.openmetadata.service.governance.workflows.Workflow.getFlowableElementId; import static org.openmetadata.service.governance.workflows.Workflow.getFlowableElementId;
import java.util.Optional; import java.util.Optional;
import org.flowable.bpmn.model.BoundaryEvent;
import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent; import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.FieldExtension; import org.flowable.bpmn.model.FieldExtension;
@ -23,6 +24,7 @@ import org.openmetadata.service.governance.workflows.flowable.builders.SubProces
public class SetEntityCertificationTask implements NodeInterface { public class SetEntityCertificationTask implements NodeInterface {
private final SubProcess subProcess; private final SubProcess subProcess;
private final BoundaryEvent runtimeExceptionBoundaryEvent;
public SetEntityCertificationTask(SetEntityCertificationTaskDefinition nodeDefinition) { public SetEntityCertificationTask(SetEntityCertificationTaskDefinition nodeDefinition) {
String subProcessId = nodeDefinition.getName(); String subProcessId = nodeDefinition.getName();
@ -50,9 +52,15 @@ public class SetEntityCertificationTask implements NodeInterface {
attachWorkflowInstanceStageListeners(subProcess); attachWorkflowInstanceStageListeners(subProcess);
this.runtimeExceptionBoundaryEvent = getRuntimeExceptionBoundaryEvent(subProcess);
this.subProcess = subProcess; this.subProcess = subProcess;
} }
@Override
public BoundaryEvent getRuntimeExceptionBoundaryEvent() {
return runtimeExceptionBoundaryEvent;
}
private ServiceTask getSetEntityCertificationServiceTask( private ServiceTask getSetEntityCertificationServiceTask(
String subProcessId, CertificationConfiguration.CertificationEnum certification) { String subProcessId, CertificationConfiguration.CertificationEnum certification) {
FieldExtension certificationExpr = FieldExtension certificationExpr =
@ -75,5 +83,6 @@ public class SetEntityCertificationTask implements NodeInterface {
public void addToWorkflow(BpmnModel model, Process process) { public void addToWorkflow(BpmnModel model, Process process) {
process.addFlowElement(subProcess); process.addFlowElement(subProcess);
process.addFlowElement(runtimeExceptionBoundaryEvent);
} }
} }

View File

@ -2,6 +2,7 @@ package org.openmetadata.service.governance.workflows.elements.nodes.automatedTa
import static org.openmetadata.service.governance.workflows.Workflow.getFlowableElementId; import static org.openmetadata.service.governance.workflows.Workflow.getFlowableElementId;
import org.flowable.bpmn.model.BoundaryEvent;
import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent; import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.FieldExtension; import org.flowable.bpmn.model.FieldExtension;
@ -21,6 +22,7 @@ import org.openmetadata.service.governance.workflows.flowable.builders.SubProces
public class SetGlossaryTermStatusTask implements NodeInterface { public class SetGlossaryTermStatusTask implements NodeInterface {
private final SubProcess subProcess; private final SubProcess subProcess;
private final BoundaryEvent runtimeExceptionBoundaryEvent;
public SetGlossaryTermStatusTask(SetGlossaryTermStatusTaskDefinition nodeDefinition) { public SetGlossaryTermStatusTask(SetGlossaryTermStatusTaskDefinition nodeDefinition) {
String subProcessId = nodeDefinition.getName(); String subProcessId = nodeDefinition.getName();
@ -46,9 +48,15 @@ public class SetGlossaryTermStatusTask implements NodeInterface {
attachWorkflowInstanceStageListeners(subProcess); attachWorkflowInstanceStageListeners(subProcess);
this.runtimeExceptionBoundaryEvent = getRuntimeExceptionBoundaryEvent(subProcess);
this.subProcess = subProcess; this.subProcess = subProcess;
} }
@Override
public BoundaryEvent getRuntimeExceptionBoundaryEvent() {
return runtimeExceptionBoundaryEvent;
}
private ServiceTask getSetGlossaryTermStatusServiceTask(String subProcessId, String status) { private ServiceTask getSetGlossaryTermStatusServiceTask(String subProcessId, String status) {
FieldExtension statusExpr = FieldExtension statusExpr =
new FieldExtensionBuilder().fieldName("statusExpr").fieldValue(status).build(); new FieldExtensionBuilder().fieldName("statusExpr").fieldValue(status).build();
@ -64,5 +72,6 @@ public class SetGlossaryTermStatusTask implements NodeInterface {
public void addToWorkflow(BpmnModel model, Process process) { public void addToWorkflow(BpmnModel model, Process process) {
process.addFlowElement(subProcess); process.addFlowElement(subProcess);
process.addFlowElement(runtimeExceptionBoundaryEvent);
} }
} }

View File

@ -1,11 +1,16 @@
package org.openmetadata.service.governance.workflows.elements.nodes.automatedTask.impl; package org.openmetadata.service.governance.workflows.elements.nodes.automatedTask.impl;
import static org.openmetadata.service.governance.workflows.Workflow.EXCEPTION_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.RELATED_ENTITY_VARIABLE; import static org.openmetadata.service.governance.workflows.Workflow.RELATED_ENTITY_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.RESULT_VARIABLE; import static org.openmetadata.service.governance.workflows.Workflow.RESULT_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.WORKFLOW_RUNTIME_EXCEPTION;
import static org.openmetadata.service.governance.workflows.WorkflowHandler.getProcessDefinitionKeyFromId;
import io.github.jamsesso.jsonlogic.JsonLogic; import io.github.jamsesso.jsonlogic.JsonLogic;
import io.github.jamsesso.jsonlogic.JsonLogicException; import io.github.jamsesso.jsonlogic.JsonLogicException;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.delegate.Expression; import org.flowable.common.engine.api.delegate.Expression;
import org.flowable.engine.delegate.BpmnError;
import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate; import org.flowable.engine.delegate.JavaDelegate;
import org.openmetadata.schema.EntityInterface; import org.openmetadata.schema.EntityInterface;
@ -14,15 +19,25 @@ import org.openmetadata.service.Entity;
import org.openmetadata.service.resources.feeds.MessageParser; import org.openmetadata.service.resources.feeds.MessageParser;
import org.openmetadata.service.util.JsonUtils; import org.openmetadata.service.util.JsonUtils;
@Slf4j
public class CheckEntityAttributesImpl implements JavaDelegate { public class CheckEntityAttributesImpl implements JavaDelegate {
private Expression rulesExpr; private Expression rulesExpr;
@Override @Override
public void execute(DelegateExecution execution) { public void execute(DelegateExecution execution) {
try {
String rules = (String) rulesExpr.getValue(execution); String rules = (String) rulesExpr.getValue(execution);
MessageParser.EntityLink entityLink = MessageParser.EntityLink entityLink =
MessageParser.EntityLink.parse((String) execution.getVariable(RELATED_ENTITY_VARIABLE)); MessageParser.EntityLink.parse((String) execution.getVariable(RELATED_ENTITY_VARIABLE));
execution.setVariable(RESULT_VARIABLE, checkAttributes(entityLink, rules)); execution.setVariable(RESULT_VARIABLE, checkAttributes(entityLink, rules));
} catch (Exception exc) {
LOG.error(
String.format(
"[%s] Failure: ", getProcessDefinitionKeyFromId(execution.getProcessDefinitionId())),
exc);
execution.setVariable(EXCEPTION_VARIABLE, exc.toString());
throw new BpmnError(WORKFLOW_RUNTIME_EXCEPTION, exc.getMessage());
}
} }
private Boolean checkAttributes(MessageParser.EntityLink entityLink, String rules) { private Boolean checkAttributes(MessageParser.EntityLink entityLink, String rules) {

View File

@ -1,11 +1,16 @@
package org.openmetadata.service.governance.workflows.elements.nodes.automatedTask.impl; package org.openmetadata.service.governance.workflows.elements.nodes.automatedTask.impl;
import static org.openmetadata.service.governance.workflows.Workflow.EXCEPTION_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.RELATED_ENTITY_VARIABLE; import static org.openmetadata.service.governance.workflows.Workflow.RELATED_ENTITY_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.RESOLVED_BY_VARIABLE; import static org.openmetadata.service.governance.workflows.Workflow.RESOLVED_BY_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.WORKFLOW_RUNTIME_EXCEPTION;
import static org.openmetadata.service.governance.workflows.WorkflowHandler.getProcessDefinitionKeyFromId;
import java.util.Optional; import java.util.Optional;
import javax.json.JsonPatch; import javax.json.JsonPatch;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.delegate.Expression; import org.flowable.common.engine.api.delegate.Expression;
import org.flowable.engine.delegate.BpmnError;
import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate; import org.flowable.engine.delegate.JavaDelegate;
import org.openmetadata.schema.EntityInterface; import org.openmetadata.schema.EntityInterface;
@ -18,11 +23,13 @@ import org.openmetadata.service.resources.tags.TagLabelUtil;
import org.openmetadata.service.util.EntityUtil; import org.openmetadata.service.util.EntityUtil;
import org.openmetadata.service.util.JsonUtils; import org.openmetadata.service.util.JsonUtils;
@Slf4j
public class SetEntityCertificationImpl implements JavaDelegate { public class SetEntityCertificationImpl implements JavaDelegate {
private Expression certificationExpr; private Expression certificationExpr;
@Override @Override
public void execute(DelegateExecution execution) { public void execute(DelegateExecution execution) {
try {
MessageParser.EntityLink entityLink = MessageParser.EntityLink entityLink =
MessageParser.EntityLink.parse((String) execution.getVariable(RELATED_ENTITY_VARIABLE)); MessageParser.EntityLink.parse((String) execution.getVariable(RELATED_ENTITY_VARIABLE));
String entityType = entityLink.getEntityType(); String entityType = entityLink.getEntityType();
@ -37,6 +44,14 @@ public class SetEntityCertificationImpl implements JavaDelegate {
.orElse(entity.getUpdatedBy()); .orElse(entity.getUpdatedBy());
setStatus(entity, entityType, user, certification); setStatus(entity, entityType, user, certification);
} catch (Exception exc) {
LOG.error(
String.format(
"[%s] Failure: ", getProcessDefinitionKeyFromId(execution.getProcessDefinitionId())),
exc);
execution.setVariable(EXCEPTION_VARIABLE, exc.toString());
throw new BpmnError(WORKFLOW_RUNTIME_EXCEPTION, exc.getMessage());
}
} }
private void setStatus( private void setStatus(

View File

@ -1,12 +1,17 @@
package org.openmetadata.service.governance.workflows.elements.nodes.automatedTask.impl; package org.openmetadata.service.governance.workflows.elements.nodes.automatedTask.impl;
import static org.openmetadata.service.governance.workflows.Workflow.EXCEPTION_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.RELATED_ENTITY_VARIABLE; import static org.openmetadata.service.governance.workflows.Workflow.RELATED_ENTITY_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.RESOLVED_BY_VARIABLE; import static org.openmetadata.service.governance.workflows.Workflow.RESOLVED_BY_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.WORKFLOW_RUNTIME_EXCEPTION;
import static org.openmetadata.service.governance.workflows.WorkflowHandler.getProcessDefinitionKeyFromId;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import javax.json.JsonPatch; import javax.json.JsonPatch;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.delegate.Expression; import org.flowable.common.engine.api.delegate.Expression;
import org.flowable.engine.delegate.BpmnError;
import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate; import org.flowable.engine.delegate.JavaDelegate;
import org.openmetadata.schema.entity.data.GlossaryTerm; import org.openmetadata.schema.entity.data.GlossaryTerm;
@ -16,11 +21,13 @@ import org.openmetadata.service.jdbi3.GlossaryTermRepository;
import org.openmetadata.service.resources.feeds.MessageParser; import org.openmetadata.service.resources.feeds.MessageParser;
import org.openmetadata.service.util.JsonUtils; import org.openmetadata.service.util.JsonUtils;
@Slf4j
public class SetGlossaryTermStatusImpl implements JavaDelegate { public class SetGlossaryTermStatusImpl implements JavaDelegate {
private Expression statusExpr; private Expression statusExpr;
@Override @Override
public void execute(DelegateExecution execution) { public void execute(DelegateExecution execution) {
try {
MessageParser.EntityLink entityLink = MessageParser.EntityLink entityLink =
MessageParser.EntityLink.parse((String) execution.getVariable(RELATED_ENTITY_VARIABLE)); MessageParser.EntityLink.parse((String) execution.getVariable(RELATED_ENTITY_VARIABLE));
GlossaryTerm glossaryTerm = Entity.getEntity(entityLink, "*", Include.ALL); GlossaryTerm glossaryTerm = Entity.getEntity(entityLink, "*", Include.ALL);
@ -31,6 +38,14 @@ public class SetGlossaryTermStatusImpl implements JavaDelegate {
.orElse(glossaryTerm.getUpdatedBy()); .orElse(glossaryTerm.getUpdatedBy());
setStatus(glossaryTerm, user, status); setStatus(glossaryTerm, user, status);
} catch (Exception exc) {
LOG.error(
String.format(
"[%s] Failure: ", getProcessDefinitionKeyFromId(execution.getProcessDefinitionId())),
exc);
execution.setVariable(EXCEPTION_VARIABLE, exc.toString());
throw new BpmnError(WORKFLOW_RUNTIME_EXCEPTION, exc.getMessage());
}
} }
private void setStatus(GlossaryTerm glossaryTerm, String user, String status) { private void setStatus(GlossaryTerm glossaryTerm, String user, String status) {

View File

@ -1,14 +1,21 @@
package org.openmetadata.service.governance.workflows.elements.nodes.endEvent; package org.openmetadata.service.governance.workflows.elements.nodes.endEvent;
import lombok.Getter;
import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.Process;
import org.openmetadata.schema.governance.workflows.elements.nodes.endEvent.EndEventDefinition; import org.openmetadata.schema.governance.workflows.elements.nodes.endEvent.EndEventDefinition;
import org.openmetadata.service.governance.workflows.elements.NodeInterface; import org.openmetadata.service.governance.workflows.elements.NodeInterface;
import org.openmetadata.service.governance.workflows.flowable.builders.EndEventBuilder; import org.openmetadata.service.governance.workflows.flowable.builders.EndEventBuilder;
@Getter
public class EndEvent implements NodeInterface { public class EndEvent implements NodeInterface {
private final org.flowable.bpmn.model.EndEvent endEvent; private final org.flowable.bpmn.model.EndEvent endEvent;
public EndEvent(String id) {
this.endEvent = new EndEventBuilder().id(id).build();
attachWorkflowInstanceStageListeners(endEvent);
}
public EndEvent(EndEventDefinition nodeDefinition) { public EndEvent(EndEventDefinition nodeDefinition) {
this.endEvent = new EndEventBuilder().id(nodeDefinition.getName()).build(); this.endEvent = new EndEventBuilder().id(nodeDefinition.getName()).build();
attachWorkflowInstanceStageListeners(endEvent); attachWorkflowInstanceStageListeners(endEvent);

View File

@ -34,6 +34,7 @@ import org.openmetadata.service.util.JsonUtils;
public class UserApprovalTask implements NodeInterface { public class UserApprovalTask implements NodeInterface {
private final SubProcess subProcess; private final SubProcess subProcess;
private final BoundaryEvent runtimeExceptionBoundaryEvent;
private final List<Message> messages = new ArrayList<>(); private final List<Message> messages = new ArrayList<>();
public UserApprovalTask(UserApprovalTaskDefinition nodeDefinition) { public UserApprovalTask(UserApprovalTaskDefinition nodeDefinition) {
@ -92,9 +93,15 @@ public class UserApprovalTask implements NodeInterface {
attachWorkflowInstanceStageListeners(subProcess); attachWorkflowInstanceStageListeners(subProcess);
this.runtimeExceptionBoundaryEvent = getRuntimeExceptionBoundaryEvent(subProcess);
this.subProcess = subProcess; this.subProcess = subProcess;
} }
@Override
public BoundaryEvent getRuntimeExceptionBoundaryEvent() {
return runtimeExceptionBoundaryEvent;
}
private ServiceTask getSetAssigneesVariableServiceTask( private ServiceTask getSetAssigneesVariableServiceTask(
String subProcessId, FieldExtension assigneesExpr, FieldExtension assigneesVarNameExpr) { String subProcessId, FieldExtension assigneesExpr, FieldExtension assigneesVarNameExpr) {
ServiceTask serviceTask = ServiceTask serviceTask =
@ -146,6 +153,7 @@ public class UserApprovalTask implements NodeInterface {
public void addToWorkflow(BpmnModel model, Process process) { public void addToWorkflow(BpmnModel model, Process process) {
process.addFlowElement(subProcess); process.addFlowElement(subProcess);
process.addFlowElement(runtimeExceptionBoundaryEvent);
for (Message message : messages) { for (Message message : messages) {
model.addMessage(message); model.addMessage(message);
} }

View File

@ -1,12 +1,17 @@
package org.openmetadata.service.governance.workflows.elements.nodes.userTask.impl; package org.openmetadata.service.governance.workflows.elements.nodes.userTask.impl;
import static org.openmetadata.service.governance.workflows.Workflow.EXCEPTION_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.RELATED_ENTITY_VARIABLE; import static org.openmetadata.service.governance.workflows.Workflow.RELATED_ENTITY_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.STAGE_INSTANCE_STATE_ID_VARIABLE; import static org.openmetadata.service.governance.workflows.Workflow.STAGE_INSTANCE_STATE_ID_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.WORKFLOW_RUNTIME_EXCEPTION;
import static org.openmetadata.service.governance.workflows.WorkflowHandler.getProcessDefinitionKeyFromId;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.BpmnError;
import org.flowable.engine.delegate.TaskListener; import org.flowable.engine.delegate.TaskListener;
import org.flowable.identitylink.api.IdentityLink; import org.flowable.identitylink.api.IdentityLink;
import org.flowable.task.service.delegate.DelegateTask; import org.flowable.task.service.delegate.DelegateTask;
@ -27,12 +32,15 @@ import org.openmetadata.service.resources.feeds.FeedResource;
import org.openmetadata.service.resources.feeds.MessageParser; import org.openmetadata.service.resources.feeds.MessageParser;
import org.openmetadata.service.util.WebsocketNotificationHandler; import org.openmetadata.service.util.WebsocketNotificationHandler;
@Slf4j
public class CreateApprovalTaskImpl implements TaskListener { public class CreateApprovalTaskImpl implements TaskListener {
@Override @Override
public void notify(DelegateTask delegateTask) { public void notify(DelegateTask delegateTask) {
try {
List<EntityReference> assignees = getAssignees(delegateTask); List<EntityReference> assignees = getAssignees(delegateTask);
MessageParser.EntityLink entityLink = MessageParser.EntityLink entityLink =
MessageParser.EntityLink.parse((String) delegateTask.getVariable(RELATED_ENTITY_VARIABLE)); MessageParser.EntityLink.parse(
(String) delegateTask.getVariable(RELATED_ENTITY_VARIABLE));
GlossaryTerm entity = Entity.getEntity(entityLink, "*", Include.ALL); GlossaryTerm entity = Entity.getEntity(entityLink, "*", Include.ALL);
Thread task = createApprovalTask(entity, assignees); Thread task = createApprovalTask(entity, assignees);
@ -44,6 +52,15 @@ public class CreateApprovalTaskImpl implements TaskListener {
(WorkflowInstanceStateRepository) (WorkflowInstanceStateRepository)
Entity.getEntityTimeSeriesRepository(Entity.WORKFLOW_INSTANCE_STATE); Entity.getEntityTimeSeriesRepository(Entity.WORKFLOW_INSTANCE_STATE);
workflowInstanceStateRepository.updateStageWithTask(task.getId(), workflowInstanceStateId); workflowInstanceStateRepository.updateStageWithTask(task.getId(), workflowInstanceStateId);
} catch (Exception exc) {
LOG.error(
String.format(
"[%s] Failure: ",
getProcessDefinitionKeyFromId(delegateTask.getProcessDefinitionId())),
exc);
delegateTask.setVariable(EXCEPTION_VARIABLE, exc.toString());
throw new BpmnError(WORKFLOW_RUNTIME_EXCEPTION, exc.getMessage());
}
} }
private List<EntityReference> getAssignees(DelegateTask delegateTask) { private List<EntityReference> getAssignees(DelegateTask delegateTask) {

View File

@ -1,12 +1,17 @@
package org.openmetadata.service.governance.workflows.elements.nodes.userTask.impl; package org.openmetadata.service.governance.workflows.elements.nodes.userTask.impl;
import static org.openmetadata.service.governance.workflows.Workflow.EXCEPTION_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.RELATED_ENTITY_VARIABLE; import static org.openmetadata.service.governance.workflows.Workflow.RELATED_ENTITY_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.WORKFLOW_RUNTIME_EXCEPTION;
import static org.openmetadata.service.governance.workflows.WorkflowHandler.getProcessDefinitionKeyFromId;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.delegate.Expression; import org.flowable.common.engine.api.delegate.Expression;
import org.flowable.engine.delegate.BpmnError;
import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate; import org.flowable.engine.delegate.JavaDelegate;
import org.openmetadata.schema.EntityInterface; import org.openmetadata.schema.EntityInterface;
@ -16,12 +21,14 @@ import org.openmetadata.service.Entity;
import org.openmetadata.service.resources.feeds.MessageParser; import org.openmetadata.service.resources.feeds.MessageParser;
import org.openmetadata.service.util.JsonUtils; import org.openmetadata.service.util.JsonUtils;
@Slf4j
public class SetApprovalAssigneesImpl implements JavaDelegate { public class SetApprovalAssigneesImpl implements JavaDelegate {
private Expression assigneesExpr; private Expression assigneesExpr;
private Expression assigneesVarNameExpr; private Expression assigneesVarNameExpr;
@Override @Override
public void execute(DelegateExecution execution) { public void execute(DelegateExecution execution) {
try {
Map<String, Object> assigneesConfig = Map<String, Object> assigneesConfig =
JsonUtils.readOrConvertValue(assigneesExpr.getValue(execution), Map.class); JsonUtils.readOrConvertValue(assigneesExpr.getValue(execution), Map.class);
Boolean addReviewers = (Boolean) assigneesConfig.get("addReviewers"); Boolean addReviewers = (Boolean) assigneesConfig.get("addReviewers");
@ -39,10 +46,19 @@ public class SetApprovalAssigneesImpl implements JavaDelegate {
} }
oExtraAssignees.ifPresent( oExtraAssignees.ifPresent(
extraAssignees -> assignees.addAll(getEntityLinkStringFromEntityReference(extraAssignees))); extraAssignees ->
assignees.addAll(getEntityLinkStringFromEntityReference(extraAssignees)));
execution.setVariableLocal( execution.setVariableLocal(
assigneesVarNameExpr.getValue(execution).toString(), JsonUtils.pojoToJson(assignees)); assigneesVarNameExpr.getValue(execution).toString(), JsonUtils.pojoToJson(assignees));
} catch (Exception exc) {
LOG.error(
String.format(
"[%s] Failure: ", getProcessDefinitionKeyFromId(execution.getProcessDefinitionId())),
exc);
execution.setVariable(EXCEPTION_VARIABLE, exc.toString());
throw new BpmnError(WORKFLOW_RUNTIME_EXCEPTION, exc.getMessage());
}
} }
private List<String> getEntityLinkStringFromEntityReference(List<EntityReference> assignees) { private List<String> getEntityLinkStringFromEntityReference(List<EntityReference> assignees) {

View File

@ -1,20 +1,37 @@
package org.openmetadata.service.governance.workflows.elements.nodes.userTask.impl; package org.openmetadata.service.governance.workflows.elements.nodes.userTask.impl;
import static org.openmetadata.service.governance.workflows.Workflow.EXCEPTION_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.WORKFLOW_RUNTIME_EXCEPTION;
import static org.openmetadata.service.governance.workflows.WorkflowHandler.getProcessDefinitionKeyFromId;
import java.util.List; import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.delegate.Expression; import org.flowable.common.engine.api.delegate.Expression;
import org.flowable.engine.delegate.BpmnError;
import org.flowable.engine.delegate.TaskListener; import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.delegate.DelegateTask; import org.flowable.task.service.delegate.DelegateTask;
import org.openmetadata.service.util.JsonUtils; import org.openmetadata.service.util.JsonUtils;
@Slf4j
public class SetCandidateUsersImpl implements TaskListener { public class SetCandidateUsersImpl implements TaskListener {
private Expression assigneesVarNameExpr; private Expression assigneesVarNameExpr;
@Override @Override
public void notify(DelegateTask delegateTask) { public void notify(DelegateTask delegateTask) {
try {
List<String> assignees = List<String> assignees =
JsonUtils.readOrConvertValue( JsonUtils.readOrConvertValue(
delegateTask.getVariable(assigneesVarNameExpr.getValue(delegateTask).toString()), delegateTask.getVariable(assigneesVarNameExpr.getValue(delegateTask).toString()),
List.class); List.class);
delegateTask.addCandidateUsers(assignees); delegateTask.addCandidateUsers(assignees);
} catch (Exception exc) {
LOG.error(
String.format(
"[%s] Failure: ",
getProcessDefinitionKeyFromId(delegateTask.getProcessDefinitionId())),
exc);
delegateTask.setVariable(EXCEPTION_VARIABLE, exc.toString());
throw new BpmnError(WORKFLOW_RUNTIME_EXCEPTION, exc.getMessage());
}
} }
} }

View File

@ -1,15 +1,19 @@
package org.openmetadata.service.governance.workflows.elements.triggers; package org.openmetadata.service.governance.workflows.elements.triggers;
import static org.openmetadata.service.governance.workflows.Workflow.EXCEPTION_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.RELATED_ENTITY_VARIABLE; import static org.openmetadata.service.governance.workflows.Workflow.RELATED_ENTITY_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.WORKFLOW_RUNTIME_EXCEPTION;
import static org.openmetadata.service.governance.workflows.Workflow.getFlowableElementId; import static org.openmetadata.service.governance.workflows.Workflow.getFlowableElementId;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import lombok.Getter; import lombok.Getter;
import org.flowable.bpmn.model.BoundaryEvent;
import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.CallActivity; import org.flowable.bpmn.model.CallActivity;
import org.flowable.bpmn.model.EndEvent; import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.ErrorEventDefinition;
import org.flowable.bpmn.model.FieldExtension; import org.flowable.bpmn.model.FieldExtension;
import org.flowable.bpmn.model.IOParameter; import org.flowable.bpmn.model.IOParameter;
import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.Process;
@ -55,6 +59,21 @@ public class EventBasedEntityTrigger implements TriggerInterface {
CallActivity workflowTrigger = getWorkflowTrigger(triggerWorkflowId, mainWorkflowName); CallActivity workflowTrigger = getWorkflowTrigger(triggerWorkflowId, mainWorkflowName);
process.addFlowElement(workflowTrigger); process.addFlowElement(workflowTrigger);
ErrorEventDefinition runtimeExceptionDefinition = new ErrorEventDefinition();
runtimeExceptionDefinition.setErrorCode(WORKFLOW_RUNTIME_EXCEPTION);
BoundaryEvent runtimeExceptionBoundaryEvent = new BoundaryEvent();
runtimeExceptionBoundaryEvent.setId(
getFlowableElementId(workflowTrigger.getId(), "runtimeExceptionBoundaryEvent"));
runtimeExceptionBoundaryEvent.addEventDefinition(runtimeExceptionDefinition);
runtimeExceptionBoundaryEvent.setAttachedToRef(workflowTrigger);
process.addFlowElement(runtimeExceptionBoundaryEvent);
EndEvent errorEndEvent =
new EndEventBuilder().id(getFlowableElementId(triggerWorkflowId, "errorEndEvent")).build();
process.addFlowElement(errorEndEvent);
EndEvent endEvent = EndEvent endEvent =
new EndEventBuilder().id(getFlowableElementId(triggerWorkflowId, "endEvent")).build(); new EndEventBuilder().id(getFlowableElementId(triggerWorkflowId, "endEvent")).build();
process.addFlowElement(endEvent); process.addFlowElement(endEvent);
@ -77,6 +96,8 @@ public class EventBasedEntityTrigger implements TriggerInterface {
process.addFlowElement(filterNotPassed); process.addFlowElement(filterNotPassed);
// WorkflowTrigger -> End // WorkflowTrigger -> End
process.addFlowElement(new SequenceFlow(workflowTrigger.getId(), endEvent.getId())); process.addFlowElement(new SequenceFlow(workflowTrigger.getId(), endEvent.getId()));
process.addFlowElement(
new SequenceFlow(runtimeExceptionBoundaryEvent.getId(), errorEndEvent.getId()));
this.process = process; this.process = process;
this.triggerWorkflowId = triggerWorkflowId; this.triggerWorkflowId = triggerWorkflowId;
@ -126,7 +147,12 @@ public class EventBasedEntityTrigger implements TriggerInterface {
inputParameter.setSource(RELATED_ENTITY_VARIABLE); inputParameter.setSource(RELATED_ENTITY_VARIABLE);
inputParameter.setTarget(RELATED_ENTITY_VARIABLE); inputParameter.setTarget(RELATED_ENTITY_VARIABLE);
IOParameter outputParameter = new IOParameter();
outputParameter.setSource(EXCEPTION_VARIABLE);
outputParameter.setTarget(EXCEPTION_VARIABLE);
workflowTrigger.setInParameters(List.of(inputParameter)); workflowTrigger.setInParameters(List.of(inputParameter));
workflowTrigger.setOutParameters(List.of(outputParameter));
return workflowTrigger; return workflowTrigger;
} }

View File

@ -1,5 +1,6 @@
package org.openmetadata.service.governance.workflows.elements.triggers; package org.openmetadata.service.governance.workflows.elements.triggers;
import static org.openmetadata.service.governance.workflows.Workflow.EXCEPTION_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.RELATED_ENTITY_VARIABLE; import static org.openmetadata.service.governance.workflows.Workflow.RELATED_ENTITY_VARIABLE;
import static org.openmetadata.service.governance.workflows.Workflow.getFlowableElementId; import static org.openmetadata.service.governance.workflows.Workflow.getFlowableElementId;
@ -121,7 +122,12 @@ public class PeriodicBatchEntityTrigger implements TriggerInterface {
inputParameter.setSource(RELATED_ENTITY_VARIABLE); inputParameter.setSource(RELATED_ENTITY_VARIABLE);
inputParameter.setTarget(RELATED_ENTITY_VARIABLE); inputParameter.setTarget(RELATED_ENTITY_VARIABLE);
IOParameter outputParameter = new IOParameter();
outputParameter.setSource(EXCEPTION_VARIABLE);
outputParameter.setTarget(EXCEPTION_VARIABLE);
workflowTrigger.setInParameters(List.of(inputParameter)); workflowTrigger.setInParameters(List.of(inputParameter));
workflowTrigger.setOutParameters(List.of(outputParameter));
workflowTrigger.setLoopCharacteristics(multiInstance); workflowTrigger.setLoopCharacteristics(multiInstance);
return workflowTrigger; return workflowTrigger;

View File

@ -1,22 +1,28 @@
package org.openmetadata.service.governance.workflows.flowable; package org.openmetadata.service.governance.workflows.flowable;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import lombok.Getter; import lombok.Getter;
import org.flowable.bpmn.model.BoundaryEvent;
import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.SequenceFlow;
import org.openmetadata.schema.governance.workflows.WorkflowDefinition; import org.openmetadata.schema.governance.workflows.WorkflowDefinition;
import org.openmetadata.schema.governance.workflows.elements.EdgeDefinition; import org.openmetadata.schema.governance.workflows.elements.EdgeDefinition;
import org.openmetadata.schema.governance.workflows.elements.WorkflowNodeDefinitionInterface; import org.openmetadata.schema.governance.workflows.elements.WorkflowNodeDefinitionInterface;
import org.openmetadata.service.governance.workflows.elements.Edge; import org.openmetadata.service.governance.workflows.elements.Edge;
import org.openmetadata.service.governance.workflows.elements.NodeFactory; import org.openmetadata.service.governance.workflows.elements.NodeFactory;
import org.openmetadata.service.governance.workflows.elements.NodeInterface;
import org.openmetadata.service.governance.workflows.elements.nodes.endEvent.EndEvent;
import org.openmetadata.service.util.JsonUtils; import org.openmetadata.service.util.JsonUtils;
@Getter @Getter
public class MainWorkflow { public class MainWorkflow {
private final BpmnModel model; private final BpmnModel model;
private final String workflowName; private final String workflowName;
private final List<BoundaryEvent> runtimeExceptionBoundaryEvents = new ArrayList<>();
public MainWorkflow(WorkflowDefinition workflowDefinition) { public MainWorkflow(WorkflowDefinition workflowDefinition) {
BpmnModel model = new BpmnModel(); BpmnModel model = new BpmnModel();
@ -33,8 +39,12 @@ public class MainWorkflow {
// Add Nodes // Add Nodes
for (Object nodeDefinitionObj : for (Object nodeDefinitionObj :
(List<WorkflowNodeDefinitionInterface>) workflowDefinition.getNodes()) { (List<WorkflowNodeDefinitionInterface>) workflowDefinition.getNodes()) {
NodeFactory.createNode(JsonUtils.readOrConvertValue(nodeDefinitionObj, Map.class)) NodeInterface node =
.addToWorkflow(model, process); NodeFactory.createNode(JsonUtils.readOrConvertValue(nodeDefinitionObj, Map.class));
node.addToWorkflow(model, process);
Optional.ofNullable(node.getRuntimeExceptionBoundaryEvent())
.ifPresent(runtimeExceptionBoundaryEvents::add);
} }
// Add Edges // Add Edges
@ -43,7 +53,18 @@ public class MainWorkflow {
edge.addToWorkflow(model, process); edge.addToWorkflow(model, process);
} }
// Configure Exception Flow
configureRuntimeExceptionFlow(process);
this.model = model; this.model = model;
this.workflowName = workflowName; this.workflowName = workflowName;
} }
private void configureRuntimeExceptionFlow(Process process) {
EndEvent errorEndEvent = new EndEvent("Error");
process.addFlowElement(errorEndEvent.getEndEvent());
for (BoundaryEvent event : runtimeExceptionBoundaryEvents) {
process.addFlowElement(new SequenceFlow(event.getId(), errorEndEvent.getEndEvent().getId()));
}
}
} }

View File

@ -197,6 +197,9 @@ public class FeedRepository {
if (repository.supportsTags) { if (repository.supportsTags) {
fieldList.add("tags"); fieldList.add("tags");
} }
if (repository.supportsReviewers) {
fieldList.add("reviewers");
}
return String.join(",", fieldList.toArray(new String[0])); return String.join(",", fieldList.toArray(new String[0]));
} }
} }

View File

@ -598,6 +598,9 @@ public class GlossaryTermRepository extends EntityRepository<GlossaryTerm> {
@Override @Override
public EntityInterface performTask(String user, ResolveTask resolveTask) { public EntityInterface performTask(String user, ResolveTask resolveTask) {
// TODO: Resolve this outside // TODO: Resolve this outside
GlossaryTerm glossaryTerm = (GlossaryTerm) threadContext.getAboutEntity();
checkUpdatedByReviewer(glossaryTerm, user);
UUID taskId = threadContext.getThread().getId(); UUID taskId = threadContext.getThread().getId();
Map<String, Object> variables = new HashMap<>(); Map<String, Object> variables = new HashMap<>();
variables.put(RESULT_VARIABLE, resolveTask.getNewValue().equalsIgnoreCase("approved")); variables.put(RESULT_VARIABLE, resolveTask.getNewValue().equalsIgnoreCase("approved"));
@ -607,7 +610,6 @@ public class GlossaryTermRepository extends EntityRepository<GlossaryTerm> {
// TODO: performTask returns the updated Entity and the flow applies the new value. // TODO: performTask returns the updated Entity and the flow applies the new value.
// This should be changed with the new Governance Workflows. // This should be changed with the new Governance Workflows.
GlossaryTerm glossaryTerm = (GlossaryTerm) threadContext.getAboutEntity();
// glossaryTerm.setStatus(Status.APPROVED); // glossaryTerm.setStatus(Status.APPROVED);
return glossaryTerm; return glossaryTerm;
} }
@ -652,7 +654,7 @@ public class GlossaryTermRepository extends EntityRepository<GlossaryTerm> {
} }
} }
private void checkUpdatedByReviewer(GlossaryTerm term, String updatedBy) { public static void checkUpdatedByReviewer(GlossaryTerm term, String updatedBy) {
// Only list of allowed reviewers can change the status from DRAFT to APPROVED // Only list of allowed reviewers can change the status from DRAFT to APPROVED
List<EntityReference> reviewers = term.getReviewers(); List<EntityReference> reviewers = term.getReviewers();
if (!nullOrEmpty(reviewers)) { if (!nullOrEmpty(reviewers)) {

View File

@ -1,5 +1,7 @@
package org.openmetadata.service.jdbi3; package org.openmetadata.service.jdbi3;
import static org.openmetadata.service.governance.workflows.Workflow.EXCEPTION_VARIABLE;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import org.openmetadata.schema.governance.workflows.WorkflowInstance; import org.openmetadata.schema.governance.workflows.WorkflowInstance;
@ -41,12 +43,17 @@ public class WorkflowInstanceRepository extends EntityTimeSeriesRepository<Workf
workflowDefinitionName); workflowDefinitionName);
} }
public void updateWorkflowInstance(UUID workflowInstanceId, Long endedAt) { public void updateWorkflowInstance(
UUID workflowInstanceId, Long endedAt, Map<String, Object> variables) {
WorkflowInstance workflowInstance = WorkflowInstance workflowInstance =
JsonUtils.readValue(timeSeriesDao.getById(workflowInstanceId), WorkflowInstance.class); JsonUtils.readValue(timeSeriesDao.getById(workflowInstanceId), WorkflowInstance.class);
workflowInstance.setEndedAt(endedAt); workflowInstance.setEndedAt(endedAt);
if (variables.containsKey(EXCEPTION_VARIABLE)) {
workflowInstance.setException(true);
}
getTimeSeriesDao().update(JsonUtils.pojoToJson(workflowInstance), workflowInstanceId); getTimeSeriesDao().update(JsonUtils.pojoToJson(workflowInstance), workflowInstanceId);
} }
} }

View File

@ -1,5 +1,7 @@
package org.openmetadata.service.jdbi3; package org.openmetadata.service.jdbi3;
import static org.openmetadata.service.governance.workflows.Workflow.EXCEPTION_VARIABLE;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import org.openmetadata.schema.governance.workflows.Stage; import org.openmetadata.schema.governance.workflows.Stage;
@ -61,6 +63,10 @@ public class WorkflowInstanceStateRepository
stage.setEndedAt(endedAt); stage.setEndedAt(endedAt);
stage.setVariables(variables); stage.setVariables(variables);
if (variables.containsKey(EXCEPTION_VARIABLE)) {
workflowInstanceState.setException(true);
}
workflowInstanceState.setStage(stage); workflowInstanceState.setStage(stage);
getTimeSeriesDao().update(JsonUtils.pojoToJson(workflowInstanceState), workflowInstanceStateId); getTimeSeriesDao().update(JsonUtils.pojoToJson(workflowInstanceState), workflowInstanceStateId);

View File

@ -30,6 +30,10 @@
"timestamp": { "timestamp": {
"description": "Timestamp on which the workflow instance state was created.", "description": "Timestamp on which the workflow instance state was created.",
"$ref": "../../type/basic.json#/definitions/timestamp" "$ref": "../../type/basic.json#/definitions/timestamp"
},
"exception": {
"description": "If the Workflow Instance has errors, 'True'. Else, 'False'.",
"type": "boolean"
} }
}, },
"required": [], "required": [],

View File

@ -54,6 +54,10 @@
"timestamp": { "timestamp": {
"description": "Timestamp on which the workflow instance state was created.", "description": "Timestamp on which the workflow instance state was created.",
"$ref": "../../type/basic.json#/definitions/timestamp" "$ref": "../../type/basic.json#/definitions/timestamp"
},
"exception": {
"description": "If the Workflow Instance has errors, 'True'. Else, 'False'.",
"type": "boolean"
} }
}, },
"required": [], "required": [],