launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #18278
[Merge] lp:~cjwatson/launchpad/git-webservice-ref into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/git-webservice-ref into lp:launchpad.
Commit message:
Export GitRef and GitRepository.{refs,branches}.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/git-webservice-ref/+merge/256014
Export GitRef and GitRepository.{refs,branches}. This is a prerequisite for merge proposals, which will need to e.g. be able to call createMergeProposal on a GitRef.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/git-webservice-ref into lp:launchpad.
=== modified file 'lib/lp/_schema_circular_imports.py'
--- lib/lp/_schema_circular_imports.py 2015-04-08 10:35:22 +0000
+++ lib/lp/_schema_circular_imports.py 2015-04-13 19:06:51 +0000
@@ -69,6 +69,8 @@
from lp.code.interfaces.codereviewcomment import ICodeReviewComment
from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference
from lp.code.interfaces.diff import IPreviewDiff
+from lp.code.interfaces.gitref import IGitRef
+from lp.code.interfaces.gitrepository import IGitRepository
from lp.code.interfaces.hasbranches import (
IHasBranches,
IHasCodeImports,
@@ -559,6 +561,13 @@
# IDistroArchSeries
patch_reference_property(IDistroArchSeries, 'main_archive', IArchive)
+# IGitRef
+patch_reference_property(IGitRef, 'repository', IGitRepository)
+
+# IGitRepository
+patch_collection_property(IGitRepository, 'branches', IGitRef)
+patch_collection_property(IGitRepository, 'refs', IGitRef)
+
# ILiveFSFile
patch_reference_property(ILiveFSFile, 'livefsbuild', ILiveFSBuild)
=== modified file 'lib/lp/code/interfaces/gitref.py'
--- lib/lp/code/interfaces/gitref.py 2015-03-24 15:11:28 +0000
+++ lib/lp/code/interfaces/gitref.py 2015-04-13 19:06:51 +0000
@@ -10,6 +10,11 @@
'IGitRefBatchNavigator',
]
+from lazr.restful.declarations import (
+ export_as_webservice_entry,
+ exported,
+ )
+from lazr.restful.fields import ReferenceChoice
from zope.interface import (
Attribute,
Interface,
@@ -29,18 +34,28 @@
class IGitRef(Interface):
"""A reference in a Git repository."""
- repository = Attribute("The Git repository containing this reference.")
-
- path = TextLine(
+ # XXX cjwatson 2015-01-19 bug=760849: "beta" is a lie to get WADL
+ # generation working. Individual attributes must set their version to
+ # "devel".
+ export_as_webservice_entry(as_of="beta")
+
+ repository = exported(ReferenceChoice(
+ title=_("Repository"), required=True, readonly=True,
+ vocabulary="GitRepository",
+ # Really IGitRepository, patched in _schema_circular_imports.py.
+ schema=Interface,
+ description=_("The Git repository containing this reference.")))
+
+ path = exported(TextLine(
title=_("Path"), required=True, readonly=True,
description=_(
- "The full path of this reference, e.g. refs/heads/master."))
+ "The full path of this reference, e.g. refs/heads/master.")))
- commit_sha1 = TextLine(
+ commit_sha1 = exported(TextLine(
title=_("Commit SHA-1"), required=True, readonly=True,
description=_(
"The full SHA-1 object name of the commit object referenced by "
- "this reference."))
+ "this reference.")))
object_type = Choice(
title=_("Object type"), required=True, readonly=True,
=== modified file 'lib/lp/code/interfaces/gitrepository.py'
--- lib/lp/code/interfaces/gitrepository.py 2015-03-20 14:54:23 +0000
+++ lib/lp/code/interfaces/gitrepository.py 2015-04-13 19:06:51 +0000
@@ -33,7 +33,10 @@
operation_returns_entry,
REQUEST_USER,
)
-from lazr.restful.fields import Reference
+from lazr.restful.fields import (
+ CollectionField,
+ Reference,
+ )
from lazr.restful.interface import copy_field
from zope.component import getUtility
from zope.interface import (
@@ -186,9 +189,17 @@
"'lp:' plus a shortcut version of the path via that target. "
"Otherwise it is simply 'lp:' plus the unique name.")))
- refs = Attribute("The references present in this repository.")
+ refs = exported(CollectionField(
+ title=_("The references present in this repository."),
+ readonly=True,
+ # Really IGitRef, patched in _schema_circular_imports.py.
+ value_type=Reference(Interface)))
- branches = Attribute("The branch references present in this repository.")
+ branches = exported(CollectionField(
+ title=_("The branch references present in this repository."),
+ readonly=True,
+ # Really IGitRef, patched in _schema_circular_imports.py.
+ value_type=Reference(Interface)))
def getRefByPath(path):
"""Look up a single reference in this repository by path.
=== modified file 'lib/lp/code/interfaces/webservice.py'
--- lib/lp/code/interfaces/webservice.py 2015-03-05 16:23:26 +0000
+++ lib/lp/code/interfaces/webservice.py 2015-04-13 19:06:51 +0000
@@ -25,6 +25,7 @@
'ICodeReviewComment',
'ICodeReviewVoteReference',
'IDiff',
+ 'IGitRef',
'IGitRepository',
'IGitRepositorySet',
'IHasGitRepositories',
@@ -60,6 +61,7 @@
IDiff,
IPreviewDiff,
)
+from lp.code.interfaces.gitref import IGitRef
from lp.code.interfaces.gitrepository import (
IGitRepository,
IGitRepositorySet,
=== modified file 'lib/lp/code/model/tests/test_gitref.py'
--- lib/lp/code/model/tests/test_gitref.py 2015-03-13 14:15:24 +0000
+++ lib/lp/code/model/tests/test_gitref.py 2015-04-13 19:06:51 +0000
@@ -5,10 +5,21 @@
__metaclass__ = type
+import hashlib
+
+from testtools.matchers import EndsWith
+
from lp.code.interfaces.gitrepository import GIT_FEATURE_FLAG
from lp.services.features.testing import FeatureFixture
-from lp.testing import TestCaseWithFactory
+from lp.services.webapp.interfaces import OAuthPermission
+from lp.testing import (
+ ANONYMOUS,
+ api_url,
+ person_logged_in,
+ TestCaseWithFactory,
+ )
from lp.testing.layers import DatabaseFunctionalLayer
+from lp.testing.pages import webservice_for_person
class TestGitRef(TestCaseWithFactory):
@@ -22,3 +33,27 @@
self.assertEqual(
["master", "people/foo/bar"],
[ref.display_name for ref in (master, personal)])
+
+
+class TestGitRefWebservice(TestCaseWithFactory):
+ """Tests for the webservice."""
+
+ layer = DatabaseFunctionalLayer
+
+ def test_attributes(self):
+ self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
+ [master] = self.factory.makeGitRefs(paths=[u"refs/heads/master"])
+ webservice = webservice_for_person(
+ master.repository.owner, permission=OAuthPermission.READ_PUBLIC)
+ webservice.default_api_version = "devel"
+ with person_logged_in(ANONYMOUS):
+ repository_url = api_url(master.repository)
+ master_url = api_url(master)
+ response = webservice.get(master_url)
+ self.assertEqual(200, response.status)
+ result = response.jsonBody()
+ self.assertThat(result["repository_link"], EndsWith(repository_url))
+ self.assertEqual(u"refs/heads/master", result["path"])
+ self.assertEqual(
+ unicode(hashlib.sha1(u"refs/heads/master").hexdigest()),
+ result["commit_sha1"])
=== modified file 'lib/lp/code/vocabularies/configure.zcml'
--- lib/lp/code/vocabularies/configure.zcml 2012-09-05 05:08:26 +0000
+++ lib/lp/code/vocabularies/configure.zcml 2015-04-13 19:06:51 +0000
@@ -55,4 +55,15 @@
<allow interface="lp.services.webapp.vocabulary.IHugeVocabulary"/>
</class>
+ <securedutility
+ name="GitRepository"
+ component=".gitrepository.GitRepositoryVocabulary"
+ provides="zope.schema.interfaces.IVocabularyFactory">
+ <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
+ </securedutility>
+
+ <class class=".gitrepository.GitRepositoryVocabulary">
+ <allow interface="lp.services.webapp.vocabulary.IHugeVocabulary"/>
+ </class>
+
</configure>
=== added file 'lib/lp/code/vocabularies/gitrepository.py'
--- lib/lp/code/vocabularies/gitrepository.py 1970-01-01 00:00:00 +0000
+++ lib/lp/code/vocabularies/gitrepository.py 2015-04-13 19:06:51 +0000
@@ -0,0 +1,64 @@
+# Copyright 2015 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Vocabularies that contain Git repositories."""
+
+__metaclass__ = type
+
+__all__ = [
+ 'GitRepositoryVocabulary',
+ ]
+
+from zope.component import getUtility
+from zope.interface import implements
+from zope.schema.vocabulary import SimpleTerm
+
+from lp.code.interfaces.gitcollection import IAllGitRepositories
+from lp.code.model.gitrepository import GitRepository
+from lp.services.webapp.interfaces import ILaunchBag
+from lp.services.webapp.vocabulary import (
+ CountableIterator,
+ IHugeVocabulary,
+ SQLObjectVocabularyBase,
+ )
+
+
+class GitRepositoryVocabulary(SQLObjectVocabularyBase):
+ """A vocabulary for searching Git repositories."""
+
+ implements(IHugeVocabulary)
+
+ _table = GitRepository
+ _orderBy = ['name', 'id']
+ displayname = 'Select a Git repository'
+ step_title = 'Search'
+
+ def toTerm(self, repository):
+ """The display should include the URL if there is one."""
+ return SimpleTerm(
+ repository, repository.unique_name, repository.unique_name)
+
+ def getTermByToken(self, token):
+ """See `IVocabularyTokenized`."""
+ search_results = self.searchForTerms(token)
+ if search_results.count() == 1:
+ return iter(search_results).next()
+ raise LookupError(token)
+
+ def searchForTerms(self, query=None, vocab_filter=None):
+ """See `IHugeVocabulary`."""
+ user = getUtility(ILaunchBag).user
+ collection = self._getCollection().visibleByUser(user)
+ if query is None:
+ repositories = collection.getRepositories(eager_load=False)
+ else:
+ repositories = collection.search(query)
+ return CountableIterator(
+ repositories.count(), repositories, self.toTerm)
+
+ def __len__(self):
+ """See `IVocabulary`."""
+ return self.search().count()
+
+ def _getCollection(self):
+ return getUtility(IAllGitRepositories)
=== added file 'lib/lp/code/vocabularies/tests/test_gitrepository_vocabularies.py'
--- lib/lp/code/vocabularies/tests/test_gitrepository_vocabularies.py 1970-01-01 00:00:00 +0000
+++ lib/lp/code/vocabularies/tests/test_gitrepository_vocabularies.py 2015-04-13 19:06:51 +0000
@@ -0,0 +1,55 @@
+# Copyright 2015 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Test the Git repository vocabularies."""
+
+__metaclass__ = type
+
+from lp.code.interfaces.gitrepository import GIT_FEATURE_FLAG
+from lp.code.vocabularies.gitrepository import GitRepositoryVocabulary
+from lp.services.features.testing import FeatureFixture
+from lp.testing import TestCaseWithFactory
+from lp.testing.layers import DatabaseFunctionalLayer
+
+
+class TestGitRepositoryVocabulary(TestCaseWithFactory):
+ """Test that the GitRepositoryVocabulary behaves as expected."""
+
+ layer = DatabaseFunctionalLayer
+
+ def setUp(self):
+ super(TestGitRepositoryVocabulary, self).setUp()
+ self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
+ self._createRepositories()
+ self.vocab = GitRepositoryVocabulary(context=None)
+
+ def _createRepositories(self):
+ widget = self.factory.makeProduct(name="widget")
+ sprocket = self.factory.makeProduct(name="sprocket")
+ scotty = self.factory.makePerson(name="scotty")
+ self.factory.makeGitRepository(
+ owner=scotty, target=widget, name=u"fizzbuzz")
+ self.factory.makeGitRepository(
+ owner=scotty, target=widget, name=u"mountain")
+ self.factory.makeGitRepository(
+ owner=scotty, target=sprocket, name=u"fizzbuzz")
+
+ def test_fizzbuzzRepositories(self):
+ """Return repositories that match the string 'fizzbuzz'."""
+ results = self.vocab.searchForTerms("fizzbuzz")
+ expected = [
+ u"~scotty/sprocket/+git/fizzbuzz", u"~scotty/widget/+git/fizzbuzz"]
+ repository_names = sorted([repository.token for repository in results])
+ self.assertEqual(expected, repository_names)
+
+ def test_singleQueryResult(self):
+ # If there is a single search result that matches, use that
+ # as the result.
+ term = self.vocab.getTermByToken("mountain")
+ self.assertEqual(
+ "~scotty/widget/+git/mountain", term.value.unique_name)
+
+ def test_multipleQueryResult(self):
+ # If there are more than one search result, a LookupError is still
+ # raised.
+ self.assertRaises(LookupError, self.vocab.getTermByToken, "fizzbuzz")
=== modified file 'lib/lp/services/webservice/wadl-to-refhtml.xsl'
--- lib/lp/services/webservice/wadl-to-refhtml.xsl 2015-04-13 15:03:00 +0000
+++ lib/lp/services/webservice/wadl-to-refhtml.xsl 2015-04-13 19:06:51 +0000
@@ -317,6 +317,34 @@
<xsl:text>/+faq/</xsl:text>
<var ><faq.id></var>
</xsl:when>
+ <xsl:when test="@id = 'git_ref'">
+ <xsl:text>/~</xsl:text>
+ <var><person.name></var>
+ <xsl:text>/</xsl:text>
+ <var><project.name></var>
+ <xsl:text>/+git/</xsl:text>
+ <var><repository.name></var>
+ <xsl:text>/+ref/</xsl:text>
+ <var><path></var>
+ or
+ <xsl:text>/~</xsl:text>
+ <var><person.name></var>
+ <xsl:text>/</xsl:text>
+ <var><distribution.name></var>
+ <xsl:text>/+source/</xsl:text>
+ <var><source_package.name></var>
+ <xsl:text>/+git/</xsl:text>
+ <var><repository.name></var>
+ <xsl:text>/+ref/</xsl:text>
+ <var><path></var>
+ or
+ <xsl:text>/~</xsl:text>
+ <var><person.name></var>
+ <xsl:text>/+git/</xsl:text>
+ <var><repository.name></var>
+ <xsl:text>/+ref/</xsl:text>
+ <var><path></var>
+ </xsl:when>
<xsl:when test="@id = 'git_repository'">
<xsl:text>/~</xsl:text>
<var><person.name></var>
Follow ups