lib/github: uses nitcorn to listen Github hook events.
authorAlexandre Terrasa <alexandre@moz-code.org>
Thu, 22 Jan 2015 11:03:07 +0000 (12:03 +0100)
committerAlexandre Terrasa <alexandre@moz-code.org>
Thu, 22 Jan 2015 11:03:07 +0000 (12:03 +0100)
Signed-off-by: Alexandre Terrasa <alexandre@moz-code.org>

lib/github/hooks.nit [new file with mode: 0644]

diff --git a/lib/github/hooks.nit b/lib/github/hooks.nit
new file mode 100644 (file)
index 0000000..351042b
--- /dev/null
@@ -0,0 +1,150 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Github hook event listening with `nitcorn`.
+#
+# Usage:
+#
+# ~~~
+# import github::hooks
+#
+# # A simple hook listener that print received events in stdout.
+# class LogHookListener
+#    super HookListener
+#
+#    # Use double dispatch to implement log behavior.
+#    redef fun apply_event(event) do event.log_event(self)
+# end
+#
+# redef class GithubEvent
+#    # Log this event.
+#    #
+#    # Do nothing by default.
+#    fun log_event(l: LogHookListener) do end
+# end
+#
+# redef class CommitCommentEvent
+#
+#    redef fun log_event(l) do
+#        print "new comment on commit {comment.commit.sha}"
+#    end
+# end
+#
+# var api = new GithubAPI(get_github_oauth)
+# var listener = new LogHookListener(api, "127.0.0.1", 8080)
+# # listener.listen # uncomment to start listening
+# ~~~
+module hooks
+
+import events
+import nitcorn
+
+# A nitcorn listener for Github hooks.
+class HookListener
+
+       # Api client used to perform Github API requests.
+       var api: GithubAPI
+
+       # Host to listen.
+       var host: String
+
+       # Port to listen.
+       var port: Int
+
+       # VirtualHost listened
+       private var vh: VirtualHost is noinit
+
+       init do
+               vh = new VirtualHost("{host}:{port}")
+               vh.routes.add new Route("/", new HookAction(self))
+       end
+
+       # Verbosity level (0: quiet, 1: debug).
+       # Default: 0
+       var verbosity = 0
+
+       # Print `message` if `lvl` <= `verbosity`
+       fun message(lvl: Int, message: String) do
+               if lvl <= verbosity then print message
+       end
+
+       # Start listening and responding to event.
+       fun listen do
+               message(1, "Hook listening on {host}:{port}")
+               var factory = new HttpFactory.and_libevent
+               factory.config.virtual_hosts.add vh
+               factory.run
+       end
+
+       # How to build events from received json objects.
+       fun event_factory(kind: String, json: JsonObject): GithubEvent do
+               if kind == "commit_comment" then
+                       return new CommitCommentEvent.from_json(api, json)
+               else if kind == "create" then
+                       return new CreateEvent.from_json(api, json)
+               else if kind == "delete" then
+                       return new DeleteEvent.from_json(api, json)
+               else if kind == "deployment" then
+                       return new DeploymentEvent.from_json(api, json)
+               else if kind == "deployment_status" then
+                       return new DeploymentStatusEvent.from_json(api, json)
+               else if kind == "fork" then
+                       return new ForkEvent.from_json(api, json)
+               else if kind == "issues" then
+                       return new IssuesEvent.from_json(api, json)
+               else if kind == "issue_comment" then
+                       return new IssueCommentEvent.from_json(api, json)
+               else if kind == "member" then
+                       return new MemberEvent.from_json(api, json)
+               else if kind == "pull_request" then
+                       return new PullRequestEvent.from_json(api, json)
+               else if kind == "pull_request_review_comment" then
+                       return new PullRequestReviewCommentEvent.from_json(api, json)
+               else if kind == "push" then
+                       return new PushEvent.from_json(api, json)
+               else if kind == "status" then
+                       return new StatusEvent.from_json(api, json)
+               else
+                       return new GithubEvent.from_json(api, json)
+               end
+       end
+
+       # What to do when we receive an event from a hook?
+       fun apply_event(event: GithubEvent) is abstract
+end
+
+# A nitcorn action dedicated to GitHub hook listening.
+private class HookAction
+       super Action
+
+       # Listener that contains this action.
+       #
+       # The `listener` is used for its `event_factory` method
+       # and the `apply_event`.
+       var listener: HookListener
+
+       # Parse hook request then call `listener.apply_event`.
+       redef fun answer(request, uri) do
+               # get event type
+               var kind = request.header.get_or_null("X-GitHub-Event")
+               if kind == null then return new HttpResponse(403)
+               # get POST object
+               var obj = request.body.parse_json
+               if not obj isa JsonObject then return new HttpResponse(403)
+               # parse event
+               var event = listener.event_factory(kind, obj)
+               listener.apply_event(event)
+               return new HttpResponse(200)
+       end
+end