{"action":"created","comment":{"url":"https://api.github.com/repos/caskdata/cdap/pulls/comments/35835530","id":35835530,"diff_hunk":"@@ -124,26 +133,139 @@ private WorkflowNode createNodeWithId(WorkflowNode node) {\n     return nodeWithId;\n   }\n \n-  private WorkflowNode createForkNodeWithId(WorkflowNode node) {\n+  private WorkflowNode createActionNodeWithId(WorkflowNode currentNode, WorkflowNode previousNode) {\n+    WorkflowActionNode actionNode = (WorkflowActionNode) currentNode;\n+    ScheduleProgramInfo program = actionNode.getProgram();\n+    Set<String> parentNodeIds = new HashSet<>();\n+    populateParentNodeIds(previousNode, parentNodeIds, currentNode);\n+    if (program.getProgramType() == SchedulableProgramType.CUSTOM_ACTION) {\n+      return new WorkflowActionNode(currentNode.getNodeId(), actionNode.getActionSpecification(), parentNodeIds);\n+    }\n+    return new WorkflowActionNode(currentNode.getNodeId(), program, parentNodeIds);\n+  }\n+\n+  private WorkflowNode createForkNodeWithId(WorkflowNode currentNode, WorkflowNode previousNode) {\n     String forkNodeId = Integer.toString(nodeIdentifier++);\n     List<List<WorkflowNode>> branches = Lists.newArrayList();\n-    WorkflowForkNode forkNode = (WorkflowForkNode) node;\n+    WorkflowForkNode forkNode = (WorkflowForkNode) currentNode;\n \n     for (List<WorkflowNode> branch : forkNode.getBranches()) {\n-      branches.add(createNodesWithId(branch));\n+      branches.add(createNodesWithId(branch, previousNode));\n     }\n     return new WorkflowForkNode(forkNodeId, branches);\n   }\n \n-  private WorkflowNode createConditionNodeWithId(WorkflowNode node) {\n-    String conditionNodeId = node.getNodeId();\n-    WorkflowConditionNode conditionNode = (WorkflowConditionNode) node;\n+  private WorkflowNode createConditionNodeWithId(WorkflowNode currentNode, WorkflowNode previousNode) {\n+    String conditionNodeId = currentNode.getNodeId();\n+    WorkflowConditionNode conditionNode = (WorkflowConditionNode) currentNode;\n     List<WorkflowNode> ifbranch = Lists.newArrayList();\n     List<WorkflowNode> elsebranch = Lists.newArrayList();\n-    ifbranch.addAll(createNodesWithId(conditionNode.getIfBranch()));\n-    elsebranch.addAll(createNodesWithId(conditionNode.getElseBranch()));\n+    ifbranch.addAll(createNodesWithId(conditionNode.getIfBranch(), conditionNode));\n+    elsebranch.addAll(createNodesWithId(conditionNode.getElseBranch(), conditionNode));\n+\n+    Set<String> parentNodeIds = new HashSet<>();\n+    populateParentNodeIds(previousNode, parentNodeIds, currentNode);\n+    return new WorkflowConditionNode(conditionNodeId, conditionNode.getPredicateClassName(), ifbranch, elsebranch,\n+                                     parentNodeIds);\n+  }\n+\n+  // This method populates the set parentNodeIds with the parent node ids of the current node.\n+  // If the previous node is of type ACTION, then that node is considered as the parent of the current node.\n+  // If the previous node if of type FORK, then the last nodes on all the branches of FORK are considered as\n+  // the parent of the current node.\n+  // If the previous node is of type CONDITION and if the node is immediate child of the CONDITION, then the\n+  // CONDITION node is considered to be parent of the current node, otherwise last nodes on the if branch and\n+  // else branch are considered to be the parent of the current node.\n+  private void populateParentNodeIds(WorkflowNode previousNodeId, Set<String> parentNodeIds, WorkflowNode currentNode) {\n+    if (previousNodeId == null) {\n+      // return in case of first node in the Workflow\n+      return;\n+    }\n+\n+    switch (previousNodeId.getType()) {\n+      case ACTION:\n+        parentNodeIds.add(previousNodeId.getNodeId());\n+        break;\n+      case FORK:\n+        WorkflowForkNode forkNode = (WorkflowForkNode) previousNodeId;\n+        for (List<WorkflowNode> branch : forkNode.getBranches()) {\n+          if (branch.isEmpty()) {\n+            continue;\n+          }\n+          populateParentNodeIds(branch.get(branch.size() - 1), parentNodeIds, currentNode);\n+        }\n+        break;\n+      case CONDITION:\n+        WorkflowConditionNode conditionNode = (WorkflowConditionNode) previousNodeId;\n+        List<WorkflowNode> ifBranch = conditionNode.getIfBranch();\n+        List<WorkflowNode> elseBranch = conditionNode.getElseBranch();\n+\n+        if (isImmediateChildOfConditionNode(conditionNode, currentNode)) {\n+          parentNodeIds.add(conditionNode.getNodeId());\n+          break;\n+        }\n+\n+        if (!ifBranch.isEmpty()) {\n+          populateParentNodeIds(ifBranch.get(ifBranch.size() - 1), parentNodeIds, currentNode);\n+        } else {\n+          // If branch of the condition is empty, so for the node following condition will have condition node\n+          // also as a parent\n+          parentNodeIds.add(conditionNode.getNodeId());\n+        }\n+\n+        if (!elseBranch.isEmpty()) {\n+          populateParentNodeIds(elseBranch.get(elseBranch.size() - 1), parentNodeIds, currentNode);\n+        } else {\n+          // Else branch of the condition is empty, so for the node following condition will have condition node\n+          // also as a parent\n+          parentNodeIds.add(conditionNode.getNodeId());\n+        }\n+        break;\n+      default:\n+        throw new IllegalStateException(\"Node type is invalid.\");\n+    }\n+  }\n \n-    return new WorkflowConditionNode(conditionNodeId, conditionNode.getPredicateClassName(), ifbranch, elsebranch);\n+  // This method determines if the node is immediate child of the CONDITION node.\n+  // For e.g. consider the following workflow where c1 is the condition node.\n+  // If branch of the condition node executes a fork with actions a1, a3, and a4\n+  // in parallel. Else branch of the condition execute a5. Node a6 gets executed after\n+  // the If/Else branch of the condition finishes the execution.\n+  //\n+  //               T |--a1---a2--|\n+  //            |----|-----a3----|----|\n+  //      c1----|    |-----a4----|    |---a6\n+  //            |----------a5---------|\n+  //               F\n+  //\n+  // For nodes a1, a3, 14, and a5 are considered to be immediate children of the c1 and method\n+  // returns true for them, so that parent set of a1, a3, a4, and a5 will contain only c1.\n+  // For node a6, even its previous node is c1, since its not the immediate child of c1, method","path":"cdap-app-fabric/src/main/java/co/cask/cdap/internal/app/workflow/DefaultWorkflowConfigurer.java","position":188,"original_position":188,"commit_id":"0ce6dd187fd3510ce287761c031d3798723c0906","original_commit_id":"0ce6dd187fd3510ce287761c031d3798723c0906","user":{"login":"awholegunch","id":6394794,"avatar_url":"https://avatars.githubusercontent.com/u/6394794?v=3","gravatar_id":"","url":"https://api.github.com/users/awholegunch","html_url":"https://github.com/awholegunch","followers_url":"https://api.github.com/users/awholegunch/followers","following_url":"https://api.github.com/users/awholegunch/following{/other_user}","gists_url":"https://api.github.com/users/awholegunch/gists{/gist_id}","starred_url":"https://api.github.com/users/awholegunch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/awholegunch/subscriptions","organizations_url":"https://api.github.com/users/awholegunch/orgs","repos_url":"https://api.github.com/users/awholegunch/repos","events_url":"https://api.github.com/users/awholegunch/events{/privacy}","received_events_url":"https://api.github.com/users/awholegunch/received_events","type":"User","site_admin":false},"body":"For node a6, though its previous node is c1, since it's not an immediate child of c1, the method","created_at":"2015-07-30T03:29:14Z","updated_at":"2015-07-30T03:29:14Z","html_url":"https://github.com/caskdata/cdap/pull/3311#discussion_r35835530","pull_request_url":"https://api.github.com/repos/caskdata/cdap/pulls/3311","_links":{"self":{"href":"https://api.github.com/repos/caskdata/cdap/pulls/comments/35835530"},"html":{"href":"https://github.com/caskdata/cdap/pull/3311#discussion_r35835530"},"pull_request":{"href":"https://api.github.com/repos/caskdata/cdap/pulls/3311"}}},"pull_request":{"url":"https://api.github.com/repos/caskdata/cdap/pulls/3311","id":40934792,"html_url":"https://github.com/caskdata/cdap/pull/3311","diff_url":"https://github.com/caskdata/cdap/pull/3311.diff","patch_url":"https://github.com/caskdata/cdap/pull/3311.patch","issue_url":"https://api.github.com/repos/caskdata/cdap/issues/3311","number":3311,"state":"open","locked":false,"title":"Added parentNodIds to the WorkflowNode.","user":{"login":"sagarkapare","id":5713570,"avatar_url":"https://avatars.githubusercontent.com/u/5713570?v=3","gravatar_id":"","url":"https://api.github.com/users/sagarkapare","html_url":"https://github.com/sagarkapare","followers_url":"https://api.github.com/users/sagarkapare/followers","following_url":"https://api.github.com/users/sagarkapare/following{/other_user}","gists_url":"https://api.github.com/users/sagarkapare/gists{/gist_id}","starred_url":"https://api.github.com/users/sagarkapare/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sagarkapare/subscriptions","organizations_url":"https://api.github.com/users/sagarkapare/orgs","repos_url":"https://api.github.com/users/sagarkapare/repos","events_url":"https://api.github.com/users/sagarkapare/events{/privacy}","received_events_url":"https://api.github.com/users/sagarkapare/received_events","type":"User","site_admin":false},"body":"Opening it against release/3.1 branch.\r\n\r\nJIRA: https://issues.cask.co/browse/CDAP-3000\r\n\r\nFollowing changes are done as a part of the PR:\r\n1. Added set of parentNodeIds to the each WorkflowNode, which points to the previous nodes in the Workflow. \r\n2. While doing the get operation on the WorkflowToken, we create the filter list of node Ids with the help of parentNodeIds set at each node. Only values from this filter list are returned.\r\n3. While forking in the Workflow, we make copies of the BasicWorkflowToken. Instead of making deep copy of it, we allow sharing of the tokenValueMap instance inside it across different branches, so that same map gets updated by different nodes. \r\n4. Added unit test cases.","created_at":"2015-07-27T18:37:12Z","updated_at":"2015-07-30T03:29:14Z","closed_at":null,"merged_at":null,"merge_commit_sha":"bd98cfd0f301aad2d221886c089cb538bf94f268","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/caskdata/cdap/pulls/3311/commits","review_comments_url":"https://api.github.com/repos/caskdata/cdap/pulls/3311/comments","review_comment_url":"https://api.github.com/repos/caskdata/cdap/pulls/comments{/number}","comments_url":"https://api.github.com/repos/caskdata/cdap/issues/3311/comments","statuses_url":"https://api.github.com/repos/caskdata/cdap/statuses/0ce6dd187fd3510ce287761c031d3798723c0906","head":{"label":"caskdata:feature/parent-node-id-workflow","ref":"feature/parent-node-id-workflow","sha":"0ce6dd187fd3510ce287761c031d3798723c0906","user":{"login":"caskdata","id":8123023,"avatar_url":"https://avatars.githubusercontent.com/u/8123023?v=3","gravatar_id":"","url":"https://api.github.com/users/caskdata","html_url":"https://github.com/caskdata","followers_url":"https://api.github.com/users/caskdata/followers","following_url":"https://api.github.com/users/caskdata/following{/other_user}","gists_url":"https://api.github.com/users/caskdata/gists{/gist_id}","starred_url":"https://api.github.com/users/caskdata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/caskdata/subscriptions","organizations_url":"https://api.github.com/users/caskdata/orgs","repos_url":"https://api.github.com/users/caskdata/repos","events_url":"https://api.github.com/users/caskdata/events{/privacy}","received_events_url":"https://api.github.com/users/caskdata/received_events","type":"Organization","site_admin":false},"repo":{"id":22542759,"name":"cdap","full_name":"caskdata/cdap","owner":{"login":"caskdata","id":8123023,"avatar_url":"https://avatars.githubusercontent.com/u/8123023?v=3","gravatar_id":"","url":"https://api.github.com/users/caskdata","html_url":"https://github.com/caskdata","followers_url":"https://api.github.com/users/caskdata/followers","following_url":"https://api.github.com/users/caskdata/following{/other_user}","gists_url":"https://api.github.com/users/caskdata/gists{/gist_id}","starred_url":"https://api.github.com/users/caskdata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/caskdata/subscriptions","organizations_url":"https://api.github.com/users/caskdata/orgs","repos_url":"https://api.github.com/users/caskdata/repos","events_url":"https://api.github.com/users/caskdata/events{/privacy}","received_events_url":"https://api.github.com/users/caskdata/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/caskdata/cdap","description":"Cask Data Application Platform (CDAP)","fork":false,"url":"https://api.github.com/repos/caskdata/cdap","forks_url":"https://api.github.com/repos/caskdata/cdap/forks","keys_url":"https://api.github.com/repos/caskdata/cdap/keys{/key_id}","collaborators_url":"https://api.github.com/repos/caskdata/cdap/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/caskdata/cdap/teams","hooks_url":"https://api.github.com/repos/caskdata/cdap/hooks","issue_events_url":"https://api.github.com/repos/caskdata/cdap/issues/events{/number}","events_url":"https://api.github.com/repos/caskdata/cdap/events","assignees_url":"https://api.github.com/repos/caskdata/cdap/assignees{/user}","branches_url":"https://api.github.com/repos/caskdata/cdap/branches{/branch}","tags_url":"https://api.github.com/repos/caskdata/cdap/tags","blobs_url":"https://api.github.com/repos/caskdata/cdap/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/caskdata/cdap/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/caskdata/cdap/git/refs{/sha}","trees_url":"https://api.github.com/repos/caskdata/cdap/git/trees{/sha}","statuses_url":"https://api.github.com/repos/caskdata/cdap/statuses/{sha}","languages_url":"https://api.github.com/repos/caskdata/cdap/languages","stargazers_url":"https://api.github.com/repos/caskdata/cdap/stargazers","contributors_url":"https://api.github.com/repos/caskdata/cdap/contributors","subscribers_url":"https://api.github.com/repos/caskdata/cdap/subscribers","subscription_url":"https://api.github.com/repos/caskdata/cdap/subscription","commits_url":"https://api.github.com/repos/caskdata/cdap/commits{/sha}","git_commits_url":"https://api.github.com/repos/caskdata/cdap/git/commits{/sha}","comments_url":"https://api.github.com/repos/caskdata/cdap/comments{/number}","issue_comment_url":"https://api.github.com/repos/caskdata/cdap/issues/comments{/number}","contents_url":"https://api.github.com/repos/caskdata/cdap/contents/{+path}","compare_url":"https://api.github.com/repos/caskdata/cdap/compare/{base}...{head}","merges_url":"https://api.github.com/repos/caskdata/cdap/merges","archive_url":"https://api.github.com/repos/caskdata/cdap/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/caskdata/cdap/downloads","issues_url":"https://api.github.com/repos/caskdata/cdap/issues{/number}","pulls_url":"https://api.github.com/repos/caskdata/cdap/pulls{/number}","milestones_url":"https://api.github.com/repos/caskdata/cdap/milestones{/number}","notifications_url":"https://api.github.com/repos/caskdata/cdap/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/caskdata/cdap/labels{/name}","releases_url":"https://api.github.com/repos/caskdata/cdap/releases{/id}","created_at":"2014-08-02T09:00:10Z","updated_at":"2015-07-29T22:30:21Z","pushed_at":"2015-07-30T03:27:41Z","git_url":"git://github.com/caskdata/cdap.git","ssh_url":"git@github.com:caskdata/cdap.git","clone_url":"https://github.com/caskdata/cdap.git","svn_url":"https://github.com/caskdata/cdap","homepage":"http://cdap.io","size":840926,"stargazers_count":58,"watchers_count":58,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":19,"mirror_url":null,"open_issues_count":22,"forks":19,"open_issues":22,"watchers":58,"default_branch":"develop"}},"base":{"label":"caskdata:release/3.1","ref":"release/3.1","sha":"b29df07c96b53b45b8f6c2ae1d2da551ac8c330e","user":{"login":"caskdata","id":8123023,"avatar_url":"https://avatars.githubusercontent.com/u/8123023?v=3","gravatar_id":"","url":"https://api.github.com/users/caskdata","html_url":"https://github.com/caskdata","followers_url":"https://api.github.com/users/caskdata/followers","following_url":"https://api.github.com/users/caskdata/following{/other_user}","gists_url":"https://api.github.com/users/caskdata/gists{/gist_id}","starred_url":"https://api.github.com/users/caskdata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/caskdata/subscriptions","organizations_url":"https://api.github.com/users/caskdata/orgs","repos_url":"https://api.github.com/users/caskdata/repos","events_url":"https://api.github.com/users/caskdata/events{/privacy}","received_events_url":"https://api.github.com/users/caskdata/received_events","type":"Organization","site_admin":false},"repo":{"id":22542759,"name":"cdap","full_name":"caskdata/cdap","owner":{"login":"caskdata","id":8123023,"avatar_url":"https://avatars.githubusercontent.com/u/8123023?v=3","gravatar_id":"","url":"https://api.github.com/users/caskdata","html_url":"https://github.com/caskdata","followers_url":"https://api.github.com/users/caskdata/followers","following_url":"https://api.github.com/users/caskdata/following{/other_user}","gists_url":"https://api.github.com/users/caskdata/gists{/gist_id}","starred_url":"https://api.github.com/users/caskdata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/caskdata/subscriptions","organizations_url":"https://api.github.com/users/caskdata/orgs","repos_url":"https://api.github.com/users/caskdata/repos","events_url":"https://api.github.com/users/caskdata/events{/privacy}","received_events_url":"https://api.github.com/users/caskdata/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/caskdata/cdap","description":"Cask Data Application Platform (CDAP)","fork":false,"url":"https://api.github.com/repos/caskdata/cdap","forks_url":"https://api.github.com/repos/caskdata/cdap/forks","keys_url":"https://api.github.com/repos/caskdata/cdap/keys{/key_id}","collaborators_url":"https://api.github.com/repos/caskdata/cdap/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/caskdata/cdap/teams","hooks_url":"https://api.github.com/repos/caskdata/cdap/hooks","issue_events_url":"https://api.github.com/repos/caskdata/cdap/issues/events{/number}","events_url":"https://api.github.com/repos/caskdata/cdap/events","assignees_url":"https://api.github.com/repos/caskdata/cdap/assignees{/user}","branches_url":"https://api.github.com/repos/caskdata/cdap/branches{/branch}","tags_url":"https://api.github.com/repos/caskdata/cdap/tags","blobs_url":"https://api.github.com/repos/caskdata/cdap/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/caskdata/cdap/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/caskdata/cdap/git/refs{/sha}","trees_url":"https://api.github.com/repos/caskdata/cdap/git/trees{/sha}","statuses_url":"https://api.github.com/repos/caskdata/cdap/statuses/{sha}","languages_url":"https://api.github.com/repos/caskdata/cdap/languages","stargazers_url":"https://api.github.com/repos/caskdata/cdap/stargazers","contributors_url":"https://api.github.com/repos/caskdata/cdap/contributors","subscribers_url":"https://api.github.com/repos/caskdata/cdap/subscribers","subscription_url":"https://api.github.com/repos/caskdata/cdap/subscription","commits_url":"https://api.github.com/repos/caskdata/cdap/commits{/sha}","git_commits_url":"https://api.github.com/repos/caskdata/cdap/git/commits{/sha}","comments_url":"https://api.github.com/repos/caskdata/cdap/comments{/number}","issue_comment_url":"https://api.github.com/repos/caskdata/cdap/issues/comments{/number}","contents_url":"https://api.github.com/repos/caskdata/cdap/contents/{+path}","compare_url":"https://api.github.com/repos/caskdata/cdap/compare/{base}...{head}","merges_url":"https://api.github.com/repos/caskdata/cdap/merges","archive_url":"https://api.github.com/repos/caskdata/cdap/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/caskdata/cdap/downloads","issues_url":"https://api.github.com/repos/caskdata/cdap/issues{/number}","pulls_url":"https://api.github.com/repos/caskdata/cdap/pulls{/number}","milestones_url":"https://api.github.com/repos/caskdata/cdap/milestones{/number}","notifications_url":"https://api.github.com/repos/caskdata/cdap/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/caskdata/cdap/labels{/name}","releases_url":"https://api.github.com/repos/caskdata/cdap/releases{/id}","created_at":"2014-08-02T09:00:10Z","updated_at":"2015-07-29T22:30:21Z","pushed_at":"2015-07-30T03:27:41Z","git_url":"git://github.com/caskdata/cdap.git","ssh_url":"git@github.com:caskdata/cdap.git","clone_url":"https://github.com/caskdata/cdap.git","svn_url":"https://github.com/caskdata/cdap","homepage":"http://cdap.io","size":840926,"stargazers_count":58,"watchers_count":58,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":19,"mirror_url":null,"open_issues_count":22,"forks":19,"open_issues":22,"watchers":58,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/caskdata/cdap/pulls/3311"},"html":{"href":"https://github.com/caskdata/cdap/pull/3311"},"issue":{"href":"https://api.github.com/repos/caskdata/cdap/issues/3311"},"comments":{"href":"https://api.github.com/repos/caskdata/cdap/issues/3311/comments"},"review_comments":{"href":"https://api.github.com/repos/caskdata/cdap/pulls/3311/comments"},"review_comment":{"href":"https://api.github.com/repos/caskdata/cdap/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/caskdata/cdap/pulls/3311/commits"},"statuses":{"href":"https://api.github.com/repos/caskdata/cdap/statuses/0ce6dd187fd3510ce287761c031d3798723c0906"}}}}