Preventing Relationship Deletion Until Workflow Completion in Collibra
Summary: miguelangel_lago_higos, Miguel Angel Lago Higos, is experiencing an issue with a workflow related to the deletion of relationships in Collibra's Data Product asset. The challenge is to prevent certain relationships from being deleted until the workflow completes successfully. Sean Pyle, Sean Pyle, suggested trying to capture relation details immediately after obtaining the resource ID, but this approach failed as the relation was not found. pareshmaharana1, Paresh Maharana, proposed a workflow that checks specific conditions before permitting the deletion and includes a rollback mechanism if the workflow ends in error.
Hi everyone,
I'm facing an issue with a workflow triggered after a relationship is deleted from a Data Product asset. The goal of the workflow is to prevent certain relationships from being removed.
The workflow successfully captures the ID of the deleted relationship (eventResourceId). However, when I try to retrieve the type of the deleted relationship using that ID into findRelations, it no longer exists in the system — presumably because the deletion has already been finalized.
My question is:
Is there a way to delay the actual deletion of the relationship until the workflow completes successfully? Ideally, the relationship should only be removed if the workflow ends without errors or explicitly allows it.
Any suggestions or best practices to handle this kind of validation or rollback mechanism would be greatly appreciated.
Thanks in advance!
Paresh Maharana
·11 months ago · EditedHi @miguelangel_lago_higos
We had created a similar workflow in the past you can check this
Make sure you are having a event trigger to start the workflow when a relation is removed and put over the rules Asset type == Data Product
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:design="http://flowable.org/design" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.collibra.com/apiv2" design:palette="flowable-core-process-palette"> <collaboration id="Collaboration"> <participant id="pool1" name="Preserving Data Set Relationship" processRef="preserveDataSetRelationships"></participant> </collaboration> <process id="preserveDataSetRelationships" name="Preserve Data Set Relationships" isExecutable="true" flowable:candidateStarterGroups="flowableUser"> <extensionElements> <design:stencilid><![CDATA[BPMNDiagram]]></design:stencilid> <design:creationdate><![CDATA[2024-12-27T07:48:50.365Z]]></design:creationdate> <design:modificationdate><![CDATA[2025-06-23T12:53:35.412Z]]></design:modificationdate> </extensionElements> <laneSet id="laneSet_preserveDataSetRelationships"> <lane id="lane1"> <flowNodeRef>startnoneevent1</flowNodeRef> <flowNodeRef>scriptTask1</flowNodeRef> <flowNodeRef>endTerminateEvent1</flowNodeRef> <flowNodeRef>sequenceFlow1</flowNodeRef> <flowNodeRef>sequenceFlow2</flowNodeRef> </lane> </laneSet> <startEvent id="startnoneevent1" flowable:initiator="initiator" flowable:formFieldValidation="false"> <extensionElements> <flowable:formProperty id="ignoreStatuses" name="List of Statues to be Ignored (Comma separated values)" type="string" default="Rejected,Obsolete,Candidate" readable="false"></flowable:formProperty> <flowable:work-form-field-validation><![CDATA[false]]></flowable:work-form-field-validation> <design:stencilid><![CDATA[StartNoneEvent]]></design:stencilid> <design:workforminputmappingtype><![CDATA[allowed]]></design:workforminputmappingtype> </extensionElements> </startEvent> <scriptTask id="scriptTask1" name="Check Relationship" scriptFormat="groovy" flowable:autoStoreVariables="false"> <extensionElements> <design:stencilid><![CDATA[ScriptTask]]></design:stencilid> </extensionElements> <script><![CDATA[import com.collibra.dgc.workflow.api.exception.WorkflowException import com.collibra.dgc.core.api.model.activitystream.ActivityType import groovy.json.JsonSlurper import groovy.json.JsonOutput def relationId = event.getEventResourceId() def assetId = item.getId() def ignoreStatusList = execution.getVariable("ignoreStatuses").toString().split(',').collect{ it.trim() } // Fetch all the activities related to the asset activityStream = activityStreamApi.getActivities( builders.get("FindActivitiesRequest") .contextId(assetId) .startDate(System.currentTimeMillis()-5000) .build() ).getResults() // Filter the activities to find the ones that match the relationId filteredActivities = activityStream.findAll { activity -> def descriptionJson = new JsonSlurper().parseText(activity.getDescription()) descriptionJson.affected?.id.toString() == relationId.toString() } loggerApi.info("relationId: ${relationId}") loggerApi.info("filteredActivities: ${filteredActivities}") // If there are activities that match the relationId, fetch the description if (filteredActivities.size() > 0) { descriptionString = filteredActivities[0].getDescription() descriptionObject = new JsonSlurper().parseText(descriptionString) // Fetch the role, coRole, datasetId from the description role = descriptionObject.role coRole = descriptionObject.coRole datasetId = descriptionObject.target.id loggerApi.info("role: ${role}, coRole: ${coRole}, datasetId: ${datasetId}") // Fetch the Dataset status dataset = assetApi.getAsset(UUID.fromString(datasetId)) datasetStatus = dataset.getStatus().getName() loggerApi.info("datasetStatus: ${datasetStatus}") /* If the following conditions are satisfied, preserve the relation: 1. Dataset status is NOT rejected 2. role = "contains" 3. coRole = "is part of" */ if ( !(ignoreStatusList.contains(datasetStatus)) && role == "contains" && coRole == "is part of"){ dgcError = new WorkflowException("The data product linkage cannot be removed for this dataset. Dataset name: ${dataset.getDisplayName()}, Dataset id: ${datasetId}") dgcError.setTitleMessage("Error: Relation cannot be removed") throw dgcError } }]]></script> </scriptTask> <endEvent id="endTerminateEvent1"> <extensionElements> <design:stencilid><![CDATA[EndTerminateEvent]]></design:stencilid> </extensionElements> <terminateEventDefinition flowable:terminateAll="true"></terminateEventDefinition> </endEvent> <sequenceFlow id="sequenceFlow1" sourceRef="startnoneevent1" targetRef="scriptTask1"> <extensionElements> <design:stencilid><![CDATA[SequenceFlow]]></design:stencilid> </extensionElements> </sequenceFlow> <sequenceFlow id="sequenceFlow2" sourceRef="scriptTask1" targetRef="endTerminateEvent1"> <extensionElements> <design:stencilid><![CDATA[SequenceFlow]]></design:stencilid> </extensionElements> </sequenceFlow> </process> <bpmndi:BPMNDiagram id="BPMNDiagram_Collaboration"> <bpmndi:BPMNPlane bpmnElement="Collaboration" id="BPMNPlane_Collaboration"> <bpmndi:BPMNShape bpmnElement="pool1" id="BPMNShape_pool1"> <omgdc:Bounds height="250.0" width="600.0" x="30.0" y="165.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="lane1" id="BPMNShape_lane1"> <omgdc:Bounds height="250.0" width="570.0" x="60.0" y="165.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="startnoneevent1" id="BPMNShape_startnoneevent1"> <omgdc:Bounds height="30.0" width="30.0" x="105.0" y="275.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="scriptTask1" id="BPMNShape_scriptTask1"> <omgdc:Bounds height="80.0" width="100.0" x="255.0" y="250.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="endTerminateEvent1" id="BPMNShape_endTerminateEvent1"> <omgdc:Bounds height="28.0" width="28.0" x="450.0" y="276.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNEdge bpmnElement="sequenceFlow1" id="BPMNEdge_sequenceFlow1" flowable:sourceDockerX="15.0" flowable:sourceDockerY="15.0" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0"> <omgdi:waypoint x="134.94999946593475" y="290.0"></omgdi:waypoint> <omgdi:waypoint x="254.9999999999298" y="290.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="sequenceFlow2" id="BPMNEdge_sequenceFlow2" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="14.0" flowable:targetDockerY="14.0"> <omgdi:waypoint x="354.95000000000005" y="290.0"></omgdi:waypoint> <omgdi:waypoint x="450.0" y="290.0"></omgdi:waypoint> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </definitions>Sean Pyle
·11 months agoNot sure if there is a way to delay the deletion.
However, maybe you can try getRelation( <eventResourceId you captured>) and chaining the method right when you get the resource ID:
UUID lostRelation = relationApi.getRelation(event.getEventResourceId())
Its a long shot, but maybe you will be able to at least keep the details of the relation and recreate it with a subsequent script.
Miguel Angel Lago Higos
OP11 months agoHi,
Thanks Sean!
I have tried but it doesn't work. Anyway there is a really curious behavior. If I use getRelation with the value eventResourceId an error is returned "relationNotFound" and the worlflow fails... but at the end the relation is not deleted. It means that some kind of rollback is executed.
It seems that the problem is that methods getRelation or findRelations can't access to the information during the process but if the workflow ended with errors the delete operation is rollbacked. After this error, and via swagger the uuid return the value.
Regards,