Merge lp:~cjwatson/launchpad/git-recipe-find into lp:launchpad
- git-recipe-find
- Merge into devel
Proposed by
Colin Watson
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 17894 | ||||
Proposed branch: | lp:~cjwatson/launchpad/git-recipe-find | ||||
Merge into: | lp:launchpad | ||||
Prerequisite: | lp:~cjwatson/launchpad/git-recipe-model | ||||
Diff against target: |
332 lines (+138/-24) 8 files modified
lib/lp/code/interfaces/gitref.py (+3/-2) lib/lp/code/interfaces/gitrepository.py (+3/-2) lib/lp/code/model/gitref.py (+13/-1) lib/lp/code/model/gitrepository.py (+25/-1) lib/lp/code/model/sourcepackagerecipedata.py (+34/-4) lib/lp/code/model/tests/test_hasrecipes.py (+38/-5) lib/lp/code/model/tests/test_sourcepackagerecipe.py (+9/-5) lib/lp/registry/model/product.py (+13/-4) |
||||
To merge this branch: | bzr merge lp:~cjwatson/launchpad/git-recipe-find | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
William Grant | code | Approve | |
Review via email: mp+282255@code.launchpad.net |
This proposal supersedes a proposal from 2016-01-12.
Commit message
Add IHasRecipes implementations and other methods for finding Git recipes.
Description of the change
Add IHasRecipes implementations and other methods for finding Git recipes.
The changes to Product.recipes aren't specifically tested here, mainly because the existing tests for that rely on having browser code in place. I have test changes for that which I'll propose in a later branch.
To post a comment you must log in.
Revision history for this message
William Grant (wgrant) : | # |
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/interfaces/gitref.py' | |||
2 | --- lib/lp/code/interfaces/gitref.py 2015-11-23 11:34:15 +0000 | |||
3 | +++ lib/lp/code/interfaces/gitref.py 2016-01-14 17:24:45 +0000 | |||
4 | @@ -1,4 +1,4 @@ | |||
6 | 1 | # Copyright 2015 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2015-2016 Canonical Ltd. This software is licensed under the |
7 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
8 | 3 | 3 | ||
9 | 4 | """Git reference ("ref") interfaces.""" | 4 | """Git reference ("ref") interfaces.""" |
10 | @@ -47,11 +47,12 @@ | |||
11 | 47 | GitObjectType, | 47 | GitObjectType, |
12 | 48 | ) | 48 | ) |
13 | 49 | from lp.code.interfaces.hasbranches import IHasMergeProposals | 49 | from lp.code.interfaces.hasbranches import IHasMergeProposals |
14 | 50 | from lp.code.interfaces.hasrecipes import IHasRecipes | ||
15 | 50 | from lp.registry.interfaces.person import IPerson | 51 | from lp.registry.interfaces.person import IPerson |
16 | 51 | from lp.services.webapp.interfaces import ITableBatchNavigator | 52 | from lp.services.webapp.interfaces import ITableBatchNavigator |
17 | 52 | 53 | ||
18 | 53 | 54 | ||
20 | 54 | class IGitRef(IHasMergeProposals, IPrivacy, IInformationType): | 55 | class IGitRef(IHasMergeProposals, IHasRecipes, IPrivacy, IInformationType): |
21 | 55 | """A reference in a Git repository.""" | 56 | """A reference in a Git repository.""" |
22 | 56 | 57 | ||
23 | 57 | # XXX cjwatson 2015-01-19 bug=760849: "beta" is a lie to get WADL | 58 | # XXX cjwatson 2015-01-19 bug=760849: "beta" is a lie to get WADL |
24 | 58 | 59 | ||
25 | === modified file 'lib/lp/code/interfaces/gitrepository.py' | |||
26 | --- lib/lp/code/interfaces/gitrepository.py 2015-11-02 15:31:39 +0000 | |||
27 | +++ lib/lp/code/interfaces/gitrepository.py 2016-01-14 17:24:45 +0000 | |||
28 | @@ -1,4 +1,4 @@ | |||
30 | 1 | # Copyright 2015 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2015-2016 Canonical Ltd. This software is licensed under the |
31 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
32 | 3 | 3 | ||
33 | 4 | """Git repository interfaces.""" | 4 | """Git repository interfaces.""" |
34 | @@ -64,6 +64,7 @@ | |||
35 | 64 | ) | 64 | ) |
36 | 65 | from lp.code.interfaces.defaultgit import ICanHasDefaultGitRepository | 65 | from lp.code.interfaces.defaultgit import ICanHasDefaultGitRepository |
37 | 66 | from lp.code.interfaces.hasgitrepositories import IHasGitRepositories | 66 | from lp.code.interfaces.hasgitrepositories import IHasGitRepositories |
38 | 67 | from lp.code.interfaces.hasrecipes import IHasRecipes | ||
39 | 67 | from lp.registry.interfaces.distributionsourcepackage import ( | 68 | from lp.registry.interfaces.distributionsourcepackage import ( |
40 | 68 | IDistributionSourcePackage, | 69 | IDistributionSourcePackage, |
41 | 69 | ) | 70 | ) |
42 | @@ -118,7 +119,7 @@ | |||
43 | 118 | return True | 119 | return True |
44 | 119 | 120 | ||
45 | 120 | 121 | ||
47 | 121 | class IGitRepositoryView(Interface): | 122 | class IGitRepositoryView(IHasRecipes): |
48 | 122 | """IGitRepository attributes that require launchpad.View permission.""" | 123 | """IGitRepository attributes that require launchpad.View permission.""" |
49 | 123 | 124 | ||
50 | 124 | id = Int(title=_("ID"), readonly=True, required=True) | 125 | id = Int(title=_("ID"), readonly=True, required=True) |
51 | 125 | 126 | ||
52 | === modified file 'lib/lp/code/model/gitref.py' | |||
53 | --- lib/lp/code/model/gitref.py 2015-11-23 11:34:15 +0000 | |||
54 | +++ lib/lp/code/model/gitref.py 2016-01-14 17:24:45 +0000 | |||
55 | @@ -1,4 +1,4 @@ | |||
57 | 1 | # Copyright 2015 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2015-2016 Canonical Ltd. This software is licensed under the |
58 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
59 | 3 | 3 | ||
60 | 4 | __metaclass__ = type | 4 | __metaclass__ = type |
61 | @@ -242,6 +242,18 @@ | |||
62 | 242 | """See `IGitRef`.""" | 242 | """See `IGitRef`.""" |
63 | 243 | return self.repository.pending_writes | 243 | return self.repository.pending_writes |
64 | 244 | 244 | ||
65 | 245 | @property | ||
66 | 246 | def recipes(self): | ||
67 | 247 | """See `IHasRecipes`.""" | ||
68 | 248 | from lp.code.model.sourcepackagerecipe import SourcePackageRecipe | ||
69 | 249 | from lp.code.model.sourcepackagerecipedata import ( | ||
70 | 250 | SourcePackageRecipeData, | ||
71 | 251 | ) | ||
72 | 252 | recipes = SourcePackageRecipeData.findRecipes( | ||
73 | 253 | self.repository, revspecs=list(set([self.path, self.name]))) | ||
74 | 254 | hook = SourcePackageRecipe.preLoadDataForSourcePackageRecipes | ||
75 | 255 | return DecoratedResultSet(recipes, pre_iter_hook=hook) | ||
76 | 256 | |||
77 | 245 | 257 | ||
78 | 246 | @implementer(IGitRef) | 258 | @implementer(IGitRef) |
79 | 247 | class GitRef(StormBase, GitRefMixin): | 259 | class GitRef(StormBase, GitRefMixin): |
80 | 248 | 260 | ||
81 | === modified file 'lib/lp/code/model/gitrepository.py' | |||
82 | --- lib/lp/code/model/gitrepository.py 2015-12-10 00:05:41 +0000 | |||
83 | +++ lib/lp/code/model/gitrepository.py 2016-01-14 17:24:45 +0000 | |||
84 | @@ -1,4 +1,4 @@ | |||
86 | 1 | # Copyright 2015 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2015-2016 Canonical Ltd. This software is licensed under the |
87 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
88 | 3 | 3 | ||
89 | 4 | __metaclass__ = type | 4 | __metaclass__ = type |
90 | @@ -130,6 +130,7 @@ | |||
91 | 130 | DEFAULT, | 130 | DEFAULT, |
92 | 131 | UTC_NOW, | 131 | UTC_NOW, |
93 | 132 | ) | 132 | ) |
94 | 133 | from lp.services.database.decoratedresultset import DecoratedResultSet | ||
95 | 133 | from lp.services.database.enumcol import EnumCol | 134 | from lp.services.database.enumcol import EnumCol |
96 | 134 | from lp.services.database.interfaces import IStore | 135 | from lp.services.database.interfaces import IStore |
97 | 135 | from lp.services.database.stormbase import StormBase | 136 | from lp.services.database.stormbase import StormBase |
98 | @@ -953,6 +954,29 @@ | |||
99 | 953 | jobs.append(UpdatePreviewDiffJob.create(merge_proposal)) | 954 | jobs.append(UpdatePreviewDiffJob.create(merge_proposal)) |
100 | 954 | return jobs | 955 | return jobs |
101 | 955 | 956 | ||
102 | 957 | def _getRecipes(self, paths=None): | ||
103 | 958 | """Undecorated version of recipes for use by `markRecipesStale`.""" | ||
104 | 959 | from lp.code.model.sourcepackagerecipedata import ( | ||
105 | 960 | SourcePackageRecipeData, | ||
106 | 961 | ) | ||
107 | 962 | if paths is not None: | ||
108 | 963 | revspecs = set() | ||
109 | 964 | for path in paths: | ||
110 | 965 | revspecs.add(path) | ||
111 | 966 | if path.startswith("refs/heads/"): | ||
112 | 967 | revspecs.add(path[len("refs/heads/"):]) | ||
113 | 968 | revspecs = list(revspecs) | ||
114 | 969 | else: | ||
115 | 970 | revspecs = None | ||
116 | 971 | return SourcePackageRecipeData.findRecipes(self, revspecs=revspecs) | ||
117 | 972 | |||
118 | 973 | @property | ||
119 | 974 | def recipes(self): | ||
120 | 975 | """See `IHasRecipes`.""" | ||
121 | 976 | from lp.code.model.sourcepackagerecipe import SourcePackageRecipe | ||
122 | 977 | hook = SourcePackageRecipe.preLoadDataForSourcePackageRecipes | ||
123 | 978 | return DecoratedResultSet(self._getRecipes(), pre_iter_hook=hook) | ||
124 | 979 | |||
125 | 956 | def _markProposalMerged(self, proposal, merged_revision_id, logger=None): | 980 | def _markProposalMerged(self, proposal, merged_revision_id, logger=None): |
126 | 957 | if logger is not None: | 981 | if logger is not None: |
127 | 958 | logger.info( | 982 | logger.info( |
128 | 959 | 983 | ||
129 | === modified file 'lib/lp/code/model/sourcepackagerecipedata.py' | |||
130 | --- lib/lp/code/model/sourcepackagerecipedata.py 2016-01-14 17:24:45 +0000 | |||
131 | +++ lib/lp/code/model/sourcepackagerecipedata.py 2016-01-14 17:24:45 +0000 | |||
132 | @@ -30,6 +30,7 @@ | |||
133 | 30 | from storm.expr import Union | 30 | from storm.expr import Union |
134 | 31 | from storm.locals import ( | 31 | from storm.locals import ( |
135 | 32 | And, | 32 | And, |
136 | 33 | In, | ||
137 | 33 | Int, | 34 | Int, |
138 | 34 | Reference, | 35 | Reference, |
139 | 35 | ReferenceSet, | 36 | ReferenceSet, |
140 | @@ -244,21 +245,50 @@ | |||
141 | 244 | return parser.parse(permitted_instructions=SAFE_INSTRUCTIONS) | 245 | return parser.parse(permitted_instructions=SAFE_INSTRUCTIONS) |
142 | 245 | 246 | ||
143 | 246 | @staticmethod | 247 | @staticmethod |
145 | 247 | def findRecipes(branch): | 248 | def findRecipes(branch_or_repository, revspecs=None): |
146 | 249 | """Find recipes for a given branch or repository. | ||
147 | 250 | |||
148 | 251 | :param branch_or_repository: The branch or repository to search for. | ||
149 | 252 | :param revspecs: If not None, return only recipes whose `revspec` is | ||
150 | 253 | in this sequence. | ||
151 | 254 | :return: a collection of `ISourcePackageRecipe`s. | ||
152 | 255 | """ | ||
153 | 248 | from lp.code.model.sourcepackagerecipe import SourcePackageRecipe | 256 | from lp.code.model.sourcepackagerecipe import SourcePackageRecipe |
155 | 249 | store = Store.of(branch) | 257 | store = Store.of(branch_or_repository) |
156 | 258 | if IGitRepository.providedBy(branch_or_repository): | ||
157 | 259 | data_clause = ( | ||
158 | 260 | SourcePackageRecipeData.base_git_repository == | ||
159 | 261 | branch_or_repository) | ||
160 | 262 | insn_clause = ( | ||
161 | 263 | _SourcePackageRecipeDataInstruction.git_repository == | ||
162 | 264 | branch_or_repository) | ||
163 | 265 | elif IBranch.providedBy(branch_or_repository): | ||
164 | 266 | data_clause = ( | ||
165 | 267 | SourcePackageRecipeData.base_branch == branch_or_repository) | ||
166 | 268 | insn_clause = ( | ||
167 | 269 | _SourcePackageRecipeDataInstruction.branch == | ||
168 | 270 | branch_or_repository) | ||
169 | 271 | else: | ||
170 | 272 | raise AssertionError( | ||
171 | 273 | "Unsupported source: %r" % (branch_or_repository,)) | ||
172 | 274 | if revspecs is not None: | ||
173 | 275 | data_clause = And( | ||
174 | 276 | data_clause, In(SourcePackageRecipeData.revspec, revspecs)) | ||
175 | 277 | insn_clause = And( | ||
176 | 278 | insn_clause, | ||
177 | 279 | In(_SourcePackageRecipeDataInstruction.revspec, revspecs)) | ||
178 | 250 | return store.find( | 280 | return store.find( |
179 | 251 | SourcePackageRecipe, | 281 | SourcePackageRecipe, |
180 | 252 | SourcePackageRecipe.id.is_in(Union( | 282 | SourcePackageRecipe.id.is_in(Union( |
181 | 253 | Select( | 283 | Select( |
182 | 254 | SourcePackageRecipeData.sourcepackage_recipe_id, | 284 | SourcePackageRecipeData.sourcepackage_recipe_id, |
184 | 255 | SourcePackageRecipeData.base_branch == branch), | 285 | data_clause), |
185 | 256 | Select( | 286 | Select( |
186 | 257 | SourcePackageRecipeData.sourcepackage_recipe_id, | 287 | SourcePackageRecipeData.sourcepackage_recipe_id, |
187 | 258 | And( | 288 | And( |
188 | 259 | _SourcePackageRecipeDataInstruction.recipe_data_id == | 289 | _SourcePackageRecipeDataInstruction.recipe_data_id == |
189 | 260 | SourcePackageRecipeData.id, | 290 | SourcePackageRecipeData.id, |
191 | 261 | _SourcePackageRecipeDataInstruction.branch == branch) | 291 | insn_clause) |
192 | 262 | ) | 292 | ) |
193 | 263 | )) | 293 | )) |
194 | 264 | ) | 294 | ) |
195 | 265 | 295 | ||
196 | === modified file 'lib/lp/code/model/tests/test_hasrecipes.py' | |||
197 | --- lib/lp/code/model/tests/test_hasrecipes.py 2015-09-16 13:26:12 +0000 | |||
198 | +++ lib/lp/code/model/tests/test_hasrecipes.py 2016-01-14 17:24:45 +0000 | |||
199 | @@ -1,4 +1,4 @@ | |||
201 | 1 | # Copyright 2010-2015 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2010-2016 Canonical Ltd. This software is licensed under the |
202 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
203 | 3 | 3 | ||
204 | 4 | """Tests for classes that implement IHasRecipes.""" | 4 | """Tests for classes that implement IHasRecipes.""" |
205 | @@ -39,6 +39,33 @@ | |||
206 | 39 | self.factory.makeSourcePackageRecipe() | 39 | self.factory.makeSourcePackageRecipe() |
207 | 40 | self.assertEqual(recipe, nonbase_branch.recipes.one()) | 40 | self.assertEqual(recipe, nonbase_branch.recipes.one()) |
208 | 41 | 41 | ||
209 | 42 | def test_git_repository_implements_hasrecipes(self): | ||
210 | 43 | # Git repositories should implement IHasRecipes. | ||
211 | 44 | repository = self.factory.makeGitRepository() | ||
212 | 45 | self.assertProvides(repository, IHasRecipes) | ||
213 | 46 | |||
214 | 47 | def test_git_repository_recipes(self): | ||
215 | 48 | # IGitRepository.recipes should provide all the SourcePackageRecipes | ||
216 | 49 | # attached to that repository. | ||
217 | 50 | base_ref1, base_ref2 = self.factory.makeGitRefs( | ||
218 | 51 | paths=[u"refs/heads/ref1", u"refs/heads/ref2"]) | ||
219 | 52 | [other_ref] = self.factory.makeGitRefs() | ||
220 | 53 | self.factory.makeSourcePackageRecipe(branches=[base_ref1]) | ||
221 | 54 | self.factory.makeSourcePackageRecipe(branches=[base_ref2]) | ||
222 | 55 | self.factory.makeSourcePackageRecipe(branches=[other_ref]) | ||
223 | 56 | self.assertEqual(2, base_ref1.repository.recipes.count()) | ||
224 | 57 | |||
225 | 58 | def test_git_repository_recipes_nonbase(self): | ||
226 | 59 | # IGitRepository.recipes should provide all the SourcePackageRecipes | ||
227 | 60 | # that refer to the repository, even as a non-base branch. | ||
228 | 61 | [base_ref] = self.factory.makeGitRefs() | ||
229 | 62 | [nonbase_ref] = self.factory.makeGitRefs() | ||
230 | 63 | [other_ref] = self.factory.makeGitRefs() | ||
231 | 64 | recipe = self.factory.makeSourcePackageRecipe( | ||
232 | 65 | branches=[base_ref, nonbase_ref]) | ||
233 | 66 | self.factory.makeSourcePackageRecipe(branches=[other_ref]) | ||
234 | 67 | self.assertEqual(recipe, nonbase_ref.repository.recipes.one()) | ||
235 | 68 | |||
236 | 42 | def test_person_implements_hasrecipes(self): | 69 | def test_person_implements_hasrecipes(self): |
237 | 43 | # Person should implement IHasRecipes. | 70 | # Person should implement IHasRecipes. |
238 | 44 | person = self.factory.makePerson() | 71 | person = self.factory.makePerson() |
239 | @@ -60,10 +87,16 @@ | |||
240 | 60 | 87 | ||
241 | 61 | def test_product_recipes(self): | 88 | def test_product_recipes(self): |
242 | 62 | # IProduct.recipes should provide all the SourcePackageRecipes | 89 | # IProduct.recipes should provide all the SourcePackageRecipes |
244 | 63 | # attached to that product's branches. | 90 | # attached to that product's branches and Git repositories. |
245 | 64 | product = self.factory.makeProduct() | 91 | product = self.factory.makeProduct() |
246 | 65 | branch = self.factory.makeBranch(product=product) | 92 | branch = self.factory.makeBranch(product=product) |
249 | 66 | self.factory.makeSourcePackageRecipe(branches=[branch]) | 93 | [ref] = self.factory.makeGitRefs(target=product) |
250 | 67 | self.factory.makeSourcePackageRecipe(branches=[branch]) | 94 | recipe1 = self.factory.makeSourcePackageRecipe(branches=[branch]) |
251 | 95 | recipe2 = self.factory.makeSourcePackageRecipe(branches=[branch]) | ||
252 | 68 | self.factory.makeSourcePackageRecipe() | 96 | self.factory.makeSourcePackageRecipe() |
254 | 69 | self.assertEqual(2, product.recipes.count()) | 97 | recipe3 = self.factory.makeSourcePackageRecipe(branches=[ref]) |
255 | 98 | recipe4 = self.factory.makeSourcePackageRecipe(branches=[ref]) | ||
256 | 99 | self.factory.makeSourcePackageRecipe( | ||
257 | 100 | branches=self.factory.makeGitRefs()) | ||
258 | 101 | self.assertContentEqual( | ||
259 | 102 | [recipe1, recipe2, recipe3, recipe4], product.recipes) | ||
260 | 70 | 103 | ||
261 | === modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py' | |||
262 | --- lib/lp/code/model/tests/test_sourcepackagerecipe.py 2016-01-14 17:24:45 +0000 | |||
263 | +++ lib/lp/code/model/tests/test_sourcepackagerecipe.py 2016-01-14 17:24:45 +0000 | |||
264 | @@ -1098,14 +1098,10 @@ | |||
265 | 1098 | self.recipe, 'date_last_modified', UTC_NOW) | 1098 | self.recipe, 'date_last_modified', UTC_NOW) |
266 | 1099 | 1099 | ||
267 | 1100 | 1100 | ||
269 | 1101 | class TestWebservice(TestCaseWithFactory): | 1101 | class TestWebserviceMixin: |
270 | 1102 | 1102 | ||
271 | 1103 | layer = AppServerLayer | 1103 | layer = AppServerLayer |
272 | 1104 | 1104 | ||
273 | 1105 | def makeRecipeText(self): | ||
274 | 1106 | branch = self.factory.makeBranch() | ||
275 | 1107 | return MINIMAL_RECIPE_TEXT_BZR % branch.bzr_identity | ||
276 | 1108 | |||
277 | 1109 | def makeRecipe(self, user=None, owner=None, recipe_text=None, | 1105 | def makeRecipe(self, user=None, owner=None, recipe_text=None, |
278 | 1110 | version='devel'): | 1106 | version='devel'): |
279 | 1111 | # rockstar 21 Jul 2010 - This function does more commits than I'd | 1107 | # rockstar 21 Jul 2010 - This function does more commits than I'd |
280 | @@ -1279,3 +1275,11 @@ | |||
281 | 1279 | with StormStatementRecorder() as recorder: | 1275 | with StormStatementRecorder() as recorder: |
282 | 1280 | webservice.get(url) | 1276 | webservice.get(url) |
283 | 1281 | self.assertThat(recorder, HasQueryCount(Equals(23))) | 1277 | self.assertThat(recorder, HasQueryCount(Equals(23))) |
284 | 1278 | |||
285 | 1279 | |||
286 | 1280 | class TestWebserviceBzr(TestWebserviceMixin, BzrMixin, TestCaseWithFactory): | ||
287 | 1281 | pass | ||
288 | 1282 | |||
289 | 1283 | |||
290 | 1284 | class TestWebserviceGit(TestWebserviceMixin, GitMixin, TestCaseWithFactory): | ||
291 | 1285 | pass | ||
292 | 1282 | 1286 | ||
293 | === modified file 'lib/lp/registry/model/product.py' | |||
294 | --- lib/lp/registry/model/product.py 2015-10-01 17:32:41 +0000 | |||
295 | +++ lib/lp/registry/model/product.py 2016-01-14 17:24:45 +0000 | |||
296 | @@ -1,4 +1,4 @@ | |||
298 | 1 | # Copyright 2009-2015 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2009-2016 Canonical Ltd. This software is licensed under the |
299 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
300 | 3 | 3 | ||
301 | 4 | """Database classes including and related to Product.""" | 4 | """Database classes including and related to Product.""" |
302 | @@ -122,6 +122,7 @@ | |||
303 | 122 | from lp.code.interfaces.gitrepository import IGitRepositorySet | 122 | from lp.code.interfaces.gitrepository import IGitRepositorySet |
304 | 123 | from lp.code.model.branch import Branch | 123 | from lp.code.model.branch import Branch |
305 | 124 | from lp.code.model.branchnamespace import BRANCH_POLICY_ALLOWED_TYPES | 124 | from lp.code.model.branchnamespace import BRANCH_POLICY_ALLOWED_TYPES |
306 | 125 | from lp.code.model.gitrepository import GitRepository | ||
307 | 125 | from lp.code.model.hasbranches import ( | 126 | from lp.code.model.hasbranches import ( |
308 | 126 | HasBranchesMixin, | 127 | HasBranchesMixin, |
309 | 127 | HasCodeImportsMixin, | 128 | HasCodeImportsMixin, |
310 | @@ -1574,12 +1575,20 @@ | |||
311 | 1574 | @property | 1575 | @property |
312 | 1575 | def recipes(self): | 1576 | def recipes(self): |
313 | 1576 | """See `IHasRecipes`.""" | 1577 | """See `IHasRecipes`.""" |
315 | 1577 | recipes = Store.of(self).find( | 1578 | tables = [ |
316 | 1579 | SourcePackageRecipe, | ||
317 | 1580 | SourcePackageRecipeData, | ||
318 | 1581 | LeftJoin(Branch, SourcePackageRecipeData.base_branch == Branch.id), | ||
319 | 1582 | LeftJoin( | ||
320 | 1583 | GitRepository, | ||
321 | 1584 | SourcePackageRecipeData.base_git_repository == | ||
322 | 1585 | GitRepository.id), | ||
323 | 1586 | ] | ||
324 | 1587 | recipes = Store.of(self).using(*tables).find( | ||
325 | 1578 | SourcePackageRecipe, | 1588 | SourcePackageRecipe, |
326 | 1579 | SourcePackageRecipe.id == | 1589 | SourcePackageRecipe.id == |
327 | 1580 | SourcePackageRecipeData.sourcepackage_recipe_id, | 1590 | SourcePackageRecipeData.sourcepackage_recipe_id, |
330 | 1581 | SourcePackageRecipeData.base_branch == Branch.id, | 1591 | Or(Branch.product == self, GitRepository.project == self)) |
329 | 1582 | Branch.product == self) | ||
331 | 1583 | hook = SourcePackageRecipe.preLoadDataForSourcePackageRecipes | 1592 | hook = SourcePackageRecipe.preLoadDataForSourcePackageRecipes |
332 | 1584 | return DecoratedResultSet(recipes, pre_iter_hook=hook) | 1593 | return DecoratedResultSet(recipes, pre_iter_hook=hook) |
333 | 1585 | 1594 |