Merge ~ines-almeida/launchpad:add-bug-webhooks/update-webhook-model into launchpad:master

Proposed by Ines Almeida
Status: Merged
Approved by: Ines Almeida
Approved revision: 3101fd6c2a4ac16bbe897909e57edfdabd07e8b8
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~ines-almeida/launchpad:add-bug-webhooks/update-webhook-model
Merge into: launchpad:master
Diff against target: 241 lines (+126/-7)
2 files modified
lib/lp/services/webhooks/model.py (+51/-1)
lib/lp/services/webhooks/tests/test_model.py (+75/-6)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+442543@code.launchpad.net

Commit message

Add new fields to webhook model

Update and add new unit tests to go with the change

Description of the change

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) wrote :

Looks good! Just one small question.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/services/webhooks/model.py b/lib/lp/services/webhooks/model.py
2index 114d953..bafd604 100644
3--- a/lib/lp/services/webhooks/model.py
4+++ b/lib/lp/services/webhooks/model.py
5@@ -19,7 +19,7 @@ import psutil
6 import transaction
7 from lazr.delegates import delegate_to
8 from lazr.enum import DBEnumeratedType, DBItem
9-from storm.expr import Desc
10+from storm.expr import And, Desc
11 from storm.properties import JSON, Bool, DateTime, Int, Unicode
12 from storm.references import Reference
13 from storm.store import Store
14@@ -92,6 +92,17 @@ class Webhook(StormBase):
15 charm_recipe_id = Int(name="charm_recipe")
16 charm_recipe = Reference(charm_recipe_id, "CharmRecipe.id")
17
18+ project_id = Int(name="project")
19+ project = Reference(project_id, "Product.id")
20+
21+ distribution_id = Int(name="distribution")
22+ distribution = Reference(distribution_id, "Distribution.id")
23+
24+ source_package_name_id = Int(name="source_package_name")
25+ source_package_name = Reference(
26+ source_package_name_id, "SourcePackageName.id"
27+ )
28+
29 registrant_id = Int(name="registrant", allow_none=False)
30 registrant = Reference(registrant_id, "Person.id")
31 date_created = DateTime(tzinfo=timezone.utc, allow_none=False)
32@@ -117,6 +128,15 @@ class Webhook(StormBase):
33 return self.oci_recipe
34 elif self.charm_recipe is not None:
35 return self.charm_recipe
36+ elif self.project is not None:
37+ return self.project
38+ elif self.distribution is not None:
39+ if self.source_package_name is not None:
40+ return self.distribution.getSourcePackage(
41+ self.source_package_name
42+ )
43+ else:
44+ return self.distribution
45 else:
46 raise AssertionError("No target.")
47
48@@ -178,6 +198,11 @@ class WebhookSet:
49 from lp.code.interfaces.branch import IBranch
50 from lp.code.interfaces.gitrepository import IGitRepository
51 from lp.oci.interfaces.ocirecipe import IOCIRecipe
52+ from lp.registry.interfaces.distribution import IDistribution
53+ from lp.registry.interfaces.distributionsourcepackage import (
54+ IDistributionSourcePackage,
55+ )
56+ from lp.registry.interfaces.product import IProduct
57 from lp.snappy.interfaces.snap import ISnap
58 from lp.soyuz.interfaces.livefs import ILiveFS
59
60@@ -194,6 +219,14 @@ class WebhookSet:
61 hook.oci_recipe = target
62 elif ICharmRecipe.providedBy(target):
63 hook.charm_recipe = target
64+ elif IProduct.providedBy(target):
65+ hook.project = target
66+ elif IDistribution.providedBy(target):
67+ hook.distribution = target
68+ elif IDistributionSourcePackage.providedBy(target):
69+ hook.distribution = target.distribution
70+ hook.source_package_name = target.sourcepackagename
71+
72 else:
73 raise AssertionError("Unsupported target: %r" % (target,))
74 hook.registrant = registrant
75@@ -220,6 +253,11 @@ class WebhookSet:
76 from lp.code.interfaces.branch import IBranch
77 from lp.code.interfaces.gitrepository import IGitRepository
78 from lp.oci.interfaces.ocirecipe import IOCIRecipe
79+ from lp.registry.interfaces.distribution import IDistribution
80+ from lp.registry.interfaces.distributionsourcepackage import (
81+ IDistributionSourcePackage,
82+ )
83+ from lp.registry.interfaces.product import IProduct
84 from lp.snappy.interfaces.snap import ISnap
85 from lp.soyuz.interfaces.livefs import ILiveFS
86
87@@ -235,6 +273,18 @@ class WebhookSet:
88 target_filter = Webhook.oci_recipe == target
89 elif ICharmRecipe.providedBy(target):
90 target_filter = Webhook.charm_recipe == target
91+ elif IProduct.providedBy(target):
92+ target_filter = Webhook.project == target
93+ elif IDistribution.providedBy(target):
94+ target_filter = And(
95+ Webhook.distribution == target,
96+ Webhook.source_package_name == None,
97+ )
98+ elif IDistributionSourcePackage.providedBy(target):
99+ target_filter = And(
100+ Webhook.distribution == target.distribution,
101+ Webhook.source_package_name == target.sourcepackagename,
102+ )
103 else:
104 raise AssertionError("Unsupported target: %r" % (target,))
105 return (
106diff --git a/lib/lp/services/webhooks/tests/test_model.py b/lib/lp/services/webhooks/tests/test_model.py
107index 85d2c40..47031b4 100644
108--- a/lib/lp/services/webhooks/tests/test_model.py
109+++ b/lib/lp/services/webhooks/tests/test_model.py
110@@ -77,10 +77,13 @@ class TestWebhookPermissions(TestCaseWithFactory):
111
112 layer = DatabaseFunctionalLayer
113
114+ def get_target_owner(self, target):
115+ return target.owner
116+
117 def test_target_owner_can_view(self):
118 target = self.factory.makeGitRepository()
119 webhook = self.factory.makeWebhook(target=target)
120- with person_logged_in(target.owner):
121+ with person_logged_in(self.get_target_owner(target)):
122 self.assertTrue(check_permission("launchpad.View", webhook))
123
124 def test_random_cannot_view(self):
125@@ -140,9 +143,12 @@ class TestWebhookSetBase:
126
127 layer = DatabaseFunctionalLayer
128
129+ def get_target_owner(self, target):
130+ return target.owner
131+
132 def test_new(self):
133 target = self.makeTarget()
134- login_person(target.owner)
135+ login_person(self.get_target_owner(target))
136 person = self.factory.makePerson()
137 hook = getUtility(IWebhookSet).new(
138 target,
139@@ -178,7 +184,7 @@ class TestWebhookSetBase:
140 self.factory.makeWebhook(
141 target, "http://path/%s/%d" % (name, i)
142 )
143- with person_logged_in(target1.owner):
144+ with person_logged_in(self.get_target_owner(target1)):
145 self.assertContentEqual(
146 [
147 "http://path/one/0",
148@@ -190,7 +196,7 @@ class TestWebhookSetBase:
149 for hook in getUtility(IWebhookSet).findByTarget(target1)
150 ],
151 )
152- with person_logged_in(target2.owner):
153+ with person_logged_in(self.get_target_owner(target2)):
154 self.assertContentEqual(
155 [
156 "http://path/two/0",
157@@ -205,7 +211,7 @@ class TestWebhookSetBase:
158
159 def test_delete(self):
160 target = self.makeTarget()
161- login_person(target.owner)
162+ login_person(self.get_target_owner(target))
163 hooks = []
164 for i in range(3):
165 hook = self.factory.makeWebhook(target, "http://path/to/%d" % i)
166@@ -237,7 +243,9 @@ class TestWebhookSetBase:
167
168 def test__checkVisibility_public_artifact(self):
169 target = self.makeTarget()
170- self.assertTrue(WebhookSet._checkVisibility(target, target.owner))
171+ self.assertTrue(
172+ WebhookSet._checkVisibility(target, self.get_target_owner(target))
173+ )
174
175 def test_trigger(self):
176 owner = self.factory.makePerson()
177@@ -520,3 +528,64 @@ class TestWebhookSetCharmRecipe(TestWebhookSetBase, TestCaseWithFactory):
178 return self.factory.makeCharmRecipe(
179 registrant=owner, owner=owner, **kwargs
180 )
181+
182+
183+class TestWebhookSetBugBase(TestWebhookSetBase):
184+
185+ event_type = "bug:0.1"
186+
187+ def test__checkVisibility_private_artifact(self):
188+ owner = self.factory.makePerson()
189+ target = self.makeTarget(
190+ owner=owner, information_type=InformationType.PROPRIETARY
191+ )
192+ self.assertTrue(WebhookSet._checkVisibility(target, owner))
193+
194+ def test_webhook_trigger_private_target(self):
195+ owner = self.factory.makePerson()
196+ target = self.makeTarget(
197+ owner=owner,
198+ information_type=InformationType.PROPRIETARY,
199+ )
200+ hook = self.factory.makeWebhook(
201+ target=target, event_types=[self.event_type]
202+ )
203+
204+ with admin_logged_in():
205+ self.assertThat(list(hook.deliveries), HasLength(0))
206+
207+ getUtility(IWebhookSet).trigger(
208+ target, self.event_type, {"some": "payload"}
209+ )
210+ with admin_logged_in():
211+ self.assertThat(list(hook.deliveries), HasLength(1))
212+ delivery = hook.deliveries.one()
213+ self.assertEqual(delivery.payload, {"some": "payload"})
214+
215+
216+class TestWebhookSetProductBugUpdate(
217+ TestWebhookSetBugBase, TestCaseWithFactory
218+):
219+ def makeTarget(self, **kwargs):
220+ return self.factory.makeProduct(**kwargs)
221+
222+
223+class TestWebhookSetDistributionBugUpdate(
224+ TestWebhookSetBugBase, TestCaseWithFactory
225+):
226+ def makeTarget(self, **kwargs):
227+ return self.factory.makeDistribution(**kwargs)
228+
229+
230+class TestWebhookSetDistributionSourcePackageBugUpdate(
231+ TestWebhookSetBugBase, TestCaseWithFactory
232+):
233+ def get_target_owner(self, target):
234+ return target.distribution.owner
235+
236+ def makeTarget(self, **kwargs):
237+ with admin_logged_in():
238+ distribution = self.factory.makeDistribution(**kwargs)
239+ return self.factory.makeDistributionSourcePackage(
240+ distribution=distribution
241+ )

Subscribers

People subscribed via source and target branches

to status/vote changes: