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
=== modified file 'lib/lp/code/model/branch.py'
--- lib/lp/code/model/branch.py 2015-09-29 15:53:47 +0000
+++ lib/lp/code/model/branch.py 2015-10-05 11:12:53 +0000
@@ -205,6 +205,10 @@
205 enum=InformationType, default=InformationType.PUBLIC)205 enum=InformationType, default=InformationType.PUBLIC)
206206
207 @property207 @property
208 def valid_webhook_event_types(self):
209 return ["bzr:push:0.1"]
210
211 @property
208 def private(self):212 def private(self):
209 return self.information_type in PRIVATE_INFORMATION_TYPES213 return self.information_type in PRIVATE_INFORMATION_TYPES
210214
211215
=== modified file 'lib/lp/code/model/gitrepository.py'
--- lib/lp/code/model/gitrepository.py 2015-09-21 10:08:08 +0000
+++ lib/lp/code/model/gitrepository.py 2015-10-05 11:12:53 +0000
@@ -234,6 +234,10 @@
234 self.owner_default = False234 self.owner_default = False
235 self.target_default = False235 self.target_default = False
236236
237 @property
238 def valid_webhook_event_types(self):
239 return ["git:push:0.1"]
240
237 # Marker for references to Git URL layouts: ##GITNAMESPACE##241 # Marker for references to Git URL layouts: ##GITNAMESPACE##
238 @property242 @property
239 def unique_name(self):243 def unique_name(self):
240244
=== modified file 'lib/lp/services/webhooks/browser.py'
--- lib/lp/services/webhooks/browser.py 2015-09-08 06:55:18 +0000
+++ lib/lp/services/webhooks/browser.py 2015-10-05 11:12:53 +0000
@@ -127,7 +127,10 @@
127127
128 @property128 @property
129 def initial_values(self):129 def initial_values(self):
130 return {'active': True}130 return {
131 'active': True,
132 'event_types': self.context.default_webhook_event_types,
133 }
131134
132 @property135 @property
133 def cancel_url(self):136 def cancel_url(self):
134137
=== modified file 'lib/lp/services/webhooks/configure.zcml'
--- lib/lp/services/webhooks/configure.zcml 2015-08-10 06:39:16 +0000
+++ lib/lp/services/webhooks/configure.zcml 2015-10-05 11:12:53 +0000
@@ -1,4 +1,4 @@
1<!-- Copyright 2010 Canonical Ltd. This software is licensed under the1<!-- Copyright 2015 Canonical Ltd. This software is licensed under the
2 GNU Affero General Public License version 3 (see the file LICENSE).2 GNU Affero General Public License version 3 (see the file LICENSE).
3-->3-->
44
@@ -22,12 +22,21 @@
22 <allow interface="lp.services.webhooks.interfaces.IWebhookSet"/>22 <allow interface="lp.services.webhooks.interfaces.IWebhookSet"/>
23 </securedutility>23 </securedutility>
24 <securedutility24 <securedutility
25 name="WebhookEventType"25 name="AnyWebhookEventType"
26 component="lp.services.webhooks.interfaces.WebhookEventTypeVocabulary"26 component="lp.services.webhooks.interfaces.AnyWebhookEventTypeVocabulary"
27 provides="zope.schema.interfaces.IVocabularyFactory">27 provides="zope.schema.interfaces.IVocabularyFactory">
28 <allow interface="zope.schema.interfaces.IVocabularyFactory"/>28 <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
29 </securedutility>29 </securedutility>
30 <class class="lp.services.webhooks.interfaces.WebhookEventTypeVocabulary">30 <class class="lp.services.webhooks.interfaces.AnyWebhookEventTypeVocabulary">
31 <allow interface="zope.schema.interfaces.IVocabularyTokenized"/>
32 </class>
33 <securedutility
34 name="ValidWebhookEventType"
35 component="lp.services.webhooks.interfaces.ValidWebhookEventTypeVocabulary"
36 provides="zope.schema.interfaces.IVocabularyFactory">
37 <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
38 </securedutility>
39 <class class="lp.services.webhooks.interfaces.ValidWebhookEventTypeVocabulary">
31 <allow interface="zope.schema.interfaces.IVocabularyTokenized"/>40 <allow interface="zope.schema.interfaces.IVocabularyTokenized"/>
32 </class>41 </class>
3342
3443
=== modified file 'lib/lp/services/webhooks/interfaces.py'
--- lib/lp/services/webhooks/interfaces.py 2015-09-24 13:44:02 +0000
+++ lib/lp/services/webhooks/interfaces.py 2015-10-05 11:12:53 +0000
@@ -6,6 +6,7 @@
6__metaclass__ = type6__metaclass__ = type
77
8__all__ = [8__all__ = [
9 'AnyWebhookEventTypeVocabulary',
9 'IWebhook',10 'IWebhook',
10 'IWebhookClient',11 'IWebhookClient',
11 'IWebhookDeliveryJob',12 'IWebhookDeliveryJob',
@@ -14,10 +15,11 @@
14 'IWebhookJobSource',15 'IWebhookJobSource',
15 'IWebhookSet',16 'IWebhookSet',
16 'IWebhookTarget',17 'IWebhookTarget',
18 'WEBHOOK_EVENT_TYPES',
17 'WebhookDeliveryFailure',19 'WebhookDeliveryFailure',
18 'WebhookDeliveryRetry',20 'WebhookDeliveryRetry',
19 'WebhookFeatureDisabled',21 'WebhookFeatureDisabled',
20 'WebhookEventTypeVocabulary',22 'ValidWebhookEventTypeVocabulary',
21 ]23 ]
2224
23import httplib25import httplib
@@ -95,13 +97,28 @@
95 pass97 pass
9698
9799
98class WebhookEventTypeVocabulary(SimpleVocabulary):100class AnyWebhookEventTypeVocabulary(SimpleVocabulary):
99101
100 def __init__(self, context):102 def __init__(self, context):
101 terms = [103 terms = [
102 self.createTerm(key, key, value)104 self.createTerm(key, key, value)
103 for key, value in WEBHOOK_EVENT_TYPES.iteritems()]105 for key, value in WEBHOOK_EVENT_TYPES.iteritems()]
104 super(WebhookEventTypeVocabulary, self).__init__(terms)106 super(AnyWebhookEventTypeVocabulary, self).__init__(terms)
107
108
109class ValidWebhookEventTypeVocabulary(SimpleVocabulary):
110
111 def __init__(self, context):
112 # When creating a webhook, the context is the target; when editing
113 # an existing webhook, the context is the webhook itself.
114 if IWebhook.providedBy(context):
115 target = context.target
116 else:
117 target = context
118 terms = [
119 self.createTerm(key, key, WEBHOOK_EVENT_TYPES[key])
120 for key in target.valid_webhook_event_types]
121 super(ValidWebhookEventTypeVocabulary, self).__init__(terms)
105122
106123
107class IWebhook(Interface):124class IWebhook(Interface):
@@ -115,7 +132,7 @@
115 required=True, readonly=True,132 required=True, readonly=True,
116 description=_("The object for which this webhook receives events.")))133 description=_("The object for which this webhook receives events.")))
117 event_types = exported(List(134 event_types = exported(List(
118 Choice(vocabulary='WebhookEventType'), title=_("Event types"),135 Choice(vocabulary='ValidWebhookEventType'), title=_("Event types"),
119 required=True, readonly=False))136 required=True, readonly=False))
120 registrant = exported(Reference(137 registrant = exported(Reference(
121 title=_("Registrant"), schema=IPerson, required=True, readonly=True,138 title=_("Registrant"), schema=IPerson, required=True, readonly=True,
@@ -195,6 +212,19 @@
195 value_type=Reference(schema=IWebhook),212 value_type=Reference(schema=IWebhook),
196 readonly=True)))213 readonly=True)))
197214
215 valid_webhook_event_types = List(
216 Choice(vocabulary='AnyWebhookEventType'), title=_("Valid event types"),
217 description=_("Valid event types for this object type."),
218 required=True, readonly=True)
219
220 default_webhook_event_types = List(
221 Choice(vocabulary='ValidWebhookEventType'),
222 title=_("Default event types"),
223 description=_(
224 "Default event types for new webhooks attached to this object "
225 "type."),
226 required=True, readonly=True)
227
198 @call_with(registrant=REQUEST_USER)228 @call_with(registrant=REQUEST_USER)
199 @export_factory_operation(229 @export_factory_operation(
200 IWebhook, ['delivery_url', 'active', 'event_types', 'secret'])230 IWebhook, ['delivery_url', 'active', 'event_types', 'secret'])
201231
=== modified file 'lib/lp/services/webhooks/model.py'
--- lib/lp/services/webhooks/model.py 2015-09-24 13:44:02 +0000
+++ lib/lp/services/webhooks/model.py 2015-10-05 11:12:53 +0000
@@ -66,6 +66,7 @@
66 IWebhookJob,66 IWebhookJob,
67 IWebhookJobSource,67 IWebhookJobSource,
68 IWebhookSet,68 IWebhookSet,
69 WEBHOOK_EVENT_TYPES,
69 WebhookDeliveryFailure,70 WebhookDeliveryFailure,
70 WebhookDeliveryRetry,71 WebhookDeliveryRetry,
71 WebhookFeatureDisabled,72 WebhookFeatureDisabled,
@@ -223,6 +224,14 @@
223 getUtility(IWebhookSet).findByTarget(self),224 getUtility(IWebhookSet).findByTarget(self),
224 pre_iter_hook=preload_registrants)225 pre_iter_hook=preload_registrants)
225226
227 @property
228 def valid_webhook_event_types(self):
229 return sorted(WEBHOOK_EVENT_TYPES)
230
231 @property
232 def default_webhook_event_types(self):
233 return self.valid_webhook_event_types
234
226 def newWebhook(self, registrant, delivery_url, event_types, active=True,235 def newWebhook(self, registrant, delivery_url, event_types, active=True,
227 secret=None):236 secret=None):
228 if not getFeatureFlag('webhooks.new.enabled'):237 if not getFeatureFlag('webhooks.new.enabled'):
229238
=== modified file 'lib/lp/services/webhooks/tests/test_browser.py'
--- lib/lp/services/webhooks/tests/test_browser.py 2015-09-24 13:44:02 +0000
+++ lib/lp/services/webhooks/tests/test_browser.py 2015-10-05 11:12:53 +0000
@@ -25,6 +25,7 @@
25 )25 )
26from lp.testing.layers import DatabaseFunctionalLayer26from lp.testing.layers import DatabaseFunctionalLayer
27from lp.testing.matchers import HasQueryCount27from lp.testing.matchers import HasQueryCount
28from lp.testing.pages import extract_text
28from lp.testing.views import create_view29from lp.testing.views import create_view
2930
3031
@@ -51,6 +52,7 @@
51class GitRepositoryTestHelpers:52class GitRepositoryTestHelpers:
5253
53 event_type = "git:push:0.1"54 event_type = "git:push:0.1"
55 expected_event_types = [("git:push:0.1", "Git push")]
5456
55 def makeTarget(self):57 def makeTarget(self):
56 return self.factory.makeGitRepository()58 return self.factory.makeGitRepository()
@@ -59,6 +61,7 @@
59class BranchTestHelpers:61class BranchTestHelpers:
6062
61 event_type = "bzr:push:0.1"63 event_type = "bzr:push:0.1"
64 expected_event_types = [("bzr:push:0.1", "Bazaar push")]
6265
63 def makeTarget(self):66 def makeTarget(self):
64 return self.factory.makeBranch()67 return self.factory.makeBranch()
@@ -204,6 +207,18 @@
204 ['delivery_url'], [error.field_name for error in view.errors])207 ['delivery_url'], [error.field_name for error in view.errors])
205 self.assertIs(None, self.target.webhooks.one())208 self.assertIs(None, self.target.webhooks.one())
206209
210 def test_event_types(self):
211 # Only event types that are valid for the target are offered.
212 browser = self.getUserBrowser(
213 canonical_url(self.target, view_name="+new-webhook"),
214 user=self.owner)
215 event_types = browser.getControl(name="field.event_types")
216 display_options = [
217 extract_text(option) for option in event_types.displayOptions]
218 self.assertContentEqual(
219 self.expected_event_types,
220 zip(event_types.options, display_options))
221
207222
208class TestWebhookAddViewGitRepository(223class TestWebhookAddViewGitRepository(
209 TestWebhookAddViewBase, GitRepositoryTestHelpers, TestCaseWithFactory):224 TestWebhookAddViewBase, GitRepositoryTestHelpers, TestCaseWithFactory):
@@ -289,6 +304,17 @@
289 active=True,304 active=True,
290 event_types=[]))305 event_types=[]))
291306
307 def test_event_types(self):
308 # Only event types that are valid for the target are offered.
309 browser = self.getUserBrowser(
310 canonical_url(self.webhook, view_name="+index"), user=self.owner)
311 event_types = browser.getControl(name="field.event_types")
312 display_options = [
313 extract_text(option) for option in event_types.displayOptions]
314 self.assertContentEqual(
315 self.expected_event_types,
316 zip(event_types.options, display_options))
317
292318
293class TestWebhookViewGitRepository(319class TestWebhookViewGitRepository(
294 TestWebhookViewBase, GitRepositoryTestHelpers, TestCaseWithFactory):320 TestWebhookViewBase, GitRepositoryTestHelpers, TestCaseWithFactory):