launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #19513
[Merge] lp:~cjwatson/launchpad/webhook-default-event-types into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/webhook-default-event-types into lp:launchpad.
Commit message:
Only offer webhook event types that are valid for the target, and set reasonable defaults.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #342729 in Launchpad itself: "Should support post-commit webhooks"
https://bugs.launchpad.net/launchpad/+bug/342729
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/webhook-default-event-types/+merge/273054
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).
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/webhook-default-event-types into lp:launchpad.
=== 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-01 12:33:30 +0000
@@ -205,6 +205,10 @@
enum=InformationType, default=InformationType.PUBLIC)
@property
+ def valid_event_types(self):
+ return ["bzr:push:0.1"]
+
+ @property
def private(self):
return self.information_type in PRIVATE_INFORMATION_TYPES
=== 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-01 12:33:30 +0000
@@ -234,6 +234,10 @@
self.owner_default = False
self.target_default = False
+ @property
+ def valid_event_types(self):
+ return ["git:push:0.1"]
+
# Marker for references to Git URL layouts: ##GITNAMESPACE##
@property
def unique_name(self):
=== 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-01 12:33:30 +0000
@@ -127,7 +127,10 @@
@property
def initial_values(self):
- return {'active': True}
+ return {
+ 'active': True,
+ 'event_types': self.context.default_event_types,
+ }
@property
def cancel_url(self):
=== 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-01 12:33:30 +0000
@@ -1,4 +1,4 @@
-<!-- Copyright 2010 Canonical Ltd. This software is licensed under the
+<!-- Copyright 2015 Canonical Ltd. This software is licensed under the
GNU Affero General Public License version 3 (see the file LICENSE).
-->
@@ -22,12 +22,21 @@
<allow interface="lp.services.webhooks.interfaces.IWebhookSet"/>
</securedutility>
<securedutility
- name="WebhookEventType"
- component="lp.services.webhooks.interfaces.WebhookEventTypeVocabulary"
- provides="zope.schema.interfaces.IVocabularyFactory">
- <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
- </securedutility>
- <class class="lp.services.webhooks.interfaces.WebhookEventTypeVocabulary">
+ name="AnyWebhookEventType"
+ component="lp.services.webhooks.interfaces.AnyWebhookEventTypeVocabulary"
+ provides="zope.schema.interfaces.IVocabularyFactory">
+ <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
+ </securedutility>
+ <class class="lp.services.webhooks.interfaces.AnyWebhookEventTypeVocabulary">
+ <allow interface="zope.schema.interfaces.IVocabularyTokenized"/>
+ </class>
+ <securedutility
+ name="ValidWebhookEventType"
+ component="lp.services.webhooks.interfaces.ValidWebhookEventTypeVocabulary"
+ provides="zope.schema.interfaces.IVocabularyFactory">
+ <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
+ </securedutility>
+ <class class="lp.services.webhooks.interfaces.ValidWebhookEventTypeVocabulary">
<allow interface="zope.schema.interfaces.IVocabularyTokenized"/>
</class>
=== 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-01 12:33:30 +0000
@@ -6,6 +6,7 @@
__metaclass__ = type
__all__ = [
+ 'AnyWebhookEventTypeVocabulary',
'IWebhook',
'IWebhookClient',
'IWebhookDeliveryJob',
@@ -14,10 +15,11 @@
'IWebhookJobSource',
'IWebhookSet',
'IWebhookTarget',
+ 'WEBHOOK_EVENT_TYPES',
'WebhookDeliveryFailure',
'WebhookDeliveryRetry',
'WebhookFeatureDisabled',
- 'WebhookEventTypeVocabulary',
+ 'ValidWebhookEventTypeVocabulary',
]
import httplib
@@ -95,13 +97,28 @@
pass
-class WebhookEventTypeVocabulary(SimpleVocabulary):
+class AnyWebhookEventTypeVocabulary(SimpleVocabulary):
def __init__(self, context):
terms = [
self.createTerm(key, key, value)
for key, value in WEBHOOK_EVENT_TYPES.iteritems()]
- super(WebhookEventTypeVocabulary, self).__init__(terms)
+ super(AnyWebhookEventTypeVocabulary, self).__init__(terms)
+
+
+class ValidWebhookEventTypeVocabulary(SimpleVocabulary):
+
+ def __init__(self, context):
+ # When creating a webhook, the context is the target; when editing
+ # an existing webhook, the context is the webhook itself.
+ if IWebhook.providedBy(context):
+ target = context.target
+ else:
+ target = context
+ terms = [
+ self.createTerm(key, key, WEBHOOK_EVENT_TYPES[key])
+ for key in target.valid_event_types]
+ super(ValidWebhookEventTypeVocabulary, self).__init__(terms)
class IWebhook(Interface):
@@ -115,7 +132,7 @@
required=True, readonly=True,
description=_("The object for which this webhook receives events.")))
event_types = exported(List(
- Choice(vocabulary='WebhookEventType'), title=_("Event types"),
+ Choice(vocabulary='ValidWebhookEventType'), title=_("Event types"),
required=True, readonly=False))
registrant = exported(Reference(
title=_("Registrant"), schema=IPerson, required=True, readonly=True,
@@ -195,6 +212,19 @@
value_type=Reference(schema=IWebhook),
readonly=True)))
+ valid_event_types = List(
+ Choice(vocabulary='AnyWebhookEventType'), title=_("Valid event types"),
+ description=_("Valid event types for this object type."),
+ required=True, readonly=True)
+
+ default_event_types = List(
+ Choice(vocabulary='ValidWebhookEventType'),
+ title=_("Default event types"),
+ description=_(
+ "Default event types for new webhooks attached to this object "
+ "type."),
+ required=True, readonly=True)
+
@call_with(registrant=REQUEST_USER)
@export_factory_operation(
IWebhook, ['delivery_url', 'active', 'event_types', 'secret'])
=== 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-01 12:33:30 +0000
@@ -66,6 +66,7 @@
IWebhookJob,
IWebhookJobSource,
IWebhookSet,
+ WEBHOOK_EVENT_TYPES,
WebhookDeliveryFailure,
WebhookDeliveryRetry,
WebhookFeatureDisabled,
@@ -223,6 +224,14 @@
getUtility(IWebhookSet).findByTarget(self),
pre_iter_hook=preload_registrants)
+ @property
+ def valid_event_types(self):
+ return sorted(WEBHOOK_EVENT_TYPES)
+
+ @property
+ def default_event_types(self):
+ return self.valid_event_types
+
def newWebhook(self, registrant, delivery_url, event_types, active=True,
secret=None):
if not getFeatureFlag('webhooks.new.enabled'):
=== 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-01 12:33:30 +0000
@@ -25,6 +25,7 @@
)
from lp.testing.layers import DatabaseFunctionalLayer
from lp.testing.matchers import HasQueryCount
+from lp.testing.pages import extract_text
from lp.testing.views import create_view
@@ -51,6 +52,7 @@
class GitRepositoryTestHelpers:
event_type = "git:push:0.1"
+ expected_event_types = [("git:push:0.1", "Git push")]
def makeTarget(self):
return self.factory.makeGitRepository()
@@ -59,6 +61,7 @@
class BranchTestHelpers:
event_type = "bzr:push:0.1"
+ expected_event_types = [("bzr:push:0.1", "Bazaar push")]
def makeTarget(self):
return self.factory.makeBranch()
@@ -204,6 +207,18 @@
['delivery_url'], [error.field_name for error in view.errors])
self.assertIs(None, self.target.webhooks.one())
+ def test_event_types(self):
+ # Only event types that are valid for the target are offered.
+ browser = self.getUserBrowser(
+ canonical_url(self.target, view_name="+new-webhook"),
+ user=self.owner)
+ event_types = browser.getControl(name="field.event_types")
+ display_options = [
+ extract_text(option) for option in event_types.displayOptions]
+ self.assertContentEqual(
+ self.expected_event_types,
+ zip(event_types.options, display_options))
+
class TestWebhookAddViewGitRepository(
TestWebhookAddViewBase, GitRepositoryTestHelpers, TestCaseWithFactory):
@@ -289,6 +304,17 @@
active=True,
event_types=[]))
+ def test_event_types(self):
+ # Only event types that are valid for the target are offered.
+ browser = self.getUserBrowser(
+ canonical_url(self.webhook, view_name="+index"), user=self.owner)
+ event_types = browser.getControl(name="field.event_types")
+ display_options = [
+ extract_text(option) for option in event_types.displayOptions]
+ self.assertContentEqual(
+ self.expected_event_types,
+ zip(event_types.options, display_options))
+
class TestWebhookViewGitRepository(
TestWebhookViewBase, GitRepositoryTestHelpers, TestCaseWithFactory):
Follow ups