{"action":"created","comment":{"url":"https://api.github.com/repos/naver/yobi/pulls/comments/31979667","id":31979667,"diff_hunk":"@@ -1138,6 +1138,78 @@ public static Result detachLabel(String ownerId, String projectName, Long id) {\n         return status(Http.Status.NO_CONTENT);\n     }\n \n+    /**\n+     * @param ownerId the owner login id\n+     * @param projectName the project name\n+     * @return\n+     */\n+    @IsAllowed(Operation.UPDATE)\n+    public static Result webhooks(String ownerId, String projectName) {\n+        Project project = Project.findByOwnerAndProjectName(ownerId, projectName);\n+        if (project == null) {\n+            // Return 404 Not Found if the project does not exist.\n+            return notFound(ErrorViews.NotFound.render());\n+        }\n+\n+        return ok(views.html.project.webhooks.render(\n+                \"project.webhook\",\n+                Webhook.findByProject(project.id),\n+                project));\n+    }\n+\n+    @Transactional\n+    @IsAllowed(Operation.UPDATE)\n+    public static Result newWebhook(String ownerId, String projectName) {\n+        Project project = Project.findByOwnerAndProjectName(ownerId, projectName);\n+        if (project == null) {\n+            // Return 404 Not Found if the project does not exist.\n+            return notFound(ErrorViews.NotFound.render());\n+        }\n+\n+        Form<Webhook> addWebhookForm = form(Webhook.class).bindFromRequest();\n+        if (validateWebhookForm(addWebhookForm)) {\n+            return badRequest(ErrorViews.BadRequest.render());\n+        }\n+\n+        Webhook.create(project.id,\n+                        addWebhookForm.field(\"payload_url\").value(),\n+                        addWebhookForm.field(\"secret\").value());\n+\n+        return redirect(routes.ProjectApp.webhooks(ownerId, projectName));\n+    }\n+\n+    @Transactional\n+    public static Result deleteWebhook(String ownerId, String projectName, Long id) {\n+        Webhook webhook = Webhook.find.byId(id);\n+        if (webhook != null) {\n+            webhook.delete();\n+            return ok(\"{}\");\n+        } else {\n+            // Return 404 Not Found if the webhook does not exist.\n+            return notFound(ErrorViews.NotFound.render());\n+        }\n+    }\n+\n+    private static boolean validateWebhookForm (Form<Webhook> addWebhookForm) {\n+        String payloadUrl = addWebhookForm.field(\"payload_url\").value(),\n+                secretToken = addWebhookForm.field(\"secret\").value();","path":"app/controllers/ProjectApp.java","position":58,"original_position":58,"commit_id":"c443c3c7b1d568d638b20cb35f49a4bbc2448af4","original_commit_id":"c443c3c7b1d568d638b20cb35f49a4bbc2448af4","user":{"login":"npcode","id":1129852,"avatar_url":"https://avatars.githubusercontent.com/u/1129852?v=3","gravatar_id":"","url":"https://api.github.com/users/npcode","html_url":"https://github.com/npcode","followers_url":"https://api.github.com/users/npcode/followers","following_url":"https://api.github.com/users/npcode/following{/other_user}","gists_url":"https://api.github.com/users/npcode/gists{/gist_id}","starred_url":"https://api.github.com/users/npcode/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/npcode/subscriptions","organizations_url":"https://api.github.com/users/npcode/orgs","repos_url":"https://api.github.com/users/npcode/repos","events_url":"https://api.github.com/users/npcode/events{/privacy}","received_events_url":"https://api.github.com/users/npcode/received_events","type":"User","site_admin":false},"body":"addWebhookForm이나 field나 value가 null이면 NPE가 발생할 것 같네요. length()를 호출하는 코드 까지 try 블럭으로 감싸서 NPE가 발생하면 false를 리턴해야 하지 않을까요?","created_at":"2015-06-09T02:57:33Z","updated_at":"2015-06-09T02:57:33Z","html_url":"https://github.com/naver/yobi/pull/905#discussion_r31979667","pull_request_url":"https://api.github.com/repos/naver/yobi/pulls/905","_links":{"self":{"href":"https://api.github.com/repos/naver/yobi/pulls/comments/31979667"},"html":{"href":"https://github.com/naver/yobi/pull/905#discussion_r31979667"},"pull_request":{"href":"https://api.github.com/repos/naver/yobi/pulls/905"}}},"pull_request":{"url":"https://api.github.com/repos/naver/yobi/pulls/905","id":36092623,"html_url":"https://github.com/naver/yobi/pull/905","diff_url":"https://github.com/naver/yobi/pull/905.diff","patch_url":"https://github.com/naver/yobi/pull/905.patch","issue_url":"https://api.github.com/repos/naver/yobi/issues/905","number":905,"state":"open","locked":false,"title":"Implement webhook for push event","user":{"login":"jihwan0321","id":3176340,"avatar_url":"https://avatars.githubusercontent.com/u/3176340?v=3","gravatar_id":"","url":"https://api.github.com/users/jihwan0321","html_url":"https://github.com/jihwan0321","followers_url":"https://api.github.com/users/jihwan0321/followers","following_url":"https://api.github.com/users/jihwan0321/following{/other_user}","gists_url":"https://api.github.com/users/jihwan0321/gists{/gist_id}","starred_url":"https://api.github.com/users/jihwan0321/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jihwan0321/subscriptions","organizations_url":"https://api.github.com/users/jihwan0321/orgs","repos_url":"https://api.github.com/users/jihwan0321/repos","events_url":"https://api.github.com/users/jihwan0321/events{/privacy}","received_events_url":"https://api.github.com/users/jihwan0321/received_events","type":"User","site_admin":false},"body":"Resolves #837, #815 \r\n\r\n- 테스트를 아직 작성하지 않았습니다. 어떤 식으로 작성해야 할지 조언을 주시면, 참고하여 이 PR에 반영하도록 하겠습니다.\r\n- 메시지의 언어 번역이 미흡합니다(특히 ja-JP locale). 각각 어떤 메시지가 들어가야 할지 알려주시면, 수정하도록 하겠습니다.\r\n\r\n----\r\n\r\n안녕하세요, naive한 웹후크 기능을 구현하여 Pull Request를 보냅니다.\r\n![viewsample](https://cloud.githubusercontent.com/assets/3176340/7785547/90751424-01cf-11e5-904f-9f654ed58a07.png)\r\n현재는 push 이벤트만을 받아 hook이 작동하도록 구현해 놓았으며, request를 보낼 URL / secret token을 관리할 수 있는 view를 위와 같이 settings 탭에 띄워 놓았습니다.\r\n\r\npush 이벤트를 받아 전송되는 hook의 Header/Body 예시는 다음과 같습니다.\r\n```\r\n[Header]\r\nContent-Type: application/json; charset=utf-8\r\nConnect-Time: 1\r\nTotal-Route-Time: 0\r\nConnection: close\r\nAccept: */*\r\nUser-Agent: Yobi-Hookshot\r\nX-Request-Id: 8abc3492-f9d8-4009-84ff-d06c6c9c09ca\r\nHost: requestb.in\r\nX-Yobi-Event: hook\r\nContent-Length: 310\r\nVia: 1.1 vegur\r\n\r\n[Body]\r\n{\r\n    \"hook_id\":33,\r\n    \"hook\":{\r\n        \"id\":33,\r\n        \"name\":\"web\",\r\n        \"active\":true,\r\n        \"events\":[\r\n            \"push\"\r\n        ],\r\n        \"config\":{\r\n            \"url\":\"http://requestb.in/pzk5cnpz\",\r\n            \"content_type\":\"json\",\r\n            \"secret\":\"secret-test\"\r\n        },\r\n        \"created_at\":\"2015-05-24T04:21:26Z\"\r\n    },\r\n    \"repository\":{\r\n        \"id\":1,\r\n        \"name\":\"ddd\",\r\n        \"owner\":\"hello\",\r\n        \"html_url\":\"http://localhost:9000/ddd\",\r\n        \"overview\":\"dd\"\r\n    }\r\n}\r\n```\r\n\r\n리뷰 부탁드리겠습니다. 감사합니다.","created_at":"2015-05-23T19:45:13Z","updated_at":"2015-06-09T02:57:33Z","closed_at":null,"merged_at":null,"merge_commit_sha":"bd488b6a4d7e20b6c277cc49129ea304c26d9d32","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/naver/yobi/pulls/905/commits","review_comments_url":"https://api.github.com/repos/naver/yobi/pulls/905/comments","review_comment_url":"https://api.github.com/repos/naver/yobi/pulls/comments{/number}","comments_url":"https://api.github.com/repos/naver/yobi/issues/905/comments","statuses_url":"https://api.github.com/repos/naver/yobi/statuses/c443c3c7b1d568d638b20cb35f49a4bbc2448af4","head":{"label":"jihwan0321:webhook","ref":"webhook","sha":"c443c3c7b1d568d638b20cb35f49a4bbc2448af4","user":{"login":"jihwan0321","id":3176340,"avatar_url":"https://avatars.githubusercontent.com/u/3176340?v=3","gravatar_id":"","url":"https://api.github.com/users/jihwan0321","html_url":"https://github.com/jihwan0321","followers_url":"https://api.github.com/users/jihwan0321/followers","following_url":"https://api.github.com/users/jihwan0321/following{/other_user}","gists_url":"https://api.github.com/users/jihwan0321/gists{/gist_id}","starred_url":"https://api.github.com/users/jihwan0321/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jihwan0321/subscriptions","organizations_url":"https://api.github.com/users/jihwan0321/orgs","repos_url":"https://api.github.com/users/jihwan0321/repos","events_url":"https://api.github.com/users/jihwan0321/events{/privacy}","received_events_url":"https://api.github.com/users/jihwan0321/received_events","type":"User","site_admin":false},"repo":{"id":34104320,"name":"yobi","full_name":"jihwan0321/yobi","owner":{"login":"jihwan0321","id":3176340,"avatar_url":"https://avatars.githubusercontent.com/u/3176340?v=3","gravatar_id":"","url":"https://api.github.com/users/jihwan0321","html_url":"https://github.com/jihwan0321","followers_url":"https://api.github.com/users/jihwan0321/followers","following_url":"https://api.github.com/users/jihwan0321/following{/other_user}","gists_url":"https://api.github.com/users/jihwan0321/gists{/gist_id}","starred_url":"https://api.github.com/users/jihwan0321/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jihwan0321/subscriptions","organizations_url":"https://api.github.com/users/jihwan0321/orgs","repos_url":"https://api.github.com/users/jihwan0321/repos","events_url":"https://api.github.com/users/jihwan0321/events{/privacy}","received_events_url":"https://api.github.com/users/jihwan0321/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jihwan0321/yobi","description":"Project hosting software","fork":true,"url":"https://api.github.com/repos/jihwan0321/yobi","forks_url":"https://api.github.com/repos/jihwan0321/yobi/forks","keys_url":"https://api.github.com/repos/jihwan0321/yobi/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jihwan0321/yobi/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jihwan0321/yobi/teams","hooks_url":"https://api.github.com/repos/jihwan0321/yobi/hooks","issue_events_url":"https://api.github.com/repos/jihwan0321/yobi/issues/events{/number}","events_url":"https://api.github.com/repos/jihwan0321/yobi/events","assignees_url":"https://api.github.com/repos/jihwan0321/yobi/assignees{/user}","branches_url":"https://api.github.com/repos/jihwan0321/yobi/branches{/branch}","tags_url":"https://api.github.com/repos/jihwan0321/yobi/tags","blobs_url":"https://api.github.com/repos/jihwan0321/yobi/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jihwan0321/yobi/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jihwan0321/yobi/git/refs{/sha}","trees_url":"https://api.github.com/repos/jihwan0321/yobi/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jihwan0321/yobi/statuses/{sha}","languages_url":"https://api.github.com/repos/jihwan0321/yobi/languages","stargazers_url":"https://api.github.com/repos/jihwan0321/yobi/stargazers","contributors_url":"https://api.github.com/repos/jihwan0321/yobi/contributors","subscribers_url":"https://api.github.com/repos/jihwan0321/yobi/subscribers","subscription_url":"https://api.github.com/repos/jihwan0321/yobi/subscription","commits_url":"https://api.github.com/repos/jihwan0321/yobi/commits{/sha}","git_commits_url":"https://api.github.com/repos/jihwan0321/yobi/git/commits{/sha}","comments_url":"https://api.github.com/repos/jihwan0321/yobi/comments{/number}","issue_comment_url":"https://api.github.com/repos/jihwan0321/yobi/issues/comments{/number}","contents_url":"https://api.github.com/repos/jihwan0321/yobi/contents/{+path}","compare_url":"https://api.github.com/repos/jihwan0321/yobi/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jihwan0321/yobi/merges","archive_url":"https://api.github.com/repos/jihwan0321/yobi/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jihwan0321/yobi/downloads","issues_url":"https://api.github.com/repos/jihwan0321/yobi/issues{/number}","pulls_url":"https://api.github.com/repos/jihwan0321/yobi/pulls{/number}","milestones_url":"https://api.github.com/repos/jihwan0321/yobi/milestones{/number}","notifications_url":"https://api.github.com/repos/jihwan0321/yobi/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jihwan0321/yobi/labels{/name}","releases_url":"https://api.github.com/repos/jihwan0321/yobi/releases{/id}","created_at":"2015-04-17T08:06:04Z","updated_at":"2015-04-17T08:06:08Z","pushed_at":"2015-06-05T15:25:38Z","git_url":"git://github.com/jihwan0321/yobi.git","ssh_url":"git@github.com:jihwan0321/yobi.git","clone_url":"https://github.com/jihwan0321/yobi.git","svn_url":"https://github.com/jihwan0321/yobi","homepage":"","size":25379,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"naver:next","ref":"next","sha":"3fbca4d7282ec65b06ac1ecb97b28183293a18e6","user":{"login":"naver","id":6589568,"avatar_url":"https://avatars.githubusercontent.com/u/6589568?v=3","gravatar_id":"","url":"https://api.github.com/users/naver","html_url":"https://github.com/naver","followers_url":"https://api.github.com/users/naver/followers","following_url":"https://api.github.com/users/naver/following{/other_user}","gists_url":"https://api.github.com/users/naver/gists{/gist_id}","starred_url":"https://api.github.com/users/naver/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/naver/subscriptions","organizations_url":"https://api.github.com/users/naver/orgs","repos_url":"https://api.github.com/users/naver/repos","events_url":"https://api.github.com/users/naver/events{/privacy}","received_events_url":"https://api.github.com/users/naver/received_events","type":"Organization","site_admin":false},"repo":{"id":5490703,"name":"yobi","full_name":"naver/yobi","owner":{"login":"naver","id":6589568,"avatar_url":"https://avatars.githubusercontent.com/u/6589568?v=3","gravatar_id":"","url":"https://api.github.com/users/naver","html_url":"https://github.com/naver","followers_url":"https://api.github.com/users/naver/followers","following_url":"https://api.github.com/users/naver/following{/other_user}","gists_url":"https://api.github.com/users/naver/gists{/gist_id}","starred_url":"https://api.github.com/users/naver/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/naver/subscriptions","organizations_url":"https://api.github.com/users/naver/orgs","repos_url":"https://api.github.com/users/naver/repos","events_url":"https://api.github.com/users/naver/events{/privacy}","received_events_url":"https://api.github.com/users/naver/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/naver/yobi","description":"Project hosting software","fork":false,"url":"https://api.github.com/repos/naver/yobi","forks_url":"https://api.github.com/repos/naver/yobi/forks","keys_url":"https://api.github.com/repos/naver/yobi/keys{/key_id}","collaborators_url":"https://api.github.com/repos/naver/yobi/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/naver/yobi/teams","hooks_url":"https://api.github.com/repos/naver/yobi/hooks","issue_events_url":"https://api.github.com/repos/naver/yobi/issues/events{/number}","events_url":"https://api.github.com/repos/naver/yobi/events","assignees_url":"https://api.github.com/repos/naver/yobi/assignees{/user}","branches_url":"https://api.github.com/repos/naver/yobi/branches{/branch}","tags_url":"https://api.github.com/repos/naver/yobi/tags","blobs_url":"https://api.github.com/repos/naver/yobi/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/naver/yobi/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/naver/yobi/git/refs{/sha}","trees_url":"https://api.github.com/repos/naver/yobi/git/trees{/sha}","statuses_url":"https://api.github.com/repos/naver/yobi/statuses/{sha}","languages_url":"https://api.github.com/repos/naver/yobi/languages","stargazers_url":"https://api.github.com/repos/naver/yobi/stargazers","contributors_url":"https://api.github.com/repos/naver/yobi/contributors","subscribers_url":"https://api.github.com/repos/naver/yobi/subscribers","subscription_url":"https://api.github.com/repos/naver/yobi/subscription","commits_url":"https://api.github.com/repos/naver/yobi/commits{/sha}","git_commits_url":"https://api.github.com/repos/naver/yobi/git/commits{/sha}","comments_url":"https://api.github.com/repos/naver/yobi/comments{/number}","issue_comment_url":"https://api.github.com/repos/naver/yobi/issues/comments{/number}","contents_url":"https://api.github.com/repos/naver/yobi/contents/{+path}","compare_url":"https://api.github.com/repos/naver/yobi/compare/{base}...{head}","merges_url":"https://api.github.com/repos/naver/yobi/merges","archive_url":"https://api.github.com/repos/naver/yobi/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/naver/yobi/downloads","issues_url":"https://api.github.com/repos/naver/yobi/issues{/number}","pulls_url":"https://api.github.com/repos/naver/yobi/pulls{/number}","milestones_url":"https://api.github.com/repos/naver/yobi/milestones{/number}","notifications_url":"https://api.github.com/repos/naver/yobi/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/naver/yobi/labels{/name}","releases_url":"https://api.github.com/repos/naver/yobi/releases{/id}","created_at":"2012-08-21T05:09:08Z","updated_at":"2015-06-08T16:25:56Z","pushed_at":"2015-06-08T12:19:39Z","git_url":"git://github.com/naver/yobi.git","ssh_url":"git@github.com:naver/yobi.git","clone_url":"https://github.com/naver/yobi.git","svn_url":"https://github.com/naver/yobi","homepage":"","size":32999,"stargazers_count":317,"watchers_count":317,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":148,"mirror_url":null,"open_issues_count":165,"forks":148,"open_issues":165,"watchers":317,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/naver/yobi/pulls/905"},"html":{"href":"https://github.com/naver/yobi/pull/905"},"issue":{"href":"https://api.github.com/repos/naver/yobi/issues/905"},"comments":{"href":"https://api.github.com/repos/naver/yobi/issues/905/comments"},"review_comments":{"href":"https://api.github.com/repos/naver/yobi/pulls/905/comments"},"review_comment":{"href":"https://api.github.com/repos/naver/yobi/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/naver/yobi/pulls/905/commits"},"statuses":{"href":"https://api.github.com/repos/naver/yobi/statuses/c443c3c7b1d568d638b20cb35f49a4bbc2448af4"}}}}