launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #25633
[Merge] ~pappacena/launchpad:ociproject-picker into launchpad:master
Thiago F. Pappacena has proposed merging ~pappacena/launchpad:ociproject-picker into launchpad:master.
Commit message:
Adding OCIProject vocabulary, to support OCIProject picker widgets in forms
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~pappacena/launchpad/+git/launchpad/+merge/393627
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~pappacena/launchpad:ociproject-picker into launchpad:master.
diff --git a/lib/lp/registry/interfaces/ociproject.py b/lib/lp/registry/interfaces/ociproject.py
index e72a4cf..75bc348 100644
--- a/lib/lp/registry/interfaces/ociproject.py
+++ b/lib/lp/registry/interfaces/ociproject.py
@@ -204,7 +204,8 @@ class IOCIProjectSet(Interface):
def getByPillarAndName(pillar, name):
"""Get the OCIProjects for a given distribution or project.
- :param pillar: An instance of Distribution or Product.
+ :param pillar: An instance of Distribution or Product, or the
+ respective pillar name.
:param name: The OCIProject name to find.
:return: The OCIProject found.
"""
@@ -213,6 +214,9 @@ class IOCIProjectSet(Interface):
"""Find OCIProjects for a given pillar that contain the provided
name."""
+ def findByName(name_substring):
+ """Find OCIProjects that contain the provided name."""
+
def preloadDataForOCIProjects(oci_projects):
"""Preload data for the given list of OCIProject objects."""
diff --git a/lib/lp/registry/model/ociproject.py b/lib/lp/registry/model/ociproject.py
index c27f2c6..bef4aa4 100644
--- a/lib/lp/registry/model/ociproject.py
+++ b/lib/lp/registry/model/ociproject.py
@@ -12,7 +12,13 @@ __all__ = [
]
import pytz
+import six
from six import text_type
+from storm.expr import (
+ Join,
+ LeftJoin,
+ Or,
+ )
from storm.locals import (
Bool,
DateTime,
@@ -263,12 +269,34 @@ class OCIProjectSet:
def getByPillarAndName(self, pillar, name):
"""See `IOCIProjectSet`."""
- target = IStore(OCIProject).find(
- OCIProject,
- self._get_pillar_attribute(pillar) == pillar,
- OCIProject.ociprojectname == OCIProjectName.id,
- OCIProjectName.name == name).one()
- return target
+ from lp.registry.model.product import Product
+ from lp.registry.model.distribution import Distribution
+
+ # If pillar is not an string, we expect it to be either an
+ # IDistribution or IProduct.
+ if not isinstance(pillar, six.string_types):
+ return IStore(OCIProject).find(
+ OCIProject,
+ self._get_pillar_attribute(pillar) == pillar,
+ OCIProject.ociprojectname == OCIProjectName.id,
+ OCIProjectName.name == name).one()
+ else:
+ # If we got a pillar name instead, we need to join with both
+ # Distribution and Product tables, to find out which one has the
+ # provided name.
+ tables = [
+ OCIProject,
+ Join(OCIProjectName,
+ OCIProject.ociprojectname == OCIProjectName.id),
+ LeftJoin(Distribution,
+ OCIProject.distribution == Distribution.id),
+ LeftJoin(Product,
+ OCIProject.project == Product.id)
+ ]
+ return IStore(OCIProject).using(*tables).find(
+ OCIProject,
+ Or(Distribution.name == pillar, Product.name == pillar),
+ OCIProjectName.name == name).one()
def findByPillarAndName(self, pillar, name_substring):
"""See `IOCIProjectSet`."""
@@ -278,6 +306,12 @@ class OCIProjectSet:
OCIProject.ociprojectname == OCIProjectName.id,
OCIProjectName.name.contains_string(name_substring))
+ def findByName(self, name_substring):
+ return IStore(OCIProject).find(
+ OCIProject,
+ OCIProject.ociprojectname == OCIProjectName.id,
+ OCIProjectName.name.contains_string(name_substring))
+
def preloadDataForOCIProjects(self, oci_projects):
"""See `IOCIProjectSet`."""
oci_projects = [removeSecurityProxy(i) for i in oci_projects]
diff --git a/lib/lp/registry/tests/test_ociproject.py b/lib/lp/registry/tests/test_ociproject.py
index c01f939..0232b2e 100644
--- a/lib/lp/registry/tests/test_ociproject.py
+++ b/lib/lp/registry/tests/test_ociproject.py
@@ -16,6 +16,7 @@ from testtools.matchers import (
)
from testtools.testcase import ExpectedException
from zope.component import getUtility
+from zope.schema.vocabulary import getVocabularyRegistry
from zope.security.interfaces import Unauthorized
from zope.security.proxy import removeSecurityProxy
@@ -313,3 +314,54 @@ class TestOCIProjectWebservice(TestCaseWithFactory):
owner=other_user))
self.assertCanCreateOCIProject(distro, self.person)
+
+
+class TestOCIProjectVocabulary(TestCaseWithFactory):
+ layer = DatabaseFunctionalLayer
+
+ def createOCIProjects(self, name_tpl="my-ociproject-%s", count=5):
+ return [self.factory.makeOCIProject(ociprojectname=name_tpl % i)
+ for i in range(count)]
+
+ def getVocabulary(self, context=None):
+ vocabulary_registry = getVocabularyRegistry()
+ return vocabulary_registry.get(context, "OCIProject")
+
+ def assertContainsSameOCIProjects(self, ociprojects, search_result):
+ """Asserts that the search result contains only the given list of OCI
+ projects.
+ """
+ naked = removeSecurityProxy
+ self.assertEqual(
+ set(naked(ociproject).id for ociproject in ociprojects),
+ set(naked(term.value).id for term in search_result))
+
+ def test_search_with_name_substring(self):
+ vocabulary = self.getVocabulary()
+ projects = self.createOCIProjects("test-project-%s", 10)
+ self.createOCIProjects("another-pattern-%s", 10)
+
+ search_result = vocabulary.searchForTerms("test-project")
+ self.assertContainsSameOCIProjects(projects, search_result)
+
+ def test_search_without_name_substring(self):
+ vocabulary = self.getVocabulary()
+ projects = self.createOCIProjects()
+ search_result = vocabulary.searchForTerms("")
+ self.assertContainsSameOCIProjects(projects, search_result)
+
+ def test_to_term(self):
+ vocabulary = self.getVocabulary()
+ ociproject = self.factory.makeOCIProject()
+ term = removeSecurityProxy(vocabulary).toTerm(ociproject)
+
+ expected_token = "%s/%s" % (ociproject.pillar.name, ociproject.name)
+ self.assertEqual(expected_token, term.title)
+ self.assertEqual(expected_token, term.token)
+
+ def test_getTermByToken(self):
+ vocabulary = self.getVocabulary()
+ ociproject = self.factory.makeOCIProject()
+ token = "%s/%s" % (ociproject.pillar.name, ociproject.name)
+ term = removeSecurityProxy(vocabulary).getTermByToken(token)
+ self.assertEqual(ociproject, term.value)
diff --git a/lib/lp/registry/vocabularies.py b/lib/lp/registry/vocabularies.py
index 05e3900..1cb109d 100644
--- a/lib/lp/registry/vocabularies.py
+++ b/lib/lp/registry/vocabularies.py
@@ -128,6 +128,7 @@ from lp.registry.interfaces.milestone import (
IMilestoneSet,
IProjectGroupMilestone,
)
+from lp.registry.interfaces.ociproject import IOCIProjectSet
from lp.registry.interfaces.person import (
IPerson,
IPersonSet,
@@ -155,6 +156,7 @@ from lp.registry.model.featuredproject import FeaturedProject
from lp.registry.model.karma import KarmaCategory
from lp.registry.model.mailinglist import MailingList
from lp.registry.model.milestone import Milestone
+from lp.registry.model.ociproject import OCIProject
from lp.registry.model.person import (
get_person_visibility_terms,
IrcID,
@@ -213,6 +215,7 @@ from lp.services.webapp.vocabulary import (
NamedSQLObjectVocabulary,
NamedStormHugeVocabulary,
SQLObjectVocabularyBase,
+ StormVocabularyBase,
VocabularyFilter,
)
from lp.soyuz.model.archive import Archive
@@ -2205,3 +2208,31 @@ class DistributionSourcePackageVocabulary(FilteredVocabularyBase):
return self.toTerm((dsp, binary_names))
return CountableIterator(results.count(), results, make_term)
+
+
+@implementer(IHugeVocabulary)
+class OCIProjectVocabulary(StormVocabularyBase):
+ """All OCI Projects."""
+
+ _table = OCIProject
+ displayname = 'Select an OCI project'
+ step_title = 'Search'
+
+ def toTerm(self, ociproject):
+ token = "%s/%s" % (ociproject.pillar.name, ociproject.name)
+ title = "%s" % token
+ return SimpleTerm(ociproject, token, title)
+
+ def getTermByToken(self, token):
+ pillar_name, name = token.split('/')
+ ociproject = getUtility(IOCIProjectSet).getByPillarAndName(
+ pillar_name, name)
+ if ociproject is None:
+ raise LookupError(token)
+ return self.toTerm(ociproject)
+
+ def search(self, query, vocab_filter=None):
+ return getUtility(IOCIProjectSet).findByName(query)
+
+ def _entries(self):
+ return getUtility(IOCIProjectSet).findByName('')
diff --git a/lib/lp/registry/vocabularies.zcml b/lib/lp/registry/vocabularies.zcml
index 4e2e488..37b7b14 100644
--- a/lib/lp/registry/vocabularies.zcml
+++ b/lib/lp/registry/vocabularies.zcml
@@ -1,4 +1,4 @@
-<!-- Copyright 2009-2016 Canonical Ltd. This software is licensed under the
+<!-- Copyright 2009-2020 Canonical Ltd. This software is licensed under the
GNU Affero General Public License version 3 (see the file LICENSE).
-->
@@ -562,4 +562,15 @@
permission="zope.Public"
attributes="name title description"/>
</class>
+
+ <securedutility
+ name="OCIProject"
+ component="lp.registry.vocabularies.OCIProjectVocabulary"
+ provides="zope.schema.interfaces.IVocabularyFactory">
+ <allow interface="zope.schema.interfaces.IVocabularyFactory" />
+ </securedutility>
+
+ <class class="lp.registry.vocabularies.OCIProjectVocabulary">
+ <allow interface="lp.services.webapp.vocabulary.IHugeVocabulary" />
+ </class>
</configure>