Reltio Connect

 View Only
  • 1.  Trigger workflow on a particular attribute change

    Posted 15 days ago

    Hi people, 

    We are working on a Reltio workflow that should trigger only when a specific attribute (SCH_Organization_Classification_Level_3) of an entity is modified, the attribute is a nested attribute since we have the Organization Classification which then has 3 levels inside. However, we are facing an issue where the workflow gets triggered on any change to the entity, rather than just the relevant attribute change.

    For what I originally understood in how I should implement the classes I have:

    • Validator: Ensures the Org Classification Level 3 is modified with valid transitions (e.g., from/to "Academic Health System" or "Academic Medical Center").

    • Listener: Assigns tasks to ROLE_AMC_REVIEWERS when the conditions validated above are met.

    What We Have Implemented

    • We have a listener (AMCChangeAndAssignListener) that assigns tasks to a specific role when a relevant change occurs.

    • We have a validator (AMCChangeValidator) that checks whether a relevant attribute change has occurred before proceeding.

    Current Behavior (Unexpected)

    • Whenever any attribute of an entity is modified, the workflow is triggered.

    • Tasks are assigned even when changes are unrelated to the expected attribute (SCH_Organization_Classification_Level_3).

    What We Have Tried

    1. Checking API Responses Manually:

    2. Updating Listener Logic:

      • We added logic to fetch change requests and check the attributePath before assigning tasks.

      • The logic is supposed to filter out irrelevant changes.

    3. Reviewing Workflow API Documentation:

      • We explored available methods to determine whether an existing workflow task is already open for the entity before creating a new one.

      • We couldn't find a clear method for this.

    So we have the following questions:

    1. How can we ensure that the workflow is only triggered when a specific attribute changes?

      • Is there a best practice for filtering tasks before they are assigned?

      • Are we missing a more efficient way to check attribute changes via the API?

    2. Is there a recommended way to check for an open task before creating a new one?

      • Are there built-in API methods that allow us to check if a task for the same entity is already open?

    3. Are there any debugging tips for diagnosing workflow triggers in Reltio?

      • How can we verify what exactly is triggering the workflow?

    Any insights or best practices would be greatly appreciated!

    Thanks in advance for any guidance!

    PD: since all the changes were triggering the flow and assigning the new role, which was being done in my Listener, I tried adding all the logic there like behind but still no luck, it gets triggered every time I change any attribute and :

    package com.reltio.cliniciannexus.workflow.listeners.amc;
    
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.reltio.workflow.api.WorkflowService;
    import com.reltio.workflow.api.listeners.WorkflowTaskListener;
    import com.reltio.workflow.api.rest.ReltioApi;
    import com.reltio.workflow.api.tasks.Task;
    import com.reltio.workflow.api.tasks.TaskService;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.List;
    
    public class AMCChangeAndAssignListener implements WorkflowTaskListener {
        private static final Logger logger = LoggerFactory.getLogger(AMCChangeAndAssignListener.class);
    
        @WorkflowService
        private TaskService taskService;
    
        @WorkflowService
        private ReltioApi reltioApi;
    
        private static final String REVIEWER_ROLE = "ROLE_AMC_REVIEWERS";
        private static final String ATTRIBUTE_URI = "configuration/entityTypes/HCO/attributes/SCH_Organization_Classification/attributes/SCH_Organization_Classification_Level_3";
        private static final List<String> AMC_VALUES = List.of("Academic Health System", "Academic Medical Center");
    
        @Override
        public void notify(Task task) {
            logger.info("AMCChangeAndAssignListener triggered for Task ID: {}", task.getId());
    
            // Ensure the task has associated object URIs
            if (task.getObjectUris() == null || task.getObjectUris().isEmpty()) {
                logger.warn("Task ID {} does not have associated object URIs. Skipping assignment.", task.getId());
                return;
            }
    
            // Get the first entity URI from the task
            String entityUri = task.getObjectUris().stream()
                    .filter(uri -> uri.startsWith("entities/"))
                    .findFirst()
                    .orElse(null);
    
            if (entityUri == null) {
                logger.warn("No entity found for Task ID {}. Skipping assignment.", task.getId());
                return;
            }
    
            try {
                // 1. CHECK IF RELEVANT CHANGE EXISTS
                if (!isRelevantChange(entityUri, task)) {
                    logger.info("No relevant changes detected for entity {}. Skipping task creation.", entityUri);
                    return;
                }
    
                // 2. CHECK FOR EXISTING OPEN TASKS
                if (hasOpenTask(entityUri, task)) {
                    logger.info("An open task already exists for entity {}. Skipping task creation.", entityUri);
                    return;
                }
    
                // 3. ASSIGN TASK TO REVIEWER ROLE
                taskService.assignTask(task, REVIEWER_ROLE);
                taskService.setVariable(task.getId(), "reviewAssigned", true);
                logger.info("Successfully assigned Task ID {} to role: {}", task.getId(), REVIEWER_ROLE);
    
            } catch (Exception e) {
                logger.error("Error while processing Task ID {}: {}", task.getId(), e.getMessage(), e);
            }
        }
    
        /**
         * Check if an open task already exists for this entity.
         */
        private boolean hasOpenTask(String entityUri, Task task) {
            try {
                String response = reltioApi.invokeApi(
                        task.getAccessToken(),
                        task.getEnvironmentUrl() + "/workflow/" + task.getTenantId() + "/tasks",
                        "POST",
                        "{ \"objectURIs\": [\"" + entityUri + "\"], \"state\": \"valid\" }"
                );
    
                JsonNode tasksNode = new ObjectMapper().readTree(response).get("data");
                return tasksNode != null && !tasksNode.isEmpty();
            } catch (Exception e) {
                logger.error("Error checking existing tasks for entity {}: {}", entityUri, e.getMessage());
                return false;
            }
        }
    
        /**
         * Check if the change request contains a relevant change.
         */
        private boolean isRelevantChange(String entityUri, Task task) {
            try {
                // Fetch change requests for the entity
                String changeRequestsResponse = reltioApi.invokeApi(
                        task.getAccessToken(),
                        task.getEnvironmentUrl() + "/changeRequests?filter=equals(objectURI, '" + entityUri + "')&max=10&offset=0&select=uri,updatedTime",
                        "GET",
                        null
                );
    
                JsonNode changeRequests = new ObjectMapper().readTree(changeRequestsResponse);
                if (changeRequests == null || !changeRequests.isArray()) return false;
    
                for (JsonNode changeRequest : changeRequests) {
                    String changeRequestUri = changeRequest.get("uri").asText();
                    String changeRequestDetailsResponse = reltioApi.invokeApi(
                            task.getAccessToken(),
                            task.getEnvironmentUrl() + "/" + changeRequestUri,
                            "GET",
                            null
                    );
    
                    JsonNode changeDetails = new ObjectMapper().readTree(changeRequestDetailsResponse).get("changes");
    
                    if (changeDetails != null && changeDetails.isObject()) {
                        for (JsonNode entityChange : changeDetails.get(entityUri)) {
                            String attributePath = entityChange.get("attributePath").asText();
                            String newValue = entityChange.at("/newValue/value").asText();
    
                            if (ATTRIBUTE_URI.equals(attributePath) && AMC_VALUES.contains(newValue)) {
                                logger.info("Relevant change detected: attributePath={}, newValue={}", attributePath, newValue);
                                return true;
                            }
                        }
                    }
                }
            } catch (Exception e) {
                logger.error("Error processing entity URI: {}", entityUri, e);
            }
            return false;
        }
    }
    

    Here is how the workflow runs for any change I do on any attribute from the entity



    ------------------------------
    Luis B.

    ------------------------------


  • 2.  RE: Trigger workflow on a particular attribute change

    Reltio Employee
    Posted 11 days ago

    Hi Luis,

    Data Change Request Review process is triggered when you Suggest changes from a profile screen. The process is started for all changes. We don't have the option to apply some changes directly and pass others to a DCR workflow for further review by data stewards.

    I would recommend you to explore a solution where metadata security is configured in a way that does not grant INITIATE_CHANGE_REQUEST privileges on attributes other than SCH_Organization_Classification_Level_3. In that setup, all attributes remain editable in the regular Editing mode (of course, if CRUD privileges are given) and become read-only (except the SCH_Organization_Classification_Level_3) in the Suggesting screen. So you would have changes of  SCH_Organization_Classification_Level_3 attributes in DCR tasks for review only.

    This approach has one disadvantage - you would have to use both screens: Editing and Suggesting if you need to update  SCH_Organization_Classification_Level_3 and other attributes.



    ------------------------------
    Yury Timofeev
    Product Owner of the Workflow
    Reltio
    ------------------------------