Merge lp:~cjwatson/launchpad/webhook-default-event-types into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 17790
Proposed branch: lp:~cjwatson/launchpad/webhook-default-event-types
Merge into: lp:launchpad
Diff against target: 260 lines (+97/-12)
7 files modified
lib/lp/code/model/branch.py (+4/-0)
lib/lp/code/model/gitrepository.py (+4/-0)
lib/lp/services/webhooks/browser.py (+4/-1)
lib/lp/services/webhooks/configure.zcml (+16/-7)
lib/lp/services/webhooks/interfaces.py (+34/-4)
lib/lp/services/webhooks/model.py (+9/-0)
lib/lp/services/webhooks/tests/test_browser.py (+26/-0)
To merge this branch: bzr merge lp:~cjwatson/launchpad/webhook-default-event-types
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+273054@code.launchpad.net

Commit message

Only offer webhook event types that are valid for the target, and set reasonable defaults.

Description of the change

Only offer webhook event types that are valid for the target, and set reasonable defaults (currently just all the valid event types, but in future it may be a subset in some cases).

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

Events are currently webhook-specific, and those properties aren't exported, so I'd say valid_webhook_event_types and default_webhook_event_types.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/model/branch.py'
2--- lib/lp/code/model/branch.py 2015-09-29 15:53:47 +0000
3+++ lib/lp/code/model/branch.py 2015-10-05 11:12:53 +0000
4@@ -205,6 +205,10 @@
5 enum=InformationType, default=InformationType.PUBLIC)
6
7 @property
8+ def valid_webhook_event_types(self):
9+ return ["bzr:push:0.1"]
10+
11+ @property
12 def private(self):
13 return self.information_type in PRIVATE_INFORMATION_TYPES
14
15
16=== modified file 'lib/lp/code/model/gitrepository.py'
17--- lib/lp/code/model/gitrepository.py 2015-09-21 10:08:08 +0000
18+++ lib/lp/code/model/gitrepository.py 2015-10-05 11:12:53 +0000
19@@ -234,6 +234,10 @@
20 self.owner_default = False
21 self.target_default = False
22
23+ @property
24+ def valid_webhook_event_types(self):
25+ return ["git:push:0.1"]
26+
27 # Marker for references to Git URL layouts: ##GITNAMESPACE##
28 @property
29 def unique_name(self):
30
31=== modified file 'lib/lp/services/webhooks/browser.py'
32--- lib/lp/services/webhooks/browser.py 2015-09-08 06:55:18 +0000
33+++ lib/lp/services/webhooks/browser.py 2015-10-05 11:12:53 +0000
34@@ -127,7 +127,10 @@
35
36 @property
37 def initial_values(self):
38- return {'active': True}
39+ return {
40+ 'active': True,
41+ 'event_types': self.context.default_webhook_event_types,
42+ }
43
44 @property
45 def cancel_url(self):
46
47=== modified file 'lib/lp/services/webhooks/configure.zcml'
48--- lib/lp/services/webhooks/configure.zcml 2015-08-10 06:39:16 +0000
49+++ lib/lp/services/webhooks/configure.zcml 2015-10-05 11:12:53 +0000
50@@ -1,4 +1,4 @@
51-<!-- Copyright 2010 Canonical Ltd. This software is licensed under the
52+<!-- Copyright 2015 Canonical Ltd. This software is licensed under the
53 GNU Affero General Public License version 3 (see the file LICENSE).
54 -->
55
56@@ -22,12 +22,21 @@
57 <allow interface="lp.services.webhooks.interfaces.IWebhookSet"/>
58 </securedutility>
59 <securedutility
60- name="WebhookEventType"
61- component="lp.services.webhooks.interfaces.WebhookEventTypeVocabulary"
62- provides="zope.schema.interfaces.IVocabularyFactory">
63- <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
64- </securedutility>
65- <class class="lp.services.webhooks.interfaces.WebhookEventTypeVocabulary">
66+ name="AnyWebhookEventType"
67+ component="lp.services.webhooks.interfaces.AnyWebhookEventTypeVocabulary"
68+ provides="zope.schema.interfaces.IVocabularyFactory">
69+ <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
70+ </securedutility>
71+ <class class="lp.services.webhooks.interfaces.AnyWebhookEventTypeVocabulary">
72+ <allow interface="zope.schema.interfaces.IVocabularyTokenized"/>
73+ </class>
74+ <securedutility
75+ name="ValidWebhookEventType"
76+ component="lp.services.webhooks.interfaces.ValidWebhookEventTypeVocabulary"
77+ provides="zope.schema.interfaces.IVocabularyFactory">
78+ <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
79+ </securedutility>
80+ <class class="lp.services.webhooks.interfaces.ValidWebhookEventTypeVocabulary">
81 <allow interface="zope.schema.interfaces.IVocabularyTokenized"/>
82 </class>
83
84
85=== modified file 'lib/lp/services/webhooks/interfaces.py'
86--- lib/lp/services/webhooks/interfaces.py 2015-09-24 13:44:02 +0000
87+++ lib/lp/services/webhooks/interfaces.py 2015-10-05 11:12:53 +0000
88@@ -6,6 +6,7 @@
89 __metaclass__ = type
90
91 __all__ = [
92+ 'AnyWebhookEventTypeVocabulary',
93 'IWebhook',
94 'IWebhookClient',
95 'IWebhookDeliveryJob',
96@@ -14,10 +15,11 @@
97 'IWebhookJobSource',
98 'IWebhookSet',
99 'IWebhookTarget',
100+ 'WEBHOOK_EVENT_TYPES',
101 'WebhookDeliveryFailure',
102 'WebhookDeliveryRetry',
103 'WebhookFeatureDisabled',
104- 'WebhookEventTypeVocabulary',
105+ 'ValidWebhookEventTypeVocabulary',
106 ]
107
108 import httplib
109@@ -95,13 +97,28 @@
110 pass
111
112
113-class WebhookEventTypeVocabulary(SimpleVocabulary):
114+class AnyWebhookEventTypeVocabulary(SimpleVocabulary):
115
116 def __init__(self, context):
117 terms = [
118 self.createTerm(key, key, value)
119 for key, value in WEBHOOK_EVENT_TYPES.iteritems()]
120- super(WebhookEventTypeVocabulary, self).__init__(terms)
121+ super(AnyWebhookEventTypeVocabulary, self).__init__(terms)
122+
123+
124+class ValidWebhookEventTypeVocabulary(SimpleVocabulary):
125+
126+ def __init__(self, context):
127+ # When creating a webhook, the context is the target; when editing
128+ # an existing webhook, the context is the webhook itself.
129+ if IWebhook.providedBy(context):
130+ target = context.target
131+ else:
132+ target = context
133+ terms = [
134+ self.createTerm(key, key, WEBHOOK_EVENT_TYPES[key])
135+ for key in target.valid_webhook_event_types]
136+ super(ValidWebhookEventTypeVocabulary, self).__init__(terms)
137
138
139 class IWebhook(Interface):
140@@ -115,7 +132,7 @@
141 required=True, readonly=True,
142 description=_("The object for which this webhook receives events.")))
143 event_types = exported(List(
144- Choice(vocabulary='WebhookEventType'), title=_("Event types"),
145+ Choice(vocabulary='ValidWebhookEventType'), title=_("Event types"),
146 required=True, readonly=False))
147 registrant = exported(Reference(
148 title=_("Registrant"), schema=IPerson, required=True, readonly=True,
149@@ -195,6 +212,19 @@
150 value_type=Reference(schema=IWebhook),
151 readonly=True)))
152
153+ valid_webhook_event_types = List(
154+ Choice(vocabulary='AnyWebhookEventType'), title=_("Valid event types"),
155+ description=_("Valid event types for this object type."),
156+ required=True, readonly=True)
157+
158+ default_webhook_event_types = List(
159+ Choice(vocabulary='ValidWebhookEventType'),
160+ title=_("Default event types"),
161+ description=_(
162+ "Default event types for new webhooks attached to this object "
163+ "type."),
164+ required=True, readonly=True)
165+
166 @call_with(registrant=REQUEST_USER)
167 @export_factory_operation(
168 IWebhook, ['delivery_url', 'active', 'event_types', 'secret'])
169
170=== modified file 'lib/lp/services/webhooks/model.py'
171--- lib/lp/services/webhooks/model.py 2015-09-24 13:44:02 +0000
172+++ lib/lp/services/webhooks/model.py 2015-10-05 11:12:53 +0000
173@@ -66,6 +66,7 @@
174 IWebhookJob,
175 IWebhookJobSource,
176 IWebhookSet,
177+ WEBHOOK_EVENT_TYPES,
178 WebhookDeliveryFailure,
179 WebhookDeliveryRetry,
180 WebhookFeatureDisabled,
181@@ -223,6 +224,14 @@
182 getUtility(IWebhookSet).findByTarget(self),
183 pre_iter_hook=preload_registrants)
184
185+ @property
186+ def valid_webhook_event_types(self):
187+ return sorted(WEBHOOK_EVENT_TYPES)
188+
189+ @property
190+ def default_webhook_event_types(self):
191+ return self.valid_webhook_event_types
192+
193 def newWebhook(self, registrant, delivery_url, event_types, active=True,
194 secret=None):
195 if not getFeatureFlag('webhooks.new.enabled'):
196
197=== modified file 'lib/lp/services/webhooks/tests/test_browser.py'
198--- lib/lp/services/webhooks/tests/test_browser.py 2015-09-24 13:44:02 +0000
199+++ lib/lp/services/webhooks/tests/test_browser.py 2015-10-05 11:12:53 +0000
200@@ -25,6 +25,7 @@
201 )
202 from lp.testing.layers import DatabaseFunctionalLayer
203 from lp.testing.matchers import HasQueryCount
204+from lp.testing.pages import extract_text
205 from lp.testing.views import create_view
206
207
208@@ -51,6 +52,7 @@
209 class GitRepositoryTestHelpers:
210
211 event_type = "git:push:0.1"
212+ expected_event_types = [("git:push:0.1", "Git push")]
213
214 def makeTarget(self):
215 return self.factory.makeGitRepository()
216@@ -59,6 +61,7 @@
217 class BranchTestHelpers:
218
219 event_type = "bzr:push:0.1"
220+ expected_event_types = [("bzr:push:0.1", "Bazaar push")]
221
222 def makeTarget(self):
223 return self.factory.makeBranch()
224@@ -204,6 +207,18 @@
225 ['delivery_url'], [error.field_name for error in view.errors])
226 self.assertIs(None, self.target.webhooks.one())
227
228+ def test_event_types(self):
229+ # Only event types that are valid for the target are offered.
230+ browser = self.getUserBrowser(
231+ canonical_url(self.target, view_name="+new-webhook"),
232+ user=self.owner)
233+ event_types = browser.getControl(name="field.event_types")
234+ display_options = [
235+ extract_text(option) for option in event_types.displayOptions]
236+ self.assertContentEqual(
237+ self.expected_event_types,
238+ zip(event_types.options, display_options))
239+
240
241 class TestWebhookAddViewGitRepository(
242 TestWebhookAddViewBase, GitRepositoryTestHelpers, TestCaseWithFactory):
243@@ -289,6 +304,17 @@
244 active=True,
245 event_types=[]))
246
247+ def test_event_types(self):
248+ # Only event types that are valid for the target are offered.
249+ browser = self.getUserBrowser(
250+ canonical_url(self.webhook, view_name="+index"), user=self.owner)
251+ event_types = browser.getControl(name="field.event_types")
252+ display_options = [
253+ extract_text(option) for option in event_types.displayOptions]
254+ self.assertContentEqual(
255+ self.expected_event_types,
256+ zip(event_types.options, display_options))
257+
258
259 class TestWebhookViewGitRepository(
260 TestWebhookViewBase, GitRepositoryTestHelpers, TestCaseWithFactory):