ci: do not error when nothing with nitunit_some
[nit.git] / lib / github / hooks.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Github hook event listening with `nitcorn`.
16 #
17 # Usage:
18 #
19 # ~~~
20 # import github::hooks
21 #
22 # # A simple hook listener that print received events in stdout.
23 # class LogHookListener
24 # super HookListener
25 #
26 # # Use double dispatch to implement log behavior.
27 # redef fun apply_event(event) do event.log_event(self)
28 # end
29 #
30 # redef class GithubEvent
31 # # Log this event.
32 # #
33 # # Do nothing by default.
34 # fun log_event(l: LogHookListener) do end
35 # end
36 #
37 # redef class CommitCommentEvent
38 #
39 # redef fun log_event(l) do
40 # print "new comment on commit {comment.commit_id}"
41 # end
42 # end
43 #
44 # var api = new GithubAPI(get_github_oauth)
45 # var listener = new LogHookListener(api, "127.0.0.1", 8080)
46 # # listener.listen # uncomment to start listening
47 # ~~~
48 module hooks
49
50 import events
51 import nitcorn
52
53 # A nitcorn listener for Github hooks.
54 abstract class HookListener
55
56 # Api client used to perform Github API requests.
57 var api: GithubAPI
58
59 # Host to listen.
60 var host: String
61
62 # Port to listen.
63 var port: Int
64
65 # VirtualHost listened
66 private var vh: VirtualHost is noinit
67
68 init do
69 vh = new VirtualHost("{host}:{port}")
70 vh.routes.add new Route("/", new HookAction(self))
71 end
72
73 # Verbosity level (0: quiet, 1: debug).
74 # Default: 0
75 var verbosity = 0
76
77 # Print `message` if `lvl` <= `verbosity`
78 fun message(lvl: Int, message: String) do
79 if lvl <= verbosity then print message
80 end
81
82 # Start listening and responding to event.
83 fun listen do
84 message(1, "Hook listening on {host}:{port}")
85 var factory = new HttpFactory.and_libevent
86 factory.config.virtual_hosts.add vh
87 factory.run
88 end
89
90 # How to build events from received json objects.
91 fun event_factory(kind: String, json: JsonObject): GithubEvent do
92 if kind == "commit_comment" then
93 return api.deserialize(json.to_json).as(CommitCommentEvent)
94 else if kind == "create" then
95 return api.deserialize(json.to_json).as(CreateEvent)
96 else if kind == "delete" then
97 return api.deserialize(json.to_json).as(DeleteEvent)
98 else if kind == "deployment" then
99 return api.deserialize(json.to_json).as(DeploymentEvent)
100 else if kind == "deployment_status" then
101 return api.deserialize(json.to_json).as(DeploymentStatusEvent)
102 else if kind == "fork" then
103 return api.deserialize(json.to_json).as(ForkEvent)
104 else if kind == "issues" then
105 return api.deserialize(json.to_json).as(IssuesEvent)
106 else if kind == "issue_comment" then
107 return api.deserialize(json.to_json).as(IssueCommentEvent)
108 else if kind == "member" then
109 return api.deserialize(json.to_json).as(MemberEvent)
110 else if kind == "pull_request" then
111 return api.deserialize(json.to_json).as(PullRequestEvent)
112 else if kind == "pull_request_review_comment" then
113 return api.deserialize(json.to_json).as(PullRequestReviewCommentEvent)
114 else if kind == "push" then
115 return api.deserialize(json.to_json).as(PushEvent)
116 else if kind == "status" then
117 return api.deserialize(json.to_json).as(StatusEvent)
118 else
119 return api.deserialize(json.to_json).as(GithubEvent)
120 end
121 end
122
123 # What to do when we receive an event from a hook?
124 fun apply_event(event: GithubEvent) is abstract
125 end
126
127 # A nitcorn action dedicated to GitHub hook listening.
128 private class HookAction
129 super Action
130
131 # Listener that contains this action.
132 #
133 # The `listener` is used for its `event_factory` method
134 # and the `apply_event`.
135 var listener: HookListener
136
137 # Parse hook request then call `listener.apply_event`.
138 redef fun answer(request, uri) do
139 # get event type
140 var kind = request.header.get_or_null("X-GitHub-Event")
141 if kind == null then return new HttpResponse(403)
142 # get POST object
143 var obj = request.body.parse_json
144 if not obj isa JsonObject then return new HttpResponse(403)
145 # parse event
146 var event = listener.event_factory(kind, obj)
147 listener.apply_event(event)
148 return new HttpResponse(200)
149 end
150 end