{"action":"created","comment":{"url":"https://api.github.com/repos/caskdata/cdap/pulls/comments/203090824","pull_request_review_id":137634424,"id":203090824,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIwMzA5MDgyNA==","diff_hunk":"@@ -70,6 +73,208 @@ public LineageOperationsProcessor(Set<Connection> stageConnections,\n       stageOutputsWithOrigins.put(stage, new LinkedHashMap<>());\n     }\n     this.noMergeRequiredStages = noMergeRequiredStages;\n+    validate(stageInputFields, stageOutputFields, stageOperations);\n+  }\n+\n+  /**\n+   * Validate the inputs and outputs of operations.\n+   *\n+   * For each operation the input must be coming from the input schema that stage receives\n+   * or it must be one of the output of operations recorded by that stage prior to the\n+   * current operation. For example, consider that stage gets input fields as [a, b, c].\n+   * First operation recorded by the stage generates outputs [d, e]. For any subsequent\n+   * operation, the valid input set will be [a, b, c, d, e].\n+   *\n+   * For each operation the output generated by it must be the part of output schema or\n+   * used as an input by the subsequent operations recorded by the stage. For example,\n+   * if operation OP1 creates outputs [a, b, c, x] and stage has output fields as [a, b, d, e],\n+   * then output c must be consumed by any subsequent operation, otherwise validation\n+   * fails. It is also possible to generate the redundant outputs by operations. For example,\n+   * consider other operations OP2 with inputs as [c] and output as [x], and OP3 with input as\n+   * [X] and output as [b]. In this case the output field [b] created by OP1 is redundant,\n+   * since the field b of the output schema will come from OP3 and b is not used as input by any\n+   * operation subsequent of OP1. Validation fails in this case.\n+   *\n+   * @param stageInputFields stage and corresponding set of input fields\n+   * @param stageOutputFields stage and corresponding set of output fields\n+   * @param stageOperations stage and corresponding List of field operations\n+   * @throws IllegalArgumentException when the validation fails\n+   */\n+  @VisibleForTesting\n+  void validate(Map<String, Set<String>> stageInputFields, Map<String, Set<String>> stageOutputFields,\n+                Map<String, List<PipelineOperation>> stageOperations) {\n+    // For a given stage, following map stores the name of the field to the List of operations\n+    // for which the field appears in the output. Eventually this map will only contains the fields\n+    // which are not used by any subsequent operation and are also not part of output schema of that stage.\n+    Map<String, Map<String, List<String>>> unusedOutputsFromStage = new HashMap<>();\n+\n+    // For a given stage, following map stores the name of the field to the List of operations\n+    // for which the field appears in the input. Eventually this map will only contains the fields\n+    // which are not part of the input schema of that stage and also not outputted by any of the operations.\n+    Map<String, Map<String, List<String>>> invalidOperationInputs = new HashMap<>();\n+\n+    for (Map.Entry<String, List<PipelineOperation>> stageOperation : stageOperations.entrySet()) {\n+      String stageName = stageOperation.getKey();\n+      List<PipelineOperation> operations = stageOperation.getValue();\n+\n+      // map of unused outputs fields for the current stage to the list of operations that outputted\n+      // those fields\n+      Map<String, List<String>> unusedOperationOutputs = new HashMap<>();\n+\n+      // map of output fields which are redundant (overwritten by subsequent operations\n+      // without using them) to the list of operations which create such redundancy\n+      Map<String, List<String>> redundantOutputs = new HashMap<>();\n+\n+      Set<String> validOperationInputs = new HashSet<>(stageInputFields.get(stageName));\n+\n+      // map of field to the operations which are using that field which are not part of\n+      // input schema and also not generated by any operations\n+      Map<String, List<String>> invalidInputs = new HashMap<>();\n+\n+      for (PipelineOperation pipelineOperation : operations) {\n+        switch (pipelineOperation.getType()) {\n+          case READ:\n+            PipelineReadOperation read = (PipelineReadOperation) pipelineOperation;\n+            processInputOutputs(read.getName(), Collections.emptyList(), read.getOutputFields(), validOperationInputs,\n+                                invalidInputs, unusedOperationOutputs, redundantOutputs);\n+            break;\n+          case TRANSFORM:\n+            PipelineTransformOperation transform = (PipelineTransformOperation) pipelineOperation;\n+            processInputOutputs(transform.getName(), transform.getInputFields(), transform.getOutputFields(),\n+                                validOperationInputs, invalidInputs, unusedOperationOutputs, redundantOutputs);\n+            break;\n+          case WRITE:\n+            PipelineWriteOperation write = (PipelineWriteOperation) pipelineOperation;\n+            processInputOutputs(write.getName(), write.getInputFields(), Collections.emptyList(), validOperationInputs,\n+                                invalidInputs, unusedOperationOutputs, redundantOutputs);\n+            break;\n+        }\n+      }\n+\n+      // Remove outputs from unusedOperationOutputs map which are not used by any operation as an input,\n+      // however they are part of output schema. We cannot simply remove all keys here by doing\n+      // \"unusedOperationOutputs.removeAll(stageOutputFields.get(stageName))\". Reason for this is\n+      // consider the case where OP1 outputs field b, and OP2 outputs the same field b. Operation OP1 is\n+      // redundant, since b will be overwritten by OP2. However since field b is  not consumed by any other\n+      // operation as an input, redundantOutputs will not have its entry. We still want to fail the validation\n+      // for operation OP1.\n+      Iterator<Map.Entry<String, List<String>>> iterator = unusedOperationOutputs.entrySet().iterator();\n+      while (iterator.hasNext()) {\n+        Map.Entry<String, List<String>> next = iterator.next();\n+        String field = next.getKey();\n+        List<String> origins = next.getValue();\n+\n+        if (stageOutputFields.get(stageName).contains(field)) {\n+          iterator.remove();\n+          if (origins.size() > 1) {\n+            unusedOperationOutputs.put(field, origins.subList(0, origins.size() - 1));\n+          }\n+        }\n+      }\n+\n+      unusedOperationOutputs.putAll(redundantOutputs);\n+\n+      if (!unusedOperationOutputs.isEmpty()) {\n+        unusedOutputsFromStage.put(stageName, unusedOperationOutputs);\n+      }\n+\n+      if (!invalidInputs.isEmpty()) {\n+        invalidOperationInputs.put(stageName, invalidInputs);\n+      }\n+    }\n+\n+    if (unusedOutputsFromStage.isEmpty() && invalidOperationInputs.isEmpty()) {\n+      return;\n+    }\n+\n+    String message = invalidOutputErrorMessage(unusedOutputsFromStage);\n+    message += invalidInputErrorMessage(invalidOperationInputs);\n+    throw new IllegalArgumentException(message);\n+  }\n+\n+  private void processInputOutputs(String operationName, List<String> inputs, List<String> outputs,\n+                                   Set<String> validOperationInputs, Map<String, List<String>> invalidInputs,\n+                                   Map<String, List<String>> unusedOperationOutputs,\n+                                   Map<String, List<String>> redundantOutputs) {\n+    for (String field : inputs) {\n+      // check if field is valid input. input is valid if it is in the validOperationInputs set\n+      if (!validOperationInputs.contains(field)) {\n+        List<String> originsWithInvalidInput = invalidInputs.computeIfAbsent(field, k -> new ArrayList<>());\n+        originsWithInvalidInput.add(operationName);\n+      }\n+\n+      // current input field can be removed from unusedOperationOutputs set now\n+      // as it is being used by current operation\n+      List<String> origins = unusedOperationOutputs.get(field);\n+      // origins can be null if the field is coming directly from the input schema\n+      if (origins != null) {\n+        if (origins.size() > 1) {\n+          // field is outputted by multiple operations. all occurrences of outputs are redundant\n+          // except the last one\n+          List<String> redundantOrigins = redundantOutputs.computeIfAbsent(field, k -> new ArrayList<>());","path":"cdap-app-templates/cdap-etl/cdap-data-pipeline/src/main/java/co/cask/cdap/datapipeline/LineageOperationsProcessor.java","position":175,"original_position":175,"commit_id":"41d5ca8090cee35f5fc52a19f2a68f8c51b07f2d","original_commit_id":"41d5ca8090cee35f5fc52a19f2a68f8c51b07f2d","user":{"login":"albertshau","id":5215004,"node_id":"MDQ6VXNlcjUyMTUwMDQ=","avatar_url":"https://avatars3.githubusercontent.com/u/5215004?v=4","gravatar_id":"","url":"https://api.github.com/users/albertshau","html_url":"https://github.com/albertshau","followers_url":"https://api.github.com/users/albertshau/followers","following_url":"https://api.github.com/users/albertshau/following{/other_user}","gists_url":"https://api.github.com/users/albertshau/gists{/gist_id}","starred_url":"https://api.github.com/users/albertshau/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/albertshau/subscriptions","organizations_url":"https://api.github.com/users/albertshau/orgs","repos_url":"https://api.github.com/users/albertshau/repos","events_url":"https://api.github.com/users/albertshau/events{/privacy}","received_events_url":"https://api.github.com/users/albertshau/received_events","type":"User","site_admin":false},"body":"does this mean we can get here multiple times? If so, doesn't this mean we'll add multiple copies of redundant operations to the redundantOrigins list? Seems like it should be a Set.","created_at":"2018-07-17T16:27:46Z","updated_at":"2018-07-17T16:37:20Z","html_url":"https://github.com/caskdata/cdap/pull/10349#discussion_r203090824","pull_request_url":"https://api.github.com/repos/caskdata/cdap/pulls/10349","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/caskdata/cdap/pulls/comments/203090824"},"html":{"href":"https://github.com/caskdata/cdap/pull/10349#discussion_r203090824"},"pull_request":{"href":"https://api.github.com/repos/caskdata/cdap/pulls/10349"}}},"pull_request":{"url":"https://api.github.com/repos/caskdata/cdap/pulls/10349","id":201786923,"node_id":"MDExOlB1bGxSZXF1ZXN0MjAxNzg2OTIz","html_url":"https://github.com/caskdata/cdap/pull/10349","diff_url":"https://github.com/caskdata/cdap/pull/10349.diff","patch_url":"https://github.com/caskdata/cdap/pull/10349.patch","issue_url":"https://api.github.com/repos/caskdata/cdap/issues/10349","number":10349,"state":"open","locked":false,"title":"CDAP-13717 Validate the inputs and outputs of operations recorded by stage.","user":{"login":"sagarkapare","id":5713570,"node_id":"MDQ6VXNlcjU3MTM1NzA=","avatar_url":"https://avatars3.githubusercontent.com/u/5713570?v=4","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":"JIRA: https://issues.cask.co/browse/CDAP-13717\r\n\r\nFollowing validations are added:\r\n1. For inputs of the operation, is has to come from either input schema of a stage or it should be output of one of the prior operation that is recorded by the current stage.\r\n2. Output of an operation should either be part of output schema of a stage, or it should be consumed by some subsequent operation recorded by that stage.\r\n3. If same output is produced by multiple operations recorded by same stage and is later consumed by some other operation recorded by the same stage or is a part of output schema, then all the operations which produced that output except the last one are considered redundant and validation fails.\r\nFor example: \r\n```java\r\nop1: [a] -> [b, c]\r\nop2: [c] -> [b]\r\nop3: [b] -> [d]\r\nop4: [c] -> [d]\r\n\r\nOutput schema has fields: [c, d]\r\n```\r\nThen the op1 is considered redundant for field b, since op3 will always use input b created by op2. Similarly op3 is considered redundant for d, since output schema will always have d which is created by op4.\r\n\r\nIf validation fails, field lineage wont be recorded.","created_at":"2018-07-16T23:28:56Z","updated_at":"2018-07-17T16:37:20Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6ef8fb9c191e9aaf15b5bb09411107f71eb9456c","assignee":{"login":"albertshau","id":5215004,"node_id":"MDQ6VXNlcjUyMTUwMDQ=","avatar_url":"https://avatars3.githubusercontent.com/u/5215004?v=4","gravatar_id":"","url":"https://api.github.com/users/albertshau","html_url":"https://github.com/albertshau","followers_url":"https://api.github.com/users/albertshau/followers","following_url":"https://api.github.com/users/albertshau/following{/other_user}","gists_url":"https://api.github.com/users/albertshau/gists{/gist_id}","starred_url":"https://api.github.com/users/albertshau/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/albertshau/subscriptions","organizations_url":"https://api.github.com/users/albertshau/orgs","repos_url":"https://api.github.com/users/albertshau/repos","events_url":"https://api.github.com/users/albertshau/events{/privacy}","received_events_url":"https://api.github.com/users/albertshau/received_events","type":"User","site_admin":false},"assignees":[{"login":"albertshau","id":5215004,"node_id":"MDQ6VXNlcjUyMTUwMDQ=","avatar_url":"https://avatars3.githubusercontent.com/u/5215004?v=4","gravatar_id":"","url":"https://api.github.com/users/albertshau","html_url":"https://github.com/albertshau","followers_url":"https://api.github.com/users/albertshau/followers","following_url":"https://api.github.com/users/albertshau/following{/other_user}","gists_url":"https://api.github.com/users/albertshau/gists{/gist_id}","starred_url":"https://api.github.com/users/albertshau/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/albertshau/subscriptions","organizations_url":"https://api.github.com/users/albertshau/orgs","repos_url":"https://api.github.com/users/albertshau/repos","events_url":"https://api.github.com/users/albertshau/events{/privacy}","received_events_url":"https://api.github.com/users/albertshau/received_events","type":"User","site_admin":false}],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":673594855,"node_id":"MDU6TGFiZWw2NzM1OTQ4NTU=","url":"https://api.github.com/repos/caskdata/cdap/labels/5.0","name":"5.0","color":"ff6600","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/caskdata/cdap/pulls/10349/commits","review_comments_url":"https://api.github.com/repos/caskdata/cdap/pulls/10349/comments","review_comment_url":"https://api.github.com/repos/caskdata/cdap/pulls/comments{/number}","comments_url":"https://api.github.com/repos/caskdata/cdap/issues/10349/comments","statuses_url":"https://api.github.com/repos/caskdata/cdap/statuses/41d5ca8090cee35f5fc52a19f2a68f8c51b07f2d","head":{"label":"caskdata:bugfix_release/CDAP-13717-fll-validations","ref":"bugfix_release/CDAP-13717-fll-validations","sha":"41d5ca8090cee35f5fc52a19f2a68f8c51b07f2d","user":{"login":"caskdata","id":8123023,"node_id":"MDEyOk9yZ2FuaXphdGlvbjgxMjMwMjM=","avatar_url":"https://avatars2.githubusercontent.com/u/8123023?v=4","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,"node_id":"MDEwOlJlcG9zaXRvcnkyMjU0Mjc1OQ==","name":"cdap","full_name":"caskdata/cdap","owner":{"login":"caskdata","id":8123023,"node_id":"MDEyOk9yZ2FuaXphdGlvbjgxMjMwMjM=","avatar_url":"https://avatars2.githubusercontent.com/u/8123023?v=4","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":"A Unified Integration Platform - 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}","deployments_url":"https://api.github.com/repos/caskdata/cdap/deployments","created_at":"2014-08-02T09:00:10Z","updated_at":"2018-07-17T01:02:06Z","pushed_at":"2018-07-17T14:58:03Z","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://docs.cask.co/cdap","size":541054,"stargazers_count":238,"watchers_count":238,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":121,"mirror_url":null,"archived":false,"open_issues_count":29,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":121,"open_issues":29,"watchers":238,"default_branch":"release/5.0"}},"base":{"label":"caskdata:release/5.0","ref":"release/5.0","sha":"ac85988e3ce11e3036c8bc5d172c48f9cbfa56c7","user":{"login":"caskdata","id":8123023,"node_id":"MDEyOk9yZ2FuaXphdGlvbjgxMjMwMjM=","avatar_url":"https://avatars2.githubusercontent.com/u/8123023?v=4","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,"node_id":"MDEwOlJlcG9zaXRvcnkyMjU0Mjc1OQ==","name":"cdap","full_name":"caskdata/cdap","owner":{"login":"caskdata","id":8123023,"node_id":"MDEyOk9yZ2FuaXphdGlvbjgxMjMwMjM=","avatar_url":"https://avatars2.githubusercontent.com/u/8123023?v=4","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":"A Unified Integration Platform - 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}","deployments_url":"https://api.github.com/repos/caskdata/cdap/deployments","created_at":"2014-08-02T09:00:10Z","updated_at":"2018-07-17T01:02:06Z","pushed_at":"2018-07-17T14:58:03Z","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://docs.cask.co/cdap","size":541054,"stargazers_count":238,"watchers_count":238,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":121,"mirror_url":null,"archived":false,"open_issues_count":29,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":121,"open_issues":29,"watchers":238,"default_branch":"release/5.0"}},"_links":{"self":{"href":"https://api.github.com/repos/caskdata/cdap/pulls/10349"},"html":{"href":"https://github.com/caskdata/cdap/pull/10349"},"issue":{"href":"https://api.github.com/repos/caskdata/cdap/issues/10349"},"comments":{"href":"https://api.github.com/repos/caskdata/cdap/issues/10349/comments"},"review_comments":{"href":"https://api.github.com/repos/caskdata/cdap/pulls/10349/comments"},"review_comment":{"href":"https://api.github.com/repos/caskdata/cdap/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/caskdata/cdap/pulls/10349/commits"},"statuses":{"href":"https://api.github.com/repos/caskdata/cdap/statuses/41d5ca8090cee35f5fc52a19f2a68f8c51b07f2d"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"albertshau"}}