##// END OF EJS Templates
integrations: add webhook integration
dan -
r444:3db70ed6 default
parent child Browse files
Show More
@@ -0,0 +1,106 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 from __future__ import unicode_literals
22
23 import logging
24 import requests
25 import colander
26 from celery.task import task
27 from mako.template import Template
28
29 from rhodecode import events
30 from rhodecode.translation import lazy_ugettext
31 from rhodecode.integrations.types.base import IntegrationTypeBase
32 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
33
34 log = logging.getLogger()
35
36
37 class WebhookSettingsSchema(IntegrationSettingsSchemaBase):
38 url = colander.SchemaNode(
39 colander.String(),
40 title=lazy_ugettext('Webhook URL'),
41 description=lazy_ugettext('URL of the webhook to receive POST event.'),
42 default='',
43 validator=colander.url,
44 placeholder='https://www.example.com/webhook',
45 widget='string'
46 )
47 secret_token = colander.SchemaNode(
48 colander.String(),
49 title=lazy_ugettext('Secret Token'),
50 description=lazy_ugettext('String used to validate received payloads.'),
51 default='',
52 placeholder='secret_token',
53 widget='string'
54 )
55
56
57 class WebhookIntegrationType(IntegrationTypeBase):
58 key = 'webhook'
59 display_name = lazy_ugettext('Webhook')
60 valid_events = [
61 events.PullRequestCloseEvent,
62 events.PullRequestMergeEvent,
63 events.PullRequestUpdateEvent,
64 events.PullRequestCommentEvent,
65 events.PullRequestReviewEvent,
66 events.PullRequestCreateEvent,
67 events.RepoPushEvent,
68 events.RepoCreateEvent,
69 ]
70
71 @classmethod
72 def settings_schema(cls):
73 schema = WebhookSettingsSchema()
74 schema.add(colander.SchemaNode(
75 colander.Set(),
76 widget='checkbox_list',
77 choices=sorted([e.name for e in cls.valid_events]),
78 description="Events activated for this integration",
79 name='events'
80 ))
81 return schema
82
83 def send_event(self, event):
84 log.debug('handling event %s with webhook integration %s',
85 event.name, self)
86
87 if event.__class__ not in self.valid_events:
88 log.debug('event not valid: %r' % event)
89 return
90
91 if event.name not in self.settings['events']:
92 log.debug('event ignored: %r' % event)
93 return
94
95 data = event.as_dict()
96 post_to_webhook(data, self.settings)
97
98
99 @task(ignore_result=True)
100 def post_to_webhook(data, settings):
101 log.debug('sending event:%s to webhook %s', data['name'], settings['url'])
102 resp = requests.post(settings['url'], json={
103 'token': settings['secret_token'],
104 'event': data
105 })
106 resp.raise_for_status() # raise exception on a failed request
@@ -1,52 +1,57 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 from rhodecode.integrations.registry import IntegrationTypeRegistry
22 from rhodecode.integrations.registry import IntegrationTypeRegistry
23 from rhodecode.integrations.types import slack
23 from rhodecode.integrations.types import webhook, slack
24
24
25 log = logging.getLogger(__name__)
25 log = logging.getLogger(__name__)
26
26
27
27
28 # TODO: dan: This is currently global until we figure out what to do about
28 # TODO: dan: This is currently global until we figure out what to do about
29 # VCS's not having a pyramid context - move it to pyramid app configuration
29 # VCS's not having a pyramid context - move it to pyramid app configuration
30 # includeme level later to allow per instance integration setup
30 # includeme level later to allow per instance integration setup
31 integration_type_registry = IntegrationTypeRegistry()
31 integration_type_registry = IntegrationTypeRegistry()
32 integration_type_registry.register_integration_type(slack.SlackIntegrationType)
32
33 integration_type_registry.register_integration_type(
34 webhook.WebhookIntegrationType)
35 integration_type_registry.register_integration_type(
36 slack.SlackIntegrationType)
37
33
38
34 def integrations_event_handler(event):
39 def integrations_event_handler(event):
35 """
40 """
36 Takes an event and passes it to all enabled integrations
41 Takes an event and passes it to all enabled integrations
37 """
42 """
38 from rhodecode.model.integration import IntegrationModel
43 from rhodecode.model.integration import IntegrationModel
39
44
40 integration_model = IntegrationModel()
45 integration_model = IntegrationModel()
41 integrations = integration_model.get_for_event(event)
46 integrations = integration_model.get_for_event(event)
42 for integration in integrations:
47 for integration in integrations:
43 try:
48 try:
44 integration_model.send_event(integration, event)
49 integration_model.send_event(integration, event)
45 except Exception:
50 except Exception:
46 log.exception(
51 log.exception(
47 'failure occured when sending event %s to integration %s' % (
52 'failure occured when sending event %s to integration %s' % (
48 event, integration))
53 event, integration))
49
54
50
55
51 def includeme(config):
56 def includeme(config):
52 config.include('rhodecode.integrations.routes')
57 config.include('rhodecode.integrations.routes')
General Comments 0
You need to be logged in to leave comments. Login now